From d1b1e69e1cd3c69b06ca4bd7fa6780bbae637c01 Mon Sep 17 00:00:00 2001 From: George Bisbas Date: Thu, 23 Nov 2023 17:04:11 +0000 Subject: [PATCH 01/12] tests: enhance indirection testing --- tests/test_operator.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/tests/test_operator.py b/tests/test_operator.py index d5759c1c92..fb0aaafae8 100644 --- a/tests/test_operator.py +++ b/tests/test_operator.py @@ -1990,8 +1990,11 @@ def test_2194_v2(self, eqns, expected, exp_trees, exp_iters): class TestInternals: - def test_indirection(self): - nt = 10 + @pytest.mark.parametrize('nt, offset, epass', + ([1, 1, True], [1, 2, False], + [5, 1, True], [3, 5, False], + [4, 1, True], [5, 10, False])) + def test_indirection(self, nt, offset, epass): grid = Grid(shape=(4, 4)) time = grid.time_dim x, y = grid.dimensions @@ -1999,7 +2002,7 @@ def test_indirection(self): f = TimeFunction(name='f', grid=grid, save=nt) g = TimeFunction(name='g', grid=grid) - idx = time + 1 + idx = time + offset s = Indirection(name='ofs0', mapped=idx) eqns = [ @@ -2010,10 +2013,10 @@ def test_indirection(self): op = Operator(eqns) assert op._dspace[time].lower == 0 - assert op._dspace[time].upper == 1 - assert op.arguments()['time_M'] == nt - 2 + assert op._dspace[time].upper == offset - op() - - assert np.all(f.data[0] == 0.) - assert np.all(f.data[i] == 3. for i in range(1, 10)) + if epass: + assert op.arguments()['time_M'] == nt - offset - 1 + op() + assert np.all(f.data[0] == 0.) + assert np.all(f.data[i] == 3. for i in range(1, nt)) From c044537cf7ee4b7ced44d4ee488f9b99ca3f8c92 Mon Sep 17 00:00:00 2001 From: George Bisbas Date: Fri, 13 Oct 2023 17:50:37 +0100 Subject: [PATCH 02/12] compiler: Intersect intervals from buffered TimeDimensions --- devito/ir/clusters/cluster.py | 16 ++++++++++++++++ devito/operator/operator.py | 1 + tests/test_buffering.py | 21 +++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/devito/ir/clusters/cluster.py b/devito/ir/clusters/cluster.py index 629ebdde4a..a69043ad8b 100644 --- a/devito/ir/clusters/cluster.py +++ b/devito/ir/clusters/cluster.py @@ -385,12 +385,28 @@ def dspace(self): # Construct the `intervals` of the DataSpace, that is a global, # Dimension-centric view of the data space + intervals = IntervalGroup.generate('union', *parts.values()) # E.g., `db0 -> time`, but `xi NOT-> x` intervals = intervals.promote(lambda d: not d.is_Sub) intervals = intervals.zero(set(intervals.dimensions) - oobs) + # Intersect with intervals from buffered dimensions. Unions of + # buffered dimension intervals may result in shrinking time size + try: + proc = [] + for f, v in parts.items(): + if f.save: + for i in v: + if i.dim.is_Time: + proc.append(intervals[i.dim].intersection(i)) + else: + proc.append(intervals[i.dim]) + intervals = IntervalGroup(proc) + except AttributeError: + pass + return DataSpace(intervals, parts) @cached_property diff --git a/devito/operator/operator.py b/devito/operator/operator.py index 9bfdcdc255..f33a81d4ae 100644 --- a/devito/operator/operator.py +++ b/devito/operator/operator.py @@ -240,6 +240,7 @@ def _build(cls, expressions, **kwargs): op._reads = filter_sorted(flatten(e.reads for e in irs.expressions)) op._writes = filter_sorted(flatten(e.writes for e in irs.expressions)) op._dimensions = set().union(*[e.dimensions for e in irs.expressions]) + # import pdb;pdb.set_trace() op._dtype, op._dspace = irs.clusters.meta op._profiler = profiler diff --git a/tests/test_buffering.py b/tests/test_buffering.py index a7472ae22c..cbc90720cd 100644 --- a/tests/test_buffering.py +++ b/tests/test_buffering.py @@ -727,3 +727,24 @@ def test_stencil_issue_1915_v2(subdomain): op1.apply(time_M=nt-2, u=u1) assert np.all(u.data == u1.data) + + +def test_default_timeM(): + """ + MFE for issue #2235 + """ + grid = Grid(shape=(4, 4)) + + u = TimeFunction(name='u', grid=grid) + usave = TimeFunction(name='usave', grid=grid, save=5) + + eqns = [Eq(u.forward, u + 1), + Eq(usave, u)] + + op = Operator(eqns) + + assert op.arguments()['time_M'] == 4 + + op.apply() + + assert all(np.all(usave.data[i] == i) for i in range(4)) From 7c02bbe9b7d584aad6499a16fa307309969f0f97 Mon Sep 17 00:00:00 2001 From: George Bisbas Date: Fri, 13 Oct 2023 18:10:14 +0100 Subject: [PATCH 03/12] tests: Edit test_indirection --- devito/ir/clusters/cluster.py | 2 -- devito/operator/operator.py | 1 - 2 files changed, 3 deletions(-) diff --git a/devito/ir/clusters/cluster.py b/devito/ir/clusters/cluster.py index a69043ad8b..a256a84517 100644 --- a/devito/ir/clusters/cluster.py +++ b/devito/ir/clusters/cluster.py @@ -385,9 +385,7 @@ def dspace(self): # Construct the `intervals` of the DataSpace, that is a global, # Dimension-centric view of the data space - intervals = IntervalGroup.generate('union', *parts.values()) - # E.g., `db0 -> time`, but `xi NOT-> x` intervals = intervals.promote(lambda d: not d.is_Sub) intervals = intervals.zero(set(intervals.dimensions) - oobs) diff --git a/devito/operator/operator.py b/devito/operator/operator.py index f33a81d4ae..9bfdcdc255 100644 --- a/devito/operator/operator.py +++ b/devito/operator/operator.py @@ -240,7 +240,6 @@ def _build(cls, expressions, **kwargs): op._reads = filter_sorted(flatten(e.reads for e in irs.expressions)) op._writes = filter_sorted(flatten(e.writes for e in irs.expressions)) op._dimensions = set().union(*[e.dimensions for e in irs.expressions]) - # import pdb;pdb.set_trace() op._dtype, op._dspace = irs.clusters.meta op._profiler = profiler From 80328262b590b0f1a7b09c8191d1ae4a05e682f8 Mon Sep 17 00:00:00 2001 From: George Bisbas Date: Tue, 17 Oct 2023 18:09:30 +0100 Subject: [PATCH 04/12] compiler: Only relax upper dspace in case of save --- devito/ir/clusters/cluster.py | 20 ++++++-------------- devito/ir/support/space.py | 10 ++++++++++ tests/test_buffering.py | 21 --------------------- tests/test_checkpointing.py | 2 +- tests/test_dimension.py | 21 ++++++++++++++++++++- 5 files changed, 37 insertions(+), 37 deletions(-) diff --git a/devito/ir/clusters/cluster.py b/devito/ir/clusters/cluster.py index a256a84517..cc655bde26 100644 --- a/devito/ir/clusters/cluster.py +++ b/devito/ir/clusters/cluster.py @@ -386,24 +386,16 @@ def dspace(self): # Construct the `intervals` of the DataSpace, that is a global, # Dimension-centric view of the data space intervals = IntervalGroup.generate('union', *parts.values()) + # E.g., `db0 -> time`, but `xi NOT-> x` intervals = intervals.promote(lambda d: not d.is_Sub) intervals = intervals.zero(set(intervals.dimensions) - oobs) - # Intersect with intervals from buffered dimensions. Unions of - # buffered dimension intervals may result in shrinking time size - try: - proc = [] - for f, v in parts.items(): - if f.save: - for i in v: - if i.dim.is_Time: - proc.append(intervals[i.dim].intersection(i)) - else: - proc.append(intervals[i.dim]) - intervals = IntervalGroup(proc) - except AttributeError: - pass + # Buffered TimeDimensions should not shirnk their upper time offset + for f, v in parts.items(): + if f.is_TimeFunction: + if f.save and not f.time_dim.is_Conditional: + intervals = intervals.ceil(v[f.time_dim]) return DataSpace(intervals, parts) diff --git a/devito/ir/support/space.py b/devito/ir/support/space.py index 6a11acdc28..5831b1b70a 100644 --- a/devito/ir/support/space.py +++ b/devito/ir/support/space.py @@ -259,6 +259,11 @@ def negate(self): def zero(self): return Interval(self.dim, 0, 0, self.stamp) + def ceil(self, o): + if o.is_Null: + return self._rebuild() + return Interval(self.dim, self.lower, o.upper, self.stamp) + def flip(self): return Interval(self.dim, self.upper, self.lower, self.stamp) @@ -496,6 +501,11 @@ def zero(self, d=None): return IntervalGroup(intervals, relations=self.relations, mode=self.mode) + def ceil(self, o=None): + d = self.dimensions if o is None else as_tuple(o.dim) + return IntervalGroup([i.ceil(o) if i.dim in d else i for i in self], + relations=self.relations) + def lift(self, d=None, v=None): d = set(self.dimensions if d is None else as_tuple(d)) intervals = [i.lift(v) if i.dim._defines & d else i for i in self] diff --git a/tests/test_buffering.py b/tests/test_buffering.py index cbc90720cd..a7472ae22c 100644 --- a/tests/test_buffering.py +++ b/tests/test_buffering.py @@ -727,24 +727,3 @@ def test_stencil_issue_1915_v2(subdomain): op1.apply(time_M=nt-2, u=u1) assert np.all(u.data == u1.data) - - -def test_default_timeM(): - """ - MFE for issue #2235 - """ - grid = Grid(shape=(4, 4)) - - u = TimeFunction(name='u', grid=grid) - usave = TimeFunction(name='usave', grid=grid, save=5) - - eqns = [Eq(u.forward, u + 1), - Eq(usave, u)] - - op = Operator(eqns) - - assert op.arguments()['time_M'] == 4 - - op.apply() - - assert all(np.all(usave.data[i] == i) for i in range(4)) diff --git a/tests/test_checkpointing.py b/tests/test_checkpointing.py index 75cca861cc..0217f46d52 100644 --- a/tests/test_checkpointing.py +++ b/tests/test_checkpointing.py @@ -10,7 +10,7 @@ @switchconfig(log_level='WARNING') -def test_segmented_incremment(): +def test_segmented_increment(): """ Test for segmented operator execution of a one-sided first order function (increment). The corresponding set of stencil offsets in diff --git a/tests/test_dimension.py b/tests/test_dimension.py index 84df27ecef..553d0b99ab 100644 --- a/tests/test_dimension.py +++ b/tests/test_dimension.py @@ -231,6 +231,25 @@ def test_degenerate_to_zero(self): assert np.all(u.data == 10) + def test_default_timeM(self): + """ + MFE for issue #2235 + """ + grid = Grid(shape=(4, 4)) + + u = TimeFunction(name='u', grid=grid) + usave = TimeFunction(name='usave', grid=grid, save=5) + + eqns = [Eq(u.forward, u + 1), + Eq(usave, u)] + + op = Operator(eqns) + + assert op.arguments()['time_M'] == 4 + op.apply() + + assert all(np.all(usave.data[i] == i) for i in range(4)) + class TestSubDimension: @@ -825,7 +844,7 @@ def test_basic(self): eqns = [Eq(u.forward, u + 1.), Eq(u2.forward, u2 + 1.), Eq(usave, u)] op = Operator(eqns) - op.apply() + op.apply(time_M=nt-2) assert np.all(np.allclose(u.data[(nt-1) % 3], nt-1)) assert np.all([np.allclose(u2.data[i], i) for i in range(nt)]) assert np.all([np.allclose(usave.data[i], i*factor) From 5cb68f792129894aa13971ef8aebe460a4c4ffed Mon Sep 17 00:00:00 2001 From: George Bisbas Date: Wed, 18 Oct 2023 12:59:22 +0100 Subject: [PATCH 05/12] compiler: add mode to IG creation, rebase on 2208 --- devito/ir/support/space.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/devito/ir/support/space.py b/devito/ir/support/space.py index 5831b1b70a..64f1726d97 100644 --- a/devito/ir/support/space.py +++ b/devito/ir/support/space.py @@ -260,7 +260,7 @@ def zero(self): return Interval(self.dim, 0, 0, self.stamp) def ceil(self, o): - if o.is_Null: + if not self.is_compatible(o): return self._rebuild() return Interval(self.dim, self.lower, o.upper, self.stamp) @@ -399,7 +399,9 @@ def generate(cls, op, *interval_groups, relations=None): relations.update(set().union(*[ig.relations for ig in interval_groups])) modes = set(ig.mode for ig in interval_groups) + assert len(modes) <= 1 + try: mode = modes.pop() except KeyError: @@ -504,7 +506,7 @@ def zero(self, d=None): def ceil(self, o=None): d = self.dimensions if o is None else as_tuple(o.dim) return IntervalGroup([i.ceil(o) if i.dim in d else i for i in self], - relations=self.relations) + relations=self.relations, mode=self.mode) def lift(self, d=None, v=None): d = set(self.dimensions if d is None else as_tuple(d)) From 0a5c09009b6504f0d00a7ce7117411f6e9db0444 Mon Sep 17 00:00:00 2001 From: George Bisbas Date: Thu, 19 Oct 2023 17:57:06 +0100 Subject: [PATCH 06/12] compiler: simplify solution --- devito/ir/clusters/cluster.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/devito/ir/clusters/cluster.py b/devito/ir/clusters/cluster.py index cc655bde26..06faeaebdf 100644 --- a/devito/ir/clusters/cluster.py +++ b/devito/ir/clusters/cluster.py @@ -391,11 +391,14 @@ def dspace(self): intervals = intervals.promote(lambda d: not d.is_Sub) intervals = intervals.zero(set(intervals.dimensions) - oobs) - # Buffered TimeDimensions should not shirnk their upper time offset + # Buffered TimeDimensions should inherit the higher upper bound + # of the involved parts for f, v in parts.items(): - if f.is_TimeFunction: - if f.save and not f.time_dim.is_Conditional: + try: + if f.save: intervals = intervals.ceil(v[f.time_dim]) + except: + pass return DataSpace(intervals, parts) From 8587533c379181f21d43a6bb3212bbe0c52f9800 Mon Sep 17 00:00:00 2001 From: George Bisbas Date: Fri, 3 Nov 2023 16:53:16 +0000 Subject: [PATCH 07/12] compiler: Make DataSpace intervals get the upper limit available of time_dims --- devito/ir/clusters/cluster.py | 7 +++---- devito/ir/support/space.py | 2 -- devito/types/dimension.py | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/devito/ir/clusters/cluster.py b/devito/ir/clusters/cluster.py index 06faeaebdf..ef584bf95e 100644 --- a/devito/ir/clusters/cluster.py +++ b/devito/ir/clusters/cluster.py @@ -391,12 +391,11 @@ def dspace(self): intervals = intervals.promote(lambda d: not d.is_Sub) intervals = intervals.zero(set(intervals.dimensions) - oobs) - # Buffered TimeDimensions should inherit the higher upper bound - # of the involved parts + # DataSpace intervals should derive their upper bound from + # the higher upper bound available in the involved parts for f, v in parts.items(): try: - if f.save: - intervals = intervals.ceil(v[f.time_dim]) + intervals = intervals.ceil(v[f.time_dim]) except: pass diff --git a/devito/ir/support/space.py b/devito/ir/support/space.py index 64f1726d97..7e1def4536 100644 --- a/devito/ir/support/space.py +++ b/devito/ir/support/space.py @@ -399,9 +399,7 @@ def generate(cls, op, *interval_groups, relations=None): relations.update(set().union(*[ig.relations for ig in interval_groups])) modes = set(ig.mode for ig in interval_groups) - assert len(modes) <= 1 - try: mode = modes.pop() except KeyError: diff --git a/devito/types/dimension.py b/devito/types/dimension.py index 784dde499e..906fa50475 100644 --- a/devito/types/dimension.py +++ b/devito/types/dimension.py @@ -346,7 +346,7 @@ def _arg_check(self, args, size, interval): # Autopadding causes non-integer upper limit from devito.symbolics import normalize_args upper = interval.upper.subs(normalize_args(args)) - if args[self.max_name] + upper >= size: + if args[self.max_name] + upper > size: raise InvalidArgument("OOB detected due to %s=%d" % (self.max_name, args[self.max_name])) From d3f19af1236cd4b628c265f59c5536eee6cb1680 Mon Sep 17 00:00:00 2001 From: George Bisbas Date: Fri, 3 Nov 2023 16:57:32 +0000 Subject: [PATCH 08/12] tests: Restore redundant, fix tutorial --- examples/userapi/02_apply.ipynb | 22 +++++++++++++--------- tests/test_dimension.py | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/examples/userapi/02_apply.ipynb b/examples/userapi/02_apply.ipynb index 693c4eed08..80584051bd 100644 --- a/examples/userapi/02_apply.ipynb +++ b/examples/userapi/02_apply.ipynb @@ -142,7 +142,11 @@ " 'y_m': 0,\n", " 'y_size': 4,\n", " 'y_M': 3,\n", - " 'timers': }" + " 'h_x': 0.33333334,\n", + " 'h_y': 0.33333334,\n", + " 'o_x': 0.0,\n", + " 'o_y': 0.0,\n", + " 'timers': }" ] }, "execution_count": 5, @@ -246,14 +250,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "OOB detected due to time_M=2\n" + "OOB detected due to time_M=3\n" ] } ], "source": [ "from devito.exceptions import InvalidArgument\n", "try:\n", - " op.apply(time_M=2)\n", + " op.apply(time_M=3)\n", "except InvalidArgument as e:\n", " print(e)" ] @@ -419,8 +423,8 @@ { "data": { "text/plain": [ - "PerformanceSummary([('section0',\n", - " PerfEntry(time=3e-06, gflopss=0.0, gpointss=0.0, oi=0.0, ops=0, itershapes=[]))])" + "PerformanceSummary([(PerfKey(name='section0', rank=None),\n", + " PerfEntry(time=1e-06, gflopss=0.0, gpointss=0.0, oi=0.0, ops=0, itershapes=[]))])" ] }, "execution_count": 14, @@ -449,14 +453,14 @@ "name": "stderr", "output_type": "stream", "text": [ - "Operator `Kernel` run in 0.00 s\n" + "Operator `Kernel` ran in 0.01 s\n" ] }, { "data": { "text/plain": [ - "PerformanceSummary([('section0',\n", - " PerfEntry(time=3e-06, gflopss=0.021333333333333333, gpointss=0.010666666666666666, oi=0.16666666666666666, ops=2, itershapes=[(2, 4, 4)]))])" + "PerformanceSummary([(PerfKey(name='section0', rank=None),\n", + " PerfEntry(time=1e-06, gflopss=0.064, gpointss=0.032, oi=0.16666666666666666, ops=2, itershapes=((2, 4, 4),)))])" ] }, "execution_count": 15, @@ -525,7 +529,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.8" + "version": "3.10.12" } }, "nbformat": 4, diff --git a/tests/test_dimension.py b/tests/test_dimension.py index 553d0b99ab..feab3fc5f0 100644 --- a/tests/test_dimension.py +++ b/tests/test_dimension.py @@ -844,7 +844,7 @@ def test_basic(self): eqns = [Eq(u.forward, u + 1.), Eq(u2.forward, u2 + 1.), Eq(usave, u)] op = Operator(eqns) - op.apply(time_M=nt-2) + op.apply() assert np.all(np.allclose(u.data[(nt-1) % 3], nt-1)) assert np.all([np.allclose(u2.data[i], i) for i in range(nt)]) assert np.all([np.allclose(usave.data[i], i*factor) From 99dd259a1ef479ee0fd41e3e9ecd4b74523a24bf Mon Sep 17 00:00:00 2001 From: George Bisbas Date: Thu, 16 Nov 2023 15:49:12 +0000 Subject: [PATCH 09/12] compiler: Derive solution from oob set of dims --- devito/ir/clusters/cluster.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/devito/ir/clusters/cluster.py b/devito/ir/clusters/cluster.py index ef584bf95e..c4a2069ec6 100644 --- a/devito/ir/clusters/cluster.py +++ b/devito/ir/clusters/cluster.py @@ -391,13 +391,12 @@ def dspace(self): intervals = intervals.promote(lambda d: not d.is_Sub) intervals = intervals.zero(set(intervals.dimensions) - oobs) - # DataSpace intervals should derive their upper bound from - # the higher upper bound available in the involved parts + # Upper bound of intervals including dimensions classified for + # shifting should retain the "oobs" upper bound for f, v in parts.items(): - try: - intervals = intervals.ceil(v[f.time_dim]) - except: - pass + for i in v: + if i.dim in oobs: + intervals = intervals.ceil(v[i.dim]) return DataSpace(intervals, parts) From 3512c287d61ec4b3cfb36c502f9ddf81cfd803f6 Mon Sep 17 00:00:00 2001 From: George Bisbas Date: Thu, 23 Nov 2023 18:36:32 +0000 Subject: [PATCH 10/12] compiler: Rework correctness for indirections --- devito/ir/clusters/cluster.py | 7 ++++++- devito/types/dimension.py | 2 +- examples/userapi/02_apply.ipynb | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/devito/ir/clusters/cluster.py b/devito/ir/clusters/cluster.py index c4a2069ec6..efce1f8f22 100644 --- a/devito/ir/clusters/cluster.py +++ b/devito/ir/clusters/cluster.py @@ -396,7 +396,12 @@ def dspace(self): for f, v in parts.items(): for i in v: if i.dim in oobs: - intervals = intervals.ceil(v[i.dim]) + try: + if intervals[i.dim].upper > v[i.dim].upper and \ + bool(i.dim in f.dimensions): + intervals = intervals.ceil(v[i.dim]) + except AttributeError: + pass return DataSpace(intervals, parts) diff --git a/devito/types/dimension.py b/devito/types/dimension.py index 906fa50475..784dde499e 100644 --- a/devito/types/dimension.py +++ b/devito/types/dimension.py @@ -346,7 +346,7 @@ def _arg_check(self, args, size, interval): # Autopadding causes non-integer upper limit from devito.symbolics import normalize_args upper = interval.upper.subs(normalize_args(args)) - if args[self.max_name] + upper > size: + if args[self.max_name] + upper >= size: raise InvalidArgument("OOB detected due to %s=%d" % (self.max_name, args[self.max_name])) diff --git a/examples/userapi/02_apply.ipynb b/examples/userapi/02_apply.ipynb index 80584051bd..f8f730dd52 100644 --- a/examples/userapi/02_apply.ipynb +++ b/examples/userapi/02_apply.ipynb @@ -250,14 +250,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "OOB detected due to time_M=3\n" + "OOB detected due to time_M=2\n" ] } ], "source": [ "from devito.exceptions import InvalidArgument\n", "try:\n", - " op.apply(time_M=3)\n", + " op.apply(time_M=2)\n", "except InvalidArgument as e:\n", " print(e)" ] From 4e1e1426f3078df2512c4424c5ed84da71a345da Mon Sep 17 00:00:00 2001 From: George Bisbas Date: Fri, 24 Nov 2023 16:31:15 +0000 Subject: [PATCH 11/12] compiler: Relax intervals with upper from not mapped dimensions --- devito/ir/clusters/cluster.py | 24 ++++++++++++------------ devito/ir/support/space.py | 12 +++++------- tests/test_operator.py | 2 +- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/devito/ir/clusters/cluster.py b/devito/ir/clusters/cluster.py index efce1f8f22..22333f529f 100644 --- a/devito/ir/clusters/cluster.py +++ b/devito/ir/clusters/cluster.py @@ -387,22 +387,22 @@ def dspace(self): # Dimension-centric view of the data space intervals = IntervalGroup.generate('union', *parts.values()) + # 'union' may consume intervals (values) from keys that have dimensions + # not mapped to intervals e.g. issue #2235, resulting in reduced + # iteration size. Here, we relax this mapped upper interval, by + # intersecting intervals with matching only dimensions + for f, v in parts.items(): + for i in v: + # oobs check is not required but helps reduce + # interval reconstruction + if i.dim in oobs and i.dim in f.dimensions: + ii = intervals[i.dim].intersection(v[i.dim]) + intervals = intervals.set_upper(i.dim, ii.upper) + # E.g., `db0 -> time`, but `xi NOT-> x` intervals = intervals.promote(lambda d: not d.is_Sub) intervals = intervals.zero(set(intervals.dimensions) - oobs) - # Upper bound of intervals including dimensions classified for - # shifting should retain the "oobs" upper bound - for f, v in parts.items(): - for i in v: - if i.dim in oobs: - try: - if intervals[i.dim].upper > v[i.dim].upper and \ - bool(i.dim in f.dimensions): - intervals = intervals.ceil(v[i.dim]) - except AttributeError: - pass - return DataSpace(intervals, parts) @cached_property diff --git a/devito/ir/support/space.py b/devito/ir/support/space.py index 7e1def4536..3f11ec0bd2 100644 --- a/devito/ir/support/space.py +++ b/devito/ir/support/space.py @@ -259,10 +259,8 @@ def negate(self): def zero(self): return Interval(self.dim, 0, 0, self.stamp) - def ceil(self, o): - if not self.is_compatible(o): - return self._rebuild() - return Interval(self.dim, self.lower, o.upper, self.stamp) + def set_upper(self, v=0): + return Interval(self.dim, self.lower, v, self.stamp) def flip(self): return Interval(self.dim, self.upper, self.lower, self.stamp) @@ -501,9 +499,9 @@ def zero(self, d=None): return IntervalGroup(intervals, relations=self.relations, mode=self.mode) - def ceil(self, o=None): - d = self.dimensions if o is None else as_tuple(o.dim) - return IntervalGroup([i.ceil(o) if i.dim in d else i for i in self], + def set_upper(self, d, v=0): + dims = as_tuple(d) + return IntervalGroup([i.set_upper(v) if i.dim in dims else i for i in self], relations=self.relations, mode=self.mode) def lift(self, d=None, v=None): diff --git a/tests/test_operator.py b/tests/test_operator.py index fb0aaafae8..9d24566468 100644 --- a/tests/test_operator.py +++ b/tests/test_operator.py @@ -1992,7 +1992,7 @@ class TestInternals: @pytest.mark.parametrize('nt, offset, epass', ([1, 1, True], [1, 2, False], - [5, 1, True], [3, 5, False], + [5, 3, True], [3, 5, False], [4, 1, True], [5, 10, False])) def test_indirection(self, nt, offset, epass): grid = Grid(shape=(4, 4)) From fc398b67925bee34e80d6515e6bd3fc282340c68 Mon Sep 17 00:00:00 2001 From: George Bisbas Date: Wed, 20 Nov 2024 18:05:54 +0200 Subject: [PATCH 12/12] compiler: Fix occurence of NullInterval after rebase --- devito/deprecations.py | 4 ++-- devito/ir/clusters/cluster.py | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/devito/deprecations.py b/devito/deprecations.py index f13c145de5..7484d25a3a 100644 --- a/devito/deprecations.py +++ b/devito/deprecations.py @@ -6,14 +6,14 @@ class DevitoDeprecation(): @cached_property def coeff_warn(self): - warn("The Coefficient API is deprecated and will be removed, coefficients should" + warn("The Coefficient API is deprecated and will be removed, coefficients should " "be passed directly to the derivative object `u.dx(weights=...)", DeprecationWarning, stacklevel=2) return @cached_property def symbolic_warn(self): - warn("coefficients='symbolic' is deprecated, coefficients should" + warn("coefficients='symbolic' is deprecated, coefficients should " "be passed directly to the derivative object `u.dx(weights=...)", DeprecationWarning, stacklevel=2) return diff --git a/devito/ir/clusters/cluster.py b/devito/ir/clusters/cluster.py index 22333f529f..562512c252 100644 --- a/devito/ir/clusters/cluster.py +++ b/devito/ir/clusters/cluster.py @@ -393,11 +393,12 @@ def dspace(self): # intersecting intervals with matching only dimensions for f, v in parts.items(): for i in v: - # oobs check is not required but helps reduce - # interval reconstruction - if i.dim in oobs and i.dim in f.dimensions: + if i.dim in self.ispace and i.dim in f.dimensions: + # oobs check is not required but helps reduce + # interval reconstruction ii = intervals[i.dim].intersection(v[i.dim]) - intervals = intervals.set_upper(i.dim, ii.upper) + if not ii.is_Null: + intervals = intervals.set_upper(i.dim, ii.upper) # E.g., `db0 -> time`, but `xi NOT-> x` intervals = intervals.promote(lambda d: not d.is_Sub)