Search code examples
pythonnetworkx

Is it possible to get unique paths in networkx with attribute included in the path?


I have the following graph and I want to be able to discover all ways that I can go from 'ip' to 'name' with the following graph in networkx it shows that I can go from 'ip' to 'name' if I follow this path [['ip', 'address', 'name']] the problem is I also defined that address to name can go from two different edges, one that has the attribute through=isptool and another that goes through the attribute through=phyonebook is it possible to have networkx list out these two paths as separate paths and also include the through attribute in the path? Something like

ip -sometool-> address -isptool-> name

ip -sometool-> address -phonebook-> name
import networkx as nx
g = nx.DiGraph()
g.add_node('email')
g.add_node('ip')
g.add_node('address')
g.add_node('name')
g.add_edges_from([('address', 'name')], through='isptool')
g.add_edges_from([('address', 'name')], through='phonebook')
g.add_edge('email', 'address')
g.add_edge('ip', 'address', through='sometool')
list(nx.all_simple_paths(g, 'ip', 'name'))
>>>[['ip', 'address', 'name']] # should have 2 paths one using isptool and one using phonebook edge
list(nx.all_simple_paths(g, 'email', 'name'))
>>>[['email', 'address', 'name']] # same here, should have 2 paths

Solution

  • The issue is that you're trying to use a DiGraph, which can only handle one directed edge from one node to another. If we switch to using a MultiDiGraph, nx.all_simple_paths() returns the expected result:

    >>> import networkx as nx
        g = nx.MultiDiGraph()
        g.add_nodes_from(['email', 'ip', 'address', 'name'])
        
        g.add_edges_from([('address', 'name')], through='isptool')
        g.add_edges_from([('address', 'name')], through='phonebook')
        g.add_edge('email', 'address')
        g.add_edge('ip', 'address', through='sometool')
    >>> list(nx.all_simple_paths(g, 'ip', 'name'))
    [['ip', 'address', 'name'], ['ip', 'address', 'name']]
    >>> list(nx.all_simple_paths(g, 'email', 'name'))
    [['email', 'address', 'name'], ['email', 'address', 'name']]
    

    However, although we get two paths now, we can't see the edge attributes. We can use nx.all_simple_edge_paths() instead:

    >>> list(nx.all_simple_edge_paths(g, 'email', 'name'))
    [[('email', 'address', 0), ('address', 'name', 0)],
     [('email', 'address', 0), ('address', 'name', 1)]]
    

    And with some f-string magic, we can use the edge data to produce the output you wanted:

    >>> for path in nx.all_simple_edge_paths(g, 'email', 'name'):
            a, b, e = path[0]
            print(f'{a} -{g[a][b][e].get("through", "sometool")}-> {b}', end='')
            for a, b, e in path[1:]:
                print(f' -{g[a][b][e].get("through", "sometool")}-> {b}', end='')
            print('\n')
    email -sometool-> address -isptool-> name
    
    email -sometool-> address -phonebook-> name