From 25c2dc1f4b2870cc3974b0d5169d5883f4f3f48e Mon Sep 17 00:00:00 2001 From: Fabio Luporini Date: Sat, 15 Aug 2026 14:27:01 +0100 Subject: [PATCH 1/2] compiler: Avoid fusing Clusters with different data types --- devito/ir/clusters/cluster.py | 3 +++ tests/test_dse.py | 2 +- tests/test_ir.py | 21 +++++++++++++++++++++ tests/test_operator.py | 10 ++++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/devito/ir/clusters/cluster.py b/devito/ir/clusters/cluster.py index c4b98ca4f9..f45fa224e6 100644 --- a/devito/ir/clusters/cluster.py +++ b/devito/ir/clusters/cluster.py @@ -531,6 +531,9 @@ def from_clusters(cls, *clusters): if not all(root.guards == c.guards for c in clusters): raise ValueError("Cannot build a Cluster from Clusters with " "non-homogeneous guards") + if not all(root.dtype == c.dtype for c in clusters): + raise ValueError("Cannot build a Cluster from Clusters with " + "non-homogeneous data types") writes = set().union(*[c.scope.writes for c in clusters]) reads = set().union(*[c.scope.reads for c in clusters]) diff --git a/tests/test_dse.py b/tests/test_dse.py index d9f210bd9e..47b623f0cb 100644 --- a/tests/test_dse.py +++ b/tests/test_dse.py @@ -2961,7 +2961,7 @@ def test_fullopt(self): assert np.isclose(summary0[('section0', None)].oi, 3.136, atol=0.001) assert summary1[('section0', None)].ops == 31 - assert summary1[('section1', None)].ops == 16 + assert summary1[('section1', None)].ops == 8 assert summary1[('section2', None)].ops == 4 assert np.isclose(summary1[('section0', None)].oi, 1.767, atol=0.001) diff --git a/tests/test_ir.py b/tests/test_ir.py index 1e4456e1f0..9f26ecb8c5 100644 --- a/tests/test_ir.py +++ b/tests/test_ir.py @@ -12,6 +12,7 @@ from devito.ir.equations import LoweredEq from devito.ir.equations.algorithms import dimension_sort from devito.ir.iet import FindNodes, Iteration +from devito.ir.stree import stree_build from devito.ir.support.basic import ( AFFINE, IRREGULAR, REGULAR, IterationInstance, Scope, TimedAccess, Vector, mocksym0, mocksym1 @@ -1167,6 +1168,26 @@ def test_dimension_sort(self, expr, expected): assert list(dimension_sort(expr)) == eval(expected) +class TestCluster: + + def test_from_clusters_mixed_dtypes(self): + grid = Grid(shape=(4,)) + x, = grid.dimensions + + f = Function(name='f', grid=grid, dtype=np.float32) + g = Function(name='g', grid=grid, dtype=np.float64) + + ispace = IterationSpace([Interval(x)]) + clusters = (Cluster(Eq(f, 1), ispace=ispace), + Cluster(Eq(g, 1), ispace=ispace)) + + with pytest.raises(ValueError, match="non-homogeneous data types"): + Cluster.from_clusters(*clusters) + + stree = stree_build(clusters) + assert len([i for i in stree.visit() if i.is_Iteration]) == 1 + + class TestClusterGroup: def test_eq_hash_include_ispace(self): diff --git a/tests/test_operator.py b/tests/test_operator.py index f781819f7c..32bc7c4953 100644 --- a/tests/test_operator.py +++ b/tests/test_operator.py @@ -1447,6 +1447,16 @@ def test_permutations_without_deps(self): exprs = FindNodes(Expression).visit(tree[-1]) assert len(exprs) == 3 + def test_fusion_mixed_dtypes(self): + grid = Grid(shape=(4, 4)) + + f = Function(name='f', grid=grid, dtype=np.uint16) + g = Function(name='g', grid=grid, dtype=np.float32) + + op = Operator([Eq(f, 1), Eq(g, 1)]) + + assert_structure(op, ['x,y'], 'x,y') + @pytest.mark.parametrize('exprs,fissioned,shared', [ # 0) Trivial case (('Eq(u, 1)', 'Eq(v, u.dxl)'), '(1,x)', [0]), From a550e39de520abd269e107fe289d758e89569e8a Mon Sep 17 00:00:00 2001 From: mloubout Date: Mon, 17 Aug 2026 16:16:13 -0400 Subject: [PATCH 2/2] tests: slightly relax tolerance to avoid compiler missfire --- tests/test_buffering.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_buffering.py b/tests/test_buffering.py index 1676b2d218..eb2ac804ab 100644 --- a/tests/test_buffering.py +++ b/tests/test_buffering.py @@ -641,8 +641,8 @@ def test_stencil_w_interp(): op0.apply(time_M=nt-2) op1.apply(time_M=nt-2) - assert np.all(u.data == u1.data) - assert np.all(rec.data == rec1.data) + assert np.allclose(u.data, u1.data, rtol=1e-6) + assert np.allclose(rec.data, rec1.data, rtol=1e-6) def test_issue_1901():