Skip to content

Commit 9d581c7

Browse files
leotrsclaude
andcommitted
test: pin down rng semantics for spring layouts
Adds explicit assertions for the four reproducibility cases Max asked about: reusing a rng advances state (different output), fresh rngs from the same seed match, repeated int seeds match, but an int seed is not equivalent to a rng built from that same int (different conversion path through _to_nx_seed). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 320e359 commit 9d581c7

1 file changed

Lines changed: 38 additions & 0 deletions

File tree

tests/drawing/test_layout.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,3 +270,41 @@ def test_spring_layouts_accept_rng(edgelist1):
270270
xgi.barycenter_spring_layout(H, seed=42)
271271
# None still works
272272
xgi.barycenter_spring_layout(H, seed=None)
273+
274+
275+
def test_spring_layouts_rng_semantics(edgelist1):
276+
"""Document expected reproducibility semantics for the seed argument.
277+
278+
The conventions mirror sklearn / scipy:
279+
280+
* Reusing the same `Generator` instance advances its state, so two
281+
consecutive calls produce different layouts.
282+
* Two fresh `Generator`s constructed from the same seed produce identical
283+
layouts.
284+
* Passing an int seed reproduces itself, but is not equivalent to passing a
285+
`Generator` constructed from that same int (the conversion path differs).
286+
"""
287+
H = xgi.Hypergraph(edgelist1)
288+
289+
# Reusing one rng across calls: state advances → different outputs
290+
rng = np.random.default_rng(42)
291+
pos_a = xgi.barycenter_spring_layout(H, seed=rng)
292+
pos_b = xgi.barycenter_spring_layout(H, seed=rng)
293+
assert any(not np.allclose(pos_a[n], pos_b[n]) for n in pos_a)
294+
295+
# Two fresh rngs from the same seed → identical outputs
296+
pos_c = xgi.barycenter_spring_layout(H, seed=np.random.default_rng(42))
297+
pos_d = xgi.barycenter_spring_layout(H, seed=np.random.default_rng(42))
298+
for n in pos_c:
299+
assert np.allclose(pos_c[n], pos_d[n])
300+
301+
# Same int seed reused → identical outputs
302+
pos_e = xgi.barycenter_spring_layout(H, seed=42)
303+
pos_f = xgi.barycenter_spring_layout(H, seed=42)
304+
for n in pos_e:
305+
assert np.allclose(pos_e[n], pos_f[n])
306+
307+
# int seed vs rng built from same seed → NOT equivalent (different paths)
308+
pos_g = xgi.barycenter_spring_layout(H, seed=42)
309+
pos_h = xgi.barycenter_spring_layout(H, seed=np.random.default_rng(42))
310+
assert any(not np.allclose(pos_g[n], pos_h[n]) for n in pos_g)

0 commit comments

Comments
 (0)