Search code examples
pythonlistnetworkx

How to combine edges in a NetworkX Multigraph by summing their (symbolic) weights?


I'm translating my own algorithm from MATLAB to Python and have run into a problemn with combining edges with weights. For example, let's say I have the following edges with symbolic weights using the NetworkX package:

import networkx as nx
import sympy as sym
import itertools

x, y, z = sym.symbols('x y z')
edges = [('a', 'b', x), ('a', 'b', y), ('b','c', z)]
G = nx.MultiGraph()
G.add_weighted_edges_from(edges)

I want the output to be:

combined_edges = [('a', 'b', x + y), ('b','c', z)]

From reading through the NetworkX tutorial, they list the following code for translating a MultiGraph to a regular Graph:

for n, nbrs in G.adjacency():
   for nbr, edict in nbrs.items():
       minvalue = min([d['weight'] for d in edict.values()])
       GG.add_edge(n, nbr, weight = minvalue)

Due to the fact that I have symbolic weights, this doesn't work.

I wrote some code to find unique edges:

unique_edges = [next(g) for _, g in itertools.groupby(edges, key=lambda x:(x[0],x[1]))]

which produces the output:

print(unique_edges)
[('a', 'b', x), ('b', 'c', z)]

How can I adapt this so that the weights of non-unique edges are summed?


Solution

  • You can iterate over the identical edges, then compute their sum:

    import networkx as nx
    import sympy as sym
    
    x, y, z = sym.symbols('x y z')
    edges = [('a', 'b', x), ('a', 'b', y), ('b','c', z)]
    
    G = nx.MultiGraph()
    for n1, n2, w in edges:
        G.add_edge(n1, n2, weight=w)
    
    H = nx.Graph()
    for n1, n2, d in G.edges(data=True):
        if not H.has_edge(n1, n2):
            H.add_edge(n1, n2, weight=d['weight'])
        else:
            H[n1][n2]['weight'] += d['weight']
    

    Or, inspired by this answer:

    H = nx.Graph()
    for n1, n2, d in G.edges(data=True):
        if not H.has_edge(n1, n2):
            H.add_edge(n1, n2, weight=sum(d['weight'] for d in G[n1][n2].values()))
    

    Check:

    G.edges(data=True)
    MultiEdgeDataView([('a', 'b', {'weight': x}),
                       ('a', 'b', {'weight': y}),
                       ('b', 'c', {'weight': z})])
    
    H.edges(data=True)
    EdgeDataView([('a', 'b', {'weight': x + y}),
                  ('b', 'c', {'weight': z})])