From 4e88cde9e90e0fc2f51931418f495ca16ff7573f Mon Sep 17 00:00:00 2001 From: Stefano Grava Date: Thu, 13 Aug 2026 18:08:24 +0200 Subject: [PATCH 1/5] Stop forcing a canonical edge order in edge_weights setter The setter validated input against sorted_edges, so a dict keyed with the "wrong" (but equally valid, for an undirected graph) orientation of an edge was rejected. Delegate straight to nx.set_edge_attributes, which handles either orientation correctly, and drop the list-input branch, which relied on the same unpredictable canonical order. Fixes #447, #448. --- qoolqit/graphs/base_graph.py | 19 ++++--------------- tests/test_graphs/test_base_graph.py | 11 +++++++++++ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/qoolqit/graphs/base_graph.py b/qoolqit/graphs/base_graph.py index 8e5f66455..2ec34d34f 100644 --- a/qoolqit/graphs/base_graph.py +++ b/qoolqit/graphs/base_graph.py @@ -275,24 +275,13 @@ def edge_weights(self) -> dict: return {(u, v): w for u, v, w in self.edges(data="weight")} @edge_weights.setter - def edge_weights(self, weights: list | dict) -> None: - """Set the dictionary of edge weights. + def edge_weights(self, weights: dict) -> None: + """Sets edge weights from a given dictionary of values. Arguments: - weights: list or dictionary of weights. + weights: a dictionary of edge weights. """ - if isinstance(weights, list): - if len(weights) != self.number_of_edges(): - raise ValueError("Size of the weights list does not match the number of nodes.") - weights_dict = {i: w for i, w in zip(self.sorted_edges, weights)} - elif isinstance(weights, dict): - edges = set(weights.keys()) - if set(self.sorted_edges) != edges: - raise ValueError( - "Set of edges in the given dictionary does not match the graph ordered edges." - ) - weights_dict = weights - nx.set_edge_attributes(self, weights_dict, "weight") + nx.set_edge_attributes(self, weights, "weight") @property def coords(self) -> dict: diff --git a/tests/test_graphs/test_base_graph.py b/tests/test_graphs/test_base_graph.py index 98e08557d..c96e185df 100644 --- a/tests/test_graphs/test_base_graph.py +++ b/tests/test_graphs/test_base_graph.py @@ -259,6 +259,17 @@ def test_to_matrix_roundtrip(n_nodes: int, seed: int) -> None: np.testing.assert_allclose(graph.to_matrix(), matrix, atol=1e-8) +def test_edge_weights_accepts_either_orientation() -> None: + # Regression test for #447: setting edge_weights should not depend on + # knowing which of (u, v) / (v, u) the graph happens to report internally. + graph = BaseGraph() + graph.add_edges_from([(0, 1), (1, 2), (2, 0)]) + + graph.edge_weights = {(0, 1): 0.3, (1, 2): 0.4, (2, 0): 0.5} + + assert graph.edge_weights == {(0, 1): 0.3, (0, 2): 0.5, (1, 2): 0.4} + + def test_to_matrix_custom_node_labels_and_none_weights() -> None: # Ensure `to_matrix()` respects `self.nodes` ordering and handles None weights. # Also ensures it works with non-0..N-1 node labels. From 9499eca3b78943dc6601c3df2737031482f4d726 Mon Sep 17 00:00:00 2001 From: Stefano Grava Date: Fri, 14 Aug 2026 08:55:48 +0200 Subject: [PATCH 2/5] minimal edge weight setter --- qoolqit/graphs/base_graph.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qoolqit/graphs/base_graph.py b/qoolqit/graphs/base_graph.py index 2ec34d34f..f0b87fee9 100644 --- a/qoolqit/graphs/base_graph.py +++ b/qoolqit/graphs/base_graph.py @@ -280,8 +280,12 @@ def edge_weights(self, weights: dict) -> None: Arguments: weights: a dictionary of edge weights. + + Raises: + KeyError: if an edge in the given weights dictionary is not found in the graph. """ - nx.set_edge_attributes(self, weights, "weight") + for (u, v), w in weights.items(): + self.edges[u, v]["weight"] = w @property def coords(self) -> dict: From 37eeb7d00df2af02dc2eb93163a143c911fa8f91 Mon Sep 17 00:00:00 2001 From: Stefano Grava Date: Fri, 14 Aug 2026 11:15:58 +0200 Subject: [PATCH 3/5] improve docstring --- qoolqit/graphs/base_graph.py | 27 +++++++++++++++++++++++++-- tests/test_graphs/test_base_graph.py | 4 ++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/qoolqit/graphs/base_graph.py b/qoolqit/graphs/base_graph.py index f0b87fee9..ab80fa471 100644 --- a/qoolqit/graphs/base_graph.py +++ b/qoolqit/graphs/base_graph.py @@ -276,14 +276,37 @@ def edge_weights(self) -> dict: @edge_weights.setter def edge_weights(self, weights: dict) -> None: - """Sets edge weights from a given dictionary of values. + """Set the dictionary of edge weights. + + Checks that the set of edges in the given dictionary matches the graph's edges. + Each edge may be keyed as (u, v) or (v, u) since the graph is undirected. + + To partially update edge weights, use the attribute-like access pattern: + ```python + graph.edges[0,1]["weight"] = 0.5 # Update weight of edge (0,1) + ``` Arguments: weights: a dictionary of edge weights. Raises: - KeyError: if an edge in the given weights dictionary is not found in the graph. + ValueError: if the set of edges in the given dictionary does not match + the graph's edges. + + Example: + >>> graph = BaseGraph([(0, 1), (1, 2), (2, 0)]) + >>> graph.edge_weights = {(0, 1): 0.3, (1, 2): 0.4, (2, 0): 0.5} + >>> graph.edge_weights + {(0, 1): 0.3, (0, 2): 0.5, (1, 2): 0.4} """ + # frozenset canonicalizes each edge regardless of orientation, e.g. + # frozenset((u, v)) == frozenset((v, u)). + given = {frozenset(e) for e in weights} + expected = {frozenset(e) for e in self.edges} + if given != expected: + raise ValueError( + "Set of edges in the given dictionary does not match the graph's edges." + ) for (u, v), w in weights.items(): self.edges[u, v]["weight"] = w diff --git a/tests/test_graphs/test_base_graph.py b/tests/test_graphs/test_base_graph.py index 232e29381..e8eb27fd8 100644 --- a/tests/test_graphs/test_base_graph.py +++ b/tests/test_graphs/test_base_graph.py @@ -148,7 +148,7 @@ def test_edge_weights_update_missing_edge() -> None: graph = BaseGraph([(0, 1), (1, 2), (2, 0)]) with pytest.raises( ValueError, - match="Set of edges in the given dictionary does not match the graph ordered edges.", + match="Set of edges in the given dictionary does not match the graph's edges.", ): graph.edge_weights = {(0, 1): 0.3, (1, 2): 0.4} @@ -157,7 +157,7 @@ def test_edge_weights_update_extra_edge() -> None: graph = BaseGraph([("a", "b"), ("b", "c"), ("c", "a")]) with pytest.raises( ValueError, - match="Set of edges in the given dictionary does not match the graph ordered edges.", + match="Set of edges in the given dictionary does not match the graph's edges.", ): graph.edge_weights = { ("a", "b"): 0.3, From 83646ff2147abb8aa603102bbf32695202805899 Mon Sep 17 00:00:00 2001 From: Stefano Grava Date: Fri, 14 Aug 2026 12:04:12 +0200 Subject: [PATCH 4/5] Antoine's comment: improve BaseGraph class docstring --- qoolqit/graphs/base_graph.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/qoolqit/graphs/base_graph.py b/qoolqit/graphs/base_graph.py index ab80fa471..2adc64f7a 100644 --- a/qoolqit/graphs/base_graph.py +++ b/qoolqit/graphs/base_graph.py @@ -21,9 +21,12 @@ class BaseGraph(nx.Graph): - """Base graph class, directly inheriting from the NetworkX Graph. + """Base graph class, directly inheriting from `networkx.Graph`. - On top of the standard networkx.Graph functionalities, adds alternative + Represents a simple graph (undirected and without self-loops), + with optional node coordinates and node/edge weights. + + On top of the standard networkx.Graph functionality, adds alternative constructors, node coordinates and weights as first-class attributes, distance and Rydberg-interaction calculations, unit-disk graph analysis, and plotting. @@ -40,7 +43,7 @@ class BaseGraph(nx.Graph): `max_distance`, `rescale_coords`. Unit-disk analysis: `is_ud_graph`, `ud_radius_range`, `ud_edges`, `set_ud_edges`. - Rydberg-analog interactions: `interactions`, `interaction_matrix`. + Rydberg analog model utils: `interactions`, `interaction_matrix`. Plotting: `draw`. """ From 350bbd57bf4508e3db05819ac072cca8085bddbc Mon Sep 17 00:00:00 2001 From: Stefano Grava Date: Fri, 14 Aug 2026 14:41:24 +0200 Subject: [PATCH 5/5] Antoine's comment: improve docstring --- qoolqit/graphs/base_graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qoolqit/graphs/base_graph.py b/qoolqit/graphs/base_graph.py index 2adc64f7a..022916ee5 100644 --- a/qoolqit/graphs/base_graph.py +++ b/qoolqit/graphs/base_graph.py @@ -281,9 +281,9 @@ def edge_weights(self) -> dict: def edge_weights(self, weights: dict) -> None: """Set the dictionary of edge weights. - Checks that the set of edges in the given dictionary matches the graph's edges. Each edge may be keyed as (u, v) or (v, u) since the graph is undirected. + Weights must be specified for all edges, otherwise a ValueError is raised. To partially update edge weights, use the attribute-like access pattern: ```python graph.edges[0,1]["weight"] = 0.5 # Update weight of edge (0,1)