예제 #1
0
def hamiltonian_path(G):
    """Returns a Hamiltonian path in the given tournament graph.

    Each tournament has a Hamiltonian path. If furthermore, the
    tournament is strongly connected, then the returned Hamiltonian path
    is a Hamiltonian cycle (by joining the endpoints of the path).

    Parameters
    ----------
    G : NetworkX graph
        A directed graph representing a tournament.

    Returns
    -------
    bool
        Whether the given graph is a tournament graph.

    Notes
    -----
    This is a recursive implementation with an asymptotic running time
    of $O(n^2)$, ignoring multiplicative polylogarithmic factors, where
    $n$ is the number of nodes in the graph.

    """
    if len(G) == 0:
        return []
    if len(G) == 1:
        return [arbitrary_element(G)]
    v = arbitrary_element(G)
    hampath = hamiltonian_path(G.subgraph(set(G) - {v}))
    # Get the index of the first node in the path that does *not* have
    # an edge to `v`, then insert `v` before that node.
    index = index_satisfying(hampath, lambda u: v not in G[u])
    hampath.insert(index, v)
    return hampath
예제 #2
0
def hamiltonian_path(G):
    """Returns a Hamiltonian path in the given tournament graph.

    Each tournament has a Hamiltonian path. If furthermore, the
    tournament is strongly connected, then the returned Hamiltonian path
    is a Hamiltonian cycle (by joining the endpoints of the path).

    Parameters
    ----------
    G : NetworkX graph
        A directed graph representing a tournament.

    Returns
    -------
    bool
        Whether the given graph is a tournament graph.

    Notes
    -----
    This is a recursive implementation with an asymptotic running time
    of $O(n^2)$, ignoring multiplicative polylogarithmic factors, where
    $n$ is the number of nodes in the graph.

    """
    if len(G) == 0:
        return []
    if len(G) == 1:
        return [arbitrary_element(G)]
    v = arbitrary_element(G)
    hampath = hamiltonian_path(G.subgraph(set(G) - {v}))
    # Get the index of the first node in the path that does *not* have
    # an edge to `v`, then insert `v` before that node.
    index = index_satisfying(hampath, lambda u: v not in G[u])
    hampath.insert(index, v)
    return hampath
예제 #3
0
def test_edge_cutset_random_graphs():
    for flow_func in flow_funcs:
        for i in range(3):
            G = nx.fast_gnp_random_graph(50, 0.25)
            if not nx.is_connected(G):
                ccs = iter(nx.connected_components(G))
                start = arbitrary_element(next(ccs))
                G.add_edges_from((start, arbitrary_element(c)) for c in ccs)
            cutset = nx.minimum_edge_cut(G, flow_func=flow_func)
            assert_equal(nx.edge_connectivity(G), len(cutset), msg=msg.format(flow_func.__name__))
            G.remove_edges_from(cutset)
            assert_false(nx.is_connected(G), msg=msg.format(flow_func.__name__))
예제 #4
0
def test_edge_cutset_random_graphs():
    for flow_func in flow_funcs:
        errmsg = f"Assertion failed in function: {flow_func.__name__}"
        for i in range(3):
            G = nx.fast_gnp_random_graph(50, 0.25, seed=42)
            if not nx.is_connected(G):
                ccs = iter(nx.connected_components(G))
                start = arbitrary_element(next(ccs))
                G.add_edges_from((start, arbitrary_element(c)) for c in ccs)
            cutset = nx.minimum_edge_cut(G, flow_func=flow_func)
            assert nx.edge_connectivity(G) == len(cutset), errmsg
            G.remove_edges_from(cutset)
            assert not nx.is_connected(G), errmsg
예제 #5
0
def test_edge_cutset_random_graphs():
    for flow_func in flow_funcs:
        for i in range(3):
            G = nx.fast_gnp_random_graph(50, 0.25)
            if not nx.is_connected(G):
                ccs = iter(nx.connected_components(G))
                start = arbitrary_element(next(ccs))
                G.add_edges_from((start, arbitrary_element(c)) for c in ccs)
            cutset = nx.minimum_edge_cut(G, flow_func=flow_func)
            assert_equal(nx.edge_connectivity(G), len(cutset),
                         msg=msg.format(flow_func.__name__))
            G.remove_edges_from(cutset)
            assert_false(nx.is_connected(G), msg=msg.format(flow_func.__name__))
예제 #6
0
    def test_quotient_graph_edge_relation(self):
        """Tests for specifying an alternate edge relation for the quotient
        graph.

        """
        G = nx.path_graph(5)
        identity = lambda u, v: u == v
        same_parity = lambda b, c: (arbitrary_element(b) % 2
                                    == arbitrary_element(c) % 2)
        actual = nx.quotient_graph(G, identity, same_parity)
        expected = nx.Graph()
        expected.add_edges_from([(0, 2), (0, 4), (2, 4)])
        expected.add_edge(1, 3)
        assert_true(nx.is_isomorphic(actual, expected))
예제 #7
0
파일: minors.py 프로젝트: 4c656554/networkx
def equivalence_classes(iterable, relation):
    """Returns the set of equivalence classes of the given ``iterable`` under
    the specified equivalence relation.

    ``relation`` must be a Boolean-valued function that takes two argument. It
    must represent an equivalence relation (that is, the relation induced by
    the function must be reflexive, symmetric, and transitive).

    The return value is a set of sets. It is a partition of the elements of
    ``iterable``; duplicate elements will be ignored so it makes the most sense
    for ``iterable`` to be a :class:`set`.

    """
    # For simplicity of implementation, we initialize the return value as a
    # list of lists, then convert it to a set of sets at the end of the
    # function.
    blocks = []
    # Determine the equivalence class for each element of the iterable.
    for y in iterable:
        # Each element y must be in *exactly one* equivalence class.
        #
        # Each block is guaranteed to be non-empty
        for block in blocks:
            x = arbitrary_element(block)
            if relation(x, y):
                block.append(y)
                break
        else:
            # If the element y is not part of any known equivalence class, it
            # must be in its own, so we create a new singleton equivalence
            # class for it.
            blocks.append([y])
    return {frozenset(block) for block in blocks}
예제 #8
0
def strategy_connected_sequential(G, colors, traversal='bfs'):
    """Returns an iterable over nodes in ``G`` in the order given by a
    breadth-first or depth-first traversal.

    ``traversal`` must be one of the strings ``'dfs'`` or ``'bfs'``,
    representing depth-first traversal or breadth-first traversal,
    respectively.

    The generated sequence has the property that for each node except
    the first, at least one neighbor appeared earlier in the sequence.

    ``G`` is a NetworkX graph. ``colors`` is ignored.

    """
    if traversal == 'bfs':
        traverse = nx.bfs_edges
    elif traversal == 'dfs':
        traverse = nx.dfs_edges
    else:
        raise nx.NetworkXError("Please specify one of the strings 'bfs' or"
                               " 'dfs' for connected sequential ordering")
    for component in nx.connected_component_subgraphs(G):
        source = arbitrary_element(component)
        # Yield the source node, then all the nodes in the specified
        # traversal order.
        yield source
        for (_, end) in traverse(component, source):
            yield end
예제 #9
0
def _find_chordality_breaker(G, s=None, treewidth_bound=sys.maxsize):
    """ Given a graph G, starts a max cardinality search
    (starting from s if s is given and from an arbitrary node otherwise)
    trying to find a non-chordal cycle.

    If it does find one, it returns (u,v,w) where u,v,w are the three
    nodes that together with s are involved in the cycle.
    """

    unnumbered = set(G)
    if s is None:
        s = arbitrary_element(G)
    unnumbered.remove(s)
    numbered = set([s])
#    current_treewidth = None
    current_treewidth = -1
    while unnumbered:  # and current_treewidth <= treewidth_bound:
        v = _max_cardinality_node(G, unnumbered, numbered)
        unnumbered.remove(v)
        numbered.add(v)
        clique_wanna_be = set(G[v]) & numbered
        sg = G.subgraph(clique_wanna_be)
        if _is_complete_graph(sg):
            # The graph seems to be chordal by now. We update the treewidth
            current_treewidth = max(current_treewidth, len(clique_wanna_be))
            if current_treewidth > treewidth_bound:
                raise nx.NetworkXTreewidthBoundExceeded(
                    "treewidth_bound exceeded: %s" % current_treewidth)
        else:
            # sg is not a clique,
            # look for an edge that is not included in sg
            (u, w) = _find_missing_edge(sg)
            return (u, v, w)
    return ()
예제 #10
0
파일: isvalid.py 프로젝트: aysunrhn/panaroo
def ramsey_R2(G):
    """Approximately computes the Ramsey number `R(2;s,t)` for graph.

    Parameters
    ----------
    G : NetworkX graph
        Undirected graph

    Returns
    -------
    max_pair : (set, set) tuple
        Maximum clique, Maximum independent set.
    """
    if not G:
        return set(), set()

    node = arbitrary_element(G)
    nbrs = set(nx.all_neighbors(G, node))
    nbrs.discard(node)
    nnbrs = nx.non_neighbors(G, node)
    c_1, i_1 = ramsey_R2(G.subgraph(nbrs).copy())
    c_2, i_2 = ramsey_R2(G.subgraph(nnbrs).copy())

    c_1.add(node)
    i_2.add(node)
    # Choose the larger of the two cliques and the larger of the two
    # independent sets, according to cardinality.
    return max(c_1, c_2, key=len), max(i_1, i_2, key=len)
예제 #11
0
def _connected_chordal_graph_cliques(G):
    """Returns the set of maximal cliques of a connected chordal graph."""
    if G.number_of_nodes() == 1:
        x = frozenset(G.nodes())
        return set([x])
    else:
        cliques = set()
        unnumbered = set(G.nodes())
        v = arbitrary_element(G)
        unnumbered.remove(v)
        numbered = set([v])
        clique_wanna_be = set([v])
        while unnumbered:
            v = _max_cardinality_node(G, unnumbered, numbered)
            unnumbered.remove(v)
            numbered.add(v)
            new_clique_wanna_be = set(G.neighbors(v)) & numbered
            sg = G.subgraph(clique_wanna_be)
            if _is_complete_graph(sg):
                new_clique_wanna_be.add(v)
                if not new_clique_wanna_be >= clique_wanna_be:
                    cliques.add(frozenset(clique_wanna_be))
                clique_wanna_be = new_clique_wanna_be
            else:
                raise nx.NetworkXError("Input graph is not chordal.")
        cliques.add(frozenset(clique_wanna_be))
        return cliques
예제 #12
0
def equivalence_classes(iterable, relation):
    """Returns the set of equivalence classes of the given `iterable` under
    the specified equivalence relation.

    `relation` must be a Boolean-valued function that takes two argument. It
    must represent an equivalence relation (that is, the relation induced by
    the function must be reflexive, symmetric, and transitive).

    The return value is a set of sets. It is a partition of the elements of
    `iterable`; duplicate elements will be ignored so it makes the most sense
    for `iterable` to be a :class:`set`.

    """
    # For simplicity of implementation, we initialize the return value as a
    # list of lists, then convert it to a set of sets at the end of the
    # function.
    blocks = []
    # Determine the equivalence class for each element of the iterable.
    for y in iterable:
        # Each element y must be in *exactly one* equivalence class.
        #
        # Each block is guaranteed to be non-empty
        for block in blocks:
            x = arbitrary_element(block)
            if relation(x, y):
                block.append(y)
                break
        else:
            # If the element y is not part of any known equivalence class, it
            # must be in its own, so we create a new singleton equivalence
            # class for it.
            blocks.append([y])
    return {frozenset(block) for block in blocks}
예제 #13
0
파일: chordal.py 프로젝트: zcf900/networkx
def _find_chordality_breaker(G, s=None, treewidth_bound=sys.maxsize):
    """Given a graph G, starts a max cardinality search
    (starting from s if s is given and from an arbitrary node otherwise)
    trying to find a non-chordal cycle.

    If it does find one, it returns (u,v,w) where u,v,w are the three
    nodes that together with s are involved in the cycle.
    """

    unnumbered = set(G)
    if s is None:
        s = arbitrary_element(G)
    unnumbered.remove(s)
    numbered = {s}
    current_treewidth = -1
    while unnumbered:  # and current_treewidth <= treewidth_bound:
        v = _max_cardinality_node(G, unnumbered, numbered)
        unnumbered.remove(v)
        numbered.add(v)
        clique_wanna_be = set(G[v]) & numbered
        sg = G.subgraph(clique_wanna_be)
        if _is_complete_graph(sg):
            # The graph seems to be chordal by now. We update the treewidth
            current_treewidth = max(current_treewidth, len(clique_wanna_be))
            if current_treewidth > treewidth_bound:
                raise nx.NetworkXTreewidthBoundExceeded(
                    f"treewidth_bound exceeded: {current_treewidth}"
                )
        else:
            # sg is not a clique,
            # look for an edge that is not included in sg
            (u, w) = _find_missing_edge(sg)
            return (u, v, w)
    return ()
def strategy_connected_sequential(G, colors, traversal='bfs'):
    """Returns an iterable over nodes in ``G`` in the order given by a
    breadth-first or depth-first traversal.

    ``traversal`` must be one of the strings ``'dfs'`` or ``'bfs'``,
    representing depth-first traversal or breadth-first traversal,
    respectively.

    The generated sequence has the property that for each node except
    the first, at least one neighbor appeared earlier in the sequence.

    ``G`` is a NetworkX graph. ``colors`` is ignored.

    """
    if traversal == 'bfs':
        traverse = nx.bfs_edges
    elif traversal == 'dfs':
        traverse = nx.dfs_edges
    else:
        raise nx.NetworkXError("Please specify one of the strings 'bfs' or"
                               " 'dfs' for connected sequential ordering")
    for component in nx.connected_component_subgraphs(G):
        source = arbitrary_element(component)
        # Yield the source node, then all the nodes in the specified
        # traversal order.
        yield source
        for (_, end) in traverse(component, source):
            yield end
예제 #15
0
파일: chordal.py 프로젝트: jj314/networkx
def _connected_chordal_graph_cliques(G):
    """Returns the set of maximal cliques of a connected chordal graph."""
    if G.number_of_nodes() == 1:
        x = frozenset(G.nodes())
        return set([x])
    else:
        cliques = set()
        unnumbered = set(G.nodes())
        v = arbitrary_element(G)
        unnumbered.remove(v)
        numbered = set([v])
        clique_wanna_be = set([v])
        while unnumbered:
            v = _max_cardinality_node(G, unnumbered, numbered)
            unnumbered.remove(v)
            numbered.add(v)
            new_clique_wanna_be = set(G.neighbors(v)) & numbered
            sg = G.subgraph(clique_wanna_be)
            if _is_complete_graph(sg):
                new_clique_wanna_be.add(v)
                if not new_clique_wanna_be >= clique_wanna_be:
                    cliques.add(frozenset(clique_wanna_be))
                clique_wanna_be = new_clique_wanna_be
            else:
                raise nx.NetworkXError("Input graph is not chordal.")
        cliques.add(frozenset(clique_wanna_be))
        return cliques
예제 #16
0
def hamiltonian_path(G, source):
    source = arbitrary_element(G)
    neighbors = set(G[source]) - set([source])
    n = len(G)
    for target in neighbors:
        for path in nx.all_simple_paths(G, source, target):
            if len(path) == n:
                yield path
예제 #17
0
def hamiltonian_path(G, source):
    source = arbitrary_element(G)
    neighbors = set(G[source]) - set([source])
    n = len(G)
    for target in neighbors:
        for path in nx.all_simple_paths(G, source, target):
            if len(path) == n:
                yield path
예제 #18
0
파일: dag.py 프로젝트: 4c656554/networkx
def is_aperiodic(G):
    """Return True if G is aperiodic.

    A directed graph is aperiodic if there is no integer k > 1 that
    divides the length of every cycle in the graph.

    Parameters
    ----------
    G : NetworkX DiGraph
        Graph

    Returns
    -------
    aperiodic : boolean
        True if the graph is aperiodic False otherwise

    Raises
    ------
    NetworkXError
        If G is not directed

    Notes
    -----
    This uses the method outlined in [1]_, which runs in O(m) time
    given m edges in G. Note that a graph is not aperiodic if it is
    acyclic as every integer trivial divides length 0 cycles.

    References
    ----------
    .. [1] Jarvis, J. P.; Shier, D. R. (1996),
       Graph-theoretic analysis of finite Markov chains,
       in Shier, D. R.; Wallenius, K. T., Applied Mathematical Modeling:
       A Multidisciplinary Approach, CRC Press.
    """
    if not G.is_directed():
        raise nx.NetworkXError(
            "is_aperiodic not defined for undirected graphs")

    s = arbitrary_element(G)
    levels = {s: 0}
    this_level = [s]
    g = 0
    l = 1
    while this_level:
        next_level = []
        for u in this_level:
            for v in G[u]:
                if v in levels:  # Non-Tree Edge
                    g = gcd(g, levels[u] - levels[v] + 1)
                else:  # Tree Edge
                    next_level.append(v)
                    levels[v] = l
        this_level = next_level
        l += 1
    if len(levels) == len(G):  # All nodes in tree
        return g == 1
    else:
        return g == 1 and nx.is_aperiodic(G.subgraph(set(G) - set(levels)))
예제 #19
0
def is_aperiodic(G):
    """Returns True if `G` is aperiodic.

    A directed graph is aperiodic if there is no integer k > 1 that
    divides the length of every cycle in the graph.

    Parameters
    ----------
    G : NetworkX DiGraph
        A directed graph

    Returns
    -------
    bool
        True if the graph is aperiodic False otherwise

    Raises
    ------
    NetworkXError
        If `G` is not directed

    Notes
    -----
    This uses the method outlined in [1]_, which runs in $O(m)$ time
    given $m$ edges in `G`. Note that a graph is not aperiodic if it is
    acyclic as every integer trivial divides length 0 cycles.

    References
    ----------
    .. [1] Jarvis, J. P.; Shier, D. R. (1996),
       "Graph-theoretic analysis of finite Markov chains,"
       in Shier, D. R.; Wallenius, K. T., Applied Mathematical Modeling:
       A Multidisciplinary Approach, CRC Press.
    """
    if not G.is_directed():
        raise nx.NetworkXError(
            "is_aperiodic not defined for undirected graphs")

    s = arbitrary_element(G)
    levels = {s: 0}
    this_level = [s]
    g = 0
    lev = 1
    while this_level:
        next_level = []
        for u in this_level:
            for v in G[u]:
                if v in levels:  # Non-Tree Edge
                    g = gcd(g, levels[u] - levels[v] + 1)
                else:  # Tree Edge
                    next_level.append(v)
                    levels[v] = lev
        this_level = next_level
        lev += 1
    if len(levels) == len(G):  # All nodes in tree
        return g == 1
    else:
        return g == 1 and nx.is_aperiodic(G.subgraph(set(G) - set(levels)))
예제 #20
0
def dominating_set(G, start_with=None):
    r"""Finds a dominating set for the graph G.

    A *dominating set* for a graph with node set *V* is a subset *D* of
    *V* such that every node not in *D* is adjacent to at least one
    member of *D* [1]_.

    Parameters
    ----------
    G : NetworkX graph

    start_with : node (default=None)
        Node to use as a starting point for the algorithm.

    Returns
    -------
    D : set
        A dominating set for G.

    Notes
    -----
    This function is an implementation of algorithm 7 in [2]_ which
    finds some dominating set, not necessarily the smallest one.

    See also
    --------
    is_dominating_set

    References
    ----------
    .. [1] http://en.wikipedia.org/wiki/Dominating_set

    .. [2] Abdol-Hossein Esfahanian. Connectivity Algorithms.
        http://www.cse.msu.edu/~cse835/Papers/Graph_connectivity_revised.pdf

    """
    all_nodes = set(G)
    if start_with is None:
        start_with = arbitrary_element(all_nodes)
    if start_with not in G:
        raise nx.NetworkXError('node {} is not in G'.format(start_with))
    dominating_set = {start_with}
    dominated_nodes = set(G[start_with])
    remaining_nodes = all_nodes - dominated_nodes - dominating_set
    while remaining_nodes:
        # Choose an arbitrary node and determine its undominated neighbors.
        v = remaining_nodes.pop()
        undominated_neighbors = set(G[v]) - dominating_set
        # Add the node to the dominating set and the neighbors to the
        # dominated set. Finally, remove all of those nodes from the set
        # of remaining nodes.
        dominating_set.add(v)
        dominated_nodes |= undominated_neighbors
        remaining_nodes -= undominated_neighbors
    return dominating_set
예제 #21
0
    def test_basic(self):
        # This example is from the Wikipedia article "Trie"
        # <https://en.wikipedia.org/wiki/Trie>.
        strings = ['a', 'to', 'tea', 'ted', 'ten', 'i', 'in', 'inn']
        T, root = nx.prefix_tree(strings)

        def source_label(v):
            return T.node[v]['source']

        # First, we check that the tree has the expected
        # structure. Recall that each node that corresponds to one of
        # the input strings has an edge to the NIL node.
        #
        # Consider the three children at level 1 in the trie.
        a, i, t = sorted(T[root], key=source_label)
        # Check the 'a' branch.
        assert_equal(len(T[a]), 1)
        nil = arbitrary_element(T[a])
        assert_equal(len(T[nil]), 0)
        # Check the 'i' branch.
        assert_equal(len(T[i]), 2)
        nil, in_ = sorted(T[i], key=source_label)
        assert_equal(len(T[nil]), 0)
        assert_equal(len(T[in_]), 2)
        nil, inn = sorted(T[in_], key=source_label)
        assert_equal(len(T[nil]), 0)
        assert_equal(len(T[inn]), 1)
        nil = arbitrary_element(T[inn])
        assert_equal(len(T[nil]), 0)
        # Check the 't' branch.
        te, to = sorted(T[t], key=source_label)
        assert_equal(len(T[to]), 1)
        nil = arbitrary_element(T[to])
        assert_equal(len(T[nil]), 0)
        tea, ted, ten = sorted(T[te], key=source_label)
        assert_equal(len(T[tea]), 1)
        assert_equal(len(T[ted]), 1)
        assert_equal(len(T[ten]), 1)
        nil = arbitrary_element(T[tea])
        assert_equal(len(T[nil]), 0)
        nil = arbitrary_element(T[ted])
        assert_equal(len(T[nil]), 0)
        nil = arbitrary_element(T[ten])
        assert_equal(len(T[nil]), 0)

        # Next, we check that the "sources" of each of the nodes is the
        # rightmost letter in the string corresponding to the path to
        # that node.
        assert_equal(source_label(root), None)
        assert_equal(source_label(a), 'a')
        assert_equal(source_label(i), 'i')
        assert_equal(source_label(t), 't')
        assert_equal(source_label(in_), 'n')
        assert_equal(source_label(inn), 'n')
        assert_equal(source_label(to), 'o')
        assert_equal(source_label(te), 'e')
        assert_equal(source_label(tea), 'a')
        assert_equal(source_label(ted), 'd')
        assert_equal(source_label(ten), 'n')
        assert_equal(source_label(NIL), NIL)
예제 #22
0
    def test_basic(self):
        # This example is from the Wikipedia article "Trie"
        # <https://en.wikipedia.org/wiki/Trie>.
        strings = ["a", "to", "tea", "ted", "ten", "i", "in", "inn"]
        T, root = nx.prefix_tree(strings)

        def source_label(v):
            return T.nodes[v]["source"]

        # First, we check that the tree has the expected
        # structure. Recall that each node that corresponds to one of
        # the input strings has an edge to the NIL node.
        #
        # Consider the three children at level 1 in the trie.
        a, i, t = sorted(T[root], key=source_label)
        # Check the 'a' branch.
        assert len(T[a]) == 1
        nil = arbitrary_element(T[a])
        assert len(T[nil]) == 0
        # Check the 'i' branch.
        assert len(T[i]) == 2
        nil, in_ = sorted(T[i], key=source_label)
        assert len(T[nil]) == 0
        assert len(T[in_]) == 2
        nil, inn = sorted(T[in_], key=source_label)
        assert len(T[nil]) == 0
        assert len(T[inn]) == 1
        nil = arbitrary_element(T[inn])
        assert len(T[nil]) == 0
        # Check the 't' branch.
        te, to = sorted(T[t], key=source_label)
        assert len(T[to]) == 1
        nil = arbitrary_element(T[to])
        assert len(T[nil]) == 0
        tea, ted, ten = sorted(T[te], key=source_label)
        assert len(T[tea]) == 1
        assert len(T[ted]) == 1
        assert len(T[ten]) == 1
        nil = arbitrary_element(T[tea])
        assert len(T[nil]) == 0
        nil = arbitrary_element(T[ted])
        assert len(T[nil]) == 0
        nil = arbitrary_element(T[ten])
        assert len(T[nil]) == 0

        # Next, we check that the "sources" of each of the nodes is the
        # rightmost letter in the string corresponding to the path to
        # that node.
        assert source_label(root) is None
        assert source_label(a) == "a"
        assert source_label(i) == "i"
        assert source_label(t) == "t"
        assert source_label(in_) == "n"
        assert source_label(inn) == "n"
        assert source_label(to) == "o"
        assert source_label(te) == "e"
        assert source_label(tea) == "a"
        assert source_label(ted) == "d"
        assert source_label(ten) == "n"
        assert source_label(NIL) == NIL
예제 #23
0
    def test_basic(self):
        # This example is from the Wikipedia article "Trie"
        # <https://en.wikipedia.org/wiki/Trie>.
        strings = ['a', 'to', 'tea', 'ted', 'ten', 'i', 'in', 'inn']
        T, root = nx.prefix_tree(strings)

        def source_label(v): return T.node[v]['source']

        # First, we check that the tree has the expected
        # structure. Recall that each node that corresponds to one of
        # the input strings has an edge to the NIL node.
        #
        # Consider the three children at level 1 in the trie.
        a, i, t = sorted(T[root], key=source_label)
        # Check the 'a' branch.
        assert_equal(len(T[a]), 1)
        nil = arbitrary_element(T[a])
        assert_equal(len(T[nil]), 0)
        # Check the 'i' branch.
        assert_equal(len(T[i]), 2)
        nil, in_ = sorted(T[i], key=source_label)
        assert_equal(len(T[nil]), 0)
        assert_equal(len(T[in_]), 2)
        nil, inn = sorted(T[in_], key=source_label)
        assert_equal(len(T[nil]), 0)
        assert_equal(len(T[inn]), 1)
        nil = arbitrary_element(T[inn])
        assert_equal(len(T[nil]), 0)
        # Check the 't' branch.
        te, to = sorted(T[t], key=source_label)
        assert_equal(len(T[to]), 1)
        nil = arbitrary_element(T[to])
        assert_equal(len(T[nil]), 0)
        tea, ted, ten = sorted(T[te], key=source_label)
        assert_equal(len(T[tea]), 1)
        assert_equal(len(T[ted]), 1)
        assert_equal(len(T[ten]), 1)
        nil = arbitrary_element(T[tea])
        assert_equal(len(T[nil]), 0)
        nil = arbitrary_element(T[ted])
        assert_equal(len(T[nil]), 0)
        nil = arbitrary_element(T[ten])
        assert_equal(len(T[nil]), 0)

        # Next, we check that the "sources" of each of the nodes is the
        # rightmost letter in the string corresponding to the path to
        # that node.
        assert_equal(source_label(root), None)
        assert_equal(source_label(a), 'a')
        assert_equal(source_label(i), 'i')
        assert_equal(source_label(t), 't')
        assert_equal(source_label(in_), 'n')
        assert_equal(source_label(inn), 'n')
        assert_equal(source_label(to), 'o')
        assert_equal(source_label(te), 'e')
        assert_equal(source_label(tea), 'a')
        assert_equal(source_label(ted), 'd')
        assert_equal(source_label(ten), 'n')
        assert_equal(source_label(NIL), NIL)
예제 #24
0
def pseudo_peripheral_node(G):
    # helper for cuthill-mckee to find a node in a "pseudo peripheral pair"
    # to use as good starting node
    u = arbitrary_element(G)
    lp = 0
    v = u
    while True:
        spl = dict(nx.shortest_path_length(G, v))
        l = max(spl.values())
        if l <= lp:
            break
        lp = l
        farthest = (n for n, dist in spl.items() if dist == l)
        v, deg = min(G.degree(farthest), key=itemgetter(1))
    return v
예제 #25
0
 def _recursive_build(H, A, source, avail):
     # Terminate once the flow has been compute to every node.
     if {source} == avail:
         return
     # pick an arbitrary node as the sink
     sink = arbitrary_element(avail - {source})
     # find the minimum cut and its weight
     value, (S, T) = nx.minimum_cut(H, source, sink)
     if H.is_directed():
         # check if the reverse direction has a smaller cut
         value_, (T_, S_) = nx.minimum_cut(H, sink, source)
         if value_ < value:
             value, S, T = value_, S_, T_
     # add edge with weight of cut to the aux graph
     A.add_edge(source, sink, weight=value)
     # recursively call until all but one node is used
     _recursive_build(H, A, source, avail.intersection(S))
     _recursive_build(H, A, sink, avail.intersection(T))
예제 #26
0
    def eulerian_circuit(self, start=0):
        G = copy.deepcopy(self.graph)
        degree = G.degree
        edges = G.edges

        vertex_stack = [start]
        last_vertex = None
        while vertex_stack:
            current_vertex = vertex_stack[-1]
            if degree(current_vertex) == 0:
                if last_vertex is not None:
                    print('(', last_vertex, '-', current_vertex, ')')
                last_vertex = current_vertex
                vertex_stack.pop()
            else:
                from networkx.utils import arbitrary_element
                next_vertex = arbitrary_element(edges(current_vertex))[-1]
                vertex_stack.append(next_vertex)
                G.remove_edge(current_vertex, next_vertex)
예제 #27
0
def _simplegraph_eulerian_circuit(G, source):
    if G.is_directed():
        degree = G.out_degree
        edges = G.out_edges
    else:
        degree = G.degree
        edges = G.edges
    vertex_stack = [source]
    last_vertex = None
    while vertex_stack:
        current_vertex = vertex_stack[-1]
        if degree(current_vertex) == 0:
            if last_vertex is not None:
                yield (last_vertex, current_vertex)
            last_vertex = current_vertex
            vertex_stack.pop()
        else:
            _, next_vertex = arbitrary_element(edges(current_vertex))
            vertex_stack.append(next_vertex)
            G.remove_edge(current_vertex, next_vertex)
예제 #28
0
def findKCenters(G, k, shortestPaths):
    allNodes = set(G)
    kCenters = set()
    start_with = arbitrary_element(allNodes)
    kCenters.add(start_with)
    nodeDict = {}
    for x in allNodes:
        nodeDict[x] = float("inf")
    k = k - 1
    nodeDict[start_with] = 0
    while k != 0:
        for node in allNodes - kCenters:
            for center in kCenters:
                if nodeDict[node] > shortestPaths[node][center]:
                    nodeDict[node] = shortestPaths[node][center]
        newCenter = max(nodeDict, key=lambda i: nodeDict[i])
        nodeDict[newCenter] = 0
        kCenters.add(newCenter)
        k -= 1
    return kCenters, max(nodeDict.values())
예제 #29
0
def is_connected(G):
    """Return True if the graph is connected, False otherwise.

    Parameters
    ----------
    G : NetworkX Graph
       An undirected graph.

    Returns
    -------
    connected : bool
      True if the graph is connected, false otherwise.

    Raises
    ------
    NetworkXNotImplemented:
        If G is directed.

    Examples
    --------
    >>> G = nx.path_graph(4)
    >>> print(nx.is_connected(G))
    True

    See Also
    --------
    is_strongly_connected
    is_weakly_connected
    is_semiconnected
    is_biconnected
    connected_components

    Notes
    -----
    For undirected graphs only.

    """
    if len(G) == 0:
        raise nx.NetworkXPointlessConcept('Connectivity is undefined ',
                                          'for the null graph.')
    return sum(1 for node in _plain_bfs(G, arbitrary_element(G))) == len(G)
예제 #30
0
 def _to_string(formula, root):
     # If there are no children, this is a variable node.
     label = formula.nodes[root]['label']
     if not formula[root]:
         return label
     # Otherwise, this is an operator.
     children = formula[root]
     # If one child, the label must be a NOT operator.
     if len(children) == 1:
         child = arbitrary_element(children)
         return '{}({})'.format(label, _to_string(formula, child))
     # NB "left" and "right" here are a little misleading: there is
     # no order on the children of a node. That's okay because the
     # Boolean AND and OR operators are symmetric. It just means that
     # the order of the operands cannot be predicted and hence the
     # function does not necessarily behave the same way on every
     # invocation.
     left, right = formula[root]
     left_subformula = _to_string(formula, left)
     right_subformula = _to_string(formula, right)
     return '({} {} {})'.format(left_subformula, label, right_subformula)
예제 #31
0
 def _to_string(formula, root):
     # If there are no children, this is a variable node.
     label = formula.node[root]['label']
     if not formula[root]:
         return label
     # Otherwise, this is an operator.
     children = formula[root]
     # If one child, the label must be a NOT operator.
     if len(children) == 1:
         child = arbitrary_element(children)
         return '{}({})'.format(label, _to_string(formula, child))
     # NB "left" and "right" here are a little misleading: there is
     # no order on the children of a node. That's okay because the
     # Boolean AND and OR operators are symmetric. It just means that
     # the order of the operands cannot be predicted and hence the
     # function does not necessarily behave the same way on every
     # invocation.
     left, right = formula[root]
     left_subformula = _to_string(formula, left)
     right_subformula = _to_string(formula, right)
     return '({} {} {})'.format(left_subformula, label, right_subformula)
예제 #32
0
def _multigraph_eulerian_circuit(G, source):
    if G.is_directed():
        degree = G.out_degree
        edges = G.out_edges
    else:
        degree = G.degree
        edges = G.edges
    vertex_stack = [(source, None)]
    last_vertex = None
    last_key = None
    while vertex_stack:
        current_vertex, current_key = vertex_stack[-1]
        if degree(current_vertex) == 0:
            if last_vertex is not None:
                yield (last_vertex, current_vertex, last_key)
            last_vertex, last_key = current_vertex, current_key
            vertex_stack.pop()
        else:
            _, next_vertex, next_key = arbitrary_element(
                edges(current_vertex, keys=True))
            vertex_stack.append((next_vertex, next_key))
            G.remove_edge(current_vertex, next_vertex, next_key)
예제 #33
0
파일: lca.py 프로젝트: EQt/graphidx
def lca_networkx(G, root, pairs):
    """
    [`networkx.algorithms.lowest_common_ancestor`][nx]
    Implemented according to CLRS page 584 (3rd edition).
    Compare to [epp]

    [nx]: https://github.com/networkx/networkx/master/
    [epp]: https://www.ics.uci.edu/~eppstein/PADS/LCA.py

    """
    from collections import defaultdict
    from networkx.utils import UnionFind, arbitrary_element
    from networkx import dfs_postorder_nodes

    pair_dict = defaultdict(set)
    for u, v in pairs:
        pair_dict[u].add(v)
        pair_dict[v].add(u)

    # Iterative implementation of Tarjan's offline lca algorithm
    # as described in CLRS on page 521.
    uf = UnionFind()
    ancestors = {}
    for node in G:
        ancestors[node] = uf[node]

    colors = defaultdict(bool)
    for node in dfs_postorder_nodes(G, root):
        colors[node] = True
        for v in pair_dict[node]:
            if colors[v]:
                if (v, node) in pairs:
                    yield (v, node), ancestors[uf[v]]
        if node != root:
            parent = arbitrary_element(G.pred[node])
            uf.union(parent, node)
            ancestors[uf[parent]] = parent
예제 #34
0
def tree_all_pairs_lowest_common_ancestor(G, root=None, pairs=None):
    r"""Yield the lowest common ancestor for sets of pairs in a tree.

    Parameters
    ----------
    G : NetworkX directed graph (must be a tree)

    root : node, optional (default: None)
        The root of the subtree to operate on.
        If None, assume the entire graph has exactly one source and use that.

    pairs : iterable or iterator of pairs of nodes, optional (default: None)
        The pairs of interest. If None, Defaults to all pairs of nodes
        under `root` that have a lowest common ancestor.

    Returns
    -------
    lcas : generator of tuples `((u, v), lca)` where `u` and `v` are nodes
        in `pairs` and `lca` is their lowest common ancestor.

    Notes
    -----
    Only defined on non-null trees represented with directed edges from
    parents to children. Uses Tarjan's off-line lowest-common-ancestors
    algorithm. Runs in time $O(4 \times (V + E + P))$ time, where 4 is the largest
    value of the inverse Ackermann function likely to ever come up in actual
    use, and $P$ is the number of pairs requested (or $V^2$ if all are needed).

    Tarjan, R. E. (1979), "Applications of path compression on balanced trees",
    Journal of the ACM 26 (4): 690-715, doi:10.1145/322154.322161.

    See Also
    --------
    all_pairs_lowest_common_ancestor (similar routine for general DAGs)
    lowest_common_ancestor           (just a single pair for general DAGs)
    """
    if len(G) == 0:
        raise nx.NetworkXPointlessConcept("LCA meaningless on null graphs.")
    elif None in G:
        raise nx.NetworkXError("None is not a valid node.")

    # Index pairs of interest for efficient lookup from either side.
    if pairs is not None:
        pair_dict = defaultdict(set)
        # See note on all_pairs_lowest_common_ancestor.
        if not isinstance(pairs, (Mapping, Set)):
            pairs = set(pairs)
        for u, v in pairs:
            for n in (u, v):
                if n not in G:
                    msg = "The node %s is not in the digraph." % str(n)
                    raise nx.NodeNotFound(msg)
            pair_dict[u].add(v)
            pair_dict[v].add(u)

    # If root is not specified, find the exactly one node with in degree 0 and
    # use it. Raise an error if none are found, or more than one is. Also check
    # for any nodes with in degree larger than 1, which would imply G is not a
    # tree.
    if root is None:
        for n, deg in G.in_degree:
            if deg == 0:
                if root is not None:
                    msg = "No root specified and tree has multiple sources."
                    raise nx.NetworkXError(msg)
                root = n
            elif deg > 1:
                msg = "Tree LCA only defined on trees; use DAG routine."
                raise nx.NetworkXError(msg)
    if root is None:
        raise nx.NetworkXError("Graph contains a cycle.")

    # Iterative implementation of Tarjan's offline lca algorithm
    # as described in CLRS on page 521 (2nd edition)/page 584 (3rd edition)
    uf = UnionFind()
    ancestors = {}
    for node in G:
        ancestors[node] = uf[node]

    colors = defaultdict(bool)
    for node in nx.dfs_postorder_nodes(G, root):
        colors[node] = True
        for v in (pair_dict[node] if pairs is not None else G):
            if colors[v]:
                # If the user requested both directions of a pair, give it.
                # Otherwise, just give one.
                if pairs is not None and (node, v) in pairs:
                    yield (node, v), ancestors[uf[v]]
                if pairs is None or (v, node) in pairs:
                    yield (v, node), ancestors[uf[v]]
        if node != root:
            parent = arbitrary_element(G.pred[node])
            uf.union(parent, node)
            ancestors[uf[parent]] = parent
예제 #35
0
파일: chordal.py 프로젝트: zcf900/networkx
def _chordal_graph_cliques(G):
    """Returns all maximal cliques of a chordal graph.

    The algorithm breaks the graph in connected components and performs a
    maximum cardinality search in each component to get the cliques.

    Parameters
    ----------
    G : graph
      A NetworkX graph

    Returns
    -------
    iterator
        An iterator over maximal cliques, each of which is a frozenset of
        nodes in `G`. The order of cliques is arbitrary.

    Raises
    ------
    NetworkXError
        The algorithm does not support DiGraph, MultiGraph and MultiDiGraph.
        If the input graph is an instance of one of these classes, a
        :exc:`NetworkXError` is raised.
        The algorithm can only be applied to chordal graphs. If the input
        graph is found to be non-chordal, a :exc:`NetworkXError` is raised.

    Examples
    --------
    >>> e = [
    ...     (1, 2),
    ...     (1, 3),
    ...     (2, 3),
    ...     (2, 4),
    ...     (3, 4),
    ...     (3, 5),
    ...     (3, 6),
    ...     (4, 5),
    ...     (4, 6),
    ...     (5, 6),
    ...     (7, 8),
    ... ]
    >>> G = nx.Graph(e)
    >>> G.add_node(9)
    >>> cliques = [c for c in _chordal_graph_cliques(G)]
    >>> cliques[0]
    frozenset({1, 2, 3})
    """
    if not is_chordal(G):
        raise nx.NetworkXError("Input graph is not chordal.")

    for C in (G.subgraph(c).copy() for c in connected_components(G)):
        if C.number_of_nodes() == 1:
            yield frozenset(C.nodes())
        else:
            unnumbered = set(C.nodes())
            v = arbitrary_element(C)
            unnumbered.remove(v)
            numbered = {v}
            clique_wanna_be = {v}
            while unnumbered:
                v = _max_cardinality_node(C, unnumbered, numbered)
                unnumbered.remove(v)
                numbered.add(v)
                new_clique_wanna_be = set(C.neighbors(v)) & numbered
                sg = C.subgraph(clique_wanna_be)
                if _is_complete_graph(sg):
                    new_clique_wanna_be.add(v)
                    if not new_clique_wanna_be >= clique_wanna_be:
                        yield frozenset(clique_wanna_be)
                    clique_wanna_be = new_clique_wanna_be
                else:
                    raise nx.NetworkXError("Input graph is not chordal.")
            yield frozenset(clique_wanna_be)
예제 #36
0
 def same_parity(b, c):
     return arbitrary_element(b) % 2 == arbitrary_element(c) % 2
예제 #37
0
    def construct(EdgeComponentAuxGraph, G):
        """Builds an auxiliary graph encoding edge-connectivity between nodes.

        Notes
        -----
        Given G=(V, E), initialize an empty auxiliary graph A.
        Choose an arbitrary source node s.  Initialize a set N of available
        nodes (that can be used as the sink). The algorithm picks an
        arbitrary node t from N - {s}, and then computes the minimum st-cut
        (S, T) with value w. If G is directed the the minimum of the st-cut or
        the ts-cut is used instead. Then, the edge (s, t) is added to the
        auxiliary graph with weight w. The algorithm is called recursively
        first using S as the available nodes and s as the source, and then
        using T and t. Recursion stops when the source is the only available
        node.

        Parameters
        ----------
        G : NetworkX graph
        """
        # workaround for classmethod decorator
        not_implemented_for('multigraph')(lambda G: G)(G)

        def _recursive_build(H, A, source, avail):
            # Terminate once the flow has been compute to every node.
            if {source} == avail:
                return
            # pick an arbitrary node as the sink
            sink = arbitrary_element(avail - {source})
            # find the minimum cut and its weight
            value, (S, T) = nx.minimum_cut(H, source, sink)
            if H.is_directed():
                # check if the reverse direction has a smaller cut
                value_, (T_, S_) = nx.minimum_cut(H, sink, source)
                if value_ < value:
                    value, S, T = value_, S_, T_
            # add edge with weight of cut to the aux graph
            A.add_edge(source, sink, weight=value)
            # recursively call until all but one node is used
            _recursive_build(H, A, source, avail.intersection(S))
            _recursive_build(H, A, sink, avail.intersection(T))

        # Copy input to ensure all edges have unit capacity
        H = G.__class__()
        H.add_nodes_from(G.nodes())
        H.add_edges_from(G.edges(), capacity=1)

        # A is the auxiliary graph to be constructed
        # It is a weighted undirected tree
        A = nx.Graph()

        # Pick an arbitrary node as the source
        if H.number_of_nodes() > 0:
            source = arbitrary_element(H.nodes())
            # Initialize a set of elements that can be chosen as the sink
            avail = set(H.nodes())

            # This constructs A
            _recursive_build(H, A, source, avail)

        # This class is a container the holds the auxiliary graph A and
        # provides access the the k_edge_components function.
        self = EdgeComponentAuxGraph()
        self.A = A
        self.H = H
        return self
예제 #38
0
def eulerian_circuit(G, source=None, keys=False):
    """Returns an iterator over the edges of an Eulerian circuit in `G`.

    An *Eulerian circuit* is a closed walk that includes each edge of a
    graph exactly once.

    Parameters
    ----------
    G : NetworkX graph
       A graph, either directed or undirected.

    source : node, optional
       Starting node for circuit.

    keys : bool
       If False, edges generated by this function will be of the form
       ``(u, v)``. Otherwise, edges will be of the form ``(u, v, k)``.
       This option is ignored unless `G` is a multigraph.

    Returns
    -------
    edges : iterator
       An iterator over edges in the Eulerian circuit.

    Raises
    ------
    NetworkXError
       If the graph is not Eulerian.

    See Also
    --------
    is_eulerian

    Notes
    -----
    This is a linear time implementation of an algorithm adapted from [1]_.

    For general information about Euler tours, see [2]_.

    References
    ----------
    .. [1] J. Edmonds, E. L. Johnson.
       Matching, Euler tours and the Chinese postman.
       Mathematical programming, Volume 5, Issue 1 (1973), 111-114.
    .. [2] https://en.wikipedia.org/wiki/Eulerian_path

    Examples
    --------
    To get an Eulerian circuit in an undirected graph::

        >>> G = nx.complete_graph(3)
        >>> list(nx.eulerian_circuit(G))
        [(0, 2), (2, 1), (1, 0)]
        >>> list(nx.eulerian_circuit(G, source=1))
        [(1, 2), (2, 0), (0, 1)]

    To get the sequence of vertices in an Eulerian circuit::

        >>> [u for u, v in nx.eulerian_circuit(G)]
        [0, 2, 1]

    """
    if not is_eulerian(G):
        raise nx.NetworkXError("G is not Eulerian.")
    if G.is_directed():
        G = G.reverse()
    else:
        G = G.copy()
    if source is None:
        source = arbitrary_element(G)
    if G.is_multigraph():
        for u, v, k in _multigraph_eulerian_circuit(G, source):
            if keys:
                yield u, v, k
            else:
                yield u, v
    else:
        for u, v in _simplegraph_eulerian_circuit(G, source):
            yield u, v
예제 #39
0
 def same_parity(b, c):
     return (arbitrary_element(b) % 2 == arbitrary_element(c) % 2)
def tree_all_pairs_lowest_common_ancestor(G, root=None, pairs=None):
    r"""Yield the lowest common ancestor for sets of pairs in a tree.

    Parameters
    ----------
    G : NetworkX directed graph (must be a tree)

    root : node, optional (default: None)
        The root of the subtree to operate on.
        If None, assume the entire graph has exactly one source and use that.

    pairs : iterable or iterator of pairs of nodes, optional (default: None)
        The pairs of interest. If None, Defaults to all pairs of nodes
        under `root` that have a lowest common ancestor.

    Returns
    -------
    lcas : generator of tuples `((u, v), lca)` where `u` and `v` are nodes
        in `pairs` and `lca` is their lowest common ancestor.

    Notes
    -----
    Only defined on non-null trees represented with directed edges from
    parents to children. Uses Tarjan's off-line lowest-common-ancestors
    algorithm. Runs in time $O(4 \times (V + E + P))$ time, where 4 is the largest
    value of the inverse Ackermann function likely to ever come up in actual
    use, and $P$ is the number of pairs requested (or $V^2$ if all are needed).

    Tarjan, R. E. (1979), "Applications of path compression on balanced trees",
    Journal of the ACM 26 (4): 690-715, doi:10.1145/322154.322161.

    See Also
    --------
    all_pairs_lowest_common_ancestor (similar routine for general DAGs)
    lowest_common_ancestor           (just a single pair for general DAGs)
    """
    if len(G) == 0:
        raise nx.NetworkXPointlessConcept("LCA meaningless on null graphs.")
    elif None in G:
        raise nx.NetworkXError("None is not a valid node.")

    # Index pairs of interest for efficient lookup from either side.
    if pairs is not None:
        pair_dict = defaultdict(set)
        # See note on all_pairs_lowest_common_ancestor.
        if not isinstance(pairs, (Mapping, Set)):
            pairs = set(pairs)
        for u, v in pairs:
            for n in (u, v):
                if n not in G:
                    msg = f"The node {str(n)} is not in the digraph."
                    raise nx.NodeNotFound(msg)
            pair_dict[u].add(v)
            pair_dict[v].add(u)

    # If root is not specified, find the exactly one node with in degree 0 and
    # use it. Raise an error if none are found, or more than one is. Also check
    # for any nodes with in degree larger than 1, which would imply G is not a
    # tree.
    if root is None:
        for n, deg in G.in_degree:
            if deg == 0:
                if root is not None:
                    msg = "No root specified and tree has multiple sources."
                    raise nx.NetworkXError(msg)
                root = n
            elif deg > 1:
                msg = "Tree LCA only defined on trees; use DAG routine."
                raise nx.NetworkXError(msg)
    if root is None:
        raise nx.NetworkXError("Graph contains a cycle.")

    # Iterative implementation of Tarjan's offline lca algorithm
    # as described in CLRS on page 521 (2nd edition)/page 584 (3rd edition)
    uf = UnionFind()
    ancestors = {}
    for node in G:
        ancestors[node] = uf[node]

    colors = defaultdict(bool)
    for node in nx.dfs_postorder_nodes(G, root):
        colors[node] = True
        for v in (pair_dict[node] if pairs is not None else G):
            if colors[v]:
                # If the user requested both directions of a pair, give it.
                # Otherwise, just give one.
                if pairs is not None and (node, v) in pairs:
                    yield (node, v), ancestors[uf[v]]
                if pairs is None or (v, node) in pairs:
                    yield (v, node), ancestors[uf[v]]
        if node != root:
            parent = arbitrary_element(G.pred[node])
            uf.union(parent, node)
            ancestors[uf[parent]] = parent
예제 #41
0
def min_edge_cover(G, matching_algorithm=None):
    """Returns a set of edges which constitutes
    the minimum edge cover of the graph.

    A smallest edge cover can be found in polynomial time by finding
    a maximum matching and extending it greedily so that all nodes
    are covered.

    Parameters
    ----------
    G : NetworkX graph
        An undirected bipartite graph.

    matching_algorithm : function
        A function that returns a maximum cardinality matching in a
        given bipartite graph. The function must take one input, the
        graph ``G``, and return a dictionary mapping each node to its
        mate. If not specified,
        :func:`~networkx.algorithms.bipartite.matching.hopcroft_karp_matching`
        will be used. Other possibilities include
        :func:`~networkx.algorithms.bipartite.matching.eppstein_matching`,
        or matching algorithms in the
        :mod:`networkx.algorithms.matching` module.

    Returns
    -------
    min_cover : set

        It contains all the edges of minimum edge cover
        in form of tuples. It contains both the edges `(u, v)` and `(v, u)`
        for given nodes `u` and `v` among the edges of minimum edge cover.

    Notes
    -----
    An edge cover of a graph is a set of edges such that every node of
    the graph is incident to at least one edge of the set.
    The minimum edge cover is an edge covering of smallest cardinality.

    Due to its implementation, the worst-case running time of this algorithm
    is bounded by the worst-case running time of the function
    ``matching_algorithm``.

    Minimum edge cover for bipartite graph can also be found using the
    function present in :mod:`networkx.algorithms.bipartite.covering`
    """
    if nx.number_of_isolates(G) > 0:
        # ``min_cover`` does not exist as there is an isolated node
        raise nx.NetworkXException(
            "Graph has a node with no edge incident on it, "
            "so no edge cover exists.")
    if matching_algorithm is None:
        matching_algorithm = partial(nx.max_weight_matching,
                                     maxcardinality=True)
    maximum_matching = matching_algorithm(G)
    # ``min_cover`` is superset of ``maximum_matching``
    min_cover = set(maximum_matching.items())
    # iterate for uncovered nodes
    uncovered_nodes = set(G) - {v for u, v in min_cover}
    for v in uncovered_nodes:
        # Since `v` is uncovered, each edge incident to `v` will join it
        # with a covered node (otherwise, if there were an edge joining
        # uncovered nodes `u` and `v`, the maximum matching algorithm
        # would have found it), so we can choose an arbitrary edge
        # incident to `v`. (This applies only in a simple graph, not a
        # multigraph.)
        u = arbitrary_element(G[v])
        min_cover.add((u, v))
        min_cover.add((v, u))
    return min_cover
예제 #42
0
파일: line.py 프로젝트: jianantian/networkx
def _select_starting_cell(G, starting_edge=None):
    """ Select a cell to initiate _find_partition

    Parameters
    ----------
    G : NetworkX Graph
    starting_edge: an edge to build the starting cell from

    Returns
    -------
    Tuple of vertices in G

    Raises
    ------
    NetworkXError
        If it is determined that G is not a line graph

    Notes
    -----
    If starting edge not specified then pick an arbitrary edge - doesn't
    matter which. However, this function may call itself requiring a
    specific starting edge. Note that the r, s notation for counting
    triangles is the same as in the Roussopoulos paper cited above.
    """
    if starting_edge is None:
        e = arbitrary_element(list(G.edges()))
    else:
        e = starting_edge
        if e[0] not in G[e[1]]:
            msg = 'starting_edge (%s, %s) is not in the Graph'
            raise nx.NetworkXError(msg % e)
    e_triangles = _triangles(G, e)
    r = len(e_triangles)
    if r == 0:
        # there are no triangles containing e, so the starting cell is just e
        starting_cell = e
    elif r == 1:
        # there is exactly one triangle, T, containing e. If other 2 edges
        # of T belong only to this triangle then T is starting cell
        T = e_triangles[0]
        a, b, c = T
        # ab was original edge so check the other 2 edges
        ac_edges = [x for x in _triangles(G, (a, c))]
        bc_edges = [x for x in _triangles(G, (b, c))]
        if len(ac_edges) == 1:
            if len(bc_edges) == 1:
                starting_cell = T
            else:
                return _select_starting_cell(G, starting_edge=(b, c))
        else:
            return _select_starting_cell(G, starting_edge=(a, c))
    else:
        # r >= 2 so we need to count the number of odd triangles, s
        s = 0
        odd_triangles = []
        for T in e_triangles:
            if _odd_triangle(G, T):
                s += 1
                odd_triangles.append(T)
        if r == 2 and s == 0:
            # in this case either triangle works, so just use T
            starting_cell = T
        elif r - 1 <= s <= r:
            # check if odd triangles containing e form complete subgraph
            # there must be exactly s+2 of them
            # and they must all be connected
            triangle_nodes = set([])
            for T in odd_triangles:
                for x in T:
                    triangle_nodes.add(x)
            if len(triangle_nodes) == s + 2:
                for u in triangle_nodes:
                    for v in triangle_nodes:
                        if u != v and (v not in G.neighbors(u)):
                            msg = "G is not a line graph (odd triangles " \
                                  "do not form complete subgraph)"
                            raise nx.NetworkXError(msg)
                # otherwise then we can use this as the starting cell
                starting_cell = tuple(triangle_nodes)
            else:
                msg = "G is not a line graph (odd triangles " \
                      "do not form complete subgraph)"
                raise nx.NetworkXError(msg)
        else:
            msg = "G is not a line graph (incorrect number of " \
                  "odd triangles around starting edge)"
            raise nx.NetworkXError(msg)
    return starting_cell
예제 #43
0
def _select_starting_cell(G, starting_edge=None):
    """ Select a cell to initiate _find_partition
    
    Parameters
    ----------
    G : NetworkX Graph
    starting_edge: an edge to build the starting cell from
    
    Returns
    -------
    Tuple of vertices in G
    
    Raises
    ------
    NetworkXError
        If it is determined that G is not a line graph

    Notes
    -----
    If starting edge not specified then pick an arbitrary edge - doesn't matter which.
    However, this function may call itself requiring a specific starting edge.
    Note that the r, s notation for counting triangles is the same as in the Roussopoulos
    paper cited above.
    """
    if starting_edge == None:
        e = arbitrary_element(list(G.edges()))
    else:
        e = starting_edge
        if e[0] not in G.neighbors(e[1]):
            raise nx.NetworkXError('starting_edge (%s, %s) is not in the Graph' % e) 
    e_triangles = _triangles(G, e)
    r = len(e_triangles)
    if r == 0:
        # there are no triangles containing e, so the starting cell is just e
        starting_cell = e
    elif r == 1:
        # there is exactly one triangle, T, containing e
        # if other 2 edges of T belong only to this triangle then T is starting cell
        T = e_triangles[0]
        a,b,c = T
        # ab was original edge so check the other 2 edges
        ac_edges = [x for x in _triangles(G, (a,c))]
        bc_edges = [x for x in _triangles(G, (b,c))]
        if len(ac_edges) == 1:
            if len(bc_edges) == 1:
                starting_cell = T
            else:
                return _select_starting_cell(G, starting_edge=(b,c))
        else:
            return _select_starting_cell(G, starting_edge=(a,c))
    else:
        # r >= 2 so we need to count the number of odd triangles, s
        s = 0
        odd_triangles = []
        for T in e_triangles:
            if _odd_triangle(G, T):
                s += 1
                odd_triangles.append(T)       
        if r==2 and s==0:
            # in this case it doesn't matter which of our two triangles we choose, so just use T
            starting_cell = T 
        elif r-1 <= s <= r:
            # check if odd triangles containing e form complete subgraph
            # there must be exactly s+2 of them
            # and they must all be connected
            triangle_nodes = set([])
            for T in odd_triangles:
                for x in T:
                    triangle_nodes.add(x)
            if len(triangle_nodes) == s+2:
                for u in triangle_nodes:
                    for v in triangle_nodes:
                        if u!=v and (v not in G.neighbors(u)):
                            raise nx.NetworkXError("G is not a line graph (odd triangles do not form complete subgraph)")
                # otherwise then we can use this as the starting cell
                starting_cell = tuple(triangle_nodes)  
            else:
                raise nx.NetworkXError("G is not a line graph (odd triangles do not form complete subgraph)")
        else:
            raise nx.NetworkXError("G is not a line graph (incorrect number of odd triangles around starting edge)")
    return starting_cell
예제 #44
0
def equivalence_classes(iterable, relation):
    """Returns equivalence classes of `relation` when applied to `iterable`.

    The equivalence classes, or blocks, consist of objects from `iterable`
    which are all equivalent. They are defined to be equivalent if the
    `relation` function returns `True` when passed any two objects from that
    class, and `False` otherwise. To define an equivalence relation the
    function must be reflexive, symmetric and transitive.

    Parameters
    ----------
    iterable : list, tuple, or set
        An iterable of elements/nodes.

    relation : function
        A Boolean-valued function that implements an equivalence relation
        (reflexive, symmetric, transitive binary relation) on the elements
        of `iterable` - it must take two elements and return `True` if
        they are related, or `False` if not.

    Returns
    -------
    set of frozensets
        A set of frozensets representing the partition induced by the equivalence
        relation function `relation` on the elements of `iterable`. Each
        member set in the return set represents an equivalence class, or
        block, of the partition.

        Duplicate elements will be ignored so it makes the most sense for
        `iterable` to be a :class:`set`.

    Examples
    --------
    Let `X` be the set of integers from `0` to `9`, and consider an equivalence
    relation `R` on `X` of congruence modulo `3`: this means that two integers
    `x` and `y` in `X` are equivalent under `R` if they leave the same
    remainder when divided by `3`, i.e. `(x - y) mod 3 = 0`.

    The equivalence classes of this relation are `{0, 3, 6, 9}`, `{1, 4, 7}`,
    `{2, 5, 8}`: `0`, `3`, `6`, `9` are all divisible by `3` and leave zero
    remainder; `1`, `4`, `7` leave remainder `1`; while `2`, `5` and `8` leave
    remainder `2`. We can see this by calling `equivalence_classes` with
    with `X` and a function implementation of `R`.::

        >>> X = set(range(10))
        >>> def mod3(x, y): return (x - y) % 3 == 0
        >>> equivalence_classes(X, mod3)    # doctest: +SKIP
        {frozenset({1, 4, 7}), frozenset({8, 2, 5}), frozenset({0, 9, 3, 6})} # doctest: +SKIP

    """
    # For simplicity of implementation, we initialize the return value as a
    # list of lists, then convert it to a set of sets at the end of the
    # function.
    blocks = []
    # Determine the equivalence class for each element of the iterable.
    for y in iterable:
        # Each element y must be in *exactly one* equivalence class.
        #
        # Each block is guaranteed to be non-empty
        for block in blocks:
            x = arbitrary_element(block)
            if relation(x, y):
                block.append(y)
                break
        else:
            # If the element y is not part of any known equivalence class, it
            # must be in its own, so we create a new singleton equivalence
            # class for it.
            blocks.append([y])
    return {frozenset(block) for block in blocks}