Ejemplo n.º 1
0
def test_well_founded_topological():
    g = nx.DiGraph()
    g.add_nodes_from(range(8))
    g.add_edges_from([(0, 1), (1, 2), (3, 4), (4, 5), (6, 7), (7, 6)])

    partition = [(0, 3), (1, 4), (2, 5), (6, 7)]

    vertexes, _ = decorate_nx_graph(g, partition)

    max_rank = max(map(lambda vx: vx.rank, vertexes))

    qpartition = [block for ls in RankedPartition(vertexes) for block in ls]

    topo = build_well_founded_topological_list(
        qpartition, vertexes[5], max_rank
    )

    assert len(topo) == 6

    last_rank = None
    for vx in topo:
        assert vx.wf
        if last_rank is not None:
            assert last_rank <= vx.rank
        last_rank = vx.rank
Ejemplo n.º 2
0
def test_iter():
    vertexes, _ = decorate_nx_graph(nx.balanced_tree(2, 3))
    partition = RankedPartition(vertexes)
    idx = 0
    for r in partition:
        assert r == partition._partition[idx]
        idx += 1
Ejemplo n.º 3
0
def test_split_upper_ranks(graph):
    vertexes, _ = decorate_nx_graph(graph)
    max_rank = max(vertex.rank for vertex in vertexes)
    partition_length = 0 if max_rank == float("-inf") else max_rank + 2

    for idx in range(partition_length):
        partition = RankedPartition(vertexes)
        split_upper_ranks(partition, partition[idx][0])
        assert all(
            check_block_stability(partition[idx][0], upper_rank_block)
            for upper_idx in range(idx + 1, partition_length)
            for upper_rank_block in partition[upper_idx])
Ejemplo n.º 4
0
def split_upper_ranks(partition: RankedPartition, block: _Block):
    """Split the blocks whose `rank` is **greater** than `block.rank` using
    `block` as *splitter*.

    :param partition: The current partition.
    :param block: The splitter block.
    """

    block_counterimage = build_block_counterimage(block)

    modified_blocks = []

    for vertex in block_counterimage:
        # if this is an upper-rank node with respect to the collapsed block, we
        # need to split. the split is not needed if the block is a singoletto.
        if vertex.rank > block.rank and not (
                vertex.qblock.split_helper_block is None
                and vertex.qblock.size <= 1):
            # if needed, create the aux block to help during the splitting
            # phase
            if vertex.qblock.split_helper_block is None:
                vertex.qblock.initialize_split_helper_block()
                modified_blocks.append(vertex.qblock)

            new_vertex_block = vertex.qblock.split_helper_block

            # remove the vertex in the counterimage from its current block
            vertex.qblock.remove_vertex(vertex)
            # put the vertex in the counterimage in the aux block
            new_vertex_block.append_vertex(vertex)

    # insert the new blocks in the partition, and then reset aux block for each
    # modified block.
    for mod_block in modified_blocks:
        # we use the rank of aux block because we're sure it's not None
        partition.append_at_rank(
            block=mod_block.split_helper_block,
            rank=mod_block.split_helper_block.rank,
        )
        mod_block.split_helper_block = None
Ejemplo n.º 5
0
def test_is_in_image():
    graph = nx.DiGraph()
    graph.add_nodes_from(range(5))
    graph.add_edges_from([(0, 1), (1, 2), (2, 3), (4, 1)])

    vertexes, _ = decorate_nx_graph(graph)
    RankedPartition(vertexes)

    assert is_in_image(vertexes[0].qblock, vertexes[1].qblock)
    assert not is_in_image(vertexes[1].qblock, vertexes[0].qblock)
    assert is_in_image(vertexes[1].qblock, vertexes[2].qblock)
    assert is_in_image(vertexes[2].qblock, vertexes[3].qblock)
    assert not is_in_image(vertexes[0].qblock, vertexes[4].qblock)
    assert not is_in_image(vertexes[4].qblock, vertexes[0].qblock)
Ejemplo n.º 6
0
def test_merge_split_resets_visited_allowvisit_oldqblockid():
    g = nx.DiGraph()
    g.add_nodes_from(range(5))
    g.add_edges_from([(0, 1), (1, 0), (2, 1), (2, 4), (3, 1)])

    partition = [(2, 3), (1, 0), (4,)]

    vertexes, _ = decorate_nx_graph(g, partition)
    qblocks = [block for ls in RankedPartition(vertexes) for block in ls]

    finishing_time_list = [vertexes[2], vertexes[1], vertexes[0]]

    merge_split_phase(qblocks, finishing_time_list)

    assert all([not vertex.visited for vertex in vertexes])
    assert all([not vertex.allow_visit for vertex in vertexes])
Ejemplo n.º 7
0
def test_different_blocks_initial_partition():
    graph = nx.balanced_tree(2, 3, create_using=nx.DiGraph)
    initial_partition = [
        (0, 1, 2),
        (3, 4),
        (5, 6),
        (7, 8, 9, 10),
        (11, 12, 13),
        (14, ),
    ]
    vertexes, _ = decorate_nx_graph(nx.balanced_tree(2, 3), initial_partition)

    partition = RankedPartition(vertexes)

    assert len(partition[1]) == 3
    assert len(partition[2]) == 2
Ejemplo n.º 8
0
def test_create_initial_partition(graph):
    vertexes, _ = decorate_nx_graph(graph)
    partition = RankedPartition(vertexes)

    # at least one block per rank, except for float('-inf')
    assert all(len(partition[idx]) for idx in range(1, len(partition)))

    # right vertexes in the right place
    for idx in range(len(partition)):
        rank = float("-inf") if idx == 0 else idx - 1

        # right number of vertexes
        assert partition[idx][0].vertexes.size == [
            vertex.rank == rank for vertex in vertexes
        ].count(True)
        # right rank
        assert all(vertex.rank == rank
                   for vertex in partition[idx][0].vertexes)
Ejemplo n.º 9
0
def test_merge_split_resets_visited_triedmerge_qblocks():
    g = nx.DiGraph()
    g.add_nodes_from(range(5))
    g.add_edges_from([(0, 1), (1, 0), (2, 1), (2, 4), (3, 1)])

    partition = [(2, 3), (1, 0), (4,)]

    vertexes, _ = decorate_nx_graph(g, partition)
    qblocks = [block for ls in RankedPartition(vertexes) for block in ls]

    qblocks[0].visited = True
    qblocks[2].visited = True

    finishing_time_list = [vertexes[2], vertexes[1], vertexes[0]]

    qpartition = merge_split_phase(qblocks, finishing_time_list)

    assert all([not block.visited for block in qpartition])
    assert all([not block.tried_merge for block in qpartition])
Ejemplo n.º 10
0
def test_add_edge():
    graph = nx.DiGraph()
    graph.add_nodes_from(range(5))
    graph.add_edges_from([(0, 1), (1, 2), (2, 3), (4, 1)])

    vertexes, _ = decorate_nx_graph(graph)
    ranked_partition = RankedPartition(vertexes)

    partition = []
    for rank in ranked_partition:
        for block in ranked_partition:
            partition.append(block)

    edge1 = add_edge(vertexes[0], vertexes[3])
    assert edge1.count is not None
    assert edge1.count.value == 2

    edge2 = add_edge(vertexes[3], vertexes[4])
    assert edge2.count is not None
    assert edge2.count.value == 1
Ejemplo n.º 11
0
def dovier_piazza_policriti_partition(
    partition: RankedPartition,
) -> Tuple[RankedPartition, List[List[_Vertex]]]:
    """Apply *Dovier-Piazza-Policriti*'s algorithm to the given ranked
    partition.

    :param partition: A ranked partition (:math:`P` in the paper).
    :returns: A tuple such that the first item is the partition at the end of
        the algorithm (which at this point is made of blocks of size 1
        containing only the vertexes which survived the collapse), and the
        second is a list which maps a survivor nodes to the list of nodes
        collapsed to that survivor node.
    """

    # maps each survivor node to a list of nodes collapsed into it
    collapse_map = [None for _ in range(partition.nvertexes)]

    # loop over the ranks
    for partition_idx in range(len(partition)):
        if len(partition[partition_idx]) == 1:
            if len(partition[partition_idx][0].vertexes):
                block = partition[partition_idx][0]
                survivor_vertex, collapsed_vertexes = collapse(block)
                if survivor_vertex is not None:
                    # update the collapsed nodes map
                    collapse_map[survivor_vertex.label] = collapsed_vertexes
                    # update the partition
                    split_upper_ranks(partition, block)
        # OPTIMIZATION: if at the current rank we only have blocks of single
        # vertexes, skip this step.
        elif any(map(lambda block: block.size > 1, partition[partition_idx])):
            current_label = 0
            for block in partition[partition_idx]:
                for vertex in block.vertexes:
                    # scale vertex
                    vertex.scale_label(current_label)
                    current_label += 1

                    # exclude nodes having the wrong rank from the image and
                    # counterimage of the vertex. from now they're gone
                    # forever.
                    vertex.restrict_to_subgraph(
                        validation=lambda vx: vx.rank == vertex.rank)

            # apply PTA to the subgraph at the current examined rank
            # CAREFUL: if you debug here, you'll see that there are some
            # "duplicate" nodes (nodes with the same label in different blocks
            # of the partition). this happens becaus of the SCALING (which is
            # used to pass a normal graph to PTA)
            rscp = paige_tarjan_qblocks(partition[partition_idx])

            # clear the partition at the current rank
            partition.clear_index(partition_idx)

            # insert the new blocks in the partition at the current rank, and
            # collapse each block.
            for block in rscp:
                block_vertexes = []
                for scaled_vertex in block.vertexes:
                    scaled_vertex.back_to_original_label()
                    scaled_vertex.back_to_original_graph()
                    block_vertexes.append(scaled_vertex)

                # we can set XBlock to None because PTA won't be called again
                # on these blocks
                internal_block = _Block(block_vertexes, None)

                survivor_vertex, collapsed_vertexes = collapse(internal_block)

                if survivor_vertex is not None:
                    # update the collapsed nodes map
                    collapse_map[survivor_vertex.label] = collapsed_vertexes
                    # add the new block to the partition
                    partition.append_at_index(internal_block, partition_idx)
                    # update the upper ranks with respect to this block
                    split_upper_ranks(partition, internal_block)
        else:
            for block in partition[partition_idx]:
                # update the upper ranks with respect to this block
                split_upper_ranks(partition, block)

    return (partition, collapse_map)
Ejemplo n.º 12
0
def test_get_item():
    vertexes, _ = decorate_nx_graph(nx.balanced_tree(2, 3))
    partition = RankedPartition(vertexes)
    assert partition[1] == partition._partition[1]
Ejemplo n.º 13
0
def test_clear_index():
    vertexes, _ = decorate_nx_graph(nx.balanced_tree(2, 3))
    partition = RankedPartition(vertexes)
    partition.clear_index(1)
    assert len(partition[1]) == 0
Ejemplo n.º 14
0
def test_scc_leaf_length():
    vertexes, _ = decorate_nx_graph(nx.balanced_tree(2, 3))
    partition = RankedPartition(vertexes)
    assert partition[0][0].size == 0
Ejemplo n.º 15
0
def test_len():
    vertexes, _ = decorate_nx_graph(nx.balanced_tree(2, 3))
    partition = RankedPartition(vertexes)
    assert len(partition) == 5
Ejemplo n.º 16
0
def test_append_at_index():
    vertexes, _ = decorate_nx_graph(nx.balanced_tree(2, 3))
    partition = RankedPartition(vertexes)
    block = _QBlock([], None)
    partition.append_at_index(block, 1)
    assert partition[1][-1] == block
Ejemplo n.º 17
0
def test_nvertexes(graph):
    vertexes, _ = decorate_nx_graph(graph)
    partition = RankedPartition(vertexes)
    assert partition.nvertexes == len(graph.nodes)
Ejemplo n.º 18
0
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)
Ejemplo n.º 19
0
def test_rank_to_partition_idx(rank, expected):
    assert RankedPartition.rank_to_partition_idx(rank) == expected