Skip to content

Commit ba6e499

Browse files
authored
Merge branch 'master' into perf/gfql-fused-tolower-one-sided
2 parents 648d222 + b6181d3 commit ba6e499

25 files changed

Lines changed: 4534 additions & 4216 deletions

CHANGELOG.md

Lines changed: 11 additions & 2 deletions
Large diffs are not rendered by default.

benchmarks/gfql/bench_ladybug_cypher.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
#!/usr/bin/env python3
22
"""FAIR GFQL-vs-Ladybug: run Ladybug's benchmark ops as GFQL Cypher MATCH...RETURN
3-
(the row pipeline), NOT dataframe shortcuts. Compares against LadybugDB's PUBLISHED
4-
5M/20M numbers (their figures, their hardware — a cross-machine comparison; treat
5-
ratios as indicative). Ladybug best (5M/20M, ms): full_scan 3789, range 7.5,
6-
point 0.3, count 3.3, out_degree100 59.8, scan_rel_props 15722,
7-
scan_rel_rowid 14562. Kuzu count 46.
3+
(the row pipeline), NOT dataframe shortcuts. Op shapes follow
4+
https://github.com/LadybugDB/kuzu-ladybug-benchmark.
5+
6+
The `LADYBUG` reference column below is a MEASUREMENT, not a literal: LadybugDB 0.18.1,
7+
embedded, on the same host and in the same session as the GFQL arm, on the same generated
8+
5M-node / 20M-edge graph, warm medians (2 warmups + 5 timed runs, median across two
9+
position-balanced slots), timed at Ladybug's FASTEST result-producing scope of the four
10+
measured (zero-copy Arrow / polars / pandas / row iterator) -- Arrow for every scan,
11+
the row iterator for the single-row point lookup -- because GFQL returns a materialized
12+
columnar frame and choosing a slower scope for the competitor is not a comparison. Result values were digest-identical
13+
across the two engines on every op. Runner, raw JSON and full provenance:
14+
pyg-bench `benchmarks/ladybug_scan/` and `results/ladybug-scan-20260728/`.
15+
16+
Numbers here are from dgx-spark (GB10); on another box, re-run the pyg-bench lane rather
17+
than reusing these.
818
919
FAIRNESS: each engine is benchmarked on a graph built in ITS OWN NATIVE frame type
1020
(pandas/polars/cuDF), built ONCE outside the timing loop. An earlier version built the
@@ -49,8 +59,9 @@ def build(engine):
4959
"scan_rel_props": "MATCH (a)-[o]->(b) RETURN a.id, b.id, o.since",
5060
"scan_rel_rowid": "MATCH (a)-[r]->(b) RETURN a.id, b.id",
5161
}
52-
LADYBUG = {"full_scan": 3789, "range": 7.5, "point": 0.3, "count": 3.3,
53-
"scan_rel_props": 15722, "scan_rel_rowid": 14562}
62+
# LadybugDB 0.18.1, dgx-spark, 2026-07-28, Arrow materialization (see module docstring).
63+
LADYBUG = {"full_scan": 364.28, "range": 7.57, "point": 0.30, "count": 3.08,
64+
"scan_rel_props": 1925.61, "scan_rel_rowid": 1449.13}
5465

5566
def _size(res):
5667
df = getattr(res, "_nodes", None)

bin/test-polars.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ POLARS_TEST_FILES=(
2626
graphistry/tests/compute/gfql/test_engine_polars_chain.py
2727
graphistry/tests/compute/gfql/test_engine_polars_row_pipeline.py
2828
graphistry/tests/compute/gfql/test_engine_polars_binding_rows.py
29+
# engine-parametrized (pandas/polars/cudf/polars-gpu); the pandas params also run in
30+
# test-gfql-core, but only this lane has polars installed
31+
graphistry/tests/compute/gfql/test_varlen_bounded_engine_parity_1787.py
2932
graphistry/tests/compute/gfql/test_engine_polars_with_match_reentry.py
33+
# engine-parametrized: its pandas params run in test-gfql-core, but the polars params
34+
# only ever run here (the file has no module-level importorskip, so nothing else flags it)
35+
graphistry/tests/compute/gfql/test_exec_context_scoping.py
3036
graphistry/tests/compute/gfql/test_engine_polars_cypher_conformance.py
3137
graphistry/tests/compute/gfql/test_engine_polars_conformance_matrix.py
3238
graphistry/tests/compute/gfql/test_polars_string_predicate_nonstring.py
@@ -38,6 +44,7 @@ POLARS_TEST_FILES=(
3844
graphistry/tests/compute/gfql/test_seeded_typed_hop_fastpath.py
3945
graphistry/tests/compute/gfql/test_residual_polars_native.py
4046
graphistry/tests/compute/gfql/cypher/test_grouped_aggregate_fused_polars.py
47+
graphistry/tests/compute/gfql/cypher/test_grouped_aggregate_lowcard_count.py
4148
# module-level `importorskip("polars")` files that previously ran in no lane at all
4249
graphistry/tests/compute/gfql/test_engine_polars_narrow_combine.py
4350
graphistry/tests/compute/gfql/test_engine_polars_semi_key_dedup.py

docs/.rstcheck.cfg

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ ignore_roles =
2020
py:attr,
2121
py:exc,
2222
py:obj,
23-
py:data
23+
py:data,
24+
bench,
25+
bench-diag
2426

2527
# Ignore Sphinx-specific directives
2628
ignore_directives =
@@ -43,7 +45,9 @@ ignore_directives =
4345
glossary,
4446
productionlist,
4547
include,
46-
graphviz
48+
graphviz,
49+
bench-provenance,
50+
bench-disclosures
4751

4852
# Ignore common informational messages
4953
ignore_messages = (Hyperlink target "[^"]*" is not referenced\.$)
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
{
2+
"contract_version": 1,
3+
"title": "pyg-bench published docs numbers",
4+
"summary": [
5+
"The boundary between this repository and the pygraphistry docs. pyg-bench MEASURES and",
6+
"PUBLISHES; pygraphistry pretty-prints. Everything a consumer is allowed to assume about",
7+
"published/docs-numbers.json is written down here, checked on the producer side before the",
8+
"artifact is written, and re-verified independently on the consumer side before it renders.",
9+
"Two independent implementations of these rules is the point: a shared library would fail",
10+
"the same way on both sides of the boundary."
11+
],
12+
"artifact_path": "published/docs-numbers.json",
13+
"top_level_required": [
14+
"contract_version",
15+
"schema_version",
16+
"generated_by",
17+
"generated_at",
18+
"policy",
19+
"runs",
20+
"cells"
21+
],
22+
"policy_required": ["max_age_days", "managed_docs"],
23+
"run_required": [
24+
"measured_at",
25+
"host",
26+
"perf_lock_held",
27+
"quiet_host",
28+
"reps",
29+
"pygraphistry_commit",
30+
"pyg_bench_commit",
31+
"runtime",
32+
"dataset",
33+
"artifact",
34+
"row_validation"
35+
],
36+
"cell_required": [
37+
"run",
38+
"workload",
39+
"engine",
40+
"value",
41+
"unit",
42+
"decimals",
43+
"status",
44+
"comparison_allowed",
45+
"board_quotable",
46+
"disclosures"
47+
],
48+
"cell_status": ["ok", "partial", "adapter_workaround", "result_mismatch"],
49+
"cell_unit": ["ms", "s", "x"],
50+
"key_pattern": "^[a-z0-9][a-z0-9_]*(\\.[a-z0-9][a-z0-9_]*)+$",
51+
"date_pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$",
52+
"max_decimals": 4,
53+
"invariants": [
54+
{
55+
"id": "cell_names_a_known_run",
56+
"rule": "every cell.run must be a key of runs",
57+
"why": "a number whose provenance record is absent is an unprovenanced number"
58+
},
59+
{
60+
"id": "quotable_implies_comparable",
61+
"rule": "board_quotable implies comparison_allowed",
62+
"why": "a diagnostic-only figure must never reach a board"
63+
},
64+
{
65+
"id": "quotable_implies_ok",
66+
"rule": "board_quotable implies status == 'ok'",
67+
"why": "partial / adapter_workaround / result_mismatch cells are not results"
68+
},
69+
{
70+
"id": "caveat_travels_with_the_number",
71+
"rule": "status != 'ok' or comparison_allowed is false implies disclosures is non-empty",
72+
"why": "a bare ratio without its asterisk launders the caveat"
73+
},
74+
{
75+
"id": "ratios_are_comparisons",
76+
"rule": "unit 'x' implies comparison_allowed",
77+
"why": "a ratio between two numbers that were never established as comparable is not a speedup"
78+
},
79+
{
80+
"id": "value_is_rounded_as_published",
81+
"rule": "value == round(value, decimals), value is finite and >= 0, 0 <= decimals <= max_decimals",
82+
"why": "the consumer prints value verbatim; rounding must not happen at render time"
83+
},
84+
{
85+
"id": "engine_is_named",
86+
"rule": "cell.engine is a non-empty string",
87+
"why": "pandas and polars are not interchangeable and the reader must be told which one ran"
88+
},
89+
{
90+
"id": "measurement_is_dated",
91+
"rule": "run.measured_at matches date_pattern",
92+
"why": "staleness is checked against the MEASUREMENT date, not the export date"
93+
}
94+
],
95+
"consumer_obligations": [
96+
"Refuse to render a key the artifact does not contain — a missing number is a build failure, never blank text.",
97+
"Refuse to render a cell whose run.measured_at is older than policy.max_age_days.",
98+
"Refuse to render a non-board_quotable cell as a bare number; it may appear only through a role that labels it diagnostic-only.",
99+
"Render every referenced cell's disclosures and its run's provenance on the same page.",
100+
"Re-verify every invariant above before rendering, with an implementation that does not import this repository."
101+
]
102+
}

0 commit comments

Comments
 (0)