Skip to content

test(laws): the absence rules under wider shapes, starting with group_sum - #316

Merged
FBumann merged 2 commits into
mainfrom
test/absence-laws-under-more-shapes
Jul 29, 2026
Merged

test(laws): the absence rules under wider shapes, starting with group_sum#316
FBumann merged 2 commits into
mainfrom
test/absence-laws-under-more-shapes

Conversation

@FBumann

@FBumann FBumann commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Follow-up to #314.

The one that had to exist

#314 routed group_sum through the same absence propagation as sum, on the argument that a group is a sum — and shipped it with no test. That made its behaviour a claim rather than a result.

It is now a result, and it holds: the two spellings separate 640 against 480, and both lanes agree about where each lands.

The rest

Every case in #314 reduced over f with a single mask on f. That is the smallest arrangement that shows the rule, and small enough to pass for the wrong reason. These vary one thing each:

case why it is not redundant
group_sum instead of sum the untested path from #314
two masks that do not nest the summand needs the intersection. The implementation collects one restriction per fragment and applies every one to every fragment — a version that stopped at the first, or composed them pairwise down the addition tree, passes every single-mask case above
a broadcast coefficient (w * x + y) a sparse parameter is encoding, not absence (SPEC §6), so it must not move presence at all; the separation has to come from y alone
a mask on a dim the reduction does not touch mask on t, reduce over f. The restriction is keyed by the dims the presence names, which is what makes this work; keying it by the reduced dim would silently do nothing here
absence created by shift two sources of "not here" behaving differently inside a reduction would mean SPEC §6 describes only one of them

Result

All five agree across both lanes and the LP re-solve. This adds coverage rather than fixing anything — which is the outcome worth recording, because the group_sum path had no evidence in either direction before it.

One fixture note worth keeping: g and the coordinate that reaches it exist only for the grouped cases. A dimension no declaration uses has no coordinate set to check against, and the executor refuses it — correctly, so the fixture bends rather than the engine.

Gates: 653 passed, 4 skipped, 1 xfailed · ruff clean · pyrefly 0 errors.

Summary by CodeRabbit

  • Tests
    • Expanded arithmetic law coverage across wider data shapes and alternative reduction forms.
    • Added validation for grouped reductions, intersecting masks, broadcasting, non-reduced dimensions, and shifted values.
    • Confirmed absence propagation remains consistent through arithmetic operations and reductions.

…_sum

Follow-up to #314, which routed `group_sum` through the same absence
propagation as `sum` on the argument that a group *is* a sum — and shipped it
with no test. Its behaviour was a claim rather than a result; it is now a
result: the two spellings separate 640 against 480, and both lanes agree about
where each lands.

The cases in #314 all reduced over `f` with one mask on `f`, which is the
smallest arrangement that shows the rule and small enough to pass for the wrong
reason. Five more, each varying one thing:

- **group_sum** rather than sum — the untested path above
- **two masks that do not nest**, so the summand needs the intersection rather
  than any one of them. The implementation collects one restriction per
  fragment and applies every one to every fragment; a version that stopped at
  the first, or composed them pairwise down the addition tree, passes every
  single-mask case
- **a broadcast coefficient**, which must not move presence at all — a sparse
  parameter is encoding, not absence (SPEC §6), so the separation has to come
  from the masked variable alone
- **a mask on a dim the reduction does not touch** — mask on `t`, reduce over
  `f`. The restriction is keyed by the dims the presence names, so this works;
  keying it by the reduced dim would silently do nothing here
- **absence created by `shift`** rather than by a mask, because two sources of
  "not here" that behaved differently inside a reduction would mean SPEC §6
  describes only one of them

All five agree across both lanes and the LP re-solve, so this adds coverage
rather than fixing anything — which is the outcome worth recording, since the
group_sum path had no evidence either way.

One fixture note: `g` and the coordinate reaching it exist only for the grouped
cases. A dimension no declaration uses has no coordinate set to check against
and the executor refuses it, correctly.
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@FBumann, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 47 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 29d9b21e-eda6-410c-a6a8-df5122a24827

📥 Commits

Reviewing files that changed from the base of the PR and between 1bfed97 and 10769ba.

📒 Files selected for processing (1)
  • tests/test_arithmetic_laws.py
📝 Walkthrough

Walkthrough

Adds wide-shape arithmetic-law regression coverage for absence propagation across grouped reductions, intersecting masks, broadcasting, non-reduced dimensions, and shifted variables.

Changes

Arithmetic absence semantics

Layer / File(s) Summary
Wide-shape test support
tests/test_arithmetic_laws.py
Adds wider fixtures and _wide_objective_of() for solving grouped and ungrouped model variants.
Absence propagation regressions
tests/test_arithmetic_laws.py
Adds five tests covering reduction non-distribution, mask intersection, broadcast coefficients, non-reduced masks, and shift-created absence.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the PR’s main change: new tests for absence rules under wider shapes, including group_sum.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/absence-laws-under-more-shapes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_arithmetic_laws.py`:
- Around line 340-362: Update
test_shift_created_absence_reaches_a_reduction_like_any_other so the
constraint’s reduced expression uses a separate variable only through shift(x,
t=1), while the objective continues maximizing x. Adjust the expected objective
to 320.0, preserving the test’s isolation of the absent shifted summand and
distinguishing correct reduction propagation from a missing restriction.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cc38228c-f17b-4261-979e-2f7fc2aca6ab

📥 Commits

Reviewing files that changed from the base of the PR and between f4bb63b and 1bfed97.

📒 Files selected for processing (1)
  • tests/test_arithmetic_laws.py

Comment thread tests/test_arithmetic_laws.py Outdated
As written it could not fail. `sum(x + shift(x, t=1), over=f) <= 120` bounds
x[.,0] through the t=1 row whether or not absence propagates at t=0, so the
t=1 row dominates and the objective reads 120 either way.

Measured rather than argued: neutering `_propagate_absence` fails six of the
sixteen cases in this file, and this was not one of them — a test guarding a
rule it cannot observe.

The shifted operand is now a separate variable from the one the objective
maximises, so t=0 is the only place the rule can show. Propagated, the summand
is absent there and x[.,0] is free to its bounds; without it the row survives as
`sum(x, over=f) <= 120` and caps them, giving 240 against 320. Under the same
neutering it now fails, and fails on the differential assertion — the two lanes
disagree — rather than on the numeric one, which is the stronger signal.

Seven of sixteen now catch the regression.
@FBumann

FBumann commented Jul 29, 2026

Copy link
Copy Markdown
Owner Author

Good catch — the finding is valid, and worse than "adjust the expected value": the test could not fail.

Verified rather than assumed

sum(x + shift(x, t=1), over=f) <= 120 bounds x[.,0] through the t=1 row whether or not absence propagates at t=0, so the t=1 row dominates and the objective reads 120 either way.

Measured by neutering _propagate_absence and re-running this file:

6 failed, 10 passed        <- and this test was one of the PASSES

A test guarding a rule it cannot observe. It would have gone on passing through any regression of the thing it names.

The fix

The shifted operand is now a separate variable from the one the objective maximises, so t=0 is the only place the rule can show:

sum(x + shift(v, t=1), over=f) <= 120     # objective still maximises x
  • propagated — the summand is absent at t=0, that row sums nothing, x[.,0] is free to its bounds → 320
  • not propagated — the row survives as sum(x, over=f) <= 120 and caps them → 240

Expected value updated to 320.0 as suggested.

One note on the suggested shape: keeping shift(x, …) as the shifted operand and making the bare one separate does not discriminate either — the surviving row would then constrain only the separate variable, which the objective never sees, and both readings give 320. It has to be the other way round: bare x, shifted v.

Re-verified under the same neutering

7 failed, 9 passed         <- now including this one

and it fails on the differential assertion (the two lanes disagree), not the numeric one — a stronger signal than the value being off.

Gates: 653 passed · ruff clean. Only tests/test_arithmetic_laws.py changed.

@FBumann
FBumann merged commit af72686 into main Jul 29, 2026
4 checks passed
@FBumann
FBumann deleted the test/absence-laws-under-more-shapes branch July 31, 2026 10:52
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