feat(gfql/cypher): standard =~ regex + numeric fns + toLower/toUpper (#1673)#1675
Merged
Conversation
lmeyerov
added a commit
that referenced
this pull request
Jul 2, 2026
…s (no crash) GPU-parity pass (viz-filter #1673 item 2) on dgx found `MATCH (n) WHERE n.name =~ '(?i)…'` CRASHES on engine='cudf' with libcudf "invalid regex pattern: nothing to repeat". Root cause: libcudf's regex engine rejects inline flag groups ((?i)/(?m)/ (?s)) at ANY position (verified: '(?i)abc', '^(?i)abc$', '(?i)^abc$' all raise; only flag-free '^abc$' works) — not merely a position issue. Fix: the Match/Fullmatch cuDF branches now translate a leading (?i) to the existing lowercase case-folding workaround (parity with pandas' (?i)), and honestly decline any other inline flag with NotImplementedError instead of crashing. Shared helper _cudf_regex_prep. pandas/polars paths untouched. Validated on dgx (RAPIDS 26.02): cudf =~ '(?i)a.c' == pandas [2,3] (was RuntimeError); 446 regex/match/fullmatch/contains/numeric tests pass across pandas/polars/cudf; +1 cudf-gated regression test (test_regex_cudf_inline_flag_parity). ruff+mypy clean. Also confirms viz-filter #1673 item 2: cuDF numeric (floor/ceil/round) + toLower/ toUpper parity OK; polars-gpu is N/A on this branch (#1675 is off #1660, below #1655 which introduces polars-gpu). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
force-pushed
the
dev/gfql-polars-contains-regex-fix
branch
from
July 2, 2026 16:19
b8f7d2c to
6c68f1c
Compare
lmeyerov
force-pushed
the
dev/gfql-cypher-viz-ops
branch
from
July 2, 2026 16:19
8b94e1a to
d90d7ea
Compare
lmeyerov
added a commit
that referenced
this pull request
Jul 2, 2026
…s (no crash) GPU-parity pass (viz-filter #1673 item 2) on dgx found `MATCH (n) WHERE n.name =~ '(?i)…'` CRASHES on engine='cudf' with libcudf "invalid regex pattern: nothing to repeat". Root cause: libcudf's regex engine rejects inline flag groups ((?i)/(?m)/ (?s)) at ANY position (verified: '(?i)abc', '^(?i)abc$', '(?i)^abc$' all raise; only flag-free '^abc$' works) — not merely a position issue. Fix: the Match/Fullmatch cuDF branches now translate a leading (?i) to the existing lowercase case-folding workaround (parity with pandas' (?i)), and honestly decline any other inline flag with NotImplementedError instead of crashing. Shared helper _cudf_regex_prep. pandas/polars paths untouched. Validated on dgx (RAPIDS 26.02): cudf =~ '(?i)a.c' == pandas [2,3] (was RuntimeError); 446 regex/match/fullmatch/contains/numeric tests pass across pandas/polars/cudf; +1 cudf-gated regression test (test_regex_cudf_inline_flag_parity). ruff+mypy clean. Also confirms viz-filter #1673 item 2: cuDF numeric (floor/ceil/round) + toLower/ toUpper parity OK; polars-gpu is N/A on this branch (#1675 is off #1660, below #1655 which introduces polars-gpu). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
added a commit
that referenced
this pull request
Jul 2, 2026
…in _cudf_regex_prep The cuDF =~ flags commit annotated _cudf_regex_prep with "tuple[object, bool]", which mypy rejects on py3.8/3.9 (builtin generics need 3.9+/3.10+); broke the python-lint-types (3.8, 3.9) lanes on #1675/#1677. Use typing.Tuple. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
added a commit
that referenced
this pull request
Jul 2, 2026
… transform) _cudf_regex_prep is a pure string transform (no cuDF required), but its only tests were cuDF-gated, so CPU CI left the new lines uncovered and #1675's changed-line-coverage gate fell to 65.9%. Add direct unit tests for every branch: non-str/no-flag passthrough, leading (?i)/(?ii) -> case-fold, other inline flags -> honest NotImplementedError. Remaining uncovered lines are the is_cudf execution branches (GPU-only; covered by the dgx parity pass). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
changed the base branch from
dev/gfql-polars-contains-regex-fix
to
dev/gfql-polars-engine
July 2, 2026 23:40
lmeyerov
added a commit
that referenced
this pull request
Jul 2, 2026
…s (no crash) GPU-parity pass (viz-filter #1673 item 2) on dgx found `MATCH (n) WHERE n.name =~ '(?i)…'` CRASHES on engine='cudf' with libcudf "invalid regex pattern: nothing to repeat". Root cause: libcudf's regex engine rejects inline flag groups ((?i)/(?m)/ (?s)) at ANY position (verified: '(?i)abc', '^(?i)abc$', '(?i)^abc$' all raise; only flag-free '^abc$' works) — not merely a position issue. Fix: the Match/Fullmatch cuDF branches now translate a leading (?i) to the existing lowercase case-folding workaround (parity with pandas' (?i)), and honestly decline any other inline flag with NotImplementedError instead of crashing. Shared helper _cudf_regex_prep. pandas/polars paths untouched. Validated on dgx (RAPIDS 26.02): cudf =~ '(?i)a.c' == pandas [2,3] (was RuntimeError); 446 regex/match/fullmatch/contains/numeric tests pass across pandas/polars/cudf; +1 cudf-gated regression test (test_regex_cudf_inline_flag_parity). ruff+mypy clean. Also confirms viz-filter #1673 item 2: cuDF numeric (floor/ceil/round) + toLower/ toUpper parity OK; polars-gpu is N/A on this branch (#1675 is off #1660, below #1655 which introduces polars-gpu). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
force-pushed
the
dev/gfql-cypher-viz-ops
branch
from
July 2, 2026 23:40
f997217 to
31054a8
Compare
lmeyerov
added a commit
that referenced
this pull request
Jul 2, 2026
…in _cudf_regex_prep The cuDF =~ flags commit annotated _cudf_regex_prep with "tuple[object, bool]", which mypy rejects on py3.8/3.9 (builtin generics need 3.9+/3.10+); broke the python-lint-types (3.8, 3.9) lanes on #1675/#1677. Use typing.Tuple. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
added a commit
that referenced
this pull request
Jul 2, 2026
… transform) _cudf_regex_prep is a pure string transform (no cuDF required), but its only tests were cuDF-gated, so CPU CI left the new lines uncovered and #1675's changed-line-coverage gate fell to 65.9%. Add direct unit tests for every branch: non-str/no-flag passthrough, leading (?i)/(?ii) -> case-fold, other inline flags -> honest NotImplementedError. Remaining uncovered lines are the is_cudf execution branches (GPU-only; covered by the dgx parity pass). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
added a commit
that referenced
this pull request
Jul 4, 2026
…s (no crash) GPU-parity pass (viz-filter #1673 item 2) on dgx found `MATCH (n) WHERE n.name =~ '(?i)…'` CRASHES on engine='cudf' with libcudf "invalid regex pattern: nothing to repeat". Root cause: libcudf's regex engine rejects inline flag groups ((?i)/(?m)/ (?s)) at ANY position (verified: '(?i)abc', '^(?i)abc$', '(?i)^abc$' all raise; only flag-free '^abc$' works) — not merely a position issue. Fix: the Match/Fullmatch cuDF branches now translate a leading (?i) to the existing lowercase case-folding workaround (parity with pandas' (?i)), and honestly decline any other inline flag with NotImplementedError instead of crashing. Shared helper _cudf_regex_prep. pandas/polars paths untouched. Validated on dgx (RAPIDS 26.02): cudf =~ '(?i)a.c' == pandas [2,3] (was RuntimeError); 446 regex/match/fullmatch/contains/numeric tests pass across pandas/polars/cudf; +1 cudf-gated regression test (test_regex_cudf_inline_flag_parity). ruff+mypy clean. Also confirms viz-filter #1673 item 2: cuDF numeric (floor/ceil/round) + toLower/ toUpper parity OK; polars-gpu is N/A on this branch (#1675 is off #1660, below #1655 which introduces polars-gpu). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
force-pushed
the
dev/gfql-cypher-viz-ops
branch
from
July 4, 2026 20:24
31054a8 to
8843b0f
Compare
lmeyerov
added a commit
that referenced
this pull request
Jul 4, 2026
…in _cudf_regex_prep The cuDF =~ flags commit annotated _cudf_regex_prep with "tuple[object, bool]", which mypy rejects on py3.8/3.9 (builtin generics need 3.9+/3.10+); broke the python-lint-types (3.8, 3.9) lanes on #1675/#1677. Use typing.Tuple. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
added a commit
that referenced
this pull request
Jul 4, 2026
… transform) _cudf_regex_prep is a pure string transform (no cuDF required), but its only tests were cuDF-gated, so CPU CI left the new lines uncovered and #1675's changed-line-coverage gate fell to 65.9%. Add direct unit tests for every branch: non-str/no-flag passthrough, leading (?i)/(?ii) -> case-fold, other inline flags -> honest NotImplementedError. Remaining uncovered lines are the is_cudf execution branches (GPU-only; covered by the dgx parity pass). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
added a commit
that referenced
this pull request
Jul 4, 2026
…s (no crash) GPU-parity pass (viz-filter #1673 item 2) on dgx found `MATCH (n) WHERE n.name =~ '(?i)…'` CRASHES on engine='cudf' with libcudf "invalid regex pattern: nothing to repeat". Root cause: libcudf's regex engine rejects inline flag groups ((?i)/(?m)/ (?s)) at ANY position (verified: '(?i)abc', '^(?i)abc$', '(?i)^abc$' all raise; only flag-free '^abc$' works) — not merely a position issue. Fix: the Match/Fullmatch cuDF branches now translate a leading (?i) to the existing lowercase case-folding workaround (parity with pandas' (?i)), and honestly decline any other inline flag with NotImplementedError instead of crashing. Shared helper _cudf_regex_prep. pandas/polars paths untouched. Validated on dgx (RAPIDS 26.02): cudf =~ '(?i)a.c' == pandas [2,3] (was RuntimeError); 446 regex/match/fullmatch/contains/numeric tests pass across pandas/polars/cudf; +1 cudf-gated regression test (test_regex_cudf_inline_flag_parity). ruff+mypy clean. Also confirms viz-filter #1673 item 2: cuDF numeric (floor/ceil/round) + toLower/ toUpper parity OK; polars-gpu is N/A on this branch (#1675 is off #1660, below #1655 which introduces polars-gpu). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
force-pushed
the
dev/gfql-cypher-viz-ops
branch
from
July 4, 2026 22:24
6d65aad to
17b127c
Compare
lmeyerov
added a commit
that referenced
this pull request
Jul 4, 2026
…in _cudf_regex_prep The cuDF =~ flags commit annotated _cudf_regex_prep with "tuple[object, bool]", which mypy rejects on py3.8/3.9 (builtin generics need 3.9+/3.10+); broke the python-lint-types (3.8, 3.9) lanes on #1675/#1677. Use typing.Tuple. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
added a commit
that referenced
this pull request
Jul 4, 2026
… transform) _cudf_regex_prep is a pure string transform (no cuDF required), but its only tests were cuDF-gated, so CPU CI left the new lines uncovered and #1675's changed-line-coverage gate fell to 65.9%. Add direct unit tests for every branch: non-str/no-flag passthrough, leading (?i)/(?ii) -> case-fold, other inline flags -> honest NotImplementedError. Remaining uncovered lines are the is_cudf execution branches (GPU-only; covered by the dgx parity pass). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
added a commit
that referenced
this pull request
Jul 4, 2026
…s (no crash) GPU-parity pass (viz-filter #1673 item 2) on dgx found `MATCH (n) WHERE n.name =~ '(?i)…'` CRASHES on engine='cudf' with libcudf "invalid regex pattern: nothing to repeat". Root cause: libcudf's regex engine rejects inline flag groups ((?i)/(?m)/ (?s)) at ANY position (verified: '(?i)abc', '^(?i)abc$', '(?i)^abc$' all raise; only flag-free '^abc$' works) — not merely a position issue. Fix: the Match/Fullmatch cuDF branches now translate a leading (?i) to the existing lowercase case-folding workaround (parity with pandas' (?i)), and honestly decline any other inline flag with NotImplementedError instead of crashing. Shared helper _cudf_regex_prep. pandas/polars paths untouched. Validated on dgx (RAPIDS 26.02): cudf =~ '(?i)a.c' == pandas [2,3] (was RuntimeError); 446 regex/match/fullmatch/contains/numeric tests pass across pandas/polars/cudf; +1 cudf-gated regression test (test_regex_cudf_inline_flag_parity). ruff+mypy clean. Also confirms viz-filter #1673 item 2: cuDF numeric (floor/ceil/round) + toLower/ toUpper parity OK; polars-gpu is N/A on this branch (#1675 is off #1660, below #1655 which introduces polars-gpu). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
added a commit
that referenced
this pull request
Jul 4, 2026
…in _cudf_regex_prep The cuDF =~ flags commit annotated _cudf_regex_prep with "tuple[object, bool]", which mypy rejects on py3.8/3.9 (builtin generics need 3.9+/3.10+); broke the python-lint-types (3.8, 3.9) lanes on #1675/#1677. Use typing.Tuple. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
added a commit
that referenced
this pull request
Jul 4, 2026
… transform) _cudf_regex_prep is a pure string transform (no cuDF required), but its only tests were cuDF-gated, so CPU CI left the new lines uncovered and #1675's changed-line-coverage gate fell to 65.9%. Add direct unit tests for every branch: non-str/no-flag passthrough, leading (?i)/(?ii) -> case-fold, other inline flags -> honest NotImplementedError. Remaining uncovered lines are the is_cudf execution branches (GPU-only; covered by the dgx parity pass). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
force-pushed
the
dev/gfql-cypher-viz-ops
branch
from
July 4, 2026 22:57
17b127c to
e9ba70f
Compare
lmeyerov
added a commit
that referenced
this pull request
Jul 4, 2026
…s (no crash) GPU-parity pass (viz-filter #1673 item 2) on dgx found `MATCH (n) WHERE n.name =~ '(?i)…'` CRASHES on engine='cudf' with libcudf "invalid regex pattern: nothing to repeat". Root cause: libcudf's regex engine rejects inline flag groups ((?i)/(?m)/ (?s)) at ANY position (verified: '(?i)abc', '^(?i)abc$', '(?i)^abc$' all raise; only flag-free '^abc$' works) — not merely a position issue. Fix: the Match/Fullmatch cuDF branches now translate a leading (?i) to the existing lowercase case-folding workaround (parity with pandas' (?i)), and honestly decline any other inline flag with NotImplementedError instead of crashing. Shared helper _cudf_regex_prep. pandas/polars paths untouched. Validated on dgx (RAPIDS 26.02): cudf =~ '(?i)a.c' == pandas [2,3] (was RuntimeError); 446 regex/match/fullmatch/contains/numeric tests pass across pandas/polars/cudf; +1 cudf-gated regression test (test_regex_cudf_inline_flag_parity). ruff+mypy clean. Also confirms viz-filter #1673 item 2: cuDF numeric (floor/ceil/round) + toLower/ toUpper parity OK; polars-gpu is N/A on this branch (#1675 is off #1660, below #1655 which introduces polars-gpu). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
commented
Jul 5, 2026
| prefix = _inline_regex_flag_prefix(case, flags) | ||
| return c.str.contains(f"{prefix}{pred.pat}", literal=False) | ||
|
|
||
| if name in ("Match", "Fullmatch") and hasattr(pred, "pat") and isinstance(pred.pat, str): |
Contributor
Author
There was a problem hiding this comment.
avoid dynamic typing patterns..
lmeyerov
commented
Jul 5, 2026
| return args[0].sqrt() | ||
| # sqrt of a negative -> NaN on both pandas and polars; Float64 cast so sqrt(int) | ||
| # returns float like neo4j/pandas; parity-verified. | ||
| return args[0].cast(pl.Float64).sqrt() |
Contributor
Author
There was a problem hiding this comment.
avoid dangerous dynamic typing patterns..
lmeyerov
commented
Jul 5, 2026
| if hasattr(inner, "astype"): | ||
| null_mask = self._gfql_null_mask(table_df, inner) | ||
| f = inner.astype(float) | ||
| if hasattr(f, "ceil" if use_ceil else "floor"): # cuDF native |
Contributor
Author
There was a problem hiding this comment.
avoid dynamic typing patterns
lmeyerov
commented
Jul 5, 2026
…lars Match/Fullmatch (I1+I2, #1673) Adopts the STANDARD openCypher/neo4j `=~` regex-match operator rather than inventing dialect (research: `=~` is standard Cypher; Java-regex, full/anchored match, inline (?i)/(?m)/(?s) flags; LIKE/ILIKE are SQL-only and stay unimplemented). Wired end-to-end: - Cypher WHERE-predicate grammar + expression grammar (`=~`), lowered to the existing `fullmatch` predicate (full/anchored match, not partial). - Shared expr engine: REGEX_MATCH terminal + regex_op -> BinaryOp("regex") (high-priority terminal so `=~` beats `=`), allow-listed, evaluated via a fullmatch dispatch (pandas/cuDF series + scalar). - polars `Match`/`Fullmatch` native lowering added (were NotImplementedError), so `=~` and match()/fullmatch() predicates run on polars. Coverage: simple `WHERE prop =~ '...'` on all engines (filter_by_dict); composes through AND/OR/NOT/RETURN on pandas/cuDF; polars declines the complex OR/NOT row-filter form with an honest NotImplementedError (pre-existing polars `where_rows` gap, not =~-specific). Differential-parity tested vs pandas oracle; 1306 tests pass across expr_parser + cypher lowering + polars chain; ruff+mypy clean. CHANGELOG + docs (docs/source/gfql/cypher.rst). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r (I3, #1673) Adopts standard openCypher/neo4j scalar functions + operator (research-verified; these are standard Cypher, not gaps to invent): - floor, ceil (alias ceiling), round(x) / round(x, precision) — numeric, return float - toLower / toUpper — the idiomatic case-insensitive compare - `^` exponentiation operator — right-associative, binds tighter than * / %, returns float (new power grammar tier in both the cypher parser and shared expr_parser; allow-listed; evaluated pandas/cuDF + polars) (abs/sqrt/sign and chained comparison `1 < x < 9` were already supported.) Differential-parity tested vs pandas oracle; 1575 tests pass across expr_parser, row pipeline, polars, and cypher lowering; ruff+mypy clean. CHANGELOG + docs. Two documented minor divergences: round uses numpy default (half-to-even) and the 3-arg round(x,prec,mode) form is deferred; `-2 ^ 2` folds to (-2)^2 due to the pre-existing negative-literal lexer (column base `-n.x ^ 2` is correct). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… sign Int64 (review) Review findings on the =~/numeric-fns branch: - IMPORTANT: the numeric/^/toLower tests ran only on pandas; parametrize them engine=[pandas, polars] so the differential-parity claim is guarded (adds a sign() case too). Regression guard for the polars row_pipeline lowerings. - SUGGESTION: polars sign() returned float vs pandas int; cast to Int64 to match the pandas engine and neo4j's Integer-returning spec. 24 parity cases pass (pandas+polars); ruff+mypy clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…th importorskip
The engine=[pandas, polars] parametrization added in the review-fix commit ran
engine='polars' unconditionally in test_lowering.py (a pandas-oriented file with
no module-level polars guard), so it ERRORED (not skipped) in polars-less CI
lanes (test-gfql-core, test-pandas-compat-gfql). Add pytest.importorskip('polars')
on the polars branch of both tests, matching the repo convention (polars-specific
suites already importorskip). Pandas variants unaffected; polars variants run
where polars is installed, skip where it isn't.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… cover numeric scalar/null
CI surfaced two real issues on the numeric/^ increment:
1. tck-gfql (correct corpus): the openCypher TCK marks exponentiation precedence
(Precedence2) as reject-expected ("expression evaluation not supported") AND
its expected `4 ^ (3*2) ^ 3 = 4^18` shows the TCK treats `^` as LEFT-
associative (my impl was right-assoc per neo4j docs). Implementing `^` both
broke the corpus xfail-contract (execute vs expected-reject) and had ambiguous
associativity. Reverted `^` — it needs a coordinated corpus xfail update + a
settled associativity decision. Kept the clean numeric fns + toLower/toUpper.
2. changed-line-coverage: added scalar + null-scalar test coverage for
floor/ceil/round/toLower/toUpper (86% of changed lines; remaining uncovered
are cuDF-native branches + pre-existing shifted lines).
Also guards the polars-parametrized tests with importorskip (fixed separately in
f17bdf8). floor/ceil/ceiling/round/toLower/toUpper + =~ unchanged and green.
1607 tests pass; ruff+mypy clean. CHANGELOG + docs updated to drop `^`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s (no crash) GPU-parity pass (viz-filter #1673 item 2) on dgx found `MATCH (n) WHERE n.name =~ '(?i)…'` CRASHES on engine='cudf' with libcudf "invalid regex pattern: nothing to repeat". Root cause: libcudf's regex engine rejects inline flag groups ((?i)/(?m)/ (?s)) at ANY position (verified: '(?i)abc', '^(?i)abc$', '(?i)^abc$' all raise; only flag-free '^abc$' works) — not merely a position issue. Fix: the Match/Fullmatch cuDF branches now translate a leading (?i) to the existing lowercase case-folding workaround (parity with pandas' (?i)), and honestly decline any other inline flag with NotImplementedError instead of crashing. Shared helper _cudf_regex_prep. pandas/polars paths untouched. Validated on dgx (RAPIDS 26.02): cudf =~ '(?i)a.c' == pandas [2,3] (was RuntimeError); 446 regex/match/fullmatch/contains/numeric tests pass across pandas/polars/cudf; +1 cudf-gated regression test (test_regex_cudf_inline_flag_parity). ruff+mypy clean. Also confirms viz-filter #1673 item 2: cuDF numeric (floor/ceil/round) + toLower/ toUpper parity OK; polars-gpu is N/A on this branch (#1675 is off #1660, below #1655 which introduces polars-gpu). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…in _cudf_regex_prep The cuDF =~ flags commit annotated _cudf_regex_prep with "tuple[object, bool]", which mypy rejects on py3.8/3.9 (builtin generics need 3.9+/3.10+); broke the python-lint-types (3.8, 3.9) lanes on #1675/#1677. Use typing.Tuple. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… transform) _cudf_regex_prep is a pure string transform (no cuDF required), but its only tests were cuDF-gated, so CPU CI left the new lines uncovered and #1675's changed-line-coverage gate fell to 65.9%. Add direct unit tests for every branch: non-str/no-flag passthrough, leading (?i)/(?ii) -> case-fold, other inline flags -> honest NotImplementedError. Remaining uncovered lines are the is_cudf execution branches (GPU-only; covered by the dgx parity pass). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lane The polars-parametrized cypher tests (numeric fns, toLower, =~ parity in graphistry/tests/compute/gfql/cypher/test_lowering.py) never executed in CI: the core lane has no polars installed (importorskip -> skip) and the polars lane's fixed file list excluded the file. So the polars numeric lowerings this branch adds were CI-untested and their changed lines uncovered (coverage gate 76.9% < 80). Add the file (-k polars) to bin/test-polars.sh and the ci.yml polars coverage step (--cov-append). Moved from the stacked #1677 to here — it belongs with the lowerings it tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ceil/ceiling/round/toLower/toUpper) The coverage ledger (cascaded from #1660) correctly flagged the new GFQL_SCALAR_FUNCTIONS entries as neither exercised nor waived. Real 4-engine parity-or-NIE cases beat waivers: each new function gets a cypher expression case through the standard matrix driver (chain + DAG surfaces). dgx: matrix + ledger 267 passed (cudf + polars-gpu lanes active). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n standards vetting; not shipped) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… dgx-verified) Composed =~ on cudf (Fullmatch delegate; honest NIE pass-through; clear invalid-regex error); cudf alternation anchoring ^(...)$; cudf (?i)-fold declines escape classes; cudf lookaround/backref decline; polars Match/Fullmatch rust-regex guard; toLower/ toUpper non-string decline (was Series-repr broadcast on the oracle); polars floor/ ceil/round Float64 cast; matrix: =~ family + null-cells + dtype-pin + decline tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…pass-through one frame up, pinned repros Casefold gate v2: decline only fold-unsound shapes (uppercase escapes, case-crossing ranges, non-ASCII) — lowercase escapes keep folding (blanket backslash decline regressed (?i)\d+ to NIE); NIE survives _gfql_eval_string_expr's broad except; scalar-path re.error clarity; (?P=/(?P< in cudf decline regex; anchored-pattern passthrough in the compat shim (ordering.py blast radius); discriminating-fixture pin tests (generic cases passed on pre-fix code). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e, class-scoped [?-Z]/[Z-~]/[X-^]/[\x41-Z] style ranges (exactly one uppercase-letter endpoint) shift or go invalid under fold — decline; check scoped to patterns containing a class so class-free literal hyphens ((?i)e-MAIL) keep folding; docs wording: uppercase escape classes only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… all decline shapes (wave-4 suggestions) \x hex escapes spell uppercase letters invisibly to .lower(); cypher.rst caveat now lists all four fold-decline shapes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…er getattr/hasattr/cast (Leo steer) isinstance((Match, Fullmatch)) narrowing + direct pred.case/flags; deleted the dead pre-existing polars Match/Fullmatch block (identical guard above always returns); _cudf_regex_prep typed str (pat declared+validated; 4 isinstance dances dropped); isinstance(Literal) replaces the .value getattr probe in round lowering (also fixes false-accept of other value-bearing nodes); dispatch typed SeriesT. Net -15 lines; dgx 4-engine lane green (1802+529). Audit (research/dynamic-patterns-audit.md): ~480 PRE-EXISTING occurrences = follow-up debt PR (is_series TypeGuard design recorded), sequenced after the stack lands. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…not version-lag) Rewrites the comments on _CUDF_REGEX_UNSUPPORTED / _cudf_regex_prep / _cudf_casefold_or_decline so the limits are re-checkable without re-research. Comment/docstring-only — zero behavior change. - Lookaround + pattern backreferences: architectural (libcudf regex is non-backtracking / finite-automata); NO RAPIDS version restores them. SAME limitation on polars + polars-gpu (Rust regex, also non-backtracking) — cross- reference the twin guard _regex_rust_incompatible. Only pandas (backtracking re) supports them. Verified vs the cuDF 26.02 regex feature doc. - Inline (?i)/(?m)/(?s): unsupported inline; and the out-of-band flags arg exposes only MULTILINE/DOTALL in the Python str.contains binding, NOT IGNORECASE. - _cudf_casefold_or_decline: the manual lowercase-fold is the ONLY case-insensitive regex mechanism cuDF's Python API offers, so its declines are PERMANENT — dgx probe (cudf 26.02.01): flags=re.IGNORECASE -> 'unsupported value for flags' (only MULTILINE/DOTALL accepted); case=False -> 'only supported when regex=False'. libcudf C++ regex_flags has an IGNORECASE bit but the Python StringMethods binding does not expose it. Records the settled negative result so it is not re-litigated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
#1682 switched WHERE parsing to LALR (contextual lexer). The =~ rules textually merged clean but the contextual lexer tokenized '=~' as COMP_OP '=' + '~' (Earley's dynamic lexer had tried all terminals), so every =~ query failed to parse. Add a filtered, higher-priority terminal (_REGEX_MATCH.5) so the lexer prefers '=~' over '='; leading underscore keeps the op token out of the tree, preserving regex_where/regex_op arity. Add =~ corpus entries to satisfy #1682's new rule-coverage gate (regex_op via expr path, regex_where via the lift chain). Grammar invariants (zero LALR conflicts, rule coverage, Earley differential) + parser + lowering suites green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lmeyerov
force-pushed
the
dev/gfql-cypher-viz-ops
branch
from
July 5, 2026 22:10
c61feb7 to
38be679
Compare
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.
Implements the streamgl-viz filter-language conformance gaps (#1673) by adopting standard openCypher/neo4j Cypher syntax — not inventing dialect. Based directly on master (the polars
containsregex fix it originally stacked on merged via #1660).Research first
Verified each construct against the neo4j Cypher manual, openCypher 9, and ISO GQL (sources in the issue). Finding: most "gaps" are standard Cypher, so we adopt the standard forms.
LIKE/ILIKE/BETWEENare SQL-only (not in Cypher or GQL) → intentionally not implemented; chained comparison (1 < n.age < 65) already worked.What's added (all standard openCypher/neo4j)
=~regex-match operator — Java-regex, full/anchored match (n.name =~ 'AB'matches only'AB'), inline(?i)/(?m)/(?s)flags; lowers tofullmatch. SimpleWHERE …=~…on all engines (filter_by_dict); composes throughAND/OR/NOT/RETURNon pandas/cuDF (polars declines the complexOR/NOTrow-filter form with an honestNotImplementedError— pre-existingwhere_rowsgap). Adds native polarsMatch/Fullmatchlowering.floor,ceil(≡ceiling),round(x)/round(x, precision)(complementing existingabs/sqrt/sign).toLower/toUpper— the idiomatic case-insensitive compare.Wired end-to-end: cypher grammar + shared
expr_parser(REGEX_MATCH terminal) + allow-lists + evaluators (pandas/cuDFrow/pipeline.py+row/dispatch.py; polarspredicates.py+row_pipeline.py).Tests / quality
Differential-parity tested pandas↔polars for
=~, Match/Fullmatch, numeric fns,sign,toLower/toUpper. Full polars lane passes; ruff+mypy clean. A focused adversarial review found no BLOCKERs; its two findings are addressed (polars parity now guarded by parametrized tests; polarssign()cast to Int64 to match pandas/neo4j Integer spec).GPU parity verified on dgx (GB10,
--gpus all, 2026-07-05): full polars lane + conformance matrix +test_engine_polars_gpu.py= 1803 passed, 0 skipped/failed — cuDF + polars-gpu (cudf_polars) matrix cases (incl. Match/Fullmatch + the scalar fns) all parity-green vs the pandas oracle.Review hardening (4-wave converged adversarial review, 2026-07-05)
A multi-wave findings-mode review (artifacts:
plans/viz-filter-pipeline/waves/pr1675/) found and fixed, each dgx-repro'd before and re-verified after: composed=~broken onengine='cudf'(no.str.fullmatch; now delegates to the hardenedFullmatchpredicate); cuDF fullmatch alternation mis-anchoring ('ab|cd'silently matched'abXXX'); the cuDF(?i)lowercase-fold silently inverting escape classes (now a precise decline gate: uppercase escapes / case-crossing ranges / hex escapes / non-ASCII decline, lowercase escapes keep folding); polarsMatch/Fullmatchmissing the Rust-regex guard (lookarounds crashed non-NIE);toLower(numeric col)broadcasting the Series repr on the pandas oracle (now declines like neo4j's type error); polarsfloor/ceil/roundreturning Int64 on int columns (now Float64 like neo4j/pandas); NotImplementedError being re-labeled by broad excepts (honest NIEs now survive to the caller); plus matrix coverage for the=~family, null cells, dtype pins, and discriminating regression fixtures. Waves 3–4 converged at zero BLOCKER/IMPORTANT.Docs
docs/source/gfql/cypher.rst:=~in WHERE Forms + a new "Scalar Functions and Operators" section (fns/toLower + why LIKE/BETWEEN aren't provided). CHANGELOG entries for both increments.Deferred (documented)
round(x, precision, mode)3-arg form; at this layerrounduses numpy half-to-even — the stacked fix(gfql/cypher): neo4j round() tie-breaking + lower/upper GQL aliases (#1673) #1677 replaces it with neo4j's documented tie-breaking.^power operator: initially implemented right-associative, then removed during standards vetting — the openCypher TCK pins^LEFT-associative and the current conformance corpus marks it reject-expected, so re-adding is a coordinated corpus change (spec record in fix(gfql/cypher): neo4j round() tie-breaking + lower/upper GQL aliases (#1673) #1677/GFQL Cypher dialect: host the streamgl-viz filter language (regex/case, floor/ceil/round/pow, prune-isolated, two-mask via GRAPH{}) #1673).🤖 Generated with Claude Code