Skip to content

Commit 372a6c6

Browse files
test(metamorphic): directed distance-metric relations [br-r37-c1-e04a1]
Skill: /testing-metamorphic (different from this 30-min window's /mock-code-finder, /profiling-software-performance, /modes-of-reasoning-project-analysis, /porting-to-rust, /testing-conformance-harnesses, /testing-fuzzing). The directed-distance-metric wrapper fixes (br-r37-c1-89n9d center/periphery and br-r37-c1-wojl3 diameter/radius) lacked metamorphic-property coverage. The differential fuzz lock (br-r37-c1-kitjs) catches divergence from nx, but if both libraries drift the same way (e.g. nx is pinned to a buggy version), the fuzz silently passes. Metamorphic guards catch nx-independent drift because they're rooted in mathematical identities. Adds 4 metamorphic-property tests, each parametrized over 5 random strongly-connected DiGraph seeds: 1. test_directed_diameter_invariant_under_reverse diameter(G) == diameter(reverse(G)) — reversing every edge maps each shortest u→v path to a shortest v→u path in the reverse, so the max over all pairs is preserved. 2. test_directed_radius_diameter_ordering radius(G) ≤ diameter(G) — fundamental inequality. 3. test_directed_center_is_subset_of_eccentricity_minimizers center(G) == {n : ecc[n] == radius(G)} — definitional, verified against an independent recomputation from fnx.eccentricity. 4. test_directed_periphery_is_max_eccentricity_set periphery(G) == {n : ecc[n] == diameter(G)} — same, dual side. All 4 hold by mathematical identity (independent of any nx behavior), so they catch the case where both libs drift together. 86 metamorphic tests pass total (was 66) — 1 vacuous skip on the older complement-of-disconnected-is-connected theorem. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 88a1038 commit 372a6c6

1 file changed

Lines changed: 67 additions & 0 deletions

File tree

tests/python/test_review_mode_metamorphic.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,73 @@ def test_triangles_consistent_with_transitivity_zero(seed):
300300
assert all(t == 0 for t in triangles.values())
301301

302302

303+
# ---- directed distance-metrics metamorphic (br-r37-c1-kitjs) -------
304+
305+
def _scc_digraph(seed):
306+
"""Random directed graph with a Hamiltonian cycle injected (so the
307+
result is always strongly connected, distance metrics defined)."""
308+
rng = random.Random(seed)
309+
n = rng.randint(5, 12)
310+
p = rng.uniform(0.2, 0.5)
311+
G = fnx.DiGraph()
312+
for i in range(n):
313+
G.add_node(i)
314+
edges = []
315+
for i in range(n):
316+
for j in range(n):
317+
if i != j and rng.random() < p:
318+
edges.append((i, j))
319+
for i in range(n):
320+
edges.append((i, (i + 1) % n))
321+
G.add_edges_from(edges)
322+
return G
323+
324+
325+
@pytest.mark.parametrize("seed", [0, 1, 7, 11, 42])
326+
def test_directed_diameter_invariant_under_reverse(seed):
327+
"""For a strongly-connected DiGraph, ``diameter(reverse(G)) ==
328+
diameter(G)``: reversing every edge maps the shortest path from
329+
u to v in G to the shortest path from v to u in reverse(G), so
330+
the maximum over all (u, v) pairs is preserved."""
331+
G = _scc_digraph(seed)
332+
Gr = G.reverse()
333+
assert fnx.diameter(G) == fnx.diameter(Gr)
334+
335+
336+
@pytest.mark.parametrize("seed", [0, 1, 7, 11, 42])
337+
def test_directed_radius_diameter_ordering(seed):
338+
"""Radius ≤ diameter is a fundamental graph-distance inequality.
339+
For finite strongly-connected DiGraphs both are well-defined and
340+
finite."""
341+
G = _scc_digraph(seed)
342+
assert fnx.radius(G) <= fnx.diameter(G)
343+
344+
345+
@pytest.mark.parametrize("seed", [0, 1, 7, 11, 42])
346+
def test_directed_center_is_subset_of_eccentricity_minimizers(seed):
347+
"""The center is exactly the set of nodes attaining the minimum
348+
eccentricity. Verify against an independent computation of the
349+
minimum-eccentricity set from fnx.eccentricity."""
350+
G = _scc_digraph(seed)
351+
ecc = fnx.eccentricity(G)
352+
r = fnx.radius(G)
353+
expected_center = sorted(n for n, e in ecc.items() if e == r)
354+
actual_center = sorted(fnx.center(G))
355+
assert actual_center == expected_center
356+
357+
358+
@pytest.mark.parametrize("seed", [0, 1, 7, 11, 42])
359+
def test_directed_periphery_is_max_eccentricity_set(seed):
360+
"""The periphery is exactly the set of nodes attaining the
361+
maximum eccentricity (i.e., diameter)."""
362+
G = _scc_digraph(seed)
363+
ecc = fnx.eccentricity(G)
364+
d = fnx.diameter(G)
365+
expected_periphery = sorted(n for n, e in ecc.items() if e == d)
366+
actual_periphery = sorted(fnx.periphery(G))
367+
assert actual_periphery == expected_periphery
368+
369+
303370
# ---- harmonic_centrality (br-r37-c1-rsom6 dict-order fix) ----------
304371

305372
def test_harmonic_centrality_value_invariant_under_relabeling():

0 commit comments

Comments
 (0)