Three silent-wrong divergences in the native polars rows(binding_ops=...) variable-length path, all found by differential fuzzing against the pandas oracle while reviewing #1781. All three reproduce on master 84835988 — they are not that PR's regressions. Pandas is the oracle and the contract is parity-or-NotImplementedError; these return a different answer with no error.
Grouped in one issue because the first two share a root cause family (the raw-edge reconstruction vs pandas' hop-window pruning) and all three are in the same gate.
1. Bounded directed min_hops >= 3 returns wrong counts
nodes 0..6
edges 4>6, 0>4, 1>2, 4>6, 5>6, 4>5, 4>6
MATCH (a)-[*3..3]->(b) RETURN count(*) pandas 0 polars 1
Same cause as the unbounded -[*k..]-> case #1781 just gated: compute/hop.py:628 returns empty when max_reached_hop < min_hops, and :636+ prunes edges labelled below min_hops, while the polars arm rebuilds pairs from the raw matching edge table. max_reached_hop is a dedup-by-node BFS eccentricity, not a longest-walk length. #1781 declines the unbounded half; the bounded half is still served.
2. Undirected -[*1..1]- / -[*1]- halves the count
nodes 0..4
edges (1,2)(1,2)(0,2)(1,0)(4,2)(3,2)(3,4)(0,3)(1,2)
MATCH (a)-[*1..1]-(b) RETURN count(*) pandas 36 polars 18
MATCH (a)-[:F*1..1]-(b) RETURN count(*) pandas 16 polars 8
-[*1..2]- and -[*1..3]- agree, which is exactly why the existing tests miss it — only the degenerate k == 1 window is wrong.
3. Undirected -[*1..k]- over-counts with a filtered seed, or as a non-first segment
nodes 0..6 (kind alternating a/b)
edges (0,3)(6,3)(4,2)(1,3)(6,3)(3,1)(3,5)(4,1)(3,3)(0,3)
MATCH (a {kind:'a'})-[*1..2]-(b) RETURN count(*) pandas 38 polars 122
MATCH (a)-[]->(b)-[*1..2]-(c) RETURN count(*) pandas 316 polars 464
All directed equivalents agree, so this is specific to the undirected doubled-pair expansion when the segment does not start from the full node set.
Suggested handling
Until the multiplicity reconstruction is fixed, the honest move is the same one #1781 made for the unbounded case: decline these shapes rather than answer differently. Concretely — bounded directed min_hops >= 3, undirected min_hops == max_hops == 1, and undirected variable-length with a filtered/non-initial seed.
A differential fuzz harness against the pandas oracle over random small graphs (DAG + cyclic, parallel edges, self-loops) catches all three; worth adding to the polars engine suite, since each of these passed the existing tests.
Note when fuzzing: undirected unbounded shapes through the pandas oracle can blow up (75 GiB RSS observed). Guard with RLIMIT_AS + SIGALRM + a container memory cap.
🤖 Generated with Claude Code
https://claude.ai/code/session_015YsqAZQLbqjSDrYSFz2GoB
Three silent-wrong divergences in the native polars
rows(binding_ops=...)variable-length path, all found by differential fuzzing against the pandas oracle while reviewing #1781. All three reproduce on master84835988— they are not that PR's regressions. Pandas is the oracle and the contract is parity-or-NotImplementedError; these return a different answer with no error.Grouped in one issue because the first two share a root cause family (the raw-edge reconstruction vs pandas' hop-window pruning) and all three are in the same gate.
1. Bounded directed
min_hops >= 3returns wrong countsSame cause as the unbounded
-[*k..]->case #1781 just gated:compute/hop.py:628returns empty whenmax_reached_hop < min_hops, and:636+prunes edges labelled belowmin_hops, while the polars arm rebuildspairsfrom the raw matching edge table.max_reached_hopis a dedup-by-node BFS eccentricity, not a longest-walk length. #1781 declines the unbounded half; the bounded half is still served.2. Undirected
-[*1..1]-/-[*1]-halves the count-[*1..2]-and-[*1..3]-agree, which is exactly why the existing tests miss it — only the degeneratek == 1window is wrong.3. Undirected
-[*1..k]-over-counts with a filtered seed, or as a non-first segmentAll directed equivalents agree, so this is specific to the undirected doubled-pair expansion when the segment does not start from the full node set.
Suggested handling
Until the multiplicity reconstruction is fixed, the honest move is the same one #1781 made for the unbounded case: decline these shapes rather than answer differently. Concretely — bounded directed
min_hops >= 3, undirectedmin_hops == max_hops == 1, and undirected variable-length with a filtered/non-initial seed.A differential fuzz harness against the pandas oracle over random small graphs (DAG + cyclic, parallel edges, self-loops) catches all three; worth adding to the polars engine suite, since each of these passed the existing tests.
Note when fuzzing: undirected unbounded shapes through the pandas oracle can blow up (75 GiB RSS observed). Guard with
RLIMIT_AS+SIGALRM+ a container memory cap.🤖 Generated with Claude Code
https://claude.ai/code/session_015YsqAZQLbqjSDrYSFz2GoB