Commit d08720e
* fix(gfql): decline three silently-wrong bounded var-length polars shapes (#1787)
Pandas is the oracle and the contract is parity-or-NotImplementedError. Three
BOUNDED var-length shapes in the native polars `rows(binding_ops=...)` path
returned a DIFFERENT count with no error. Same root-cause family as the
unbounded case #1781 declined: pandas' step_pairs come from the var-length
`edge_op.execute` hop, whose hop-window pruning -- and, when seeded, its
per-seed BFS -- changes the edge multiplicity in a way a rebuild from the raw
matching edge table does not reproduce.
Declined (diverging graphs out of 60 random ones per shape, differential fuzz
against the pandas oracle):
* directed `-[*k..m]->` with min_hops >= 3 (53/60; pandas 0 vs polars 35),
and min_hops >= 2 off a FILTERED seed (27-30/60). Plain min_hops <= 2 is
fuzz-clean, including as a non-first segment -- that is the graph-bench q3
`-[*1..k]->` shape, still served.
* undirected `-[*1..1]-` / `-[*1]-`, the degenerate window: 60/60, halves the
count (pandas 36 vs polars 18). `-[*1..2]-` and wider agree, which is why
the existing tests missed it.
* undirected `-[*1..k]-` that does not start from the full node set: filtered
seed 51/60, non-first segment 36/60. Every directed equivalent agrees, so
this is specific to the undirected doubled-pair expansion.
The gate keys on an EXPLICIT var-length window rather than on
`EdgeSemantics.is_multihop`, because `-[*1..1]-` resolves to min == max == 1 and
so is NOT multihop -- yet pandas still routes it through the var-length hop
(`-[]-` gives 18 on a graph where `-[*1..1]-` gives 36). Plain `-[]-` is
untouched.
Tests: explicit decline + still-served neighbours for each of the three, plus a
seeded differential fuzz over random small graphs (cyclic, parallel edges,
self-loops) that asserts parity-or-raise AND that enough shapes are still served
for the check to mean anything. Bounded windows only: undirected unbounded
shapes through the pandas oracle can exhaust the box.
The new test file is REGISTERED in bin/test-polars.sh. That list is an explicit
allowlist, and the polars lane is the only CI lane with polars installed -- so
the only lane that can execute this file at all. Without the registration the
three `return None` decline statements were the only changed lines no lane ever
ran (10/13 = 76.92%, exactly the changed-line-coverage gate failure); with it
the block is 13/13.
Mutation-checked: dropping the min_hops clause fails 4 tests, the degenerate
undirected window 3, the undirected-seed clause 3, the whole gate 8. Full
graphistry/tests/compute is unchanged by the declines (9 pre-existing failures,
same set).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015YsqAZQLbqjSDrYSFz2GoB
* test(gfql): engine-parametrize the #1787 varlen contract (pandas/polars/cuDF/polars-gpu)
Review follow-ups on #1794.
Comments -> tests: the gate in row_pipeline.py carried per-shape prose asserting which
shapes diverge and by how much. A comment asserting behaviour is unverified and rots.
Every numeric claim is now a named test; the comment keeps only the WHY of the
deliberate divergence from master's serving decision plus a pointer to the tests.
29 -> 18 comment lines, executable logic byte-identical (the only non-comment hunks are
two trailing comments on existing lines).
Engine-parametrized: renamed to test_varlen_bounded_engine_parity_1787.py and
parametrized over pandas / polars / cuDF / polars-gpu. It encodes the INTENDED
per-engine behaviour rather than assuming identity -- the polars engines must decline
exactly the shapes the pandas-API engines must answer -- with oracle counts pinned as
literals so a regression that makes every engine equally wrong still fails. GPU params
gate on a runtime probe and report a reasoned SKIP, never a silent pass; the coverage
boundary (no CI lane runs cuDF or polars-gpu) is stated in the module docstring.
Dropping the module-level polars importorskip also means the pandas params now execute
in test-gfql-core, where the old file was skipped in its entirety (re: #1795).
Found while doing so: cuDF silently returns 1 where pandas returns 9 on seeded
undirected degenerate `(a {p:v})-[*1..1]-(b)` (23/40 random graphs; drop any one of
seeded/undirected/max==1 and it agrees). Separate defect, filed as #1798 and pinned here
with xfail(strict=True).
Mutation-checked both directions: reverting the gate to 233b64c fails 13 tests;
widening it to decline all bounded var-length fails 12.
Verified: dgx-spark GB10 / cuDF 26.02.01 / polars 1.35.2 via
graphistry/test-rapids-official:26.02-gfql-polars with --gpus all -- 80 passed,
1 xfailed, 0 skipped. CI-equivalent local polars lane (cuDF import-blocked) 2395 passed,
54 skipped, 0 failed. bin/lint.sh and bin/typecheck.sh clean.
Note: the accompanying CI fix for the test-polars py3.12 timeout (#1797) touches
.github/workflows/ci.yml and could not be pushed with the available token, which lacks
the `workflow` OAuth scope. The patch is attached to the PR.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015YsqAZQLbqjSDrYSFz2GoB
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 02041a4 commit d08720e
4 files changed
Lines changed: 466 additions & 0 deletions
File tree
- bin
- graphistry
- compute/gfql/lazy/engine/polars
- tests/compute/gfql
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| 32 | + | |
32 | 33 | | |
33 | 34 | | |
34 | 35 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
29 | 32 | | |
30 | 33 | | |
31 | 34 | | |
| |||
Lines changed: 35 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1312 | 1312 | | |
1313 | 1313 | | |
1314 | 1314 | | |
| 1315 | + | |
| 1316 | + | |
| 1317 | + | |
| 1318 | + | |
| 1319 | + | |
| 1320 | + | |
| 1321 | + | |
| 1322 | + | |
| 1323 | + | |
| 1324 | + | |
| 1325 | + | |
| 1326 | + | |
| 1327 | + | |
| 1328 | + | |
| 1329 | + | |
| 1330 | + | |
| 1331 | + | |
| 1332 | + | |
| 1333 | + | |
| 1334 | + | |
| 1335 | + | |
| 1336 | + | |
| 1337 | + | |
| 1338 | + | |
| 1339 | + | |
| 1340 | + | |
| 1341 | + | |
| 1342 | + | |
| 1343 | + | |
| 1344 | + | |
| 1345 | + | |
| 1346 | + | |
| 1347 | + | |
| 1348 | + | |
| 1349 | + | |
1315 | 1350 | | |
1316 | 1351 | | |
1317 | 1352 | | |
| |||
0 commit comments