Repository navigation
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3061 +/- ##
==========================================
- Coverage 84.27% 84.27% -0.01%
==========================================
Files 258 258
Lines 57421 57473 +52
Branches 4911 4913 +2
==========================================
+ Hits 48392 48434 +42
- Misses 8205 8214 +9
- Partials 824 825 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| guards = {d: mode(*v, evaluate=False) for d, v in guards.items()} | ||
|
|
||
| # A condition folded to `true` (e.g., `time >= 0`) imposes no guard | ||
| guards = {d: v for d, v in guards.items() if v is not sympy.true} |
There was a problem hiding this comment.
I think this should rather be fixed directly in class Guards, within devito/ir/support/guards.py: a guard, along any Dimension, should never be set (being pointless) if its value reduces to sympy.true
There was a problem hiding this comment.
Done in 4c8c1cd: Guards.__init__ now drops any entry equal to sympy.true, so no entry point (constructor, andg, xandg, impose, subs, ...) can set one, and the filter in the guard pass is gone. Added TestGuards::test_true_never_set; the buffering test still covers the original case.
|
|
||
| ct = ConditionalDimension(name='ct', parent=time, factor=2) | ||
| cn = ConditionalDimension(name='cn', parent=time, | ||
| condition=Not(GuardFactor(ct)), relation='strict') |
There was a problem hiding this comment.
GuardFactor should not be used at this level this is an internal object, this needs to be a user API def, like Not(CondEq(...
There was a problem hiding this comment.
Fair point, fixed in 763b8b2. GuardFactor is only ever built by the compiler from a ConditionalDimension(factor=...), and its negation only arises in buffering, when a copy-back is overwritten by a later one guarded by the factor dimension — so the test now goes through that: two writes to the same usave slot, one at the last timestep and one under factor=2, noop vs buffering (test_buffering.py::test_negated_factor_guard). Fails on main with if (time == 7 && time%tsubf == 0), passes with != 0. The old test is gone from test_dimension.py.
| assert np.allclose(h.data[0], 12.) | ||
|
|
||
|
|
||
| def test_negated_factor_guard(): |
There was a problem hiding this comment.
that's an abuse of the API,
- the
usaveisn't defined ontsubbut ontime - It saves two different values at the same steps which doesn't make sense.
There was a problem hiding this comment.
You're right, it was contrived. I checked it against real code (recipes, devitohorizon, the devito examples and the devitopro demos/tests): every snapshot schedule saves on the subsampled dimension with a single writer, and the only same-slot two-writer case is the last-step save in your test_multi_cond_same_slot, which negates the CondEq guard, not the factor one, and is unaffected by the GuardFactorNe change. So nothing real reaches a negated factor guard, and I've dropped both that change and the test (4d820ac). The PR is now just the true/false guard fixes.
| super().__init__(*args, **kwargs) | ||
|
|
||
| # A guard that reduces to `true` is pointless, hence never set | ||
| self._dict = {d: v for d, v in self._dict.items() if v != true} |
There was a problem hiding this comment.
what's the point of a _dict inside __init__? this is nonsensical
There was a problem hiding this comment.
oh _dict is the internal of frozendict, I guess...
well then u should rather apply the filtering to args/kwargs, not change _dict subsequently after calling into super()
There was a problem hiding this comment.
Right, that was poking frozendict's internals. Now Guards.__init__ builds the mapper from args/kwargs, drops the true entries and then calls super().__init__(mapper), as Properties does.
4d820ac to
cdd0ce8
Compare
| class TestGuards: | ||
|
|
||
| @pytest.mark.parametrize('relation', [And, Or, 'strict']) | ||
| def test_true_never_set(self, relation): |
There was a problem hiding this comment.
this test belongs to test_dimensions.py::TestConditionalDimension (and it should be placed at the bottom of the class, not at the top)
There was a problem hiding this comment.
Moved to the bottom of TestConditionalDimension in 953054a; test_ir.py is back to what main has.
GuardFactorNe inherited CondEq through GuardFactor ahead of CondNe, so it was an equality and printed `==`. Negating a subsampling guard thus reproduced the guard itself, selecting the timesteps on the subsampled grid instead of those off it
A condition SymPy folds to `false`, e.g. `time < 0`, was printed as `if (false)`, which is not C99 without `stdbool.h` and failed to compile
A condition such as `time >= 0` folds to `true` (`time` is nonnegative)
and gave a Cluster the guard {time: true}, distinct from no guard.
Buffering then created one buffer per guard for equivalent accesses
Drop such guards in `Guards` itself, at construction, rather than in the `guard` pass, so that no entry point (`andg`, `xandg`, `impose`, `subs`, ...) can set one
The negated subsampling guard is reached through buffering, when a copy-back is overwritten by a later one under a factor-based ConditionalDimension, so test it there instead of constructing the internal GuardFactor
A ConditionalDimension whose condition folds to true must yield no Conditional in the generated code, under any relation
No user program reaches a negated factor guard: real snapshot schedules save on the subsampled dimension with a single writer, and the same-slot last-step save negates a CondEq instead. The test needed a contrived program to get there, so drop both
cdd0ce8 to
953054a
Compare
|
The osx failure is the Apple-silicon detection flake, not this change: the job header reads |
Two guard bugs that buffering exposes, each covered by a regression test:
if (false)was emitted. A condition SymPy folds tofalse(e.g.time < 0) printedif (false), which is not C99 withoutstdbool.h: a compile error. True and false now print as1and0in C output (the C++ printer already emitstrue/false, which C++ accepts).{time: true}guards.time >= 0folds totrueand gave a Cluster a guard distinct from no guard, so buffering created one buffer per "guard" for equivalent accesses.Guardsnow never sets a guard that reduces totrue, whatever the entry point.Tests:
test_dimension.py::TestConditionalDimension::test_false_condition,test_buffering.py::test_true_condition_single_bufferandtest_dimension.py::TestConditionalDimension::test_true_never_set. Each fails without its source change.An earlier
GuardFactorNechange was dropped ("compiler: Drop the GuardFactorNe change and its test"): no real program reaches a negated factor guard.First of three stacked PRs: this one, then #3062 (copy-back skip rule) and #3063 (stale reads and lost writes).
🤖 Generated with Claude Code