refactor(gfql): route flow productized: chain_specializations layout, routes-off replay, shape registry + route harness, CI ledger - #2054
Conversation
|
Slice 2 added on the same PR (462202f): |
| raise NotImplementedError(f"polars chain engine does not support op {type(op).__name__}") | ||
|
|
||
|
|
||
| PolarsPlainSingleHopShape = Literal["seeded-index", "skip-combine"] |
There was a problem hiding this comment.
maybe we take this as a chance to cleanup the code a bit, like
polars/chain_specializations/{admission,hotpaths}.py: keep in same files to aid DRY, understanding, etc- unit tests can then label (& verify) inputs that can tried against multiple hotpaths instead of just 1, increasing tested shapes
note: this is a disruptive refactor, so should rebase & redo once things settle (or prioritize for before more?) to avoid issues like incomplete/drifted refactors
|
Agreed on both points, and on the sequencing. Structure. Redo as Corpus. The shape corpus becomes a registry, not a hand-written list: each specialization's own test module registers its shape table (the fast/bypass tables in Sequencing. This PR stays as-is (behavior-neutral, green) but I am converting it to draft: the restructure is disruptive to |
|
CI on 462202f: 77/77 check-runs success (kept as draft per the restructure plan above). |
462202f to
083078d
Compare
083078d to
c836187
Compare
083078d to
511a36f
Compare
|
CI receipt at ae307f6: 84 check-runs, all success (python-lint-types incl. the comment/type guards and the relative-import rule; test-polars 3.12 coverage audit with floors for the three new polars modules; tck-gfql; gfql-benchmarks; test-gfql-core; test-pandas-compat-gfql). Local at this head: cuDF lane 1390 pass / 2 xfail; broad CPU 12592 pass (the 2 failures were the polars-lane completeness pin for the harness file, which #2061 registers); routes-off replay per mode posted in the stacked PR. The dgx cuDF + polars-gpu lane runs on the stack head (#2062, which contains this PR) once the current dgx batch finishes. |
|
Routes-off replay ledger at the harness head (bin/test-routes-off.sh, local, cuDF on; suites test_chain/test_hop/test_gfql + tests/compute/gfql, ~13.5k tests per mode):
Zero result divergences outside the filed classes (#2058 → #2062, #2059, #2034). Ledger files: reviews/2054/ on #2061's branch. tck-gfql per-route ledger (graphistry/tck-gfql#196): every mode 4143 passed / 689 xfailed, 0 masked, 0 route-only. |
|
GPU receipt (dgx-spark, graphistry/test-rapids-official:26.02-gfql-polars, cudf 26.02.01 / cupy 13.6.0 / polars 1.35.2, safe_run, TEST_CUDF=1; suites test_chain.py, test_hop.py, test_chain_alias_column_collision.py, tests/compute/gfql/**) on the stack head 676232a (#2062, which contains #2055, #2056, #2054 and #2061): 13383 passed / 93 failed / 55 skipped / 44 xfailed / 4 xpassed. The 93 failures are exactly master 86de0f5's set in the same image (13227 passed / 93 failed: the polars-gpu fused grouped-aggregate lane and strictness tests, #2064): set difference empty, zero branch-specific failures. |
157a55c to
f04fe11
Compare
ae307f6 to
70ddc36
Compare
|
GPU receipt (dgx-spark, graphistry/test-rapids-official:26.02-gfql-polars, cudf 26.02.01 / polars 1.35.2, safe_run, TEST_CUDF=1; suites test_chain.py, test_hop.py, test_chain_alias_column_collision.py, tests/compute/gfql/**) on the restacked stack head a54ac4b (#2065, containing #2055 rework, #2056, #2054, #2061, #2062): 13386 passed / 95 failed / 38 xfailed / 4 xpassed. 93 of the failures are master 86de0f5's polars-gpu fused-lane set (#2064, fixed separately in #2066). The other 2 are strict XPASS of the #2043 prune_to_endpoints markers on cuDF 26.02: the prune shapes now agree fast-vs-full on this stack (the #2062 closure fix removed the divergence), so #2062 drops that marker in a follow-up commit (tree otherwise identical; #2065 restacked). |
11ad4f4 to
6d2c654
Compare
00002b8 to
68c4ac2
Compare
c40b996 to
b19ac3c
Compare
|
GPU receipt (dgx-spark, graphistry/test-rapids-official:26.02-gfql-polars, cudf 26.02.01 / polars 1.35.2, safe_run, TEST_CUDF=1; suites test_chain.py, test_hop.py, test_chain_alias_column_collision.py, tests/compute/gfql/**) on the folded stack top 500265e (#2062, containing #2056 and #2054): 13532 passed / 7 failed / 37 xfailed / 4 xpassed, versus master 86de0f5's 13227 / 93 in the same image. Every one of the 7 is inside master's set (5 polars-gpu strictness cases and the routing-contract / indexed-bypass pins tracked on #2064 and #1803); zero branch-specific failures, and the fused-lane fix folded from #2066 clears the other 86. |
|
CI receipt at b19ac3c (folded: layout + routes-off replay + registry/harness + CI filter; includes the fold repair of the polars plain lane): 84 check-runs, all success. Local on the stack top: broad CPU + cuDF 12939 / 0; tck-gfql 4143 / 689 xfail; GPU receipt above. |
2a645a3 to
5775f76
Compare
b19ac3c to
b5ba132
Compare
|
Rebased onto the #2056 head 5775f76 → b5ba132. The one conflict was the chain.py block this PR moves out plus #2056's new helper in the same region; resolved by keeping only the helper. The PR's own product delta is line-for-line identical before and after the rebase (sorted diff: 0 lines). Gates on the new head: routes harness, both specialization packages, collision matrix, chain, scoping (609 pass / 14 xfail incl. cuDF), lane completeness 12/12, lint/mypy unchanged vs master. CI 84/84. |
The pandas/cuDF chain fast path and the polars chain's plain single-hop branches decided admission with inline checks and closures, so a test could only learn which route served a shape by spying. Each gate is now a function the dispatcher calls — `native_fast_path_admits` and `polars_plain_single_hop_admits` — and a shared route corpus (graphistry/tests/compute/gfql/routes/corpus.py) is filtered per route by that same function: the decision table is pinned per shape, served-by is asserted against the predicate on pandas and cuDF, and every admitted shape is checked against the pandas full path. No route admits or declines anything it did not before. Found while pinning: the polars plain branch admits `prune_to_endpoints` where the pandas gate declines it, and the results differ (#2053, strict expected failure). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA (cherry picked from commit 82b2f0e)
…edicate `polars_seeded_lane_admits` is the structural gate `_try_seeded_chain_polars` now calls first; frame conditions (polars frames, id dtypes, valid resident indexes, scalar filters, alias collisions) stay in the body. Pins over the shared corpus: the admitted set, the lane never serves a shape it does not admit under the real dispatch, and called directly on an indexed fixture it serves every admitted non-colliding shape and declines the colliding ones. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA (cherry picked from commit 083078d)
…edicates
The pandas/cuDF lanes (single-node, seeded typed single hop, seeded typed
RETURN-destination) move from chain.py/chain_fast_paths.py into
graphistry/compute/chain_specializations/{admission,hotpaths}.py; the polars
lanes (plain single-hop branches, seeded lane, RETURN-destination) move into
graphistry/compute/gfql/lazy/engine/polars/chain_specializations/. chain.py
and the polars chain only dispatch; chain_fast_paths.py keeps the shared
seed/index helpers. No route admits or declines anything it did not before.
Tests mirror the new module paths.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA
…blocking CI ledger GFQL_ROUTES_OFF=<route,...> (tests/conftest.py) makes named hot paths decline so every existing test replays through the other routes. Tests that assert a route serves carry @pytest.mark.route_engaged(<route>, ...) and are skipped in that mode, so bin/test-routes-off.sh reports result divergences only; the gfql-routes-off CI matrix uploads the per-route ledger without blocking. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA
…arness Each specialization's test module registers the shape table it already owns (routes corpus, the six test_chain tables, the alias-collision matrix) with its frames and defect-class tags. The harness tries every registered shape against every chain route whose admission predicate admits it and pins that the lane serves, that the answer matches the same engine's general path on values, and that node/edge sets match the pandas general path. A lane that declines an admitted shape is recorded as an expected failure (the attenuation ledger); filed divergences are strict expected failures keyed by tag. The pandas bypass table's prune shapes surface #2053 on the polars route. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA
…gement pins marked The route harness mentions polars, so it runs in bin/test-polars.sh (lane completeness pin). Six more tests that assert a native-fast serve (hits == 1, served spies) carry the route_engaged marker, so the routes-off replay for native-fast reports result divergences only. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA
…he route_engaged marker Replay at the harness head (scratchpad ledger kept under reviews/2054/): every remaining single-route id was an engagement pin (a served spy, a trace or a lane-specific explain step) or the #2058 dtype class; the all-off residue adds four combined-route engagement pins and the #2034 duplicate-id case. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA
The gfql change filter listed compute/gfql/** and the unified entrypoint but not the chain engine itself (chain.py, chain_fast_paths.py, hop.py, gfql_fast_paths.py, filter_by_dict.py, ast.py, predicates/), so a PR touching only those skipped tck-gfql, the Cypher-frontend gates and the gfql benchmark lane. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA (cherry picked from commit c402b7f)
…rule (fold repair: the inline branch the layout extracted had come back beside the dispatch)
b5ba132 to
9616f11
Compare
Post-#2056 audit of the moved code (owner ask: did the files this PR moves change in #2056?)Checked mechanically at head 9616f11 (rebased onto master f45469c):
CI on 9616f11 in progress; posted when it lands. |
Read-through of the moved code vs master f45469c (owner: "don't trust mechanical; read them")Read side by side, function by function, at head 9616f11:
Nothing from #2056 is missing and nothing moved changed meaning. CI on 9616f11: 70 green, 15 running. |
|
CI on 9616f11: 86/86 green. Head is final; ready to merge. |
…e the tree The shape corpus now runs over six frame variants (string ids, nullable ids with nulls, duplicate ids, self-loops and a cycle, an empty edge table, no edge-id binding) as well as the base frames; the harness oracle normalizes null ids, the serve-ratio pin reads the base corpus only, and known-divergence xfails are non-strict on variants where a shape can coincide. The only divergence the variants surfaced is #2034 (duplicate node ids on the single-node lookup), pinned by row. Route replay ledgers are PR comments, not tracked files. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HdYcPgiafGGutW9KfG3gg1
2efeb04 to
f5cee3b
Compare
Corpus breadth (owner: "corpus.py seems myopic") and review ledgers (head f5cee3b)The registry the harness runs is wider than corpus.py (80 shapes from four test modules, 161 route cases), but every frame was a 4–5 node graph with int64 ids and nothing structural to trip on. This head adds a frame dimension: the same 22 shapes now also run over string ids, nullable ids with nulls, duplicate ids, self-loops plus a cycle, an empty edge table, and no edge-id binding, on pandas, cuDF and polars (359 harness cases). Findings:
Still narrow, and queued in the plan rather than grown by hand here: shapes should be harvested from the main GFQL shape suites (record every op list + small frames the suites execute and replay them through the harness) instead of cherry-picked; the harness wires three routes (native-fast, polars-plain, polars-seeded), not index-hop, indexed-kernel, cypher-fast or rows; no polars-gpu engine.
|
The route-centric test flow from the #2054 review, folded into one PR at the owner's request (formerly #2054 + #2061 + #2057). Stacked on #2056.
graphistry/compute/chain_specializations/{admission,hotpaths}.py(pandas/cuDF) andgfql/lazy/engine/polars/chain_specializations/{admission,hotpaths}.py; each lane next to the admission predicate its dispatcher calls;chain.pyand the polars chain only dispatch;chain_fast_paths.pykeeps the shared seed/index helpers. Behavior-neutral. Tests mirror the paths.GFQL_ROUTES_OFF=<route,...>(tests/conftest.py) makes named hot paths decline so every existing test replays through the other routes;@pytest.mark.route_engaged(...)marks the tests that assert a route serves (skipped in that mode);bin/test-routes-off.shwrites the per-route ledger; thegfql-routes-offCI matrix publishes it non-blocking. Ledger table from the 7-mode replay: every remaining id was an engagement pin or a filed class (gfql: the general chain path upcasts integer node/property columns to float where every fast lane keeps int (route-dependent dtypes) #2058 → fix(gfql): fixes found by the route sweeps: source dtypes on every route, polars prune decline, polars-gpu fused lane (#2058, #2053, #2064) #2062, gfql polars: the general (lowered) path fails on Cypher shapes that only the fast lanes serve #2059, gfql: rows(table=nodes, source=alias) multiplies rows for duplicate node ids and joins null ids to each other #2034).tests/compute/gfql/routes/registry.py(80 shapes registered by the specialization tables themselves) andtest_route_harness.py(per admitted cell: served-by, same-engine general-path value parity, pandas general-path set oracle; declines recorded as the attenuation ledger → gfql polars: the seeded lane declines alias-collision shapes its predicate admits (29 ms full chain vs 1.9 ms pandas) #2063).Receipts on this head below; earlier receipts on the closed #2061 and #2057.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA