Skip to content

Commit

Permalink
Add dfs_labeled_edges reporting of reverse edges due to depth_limit. (n…
Browse files Browse the repository at this point in the history
  • Loading branch information
dschult authored and MridulS committed Feb 4, 2023
1 parent 6a805e2 commit d39e5c6
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 6 deletions.
13 changes: 9 additions & 4 deletions networkx/algorithms/traversal/depth_first_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,12 +364,15 @@ def dfs_labeled_edges(G, source=None, depth_limit=None):
edges: generator
A generator of triples of the form (*u*, *v*, *d*), where (*u*,
*v*) is the edge being explored in the depth-first search and *d*
is one of the strings 'forward', 'nontree', or 'reverse'. A
'forward' edge is one in which *u* has been visited but *v* has
is one of the strings 'forward', 'nontree', 'reverse', or 'reverse-depth_limit'.
A 'forward' edge is one in which *u* has been visited but *v* has
not. A 'nontree' edge is one in which both *u* and *v* have been
visited but the edge is not in the DFS tree. A 'reverse' edge is
on in which both *u* and *v* have been visited and the edge is in
the DFS tree.
one in which both *u* and *v* have been visited and the edge is in
the DFS tree. When the `depth_limit` is reached via a 'forward' edge,
a 'reverse' edge is immediately generated rather than the subtree
being explored. To indicate this flavor of 'reverse' edge, the string
yielded is 'reverse-depth_limit'.
Examples
--------
Expand Down Expand Up @@ -436,6 +439,8 @@ def dfs_labeled_edges(G, source=None, depth_limit=None):
visited.add(child)
if depth_now > 1:
stack.append((child, depth_now - 1, iter(G[child])))
else:
yield parent, child, "reverse-depth_limit"
except StopIteration:
stack.pop()
if stack:
Expand Down
103 changes: 101 additions & 2 deletions networkx/algorithms/traversal/tests/test_dfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,43 @@ def test_dfs_labeled_edges(self):
edges = list(nx.dfs_labeled_edges(self.G, source=0))
forward = [(u, v) for (u, v, d) in edges if d == "forward"]
assert forward == [(0, 0), (0, 1), (1, 2), (2, 4), (1, 3)]
assert edges == [
(0, 0, "forward"),
(0, 1, "forward"),
(1, 0, "nontree"),
(1, 2, "forward"),
(2, 1, "nontree"),
(2, 4, "forward"),
(4, 2, "nontree"),
(4, 0, "nontree"),
(2, 4, "reverse"),
(1, 2, "reverse"),
(1, 3, "forward"),
(3, 1, "nontree"),
(3, 0, "nontree"),
(1, 3, "reverse"),
(0, 1, "reverse"),
(0, 3, "nontree"),
(0, 4, "nontree"),
(0, 0, "reverse"),
]

def test_dfs_labeled_disconnected_edges(self):
edges = list(nx.dfs_labeled_edges(self.D))
forward = [(u, v) for (u, v, d) in edges if d == "forward"]
assert forward == [(0, 0), (0, 1), (2, 2), (2, 3)]
assert edges == [
(0, 0, "forward"),
(0, 1, "forward"),
(1, 0, "nontree"),
(0, 1, "reverse"),
(0, 0, "reverse"),
(2, 2, "forward"),
(2, 3, "forward"),
(3, 2, "nontree"),
(2, 3, "reverse"),
(2, 2, "reverse"),
]

def test_dfs_tree_isolates(self):
G = nx.Graph()
Expand Down Expand Up @@ -141,12 +173,79 @@ def test_dls_edges(self):
edges = nx.dfs_edges(self.G, source=9, depth_limit=4)
assert list(edges) == [(9, 8), (8, 7), (7, 2), (2, 1), (2, 3), (9, 10)]

def test_dls_labeled_edges(self):
def test_dls_labeled_edges_depth_1(self):
edges = list(nx.dfs_labeled_edges(self.G, source=5, depth_limit=1))
forward = [(u, v) for (u, v, d) in edges if d == "forward"]
assert forward == [(5, 5), (5, 4), (5, 6)]
# Note: reverse-depth_limit edge types were not reported before gh-6240
assert edges == [
(5, 5, "forward"),
(5, 4, "forward"),
(5, 4, "reverse-depth_limit"),
(5, 6, "forward"),
(5, 6, "reverse-depth_limit"),
(5, 5, "reverse"),
]

def test_dls_labeled_disconnected_edges(self):
def test_dls_labeled_edges_depth_2(self):
edges = list(nx.dfs_labeled_edges(self.G, source=6, depth_limit=2))
forward = [(u, v) for (u, v, d) in edges if d == "forward"]
assert forward == [(6, 6), (6, 5), (5, 4)]
assert edges == [
(6, 6, "forward"),
(6, 5, "forward"),
(5, 4, "forward"),
(5, 4, "reverse-depth_limit"),
(5, 6, "nontree"),
(6, 5, "reverse"),
(6, 6, "reverse"),
]

def test_dls_labeled_disconnected_edges(self):
edges = list(nx.dfs_labeled_edges(self.D, depth_limit=1))
assert edges == [
(0, 0, "forward"),
(0, 1, "forward"),
(0, 1, "reverse-depth_limit"),
(0, 0, "reverse"),
(2, 2, "forward"),
(2, 3, "forward"),
(2, 3, "reverse-depth_limit"),
(2, 7, "forward"),
(2, 7, "reverse-depth_limit"),
(2, 2, "reverse"),
(8, 8, "forward"),
(8, 7, "nontree"),
(8, 9, "forward"),
(8, 9, "reverse-depth_limit"),
(8, 8, "reverse"),
(10, 10, "forward"),
(10, 9, "nontree"),
(10, 10, "reverse"),
]
# large depth_limit has no impact
edges = list(nx.dfs_labeled_edges(self.D, depth_limit=19))
assert edges == [
(0, 0, "forward"),
(0, 1, "forward"),
(1, 0, "nontree"),
(0, 1, "reverse"),
(0, 0, "reverse"),
(2, 2, "forward"),
(2, 3, "forward"),
(3, 2, "nontree"),
(2, 3, "reverse"),
(2, 7, "forward"),
(7, 2, "nontree"),
(7, 8, "forward"),
(8, 7, "nontree"),
(8, 9, "forward"),
(9, 8, "nontree"),
(9, 10, "forward"),
(10, 9, "nontree"),
(9, 10, "reverse"),
(8, 9, "reverse"),
(7, 8, "reverse"),
(2, 7, "reverse"),
(2, 2, "reverse"),
]

0 comments on commit d39e5c6

Please sign in to comment.