Skip to content

Commit 0164e85

Browse files
Merge pull request #3043 from devitocodes/fix-condeq-factor-shift
compiler: Skip CondEq snapshot shift when the point is on the factor grid
2 parents 00865f8 + a9964bb commit 0164e85

4 files changed

Lines changed: 61 additions & 13 deletions

File tree

‎devito/ir/equations/algorithms.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ def generate_conditionals(expr, input_expr, ordering):
393393
index = d.index
394394
if d.condition is not None and expr.has(d):
395395
index = index - relational_min(cond, d.parent)
396-
shift = relational_shift(cond, d.parent)
396+
shift = relational_shift(cond, d.parent, d.symbolic_factor)
397397
expr = uxreplace(expr, {d: IntDiv(index, d.symbolic_factor) + shift})
398398

399399
return expr, conditionals

‎devito/types/relational.py‎

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -294,30 +294,34 @@ def _(expr, s):
294294
return sympy.S.Infinity
295295

296296

297-
def relational_shift(expr, s):
297+
def relational_shift(expr, s, factor=None):
298298
"""
299299
Infer shift incurred by the expression. Generally only
300300
applies when a CondEq is used as it adds a single value.
301+
302+
When `factor` is provided, the CondEq point only adds a value if it does
303+
not already fall onto the subsampling grid defined by `factor`, as it
304+
would otherwise overlap with an already counted point.
301305
"""
302306
if not expr.has(s):
303307
return 0
304308

305-
return _relational_shift(expr, s)
309+
return _relational_shift(expr, s, factor)
306310

307311

308312
@singledispatch
309-
def _relational_shift(s, expr):
313+
def _relational_shift(expr, s, factor):
310314
return 0
311315

312316

313317
@_relational_shift.register(sympy.Or)
314318
@_relational_shift.register(sympy.And)
315-
def _(expr, s):
316-
return sum([_relational_shift(e, s) for e in expr.args])
319+
def _(expr, s, factor):
320+
return sum([_relational_shift(e, s, factor) for e in expr.args])
317321

318322

319323
@_relational_shift.register(sympy.Eq)
320-
def _(expr, s):
324+
def _(expr, s, factor):
321325
if isinstance(expr.lhs, sympy.Mod):
322326
return 0
323327

@@ -328,4 +332,11 @@ def _(expr, s):
328332
except (AttributeError, AssertionError):
329333
# Stepping dimension (time), requires shift
330334
from devito.symbolics.extended_dtypes import INT
331-
return INT(Ge(*expr.args))
335+
shift = INT(Ge(*expr.args))
336+
if factor is None:
337+
return shift
338+
# No shift if the point is already on the subsampling grid
339+
remainder = sympy.Mod(expr.rhs, factor)
340+
if remainder.is_Integer:
341+
return shift if remainder != 0 else 0
342+
return shift * INT(Ne(remainder, 0))

‎tests/test_buffering.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1128,4 +1128,8 @@ def test_buffering_multi_cond(factor):
11281128
eq_all.append(Eq(f_all, f, implicit_dims=ctend))
11291129
op_all = Operator(eq_all, opt='buffering')
11301130
op_all.apply(time_m=0, time_M=ntmod-2)
1131-
assert np.allclose(f_all.data[:, 11, 11], factor * np.arange(nt))
1131+
expected = factor * np.arange(nt)
1132+
if (ntmod - 2) % factor == 0:
1133+
# The last sample is on the subsampled grid, hence no extra slot
1134+
expected[-2:] = [ntmod - 1, 0]
1135+
assert np.allclose(f_all.data[:, 11, 11], expected)

‎tests/test_dimension.py‎

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77

88
from conftest import assert_blocking, assert_structure, opts_tiling, skipif
99
from devito import ( # noqa
10-
Buffer, ConditionalDimension, Constant, CustomDimension, DefaultDimension, Dimension,
11-
Eq, Function, Ge, Grid, Gt, Inc, Le, Lt, Ne, Operator, SpaceDimension, SparseFunction,
12-
SparseTimeFunction, SubDimension, SubDomain, TimeFunction, configuration, dimensions,
13-
floor, norm, sin, sum, switchconfig
10+
Buffer, CondEq, ConditionalDimension, Constant, CustomDimension, DefaultDimension,
11+
Dimension, Eq, Function, Ge, Grid, Gt, Inc, Le, Lt, Ne, Operator, SpaceDimension,
12+
SparseFunction, SparseTimeFunction, SubDimension, SubDomain, TimeFunction,
13+
configuration, dimensions, floor, norm, sin, sum, switchconfig
1414
)
1515
from devito.exceptions import InvalidArgument
1616
from devito.ir import SymbolRegistry
@@ -2074,6 +2074,39 @@ def test_factor_and_condition(self):
20742074
for t in range(buffer_size):
20752075
assert np.all(usaved.data[t] == t*factor + bounds[0] - 1)
20762076

2077+
@pytest.mark.parametrize('factor', [1, 2, 3])
2078+
@pytest.mark.parametrize('relation', [Or, 'strict'])
2079+
def test_factor_and_condeq_overlap(self, factor, relation):
2080+
"""
2081+
A snapshot at `t == nt - 1` via an implicit CondEq ConditionalDimension
2082+
only adds an extra slot when `nt - 1` is not on the subsampling grid.
2083+
Otherwise it must write the slot of the subsampled point, rather than
2084+
shift past it (and out of bounds for `factor == 1`).
2085+
"""
2086+
grid = Grid(shape=(4, 4))
2087+
time = grid.time_dim
2088+
2089+
nt = 10
2090+
last = nt - 1
2091+
nsave = (last + factor - 1) // factor + 1
2092+
2093+
ctsnap = ConditionalDimension(name='ctsnap', parent=time,
2094+
condition=CondEq(time, last),
2095+
relation=relation)
2096+
ct0 = ConditionalDimension(name='ct0', parent=time, factor=factor,
2097+
relation=Or)
2098+
2099+
u = TimeFunction(name='u', grid=grid, save=nt)
2100+
usave = TimeFunction(name='usave', grid=grid, time_dim=ct0, save=nsave)
2101+
u.data[:] = np.arange(nt).reshape(nt, 1, 1)
2102+
2103+
Operator(Eq(usave, u))(time_m=0, time_M=last - 1)
2104+
Operator(Eq(usave, u, implicit_dims=ctsnap))(time_m=last, time_M=last)
2105+
2106+
expected = factor * np.arange(nsave)
2107+
expected[-1] = last
2108+
assert np.all(usave.data[:, 1, 1] == expected)
2109+
20772110
def test_blocking_w_guard(self):
20782111
grid = Grid(shape=(8, 8, 8))
20792112
x, y, z = grid.dimensions

0 commit comments

Comments
 (0)