-
Notifications
You must be signed in to change notification settings - Fork 231
feat(gfql): native lazy Polars engine — collect-once traversals + cypher row pipeline + followups (multi-hop, to_fixed_point, undirected, more predicates, NIE→native) #1660
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
115 commits
Select commit
Hold shift + click to select a range
21ca7da
feat(gfql/polars): native lazy Polars engine, reconciled onto #1656 s…
lmeyerov d485b54
perf(gfql/polars): chain combine as one lazy collect-once over materi…
lmeyerov df82ebf
perf(gfql/polars): node-only MATCH fast path (skip traversal machinery)
lmeyerov f803534
perf(gfql/polars): unconstrained 1-hop fast path (all edges + endpoints)
lmeyerov b8f1365
perf(gfql/polars): generalize fast path to filtered single-hop
lmeyerov b03f1e3
perf(gfql/polars-gpu): use cudf-polars in-memory executor (stable + f…
lmeyerov 4f8916e
docs(gfql): regression-guard the absent-entity text fallback (#1650)
lmeyerov 7bc6cb3
fix(gfql/polars): UNION DISTINCT crash — engine-aware df_unique (not …
lmeyerov f528e16
fix(gfql/polars): 3-valued boolean over null literals (AND/OR/NOT cas…
lmeyerov 65cab86
fix(gfql/polars): honest NIE on heterogeneous (mixed-type) columns, n…
lmeyerov e631bff
fix(gfql/polars): honest NIE on temporal-constructor-string property …
lmeyerov 20dffc4
fix(gfql/polars): honest NIE on temporal arithmetic (duration literal…
lmeyerov 1fb2880
fix(gfql/polars): remove predicate pandas-bridge (NO-CHEATING) + wide…
lmeyerov a1279f6
fix(gfql/polars): engine-aware WITH-scalar MATCH re-entry (no pandas …
lmeyerov 11a28a4
fix(gfql/polars): honest NIE on OPTIONAL MATCH null-fill alignment (n…
lmeyerov c3f6992
fix(gfql/polars): OPTIONAL MATCH absent-entity renders null, not '()'
lmeyerov 346b81d
fix(gfql/polars): label match on labels List column via list.contains…
lmeyerov 23f26ea
fix(gfql/polars): IEEE NaN comparison semantics + numeric-vs-string NIE
lmeyerov ecc3794
fix(gfql/polars): honest NIE on ISO temporal comparison (not lexicogr…
lmeyerov b6da5d5
fix(gfql/polars): adversarial-review fixes — NaN/cross-type/List/temp…
lmeyerov f83f684
fix(gfql): lower-layer review — polars chain dtype-join crash + chain…
lmeyerov b4a10c2
fix(gfql/polars): adversarial L2 review — dup-alias, int-div, seed-dt…
lmeyerov f06e05c
test(gfql/polars): absent-entity null assertion robust to polars vers…
lmeyerov 8577f4f
refactor(gfql/polars): home the polars backend under lazy/engine/pola…
lmeyerov ad85ac0
feat(gfql/lazy): polars-gpu = GPU execution target of the lazy engine
lmeyerov 23e2b9a
perf(gfql/polars): opt-in CPU streaming collect for large traversals …
lmeyerov 579b86e
test(gfql): cover CPU lazy-collect + polars engine-helper branches; p…
lmeyerov c5bc2fd
fix(gfql/polars-gpu): raise on GPU-incapable node, never silent CPU f…
lmeyerov fe8218c
garden(gfql/polars): team-polish — stale paths, user-facing messages,…
lmeyerov a18c594
fix(gfql/polars): Contains honors regex=/flags=, not just case= (B1)
lmeyerov 15ac696
fix(gfql/polars): clean "library not installed" errors for engine='po…
lmeyerov 2a5c544
feat(gfql/polars-gpu): GFQL_POLARS_GPU_EXECUTOR flag (in-memory defau…
lmeyerov 0784a98
fix(gfql/polars): 3 correctness defects from the NIE audit (no-silent…
lmeyerov ff2c96e
test(gfql/polars): generative cross-surface conformance matrix (Phase…
lmeyerov e58814e
test(gfql/polars): conformance matrix v2 — all 4 engines + hot-path c…
lmeyerov 1364e87
feat(gfql/polars): native sqrt / sign scalar fns + count(DISTINCT) ag…
lmeyerov 0fbd0cb
feat(gfql/polars): native CASE WHEN + Match/Fullmatch predicates
lmeyerov cc8a69e
feat(gfql/polars): native tuple-pattern Startswith/Endswith (OR-fold)
lmeyerov 7d33914
feat(gfql/polars): native get_degrees + with_(extend=True) + DateValu…
lmeyerov cb79f25
feat(gfql/polars): native list literals + x IN [literals] + IsLeapYea…
lmeyerov c71af85
refactor(gfql/polars): fix 2 rename stragglers (new engine_polars ref…
lmeyerov 66ce62d
feat(gfql/polars): native get_indegrees/get_outdegrees + size/substri…
lmeyerov 529e443
feat(gfql/polars): native toInteger / toBoolean / toString (provable …
lmeyerov 5d0f31f
test(gfql/polars): Stage 0 — amplify fuzz to exercise multi-hop (skip…
lmeyerov 4361afb
feat(gfql/polars): native fixed-length multi-hop chain (hops=N / max_…
lmeyerov aee2ce6
test(gfql/polars): cross-surface multi-hop — DAG let() mirror of trav…
lmeyerov 0fd391e
test(gfql/polars): coverage-ledger AXIS 2 — cypher scalar functions
lmeyerov e8b3ea0
test(gfql/polars): coverage-ledger AXIS 3 — call() safelist
lmeyerov ddacd25
test(gfql/polars): coverage-ledger AXIS 4 — row-pipeline ops (rollout…
lmeyerov c65fd6e
feat(gfql/polars): native single-hop undirected edges in multi-edge c…
lmeyerov 42b84cf
feat(gfql/polars): native forward/reverse to_fixed_point (unbounded m…
lmeyerov 0508d55
feat(gfql/polars): native fixed-length undirected multi-hop (last mul…
lmeyerov 9cc21df
feat(gfql/polars): native temporal Between (DateValue bounds); IsIn s…
lmeyerov 3e84a36
test(gfql/polars): conformance for native unwind (literal list) + hon…
lmeyerov fe4ce06
test(gfql/polars): coverage-ledger closures — 10 row-ops + scalar EQ/…
lmeyerov 509f0e3
test(gfql/polars): scope group_by row-op to polars-vs-pandas (orthogo…
lmeyerov 3d3ef08
test(gfql/polars): lock undirected-single + directed-multihop mixed q…
lmeyerov 62a9391
feat(gfql/polars): native date-part boundary predicates (IsMonthStart…
lmeyerov 73dbab6
fix(gfql/cudf): 3 cross-engine row-pipeline divergences (issue #1663 …
lmeyerov cb9744e
feat(gfql/polars): native forward/reverse min_hops>1 — 500/500 fuzz p…
lmeyerov 439cf66
fix(gfql): ne()/<> on NULL follows openCypher/SQL 3-valued logic
lmeyerov d9e7f2f
fix(gfql): membership filter / IN on NULL follows openCypher 3VL (cuDF)
lmeyerov 9935583
refactor(gfql/polars): de-dup eager+lazy hop setup into shared helpers
lmeyerov 960e476
feat(gfql/polars): native toFloat, collect/collect_distinct, WHERE IN…
lmeyerov 3bfe641
fix(gfql): gate DAG NIE pass-through to polars; skip polars conforman…
lmeyerov 3c20c50
feat(gfql): count(*) short-circuit — O(1) count_table op, no full-fra…
lmeyerov 6ea6897
test(gfql): pandas-lane coverage for count(*) short-circuit (count_ta…
lmeyerov 85e1ba6
garden(gfql/polars): followups team-polish — guard doc, dead guards, …
lmeyerov ba8d360
test(gfql): count_table frame-op unit coverage + per-file floor regen…
lmeyerov fda318e
Merge remote-tracking branch 'origin/master' into dev/gfql-polars-engine
lmeyerov 66ae4b5
ci(polars): DRY the polars test file list into bin/test-polars.sh
lmeyerov 481c2cc
fix(gfql/polars): convert cuDF to polars via Arrow, not a pandas detour
lmeyerov 7266ba0
fix(gfql/polars): honor validate/warn convention in _pl_from_pandas
lmeyerov 48b4c93
fix(gfql): unify _cudf_from_pandas_best_effort onto validate/warn con…
lmeyerov eb705c3
refactor(gfql/cypher): drop getattr no-op on declared Plottable attrs
lmeyerov d3152f6
refactor(gfql/polars): move degree helpers out of chain.py into degre…
lmeyerov 3e3ecb2
refactor(gfql/row): drop getattr no-op on declared Plottable attrs (p…
lmeyerov d016389
fix(gfql/polars): import degree helpers into chain.py after relocation
lmeyerov 17e7087
refactor(gfql/cypher): remove verified-redundant cast() no-ops
lmeyerov 32ecdb0
refactor(gfql/polars): type dtypes.py params (pl.DataType, not Any)
lmeyerov 9f9c516
feat(gfql/polars): make CPU-streaming/GPU-executor config Python-sett…
lmeyerov 88346bc
refactor(gfql/polars): type row_pipeline expr/dtype helpers (Any -> p…
lmeyerov d272132
refactor(gfql/polars): type row_pipeline lowering return types (-> Op…
lmeyerov aca792b
refactor(gfql/polars): type cypher-AST lowering params + lazy config;…
lmeyerov ee34387
refactor(gfql): drop getattr no-op on declared Plottable attrs (gfql_…
lmeyerov 6c38c15
docs(changelog): accuracy + completeness pass for the polars engine PR
lmeyerov dadc2b8
fix(gfql/polars): nan_to_null native-polars input (parity with pandas…
lmeyerov 85d082c
refactor(gfql/polars): projection.py getattr->direct + type projectio…
lmeyerov 1b541d1
refactor(gfql): DRY polars-frame detection into Engine.is_polars_df/a…
lmeyerov a48ce41
fix(gfql/polars): in-query NaN aggregation matches pandas skipna
lmeyerov 5edf027
fix(gfql/polars): NIE-decline Rust-incompatible regex + non-homogeneo…
lmeyerov 37a5c68
docs(gfql/polars): note streaming/executor config is process-global (…
lmeyerov 7ac31ca
fix(types): correct Between._normalize_value return type (own the myp…
lmeyerov cec3a92
fix(gfql/polars): NIE-decline Boolean modulo to match pandas
lmeyerov af7f0ad
test(gfql): add polars-lane per-file coverage floors (gfql-polars aud…
lmeyerov 6690a3f
test(gfql): conservative polars coverage floors + surface audit repor…
lmeyerov 022e652
feat(gfql/polars): off-engine call() modality policy (call_mode auto/…
lmeyerov 451743f
docs(changelog): off-engine call() modality policy (call_mode auto/st…
lmeyerov 5ee9f3a
refactor(gfql/polars): type predicates.py + explicit lazy __all__ (P1…
lmeyerov 00fcba6
refactor(gfql/row): type frame_ops ctx via RowPipelineCtx Protocol (P…
lmeyerov f0a287a
fix(gfql/polars): materialize_nodes edges-only + df_to_engine LazyFra…
lmeyerov c2c8cf9
docs(gfql): maintenance note on the cuDF toString(float) host round-t…
lmeyerov aa8c3e5
test(gfql/polars-gpu): GPU-gated parity for both executor modes + 'au…
lmeyerov 4db768a
refactor(gfql/polars): comparison-op whitelists replace 6-way operato…
lmeyerov 8ae3195
refactor(gfql/polars): merge select/with_columns bodies, hoist pl imp…
lmeyerov 2424f45
refactor(gfql/polars): shared endpoint_ids() replaces 8 endpoint-conc…
lmeyerov 4ebb43d
refactor(gfql/polars): degree-dispatch getattr + shared _endpoint_cou…
lmeyerov 1a1061c
refactor(gfql): shared _alias_true_mask + is_polars_df consistency (c…
lmeyerov 1f53e71
refactor(gfql/tests): conformance ledger — 4 hand-copied axis blocks …
lmeyerov 38ad32e
refactor(gfql/tests): matrix clone families -> declarative tables + s…
lmeyerov 33908cd
refactor(gfql/tests): chain/hop graph-shape helpers -> shared utils +…
lmeyerov f9fa0c2
docs(gfql/polars): review-nit fixes — honest % comment, endpoint_ids …
lmeyerov f862657
docs(gfql/polars): prose pass — tighter comments/docstrings, zero cod…
lmeyerov b956d11
refactor(gfql/polars): unify the lazy single-hop into hop_polars — on…
lmeyerov 7acdd41
types(gfql/polars): annotate hop_eager module helpers (review)
lmeyerov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
Large diffs are not rendered by default.
Oops, something went wrong.
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| #!/usr/bin/env python3 | ||
| """Benchmark the native polars GFQL row pipeline vs pandas for cypher queries. | ||
|
|
||
| Phase 2 of the polars engine enables cypher RETURN / LIMIT / SKIP / DISTINCT / | ||
| single-entity WHERE on ``engine='polars'`` (before this increment these raised | ||
| NotImplementedError on polars). The heavy traversal + frame ops (filter, dedup, | ||
| slice) run natively in polars; only the final row-wise entity-text projection is | ||
| host-bridged to pandas. So polars wins most where a row op reduces the set | ||
| before projection (LIMIT, selective WHERE, DISTINCT), and is closest to neutral | ||
| on a full-table whole-entity RETURN (projection dominates, bridge roundtrip). | ||
|
|
||
| Reports median latency and the polars speedup (pandas_ms / polars_ms; > 1 means | ||
| polars wins). On a shared host, interleave is implicit (pandas then polars | ||
| back-to-back per query); for regression-grade claims run several times and | ||
| compare distributions (see plans/gfql-polars-engine memory). | ||
|
|
||
| Example:: | ||
|
|
||
| python benchmarks/gfql/cypher_row_pipeline.py --runs 7 --warmup 2 \ | ||
| --sizes 10000,100000,1000000 --output /tmp/cypher-row.md | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import argparse | ||
| import statistics | ||
| import time | ||
| from dataclasses import dataclass | ||
| from typing import Callable, List, Optional, Tuple | ||
|
|
||
| import numpy as np | ||
| import pandas as pd | ||
|
|
||
| import graphistry | ||
|
|
||
| # (name, cypher) — exercised on both engines via g.gfql(cypher, engine=...) | ||
| # Native = frame ops (rows/limit/skip/distinct) run in polars; Bridged = the | ||
| # cypher expression engine (select/order_by/group_by) runs host-bridged to pandas. | ||
| WORKLOADS: List[Tuple[str, str]] = [ | ||
| # native frame-op path | ||
| ("RETURN n LIMIT 10", "MATCH (n) RETURN n LIMIT 10"), | ||
| ("RETURN n SKIP/LIMIT", "MATCH (n) RETURN n SKIP 5 LIMIT 100"), | ||
| ("WHERE > RETURN LIMIT", "MATCH (n) WHERE n.score > 90 RETURN n LIMIT 50"), | ||
| ("RETURN DISTINCT n", "MATCH (n) RETURN DISTINCT n"), | ||
| ("WHERE > RETURN n", "MATCH (n) WHERE n.score > 50 RETURN n"), | ||
| ("RETURN n (full)", "MATCH (n) RETURN n"), | ||
| ("rel RETURN m LIMIT", "MATCH (n)-[e]->(m) RETURN m LIMIT 100"), | ||
| # host-bridged expression path | ||
| ("select n.score", "MATCH (n) RETURN n.score"), | ||
| ("select 2 cols", "MATCH (n) RETURN n.score, n.kind"), | ||
| ("order_by", "MATCH (n) RETURN n.score ORDER BY n.score DESC"), | ||
| ("where+select+limit", "MATCH (n) WHERE n.score > 50 RETURN n.score ORDER BY n.score LIMIT 100"), | ||
| ("group_by count", "MATCH (n) RETURN n.kind, count(n) AS c"), | ||
| ] | ||
|
|
||
|
|
||
| @dataclass | ||
| class ResultRow: | ||
| workload: str | ||
| n_nodes: int | ||
| n_edges: int | ||
| pandas_ms: Optional[float] | ||
| polars_ms: Optional[float] | ||
| error: Optional[str] = None | ||
|
|
||
| @property | ||
| def speedup(self) -> Optional[float]: | ||
| if self.pandas_ms and self.polars_ms: | ||
| return self.pandas_ms / self.polars_ms | ||
| return None | ||
|
|
||
|
|
||
| def make_graph(n_nodes: int, n_edges: int, seed: int = 0): | ||
| rng = np.random.default_rng(seed) | ||
| nodes = pd.DataFrame({ | ||
| "id": np.arange(n_nodes), | ||
| "kind": rng.choice(["x", "y", "z"], size=n_nodes), | ||
| "score": rng.integers(0, 100, size=n_nodes), | ||
| }) | ||
| edges = pd.DataFrame({ | ||
| "s": rng.integers(0, n_nodes, size=n_edges), | ||
| "d": rng.integers(0, n_nodes, size=n_edges), | ||
| "rel": rng.choice(["r1", "r2", "r3"], size=n_edges), | ||
| }) | ||
| return graphistry.nodes(nodes, "id").edges(edges, "s", "d") | ||
|
|
||
|
|
||
| def timeit(fn: Callable[[], object], runs: int, warmup: int) -> float: | ||
| for _ in range(warmup): | ||
| fn() | ||
| samples = [] | ||
| for _ in range(runs): | ||
| t0 = time.perf_counter() | ||
| fn() | ||
| samples.append((time.perf_counter() - t0) * 1000.0) | ||
| return statistics.median(samples) | ||
|
|
||
|
|
||
| def _polars_graph(g): | ||
| """Same graph with node/edge frames already in polars, so the polars runs | ||
| don't pay a per-call pandas->polars input coercion that the pandas runs avoid | ||
| (a real deployment keeps the graph in its engine's native frame type).""" | ||
| from graphistry.Engine import Engine, df_to_engine | ||
| return g.nodes(df_to_engine(g._nodes, Engine.POLARS), g._node).edges( | ||
| df_to_engine(g._edges, Engine.POLARS), g._source, g._destination) | ||
|
|
||
|
|
||
| def run(sizes: List[Tuple[int, int]], runs: int, warmup: int) -> List[ResultRow]: | ||
| rows: List[ResultRow] = [] | ||
| for n_nodes, n_edges in sizes: | ||
| g_pd = make_graph(n_nodes, n_edges) | ||
| g_pl = _polars_graph(g_pd) | ||
| for name, query in WORKLOADS: | ||
| try: | ||
| pandas_ms = timeit(lambda: g_pd.gfql(query, engine="pandas"), runs, warmup) | ||
| polars_ms = timeit(lambda: g_pl.gfql(query, engine="polars"), runs, warmup) | ||
| rows.append(ResultRow(name, n_nodes, n_edges, pandas_ms, polars_ms)) | ||
| except Exception as exc: # noqa: BLE001 - bench harness reports, never crashes the sweep | ||
| rows.append(ResultRow(name, n_nodes, n_edges, None, None, error=f"{type(exc).__name__}: {exc}")) | ||
| return rows | ||
|
|
||
|
|
||
| def to_markdown(rows: List[ResultRow]) -> str: | ||
| lines = [ | ||
| "| workload | nodes | edges | pandas_ms | polars_ms | speedup |", | ||
| "|----------|-------|-------|-----------|-----------|---------|", | ||
| ] | ||
| for r in rows: | ||
| if r.error: | ||
| lines.append(f"| {r.workload} | {r.n_nodes} | {r.n_edges} | ERROR | ERROR | {r.error} |") | ||
| else: | ||
| lines.append( | ||
| f"| {r.workload} | {r.n_nodes} | {r.n_edges} | " | ||
| f"{r.pandas_ms:.1f} | {r.polars_ms:.1f} | {r.speedup:.2f}x |" | ||
| ) | ||
| return "\n".join(lines) | ||
|
|
||
|
|
||
| def _parse_sizes(text: str) -> List[Tuple[int, int]]: | ||
| # "nodes:edges,nodes:edges" or "nodes" (edges defaults to 5x nodes) | ||
| out: List[Tuple[int, int]] = [] | ||
| for chunk in text.split(","): | ||
| chunk = chunk.strip() | ||
| if not chunk: | ||
| continue | ||
| if ":" in chunk: | ||
| nn, ne = chunk.split(":") | ||
| out.append((int(nn), int(ne))) | ||
| else: | ||
| nn = int(chunk) | ||
| out.append((nn, nn * 5)) | ||
| return out | ||
|
|
||
|
|
||
| def main() -> None: | ||
| try: | ||
| import polars # noqa: F401 | ||
| except ImportError: | ||
| raise SystemExit("polars is not installed; install with `pip install polars`") | ||
|
|
||
| parser = argparse.ArgumentParser(description="Benchmark GFQL cypher row pipeline pandas vs polars.") | ||
| parser.add_argument("--runs", type=int, default=7) | ||
| parser.add_argument("--warmup", type=int, default=2) | ||
| parser.add_argument( | ||
| "--sizes", | ||
| default="10000,100000,1000000", | ||
| help="Comma list of node counts (edges=5x) or nodes:edges pairs.", | ||
| ) | ||
| parser.add_argument("--output", default="", help="Optional path to write the markdown table.") | ||
| args = parser.parse_args() | ||
|
|
||
| rows = run(_parse_sizes(args.sizes), args.runs, args.warmup) | ||
| table = to_markdown(rows) | ||
| print(table) | ||
| if args.output: | ||
| with open(args.output, "w") as fh: | ||
| fh.write(table + "\n") | ||
| print(f"\nwrote {args.output}") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| """Multi-hop polars-vs-pandas chain benchmark (validation for the native multi-hop port). | ||
|
|
||
| Confirms native multi-hop polars is on-par-or-better than pandas on non-trivial graphs. | ||
| Run on dgx (CPU lane is the relevant comparison; cudf/polars-gpu optional). | ||
| """ | ||
| import time | ||
| import numpy as np | ||
| import pandas as pd | ||
| import graphistry | ||
| from graphistry.compute.ast import n, e_forward, e_undirected | ||
|
|
||
|
|
||
| def _graph(nn, ne, seed=0): | ||
| rng = np.random.default_rng(seed) | ||
| nd = pd.DataFrame({"id": np.arange(nn), "k": rng.integers(0, 5, nn)}) | ||
| ed = pd.DataFrame({ | ||
| "s": rng.integers(0, nn, ne), | ||
| "d": rng.integers(0, nn, ne), | ||
| "w": rng.integers(0, 100, ne), | ||
| }) | ||
| return graphistry.nodes(nd, "id").edges(ed, "s", "d") | ||
|
|
||
|
|
||
| def _time(g, ch, engine, reps=3): | ||
| g.chain(ch, engine=engine) # warm | ||
| best = float("inf") | ||
| for _ in range(reps): | ||
| t = time.perf_counter() | ||
| out = g.chain(ch, engine=engine) | ||
| dt = time.perf_counter() - t | ||
| best = min(best, dt) | ||
| ne = 0 if out._edges is None else len(out._edges) | ||
| nn = 0 if out._nodes is None else len(out._nodes) | ||
| return best * 1000, nn, ne | ||
|
|
||
|
|
||
| CHAINS = { | ||
| "fwd-hops2": [n({"id": [0]}), e_forward(hops=2), n()], | ||
| "fwd-hops3": [n({"id": [0]}), e_forward(hops=3), n()], | ||
| "maxhops4": [n({"id": [0]}), e_forward(max_hops=4), n()], | ||
| "sandwiched": [n({"id": [0]}), e_forward(), n(), e_forward(hops=2), n()], | ||
| "fwd-tofixed": [n({"id": [0]}), e_forward(to_fixed_point=True), n()], | ||
| "und-hops2": [n({"id": [0]}), e_undirected(hops=2), n()], | ||
| "und-maxhops3": [n({"id": [0]}), e_undirected(max_hops=3), n()], | ||
| } | ||
|
|
||
| for (nn, ne) in [(10_000, 50_000), (100_000, 500_000), (500_000, 2_000_000)]: | ||
| g = _graph(nn, ne) | ||
| print(f"\n=== graph nn={nn:,} ne={ne:,} ===") | ||
| for name, ch in CHAINS.items(): | ||
| engines = ["pandas", "polars"] | ||
| try: | ||
| import cudf # noqa: F401 | ||
| engines.append("cudf") | ||
| except Exception: | ||
| pass | ||
| row = [] | ||
| ref = None | ||
| for eng in engines: | ||
| try: | ||
| ms, rnn, rne = _time(g, ch, eng) | ||
| if eng == "pandas": | ||
| ref = ms | ||
| spd = f"{ref/ms:.2f}x" if (ref and eng != "pandas") else "1.00x" | ||
| row.append(f"{eng}={ms:.1f}ms({spd},n={rnn},e={rne})") | ||
| except NotImplementedError: | ||
| row.append(f"{eng}=NIE") | ||
| except Exception as ex: | ||
| row.append(f"{eng}=ERR:{type(ex).__name__}") | ||
| print(f" {name:12s} " + " ".join(row)) |
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
combine gfql + polars coverage