Beispiel #1
0
def stg2condensationgraph(stg: networkx.DiGraph) -> networkx.DiGraph:
    """
    Converts the *stg* into the condensation graph, for a definition see :ref:`Klarner2015(b) <klarner2015approx>`.

    **arguments**:
        * *stg*: state transition graph

    **returns**:
        * *cgraph*: the condensation graph of *stg*

    **example**::

        >>> cgraph = stg2condensationgraph(stg)
    """

    graph = digraph2condensationgraph(stg)
    graph.graph["node"] = {
        "color": "none",
        "style": "filled",
        "fillcolor": "lightgray",
        "shape": "rect"
    }

    for node in graph.nodes():
        lines = [
            ",".join(x) for x in divide_list_into_similar_length_lists(node)
        ]
        graph.nodes[node]["label"] = "<%s>" % ",<br/>".join(lines)

    return graph
def add_style_sccs(igraph: networkx.DiGraph):
    """
    Adds a subgraph for every non-trivial strongly connected component (SCC) to the *dot* representation of *igraph*.
    Nodes that belong to the same *dot* subgraph are contained in a rectangle and treated separately during layout computations.
    Each subgraph is filled by a shade of gray that gets darker with an increasing number of SCCs that are above it in the condensation graph.
    Shadings repeat after a depth of 9.

    **arguments**:
        * *igraph*: interaction graph

    **example**::

          >>> add_style_sccs(igraph)
    """

    condensation_graph = digraph2condensationgraph(igraph)

    for scc in condensation_graph.nodes():
        depth = condensation_graph.nodes[scc]["depth"]
        col = 2 + (depth % 8)

        subgraph = networkx.DiGraph()
        subgraph.add_nodes_from(scc)
        subgraph.graph["style"] = "filled"
        subgraph.graph["color"] = "none"
        subgraph.graph["fillcolor"] = f"/greys9/{col}"

        igraph.graph["subgraphs"].append(subgraph)
def find_minimal_autonomous_nodes(igraph: networkx.DiGraph, core: Set[str]) -> List[Set[str]]:
    """
    Returns the minimal autonomous node sets of *igraph*.
    See :ref:`Klarner2015(b) <klarner2015approx>` Sec. 5.2 for a formal definition and details.
    Minimal autonomous sets generalize inputs, which are autonomous sets of size 1.
    If *Superset* is specified then all autonomous sets that are not supersets of it are ignored.

    **arguments**:
        * *igraph*: interaction graph
        * *core*: all autonomous sets must be supersets of these components

    **returns**:
        * *autonomous_nodes* (list of sets): the minimal autonomous node sets of *igraph*

    **example**::

          >>> find_minimal_autonomous_nodes(igraph)
          [set(["raf"]), set(["v1","v8","v9"])]
    """

    cgraph = digraph2condensationgraph(igraph)
    for x in cgraph.nodes():
        if set(x).issubset(core):
            cgraph.remove_node(x)

    return [set(x) for x in cgraph.nodes() if cgraph.in_degree(x) == 0]
Beispiel #4
0
def compute_attractors_tarjan(stg: networkx.DiGraph):
    """
    Uses `networkx.strongly_connected_components <https://networkx.github.io/documentation/latest/reference/generated/networkx.algorithms.components.strongly_connected.strongly_connected_components.html>`_
    , i.e., Tarjan's algorithm with Nuutila's modifications, to compute the SCCs of *stg* and
    `networkx.has_path <https://networkx.github.io/documentation/latest/reference/generated/networkx.algorithms.shortest_paths.generic.has_path.html>`_
    to decide whether a SCC is reachable from another.
    Returns the attractors as lists of states.


    **arguments**:
        * *stg*: state transition graph

    **returns**:
        * *steady_states*: the steady states
        * *cyclic_attractors*: the cyclic attractors

    **example**:

        >>> bnet = ["x, !x&y | z",
        ...         "y, !x | !z",
        ...         "z, x&!y"]
        >>> bnet = "\\n".join(bnet)
        >>> primes = bnet2primes(bnet)
        >>> stg = primes2stg(primes, "asynchronous")
        >>> steady_states, cyclic_attractors = compute_attractors_tarjan(stg)
        >>> steady_states
        ["101","000"]
        >>> cyclic_attractors
        [{"111", "110"}, {"001", "011"}]
    """

    condensation_graph = digraph2condensationgraph(digraph=stg)
    steady_states = []
    cyclic = []
    for scc in condensation_graph.nodes():
        if not list(condensation_graph.successors(scc)):
            if len(scc) == 1:
                steady_states.append(scc[0])
            else:
                cyclic.append(set(scc))

    return steady_states, cyclic
Beispiel #5
0
def add_style_sccs(stg: networkx.DiGraph):
    """
    Adds a subgraph for every non-trivial strongly connected component (SCC) to the *dot* representation of *stg*.
    Nodes that belong to the same *dot* subgraph are contained in a rectangle and treated separately during layout computations.
    Each subgraph is filled by a shade of gray that gets darker with an increasing number of SCCs that are above it in the condensation graph.
    Shadings repeat after a depth of 9.

    **arguments**:
        * *stg*: state transition graph

    **example**::

          >>> add_style_sccs(stg)
    """

    condensation_graph = digraph2condensationgraph(stg)

    for i, scc in enumerate(condensation_graph.nodes()):
        depth = condensation_graph.nodes[scc]["depth"]
        col = 2 + (depth % 8)

        subgraph = networkx.DiGraph()
        subgraph.add_nodes_from(scc)
        subgraph.graph["style"] = "filled"
        subgraph.graph["color"] = "black"
        subgraph.graph["fillcolor"] = f"/greys9/{col}"

        if not list(condensation_graph.successors(scc)):
            if len(scc) == 1:
                subgraph.graph["label"] = "steady state"
            else:
                subgraph.graph["label"] = "cyclic attractor"

        if not stg.graph["subgraphs"]:
            stg.graph["subgraphs"] = []

        for x in list(stg.graph["subgraphs"]):
            if sorted(x.nodes()) == sorted(subgraph.nodes()):
                stg.graph["subgraphs"].remove(x)

        stg.graph["subgraphs"].append(subgraph)
Beispiel #6
0
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