0

Let's assume I have this directed graph:

Original Directed Graph

What's the optimal (complexity and simplicity) way to get every path that passes through a given node?
Let's say I choose node = 4 I want to get this sub-graph:

Sub graph that passes a single node Currently I am using NetworkX python library, however I am open to choosing another one if it has this capability.

subspring
  • 690
  • 2
  • 7
  • 23
Am1rr3zA
  • 7,115
  • 18
  • 83
  • 125

2 Answers2

3

Let me propose an "ugly" solution (and I am sure there is a better way). First, find all descendants and ancestors of the node:

desc = nx.descendants(G, 4)
anc = nx.ancestors(G, 4)

Next, find the simple paths from each ancestor to the node and from the node to each descendant:

paths = [path for p in desc for path in nx.all_simple_paths(G, 4, p)]
paths.extend([path for p in anc for path in nx.all_simple_paths(G, p, 4)])

Finally, reconstruct the digraph from the paths:

Q = nx.DiGraph()
for p in paths:
    nx.add_path(Q, p)
DYZ
  • 55,249
  • 10
  • 64
  • 93
0

Here is another approach. As in the answer by DYZ, let anc denote the ancestors and desc denote the descendants of the chosen node. Let nodes = anc union desc union {chosen_node}. Iterate over edges (i, j) of the original graph, and delete an edge if either of the following holds:

  • i or j are not in the set nodes.
  • i is in anc and j is in desc.

A node x not in nodes cannot be on any path containing chosen_node, or else x would have to be an ancestor, a descendant, or chosen_node itself (condition 1). If (i, j) is a node from an ancestor i to descendant j, it cannot be on any path containing chosen_node because of the acyclic assumption (condition 2).

Every remaining edge is on some path containing the chosen_node. I believe you can prove it by induction on min(distance(i, chosen_node), distance(j, chosen_node)).


def remove_edges(G, node=0):
  desc = nx.descendants(G, node)
  anc = nx.ancestors(G, node)
  nodes = anc.union(desc).union({node})
  Q = nx.DiGraph()
  for i, j in G.edges:
    # remove edges with either source or target not in nodes
    if (i not in nodes) or (j not in nodes):
      continue
    # remove edges immediately connecting ancestors to descendants
    if (i in anc) and (j in desc):
      continue
    Q.add_edge(i,j)
  return Q  
hilberts_drinking_problem
  • 11,322
  • 3
  • 22
  • 51
  • Your solution would also include a two-edge path that starts, say, at 10 and ends at 6, but does not go through 4. – DYZ Apr 04 '22 at 04:43
  • Do you have a small example to show this? In OP's picture, the only such path is 10 -> 11 -> 6, and will be excluded because edges containing 11 are filtered out by the first condition. – hilberts_drinking_problem Apr 04 '22 at 04:59
  • I tested my solution against yours on random graphs using generator from [here](https://stackoverflow.com/questions/13543069/how-to-create-random-single-source-random-acyclic-directed-graphs-with-negative), but maybe I missed some special cases. – hilberts_drinking_problem Apr 04 '22 at 05:02
  • Just add another path from 10 to 6 through a new node 15: 10->15->6. – DYZ Apr 04 '22 at 16:30
  • That wouldn't cause a problem either as 15 will be neither ancestor or descendant of 4, so all edges containing 15 will be filtered out. – hilberts_drinking_problem Apr 05 '22 at 05:49
  • Yes, probably true. – DYZ Apr 08 '22 at 19:02