@@ -354,6 +354,42 @@ never selects Polars or Polars-GPU**, so those two are always an explicit opt-in
354354 ``g.gfql_index_all() `` (or ``index_policy= ``) — it works on all four engines
355355 and turns the O(E) scan into an O(degree) gather. See :doc: `index_adjacency `.
356356
357+ .. _gfql-offengine-calls :
358+
359+ Analytics under Polars (``umap `` / ``hypergraph `` / ``compute_cugraph `` …)
360+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
361+
362+ A GFQL ``call() `` that runs a **whole-graph analytic ** — ``umap ``, ``hypergraph ``,
363+ ``compute_cugraph `` / ``compute_igraph ``, the ``*_layout `` ops, ``collapse `` — has
364+ **no native Polars implementation ** (these wrap pandas / cuDF / GPU libraries and
365+ always will). Under ``engine='polars' `` / ``'polars-gpu' `` GFQL runs them as a
366+ **mode-gated, off-engine modality switch ** rather than declining outright:
367+
368+ - **``call_mode='auto'`` (the default): ** the analytic runs off-engine — on
369+ **pandas ** for ``polars ``, on **cuDF (on device) ** for ``polars-gpu `` — and its
370+ result is coerced back to Polars **losslessly ** (via Arrow). A one-time
371+ ``RuntimeWarning `` per analytic notes the off-engine run. ``polars-gpu `` is
372+ **GPU-or-error **: it bridges to cuDF and *declines * if the GPU/cuDF stack is
373+ missing (it never silently drops a GPU analytic to host pandas).
374+ - **``call_mode='strict'``: ** decline with ``NotImplementedError `` instead of
375+ bridging — for benchmark integrity (no hidden modality switch attributed to the
376+ Polars engine) or a hard memory ceiling.
377+
378+ This is **deliberately narrower ** than traversal / filter / row ops (``hop ``,
379+ ``WHERE ``, ``RETURN `` …), which stay **parity-or-``NotImplementedError`` ** and are
380+ never bridged — a bridge there would hide a missing native impl and misreport
381+ pandas performance as Polars. Set the mode from Python or the environment (live,
382+ Python override > env > default):
383+
384+ .. doc-test: skip
385+
386+ .. code-block :: python
387+
388+ from graphistry.compute.gfql.lazy import set_call_mode, CALL_MODES # ('auto', 'strict')
389+
390+ set_call_mode(' strict' ) # decline off-engine analytics (pass None to reset to env/default)
391+ # or: export GFQL_POLARS_CALL_MODE=strict
392+
357393 cuDF vs Polars-GPU
358394------------------
359395
@@ -479,11 +515,18 @@ Parity and honesty
479515- **Identical results across engines. ** Differential parity — every engine's output must match
480516 the pandas oracle — is a release gate, exercised across forward/reverse/undirected, 1-3 hop,
481517 filters, and aggregations.
482- - **No silent fallback — parity-verified. ** The Polars engine runs natively or raises
483- ``NotImplementedError `` — it never quietly converts to pandas. ``polars-gpu `` is
484- **GPU-or-error **: if any step of the plan cannot run on the GPU it raises (pointing at
485- ``engine='polars' ``) rather than silently running on CPU and labelling it a GPU result.
486- So any latency you measure is real work on the engine you asked for.
518+ - **No silent fallback for traversal / filter / row ops — parity-verified. ** For ``hop `` /
519+ ``WHERE `` / ``RETURN `` / aggregation, the Polars engine runs natively or raises
520+ ``NotImplementedError `` — it never quietly converts to pandas, so a *traversal * latency you
521+ measure is real work on the engine you asked for. ``polars-gpu `` is **GPU-or-error **: if any
522+ step of the plan cannot run on the GPU it raises (pointing at ``engine='polars' ``) rather than
523+ silently running on CPU and labelling it a GPU result.
524+ - **Whole-graph analytics are the one mode-gated exception. ** ``umap `` / ``hypergraph `` /
525+ ``compute_cugraph `` and friends have no Polars kernel; under ``call_mode='auto' `` (default)
526+ they run off-engine and warn once (see
527+ :ref: `Analytics under Polars <gfql-offengine-calls >`). This is *not * silent — it warns — and
528+ ``call_mode='strict' `` restores strict parity-or-decline for benchmark integrity, so a
529+ benchmarked run can guarantee no hidden modality switch.
487530
488531Methodology
489532-----------
0 commit comments