Search code examples
pythonnumpynetworkx

How to convert a NetworkX graph with complex weights to a matrix?


I have a graph whose weights are complex numbers. networkx has a few functions for converting the graph to a matrix of edge weights, however, it doesn't seem to work for complex numbers (though the reverse conversion works fine). It seems to require either int or float edge weights in order to convert them into a NumPy array/matrix.

Python 3.9.7 | packaged by conda-forge | (default, Sep 29 2021, 19:20:46) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.29.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import numpy as np

In [2]: import networkx as nx

In [3]: X = np.random.normal(size=(5,5)) + 1j*np.random.normal(size=(5,5))

In [4]: X
Out[4]: 
array([[ 1.64351378-0.83369888j, -2.29785353-0.86089473j,
...
...   
         0.50504368-0.67854997j, -0.29049118-0.48822688j,
         0.22752377-1.38491981j]])

In [5]: g = nx.DiGraph(X)

In [6]: for i,j in g.edges(): print(f"{(i,j)}: {g[i][j]['weight']}")
(0, 0): (1.6435137789271903-0.833698877745345j)
...
(4, 4): (0.2275237661137745-1.3849198099771993j)

# So conversion from matrix to nx.DiGraph works just fine.
# But the other way around gives an error.

In [7]: Z = nx.to_numpy_array(g, dtype=np.complex128)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-b0b717e5ec8a> in <module>
----> 1 Z = nx.to_numpy_array(g, dtype=np.complex128)

~/miniconda3/envs/coupling/lib/python3.9/site-packages/networkx/convert_matrix.py in to_numpy_array(G, nodelist, dtype, order, multigraph_weight, weight, nonedge)
   1242             for v, d in nbrdict.items():
   1243                 try:
-> 1244                     A[index[u], index[v]] = d.get(weight, 1)
   1245                 except KeyError:
   1246                     # This occurs when there are fewer desired nodes than

TypeError: can't convert complex to float

I have looked at the documentation and all it seems to say is that this works only for a simple NumPy datatype and for compound types, one should use recarrays. I don't understand recarrays well and using np.to_numpy_recarray also yields an error.

In [8]: Z = nx.to_numpy_recarray(g, dtype=np.complex128)
...
TypeError: 'NoneType' object is not iterable

So the question is how to convert the graph into a matrix of edge weights correctly?


Solution

  • Below is a quick hack that could be useful until a fix is implemented:

    import networkx as nx
    import numpy as np
    
    
    def to_numpy_complex(G):
    
        # create an empty array
        N_size = len(G.nodes())
        E = np.empty(shape=(N_size, N_size), dtype=np.complex128)
    
        for i, j, attr in G.edges(data=True):
            E[i, j] = attr.get("weight")
    
        return E
    
    
    X = np.random.normal(size=(5, 5)) + 1j * np.random.normal(size=(5, 5))
    
    g = nx.DiGraph(X)
    
    Y = to_numpy_complex(g)
    
    print(np.allclose(X, Y)) # True