fix(gfql): one op-list alias-shadowing contract on every engine (#2049, #2039 polars edge side) - #2055
Conversation
…olumn is served on pandas and cuDF (#2049) The eager half of the #2039 class: for a non-simple step (hops=2, to_fixed_point) the backward pass re-executed the reversed hop on the forward step's edges, where the alias marker had already replaced the column the step's own edge_match filters on, so the filter saw booleans and raised incompatible-column-type. The backward re-execution now reads the graph's edge columns for that step's edge ids when the alias collides, so the eager engines return the same rows as polars and as the same chain with a non-colliding alias. Polars keeps its pinned alias-scoping contract for the colliding column (user values stay, marker beside them); the collision matrix's multi-hop pins compare rows, not column contents, and flip from strict expected failure to green here. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA
3cc3846 to
3312f00
Compare
|
READY (3312f00, on master b8f3e99) — CI 69/69 non-skipped check-runs success; the 8 skipped lanes are the gfql path filter (chain.py is outside it; #2057 proposes the fix). tck-gfql run locally from its current main (66fee10, the ref CI would use) against this head: exit 0 — 3658 scenarios, 2964 supported, 1977 translated-supported, 689 xfail (baseline), direct-Cypher non-validation debt 0, no outcome drift. Local suites: 606 passed / 9 xfailed across collision matrix, chain, hop, alias-scoping, residual-polars, hop-scaling and native-seed with the cuDF lane; guards OK; mypy = master; ruff clean. |
…takes the name, the values keep the restore column The polars chain used to keep a colliding user edge column under the alias and leak the marker as a `_right` join suffix, so op-list results differed by engine. Now the marker shadows the column on every engine, and the shadowed values stay under the internal restore column the row pipeline resolves, so Cypher still reads the user value through the variable on polars as on pandas (rows route resolver and the binding-rows payload prefer the restore column). Pins: the collision matrix compares the marker values across engines and rejects join suffixes; the polars 2039 file pins the single- and two-hop edge alias; the polars residual for `type.type` flips to parity. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA
|
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). |
|
CI receipt at 510dcc6 (rework with the unified alias-shadowing contract): 72 check-runs success, 5 path-skipped as whole jobs (tck-gfql, test-polars ×6, test-gfql-core, gfql-benchmarks and the guards green). Broad local suites 12557 pass; GPU receipt on the stack head above. |
Closes #2049. Two commits.
Contract (the surface decides, not the engine). Op-list chains: an alias named like a column of the frame it marks becomes the boolean marker under that name, on pandas, cuDF and polars alike (shadowing). Cypher: the variable keeps reading the user value through its properties on every engine (
MATCH (a)-[type:K]->(b) RETURN type.typeanswers the user values), because the shadowed values ride under the internal restore column that the row pipeline already resolves.Commit 1 (eager multi-hop, #2049). A non-simple step (
hops=2,to_fixed_point) whose edge alias names the column its ownedge_matchfilters on had the chain's backward pass re-read the marker instead of the user values; the backward re-execution now runs on the graph's own edge rows for the step's ids (_step_with_source_edge_columns).Commit 2 (polars edge side). The polars chain kept the user values under the alias and leaked the marker as a
type_rightjoin suffix (_combine_edgesjoined the marker frame onto the original edges). Now the marker takes the name and the user column moves toshadow_restore_column(alias); the polars rows-route resolver and the binding-rows payload prefer that column foralias.alias, and the rows-route restore short-circuits when it is present. No_rightcolumns.Pins.
test_chain_alias_column_collision.py: every served single-hop shape and both multi-hop shapes compare the marker values across pandas/cuDF/polars and reject join suffixes..../polars/test_chain_alias_column_collision_2039.py: single-hop and two-hop edge alias marker equals pandas'.test_alias_scoping_semantics.py: the polars residual (type.typeraised a typed decline) flips to parity with pandas; the other 14 scoping pins unchanged (Cypher reads user values on both engines).Visible change. On polars, an op-list result whose alias shadowed an edge column now carries the marker under the alias and the user values under
__gfql_shadow_restore__<alias>__instead of<alias>+<alias>_right. Stated in CHANGELOG.Receipts: collision + 2039 + scoping + polars seed-resolution suites 222 pass / 9 xfail (cuDF on); guards + mypy green; broad suites running, dgx lane on the stack head to follow.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QztW7jYsDd66e8rb8pJNQA