"""Tests for Layout and Fragment equality comparison.""" import tilelang import tilelang.testing from tilelang.layout import Layout from tilelang.layout.fragment import Fragment tilelang.testing.set_random_seed() class TestLayoutEqual: """Test cases for Layout.is_equal().""" def test_same_layout_is_equal(self): """Two layouts with identical mapping should be equal.""" layout1 = Layout([32, 4], lambda i, j: i * 4 + j) layout2 = Layout([32, 4], lambda i, j: i * 4 + j) assert layout1.is_equal(layout2) def test_different_index_order_not_equal(self): """Layouts with different index order (i*4+j vs j*4+i) should not be equal.""" layout1 = Layout([32, 4], lambda i, j: i * 4 + j) layout2 = Layout([32, 4], lambda i, j: j * 4 + i) assert not layout1.is_equal(layout2) def test_different_coefficient_not_equal(self): """Layouts with different coefficients should not be equal.""" layout1 = Layout([32, 4], lambda i, j: i * 4 + j) layout2 = Layout([32, 4], lambda i, j: i * 8 + j) assert not layout1.is_equal(layout2) def test_different_shape_not_equal(self): """Layouts with different shapes should not be equal.""" layout1 = Layout([32, 4], lambda i, j: i * 4 + j) layout2 = Layout([16, 8], lambda i, j: i * 8 + j) assert not layout1.is_equal(layout2) def test_same_layout_different_var_names(self): """Layouts with same mapping but created with different variable names should be equal.""" layout1 = Layout([32, 4], lambda x, y: x * 4 + y) layout2 = Layout([32, 4], lambda a, b: a * 4 + b) assert layout1.is_equal(layout2) def test_2d_output_layout_equal(self): """Layouts with 2D output should compare correctly.""" layout1 = Layout([32, 4], lambda i, j: [i, j]) layout2 = Layout([32, 4], lambda i, j: [i, j]) assert layout1.is_equal(layout2) def test_2d_output_layout_different_order(self): """Layouts with swapped output dimensions should not be equal.""" layout1 = Layout([32, 4], lambda i, j: [i, j]) layout2 = Layout([32, 4], lambda i, j: [j, i]) assert not layout1.is_equal(layout2) def test_complex_expression_equal(self): """Layouts with complex but equivalent expressions should be equal.""" layout1 = Layout([16, 8], lambda i, j: i * 8 + j) layout2 = Layout([16, 8], lambda i, j: j + i * 8) # Note: This tests if the comparison handles commutative operations # With StructuralEqual, a*b+c and c+a*b have different AST structure # So this may or may not be equal depending on implementation # For now we test the actual behavior result = layout1.is_equal(layout2) # The key point is it should not crash and return a boolean assert isinstance(result, bool) class TestFragmentEqual: """Test cases for Fragment.is_equal().""" def test_same_fragment_is_equal(self): """Two fragments with identical mapping should be equal.""" frag1 = Fragment([32, 4], forward_thread_fn=lambda i, j: i * 4 + j) frag2 = Fragment([32, 4], forward_thread_fn=lambda i, j: i * 4 + j) assert frag1.is_equal(frag2) def test_different_thread_mapping_not_equal(self): """Fragments with different thread mapping (i*4+j vs j*4+i) should not be equal.""" frag1 = Fragment([32, 4], forward_thread_fn=lambda i, j: i * 4 + j) frag2 = Fragment([32, 4], forward_thread_fn=lambda i, j: j * 4 + i) assert not frag1.is_equal(frag2) def test_different_forward_index_not_equal(self): """Fragments with different forward_index should not be equal.""" frag1 = Fragment([32, 4], forward_thread_fn=lambda i, j: i * 4 + j, forward_index_fn=lambda i, j: i) frag2 = Fragment([32, 4], forward_thread_fn=lambda i, j: i * 4 + j, forward_index_fn=lambda i, j: j) assert not frag1.is_equal(frag2) def test_same_fragment_different_var_names(self): """Fragments with same mapping but different variable names should be equal.""" frag1 = Fragment([32, 4], forward_thread_fn=lambda x, y: x * 4 + y) frag2 = Fragment([32, 4], forward_thread_fn=lambda a, b: a * 4 + b) assert frag1.is_equal(frag2) def test_fragment_with_replicate_equal(self): """Fragments with same replicate factor should be equal.""" frag1 = Fragment([32, 4], forward_thread_fn=lambda i, j, rep: i * 4 + rep, replicate=4) frag2 = Fragment([32, 4], forward_thread_fn=lambda i, j, rep: i * 4 + rep, replicate=4) assert frag1.is_equal(frag2) def test_fragment_different_replicate_not_equal(self): """Fragments with different replicate factors should not be equal.""" frag1 = Fragment([32, 4], forward_thread_fn=lambda i, j, rep: i * 4 + rep, replicate=4) frag2 = Fragment([32, 4], forward_thread_fn=lambda i, j, rep: i * 4 + rep, replicate=2) assert not frag1.is_equal(frag2) def test_fragment_with_forward_fn(self): """Fragments created with forward_fn should compare correctly.""" frag1 = Fragment([32, 4], forward_fn=lambda i, j: (i * 4 + j, i * 4 + j)) frag2 = Fragment([32, 4], forward_fn=lambda i, j: (i * 4 + j, i * 4 + j)) assert frag1.is_equal(frag2) def test_fragment_forward_fn_different_thread(self): """Fragments with different thread mapping via forward_fn should not be equal.""" frag1 = Fragment([32, 4], forward_fn=lambda i, j: (i * 4 + j, i)) frag2 = Fragment([32, 4], forward_fn=lambda i, j: (j * 4 + i, i)) assert not frag1.is_equal(frag2) def test_fragment_forward_fn_different_index(self): """Fragments with different forward_index via forward_fn should not be equal.""" frag1 = Fragment([32, 4], forward_fn=lambda i, j: (i * 4 + j, i)) frag2 = Fragment([32, 4], forward_fn=lambda i, j: (i * 4 + j, j)) assert not frag1.is_equal(frag2) class TestLayoutFragmentEdgeCases: """Edge cases and regression tests.""" def test_single_dim_layout_equal(self): """Single dimension layouts should compare correctly.""" layout1 = Layout([128], lambda i: i) layout2 = Layout([128], lambda i: i) assert layout1.is_equal(layout2) def test_single_dim_layout_not_equal(self): """Single dimension layouts with different mappings should not be equal.""" layout1 = Layout([128], lambda i: i) layout2 = Layout([128], lambda i: i * 2) assert not layout1.is_equal(layout2) def test_three_dim_layout_equal(self): """Three dimension layouts should compare correctly.""" layout1 = Layout([8, 16, 4], lambda i, j, k: i * 64 + j * 4 + k) layout2 = Layout([8, 16, 4], lambda i, j, k: i * 64 + j * 4 + k) assert layout1.is_equal(layout2) def test_three_dim_layout_different_order(self): """Three dimension layouts with different index order should not be equal.""" layout1 = Layout([8, 16, 4], lambda i, j, k: i * 64 + j * 4 + k) layout2 = Layout([8, 16, 4], lambda i, j, k: k * 64 + j * 4 + i) assert not layout1.is_equal(layout2) def test_fragment_empty_forward_index(self): """Fragments with empty forward_index should compare correctly.""" frag1 = Fragment([32, 4], forward_thread_fn=lambda i, j: i * 4 + j) frag2 = Fragment([32, 4], forward_thread_fn=lambda i, j: i * 4 + j) assert frag1.is_equal(frag2) def test_constant_layout_equal(self): """Layouts mapping to constant should be equal.""" from tvm.tirx import const layout1 = Layout([32, 4], lambda i, j: const(0, "int32")) layout2 = Layout([32, 4], lambda i, j: const(0, "int32")) assert layout1.is_equal(layout2) def test_constant_vs_variable_layout_not_equal(self): """Layout mapping to constant vs variable should not be equal.""" from tvm.tirx import const layout1 = Layout([32, 4], lambda i, j: const(0, "int32")) layout2 = Layout([32, 4], lambda i, j: i) assert not layout1.is_equal(layout2) if __name__ == "__main__": tilelang.testing.main()