Repository: networkx/networkx

Issue:
find_cycle() doesn't find cycle in cyclic digraph
```python
print("version: {}".format(nx.__version__))
dg = nx.DiGraph()
dg.add_edge(1, 0)
dg.add_edge(2, 0)
dg.add_edge(1, 2)
dg.add_edge(2, 1)
find_cycle(dg)
```

```
version: 1.11
Traceback (most recent call last):

  File "<ipython-input-127-ea87764febbd>", line 7, in <module>
    find_cycle(dg)

  File "xxxx\AppData\Local\Continuum\Anaconda\lib\site-packages\networkx\algorithms\cycles.py", line 453, in find_cycle
    raise nx.exception.NetworkXNoCycle('No cycle found.')
```

I believe the problem lies in that when `head` is a previously explored node all paths from the current `start_node` are ignored instead of just pruning from `head`

```python
elif head in explored:
     # Then we've already explored it. No loop is possible.
     break
```

Removing this condition fixes the bug as far as I can tell given the graphs I am working with. However, a more efficient solution that does proper pruning would be more efficient especially for larger graphs.

Discussion:

Patch:
diff --git a/networkx/algorithms/cycles.py b/networkx/algorithms/cycles.py
index a82665e10..77d2c4e87 100644
--- a/networkx/algorithms/cycles.py
+++ b/networkx/algorithms/cycles.py
@@ -1,14 +1,18 @@
-"""
-========================
-Cycle finding algorithms
-========================
-"""
 #    Copyright (C) 2010-2012 by
 #    Aric Hagberg <hagberg@lanl.gov>
 #    Dan Schult <dschult@colgate.edu>
 #    Pieter Swart <swart@lanl.gov>
 #    All rights reserved.
 #    BSD license.
+#
+# Authors: Jon Olav Vik <jonovik@gmail.com>,
+#          Dan Schult <dschult@colgate.edu>
+#          Aric Hagberg <hagberg@lanl.gov>
+"""
+========================
+Cycle finding algorithms
+========================
+"""
 
 from collections import defaultdict
 
@@ -17,16 +21,13 @@ from networkx.utils import *
 from networkx.algorithms.traversal.edgedfs import helper_funcs, edge_dfs
 
 __all__ = [
-    'cycle_basis','simple_cycles','recursive_simple_cycles', 'find_cycle'
+    'cycle_basis', 'simple_cycles', 'recursive_simple_cycles', 'find_cycle'
 ]
 
-__author__ = "\n".join(['Jon Olav Vik <jonovik@gmail.com>',
-                        'Dan Schult <dschult@colgate.edu>',
-                        'Aric Hagberg <hagberg@lanl.gov>'])
 
 @not_implemented_for('directed')
 @not_implemented_for('multigraph')
-def cycle_basis(G,root=None):
+def cycle_basis(G, root=None):
     """ Returns a list of cycles which form a basis for cycles of G.
 
     A basis for cycles of a network is a minimal collection of
@@ -49,10 +50,10 @@ def cycle_basis(G,root=None):
 
     Examples
     --------
-    >>> G=nx.Graph()
+    >>> 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))
+    >>> print(nx.cycle_basis(G, 0))
     [[3, 4, 5, 0], [1, 2, 3, 0]]
 
     Notes
@@ -68,36 +69,36 @@ def cycle_basis(G,root=None):
     --------
     simple_cycles
     """
-    gnodes=set(G.nodes())
-    cycles=[]
+    gnodes = set(G.nodes())
+    cycles = []
     while gnodes:  # loop over connected components
         if root is None:
-            root=gnodes.pop()
-        stack=[root]
-        pred={root:root}
-        used={root:set()}
+            root = gnodes.pop()
+        stack = [root]
+        pred = {root: root}
+        used = {root: set()}
         while stack:  # walk the spanning tree finding cycles
-            z=stack.pop()  # use last-in so cycles easier to find
-            zused=used[z]
+            z = stack.pop()  # use last-in so cycles easier to find
+            zused = used[z]
             for nbr in G[z]:
                 if nbr not in used:   # new node
-                    pred[nbr]=z
+                    pred[nbr] = z
                     stack.append(nbr)
-                    used[nbr]=set([z])
-                elif nbr == z:        # self loops
+                    used[nbr] = set([z])
+                elif nbr == z:          # self loops
                     cycles.append([z])
-                elif nbr not in zused:# found a cycle
-                    pn=used[nbr]
-                    cycle=[nbr,z]
-                    p=pred[z]
+                elif nbr not in zused:  # found a cycle
+                    pn = used[nbr]
+                    cycle = [nbr, z]
+                    p = pred[z]
                     while p not in pn:
                         cycle.append(p)
-                        p=pred[p]
+                        p = pred[p]
                     cycle.append(p)
                     cycles.append(cycle)
                     used[nbr].add(z)
-        gnodes-=set(pred)
-        root=None
+        gnodes -= set(pred)
+        root = None
     return cycles
 
 
@@ -125,7 +126,8 @@ def simple_cycles(G):
 
     Examples
     --------
-    >>> G = nx.DiGraph([(0, 0), (0, 1), (0, 2), (1, 2), (2, 0), (2, 1), (2, 2)])
+    >>> 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
 
@@ -161,10 +163,10 @@ def simple_cycles(G):
     --------
     cycle_basis
     """
-    def _unblock(thisnode,blocked,B):
-        stack=set([thisnode])
+    def _unblock(thisnode, blocked, B):
+        stack = set([thisnode])
         while stack:
-            node=stack.pop()
+            node = stack.pop()
             if node in blocked:
                 blocked.remove(node)
                 stack.update(B[node])
@@ -173,51 +175,49 @@ def simple_cycles(G):
     # Johnson's algorithm requires some ordering of the nodes.
     # We assign the arbitrary ordering given by the strongly connected comps
     # There is no need to track the ordering as each node removed as processed.
-    subG = type(G)(G.edges()) # save the actual graph so we can mutate it here
-                              # We only take the edges because we do not want to
-                              # copy edge and node attributes here.
+    # Also we save the actual graph so we can mutate it. We only take the
+    # edges because we do not want to copy edge and node attributes here.
+    subG = type(G)(G.edges())
     sccs = list(nx.strongly_connected_components(subG))
     while sccs:
-        scc=sccs.pop()
+        scc = sccs.pop()
         # order of scc determines ordering of nodes
         startnode = scc.pop()
         # Processing node runs "circuit" routine from recursive version
-        path=[startnode]
-        blocked = set() # vertex: blocked from search?
-        closed = set() # nodes involved in a cycle
+        path = [startnode]
+        blocked = set()  # vertex: blocked from search?
+        closed = set()   # nodes involved in a cycle
         blocked.add(startnode)
-        B=defaultdict(set) # graph portions that yield no elementary circuit
-        stack=[ (startnode,list(subG[startnode])) ]  # subG gives component nbrs
+        B = defaultdict(set)  # graph portions that yield no elementary circuit
+        stack = [(startnode, list(subG[startnode]))]  # subG gives comp nbrs
         while stack:
-            thisnode,nbrs = stack[-1]
+            thisnode, nbrs = stack[-1]
             if nbrs:
                 nextnode = nbrs.pop()
-#                    print thisnode,nbrs,":",nextnode,blocked,B,path,stack,startnode
-#                    f=raw_input("pause")
                 if nextnode == startnode:
                     yield path[:]
                     closed.update(path)
-#                        print "Found a cycle",path,closed
+#                        print "Found a cycle", path, closed
                 elif nextnode not in blocked:
                     path.append(nextnode)
-                    stack.append( (nextnode,list(subG[nextnode])) )
+                    stack.append((nextnode, list(subG[nextnode])))
                     closed.discard(nextnode)
                     blocked.add(nextnode)
                     continue
             # done with nextnode... look for more neighbors
             if not nbrs:  # no more nbrs
                 if thisnode in closed:
-                    _unblock(thisnode,blocked,B)
+                    _unblock(thisnode, blocked, B)
                 else:
                     for nbr in subG[thisnode]:
                         if thisnode not in B[nbr]:
                             B[nbr].add(thisnode)
                 stack.pop()
-#                assert path[-1]==thisnode
+#                assert path[-1] == thisnode
                 path.pop()
         # done processing this node
         subG.remove_node(startnode)
-        H=subG.subgraph(scc)  # make smaller to avoid work in SCC routine
+        H = subG.subgraph(scc)  # make smaller to avoid work in SCC routine
         sccs.extend(list(nx.strongly_connected_components(H)))
 
 
@@ -245,7 +245,8 @@ def recursive_simple_cycles(G):
 
     Example:
 
-    >>> G = nx.DiGraph([(0, 0), (0, 1), (0, 2), (1, 2), (2, 0), (2, 1), (2, 2)])
+    >>> 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]]
 
@@ -279,10 +280,10 @@ def recursive_simple_cycles(G):
                 _unblock(B[thisnode].pop())
 
     def circuit(thisnode, startnode, component):
-        closed = False # set to True if elementary path is closed
+        closed = False  # set to True if elementary path is closed
         path.append(thisnode)
         blocked[thisnode] = True
-        for nextnode in component[thisnode]: # direct successors of thisnode
+        for nextnode in component[thisnode]:  # direct successors of thisnode
             if nextnode == startnode:
                 result.append(path[:])
                 closed = True
@@ -293,18 +294,18 @@ def recursive_simple_cycles(G):
             _unblock(thisnode)
         else:
             for nextnode in component[thisnode]:
-                if thisnode not in B[nextnode]: # TODO: use set for speedup?
+                if thisnode not in B[nextnode]:  # TODO: use set for speedup?
                     B[nextnode].append(thisnode)
-        path.pop() # remove thisnode from path
+        path.pop()  # remove thisnode from path
         return closed
 
-    path = [] # stack of nodes in current path
-    blocked = defaultdict(bool) # vertex: blocked from search?
-    B = defaultdict(list) # graph portions that yield no elementary circuit
-    result = [] # list to accumulate the circuits found
+    path = []              # stack of nodes in current path
+    blocked = defaultdict(bool)  # vertex: blocked from search?
+    B = defaultdict(list)  # graph portions that yield no elementary circuit
+    result = []            # list to accumulate the circuits found
     # Johnson's algorithm requires some ordering of the nodes.
     # They might not be sortable so we assign an arbitrary ordering.
-    ordering=dict(zip(G,range(len(G))))
+    ordering = dict(zip(G, range(len(G))))
     for s in ordering:
         # Build the subgraph induced by s and following nodes in the ordering
         subgraph = G.subgraph(node for node in G
@@ -312,18 +313,18 @@ def recursive_simple_cycles(G):
         # Find the strongly connected component in the subgraph
         # that contains the least node according to the ordering
         strongcomp = nx.strongly_connected_components(subgraph)
-        mincomp=min(strongcomp,
-                    key=lambda nodes: min(ordering[n] for n in nodes))
+        mincomp = min(strongcomp, key=lambda ns: min(ordering[n] for n in ns))
         component = G.subgraph(mincomp)
         if component:
             # smallest node in the component according to the ordering
-            startnode = min(component,key=ordering.__getitem__)
+            startnode = min(component, key=ordering.__getitem__)
             for node in component:
                 blocked[node] = False
                 B[node][:] = []
-            dummy=circuit(startnode, startnode, component)
+            dummy = circuit(startnode, startnode, component)
     return result
 
+
 def find_cycle(G, source=None, orientation='original'):
     """
     Returns the edges of a cycle found via a directed, depth-first traversal.
@@ -379,7 +380,7 @@ def find_cycle(G, source=None, orientation='original'):
     is also known as a polytree).
 
     >>> import networkx as nx
-    >>> G = nx.DiGraph([(0,1), (0,2), (1,2)])
+    >>> G = nx.DiGraph([(0, 1), (0, 2), (1, 2)])
     >>> try:
     ...    find_cycle(G, orientation='original')
     ... except:
@@ -391,32 +392,6 @@ def find_cycle(G, source=None, orientation='original'):
     """
     out_edge, key, tailhead = helper_funcs(G, orientation)
 
-    def prune(edges, active_nodes):
-        # This edge results from backtracking.
-        # Pop until we get a node whose head equals the current tail.
-        # So for example, we might have:
-        #  [(0,1), (1,2), (2,3)], (1,4)
-        # which must become:
-        #  [(0,1)], (1,4)
-        while True:
-            try:
-                popped_edge = edges.pop()
-            except IndexError:
-                edges = []
-                active_nodes = {tail}
-                break
-            else:
-                popped_head = tailhead(popped_edge)[1]
-                active_nodes.remove(popped_head)
-
-            if edges:
-                previous_head = tailhead(edges[-1])[1]
-                if tail == previous_head:
-                    break
-            else:
-                previous_head = None
-        return edges, active_nodes, previous_head
-
     explored = set()
     cycle = []
     final_node = None
@@ -435,8 +410,31 @@ def find_cycle(G, source=None, orientation='original'):
         for edge in edge_dfs(G, start_node, orientation):
             # Determine if this edge is a continuation of the active path.
             tail, head = tailhead(edge)
+            if head in explored:
+                # Then we've already explored it. No loop is possible.
+                continue
             if previous_head is not None and tail != previous_head:
-                edges, active_nodes, previous_head = prune(edges, active_nodes)
+                # This edge results from backtracking.
+                # Pop until we get a node whose head equals the current tail.
+                # So for example, we might have:
+                #  (0, 1), (1, 2), (2, 3), (1, 4)
+                # which must become:
+                #  (0, 1), (1, 4)
+                while True:
+                    try:
+                        popped_edge = edges.pop()
+                    except IndexError:
+                        edges = []
+                        active_nodes = {tail}
+                        break
+                    else:
+                        popped_head = tailhead(popped_edge)[1]
+                        active_nodes.remove(popped_head)
+
+                    if edges:
+                        last_head = tailhead(edges[-1])[1]
+                        if tail == last_head:
+                            break
             edges.append(edge)
 
             if head in active_nodes:
@@ -445,11 +443,9 @@ def find_cycle(G, source=None, orientation='original'):
                 final_node = head
                 break
             else:
-                previous_head = head
                 seen.add(head)
                 active_nodes.add(head)
-                if head in explored:
-                    edges, active_nodes, previous_head = prune(edges, active_nodes)
+                previous_head = head
 
         if cycle:
             break
@@ -469,4 +465,3 @@ def find_cycle(G, source=None, orientation='original'):
             break
 
     return cycle[i:]
-


Test patch:
diff --git a/networkx/algorithms/tests/test_cycles.py b/networkx/algorithms/tests/test_cycles.py
index f4335d134..6f13c4c2c 100644
--- a/networkx/algorithms/tests/test_cycles.py
+++ b/networkx/algorithms/tests/test_cycles.py
@@ -7,55 +7,58 @@ from networkx.algorithms import find_cycle
 FORWARD = nx.algorithms.edgedfs.FORWARD
 REVERSE = nx.algorithms.edgedfs.REVERSE
 
+
 class TestCycles:
     def setUp(self):
-        G=networkx.Graph()
-        nx.add_cycle(G, [0,1,2,3])
-        nx.add_cycle(G, [0,3,4,5])
-        nx.add_cycle(G, [0,1,6,7,8])
-        G.add_edge(8,9)
-        self.G=G
-
-    def is_cyclic_permutation(self,a,b):
-        n=len(a)
-        if len(b)!=n:
+        G = networkx.Graph()
+        nx.add_cycle(G, [0, 1, 2, 3])
+        nx.add_cycle(G, [0, 3, 4, 5])
+        nx.add_cycle(G, [0, 1, 6, 7, 8])
+        G.add_edge(8, 9)
+        self.G = G
+
+    def is_cyclic_permutation(self, a, b):
+        n = len(a)
+        if len(b) != n:
             return False
-        l=a+a
-        return any(l[i:i+n]==b for i in range(2*n-n+1))
+        l = a + a
+        return any(l[i:i+n] == b for i in range(2 * n - n + 1))
 
     def test_cycle_basis(self):
-        G=self.G
-        cy=networkx.cycle_basis(G,0)
-        sort_cy= sorted( sorted(c) for c in cy )
-        assert_equal(sort_cy, [[0,1,2,3],[0,1,6,7,8],[0,3,4,5]])
-        cy=networkx.cycle_basis(G,1)
-        sort_cy= sorted( sorted(c) for c in cy )
-        assert_equal(sort_cy, [[0,1,2,3],[0,1,6,7,8],[0,3,4,5]])
-        cy=networkx.cycle_basis(G,9)
-        sort_cy= sorted( sorted(c) for c in cy )
-        assert_equal(sort_cy, [[0,1,2,3],[0,1,6,7,8],[0,3,4,5]])
+        G = self.G
+        cy = networkx.cycle_basis(G, 0)
+        sort_cy = sorted(sorted(c) for c in cy)
+        assert_equal(sort_cy, [[0, 1, 2, 3], [0, 1, 6, 7, 8], [0, 3, 4, 5]])
+        cy = networkx.cycle_basis(G, 1)
+        sort_cy = sorted(sorted(c) for c in cy)
+        assert_equal(sort_cy, [[0, 1, 2, 3], [0, 1, 6, 7, 8], [0, 3, 4, 5]])
+        cy = networkx.cycle_basis(G, 9)
+        sort_cy = sorted(sorted(c) for c in cy)
+        assert_equal(sort_cy, [[0, 1, 2, 3], [0, 1, 6, 7, 8], [0, 3, 4, 5]])
         # test disconnected graphs
         nx.add_cycle(G, "ABC")
-        cy=networkx.cycle_basis(G,9)
-        sort_cy= sorted(sorted(c) for c in cy[:-1]) + [sorted(cy[-1])]
-        assert_equal(sort_cy, [[0,1,2,3],[0,1,6,7,8],[0,3,4,5],['A','B','C']])
+        cy = networkx.cycle_basis(G, 9)
+        sort_cy = sorted(sorted(c) for c in cy[:-1]) + [sorted(cy[-1])]
+        assert_equal(sort_cy, [[0, 1, 2, 3], [0, 1, 6, 7, 8], [0, 3, 4, 5],
+                     ['A', 'B', 'C']])
 
     @raises(nx.NetworkXNotImplemented)
     def test_cycle_basis(self):
-        G=nx.DiGraph()
-        cy=networkx.cycle_basis(G,0)
+        G = nx.DiGraph()
+        cy = networkx.cycle_basis(G, 0)
 
     @raises(nx.NetworkXNotImplemented)
     def test_cycle_basis(self):
-        G=nx.MultiGraph()
-        cy=networkx.cycle_basis(G,0)
+        G = nx.MultiGraph()
+        cy = networkx.cycle_basis(G, 0)
 
     def test_simple_cycles(self):
-        G = nx.DiGraph([(0, 0), (0, 1), (0, 2), (1, 2), (2, 0), (2, 1), (2, 2)])
-        cc=sorted(nx.simple_cycles(G))
-        ca=[[0], [0, 1, 2], [0, 2], [1, 2], [2]]
+        edges = [(0, 0), (0, 1), (0, 2), (1, 2), (2, 0), (2, 1), (2, 2)]
+        G = nx.DiGraph(edges)
+        cc = sorted(nx.simple_cycles(G))
+        ca = [[0], [0, 1, 2], [0, 2], [1, 2], [2]]
         for c in cc:
-            assert_true(any(self.is_cyclic_permutation(c,rc) for rc in ca))
+            assert_true(any(self.is_cyclic_permutation(c, rc) for rc in ca))
 
     @raises(nx.NetworkXNotImplemented)
     def test_simple_cycles_graph(self):
@@ -64,88 +67,89 @@ class TestCycles:
 
     def test_unsortable(self):
         #  TODO What does this test do?  das 6/2013
-        G=nx.DiGraph()
-        nx.add_cycle(G, ['a',1])
-        c=list(nx.simple_cycles(G))
+        G = nx.DiGraph()
+        nx.add_cycle(G, ['a', 1])
+        c = list(nx.simple_cycles(G))
 
     def test_simple_cycles_small(self):
         G = nx.DiGraph()
-        nx.add_cycle(G, [1,2,3])
-        c=sorted(nx.simple_cycles(G))
-        assert_equal(len(c),1)
-        assert_true(self.is_cyclic_permutation(c[0],[1,2,3]))
-        nx.add_cycle(G, [10,20,30])
-        cc=sorted(nx.simple_cycles(G))
-        ca=[[1,2,3],[10,20,30]]
+        nx.add_cycle(G, [1, 2, 3])
+        c = sorted(nx.simple_cycles(G))
+        assert_equal(len(c), 1)
+        assert_true(self.is_cyclic_permutation(c[0], [1, 2, 3]))
+        nx.add_cycle(G, [10, 20, 30])
+        cc = sorted(nx.simple_cycles(G))
+        ca = [[1, 2, 3], [10, 20, 30]]
         for c in cc:
-            assert_true(any(self.is_cyclic_permutation(c,rc) for rc in ca))
+            assert_true(any(self.is_cyclic_permutation(c, rc) for rc in ca))
 
     def test_simple_cycles_empty(self):
         G = nx.DiGraph()
-        assert_equal(list(nx.simple_cycles(G)),[])
+        assert_equal(list(nx.simple_cycles(G)), [])
 
     def test_complete_directed_graph(self):
         # see table 2 in Johnson's paper
-        ncircuits=[1,5,20,84,409,2365,16064]
-        for n,c in zip(range(2,9),ncircuits):
-            G=nx.DiGraph(nx.complete_graph(n))
-            assert_equal(len(list(nx.simple_cycles(G))),c)
+        ncircuits = [1, 5, 20, 84, 409, 2365, 16064]
+        for n, c in zip(range(2, 9), ncircuits):
+            G = nx.DiGraph(nx.complete_graph(n))
+            assert_equal(len(list(nx.simple_cycles(G))), c)
 
-    def worst_case_graph(self,k):
+    def worst_case_graph(self, k):
         # see figure 1 in Johnson's paper
         # this graph has excactly 3k simple cycles
-        G=nx.DiGraph()
-        for n in range(2,k+2):
-            G.add_edge(1,n)
-            G.add_edge(n,k+2)
-        G.add_edge(2*k+1,1)
-        for n in range(k+2,2*k+2):
-            G.add_edge(n,2*k+2)
-            G.add_edge(n,n+1)
-        G.add_edge(2*k+3,k+2)
-        for n in range(2*k+3,3*k+3):
-            G.add_edge(2*k+2,n)
-            G.add_edge(n,3*k+3)
-        G.add_edge(3*k+3,2*k+2)
+        G = nx.DiGraph()
+        for n in range(2, k+2):
+            G.add_edge(1, n)
+            G.add_edge(n, k+2)
+        G.add_edge(2*k+1, 1)
+        for n in range(k+2, 2*k+2):
+            G.add_edge(n, 2*k+2)
+            G.add_edge(n, n+1)
+        G.add_edge(2*k+3, k+2)
+        for n in range(2*k+3, 3*k+3):
+            G.add_edge(2*k+2, n)
+            G.add_edge(n, 3*k+3)
+        G.add_edge(3*k+3, 2*k+2)
         return G
 
     def test_worst_case_graph(self):
         # see figure 1 in Johnson's paper
-        for k in range(3,10):
-            G=self.worst_case_graph(k)
-            l=len(list(nx.simple_cycles(G)))
-            assert_equal(l,3*k)
+        for k in range(3, 10):
+            G = self.worst_case_graph(k)
+            l = len(list(nx.simple_cycles(G)))
+            assert_equal(l, 3*k)
 
     def test_recursive_simple_and_not(self):
-        for k in range(2,10):
-            G=self.worst_case_graph(k)
-            cc=sorted(nx.simple_cycles(G))
-            rcc=sorted(nx.recursive_simple_cycles(G))
-            assert_equal(len(cc),len(rcc))
+        for k in range(2, 10):
+            G = self.worst_case_graph(k)
+            cc = sorted(nx.simple_cycles(G))
+            rcc = sorted(nx.recursive_simple_cycles(G))
+            assert_equal(len(cc), len(rcc))
             for c in cc:
-                assert_true(any(self.is_cyclic_permutation(c,rc) for rc in rcc))
+                assert_true(any(self.is_cyclic_permutation(c, r) for r in rcc))
             for rc in rcc:
-                assert_true(any(self.is_cyclic_permutation(rc,c) for c in cc))
+                assert_true(any(self.is_cyclic_permutation(rc, c) for c in cc))
 
     def test_simple_graph_with_reported_bug(self):
-        G=nx.DiGraph()
-        edges = [(0, 2), (0, 3), (1, 0), (1, 3), (2, 1), (2, 4), \
-                (3, 2), (3, 4), (4, 0), (4, 1), (4, 5), (5, 0), \
-                (5, 1), (5, 2), (5, 3)]
+        G = nx.DiGraph()
+        edges = [(0, 2), (0, 3), (1, 0), (1, 3), (2, 1), (2, 4),
+                 (3, 2), (3, 4), (4, 0), (4, 1), (4, 5), (5, 0),
+                 (5, 1), (5, 2), (5, 3)]
         G.add_edges_from(edges)
-        cc=sorted(nx.simple_cycles(G))
-        assert_equal(len(cc),26)
-        rcc=sorted(nx.recursive_simple_cycles(G))
-        assert_equal(len(cc),len(rcc))
+        cc = sorted(nx.simple_cycles(G))
+        assert_equal(len(cc), 26)
+        rcc = sorted(nx.recursive_simple_cycles(G))
+        assert_equal(len(cc), len(rcc))
         for c in cc:
-            assert_true(any(self.is_cyclic_permutation(c,rc) for rc in rcc))
+            assert_true(any(self.is_cyclic_permutation(c, rc) for rc in rcc))
         for rc in rcc:
-            assert_true(any(self.is_cyclic_permutation(rc,c) for c in cc))
+            assert_true(any(self.is_cyclic_permutation(rc, c) for c in cc))
 
 # These tests might fail with hash randomization since they depend on
 # edge_dfs. For more information, see the comments in:
 #    networkx/algorithms/traversal/tests/test_edgedfs.py
 
+
 class TestFindCycle(object):
     def setUp(self):
         self.nodes = [0, 1, 2, 3]
@@ -158,13 +162,13 @@ class TestFindCycle(object):
     def test_digraph(self):
         G = nx.DiGraph(self.edges)
         x = list(find_cycle(G, self.nodes))
-        x_= [(0, 1), (1, 0)]
+        x_ = [(0, 1), (1, 0)]
         assert_equal(x, x_)
 
     def test_multigraph(self):
         G = nx.MultiGraph(self.edges)
         x = list(find_cycle(G, self.nodes))
-        x_ = [(0, 1, 0), (1, 0, 1)] # or (1, 0, 2)
+        x_ = [(0, 1, 0), (1, 0, 1)]  # or (1, 0, 2)
         # Hash randomization...could be any edge.
         assert_equal(x[0], x_[0])
         assert_equal(x[1][:2], x_[1][:2])
@@ -172,7 +176,7 @@ class TestFindCycle(object):
     def test_multidigraph(self):
         G = nx.MultiDiGraph(self.edges)
         x = list(find_cycle(G, self.nodes))
-        x_ = [(0, 1, 0), (1, 0, 0)] # (1, 0, 1)
+        x_ = [(0, 1, 0), (1, 0, 0)]  # (1, 0, 1)
         assert_equal(x[0], x_[0])
         assert_equal(x[1][:2], x_[1][:2])
 
@@ -185,16 +189,16 @@ class TestFindCycle(object):
     def test_multidigraph_ignore(self):
         G = nx.MultiDiGraph(self.edges)
         x = list(find_cycle(G, self.nodes, orientation='ignore'))
-        x_ = [(0, 1, 0, FORWARD), (1, 0, 0, FORWARD)] # or (1, 0, 1, 1)
+        x_ = [(0, 1, 0, FORWARD), (1, 0, 0, FORWARD)]  # or (1, 0, 1, 1)
         assert_equal(x[0], x_[0])
         assert_equal(x[1][:2], x_[1][:2])
         assert_equal(x[1][3], x_[1][3])
 
     def test_multidigraph_ignore2(self):
         # Loop traversed an edge while ignoring its orientation.
-        G = nx.MultiDiGraph([(0,1), (1,2), (1,2)])
-        x = list(find_cycle(G, [0,1,2], orientation='ignore'))
-        x_ = [(1,2,0,FORWARD), (1,2,1,REVERSE)]
+        G = nx.MultiDiGraph([(0, 1), (1, 2), (1, 2)])
+        x = list(find_cycle(G, [0, 1, 2], orientation='ignore'))
+        x_ = [(1, 2, 0, FORWARD), (1, 2, 1, REVERSE)]
         assert_equal(x, x_)
 
     def test_multidigraph_ignore2(self):
@@ -202,24 +206,23 @@ class TestFindCycle(object):
         # The goal here is to cover the case when 2 to be researched from 4,
         # when 4 is visited from the first time (so we must make sure that 4
         # is not visited from 2, and hence, we respect the edge orientation).
-        G = nx.MultiDiGraph([(0,1), (1,2), (2,3), (4,2)])
+        G = nx.MultiDiGraph([(0, 1), (1, 2), (2, 3), (4, 2)])
         assert_raises(nx.exception.NetworkXNoCycle,
-                      find_cycle, G, [0,1,2,3,4], orientation='original')
+                      find_cycle, G, [0, 1, 2, 3, 4], orientation='original')
 
     def test_dag(self):
-        G = nx.DiGraph([(0,1), (0,2), (1,2)])
+        G = nx.DiGraph([(0, 1), (0, 2), (1, 2)])
         assert_raises(nx.exception.NetworkXNoCycle,
                       find_cycle, G, orientation='original')
         x = list(find_cycle(G, orientation='ignore'))
-        assert_equal(x, [(0,1,FORWARD), (1,2,FORWARD), (0,2,REVERSE)])
+        assert_equal(x, [(0, 1, FORWARD), (1, 2, FORWARD), (0, 2, REVERSE)])
 
     def test_prev_explored(self):
         # https://github.com/networkx/networkx/issues/2323
 
         G = nx.DiGraph()
-        G.add_edges_from([(1,0), (2,0), (1,2), (2,1)])
-        assert_raises(nx.exception.NetworkXNoCycle,
-                      find_cycle, G, source=0)
+        G.add_edges_from([(1, 0), (2, 0), (1, 2), (2, 1)])
+        assert_raises(nx.NetworkXNoCycle, find_cycle, G, source=0)
         x = list(nx.find_cycle(G, 1))
         x_ = [(1, 2), (2, 1)]
         assert_equal(x, x_)
@@ -227,3 +230,15 @@ class TestFindCycle(object):
         x = list(nx.find_cycle(G, 2))
         x_ = [(2, 1), (1, 2)]
         assert_equal(x, x_)
+
+        x = list(nx.find_cycle(G))
+        x_ = [(1, 2), (2, 1)]
+        assert_equal(x, x_)
+
+    def test_no_cycle(self):
+        # https://github.com/networkx/networkx/issues/2439
+
+        G = nx.DiGraph()
+        G.add_edges_from([(1, 2), (2, 0), (3, 1), (3, 2)])
+        assert_raises(nx.NetworkXNoCycle, find_cycle, G, source=0)
+        assert_raises(nx.NetworkXNoCycle, find_cycle, G)