Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions devito/ir/clusters/algorithms.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,9 @@ def guard(clusters):
else:
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}

# Construct a guarded Cluster
processed.append(c.rebuild(exprs=exprs, guards=guards))

Expand Down
9 changes: 6 additions & 3 deletions devito/ir/support/guards.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def negated(self):
# *** GuardFactor


class GuardFactor(Guard, CondEq, Pickable):
class GuardFactor(Guard):

"""
A guard for factor-based ConditionalDimensions.
Expand Down Expand Up @@ -86,11 +86,14 @@ def _args_rebuild(self):
return (self.d,)


class GuardFactorEq(GuardFactor, CondEq):
# The relational bases are mixed in by the leaves only, or `CondEq` would
# precede `CondNe` in the MRO of `GuardFactorNe`, making it an equality. E.g.,
# `Not(GuardFactorEq(d))` would then be printed as `time % k == 0`
class GuardFactorEq(GuardFactor, CondEq, Pickable):
pass


class GuardFactorNe(GuardFactor, CondNe):
class GuardFactorNe(GuardFactor, CondNe, Pickable):
pass


Expand Down
7 changes: 7 additions & 0 deletions devito/passes/iet/languages/C.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ class CPrinter(BasePrinter, C99CodePrinter):
def _print_ImaginaryUnit(self, expr):
return '_Complex_I'

# C99 has no `true`/`false` without `stdbool.h`, which is never included
def _print_BooleanTrue(self, expr):
return '1'

def _print_BooleanFalse(self, expr):
return '0'

def _print_ListInitializer(self, expr):
li = super()._print_ListInitializer(expr)
if expr.dtype:
Expand Down
31 changes: 31 additions & 0 deletions tests/test_buffering.py
Original file line number Diff line number Diff line change
Expand Up @@ -1179,3 +1179,34 @@ def test_buffering_multi_cond(factor):
# The last sample is on the subsampled grid, hence no extra slot
expected[-2:] = [ntmod - 1, 0]
assert np.allclose(f_all.data[:, 11, 11], expected)


def test_true_condition_single_buffer():
"""
A condition folded to `true` (`time` is nonnegative, so `time >= 0` is
`true`) imposes no guard. Reads of `f` under it and unguarded reads of `f`
must thus share one buffer.
"""
grid = Grid(shape=(4, 4))
time = grid.time_dim
nt = 4

f = TimeFunction(name='f', grid=grid, time_order=0, space_order=0, save=nt)
g = TimeFunction(name='g', grid=grid, time_order=0, space_order=0)
h = TimeFunction(name='h', grid=grid, time_order=0, space_order=0)
for i in range(nt):
f.data[i] = i

ctrue = ConditionalDimension(name='ctrue', parent=time, condition=time >= 0,
relation='strict')

eqs = [Eq(g, g + f, implicit_dims=ctrue), Eq(h, h + 2*f)]

op = Operator(eqs, opt='buffering', name='TrueCondition')

buffers = [i for i in FindSymbols().visit(op.body) if i.is_Array and i._mem_heap]
assert len(buffers) == 1

op.apply(time_m=0, time_M=nt-1)
assert np.allclose(g.data[0], 6.)
assert np.allclose(h.data[0], 12.)
44 changes: 42 additions & 2 deletions tests/test_dimension.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import numpy as np
import pytest
from sympy import And, Or
from sympy import And, Not, Or, false

from conftest import assert_blocking, assert_structure, opts_tiling, skipif
from devito import ( # noqa
Expand All @@ -13,7 +13,7 @@
configuration, dimensions, floor, norm, sin, sum, switchconfig
)
from devito.exceptions import InvalidArgument
from devito.ir import SymbolRegistry
from devito.ir import GuardFactor, SymbolRegistry
from devito.ir.equations.algorithms import concretize_subdims
from devito.ir.iet import (
Conditional, Expression, FindNodes, FindSymbols, Iteration, retrieve_iteration_tree
Expand Down Expand Up @@ -2128,6 +2128,46 @@ def test_blocking_w_guard(self):
't,x0_blk0,y0_blk0,x,y,z'],
't,x0_blk0,y0_blk0,x,y,z,z')

def test_false_condition(self):
"""
A condition that SymPy folds to `false` (`time` is nonnegative) must
still be printed as valid C.
"""
grid = Grid(shape=(4, 4))
time = grid.time_dim

condition = time < 0
assert condition is false
ct = ConditionalDimension(name='ct', parent=time, condition=condition)

T = TimeFunction(name='T', grid=grid, time_order=0)
f = TimeFunction(name='f', grid=grid, time_order=0, save=2)

op = Operator([Eq(f, T + 1, implicit_dims=ct), Eq(T, T + 1)],
name='FalseCondition')
op.apply(time_M=1)

assert np.all(f.data[:, 1, 1] == [0, 0])

def test_negated_factor_condition(self):
"""
Negating a subsampling guard must select the timesteps off the
subsampled grid.
"""
grid = Grid(shape=(4, 4))
time = grid.time_dim

ct = ConditionalDimension(name='ct', parent=time, factor=2)
cn = ConditionalDimension(name='cn', parent=time,
condition=Not(GuardFactor(ct)), relation='strict')

f = TimeFunction(name='f', grid=grid, time_order=0, save=4)

op = Operator(Eq(f, time + 1, implicit_dims=cn), name='NegatedFactor')
op.apply(time_M=3, ctf=2)

assert np.all(f.data[:, 1, 1] == [0, 2, 0, 4])


class TestCustomDimension:

Expand Down
Loading