From 1c717588223e3aa015825863ebe45fa6ad3a809d Mon Sep 17 00:00:00 2001 From: Fabio Luporini Date: Sat, 15 Aug 2026 14:27:01 +0100 Subject: [PATCH] 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 a805fb01cf..a672f1fa53 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 @@ -1162,6 +1163,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]),