Reduce edges in a MultiDiGraph

Question:

I’ve a MultiDiGraph in which there are some edges that I need remove.

import networkx as ntx
import matplotlib.pyplot as plt

edges = [
    (6, 7), (7, 6), (7, 11), (11, 7), (11, 8), (8, 11), (8, 9), (9, 8), (9, 5), (5, 9),
    (5, 10), (10, 5), (10, 2), (2, 10),
    (2, 1), (1, 2), (1, 0), (0, 1),
    (0, 12), (12, 0),
    (11, 14), (14, 11),
    (5, 3),
    (3, 4),
    (4, 13), (13, 4),
]

G = ntx.MultiDiGraph()
G.add_edges_from(edges)

fig, ax = plt.subplots(figsize=(10, 10))
ntx.draw_networkx(G, with_labels=True)

plt.show()

enter image description here

Above an example of my graph, I will remove the edges between nodes 5 and 12 and I will the same with all situation in which an edge is linked with only one edge. There are some situation in which a bidirectional edge is linked to a one way edge, in this case I don’t wont to merge the edges.

So, my aim is to obtain the graph below:

new_edges = [
    (6, 11), (11, 6),
    (11, 14), (14, 11),
    (11, 5), (5, 11),
    (5, 12), (12, 5),
    (5, 4),
    (4, 13), (13, 4),
]

new_G = ntx.MultiDiGraph()
new_G.add_edges_from(new_edges)

fig, ax = plt.subplots(figsize=(10, 10))
ntx.draw_networkx(new_G, with_labels=True)

plt.show()

enter image description here

I found this question but is useful for a Graph not for a MultiDiGraph.

Answers:

Firstly, for the bidirectional edges, it’s pretty much the same as the one for Graph but we add another edge in the opposite direction to make it bidirectional:

# Select all nodes with only 2 neighbors
nodes_to_remove_bidirectional = [n for n in G.nodes if len(list(G.neighbors(n))) == 2]
# For each of those nodes
for node in nodes_to_remove_bidirectional:
    # We add an edge between neighbors (len == 2 so it is correct)
    neighbors = list(G.neighbors(node))
    G.add_edge(*neighbors)
    neighbors.reverse() # add the edge with opposite direction
    G.add_edge(*neighbors)
    # And delete the node
    G.remove_node(node)

This will result:
enter image description here

Now for the unidirectional edges, we select nodes with one incoming edge (predecessor) and one outgoing edge (successor) to different nodes:

nodes_to_remove_unidirectional = [n for n in G.nodes if 
    len(list(G.neighbors(n))) == 1 and
    len(list(G.predecessors(n))) == 1 and
    next(G.neighbors(n)) != next(G.predecessors(n))
    ]

# For each of those nodes
for node in nodes_to_remove_unidirectional:
    # We add an edge between neighbors (len == 2 so it is correct)
    neighbors = list(G.neighbors(node))
    G.add_edge(next(G.predecessors(node)), next(G.neighbors(node)))
    # And delete the node
    G.remove_node(node)

This will result:
enter image description here

Answered By: Muhammad Nizami
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.