Repository: xgi-org/xgi

Issue:
Can't load one of the BiGG datasets
Running
```python
DH = xgi.load_bigg_data("iCN718")
```
returns `XGIError: Cannot add an empty edge`.

Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/xgi-org/xgi/pull/447?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xgi-org) Report
Patch coverage: **`83.33%`** and no project coverage change.
> Comparison is base [(`ab2a2c7`)](https://app.codecov.io/gh/xgi-org/xgi/commit/ab2a2c7ddb9ef32f26ea216171c9715e49712f9b?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xgi-org) 91.83% compared to head [(`301e9c5`)](https://app.codecov.io/gh/xgi-org/xgi/pull/447?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xgi-org) 91.84%.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main     #447   +/-   ##
=======================================
  Coverage   91.83%   91.84%           
=======================================
  Files          60       60           
  Lines        4251     4255    +4     
=======================================
+ Hits         3904     3908    +4     
  Misses        347      347           
```


| [Files Changed](https://app.codecov.io/gh/xgi-org/xgi/pull/447?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xgi-org) | Coverage Δ | |
|---|---|---|
| [xgi/convert/encapsulation\_dag.py](https://app.codecov.io/gh/xgi-org/xgi/pull/447?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xgi-org#diff-eGdpL2NvbnZlcnQvZW5jYXBzdWxhdGlvbl9kYWcucHk=) | `95.08% <75.00%> (ø)` | |
| [xgi/convert/line\_graph.py](https://app.codecov.io/gh/xgi-org/xgi/pull/447?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xgi-org#diff-eGdpL2NvbnZlcnQvbGluZV9ncmFwaC5weQ==) | `95.00% <75.00%> (ø)` | |
| [xgi/readwrite/bigg\_data.py](https://app.codecov.io/gh/xgi-org/xgi/pull/447?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xgi-org#diff-eGdpL3JlYWR3cml0ZS9iaWdnX2RhdGEucHk=) | `100.00% <100.00%> (ø)` | |


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/xgi-org/xgi/pull/447?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xgi-org).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=xgi-org).


Patch:
diff --git a/xgi/convert/encapsulation_dag.py b/xgi/convert/encapsulation_dag.py
index 958100f..90e9066 100644
--- a/xgi/convert/encapsulation_dag.py
+++ b/xgi/convert/encapsulation_dag.py
@@ -1,9 +1,10 @@
-from ..exception import XGIError
-
 import networkx as nx
 
+from ..exception import XGIError
+
 __all__ = ["to_encapsulation_dag", "empirical_subsets_filter"]
 
+
 def to_encapsulation_dag(H, subset_types="all"):
     """The encapsulation DAG (Directed Acyclic Graph) of
     the hypergraph H.
@@ -57,8 +58,10 @@ def to_encapsulation_dag(H, subset_types="all"):
 
     """
     if not (subset_types in ["all", "immediate", "empirical"]):
-        raise XGIError(f"{subset_types} not a valid subset_types option. Choices are "
-                       "'all', 'immediate', and 'empirical'.")
+        raise XGIError(
+            f"{subset_types} not a valid subset_types option. Choices are "
+            "'all', 'immediate', and 'empirical'."
+        )
 
     # Construct the dag
     dag = nx.DiGraph()
@@ -90,6 +93,7 @@ def to_encapsulation_dag(H, subset_types="all"):
 
     return dag
 
+
 def _encapsulated(larger, smaller):
     """Test if a larger hyperedge encapsulates a smaller hyperedge.
 
@@ -108,6 +112,7 @@ def _encapsulated(larger, smaller):
     """
     return len(set(larger).intersection(set(smaller))) == len(smaller)
 
+
 def _get_candidates(subset_types, H, he):
     """Get the candidate hyperedges for encapsulation by he based on the subset type.
 
@@ -135,33 +140,35 @@ def _get_candidates(subset_types, H, he):
 
     return candidates
 
+
 def _check_candidate(subset_types, he, cand):
     """Check whether a hyperedge cand is a candidate to be encapsulated by
-        hyperedge he based on subset_types.
-
-        Parameters
-        ----------
-        subset_types : str
-            Type of subset relationships
-        he : Set
-            The hyperedge
-        cand : Set
-            The candidate
-        Returns
-        -------
-        is_candidate : bool
-            True if cand is a valid candidate to be encapsulated by he, False
-            otherwise.
+    hyperedge he based on subset_types.
+
+    Parameters
+    ----------
+    subset_types : str
+        Type of subset relationships
+    he : Set
+        The hyperedge
+    cand : Set
+        The candidate
+    Returns
+    -------
+    is_candidate : bool
+        True if cand is a valid candidate to be encapsulated by he, False
+        otherwise.
     """
     is_candidate = False
     if subset_types in ["all", "empirical"]:
         if len(he) != len(cand):
             is_candidate = True
     elif subset_types == "immediate":
-        if len(he) == len(cand)-1 or len(he)-1 == len(cand):
+        if len(he) == len(cand) - 1 or len(he) - 1 == len(cand):
             is_candidate = True
     return is_candidate
 
+
 def empirical_subsets_filter(H, dag):
     """
     Filters encapsulation DAG of H in place to only include edges between hyperedges
@@ -190,8 +197,7 @@ def empirical_subsets_filter(H, dag):
         preds = list(dag.predecessors(edge_idx))
         if len(preds) > 0:
             # Get the minimum superface size
-            min_sup_size = min([len(H.edges.members(cand_idx)) for cand_idx in
-                               preds])
+            min_sup_size = min([len(H.edges.members(cand_idx)) for cand_idx in preds])
             # Keep only the superfaces with that size
             to_remove = []
             for cand_idx in preds:
diff --git a/xgi/convert/line_graph.py b/xgi/convert/line_graph.py
index 553557e..ca0b738 100644
--- a/xgi/convert/line_graph.py
+++ b/xgi/convert/line_graph.py
@@ -2,10 +2,10 @@
 
 from itertools import combinations
 
-from ..exception import XGIError
-
 import networkx as nx
 
+from ..exception import XGIError
+
 __all__ = ["to_line_graph"]
 
 
@@ -47,8 +47,10 @@ def to_line_graph(H, s=1, weights=None):
 
     """
     if weights not in [None, "absolute", "normalized"]:
-        raise XGIError(f"{weights} not a valid weights option. Choices are "
-                       "None, 'absolute', and 'normalized'.")
+        raise XGIError(
+            f"{weights} not a valid weights option. Choices are "
+            "None, 'absolute', and 'normalized'."
+        )
     LG = nx.Graph()
 
     edge_label_dict = {tuple(edge): index for index, edge in H._edge.items()}
@@ -61,16 +63,19 @@ def to_line_graph(H, s=1, weights=None):
         if intersection_size >= s:
             if not weights:
                 # Add unweighted edge
-                LG.add_edge(edge_label_dict[tuple(edge1)],
-                            edge_label_dict[tuple(edge2)])
+                LG.add_edge(
+                    edge_label_dict[tuple(edge1)], edge_label_dict[tuple(edge2)]
+                )
             else:
                 # Compute the (normalized) weight
                 weight = intersection_size
                 if weights == "normalized":
                     weight /= min([len(edge1), len(edge2)])
                 # Add edge with weight
-                LG.add_edge(edge_label_dict[tuple(edge1)],
-                        edge_label_dict[tuple(edge2)],
-                        weight=weight)
+                LG.add_edge(
+                    edge_label_dict[tuple(edge1)],
+                    edge_label_dict[tuple(edge2)],
+                    weight=weight,
+                )
 
     return LG
diff --git a/xgi/generators/uniform.py b/xgi/generators/uniform.py
index c7df5da..2b95883 100644
--- a/xgi/generators/uniform.py
+++ b/xgi/generators/uniform.py
@@ -195,7 +195,7 @@ def uniform_HSBM(n, m, p, sizes, seed=None):
     return H
 
 
-def uniform_HPPM(n, m, rho, k, epsilon, seed=None):
+def uniform_HPPM(n, m, k, epsilon, rho=0.5, seed=None):
     """Construct the m-uniform hypergraph planted partition model (m-HPPM)
 
     Parameters
@@ -204,12 +204,12 @@ def uniform_HPPM(n, m, rho, k, epsilon, seed=None):
         Number of nodes
     m : int > 0
         Hyperedge size
-    rho : float between 0 and 1
-        The fraction of nodes in community 1
     k : float > 0
         Mean degree
     epsilon : float > 0
         Imbalance parameter
+    rho : float between 0 and 1, optional
+        The fraction of nodes in community 1, default 0.5
     seed : integer or None (default)
         The seed for the random number generator
 
diff --git a/xgi/readwrite/bigg_data.py b/xgi/readwrite/bigg_data.py
index 03b8f7a..c885bf2 100644
--- a/xgi/readwrite/bigg_data.py
+++ b/xgi/readwrite/bigg_data.py
@@ -1,5 +1,7 @@
 """Load a metabolic network from the BiGG models database."""
 
+from warnings import warn
+
 from ..utils import request_json_from_url, request_json_from_url_cached
 
 __all__ = ["load_bigg_data"]
@@ -106,6 +108,9 @@ def _bigg_to_dihypergraph(d_index, d_model):
             else:
                 tail.add(m)
 
+        if not head and not tail:
+            warn(f"{r['id']} is an empty reaction!")
+            continue
         DH.add_edge((tail, head), id=r["id"], name=r["name"])
 
     return DH


Test patch:
diff --git a/tests/convert/test_encapsulation_dag.py b/tests/convert/test_encapsulation_dag.py
index 03c06af..b8fc6bd 100644
--- a/tests/convert/test_encapsulation_dag.py
+++ b/tests/convert/test_encapsulation_dag.py
@@ -21,7 +21,6 @@ def test_to_encapsulation_dag(edgelist1, edgelist8, hypergraph1, hypergraph2):
     assert len(list(L.predecessors(0))) == 1
     assert len(list(L.successors(1))) == 1
 
-
     L = xgi.to_encapsulation_dag(H, subset_types="empirical")
     assert isinstance(L, DiGraph)
     assert set(L.nodes) == set(list(range(9)))
@@ -47,7 +46,6 @@ def test_to_encapsulation_dag(edgelist1, edgelist8, hypergraph1, hypergraph2):
     assert len(list(L.predecessors(0))) == 1
     assert len(list(L.successors(1))) == 1
 
-
     L = xgi.to_encapsulation_dag(H, subset_types="empirical")
     assert isinstance(L, DiGraph)
     assert set(L.nodes) == set(list(range(9)))
diff --git a/tests/convert/test_line_graph.py b/tests/convert/test_line_graph.py
index 68223b6..ad48002 100644
--- a/tests/convert/test_line_graph.py
+++ b/tests/convert/test_line_graph.py
@@ -42,47 +42,46 @@ def test_abs_weighted_line_graph(edgelist1, hypergraph1, hypergraph2):
 
     L = xgi.to_line_graph(H, weights="absolute")
     assert isinstance(L, Graph)
-    assert all(["weight" in dat for u,v,dat in L.edges(data=True)])
+    assert all(["weight" in dat for u, v, dat in L.edges(data=True)])
     assert set(L.nodes) == {0, 1, 2, 3}
     assert [set(e) for e in L.edges] == [{2, 3}]
-    assert L.edges[(2,3)]['weight'] == 1
+    assert L.edges[(2, 3)]["weight"] == 1
 
     L = xgi.to_line_graph(hypergraph1, weights="absolute")
     assert isinstance(L, Graph)
-    assert all(["weight" in dat for u,v,dat in L.edges(data=True)])
+    assert all(["weight" in dat for u, v, dat in L.edges(data=True)])
     assert set(L.nodes) == {"e1", "e2", "e3"}
     assert [set(e) for e in L.edges] == [{"e1", "e2"}, {"e2", "e3"}]
-    assert L.edges[("e1","e2")]['weight'] == 2
-    assert L.edges[("e2","e3")]['weight'] == 1
-    assert sum([dat["weight"] for _,_,dat in L.edges(data=True)]) == 3
-
+    assert L.edges[("e1", "e2")]["weight"] == 2
+    assert L.edges[("e2", "e3")]["weight"] == 1
+    assert sum([dat["weight"] for _, _, dat in L.edges(data=True)]) == 3
 
     L = xgi.to_line_graph(hypergraph1, s=2, weights="absolute")
     assert isinstance(L, Graph)
-    assert all(["weight" in dat for u,v,dat in L.edges(data=True)])
+    assert all(["weight" in dat for u, v, dat in L.edges(data=True)])
     assert set(L.nodes) == {"e1", "e2", "e3"}
     assert [set(e) for e in L.edges] == [{"e1", "e2"}]
-    assert L.edges[("e1","e2")]['weight'] == 2
-    assert sum([dat["weight"] for _,_,dat in L.edges(data=True)]) == 2
+    assert L.edges[("e1", "e2")]["weight"] == 2
+    assert sum([dat["weight"] for _, _, dat in L.edges(data=True)]) == 2
 
     L = xgi.to_line_graph(hypergraph2, weights="absolute")
     assert isinstance(L, Graph)
-    assert all(["weight" in dat for u,v,dat in L.edges(data=True)])
+    assert all(["weight" in dat for u, v, dat in L.edges(data=True)])
     assert set(L.nodes) == {"e1", "e2", "e3"}
     assert [set(e) for e in L.edges] == [{"e1", "e2"}, {"e1", "e3"}, {"e2", "e3"}]
-    assert L.edges[("e1","e2")]['weight'] == 1
-    assert L.edges[("e2","e3")]['weight'] == 2
-    assert L.edges[("e1","e3")]['weight'] == 2
-    assert sum([dat["weight"] for _,_,dat in L.edges(data=True)]) == 5
+    assert L.edges[("e1", "e2")]["weight"] == 1
+    assert L.edges[("e2", "e3")]["weight"] == 2
+    assert L.edges[("e1", "e3")]["weight"] == 2
+    assert sum([dat["weight"] for _, _, dat in L.edges(data=True)]) == 5
 
     L = xgi.to_line_graph(hypergraph2, s=2, weights="absolute")
     assert isinstance(L, Graph)
-    assert all(["weight" in dat for u,v,dat in L.edges(data=True)])
+    assert all(["weight" in dat for u, v, dat in L.edges(data=True)])
     assert set(L.nodes) == {"e1", "e2", "e3"}
     assert [set(e) for e in L.edges] == [{"e1", "e3"}, {"e2", "e3"}]
-    assert L.edges[("e2","e3")]['weight'] == 2
-    assert L.edges[("e1","e3")]['weight'] == 2
-    assert sum([dat["weight"] for _,_,dat in L.edges(data=True)]) == 4
+    assert L.edges[("e2", "e3")]["weight"] == 2
+    assert L.edges[("e1", "e3")]["weight"] == 2
+    assert sum([dat["weight"] for _, _, dat in L.edges(data=True)]) == 4
 
 
 def test_normed_weighted_line_graph(edgelist1, hypergraph1, edgelist2, hypergraph2):
@@ -91,26 +90,26 @@ def test_normed_weighted_line_graph(edgelist1, hypergraph1, edgelist2, hypergrap
 
     L = xgi.to_line_graph(H, weights="normalized")
     assert isinstance(L, Graph)
-    assert all(["weight" in dat for u,v,dat in L.edges(data=True)])
+    assert all(["weight" in dat for u, v, dat in L.edges(data=True)])
     assert set(L.nodes) == {0, 1, 2, 3}
     assert [set(e) for e in L.edges] == [{2, 3}]
-    assert L.edges[(2,3)]['weight'] == 0.5
+    assert L.edges[(2, 3)]["weight"] == 0.5
 
     L = xgi.to_line_graph(hypergraph1, weights="normalized")
     assert isinstance(L, Graph)
-    assert all(["weight" in dat for u,v,dat in L.edges(data=True)])
+    assert all(["weight" in dat for u, v, dat in L.edges(data=True)])
     assert set(L.nodes) == {"e1", "e2", "e3"}
     assert [set(e) for e in L.edges] == [{"e1", "e2"}, {"e2", "e3"}]
-    assert L.edges[("e1","e2")]['weight'] == 1.0
-    assert L.edges[("e2","e3")]['weight'] == 1.0
-    assert sum([dat["weight"] for _,_,dat in L.edges(data=True)]) == 2.0
+    assert L.edges[("e1", "e2")]["weight"] == 1.0
+    assert L.edges[("e2", "e3")]["weight"] == 1.0
+    assert sum([dat["weight"] for _, _, dat in L.edges(data=True)]) == 2.0
 
     L = xgi.to_line_graph(hypergraph2, weights="normalized")
     assert isinstance(L, Graph)
-    assert all(["weight" in dat for u,v,dat in L.edges(data=True)])
+    assert all(["weight" in dat for u, v, dat in L.edges(data=True)])
     assert set(L.nodes) == {"e1", "e2", "e3"}
     assert [set(e) for e in L.edges] == [{"e1", "e2"}, {"e1", "e3"}, {"e2", "e3"}]
-    assert L.edges[("e1","e2")]['weight'] == 0.5
-    assert L.edges[("e2","e3")]['weight'] == 1.0
-    assert L.edges[("e1","e3")]['weight'] == 1.0
-    assert sum([dat["weight"] for _,_,dat in L.edges(data=True)]) == 2.5
+    assert L.edges[("e1", "e2")]["weight"] == 0.5
+    assert L.edges[("e2", "e3")]["weight"] == 1.0
+    assert L.edges[("e1", "e3")]["weight"] == 1.0
+    assert sum([dat["weight"] for _, _, dat in L.edges(data=True)]) == 2.5
diff --git a/tests/generators/test_uniform.py b/tests/generators/test_uniform.py
index 8554d4c..ec0b411 100644
--- a/tests/generators/test_uniform.py
+++ b/tests/generators/test_uniform.py
@@ -93,7 +93,7 @@ def test_uniform_HPPM():
         rho = -0.1
         k = 2
         epsilon = 0.5
-        xgi.uniform_HPPM(n, m, rho, k, epsilon)
+        xgi.uniform_HPPM(n, m, k, epsilon, rho)
 
     # rho > 1
     with pytest.raises(XGIError):
@@ -102,7 +102,7 @@ def test_uniform_HPPM():
         rho = 1.1
         k = 2
         epsilon = 0.5
-        xgi.uniform_HPPM(n, m, rho, k, epsilon)
+        xgi.uniform_HPPM(n, m, k, epsilon, rho)
 
     # k < 0
     with pytest.raises(XGIError):
@@ -111,7 +111,7 @@ def test_uniform_HPPM():
         rho = 0.5
         k = -2
         epsilon = 0.5
-        xgi.uniform_HPPM(n, m, rho, k, epsilon)
+        xgi.uniform_HPPM(n, m, k, epsilon, rho)
 
     # epsilon < 0
     with pytest.raises(XGIError):
@@ -120,7 +120,7 @@ def test_uniform_HPPM():
         rho = 0.5
         k = 2
         epsilon = -0.1
-        xgi.uniform_HPPM(n, m, rho, k, epsilon)
+        xgi.uniform_HPPM(n, m, k, epsilon)
 
     # epsilon < 0
     with pytest.raises(XGIError):
@@ -129,20 +129,20 @@ def test_uniform_HPPM():
         rho = 0.5
         k = 2
         epsilon = 1.1
-        xgi.uniform_HPPM(n, m, rho, k, epsilon)
+        xgi.uniform_HPPM(n, m, k, epsilon)
 
     m = 2
     n = 10
     rho = 0.5
     k = 2
     epsilon = 0.8
-    H1 = xgi.uniform_HPPM(n, m, rho, k, epsilon, seed=0)
+    H1 = xgi.uniform_HPPM(n, m, k, epsilon, seed=0)
 
     assert H1.num_nodes == 10
     assert xgi.unique_edge_sizes(H1) == [2]
 
     # test that the seed works
-    H2 = xgi.uniform_HPPM(n, m, rho, k, epsilon, seed=0)
+    H2 = xgi.uniform_HPPM(n, m, k, epsilon, rho, seed=0)
 
     assert H1.edges.members(dtype=dict) == H2.edges.members(dtype=dict)
 
diff --git a/tests/readwrite/test_bigg_data.py b/tests/readwrite/test_bigg_data.py
index a64b555..fbaf23d 100644
--- a/tests/readwrite/test_bigg_data.py
+++ b/tests/readwrite/test_bigg_data.py
@@ -28,3 +28,11 @@ def test_load_bigg_data(capfd):
 
     with pytest.raises(XGIError):
         load_bigg_data("test")
+
+
+def test_411():
+    with pytest.warns(Warning):
+        H = load_bigg_data("iCN718")
+    assert H["name"] == "iCN718"
+    assert H.num_nodes == 888
+    assert H.num_edges == 1014