Skip to content

Commit 9d78954

Browse files
Merge pull request #2734 from devitocodes/guard-expr
compiler: Add GuardExpr utility class
2 parents 8fd3824 + 5413120 commit 9d78954

File tree

2 files changed

+53
-6
lines changed

2 files changed

+53
-6
lines changed

devito/ir/support/guards.py

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
"""
66

77
from sympy import And, Ge, Gt, Le, Lt, Mul, true
8+
from sympy.logic.boolalg import BooleanFunction
9+
import numpy as np
810

911
from devito.ir.support.space import Forward, IterationDirection
1012
from devito.symbolics import CondEq, CondNe
1113
from devito.tools import Pickable, as_tuple, frozendict, split
12-
from devito.types import Dimension
14+
from devito.types import Dimension, LocalObject
1315

1416
__all__ = ['GuardFactor', 'GuardBound', 'GuardBoundNext', 'BaseGuardBound',
15-
'BaseGuardBoundNext', 'GuardOverflow', 'Guards']
17+
'BaseGuardBoundNext', 'GuardOverflow', 'Guards', 'GuardExpr']
1618

1719

1820
class Guard:
@@ -273,6 +275,40 @@ def filter(self, key):
273275
return Guards(m)
274276

275277

278+
class GuardExpr(LocalObject, BooleanFunction):
279+
280+
"""
281+
A boolean symbol that can be used as a guard. As such, it can be chained
282+
with other relations using the standard boolean operators (&, |, ...).
283+
284+
Being a LocalObject, a GuardExpr may carry an `initvalue`, which is
285+
the value that the guard assumes at the beginning of the scope where
286+
it is defined.
287+
288+
Through the `supersets` argument, a GuardExpr may also carry a set of
289+
GuardExprs that are known to be more restrictive than itself. This is
290+
usesful, e.g., to avoid redundant checks when chaining multiple guards
291+
together (see `simplify_and`).
292+
"""
293+
294+
dtype = np.bool
295+
296+
def __init__(self, name, liveness='eager', supersets=None, **kwargs):
297+
super().__init__(name, liveness=liveness, **kwargs)
298+
299+
self.supersets = frozenset(as_tuple(supersets))
300+
301+
def _hashable_content(self):
302+
return super()._hashable_content() + (self.supersets,)
303+
304+
__hash__ = LocalObject.__hash__
305+
306+
def __eq__(self, other):
307+
return (isinstance(other, GuardExpr) and
308+
super().__eq__(other) and
309+
self.supersets == other.supersets)
310+
311+
276312
# *** Utils
277313

278314
def simplify_and(relation, v):
@@ -291,10 +327,18 @@ def simplify_and(relation, v):
291327
else:
292328
candidates, other = [], [relation, v]
293329

330+
# Quick check based on GuardExpr.supersets to avoid adding `v` to `relation`
331+
# if `relation` already includes a more restrictive guard than `v`
332+
if isinstance(v, GuardExpr):
333+
if any(a in v.supersets for a in candidates):
334+
return relation
335+
294336
covered = False
295337
new_args = []
296338
for a in candidates:
297-
if a.lhs is v.lhs:
339+
if isinstance(a, GuardExpr) or a.lhs is not v.lhs:
340+
new_args.append(a)
341+
else:
298342
covered = True
299343
try:
300344
if type(a) in (Gt, Ge) and v.rhs > a.rhs:
@@ -307,8 +351,7 @@ def simplify_and(relation, v):
307351
# E.g., `v.rhs = const + z_M` and `a.rhs = z_M`, so the inequalities
308352
# above are not evaluable to True/False
309353
new_args.append(a)
310-
else:
311-
new_args.append(a)
354+
312355
if not covered:
313356
new_args.append(v)
314357

devito/types/object.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,11 @@ def __init__(self, name, cargs=None, initvalue=None, liveness='lazy',
182182
is_global=False, **kwargs):
183183
self.name = name
184184
self.cargs = as_tuple(cargs)
185-
self.initvalue = initvalue or self.default_initvalue
185+
186+
if initvalue is None:
187+
self.initvalue = self.default_initvalue
188+
else:
189+
self.initvalue = initvalue
186190

187191
assert liveness in ['eager', 'lazy']
188192
self._liveness = liveness

0 commit comments

Comments
 (0)