Exemplo n.º 1
0
def successors_mixed(primes: dict, state: Union[dict, str]) -> List[dict]:
    """
    Returns the successors of *state* in the mixed transition system defined by *primes*.
    The mixed update contains the synchronous and asynchronous STGs
    but it also allows transitions in which an arbitrary number of unstable components (but at least one) are updated.

    .. note::
        There are up to 2^n mixed successors for a state (n is the number of variables).

    **arguments**:
        * *primes*: prime implicants
        * *state*: a state

    **returns**:
        * *successors*: the mixed successors of *state*

    **example**::
        >>> primes = {
        'v1': [[{'v2': 0}], [{'v2': 1}]],
        'v2': [[{'v3': 0}, {'v1': 0}], [{'v1': 1, 'v3': 1}]],
        'v3': [[{'v1': 1, 'v2': 0}], [{'v2': 1}, {'v1': 0}]]
        }
        >>> state = "010"
        >>> successors_mixed(primes, state)
        [{'v1': 1, 'v2': 1, 'v3': 0},
         {'v1': 0, 'v2': 0, 'v3': 0},
         {'v1': 0, 'v2': 1, 'v3': 1},
         {'v1': 1, 'v2': 0, 'v3': 0},
         {'v1': 1, 'v2': 1, 'v3': 1},
         {'v1': 0, 'v2': 0, 'v3': 1},
         {'v1': 1, 'v2': 0, 'v3': 1}]
    """
    if type(state) == str:
        state = state2dict(primes, state)

    target = successor_synchronous(primes, state)
    if target == state:
        return [target]

    successors = []

    diff = [x for x in target if target[x] != state[x]]
    combinations = itertools.chain.from_iterable(
        itertools.combinations(diff, r) for r in range(1,
                                                       len(diff) + 1))

    for combination in combinations:
        successor = state.copy()
        for name in combination:
            successor[name] = target[name]
        successors.append(successor)

    return successors
Exemplo n.º 2
0
def local_igraph_of_state(primes: dict, state: Union[dict, str]) -> networkx.DiGraph:
    """
    Computes the local interaction graph dF/dx of a state x.

    **arguments**:
        * *primes*: prime implicants
        * *state*: a state

    **returns**:
        * *local_igraph*: the local interaction graph

    **example**::

        >>> primes = get_primes("remy_tumorigenesis")
        >>> state = random_state(primes)
        >>> local_igraph = local_igraph_of_state(primes, state)
        >>> add_style_interactionsigns(local_igraph)
        >>> igraph2image(local_igraph, "local_igraph.pdf")
        created local_igraph.pdf
    """

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

    local_igraph = create_empty_igraph(primes)

    def func(x):
        return successor_synchronous(primes, x)

    x = state

    for i in primes:
        for j in primes:

            y = dict(state)
            y[i] = 1 - x[i]

            dx = x[i] - y[i]
            df = func(x)[j] - func(y)[j]
            sign = int(df / dx)

            if sign:
                local_igraph.add_edge(i, j, sign={sign})

    return local_igraph
Exemplo n.º 3
0
def successor_synchronous(primes: dict, state: Union[dict, str]) -> dict:
    """
    Returns the successor of *state* in the fully synchronous transition system defined by *primes*.
    See :ref:`Klarner2015(b) <klarner2015approx>` Sec. 2.2 for a formal definition.

    **arguments**:
        * *primes*: prime implicants
        * *state*: a state

    **returns**:
        * *successor*: the synchronous successor of *state*

    **example**::
        >>> primes = {
        'v1': [[{'v2': 0}], [{'v2': 1}]],
        'v2': [[{'v3': 0}, {'v1': 0}], [{'v1': 1, 'v3': 1}]],
        'v3': [[{'v1': 1, 'v2': 0}], [{'v2': 1}, {'v1': 0}]]
        }
        >>> state = "100"
        >>> successor_synchronous(primes, state)
        {'v1': 0, 'v2': 0, 'v3': 0}
    """

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

    successor = {}
    for name in primes:
        stop = False
        for value in [0, 1]:
            if stop:
                break
            for prime in primes[name][value]:
                if stop:
                    break

                if all([state[d] == v for d, v in prime.items()]):
                    successor[name] = value
                    stop = True

    return successor
Exemplo n.º 4
0
def random_successor_mixed(primes: dict, state: Union[dict, str]) -> dict:
    """
    Returns a random successor of *state* in the mixed transition system defined by *primes*.
    The mixed update contains the synchronous and asynchronous STGs
    but it also allows transitions in which an arbitrary number of unstable components (but at least one) are updated.

    .. note::
        The reason why this function returns a random mixed transition rather than all mixed successors is that there are up to
        2^n mixed successors for a state (n is the number of variables).

    **arguments**:
        * *primes*: prime implicants
        * *state*: a state

    **returns**:
        * *successor*: a random successor of *state* using the mixed update

    **example**::

        >>> state = "100"
        >>> random_successor_mixed(primes, state)
        {'v1':1, 'v2':1, 'v3':1}
    """

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

    target = successor_synchronous(primes, state)
    if target == state:
        return target

    names = [x for x in target if target[x] != state[x]]
    k = random.randint(1, len(names))

    successor = state.copy()
    for name in random.sample(names, k):
        successor[name] = target[name]

    return successor
Exemplo n.º 5
0
def successors_asynchronous(primes: dict, state: Union[dict,
                                                       str]) -> List[dict]:
    """
    Returns the successors of *state* in the fully asynchronous transition system defined by *primes*.
    See :ref:`Klarner2015(b) <klarner2015approx>` Sec. 2.2 for a formal definition.

    **arguments**:
        * *primes*: prime implicants
        * *state*: a state

    **returns**:
        * *successors*: the asynchronous successors of *state*

    **example**::
        >>> primes = {
        'v1': [[{'v2': 0}], [{'v2': 1}]],
        'v2': [[{'v3': 0}, {'v1': 0}], [{'v1': 1, 'v3': 1}]],
        'v3': [[{'v1': 1, 'v2': 0}], [{'v2': 1}, {'v1': 0}]]}
        >>> state = "101"
        >>> successors_asynchronous(primes, state)
        [{'v1': 0, 'v2': 0, 'v3': 1}, {'v1': 1, 'v2': 1, 'v3': 1}, {'v1': 1, 'v2': 0, 'v3': 0}]
    """

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

    target = successor_synchronous(primes, state)
    if target == state:
        return [target]

    successors = []
    for name in target:
        if state[name] != target[name]:
            successor = state.copy()
            successor[name] = target[name]
            successors.append(successor)

    return successors
Exemplo n.º 6
0
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
Exemplo n.º 7
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.")