This is an automated email from the ASF dual-hosted git repository.
tlopex pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git
The following commit(s) were added to refs/heads/main by this push:
new 6b34c75a52 [Relax] Preserve out_dtype in AdjustMatmulOrder (#20296)
6b34c75a52 is described below
commit 6b34c75a52f4117e7d6a5452ae72b721e8be56c7
Author: T1Gang <[email protected]>
AuthorDate: Fri Sep 11 02:15:31 2026 +0800
[Relax] Preserve out_dtype in AdjustMatmulOrder (#20296)
Fixes #20202.
`AdjustMatmulOrder` rebuilt reassociated outer matmuls with
`out_dtype=void`, which could change the result dtype of the original
program. For example, a `float16` matmul chain whose outer matmul
explicitly
returns `float32` was rewritten to return `float16`.
This patch preserves the original outer `MatmulAttrs::out_dtype` when
constructing the replacement outer matmul. Newly introduced inner
matmuls
continue to infer their output dtype.
Tests:
- Added regression coverage for both reassociation directions.
- Covered explicit and inferred output dtypes.
- 12 new regression cases passed.
- Full `AdjustMatmulOrder` test file: 41 passed.
- `pre-commit` and `git diff --check`: passed.
---
src/relax/transform/adjust_matmul_order.cc | 10 ++--
.../relax/test_transform_adjust_matmul_order.py | 56 ++++++++++++++++++++++
2 files changed, 62 insertions(+), 4 deletions(-)
diff --git a/src/relax/transform/adjust_matmul_order.cc
b/src/relax/transform/adjust_matmul_order.cc
index 615bb6fd18..1630aeb664 100644
--- a/src/relax/transform/adjust_matmul_order.cc
+++ b/src/relax/transform/adjust_matmul_order.cc
@@ -24,6 +24,7 @@
#include <tvm/ffi/reflection/registry.h>
#include <tvm/relax/analysis.h>
+#include <tvm/relax/attrs/linear_algebra.h>
#include <tvm/relax/attrs/manipulate.h>
#include <tvm/relax/dataflow_matcher.h>
#include <tvm/relax/expr.h>
@@ -167,6 +168,7 @@ std::tuple<DFPattern, ffi::TypedFunction<Expr(Expr,
ffi::Map<DFPattern, Expr>)>>
auto expr_a = matches[pat_a];
auto expr_b = matches[pat_b];
auto expr_c = matches[pat_c];
+ auto out_dtype = expr.as<CallNode>()->attrs.as<MatmulAttrs>()->out_dtype;
// If all three components are compile-time, the order doesn't
// matter as the entire expression can be lifted out and
@@ -242,9 +244,9 @@ std::tuple<DFPattern, ffi::TypedFunction<Expr(Expr,
ffi::Map<DFPattern, Expr>)>>
// If two of the three are compile-time, group those two values
// together, to allow them to be lifted out and pre-computed.
if (is_compile_time(expr_a) && is_compile_time(expr_b)) {
- return matmul(matmul(expr_a, expr_b, std::nullopt), expr_c,
std::nullopt);
+ return matmul(matmul(expr_a, expr_b, std::nullopt), expr_c, out_dtype);
} else if (is_compile_time(expr_b) && is_compile_time(expr_c)) {
- return matmul(expr_a, matmul(expr_b, expr_c, std::nullopt),
std::nullopt);
+ return matmul(expr_a, matmul(expr_b, expr_c, std::nullopt), out_dtype);
}
// Otherwise, select the order that reduces the total number of
@@ -319,9 +321,9 @@ std::tuple<DFPattern, ffi::TypedFunction<Expr(Expr,
ffi::Map<DFPattern, Expr>)>>
size_N > 0 && size_R > 0 && size_M > 0 && size_B > 0);
if (analyzer->CanProve(ops_with_lhs_first < ops_with_rhs_first)) {
- return matmul(matmul(expr_a, expr_b, std::nullopt), expr_c,
std::nullopt);
+ return matmul(matmul(expr_a, expr_b, std::nullopt), expr_c, out_dtype);
} else if (analyzer->CanProve(ops_with_rhs_first < ops_with_lhs_first)) {
- return matmul(expr_a, matmul(expr_b, expr_c, std::nullopt),
std::nullopt);
+ return matmul(expr_a, matmul(expr_b, expr_c, std::nullopt), out_dtype);
}
// If we cannot determine which order is best, keep the existing order.
diff --git a/tests/python/relax/test_transform_adjust_matmul_order.py
b/tests/python/relax/test_transform_adjust_matmul_order.py
index eb886fd4d0..29603489f3 100644
--- a/tests/python/relax/test_transform_adjust_matmul_order.py
+++ b/tests/python/relax/test_transform_adjust_matmul_order.py
@@ -847,6 +847,62 @@ class TestBatchedSharedPrefixPreferLHSFirst(Base):
return out
[email protected]("lhs_first", [False, True])
[email protected]("compile_time,transpose", [(False, False), (False,
True), (True, False)])
[email protected]("out_dtype", [None, "float32"])
+def test_preserve_outer_out_dtype(lhs_first, compile_time, transpose,
out_dtype):
+ # Compile-time grouping should override the cheaper evaluation order.
+ shapes = [(2, 20), (20, 2), (2, 10)]
+ if lhs_first == compile_time:
+ shapes = [(10, 2), (2, 20), (20, 2)]
+ inner_indices = (1, 2) if lhs_first else (0, 1)
+ if transpose:
+ for i in inner_indices:
+ shapes[i] = shapes[i][::-1]
+
+ def build_module(expected):
+ bb = relax.BlockBuilder()
+ a, b, c = [
+ relax.Var(name, relax.TensorType(shape, "float16"))
+ for name, shape in zip("abc", shapes)
+ ]
+ params = [c, a, b] if lhs_first else [a, b, c]
+ attrs = {"num_input": 1} if compile_time else None
+ with bb.function("main", params, attrs=attrs):
+ with bb.dataflow():
+ operands = [a, b, c]
+ if expected and transpose:
+ for i in inner_indices:
+ operands[i] = relax.op.permute_dims(operands[i])
+ a, b, c = operands
+ if expected:
+ if lhs_first:
+ out = relax.op.matmul(relax.op.matmul(a, b), c,
out_dtype=out_dtype)
+ else:
+ out = relax.op.matmul(a, relax.op.matmul(b, c),
out_dtype=out_dtype)
+ else:
+ x, y = (b, c) if lhs_first else (a, b)
+ inner = bb.emit(relax.op.matmul(y, x) if transpose else
relax.op.matmul(x, y))
+ if transpose:
+ inner = bb.emit(relax.op.permute_dims(inner))
+ out = (
+ relax.op.matmul(a, inner, out_dtype=out_dtype)
+ if lhs_first
+ else relax.op.matmul(inner, c, out_dtype=out_dtype)
+ )
+ output = bb.emit_output(out)
+ bb.emit_func_output(output)
+ return bb.finalize()
+
+ before = build_module(expected=False)
+ expected = build_module(expected=True)
+ transform = relax.transform.AdjustMatmulOrder()
+ after = transform(before)
+ assert after["main"].ret_ty.dtype == (out_dtype or "float16")
+ tvm.ir.assert_structural_equal(after, expected)
+ tvm.ir.assert_structural_equal(transform(after), after)
+
+
class TestAdjustMatmulOrderAttentionBlock:
"""AdjustMatmulOrder preserves numerics on a batched attention block.