Exemple #1
0
def random_walk(primes: dict, update: str, initial_state: Union[dict, str],
                length: int):
    """
    Returns a random walk of *length* many states in the transition system defined by *primes* and *update*
    starting from a state defined by *initial_state*.
    If *initial_state* is a subspace then :ref:`random_state` will be used to draw a random state from inside it.

    **arguments**:
        * *primes*: prime implicants
        * *update*: the update strategy, one of *"asynchronous"*, *"synchronous"*, *"mixed"*
        * *initial_state*: an initial state or subspace
        * *length*: length of the random walk

    **returns**:
        * *path*: sequence of states

    **example**::

        >>> path = random_walk(primes, "asynchronous", "11---0", 4)
    """

    assert update in UPDATE_STRATEGIES

    if type(initial_state) is str:
        assert (len(initial_state) <= len(primes))
        x = {}
        for name, value in zip(sorted(primes), initial_state):
            if value.isdigit():
                x[name] = int(value)
        initial_state = x
    else:
        assert (set(initial_state).issubset(set(primes)))

    if update == "asynchronous":
        transition = lambda current_state: random.choice(
            successors_asynchronous(primes, current_state))

    elif update == "synchronous":
        transition = lambda current_state: successor_synchronous(
            primes, current_state)

    else:
        transition = lambda current_state: random_successor_mixed(
            primes, current_state)

    x = random_state(primes, subspace=initial_state)

    path = [x]
    while len(path) < length:
        path.append(transition(path[-1]))

    return path
def test_stg2image():
    fname_in = get_tests_path_in(fname="irma.primes")
    fname_out1 = get_tests_path_out(fname="irma_stg_async.pdf")
    fname_out2 = get_tests_path_out(fname="irma_stg_tendencies_async.pdf")
    fname_out3 = get_tests_path_out(fname="irma_stg_sccs_async.pdf")

    primes = read_primes(fname_json=fname_in)
    stg = primes2stg(primes=primes, update="asynchronous")
    stg2image(stg, fname_out1)

    add_style_tendencies(stg)
    stg2image(stg, fname_out2)

    stg = primes2stg(primes=primes, update="asynchronous")
    add_style_sccs(stg)
    stg2image(stg, fname_out3)

    fname_out1 = get_tests_path_out(fname="irma_stg_sync.pdf")
    fname_out2 = get_tests_path_out(fname="irma_stg_tendencies_sync.pdf")
    fname_out3 = get_tests_path_out(fname="irma_stg_sccs_sync.pdf")
    fname_out4 = get_tests_path_out(fname="irma_stg_path.pdf")

    primes = read_primes(fname_json=fname_in)
    stg = primes2stg(primes=primes, update="synchronous")
    stg2image(stg, fname_out1)

    stg = primes2stg(primes=primes, update="asynchronous")
    add_style_tendencies(stg)
    stg2image(stg, fname_out2)

    stg = primes2stg(primes=primes, update="synchronous")
    add_style_sccs(stg)
    stg2image(stg, fname_out3)

    init = random_state(primes=primes)
    walk = random_walk(primes=primes,
                       update="asynchronous",
                       initial_state=init,
                       length=5)
    stg = primes2stg(primes=primes, update="asynchronous")
    add_style_path(stg, walk, "red")
    init = pyboolnet.state_space.random_state(primes=primes)
    walk = random_walk(primes=primes,
                       update="asynchronous",
                       initial_state=init,
                       length=5)
    add_style_path(stg, walk, "blue")
    stg2image(stg, fname_out4)
def iterative_completeness_algorithm(
        primes: dict,
        update: str,
        compute_counterexample: bool,
        max_output: int = 1000) -> Union[Tuple[bool, Optional[dict]], bool]:
    """
    The iterative algorithm for deciding whether the minimal trap spaces are complete.
    The function is implemented by line-by-line following of the pseudo code algorithm given in
    "Approximating attractors of Boolean networks by iterative CTL model checking", Klarner and Siebert 2015.

    **arguments**:
        * *primes*: prime implicants
        * *update*: the update strategy, one of *"asynchronous"*, *"synchronous"*, *"mixed"*
        * *compute_counterexample*: whether to compute a counterexample

    **returns**:
        * *answer*: whether *subspaces* is complete in the STG defined by *primes* and *update*,
        * *counterexample*: a state that can not reach one of the minimal trap spaces of *primes* or *None* if no counterexample exists

    **example**::

        >>> answer, counterexample = completeness_with_counterexample(primes, "asynchronous")
        >>> answer
        False
        >>> state2str(counterexample)
        10010111101010100001100001011011111111
    """

    primes = percolate(primes=primes, copy=True)
    constants_global = find_constants(primes=primes)
    remove_all_constants(primes=primes)

    min_trap_spaces = compute_trap_spaces(primes=primes,
                                          type_="min",
                                          max_output=max_output)
    if min_trap_spaces == [{}]:
        if compute_counterexample:
            return True, None
        else:
            return True

    current_set = [({}, set([]))]
    while current_set:
        p, w = current_set.pop()
        primes_reduced = copy_primes(primes=primes)
        create_constants(primes=primes_reduced, constants=p)
        igraph = primes2igraph(primes=primes_reduced)

        cgraph = digraph2condensationgraph(digraph=igraph)
        cgraph_dash = cgraph.copy()

        for U in cgraph.nodes():
            if set(U).issubset(set(w)):
                cgraph_dash.remove_node(U)

        w_dash = w.copy()
        refinement = []
        top_layer = [
            U for U in cgraph_dash.nodes() if cgraph_dash.in_degree(U) == 0
        ]

        for U in top_layer:
            u_dash = find_ancestors(igraph, U)

            primes_restricted = copy_primes(primes_reduced)
            remove_all_variables_except(primes=primes_restricted, names=u_dash)

            q = compute_trap_spaces(primes=primes_restricted,
                                    type_="min",
                                    max_output=max_output)

            phi = exists_finally_one_of_subspaces(primes=primes_restricted,
                                                  subspaces=q)

            init = "INIT TRUE"
            spec = f"CTLSPEC {phi}"

            if compute_counterexample:
                answer, counterexample = model_checking(
                    primes=primes_restricted,
                    update=update,
                    initial_states=init,
                    specification=spec,
                    enable_counterexample=True)
                if not answer:
                    downstream = [x for x in igraph if x not in U]
                    arbitrary_state = random_state(downstream)
                    top_layer_state = counterexample[-1]
                    counterexample = merge_dicts([
                        constants_global, p, top_layer_state, arbitrary_state
                    ])

                    return False, counterexample
            else:
                answer = model_checking(primes=primes_restricted,
                                        update=update,
                                        initial_states=init,
                                        specification=spec)
                if not answer:
                    return False

            refinement += intersection([p], q)
            w_dash.update(u_dash)

        for q in intersection(refinement):
            q_tilde = find_constants(
                primes=percolate(primes=primes, add_constants=q, copy=True))

            if q_tilde not in min_trap_spaces:
                current_set.append((q_tilde, w_dash))

    if compute_counterexample:
        return True, None
    else:
        return True
def find_attractor_state_by_randomwalk_and_ctl(primes: dict,
                                               update: str,
                                               initial_state: Union[dict,
                                                                    str] = {},
                                               length: int = 0,
                                               attempts: int = 10) -> dict:
    """
    Attempts to find a state inside an attractor by the "long random walk" method,
    see :ref:`Klarner2015(b) <klarner2015approx>` Sec. 3.2 for a formal definition.
    The method generates a random walk of *length* states, starting from a state defined by *initial_state*.
    If *initial_state* is a subspace then :ref:`random_state` will be used to draw a random state from inside it.
    The function then uses CTL model checking, i.e., :ref:`check_primes <check_primes>`,
    to decide whether the last state of the random walk is inside an attractor.
    If so it is returned, otherwise the process is repeated.
    If no attractor state has been found after *Attempts* many trials an exception is raised.

    .. note::
        The default value for length, namely *length=0*, will be replaced by::

            length = 10*len(primes)

        which proved sufficiently large in our computer experiments.

    **arguments**:
        * *primes*: prime implicants
        * *update*: the update strategy, one of *"asynchronous"*, *"synchronous"*, *"mixed"*
        * *initial_state*: an initial state or subspace
        * *length*: length of random walk
        * *attempts*: number of attempts before exception is raised

    **returns**:
        * *attractor_state*: a state that belongs to some attractor
        * raises *Exception* if no attractor state is found

    **example**::
            >>> find_attractor_state_by_randomwalk_and_ctl(primes, "asynchronous")
            {"v1": 1, "v2": 1, "v3": 1}
    """

    if type(initial_state) == str:
        initial_state = state2dict(primes=primes, state=initial_state)

    assert update in UPDATE_STRATEGIES
    assert set(initial_state).issubset(set(primes))

    if length == 0:
        length = 10 * len(primes)

    if update == "asynchronous":
        transition = partial(random_successor_asynchronous, primes)

    elif update == "synchronous":
        transition = partial(successor_synchronous, primes)

    elif update == "mixed":
        transition = partial(random_successor_mixed, primes)
    else:
        log.error(f"unknown update strategy: update={update}")
        raise Exception

    log.info("find_attractor_state_by_randomwalk_and_ctl(..)")
    log.info(
        f"len(primes)={len(primes)}, update={update}, length={length}, attempts={attempts}"
    )

    trials = 0
    while trials < attempts:
        log.info(f"trial {trials}")

        current_state = random_state(primes, initial_state)
        log.info(f"start: {state2str(current_state)}")

        transitions = 0
        while transitions < length:
            current_state = transition(current_state)
            transitions += 1

        log.info(f"end: {state2str(current_state)}")

        formula = all_globally_exists_finally_one_of_sub_spaces(
            primes=primes, sub_spaces=[current_state])
        spec = f"CTLSPEC {formula}"
        init = f"INIT {subspace2proposition(primes=primes, subspace=current_state)}"

        if model_checking(primes, update, init, spec):
            log.info("is attractor state")
            return current_state

        trials += 1

    log.error(
        "could not find attractor state: increase Length or Attempts parameter."
    )
    raise Exception
Exemple #5
0
def best_first_reachability(primes: dict,
                            initial_space: Union[str, dict],
                            goal_space: Union[str, dict],
                            memory: int = 1000):
    """
    Performs a best-first search in the asynchronous transition system defined by *primes* to answer the question whether there
    is a path from a random state in *InitalSpace* to a state in *GoalSpace*.
    *Memory* specifies the maximal number of states that can be kept in memory as "already explored" before the algorithm terminates.
    The search is guided by minimizing the Hamming distance between the current state of an incomplete path and the *GoalSpace*
    where variables that are free in *GoalSpace* are ignored.

    .. note::
        If the number of variables is less than 40 you should use LTL or CTL model checking to answer questions of reachability.
        :ref:`best_first_reachability` is meant for systems with more than 40 variables.
        If :ref:`best_first_reachability` returns *None* then that does not prove that there is no path between *InitialSpace* and *GoalSpace*.

    **arguments**:
        * *primes*: prime implicants
        * *initial_space*: initial subspace
        * *goal_space*: goal subspace
        * *memory*: maximal number of states memorized before search is stopped

    **returns**:
        * *path*: a path from *InitalSpace* to *GoalSpace* if it was found, or *None* otherwise.

    **example**::

        >>> initspace = "1--0"
        >>> goalspace = "0--1"
        >>> path = best_first_reachability(primes, initialstate, goalspace)
        >>> if path: print(len(path))
        4
    """

    if type(initial_space) is str:
        initial_space = subspace2dict(primes, initial_space)
    if type(goal_space) is str:
        goal_space = subspace2dict(primes, goal_space)

    xdict = random_state(primes, subspace=initial_space)
    x = state2str(xdict)

    fringe = []
    seen = set([])
    heapq.heappush(fringe, (hamming_distance(xdict, goal_space), [x]))
    seen.add(x)

    while fringe:
        dist, path = heapq.heappop(fringe)
        if dist == 0:
            return path

        x = path[-1]
        for ydict in successors_asynchronous(primes, state2dict(primes, x)):
            y = state2str(ydict)
            if y not in seen:
                seen.add(y)
                heapq.heappush(
                    fringe, (hamming_distance(ydict, goal_space), path + [y]))

        if len(seen) > memory:
            break

    log.info(f"explored {len(seen)} transitions, no path found.")
def test_random_state():
    primes = get_primes(name="n6s1c2")
    assert type(random_state(primes=primes)) is dict
    assert type(random_state(primes=primes, subspace="111-0-")) is dict