Skip to content

feat(gfql): add built-in standard graph algorithms - #2006

Open
lmeyerov wants to merge 9 commits into
developfrom
feat/gfql-std-procedures
Open

feat(gfql): add built-in standard graph algorithms#2006
lmeyerov wants to merge 9 commits into
developfrom
feat/gfql-std-procedures

Conversation

@lmeyerov

@lmeyerov lmeyerov commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add engine-agnostic dataframe kernels for WCC, PageRank, CDLP, weighted SSSP, and MIS
  • expose graph-write and row-returning forms through CALL graphistry.std.*
  • preserve original node IDs, explicit isolates, edge weight columns, and truthful schema effects
  • document syntax, options, engine behavior, and result status

PageRank compatibility

graphistry.std.pagerank now accepts the current cuGraph PageRank signature and defaults:

alpha=0.85
personalization=None
precomputed_vertex_out_weight=None
max_iter=100
tol=1.0e-5
nstart=None
dangling=None
fail_on_nonconvergence=True

The std implementation also keeps Graphistry extras: dataframe-engine portability (pandas on CPU or cuDF on GPU), weight, chunk sizing, stopping="fixed_iterations", legacy iterations / damping aliases, and optional converged_col. Vector inputs accept original-ID maps, record lists, and dataframe forms. dangling is accepted for signature compatibility and intentionally ignored, matching cuGraph's public contract.

This is a portable GFQL/std fallback, not a replacement for native backends on performance. Native graphistry.cugraph.* and igraph integrations remain the preferred fast path when installed.

Correctness boundaries

  • PageRank convergence uses L1 change on the unit-mass rank vector; fail_on_nonconvergence=False returns convergence status
  • SSSP source is an original node ID; unknown or incompatible IDs fail clearly
  • WCC/CDLP labels map back to original node IDs
  • MIS ignores self-loops and includes isolated nodes
  • PageRank/SSSP/MIS schema types are float64/float32/bool; WCC/CDLP remain caller-ID-dependent
  • implementations follow documented Graphalytics-style semantics, but are not official or audited LDBC Graphalytics submissions; MIS is not an official Graphalytics algorithm

Matched PageRank performance

Guarded same-dataset measurements on directed, unweighted cit-Patents (3,774,768 vertices / 16,518,947 edges), with graph preparation excluded and three timed repetitions:

backend/profile median native comparison
cuGraph 26.02, default 0.0352s baseline
std-cuDF, default 1.7796s 50.6x slower
cuGraph 26.02, tol=1e-8 0.0443s baseline
std-cuDF, tol=1e-8 2.5368s 57.2x slower
igraph 1.0 PRPACK 1.4495s baseline
std-pandas, default 6.0490s 4.17x slower
std-pandas, tol=1e-8 8.1265s 5.61x slower

Retained top-10 vertex IDs match in every comparison. Maximum retained-rank deltas are below 1e-10 for the aligned GPU profiles; igraph PRPACK does not expose a matching tolerance control. Full artifacts and reproducibility details are in graphistry/pyg-bench#193.

Validation

  • focused std/schema/docs regression: 66 passed
  • post-CI dependency-neutral PageRank kernel module: 31 passed
  • ./bin/lint.sh
  • ./bin/typecheck.sh (337 source files)
  • ./bin/check_docs_latex_unicode.sh
  • git diff --check

No Docker, GPU, dataset, or benchmark workload was run locally. All performance work ran on dgx-spark through the shared perf lock and safe_run.sh, with cgroup/RMM limits, host-memory floor, watchdog, and hard timeouts.

Closes #1975

lmeyerov and others added 3 commits August 19, 2026 06:39
… engine

Adds `graphistry/compute/algorithms/`: five graph kernels written once against
the dataframe API so they run on pandas and cudf from a single implementation,
plus `compute_std()` to bind results onto a Plottable.

These exist because no backend implements the LDBC Graphalytics semantics.
cuGraph has no label propagation and no maximal independent set — confirmed
across cugraph, nx-cugraph and pylibcugraph's 79 algorithms. igraph's label
propagation is randomized rather than deterministic. Both backends' PageRank
converges to a tolerance while LDBC fixes the iteration count.

Named `std` in the standard-library sense: no optional third-party dependency,
runs on whichever engine holds the frames. Deliberately NOT named after the
benchmark that motivated it — "Graphalytics" is LDBC's benchmark name, and
putting it in a shipped public API would imply an endorsement and conformance
audit we do not have. Conformance belongs in docs and tests, not identifiers.

Two details that are easy to get wrong:

  * The kernels need dense int32 ids 0..V-1 — that is what makes frontier
    expansion a positional gather instead of a hash join. compute_std renumbers
    and maps back, as compute_cugraph does for cuGraph's own renumbering.
  * WCC and CDLP results ARE vertex ids: the label is the minimum member id. So
    the values, not just the row order, map back to the caller's id space.
    Monotone renumbering makes dense and original labels denote the same vertex,
    but a user comparing a label to their own node ids needs the original.

Validated against independent references rather than self-consistency: networkx
for WCC, Dijkstra for SSSP (bitwise exact — integer weights in [1,255] keep every
distance exactly representable in float32), a naive Python LDBC implementation
for CDLP, a hand-written power iteration for PageRank, and independence plus
maximality invariants for MIS. The validators are themselves negative-tested
against corrupted results, since a validator that always passes makes an
unchecked number look checked. 27 tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YXm45Nzm1BgWs6iR2exufT
…ries

Wires the std backend into the Cypher CALL registry, so the engine-agnostic
kernels are reachable from a query rather than only from Python:

    CALL graphistry.std.wcc.write()
    CALL graphistry.std.cdlp.write({params: {iterations: 10}})
    CALL graphistry.std.pagerank.write({out_col: 'pr'})

This matters because these are the only implementations of two of the five
algorithms: cuGraph has no label propagation and no maximal independent set
(checked across cugraph, nx-cugraph and pylibcugraph's 79 algorithms), and
igraph's label propagation is randomized rather than LDBC-deterministic.

Five touch points, all of which the compiler needs and none of which are
inferable from the resolver alone:
  * `"std"` in the backend allowlist and in `_BACKEND`
  * a resolver branch validating against `algorithms.registry.STD_COMPUTE_ALGS`
  * an executor branch calling `compute_std`
  * `compute_std` in the call safelist (`gfql/call/validation.py`) -- without it
    the resolver succeeds and execution is refused
  * a `std` branch in `_source_value_columns`, which otherwise asserts cugraph

That last one is worth stating: the output column is the REGISTRY's name for the
algorithm, not the algorithm name. `wcc` writes `component`, `sssp` writes
`distance`. Only cuGraph and igraph have the property that column == algorithm.

Verified the query surface does not change the answer: `CALL graphistry.std.wcc`
equals the direct kernel, and labels come back in the caller's id space, since a
WCC label IS a vertex id.

Unrelated pre-existing failures in test_lowering.py (7 cudf-parametrized cases)
are unchanged: 7 failed / 1566 passed both with and without this change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YXm45Nzm1BgWs6iR2exufT
@lmeyerov
lmeyerov marked this pull request as ready for review August 21, 2026 05:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant