ó žÃÒYc@sàdZddlmZddlmZddlmZddlmZddlZddlmZddl m Z dd l m Z d d d d gZ ejZd„Zdddedd„Zed„ZeZed„ZdS(s3Provides functions for computing minors of a graph.iÿÿÿÿ(tchain(t combinations(t permutations(tproductN(tdensity(tNetworkXException(tarbitrary_elementtcontracted_edgetcontracted_nodestidentified_nodestquotient_graphcCsrg}x[|D]S}xJ|D]2}t|ƒ}|||ƒr|j|ƒPqqW|j|gƒq Wd„|DƒS(sReturns 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`. cSsh|]}t|ƒ’qS((t frozenset(t.0tblock((sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pys <s (Rtappend(titerabletrelationtblockstyR tx((sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pytequivalence_classess    c s¼tˆƒrtˆˆƒ‰nt‡fd†ˆDƒƒrItdƒ‚n|d k rdt|ƒƒn tˆƒƒ}ˆd kr‘‡fd†‰ngˆD]}t|ƒ^q˜‰|j‡fd†ˆDƒƒˆd krë‡fd†‰nˆd kr ‡fd†‰n|jƒr$t |dƒn t |dƒ} |j ƒrat ‡‡fd†| Dƒƒ} n‡‡fd †| Dƒ} |j | ƒ|r¸d „tˆƒDƒ} tj|| ƒ}n|S( s+Returns the quotient graph of `G` under the specified equivalence relation on nodes. Parameters ---------- G : NetworkX graph The graph for which to return the quotient graph with the specified node relation. partition : function or list of sets If a function, this function must represent an equivalence relation on the nodes of `G`. It must take two arguments *u* and *v* and return True exactly when *u* and *v* are in the same equivalence class. The equivalence classes form the nodes in the returned graph. If a list of sets, the list must form a valid partition of the nodes of the graph. That is, each node must be in exactly one block of the partition. edge_relation : Boolean function with two arguments This function must represent an edge relation on the *blocks* of `G` in the partition induced by `node_relation`. It must take two arguments, *B* and *C*, each one a set of nodes, and return True exactly when there should be an edge joining block *B* to block *C* in the returned graph. If `edge_relation` is not specified, it is assumed to be the following relation. Block *B* is related to block *C* if and only if some node in *B* is adjacent to some node in *C*, according to the edge set of `G`. edge_data : function This function takes two arguments, *B* and *C*, each one a set of nodes, and must return a dictionary representing the edge data attributes to set on the edge joining *B* and *C*, should there be an edge joining *B* and *C* in the quotient graph (if no such edge occurs in the quotient graph as determined by `edge_relation`, then the output of this function is ignored). If the quotient graph would be a multigraph, this function is not applied, since the edge data from each edge in the graph `G` appears in the edges of the quotient graph. node_data : function This function takes one argument, *B*, a set of nodes in `G`, and must return a dictionary representing the node data attributes to set on the node representing *B* in the quotient graph. If None, the following node attributes will be set: * 'graph', the subgraph of the graph `G` that this block represents, * 'nnodes', the number of nodes in this block, * 'nedges', the number of edges within this block, * 'density', the density of the subgraph of `G` that this block represents. relabel : bool If True, relabel the nodes of the quotient graph to be nonnegative integers. Otherwise, the nodes are identified with :class:`frozenset` instances representing the blocks given in `partition`. create_using : NetworkX graph If specified, this must be an instance of a NetworkX graph class. The nodes and edges of the quotient graph will be added to this graph and returned. If not specified, the returned graph will have the same type as the input graph. Returns ------- NetworkX graph The quotient graph of `G` under the equivalence relation specified by `partition`. If the partition were given as a list of :class:`set` instances and `relabel` is False, each node will be a :class:`frozenset` corresponding to the same :class:`set`. Raises ------ NetworkXException If the given partition is not a valid partition of the nodes of `G`. Examples -------- The quotient graph of the complete bipartite graph under the "same neighbors" equivalence relation is `K_2`. Under this relation, two nodes are equivalent if they are not adjacent but have the same neighbor set:: >>> import networkx as nx >>> G = nx.complete_bipartite_graph(2, 3) >>> same_neighbors = lambda u, v: (u not in G[v] and v not in G[u] ... and G[u] == G[v]) >>> Q = nx.quotient_graph(G, same_neighbors) >>> K2 = nx.complete_graph(2) >>> nx.is_isomorphic(Q, K2) True The quotient graph of a directed graph under the "same strongly connected component" equivalence relation is the condensation of the graph (see :func:`condensation`). This example comes from the Wikipedia article *`Strongly connected component`_*:: >>> import networkx as nx >>> G = nx.DiGraph() >>> edges = ['ab', 'be', 'bf', 'bc', 'cg', 'cd', 'dc', 'dh', 'ea', ... 'ef', 'fg', 'gf', 'hd', 'hf'] >>> G.add_edges_from(tuple(x) for x in edges) >>> components = list(nx.strongly_connected_components(G)) >>> sorted(sorted(component) for component in components) [['a', 'b', 'e'], ['c', 'd', 'h'], ['f', 'g']] >>> >>> C = nx.condensation(G, components) >>> component_of = C.graph['mapping'] >>> same_component = lambda u, v: component_of[u] == component_of[v] >>> Q = nx.quotient_graph(G, same_component) >>> nx.is_isomorphic(C, Q) True Node identification can be represented as the quotient of a graph under the equivalence relation that places the two nodes in one block and each other node in its own singleton block:: >>> import networkx as nx >>> K24 = nx.complete_bipartite_graph(2, 4) >>> K34 = nx.complete_bipartite_graph(3, 4) >>> C = nx.contracted_nodes(K34, 1, 2) >>> nodes = {1, 2} >>> is_contracted = lambda u, v: u in nodes and v in nodes >>> Q = nx.quotient_graph(K34, is_contracted) >>> nx.is_isomorphic(Q, C) True >>> nx.is_isomorphic(Q, K24) True The blockmodeling technique described in [1]_ can be implemented as a quotient graph:: >>> G = nx.path_graph(6) >>> partition = [{0, 1}, {2, 3}, {4, 5}] >>> M = nx.quotient_graph(G, partition, relabel=True) >>> list(M.edges()) [(0, 1), (1, 2)] .. _Strongly connected component: https://en.wikipedia.org/wiki/Strongly_connected_component References ---------- .. [1] Patrick Doreian, Vladimir Batagelj, and Anuska Ferligoj. *Generalized Blockmodeling*. Cambridge University Press, 2004. c3s1|]'‰t‡fd†ˆDƒƒdkVqdS(c3s!|]}ˆ|krdVqdS(iN((R tb(tv(sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pys ásiN(tsum(R (t partition(Rsp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pys áss&each node must be in exactly one blockc s@ˆj|ƒ}td|dt|ƒd|jƒdt|ƒƒS(NtgraphtnnodestnedgesR(tsubgraphtdicttlentnumber_of_edgesR(RtS(tG(sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pyt node_dataçs$c3s!|]}|ˆ|ƒfVqdS(N((R R(R"(sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pys íscs#t‡fd†t||ƒDƒƒS(Nc3s%|]\}}|ˆ|kVqdS(N((R tuR(R!(sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pys ÷s(tanyR(Rtc(R!(sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pyt edge_relationöscsG‡‡fd†ˆjˆˆBdtƒDƒ}itd„|Dƒƒd6S(Nc3sN|]D\}}}|ˆkr*|ˆksB|ˆkr|ˆkr|VqdS(N((R R#Rtd(RR%(sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pys üs tdatacss!|]}|jddƒVqdS(tweightiN(tget(R R'((sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pys þsR)(tedgestTrueR(RR%tedgedata(R!(RR%sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pyt edge_dataûs,ic3sI|]?\‰‰ˆˆˆƒr‡‡‡fd†tˆˆƒDƒVqdS(c3sF|]<\}}|ˆ|krˆˆˆj||diƒfVqdS(tdefaultN(t get_edge_data(R R#R(R!RR%(sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pys sN(R(R (R!R&(RR%sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pys sc3s<|]2\}}ˆ||ƒr||ˆ||ƒfVqdS(N((R RR%(R.R&(sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pys s cSsi|]\}}||“qS(((R tiR((sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pys s N(tcallableRR$RtNonettypeR tadd_nodes_fromt is_directedRRt is_multigraphtchainitadd_edges_fromt enumeratetnxt relabel_nodes( R!RR&R"R.trelabelt create_usingtHRt block_pairsR+tlabels((R!R.R&R"Rsp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pyR ?s.Ÿ *   *  c s|jƒ}|jƒr€‡‡‡fd†|jˆdtƒDƒ}‡‡‡fd†|jˆdtƒDƒ}t||ƒ}n+‡‡‡fd†|jˆdtƒDƒ}|jˆ}|jˆƒ|j |ƒd|jˆkrý||jˆdˆ>> G = nx.cycle_graph(4) >>> M = nx.contracted_nodes(G, 1, 3) >>> P3 = nx.path_graph(3) >>> nx.is_isomorphic(M, P3) True >>> G = nx.MultiGraph(P3) >>> M = nx.contracted_nodes(G, 0, 2) >>> M.edges MultiEdgeView([(0, 1, 0), (0, 1, 1)]) >>> G = nx.Graph([(1,2), (2,2)]) >>> H = nx.contracted_nodes(G, 1, 2, self_loops=False) >>> list(H.nodes()) [1] >>> list(H.edges()) [(1, 1)] See also -------- contracted_edge quotient_graph Notes ----- This function is also available as `identified_nodes`. c3sK|]A\}}}ˆs$|ˆkr|ˆkr6|nˆˆ|fVqdS(N((R twRR'(t self_loopsR#R(sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pys \s R(c3sK|]A\}}}ˆs$|ˆkrˆ|ˆkr9|nˆ|fVqdS(N((R RRBR'(RCR#R(sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pys _s c3sK|]A\}}}ˆs$|ˆkrˆ|ˆkr9|nˆ|fVqdS(N((R RRBR'(RCR#R(sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pys ds t contraction( tcopyR6tin_edgesR,t out_edgesRR+tnodest remove_nodeR9( R!R#RRCR?RFRGt new_edgestv_data((RCR#Rsp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pyRs @     cCs:|j|Œs'tdj|ƒƒ‚nt|d||ŒS(sReturns the graph that results from contracting the specified edge. Edge contraction identifies the two endpoints of the edge as a single node incident to any edge that was incident to the original two nodes. A graph that results from edge contraction is called a *minor* of the original graph. Parameters ---------- G : NetworkX graph The graph whose edge will be contracted. edge : tuple Must be a pair of nodes in `G`. self_loops : Boolean If this is True, any edges (including `edge`) joining the endpoints of `edge` in `G` become self-loops on the new node in the returned graph. Returns ------- Networkx graph A new graph object of the same type as `G` (leaving `G` unmodified) with endpoints of `edge` identified in a single node. The right node of `edge` will be merged into the left one, so only the left one will appear in the returned graph. Raises ------ ValueError If `edge` is not an edge in `G`. Examples -------- Attempting to contract two nonadjacent nodes yields an error:: >>> import networkx as nx >>> G = nx.cycle_graph(4) >>> nx.contracted_edge(G, (1, 3)) Traceback (most recent call last): ... ValueError: Edge (1, 3) does not exist in graph G; cannot contract it Contracting two adjacent nodes in the cycle graph on *n* nodes yields the cycle graph on *n - 1* nodes:: >>> import networkx as nx >>> C5 = nx.cycle_graph(5) >>> C4 = nx.cycle_graph(4) >>> M = nx.contracted_edge(C5, (0, 1), self_loops=False) >>> nx.is_isomorphic(M, C4) True See also -------- contracted_nodes quotient_graph s6Edge {0} does not exist in graph G; cannot contract itRC(thas_edget ValueErrortformatR(R!tedgeRC((sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pyRts= (t__doc__t itertoolsRRRRtnetworkxR;Rtnetworkx.exceptionRtnetworkx.utilsRt__all__t from_iterableR8RR3tFalseR R,RR R(((sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/minors.pyt s"    #Ù X