Skip to content

Commit 0e262d9

Browse files
committed
Fix some bugs
1 parent 8b4f938 commit 0e262d9

File tree

7 files changed

+71
-10
lines changed

7 files changed

+71
-10
lines changed

hypergraphx/core/base.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,9 +752,17 @@ def get_weights(self, order=None, size=None, up_to=False, asdict=False):
752752

753753
# Info
754754
def max_order(self):
755+
if not self._edge_list:
756+
raise InvalidParameterError(
757+
"Cannot compute max order of an empty hypergraph."
758+
)
755759
return self.max_size() - 1
756760

757761
def max_size(self):
762+
if not self._edge_list:
763+
raise InvalidParameterError(
764+
"Cannot compute max size of an empty hypergraph."
765+
)
758766
return max(self.get_sizes())
759767

760768
def num_nodes(self):
@@ -881,6 +889,9 @@ def clear(self):
881889
adj.clear()
882890
self._weights.clear()
883891
self._hypergraph_metadata.clear()
892+
self._hypergraph_metadata.update(
893+
{"weighted": self._weighted, "type": self._type_name()}
894+
)
884895
self._incidences_metadata.clear()
885896
self._node_metadata.clear()
886897
self._edge_metadata.clear()

hypergraphx/core/directed.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -311,16 +311,25 @@ def get_incident_edges(self, node, order: int = None, size: int = None):
311311
if order is not None and size is not None:
312312
raise InvalidParameterError("Order and size cannot be both specified.")
313313
if order is None and size is None:
314-
return self.get_source_edges(node) + self.get_target_edges(node)
314+
edges = self.get_source_edges(node) + self.get_target_edges(node)
315315
elif size is not None:
316-
return self.get_source_edges(node, size=size) + self.get_target_edges(
316+
edges = self.get_source_edges(node, size=size) + self.get_target_edges(
317317
node, size=size
318318
)
319-
elif order is not None:
320-
return self.get_source_edges(node, order=order) + self.get_target_edges(
319+
else:
320+
edges = self.get_source_edges(node, order=order) + self.get_target_edges(
321321
node, order=order
322322
)
323323

324+
seen = set()
325+
unique_edges = []
326+
for edge in edges:
327+
if edge in seen:
328+
continue
329+
seen.add(edge)
330+
unique_edges.append(edge)
331+
return unique_edges
332+
324333
def get_sources(self):
325334
"""Returns the list of sources of the hyperedges in the hypergraph.
326335

hypergraphx/core/multiplex.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def _normalize_edge(self, edge, layer=None, **kwargs):
106106
Public API convention: multiplex edges are represented as an edge key
107107
`(layer, edge)` at boundaries. Most methods accept either:
108108
- separate arguments: `edge=<tuple>, layer=<str>`
109-
- a packed edge key: `edge=(<tuple>, <str>)` and `layer=None`
109+
- a packed edge key: `edge=(<str>, <tuple>)` and `layer=None`
110110
111111
Note: to avoid ambiguity with 2-node hyperedges, a packed edge key is
112112
only inferred when the first element looks like a layer (string).
@@ -242,7 +242,7 @@ def add_edges(
242242
243243
edge_layer : list, optional
244244
The list of layers to which the hyperedges belong. If not provided,
245-
`edge_list` must contain packed `(edge, layer)` tuples.
245+
`edge_list` must contain packed `(layer, edge)` tuples.
246246
247247
weights : list, optional
248248
The list of weights of the hyperedges. If the hypergraph is weighted, this must be provided.
@@ -329,7 +329,7 @@ def add_edge(
329329
The hyperedge to add.
330330
layer : str, optional
331331
The layer to which the hyperedge belongs. If not provided, `edge`
332-
must be a packed `(edge, layer)` tuple.
332+
must be a packed `(layer, edge)` tuple.
333333
weight : float, optional
334334
The weight of the hyperedge. If the hypergraph is weighted, this must be provided.
335335
metadata : dict, optional
@@ -449,7 +449,7 @@ def set_weight(self, edge, layer=None, weight=None):
449449
Set edge weight.
450450
451451
Accepts:
452-
- `set_weight(edge, layer, weight)` (legacy, explicit layer)
452+
- `set_weight(edge, layer, weight)` (explicit layer argument)
453453
- `set_weight((layer, edge), weight=<...>)` (packed edge key)
454454
- `set_weight((layer, edge), <weight>)` (packed edge key, positional weight)
455455
- `set_weight((edge, layer), <weight>)` (legacy packed edge key, positional weight)

hypergraphx/core/temporal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,7 @@ def subhypergraph(
728728
if add_all_nodes:
729729
for node in self.get_nodes():
730730
for k, v in res.items():
731-
if v.check_node(node):
731+
if not v.check_node(node):
732732
v.add_node(node)
733733

734734
return res

tests/core/directed_hypergraphs/test_directed_hypergraph.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,14 @@ def test_get_incident_edges_filters_by_size():
110110
assert incident_edges == [(("A", "C"), ("D",))]
111111

112112

113+
def test_get_incident_edges_deduplicates_source_and_target_overlap():
114+
hg = DirectedHypergraph()
115+
edge = (("A",), ("A", "B"))
116+
hg.add_edge(edge)
117+
incident_edges = hg.get_incident_edges("A")
118+
assert incident_edges == [edge]
119+
120+
113121
def test_get_neighbors_undirected_union():
114122
hg = DirectedHypergraph()
115123
hg.add_edge((("A",), ("B",)))

tests/core/hypergraphs/test_hypergraphs.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22

33
from hypergraphx import Hypergraph
4-
from hypergraphx.exceptions import MissingEdgeError
4+
from hypergraphx.exceptions import InvalidParameterError, MissingEdgeError
55

66

77
def test_hypergraph_initialization_with_metadata():
@@ -43,6 +43,28 @@ def test_is_uniform_empty_hypergraph():
4343
assert hg.is_uniform() is True # An empty hypergraph is trivially uniform
4444

4545

46+
def test_max_size_and_order_on_empty_hypergraph_raise_invalid_parameter():
47+
hg = Hypergraph()
48+
with pytest.raises(
49+
InvalidParameterError, match="Cannot compute max size of an empty hypergraph."
50+
):
51+
hg.max_size()
52+
with pytest.raises(
53+
InvalidParameterError, match="Cannot compute max order of an empty hypergraph."
54+
):
55+
hg.max_order()
56+
57+
58+
def test_clear_preserves_core_metadata_invariants():
59+
hg = Hypergraph(weighted=False, hypergraph_metadata={"name": "demo"})
60+
hg.add_edge((1, 2))
61+
hg.clear()
62+
63+
metadata = hg.get_hypergraph_metadata()
64+
assert metadata["weighted"] is False
65+
assert metadata["type"] == "Hypergraph"
66+
67+
4668
def test_is_uniform_single_edge():
4769
"""Test `is_uniform` method with a single hyperedge."""
4870
edges = [(1, 2, 3)]

tests/core/temporal_hypergraphs/test_temporal_hypergraphs.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,17 @@ def test_aggregate_single_window():
267267
assert window_0.get_edge_metadata((3, 4)) == {"type": "B"}
268268

269269

270+
def test_subhypergraph_add_all_nodes_adds_missing_nodes_to_each_snapshot():
271+
thg = TemporalHypergraph(weighted=False)
272+
thg.add_edges([("A", "B"), ("B", "C")], [0, 1])
273+
274+
snapshots = thg.subhypergraph(add_all_nodes=True)
275+
276+
assert set(snapshots.keys()) == {0, 1}
277+
assert set(snapshots[0].get_nodes()) == {"A", "B", "C"}
278+
assert set(snapshots[1].get_nodes()) == {"A", "B", "C"}
279+
280+
270281
def test_aggregate_with_isolated_nodes():
271282
"""Test aggregation ensures isolated nodes are preserved."""
272283
thg = TemporalHypergraph(weighted=True)

0 commit comments

Comments
 (0)