Skip to content

Commit 9904709

Browse files
CodexFabioLuporini
authored andcommitted
compiler: Cache finite-difference weight properties
1 parent e4252f1 commit 9904709

2 files changed

Lines changed: 99 additions & 0 deletions

File tree

‎devito/finite_differences/differentiable.py‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,6 +1111,57 @@ def dimension(self):
11111111

11121112
weights = Array.initvalue
11131113

1114+
@cached_property
1115+
def symmetry(self):
1116+
"""
1117+
The sign relating mirrored coefficients: `1` for symmetric weights,
1118+
`-1` for antisymmetric weights, or `None` otherwise.
1119+
1120+
An odd antisymmetric sequence must have a zero center coefficient.
1121+
"""
1122+
weights = self.weights
1123+
if not weights:
1124+
return None
1125+
1126+
middle = len(weights) // 2
1127+
pairs = tuple(zip(weights[:middle],
1128+
reversed(weights[middle + len(weights) % 2:])))
1129+
1130+
if all(left.equals(right) is True for left, right in pairs):
1131+
return sympy.S.One
1132+
if all(left.equals(-right) is True for left, right in pairs) and \
1133+
(len(weights) % 2 == 0 or weights[middle].equals(0) is True):
1134+
return sympy.S.NegativeOne
1135+
1136+
return None
1137+
1138+
@cached_property
1139+
def coefficient_span(self):
1140+
"""
1141+
Paired weights and their offset within the original stencil.
1142+
1143+
Symmetric or antisymmetric weights retain their full span, including
1144+
symmetric zero padding. Otherwise, trim known zeros at both ends and
1145+
return rebuilt weights together with their starting offset. The
1146+
resulting weights may still have no symmetry.
1147+
"""
1148+
if self.symmetry is not None:
1149+
return self, 0
1150+
1151+
weights = self.weights
1152+
first, last = 0, len(weights)
1153+
while first < last and weights[first].is_zero is True:
1154+
first += 1
1155+
while last > first and weights[last - 1].is_zero is True:
1156+
last -= 1
1157+
1158+
if first == 0 and last == len(weights):
1159+
return self, 0
1160+
1161+
d = self.dimension
1162+
dimension = d._rebuild(_min=d._min + first, _max=d._min + last - 1)
1163+
return self._rebuild(dimensions=dimension, initvalue=weights[first:last]), first
1164+
11141165
def _xreplace(self, rule):
11151166
if self in rule:
11161167
return rule[self], True

‎tests/test_derivatives.py‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,54 @@ def test_unexpand_space_interp_w_saved_timefunc(self):
941941
assert_structure(op, ['t,x,y,z', 't,x,y,z,i1', 't,x,y,z,i1,i0'])
942942

943943

944+
class TestWeights:
945+
946+
@pytest.mark.parametrize('coeffs,expected', [
947+
((1, 2, -2, -1), -1),
948+
((1, 2, 0, -2, -1), -1),
949+
((1, 2, 2, 1), 1),
950+
((1, 2, 3, 2, 1), 1),
951+
((1, 2, 3, -2, -1), None),
952+
((0, 0, 0), 1),
953+
((0,), 1),
954+
((3,), 1),
955+
((Symbol('a'), 0, -Symbol('a')), -1),
956+
((Symbol('a'), Symbol('b'), Symbol('a')), 1),
957+
((Symbol('a'), Symbol('b')), None),
958+
])
959+
def test_symmetry(self, coeffs, expected):
960+
i = StencilDimension('i', 0, len(coeffs) - 1)
961+
weights = Weights(name='w', dimensions=i, initvalue=coeffs)
962+
963+
assert weights.symmetry == expected
964+
assert weights._rebuild().symmetry == expected
965+
966+
@pytest.mark.parametrize('coeffs,expected,offset,sign', [
967+
((0, 1, 2, -2, -1), (1, 2, -2, -1), 1, -1),
968+
((1, 2, -2, -1, 0), (1, 2, -2, -1), 0, -1),
969+
((0, 0, 1, 2, 2, 1, 0), (1, 2, 2, 1), 2, 1),
970+
((0, 1, 2, -2, -1, 0), (0, 1, 2, -2, -1, 0), 0, -1),
971+
((0, 1, 2, 3, 0), (1, 2, 3), 1, None),
972+
((1, 2, 3), (1, 2, 3), 0, None),
973+
((0, 0, 0), (0, 0, 0), 0, 1),
974+
((0, Symbol('a'), -Symbol('a')), (Symbol('a'), -Symbol('a')), 1, -1),
975+
])
976+
def test_coefficient_span(self, coeffs, expected, offset, sign):
977+
i = StencilDimension('i', -3, len(coeffs) - 4)
978+
weights = Weights(name='w', dimensions=i, initvalue=coeffs)
979+
span, start = weights.coefficient_span
980+
981+
assert span.weights == expected
982+
assert span.symmetry == sign
983+
assert start == offset
984+
assert span.dimension._min == i._min + offset
985+
assert span.dimension._size == len(expected)
986+
assert weights.coefficient_span[0] is span
987+
assert weights._rebuild().coefficient_span == (span, start)
988+
if coeffs == expected:
989+
assert span is weights
990+
991+
944992
class TestTwoStageEvaluation:
945993

946994
def test_exceptions(self):

0 commit comments

Comments
 (0)