perf(gfql): index AUTO preserves polars frames (rework of #1767, stacked on #1743) - #1843
Draft
lmeyerov wants to merge 3 commits into
Draft
perf(gfql): index AUTO preserves polars frames (rework of #1767, stacked on #1743)#1843lmeyerov wants to merge 3 commits into
lmeyerov wants to merge 3 commits into
Conversation
…1841 show_indexes usable/reason for the flip test) # Conflicts: # CHANGELOG.md # graphistry/tests/compute/gfql/index/test_index.py
, stacked on #1743) resolve_engine(AUTO) maps polars input frames to PANDAS (legacy input-format policy), so gfql_index_all()/create_index() with the default engine coerce-and-REPLACED a user's polars frames with pandas copies: every later polars-engine query then paid a full-frame pandas->polars re-conversion (O(E) per call) and the resident pandas index could never fingerprint-match. New resolve_index_engine(): when the caller said AUTO and BOTH resident frames are eager polars, the index layer resolves POLARS and indexes the frames in place (the layer is already engine-polymorphic: numpy sidecar arrays + polars row-gather). Wired into create_index, _is_resident_index_valid, show_indexes, and the DDL wire's NODE_PROP reuse check. Explicit engines are unchanged. Why this is safe NOW and was not in 2026-07: the retracted #1767 shipped this without query-side AUTO routing, so an AUTO-built polars index met an AUTO(pandas) query -> every hop declined to the scan floor, a measured 125-534x DEFAULT-path regression. This branch is STACKED ON #1743 (fix/gfql-auto-engine-polars-native), whose gfql() guard routes AUTO on all-polars-frame graphs to the native polars engine -- so the cliff inverts into the win: AUTO builds the polars index, AUTO queries route polars, index serves. Pinned by test_inversion_auto_index_auto_gfql_serves_polars_index (index_trace() must show path=index engine=polars with NO engine argument anywhere, plus value parity vs both explicit-engine spellings). show_indexes() under AUTO now reports the routed truth through the #1841 usable/reason columns: the previously not-usable "polars index + AUTO query" combination flips to usable=True (test renamed to test_polars_index_auto_query_usable_the_1841_flip); explicit mismatched-engine previews keep the #1838 decline wording. Deliberately narrower than #1743's query gate, self-consistent either way because create_index coerces the frames it indexes: LazyFrame graphs keep the legacy pandas build (an index cannot gather from a lazy plan), and edges-only graphs keep it too (materialize_nodes() does not yet produce polars nodes from polars edges -- pre-existing gap, also reachable via explicit engine='polars'). Pandas/cuDF graphs, mixed frames, and nodes-only graphs are unchanged, each pinned. Verification (local CPU lane, cudf/gpu params env-excluded on this box): index suite 229 passed; polars cypher conformance + cache coverage lock 177 passed / 3 skipped; lint.sh clean; scoped mypy (api.py, wire.py) clean; type-hygiene guard OK (no growth). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011AB4RZpph3uSFUpzKnZJcr
# Conflicts: # CHANGELOG.md # graphistry/tests/compute/gfql/index/test_index.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
DO NOT MERGE BEFORE #1743
Stacked on #1743 (
fix/gfql-auto-engine-polars-native) and contains its commits until it lands (plus a sync merge of current master to pick up #1841'susable/reasoncolumns). Draft against master so CI runs; once #1743 merges, this rebases/retargets to a clean index-only diff. Do not land this first — the whole point of the rework is the ordering.What this is: the retracted #1767, made safe by sequencing
The retracted #1767 made
gfql_index_all()/create_index()underengine='auto'preserve resident polars frames (index in place, no pandas coerce-and-replace). It was retracted because query-side AUTO still routed polars-frame graphs to pandas, so the AUTO-built polars index met an AUTO(pandas) query and every hop declined to the scan floor — a measured 125–534x DEFAULT-path regression (250K/1M/4M edges, dgx-spark, receipts in the #1767 thread).#1743 removes the mismatch at its root:
g.gfql(...)with no engine on an all-polars-frame graph now routes to the native polars engine. With that underneath, the index-side preservation inverts from the cliff into the win:The inversion pin
test_inversion_auto_index_auto_gfql_serves_polars_index:gfql_index_all()+g.gfql(<index-served query>)with no engine argument anywhere — the exact two-default-spellings scenario that regressed — must showpath=index,engine=polarsinindex_trace(), answer in polars frames, and value-match BOTH explicit-engine spellings (polars/polars and pandas/pandas oracles).The #1841 flip
show_indexes()under AUTO now reports the routed truth: the "polars index + AUTO query" combination that #1841 was built to call out asusable=Falseflips tousable=True(test_polars_index_auto_query_usable_the_1841_flip, plus the all-default spelling intest_auto_built_index_reports_usable_under_auto). Explicit mismatched-engine previews (show_indexes(engine='pandas')against a polars index) keep #1838's decline wording, pinned per kind.Change surface (index-only; no routing — that is #1743's job, underneath)
graphistry/compute/gfql/index/api.py: newresolve_index_engine()— AUTO + both frames eager polars →Engine.POLARS; wired intocreate_index,_is_resident_index_valid,show_indexes. Explicit engines untouched.graphistry/compute/gfql/index/wire.py: the DDLCREATE INDEXNODE_PROP reuse check uses the same resolution (otherwise IF-valid reuse would mismatch the build it just did).Ported from old #1767: the
_resolve_index_enginemechanism (now also coveringshow_indexes/wire, which the old diff predated — #1841/#1838 didn't exist then), the frame-identity preservation contract, and the whole negative-pin test suite (LazyFrame, mixed-frame, nodes-only, edges-only, pandas-stays-pandas, cudf-stays-cudf, explicit-engine-still-coerces, index-service spy with pandas-oracle parity).Dropped from old #1767: its 276x standalone-perf framing (the thread showed the explicit-engine fast path was already reachable on master; the honest claim here is default-path consistency), and its CHANGELOG entry (rewritten as behavior-only with the landing-order dependency stated).
Gate deliberately narrower than #1743's query gate, self-consistent because
create_indexcoerces the frames it indexes (later AUTO queries route with the post-build frames): LazyFrame graphs keep the legacy pandas build (an index cannot gather rows from a lazy plan); edges-only graphs too —materialize_nodes()does not yet produce polars nodes from polars edges, a pre-existing gap also reachable via explicitengine='polars'on such a graph (probed empirically; guessing polars there would crash the build).Known interaction left to #1743's review: on cudf-frame graphs, #1743's polars-gpu arm can route an AUTO query away from a resident cudf index (GPU lanes only); index resolution here deliberately leaves cuDF source-native and takes no position on that arm.
Tests (CPU lane, this box: cudf/gpu params env-excluded — no GPU libs)
graphistry/tests/compute/gfql/index/: 229 passed (83 deselected cudf/gpu)test_index.pyalone: 142 passed; new/changed: inversion pin, fix(gfql): show_indexes reports engine usability, not just fingerprint validity #1841 flip x2, DDLSHOW GFQL INDEXESflip, per-kind reason under explicit mismatch, + the portedTestIndexAutoPreservesPolarsFrames(11 tests)bin/lint.shclean · scoped mypy (api.py,wire.py) clean · type-hygiene guard OK (no growth)libnvrtcOSError(master's owntest_cudf_index_pandas_query_not_usablefails identically here); they are GPU-lane tests.🤖 Generated with Claude Code
https://claude.ai/code/session_011AB4RZpph3uSFUpzKnZJcr