Pytorch Geometric: 'from_networkx' doesn't work with my 'group_node_attrs'

Question:

I’ trying to convert a NetwrokX graph into the pyg format to feed it to a GCN.

from_networkx(G) works without problems

from_networkx(G, group_node_attrs=x) # doesn't work, and I get the following error:

Here the documentation about how the function ‘from_networkx’: https://pytorch-geometric.readthedocs.io/en/latest/_modules/torch_geometric/utils/convert.html

Traceback (most recent call last):
File "/home/iris/PycharmProjects/GNN/input_preprocessing.py", line 161, in
pyg_graph1 = from_networkx(G1, group_node_attrs=x_1_str)
File "/home/iris/venv/GNN/lib/python3.10/site-packages/torch_geometric/utils/convert.py", line 262, in from_networkx
x = data[key]
File "/home/iris/venv/GNN/lib/python3.10/site-packages/torch_geometric/data/data.py", line 444, in getitem
return self._store[key]
File "/home/iris/venv/GNN/lib/python3.10/site-packages/torch_geometric/data/storage.py", line 85, in getitem
return self._mapping[key]
TypeError: unhashable type: ‘list’

Here the example (the original x is actually longer, each list consists of the 768 dim, but here is shorter for a general representation):

import networkx as nx
from torch_geometric.utils.convert import from_networkx
from torch_geometric.data import Data
from torch_geometric.loader import DataLoader

nodes= ['1', '5', '28']
edges= [('1', '5'), ('5', '28')]

G = nx.DiGraph()
G.add_nodes_from(nodes)
G.add_edges_from(edges)

x=[['0.7844669818878174', '-0.40328940749168396', '-0.9366764426231384'],['0.14061762392520905', '-1.1449155807495117', '-0.1811756044626236'],['-1.8840126991271973', '-1.2096494436264038', '1.0780194997787476']]

pyg_graph = from_networkx(G, group_node_attrs=x)

The format of my list of features isn’t correct, but I don’t know which shape it should have to work.

I tried to change the format of the elements elements of the nested list of features from str to int, but it isn’t the problem.

Thank you very much in advance!

Asked By: Iris

||

Answers:

There are a number of problems here (quote from the docs, # <— insertions mine):

def from_networkx(
    G: Any,
    group_node_attrs: Optional[Union[List[str], all]] = None,
    group_edge_attrs: Optional[Union[List[str], all]] = None,
) -> 'torch_geometric.data.Data':
    r"""Converts a :obj:`networkx.Graph` or :obj:`networkx.DiGraph` to a
    :class:`torch_geometric.data.Data` instance.

    Args:
        G (networkx.Graph or networkx.DiGraph): A networkx graph.
        group_node_attrs (List[str] or all, optional): The node attributes to # <---- (point 1)
            be concatenated and added to  :obj:`data.x`. (default: :obj:`None`) # <----
        group_edge_attrs (List[str] or all, optional): The edge attributes to
            be concatenated and added to :obj:`data.edge_attr`.
            (default: :obj:`None`)

    .. note::

        All :attr:`group_node_attrs` and :attr:`group_edge_attrs` values must
        be numeric. # <-- point 2

[…]

  1. You attempt to add the node attributes to the pyg graph when you create it. The from_networkx function expects the node attributes to be present in the original networkx graph already, which it concatenates into a single array and adds to node attributes. Node attributes that are present in your nx Graph but that you are not explicitly grouping are still added to the PyG graph, but as a collection of arrays under the attribute names they have in the nx graph.
  2. the data type in your attributes is str, it should be some numerical type, otherwise the library doesnt know what to do with it.

To fix these errors I have taken your code and augmented it.

import networkx as nx
from torch_geometric.utils.convert import from_networkx
from torch_geometric.data import Data
from torch_geometric.loader import DataLoader


edges= [('1', '5'), ('5', '28')]


nodes= ['1', '5', '28']
x=[['0.7844669818878174', '-0.40328940749168396', '-0.9366764426231384'],['0.14061762392520905', '-1.1449155807495117', '-0.1811756044626236'],['-1.8840126991271973', '-1.2096494436264038', '1.0780194997787476']]

# make a dedicated node list, which assigns nodes to their attributes
# and ensures proper data types.
node_list = []
for node, features in zip(nodes, x):
    node_list.append((node, {str(a):float(b) for a, b in enumerate(features)}))

G = nx.DiGraph()
G.add_nodes_from(node_list) # now the attributes are part of the original nx-graph
G.add_edges_from(edges)

pyg_graph = from_networkx(G, group_node_attrs=['0', '1', '2'])

print(pyg_graph.x)

output:

tensor([[ 0.7845, -0.4033, -0.9367],
        [ 0.1406, -1.1449, -0.1812],
        [-1.8840, -1.2096,  1.0780]])
Answered By: warped