Beispiel #1
0
def test_back_to_original():
    nodes = [0, 1, 2, "a", "b", frozenset([5])]

    graph = nx.DiGraph()
    graph.add_nodes_from(nodes)

    _, node_to_idx = convert_to_integer_graph(graph)

    partition = [("a", "b"), (0, 1, 2), (frozenset([5]), )]
    integer_partition = [(node_to_idx[node] for node in block)
                         for block in partition]

    assert set(
        frozenset(tp)
        for tp in back_to_original(integer_partition, node_to_idx)) == set(
            frozenset(tp) for tp in partition)
Beispiel #2
0
def saha(graph,
         initial_partition=None,
         is_integer_graph=False) -> SahaPartition:
    """
    Returns an instance of the class :class:`SahaPartition` which can be used
    to recompute the maximum bisimulation incrementally.

    :param graph: The initial graph.
    :initial_partition: The initial partition, or labeling set. This is
        **not** the partition from which we start, but an indication of which
        nodes cannot be bisimilar. Defaultsto `None`, in which case the trivial
        labeling set (one block which contains all the nodes) is used.
    :param is_integer_graph: If `True`, the function assumes that
        the graph is integer, and skips the integer check (may slightly
        improve performance). Defaults to `False`.
    """

    if not isinstance(graph, nx.DiGraph):
        raise Exception("graph should be a directed graph (nx.DiGraph)")

    # if True, the input graph is already an integer graph
    original_graph_is_integer = is_integer_graph or check_normal_integer_graph(
        graph)
    if not original_graph_is_integer:
        # convert the graph to an "integer" graph
        integer_graph, node_to_idx = convert_to_integer_graph(graph)

        if initial_partition is not None:
            # convert the initial partition to a integer partition
            integer_initial_partition = [[
                node_to_idx[old_node] for old_node in block
            ] for block in initial_partition]
        else:
            integer_initial_partition = None
    else:
        integer_graph = graph
        integer_initial_partition = initial_partition
        node_to_idx = None

    vertexes, q_partition = decorate_nx_graph(
        integer_graph,
        integer_initial_partition,
    )

    # compute the current maximum bisimulation
    q_partition = paige_tarjan_qblocks(q_partition)
    return SahaPartition(q_partition, vertexes, node_to_idx)
Beispiel #3
0
def test_integer_graph():
    nodes = [0, 1, 2, "a", "b", frozenset([5]), nx.DiGraph()]

    graph = nx.DiGraph()
    graph.add_nodes_from(nodes)

    # add an edge to the next node, and to the first node in the list
    for i in range(len(nodes) - 1):
        graph.add_edge(nodes[i], nodes[0])
        graph.add_edge(nodes[i], nodes[i + 1])

    integer_graph, node_to_idx = convert_to_integer_graph(graph)

    # test the map's correctness
    for node in nodes:
        assert nodes[node_to_idx[node]] == node

    # test the correctness of edges
    for edge in integer_graph.edges:
        assert edge[1] == edge[0] + 1 or edge[1] == 0
Beispiel #4
0
def graph_to_integer_graph(graph, initial_partition):
    original_graph_is_integer = check_normal_integer_graph(graph)

    # if initial_partition is None, then it's the trivial partition
    if initial_partition is None:
        # only list(graph.nodes) isn't OK
        initial_partition = [list(graph.nodes)]

    if not original_graph_is_integer:
        # convert the graph to an "integer" graph
        integer_graph, node_to_idx = convert_to_integer_graph(graph)

        # convert the initial partition to a integer partition
        integer_initial_partition = [[
            node_to_idx[old_node] for old_node in block
        ] for block in initial_partition]
    else:
        integer_graph = graph
        integer_initial_partition = initial_partition

    return (integer_graph, integer_initial_partition)
def dovier_piazza_policriti(
    graph: nx.Graph,
    initial_partition: List[Tuple[int]] = None,
    is_integer_graph: bool = False,
) -> List[Tuple]:
    """Compute the RSCP/maximum bisimulation of the given graph using
    *Dovier-Piazza-Policriti*'s algorithm.

    Example:
        >>> graph = networkx.balanced_tree(2,3)
        >>> dovier_piazza_policriti(graph)
        [(7, 8, 9, 10, 11, 12, 13, 14), (3, 4, 5, 6), (1, 2), (0,)]

    This function works with integer graph (nodes are integers starting from
    0 and form an interval without holes). If the given graph is non-integer
    it is converted to an isomorphic integer graph automatically (unless
    `is_integer_graph` is `True`) and then re-converted to its original form
    after the end of the computation. For this reason nodes of `graph` **must**
    be hashable objects.

    .. warning::
        Using a non integer graph and setting `is_integer_graph` to `True`
        will probably make the function fail with an exception, or, even worse,
        return a wrong output.

    :param graph: The input graph.
    :param initial_partition: The initial partition (or labeling set). Defaults
        to `None`, in which case the trivial labeling set (one block which
        contains all the nodes) is used.
    :param is_integer_graph: If `True`, we do not check if the given graph is
        integer (saves time). If `is_integer_graph` is `True` but the graph
        is not integer the output may be wrong. Defaults to False.
    :returns: The RSCP/maximum bisimulation of the given labeling set as a
        list of tuples, each of which contains bisimilar nodes.
    """

    if not isinstance(graph, nx.DiGraph):
        raise Exception("graph should be a directed graph (nx.DiGraph)")

    # if True, the input graph is already an integer graph
    original_graph_is_integer = is_integer_graph or check_normal_integer_graph(
        graph)

    if not original_graph_is_integer:
        # convert the graph to an "integer" graph
        integer_graph, node_to_idx = convert_to_integer_graph(graph)
    else:
        integer_graph = graph

    vertexes, _ = decorate_nx_graph(integer_graph, initial_partition)
    partition = RankedPartition(vertexes)

    tp = dovier_piazza_policriti_partition(partition)
    collapsed_partition, collapse_map = tp

    # from the collapsed partition obtained from FBA, build the RSCP (external
    # representation, List[Tuple[int]])
    rscp = []
    for rank in collapsed_partition:
        for block in rank:
            if block.vertexes.size > 0:
                block_survivor_node = block.vertexes.first.value
                block_vertexes = [block_survivor_node.label]

                if collapse_map[block_survivor_node.label] is not None:
                    block_vertexes.extend(
                        map(
                            lambda vertex: vertex.label,
                            collapse_map[block_survivor_node.label],
                        ))

                rscp.append(tuple(block_vertexes))

    if original_graph_is_integer:
        return rscp
    else:
        return back_to_original(rscp, node_to_idx)