ó žÃÒYc@sµdZddlmZddlZddlTddlmZmZdddd gZ e d ƒe d ƒdd „ƒƒZ e d ƒd„ƒZ e d ƒd„ƒZddd„ZdS(sL ======================== Cycle finding algorithms ======================== iÿÿÿÿ(t defaultdictN(t*(t helper_funcstedge_dfst cycle_basist simple_cyclestrecursive_simple_cyclest find_cycletdirectedt multigraphc Cs”t|jƒƒ}g}xu|r|dkr<|jƒ}n|g}i||6}itƒ|6}x|ru|jƒ}||}xî||D]â} | |krË||| <|j| ƒt|gƒ|| >> G = nx.Graph() >>> nx.add_cycle(G, [0, 1, 2, 3]) >>> nx.add_cycle(G, [0, 3, 4, 5]) >>> print(nx.cycle_basis(G, 0)) [[3, 4, 5, 0], [1, 2, 3, 0]] Notes ----- This is adapted from algorithm CACM 491 [1]_. References ---------- .. [1] Paton, K. An algorithm for finding a fundamental set of cycles of a graph. Comm. ACM 12, 9 (Sept 1969), 514-518. See Also -------- simple_cycles N(tsettnodestNonetpoptappendtadd( tGtroottgnodestcycleststacktpredtusedtztzusedtnbrtpntcycletp((sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/cycles.pyRs>,                   t undirectedccsd„}t|ƒ|jƒƒ}ttj|ƒƒ}xÆ|rþ|jƒ}|jƒ}|g}tƒ}tƒ}|j|ƒttƒ} |t||ƒfg} x| rÂ| d\} } | rJ| jƒ} | |krñ|V|j |ƒqJ| |krJ|j | ƒ| j | t|| ƒfƒ|j | ƒ|j| ƒq§qJn| s§| |kro|| || ƒn9x6|| D]*}| | |krz| |j| ƒqzqzW| jƒ|jƒq§q§W|j |ƒ|j |ƒ}|jttj|ƒƒƒq9WdS(swFind simple cycles (elementary circuits) of a directed graph. A `simple cycle`, or `elementary circuit`, is a closed path where no node appears twice. Two elementary circuits are distinct if they are not cyclic permutations of each other. This is a nonrecursive, iterator/generator version of Johnson's algorithm [1]_. There may be better algorithms for some cases [2]_ [3]_. Parameters ---------- G : NetworkX DiGraph A directed graph Returns ------- cycle_generator: generator A generator that produces elementary cycles of the graph. Each cycle is represented by a list of nodes along the cycle. Examples -------- >>> edges = [(0, 0), (0, 1), (0, 2), (1, 2), (2, 0), (2, 1), (2, 2)] >>> G = nx.DiGraph(edges) >>> len(list(nx.simple_cycles(G))) 5 To filter the cycles so that they don't include certain nodes or edges, copy your graph and eliminate those nodes or edges before calling >>> copyG = G.copy() >>> copyG.remove_nodes_from([1]) >>> copyG.remove_edges_from([(0, 1)]) >>> len(list(nx.simple_cycles(copyG))) 3 Notes ----- The implementation follows pp. 79-80 in [1]_. The time complexity is $O((n+e)(c+1))$ for $n$ nodes, $e$ edges and $c$ elementary circuits. References ---------- .. [1] Finding all the elementary circuits of a directed graph. D. B. Johnson, SIAM Journal on Computing 4, no. 1, 77-84, 1975. http://dx.doi.org/10.1137/0204007 .. [2] Enumerating the cycles of a digraph: a new preprocessing strategy. G. Loizou and P. Thanish, Information Sciences, v. 27, 163-182, 1982. .. [3] A search strategy for the elementary cycles of a directed graph. J.L. Szwarcfiter and P.E. Lauer, BIT NUMERICAL MATHEMATICS, v. 16, no. 2, 192-204, 1976. See Also -------- cycle_basis cSsgt|gƒ}xQ|rb|jƒ}||kr|j|ƒ|j||ƒ||jƒqqWdS(N(R R tremovetupdatetclear(tthisnodetblockedtBRtnode((sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/cycles.pyt_unblock¦s    iÿÿÿÿN(ttypetedgestlisttnxtstrongly_connected_componentsR R RRRRtdiscardt remove_nodetsubgraphtextend(RR%tsubGtsccstscct startnodetpathR"tclosedR#RR!tnbrstnextnodeRtH((sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/cycles.pyRisH=                    cs>‡‡‡fd†‰‡‡‡‡‡‡fd†‰g‰ttƒ‰ttƒ‰g‰tt|tt|ƒƒƒƒ‰x¿ˆD]·‰|j‡‡fd†|Dƒƒ}tj |ƒ}t |d‡fd†ƒ}|j|ƒ}|rt |dˆj ƒ}x#|D]}t ˆ|>> edges = [(0, 0), (0, 1), (0, 2), (1, 2), (2, 0), (2, 1), (2, 2)] >>> G = nx.DiGraph(edges) >>> nx.recursive_simple_cycles(G) [[0], [0, 1, 2], [0, 2], [1, 2], [2]] See Also -------- cycle_basis (for undirected graphs) Notes ----- The implementation follows pp. 79-80 in [1]_. The time complexity is $O((n+e)(c+1))$ for $n$ nodes, $e$ edges and $c$ elementary circuits. References ---------- .. [1] Finding all the elementary circuits of a directed graph. D. B. Johnson, SIAM Journal on Computing 4, no. 1, 77-84, 1975. http://dx.doi.org/10.1137/0204007 See Also -------- simple_cycles, cycle_basis cs@ˆ|r<tˆ|7stkeycst‡fd†|DƒƒS(Nc3s|]}ˆ|VqdS(N((R=tn(R>(sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/cycles.pys <s(tmin(tns(R>(sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/cycles.pyt<s( RtboolR(tdicttziptrangetlenR-R)R*RBt __getitem__R8(RR-t strongcomptmincompR:R2R$tdummy((R#R%R"R;R>R3R<R?sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/cycles.pyRàs&3  ! "  toriginalcCs6t||ƒ\}}}tƒ}g}d}x¿|j|ƒD]„} | |krUq=ng} | h} | h} d} x4t|| |ƒD] }||ƒ\}}||kr°q†n| dk rV|| krVx‹trRy| jƒ}Wn!tk rg} |h} PnX||ƒd}| j|ƒ| rË|| dƒd}||krOPqOqËqËWn| j |ƒ|| kr†|j | ƒ|}Pq†| j |ƒ| j |ƒ|} q†W|r´Pq=|j | ƒq=Wt |ƒdksÝt‚tjjdƒ‚x<t|ƒD].\}}||ƒ\}}||krüPqüqüW||S(sÍ Returns the edges of a cycle found via a directed, depth-first traversal. Parameters ---------- G : graph A directed/undirected graph/multigraph. source : node, list of nodes The node from which the traversal begins. If None, then a source is chosen arbitrarily and repeatedly until all edges from each node in the graph are searched. orientation : 'original' | 'reverse' | 'ignore' For directed graphs and directed multigraphs, edge traversals need not respect the original orientation of the edges. When set to 'reverse', then every edge will be traversed in the reverse direction. When set to 'ignore', then each directed edge is treated as a single undirected edge that can be traversed in either direction. For undirected graphs and undirected multigraphs, this parameter is meaningless and is not consulted by the algorithm. Returns ------- edges : directed edges A list of directed edges indicating the path taken for the loop. If no cycle is found, then an exception is raised. For graphs, an edge is of the form `(u, v)` where `u` and `v` are the tail and head of the edge as determined by the traversal. For multigraphs, an edge is of the form `(u, v, key)`, where `key` is the key of the edge. When the graph is directed, then `u` and `v` are always in the order of the actual directed edge. If orientation is 'ignore', then an edge takes the form `(u, v, key, direction)` where direction indicates if the edge was followed in the forward (tail to head) or reverse (head to tail) direction. When the direction is forward, the value of `direction` is 'forward'. When the direction is reverse, the value of `direction` is 'reverse'. Raises ------ NetworkXNoCycle If no cycle was found. Examples -------- In this example, we construct a DAG and find, in the first call, that there are no directed cycles, and so an exception is raised. In the second call, we ignore edge orientations and find that there is an undirected cycle. Note that the second call finds a directed cycle while effectively traversing an undirected graph, and so, we found an "undirected cycle". This means that this DAG structure does not form a directed tree (which is also known as a polytree). >>> import networkx as nx >>> G = nx.DiGraph([(0, 1), (0, 2), (1, 2)]) >>> try: ... nx.find_cycle(G, orientation='original') ... except: ... pass ... >>> list(nx.find_cycle(G, orientation='ignore')) [(0, 1, 'forward'), (1, 2, 'forward'), (0, 2, 'reverse')] iiÿÿÿÿisNo cycle found.N(RR R t nbunch_iterRR9R t IndexErrorRRR.RRRItAssertionErrorR)t exceptiontNetworkXNoCyclet enumerate(Rtsourcet orientationtout_edgeR@ttailheadtexploredRt final_nodet start_nodeR'tseent active_nodest previous_headtedgettailtheadt popped_edget popped_headt last_headti((sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/cycles.pyRHs^A                 (t__doc__t collectionsRtnetworkxR)tnetworkx.utilst%networkx.algorithms.traversal.edgedfsRRt__all__tnot_implemented_forR RRRR(((sp/private/var/folders/w6/vb91730s7bb1k90y_rnhql1dhvdd44/T/pip-build-w4MwvS/networkx/networkx/algorithms/cycles.pyts    Kwh