Skip to content

Commit a5afdbc

Browse files
fix(barycenter): native directed path via shortest_path_length [br-r37-c1-ecqmz]
Skill: /porting-to-rust (was 30+ min ago at the boundary; same pattern as br-r37-c1-89n9d / br-r37-c1-wojl3 — drop a directed-graph delegate to a buggy Rust path, reuse a correct fnx primitive). Found via differential probe extending the directed-collapse-defect audit: fnx.barycenter on a strongly-connected DiGraph cycle5+chord returns [0, 2], nx returns [0]. Manual computation: bc[0] = 0+1+1+2+3 = 7 ← minimum, the only barycenter bc[1] = 4+0+1+2+3 = 10 bc[2] = 3+4+0+1+2 = 10 bc[3] = 2+3+4+0+1 = 10 bc[4] = 1+2+2+3+0 = 8 The Rust _raw_barycenter has the same gr.undirected() collapse as _raw_diameter / _raw_radius — computes barycentricity on the undirected projection where node 2 sits symmetrically with node 0. Fix: in fnx.barycenter, after the strongly-connected guard, the directed branch now computes barycentricity natively via ``fnx.shortest_path_length(G)`` (already directed-aware) and finds the argmin. Same pattern as the existing ``attr=`` Python branch in the same wrapper. Rust _raw_barycenter stays for undirected. Verified bit-exact against nx 3.6.1 on: - directed cycle5: [0,1,2,3,4] (vertex-transitive) - directed cycle5+chord: [0] (was [0,2] — wrong) - directed K4: [0,1,2,3] - directed cycle3: [0,1,2] - undirected K4 / P5: unchanged Wider test surface: 112 barycenter-family tests pass. Golden snapshot regenerated to capture the corrected directed barycenter output for the dicycle5 / dicycle5+chord fixtures added in br-r37-c1-zr9e0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ab8f8f2 commit a5afdbc

2 files changed

Lines changed: 22 additions & 7 deletions

File tree

python/franken_networkx/__init__.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9612,11 +9612,27 @@ def barycenter(G, weight=None, attr=None, sp=None, *, backend=None, **backend_kw
96129612
# NetworkXNoPath in that case. Route directed inputs through
96139613
# the Python path below when the graph is not strongly
96149614
# connected so the parity error surfaces correctly.
9615-
if G.is_directed() and not is_strongly_connected(G):
9616-
raise NetworkXNoPath(
9617-
f"Input graph {G} is disconnected, so every induced subgraph "
9618-
"has infinite barycentricity."
9619-
)
9615+
if G.is_directed():
9616+
if not is_strongly_connected(G):
9617+
raise NetworkXNoPath(
9618+
f"Input graph {G} is disconnected, so every induced subgraph "
9619+
"has infinite barycentricity."
9620+
)
9621+
# br-r37-c1-ecqmz: _raw_barycenter has the directed-collapse
9622+
# defect (gr.undirected() before computing) — returns wrong
9623+
# barycenter on directed cycle5+chord (gives [0, 2] instead
9624+
# of nx's [0]). Compute natively via shortest_path_length
9625+
# which IS directed-aware.
9626+
smallest = float("inf")
9627+
result: list = []
9628+
for v, dists in shortest_path_length(G):
9629+
bc = sum(dists.values())
9630+
if bc < smallest:
9631+
smallest = bc
9632+
result = [v]
9633+
elif bc == smallest:
9634+
result.append(v)
9635+
return result
96209636
return _raw_barycenter(G)
96219637
if attr is None:
96229638
return _call_networkx_for_parity(

tests/python/goldens/review_mode_algorithms.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5549,8 +5549,7 @@
55495549
]
55505550
],
55515551
"barycenter": [
5552-
0,
5553-
2
5552+
0
55545553
]
55555554
}
55565555
}

0 commit comments

Comments
 (0)