xgi-org / xgi

CompleX Group Interactions (XGI) is a Python package for higher-order networks.
https://xgi.readthedocs.io
Other
172 stars 27 forks source link

Add nodelist attribute to adjacency and incidence matrix #495

Open aleable opened 7 months ago

aleable commented 7 months ago

Hi all,

it would be nice to add a nodelist parameter for both the adjacency and incidence matrix of a hypergraph (similarly to what done for networks in networkx).

Currently, the order of rows and columns of the adjacency matrix (similarly the rows of the incidence matrix) is as per nodes attribute. However, this forces a workaround in some presumably typical scenarios when nodes' labels are their indexes in the incidence matrix.

For instance, if a nx.Graph and an xgi.HyperGraph are given in input their edges = [(2, 3), (0, 1), (1, 2)], the nodes are consistently ordered as 2, 3, 0, 1 in both libraries. However, I could not find a way to get the incidence matrix of the hyperpergraph with rows and columns sorted as 0, 1, 2, 3, other than by adding a fix similar to the one below.

import xgi
import networkx as nx

G = nx.Graph()
edges = [(2, 3), (0, 1), (1, 2)]
G.add_edges_from(edges)

H = xgi.Hypergraph()
edges = [(2, 3), (0, 1), (1, 2)]
H.add_edges_from(edges)

adj_G = nx.adjacency_matrix(G, nodelist=sorted(G.nodes)).todense()

node_values = list(H.nodes)
adj_H = xgi.adjacency_matrix(H).todense()
adj_H_sorted = adj_H[:, node_values][node_values, :]

The code snippet outputs:

adj_G = [[0 1 0 0]   adj_H = [[0 1 0 1]   adj_H_sorted = [[0 1 0 0]
         [1 0 1 0]            [1 0 0 0]                   [1 0 1 0]
         [0 1 0 1]            [0 0 0 1]                   [0 1 0 1]
         [0 0 1 0]]           [1 0 1 0]]                  [0 0 1 0]]

Thanks!

maximelucas commented 7 months ago

Hi @aleable, thanks for your suggestion! Indeed this comes from the fact that nodes are added in the order that they appear in the edges, unless nodes are added first (like in networkx). A probably simpler fix would be to add the sorted nodes to the hypergraph before the edges:

H = xgi.Hypergraph()
edges = [(2, 3), (0, 1), (1, 2)]
nodes = np.unique(edges) # returns sorted unique nodes

H.add_nodes_from(nodes) # add nodes before edges to control their order
H.add_edges_from(edges)

This way the nodes are ordered, in the hypergraph:

>>> H.nodes
NodeView((0, 1, 2, 3))

This will make the node order more natural for all matrices (incidence, adjcacency, etc) and other measures.

Would that solve your issue? We'll consider adding a nodelist argument too.

aleable commented 7 months ago

Hi @maximelucas, thanks for the response!

Yes, surely this solves it, even more conveniently.

My issue was to be intended as a feature request rather than a bug. I think that adding nodelist would make operations on graph matrices, e.g. slicing, neater.

Thanks again for considering it.

maximelucas commented 7 months ago

Great. And sure, I understood it as a suggestion for a new feature :) I labeled it as a potential new feature, and we'll also add the trick above to the recipes. Thanks a don't hesitate to post more suggestions/bugs!