I would like to use NetworkX Graph
objects as keys in a Python dict
. However, I do not want the default behavior for comparison (i.e., by the address of the object). Instead, I would like isomorphic graphs to refer to be keys to the same elements in the dict
.
Is this behavior already implemented somewhere? I could not find any information in this direction.
If I have to implement it myself, is the following assessment realistic?
networkx.Graph
in a class.__eq__
such that it calls is_isomorphic
.__hash__
somehow (suggestions welcomed).I think that I would have to make this wrapped Graph immutable, because:
If a class defines mutable objects and implements an
__eq__()
method, it should not implement__hash__()
, since the implementation of hashable collections requires that a key’s hash value is immutable (if the object’s hash value changes, it will be in the wrong hash bucket).
The igraph library requires less resources for compilation, and comes with additional bindings for the R and C languages which the other two lack. NetworkX is comparatively very inefficient, but it is trivial to install --- requiring no compilation at all, since it is pure python.
For NetworkX, a graph with more than 100K nodes may be too large. I'll demonstrate that it can handle a network with 187K nodes in this post, but the centrality calculations were prolonged. Luckily, there are some other packages available to help us with even larger graphs.
RAPIDS's graph algorithms like PageRank and functions like NetworkX make efficient use of the massive parallelism of GPUs to accelerate analysis of large graphs by over 1000X.
Here is an example of subclassing a networkx Graph and adding a eq and hash function as you describe. I'm not sure if it solves your problem but should be a start.
import networkx as nx
class myGraph(nx.Graph):
def __eq__(self, other):
return nx.is_isomorphic(self, other)
def __hash__(self):
return hash(tuple(sorted(self.degree().values())))
if __name__ == '__main__':
G1 = myGraph([(1,2)])
G2 = myGraph([(2,3)])
G3 = myGraph([(1,2),(2,3)])
print G1.__hash__(), G1.edges()
print G2.__hash__(), G2.edges()
print G3.__hash__(), G3.edges()
print G1 == G2
print G1 == G3
graphs = {}
graphs[G1] = 'G1'
graphs[G2] = 'G2'
graphs[G3] = 'G3'
print graphs.items()
Outputs something like:
3713081631935493181 [(1, 2)]
3713081631935493181 [(2, 3)]
2528504235175490287 [(1, 2), (2, 3)]
True
False
[(<__main__.myGraph object at 0xe47a90>, 'G2'), (<__main__.myGraph object at 0x1643250>, 'G3')]
[aric@hamerkop tmp]$ python gc.py
3713081631935493181 [(1, 2)]
3713081631935493181 [(2, 3)]
2528504235175490287 [(1, 2), (2, 3)]
True
False
[(<__main__.myGraph object at 0x1fefad0>, 'G2'), (<__main__.myGraph object at 0x27ea290>, 'G3')]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With