Skip to content

fix[next]: concat_where(Dim != value, ...) gives wrong results - #2819

Merged
tehrengruber merged 3 commits into
GridTools:mainfrom
tehrengruber:fix_not_eq_domain_inference
Aug 25, 2026
Merged

fix[next]: concat_where(Dim != value, ...) gives wrong results#2819
tehrengruber merged 3 commits into
GridTools:mainfrom
tehrengruber:fix_not_eq_domain_inference

Conversation

@tehrengruber

@tehrengruber tehrengruber commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Noticed while reviewing #2795, see https://github.com/GridTools/gt4py/pull/2795\#issuecomment-5395705263. Independent of that PR — the bug predates it and reproduces identically on main.

Problem

InferDomainOps rewrote IDim != a into IDim < a & IDim > a. The two half-open ranges are disjoint, so the intersection is empty and the true branch of a concat_where with such a condition can never be selected: concat_where(IDim != a, x, y) silently evaluates to y everywhere.

With a = ones, b = zeros on I: [0, 6):

concat_where(I != 2, a, b)      -> [0. 0. 0. 0. 0. 0.]
concat_where((I<2)|(I>2), a, b) -> [1. 1. 0. 1. 1. 1.]   # expected
concat_where(I == 2, b, a)      -> [1. 1. 0. 1. 1. 1.]   # expected

Only the compiled path is affected; in embedded execution Dimension.__ne__ with an integer raises NotImplementedError (ADR 22).

Fix

Lower to IDim < a | IDim > a. canonicalize_domain_argument already expands the union into a nested concat_where — it is the same form it produces for the equivalent IDim == a condition with swapped branches, so no new machinery is involved.

Why this was not caught

  • test_infer_domain_ops.py asserted the and_ shape: the expected value was transcribed from the implementation rather than derived from the semantics, so the test pinned the bug instead of catching it.
  • No integration test used != at all. The construct reads as unsupported per ADR 22, but that is only enforced in embedded execution, where the field operator body runs as Python — a traced field operator never calls Dimension.__ne__.

Tests

For the bug itself: the unit test that pinned the and_ shape now asserts the union, and an integration test for concat_where(Dim != n, ...) is added.

Independently of the bug, integration tests were added for the remaining comparison operator and operand-order combinations that had no coverage, and the existing concat_where tests were simplified.

AI disclaimer: This PR was written with the help of AI tools. I reviewed the fix in detail and the respective unit tests in detail. The test refactoring was a simple precise prompt, should be alright, but I only browsed through the changes.

`InferDomainOps` rewrote `IDim != a` into `IDim < a & IDim > a`. The two
half-open ranges are disjoint, so the intersection is empty and the true
branch of a `concat_where` with such a condition could never be selected:
`concat_where(IDim != a, x, y)` silently evaluated to `y` everywhere.

Lower to `IDim < a | IDim > a` instead. `canonicalize_domain_argument`
already expands the union into a nested `concat_where`, which is the same
form it produces for the equivalent `IDim == a` condition with swapped
branches.

Only the compiled path was affected; in embedded execution
`Dimension.__ne__` with an integer raises `NotImplementedError` (ADR 22).
The existing unit test asserted the `and_` shape and hence pinned the bug,
and no integration test used `!=` at all.

Adds integration tests for the operand forms that had no coverage:
`Dim != n`, `Dim <= n`, and the reversed `n > Dim`, `n >= Dim`, `n == Dim`,
`n != Dim`. Only `!=` was broken; the others were correct but untested.
Completes the unit test table with the operand orders that were missing
for `eq` / `not_eq`.
The tests that allocate all arguments and the output on the default domain
repeat the allocation and `cases.verify` boilerplate that
`cases.verify_with_default_data` already encapsulates: it allocates the
default data, applies `asnumpy` and calls the reference with the arguments.

Converts the nine such tests. The remaining ones keep the explicit form
since they either restrict a domain (`allocate(..., domain=)` / `sizes=`)
or fix the value of a scalar argument, neither of which the default
allocation can express.
@tehrengruber
tehrengruber force-pushed the fix_not_eq_domain_inference branch from dfa939a to c042178 Compare August 24, 2026 15:52
@tehrengruber
tehrengruber requested a review from havogt August 24, 2026 16:30
@tehrengruber tehrengruber changed the title fix[next]: Lower Dim != value domain condition to a union fix[next]: concat_where(Dim != value, ...) gives wrong results Aug 24, 2026
@havogt
havogt requested a lite review from Copilot August 25, 2026 06:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes compiled concat_where(Dim != value, ...) by lowering inequality to a union of less-than and greater-than domains.

Changes:

  • Corrects != domain lowering.
  • Updates unit expectations.
  • Adds integration and comparison coverage.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Summary
tests/next_tests/unit_tests/iterator_tests/transforms_tests/test_infer_domain_ops.py Verifies union-based lowering.
tests/next_tests/integration_tests/feature_tests/ffront_tests/test_concat_where.py Adds regression and comparison coverage.
src/gt4py/next/iterator/transforms/infer_domain_ops.py Corrects != domain transformation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@tehrengruber
tehrengruber merged commit 9634071 into GridTools:main Aug 25, 2026
24 checks passed
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.

3 participants