Add support for directed hypergraphs
It would be good to add a class that allows users to specify a directed hypergraph. I've seen several ways to do this, but not sure which is best.
Can you please comment on a couple of those ways? So we can start a discussion comparing and contrasting them.
A few ways that I've seen:
- Oriented hypergraphs with a labeling function specifying inputs/outputs formalized in Spectral classes of hypergraphs by Raffaella Mulas.
- Directed hypergraphs with head (output) and tail (input) subsets of a given hyperedge, where the head and tail may be overlapping formalized in Exchange Pattern Mining in the Bitcoin Transaction Directed Hypergraph by Ranshous et al.
There may be other papers discussing these topics, but this is what came to mind.
Thanks! We should seek what is the most general way of implementing this so it covers different cases.
We should think of implementing this after #82
An initial idea that I have about this is to modify the Hypergraph class so that there are H._node_in and H._node_out and H._edge_in and H._edge_out dictionaries (Directed bipartite network) instead of H._node and H._edge. Ideally, I would like to then be able to define H.node.in_degree and H.node.out_degree as Stats objects that accept the "in" and "out" dicts respectively, but not sure what would need to change to make this possible.
but not sure what would need to change to make this possible.
Pretty much everything? Like, literally everything depends on the underlying _node and _edge dicts.
Fair enough. But perhaps at least initially, we could separately create directed versions of the views and the stats?
The other thing is that unless I'm mistaken, edge size is an undirected notion, whereas (for instance) the degrees would not be.
Some initial thoughts about the API for this:
H = xgi.DiHypergraph()
H.nodes.degree # sum of the in- and out-degree
H.nodes.in_degree
H.nodes.out_degree
H.edges.size
H.add_edge([{0, 1, 2}, {2, 3, 4}], id=0)
H.add_edge({"head": {0, 1, 2}, "tail": {2, 3, 4}}) # would this be better named as source/sink or source/target or something like that?
H.edges.size.asdict()[0] = 5
H.nodes.out_degree.as_dict()[0] = 1
xgi.adjacency(H) # A_ij = 1 if i is in the tail of an edge and j is in the head of an edge.
xgi.incidence(H) # I've seen this with +/- entries, but this won't work with the head/tail formalism.
xgi.is_connected(H)
xgi.is_connected(H, strongly=False) # run the connectedness on an undirected hypergraph
# many of the algorithms that we have won't work for directed hypergraphs.