11"""Methods for converting to and from bipartite graphs."""
22
33import networkx as nx
4- from networkx import bipartite
54
5+ from ..core import DiHypergraph , Hypergraph
66from ..exception import XGIError
7- from ..generators import empty_hypergraph
87
98__all__ = ["from_bipartite_graph" , "to_bipartite_graph" ]
109
1110
12- def from_bipartite_graph (G , create_using = None , dual = False ):
11+ def from_bipartite_graph (G , dual = False ):
1312 """
1413 Create a Hypergraph from a NetworkX bipartite graph.
1514
@@ -30,16 +29,13 @@ def from_bipartite_graph(G, create_using=None, dual=False):
3029 A networkx bipartite graph. Each node in the graph has a property
3130 'bipartite' taking the value of 0 or 1 indicating the type of node.
3231
33- create_using : Hypergraph constructor, optional
34- The hypergraph object to add the data to, by default None
35-
3632 dual : bool, default : False
3733 If True, get edges from bipartite=0 and nodes from bipartite=1
3834
3935 Returns
4036 -------
41- Hypergraph
42- The equivalent hypergraph
37+ Hypergraph or DiHypergraph
38+ The equivalent hypergraph or directed hypergraph
4339
4440 References
4541 ----------
@@ -58,8 +54,14 @@ def from_bipartite_graph(G, create_using=None, dual=False):
5854 >>> H = xgi.from_bipartite_graph(G)
5955
6056 """
57+ if isinstance (G , nx .DiGraph ):
58+ directed = True
59+ else :
60+ directed = False
61+
6162 edges = []
6263 nodes = []
64+
6365 for n , d in G .nodes (data = True ):
6466 try :
6567 node_type = d ["bipartite" ]
@@ -73,34 +75,59 @@ def from_bipartite_graph(G, create_using=None, dual=False):
7375 else :
7476 raise XGIError ("Invalid type specifier" )
7577
76- if not bipartite . is_bipartite_node_set (G , nodes ):
78+ if not _is_bipartite (G , nodes , edges ):
7779 raise XGIError ("The network is not bipartite" )
7880
79- H = empty_hypergraph (create_using )
81+ if directed :
82+ H = DiHypergraph ()
83+ else :
84+ H = Hypergraph ()
85+
8086 H .add_nodes_from (nodes )
81- for edge in edges :
82- nodes_in_edge = list (G .neighbors (edge ))
83- H .add_edge (nodes_in_edge , idx = edge )
87+
88+ for u , v in G .edges :
89+ if directed :
90+ if v in edges :
91+ H .add_node_to_edge (v , u , direction = "in" )
92+ else :
93+ H .add_node_to_edge (u , v , direction = "out" )
94+ else :
95+ H .add_node_to_edge (v , u )
96+
8497 return H .dual () if dual else H
8598
8699
100+ def _is_bipartite (G , nodes1 , nodes2 ):
101+ """Assumption is that nodes1.union(nodes2) == G.nodes"""
102+ for i , j in G .edges :
103+ cond1 = i in nodes1
104+ cond2 = j in nodes2
105+ if not cond1 == cond2 : # if not both true or both false
106+ return False
107+ return True
108+
109+
87110def to_bipartite_graph (H , index = False ):
88111 """Create a NetworkX bipartite network from a hypergraph.
89112
90113 Parameters
91114 ----------
92- H: xgi.Hypergraph
115+ H: xgi.Hypergraph or xgi.DiHypergraph
93116 The XGI hypergraph object of interest
94117 index: bool (default False)
95118 If False (default), return only the graph. If True, additionally return the
96119 index-to-node and index-to-edge mappings.
97120
98121 Returns
99122 -------
100- nx.Graph[, dict, dict]
101- The resulting equivalent bipartite graph, and optionally the index-to-unit
102- mappings.
103-
123+ if xgi.Hypergraph
124+ nx.Graph[, dict, dict]
125+ The resulting equivalent bipartite graph, and optionally the index-to-unit
126+ mappings.
127+ if xgi.Hypergraph
128+ nx.DiGraph[, dict, dict]
129+ The resulting equivalent directed bipartite graph, and optionally the index-to-unit
130+ mappings.
104131 References
105132 ----------
106133 The Why, How, and When of Representations for Complex Systems,
@@ -116,24 +143,41 @@ def to_bipartite_graph(H, index=False):
116143 >>> G, itn, ite = xgi.to_bipartite_graph(H, index=True)
117144
118145 """
119- G = nx .Graph ()
146+ if isinstance (H , DiHypergraph ):
147+ directed = True
148+ else :
149+ directed = False
120150
121151 n = H .num_nodes
122152 m = H .num_edges
123153
124154 node_dict = dict (zip (H .nodes , range (n )))
125155 edge_dict = dict (zip (H .edges , range (n , n + m )))
156+
157+ if directed :
158+ G = nx .DiGraph ()
159+ else :
160+ G = nx .Graph ()
161+
126162 G .add_nodes_from (node_dict .values (), bipartite = 0 )
127163 G .add_nodes_from (edge_dict .values (), bipartite = 1 )
128- for node in H .nodes :
129- for edge in H .nodes .memberships (node ):
130- G .add_edge (node_dict [node ], edge_dict [edge ])
164+
165+ if directed :
166+ for e in H .edges :
167+ for v in H .edges .tail (e ):
168+ G .add_edge (node_dict [v ], edge_dict [e ])
169+ for v in H .edges .head (e ):
170+ G .add_edge (edge_dict [e ], node_dict [v ])
171+ else :
172+ for e in H .edges :
173+ for v in H .edges .members (e ):
174+ G .add_edge (node_dict [v ], edge_dict [e ])
131175
132176 if index :
133177 return (
134178 G ,
135- dict ( zip ( range ( n ), H . nodes )) ,
136- dict ( zip ( range ( n , n + m ), H . edges )) ,
179+ { v : k for k , v in node_dict . items ()} ,
180+ { v : k for k , v in edge_dict . items ()} ,
137181 )
138182 else :
139183 return G
0 commit comments