# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # ruff: noqa: E501, F401, F841 import operator import numpy as np import pytest import torch from torch import nn from torch.export import export from torch.nn import Module import tvm import tvm.testing from tvm import relax from tvm.relax.frontend.torch import from_exported_program from tvm.script import ir as I from tvm.script import relax as R from tvm.script import tirx as T from tvm.testing import env def verify_model( torch_model, example_args, binding, expected, dynamic_shapes=None, run_ep_decomposition=True, keep_params_as_input=False, unwrap_unit_return_tuple=False, no_bind_return_tuple=False, map_free_vars=False, custom_convert_map=None, ): exported_program = export(torch_model, args=example_args, dynamic_shapes=dynamic_shapes) mod = from_exported_program( exported_program, run_ep_decomposition=run_ep_decomposition, keep_params_as_input=keep_params_as_input, unwrap_unit_return_tuple=unwrap_unit_return_tuple, no_bind_return_tuple=no_bind_return_tuple, custom_convert_map=custom_convert_map, ) binding = {k: tvm.runtime.tensor(v) for k, v in binding.items()} expected = relax.transform.BindParams("main", binding)(expected) tvm.ir.assert_structural_equal(mod, expected, map_free_vars=map_free_vars) def verify_model_numerically(torch_model, example_args, rtol=1e-7, atol=1e-7): """Verify model by comparing numerical outputs between PyTorch and TVM.""" with torch.no_grad(): pytorch_output = torch_model(*example_args) exported_program = export(torch_model, args=example_args) mod = from_exported_program(exported_program) target = tvm.target.Target("llvm") ex = relax.build(mod, target) vm = relax.VirtualMachine(ex, tvm.cpu()) tvm_args = [tvm.runtime.tensor(arg.numpy()) for arg in example_args] tvm_output = vm["main"](*tvm_args) if hasattr(tvm_output, "numpy"): tvm_output_np = tvm_output.numpy() else: tvm_output_np = tvm_output[0].numpy() pytorch_output_np = ( pytorch_output.numpy() if isinstance(pytorch_output, torch.Tensor) else pytorch_output[0].numpy() ) assert pytorch_output_np.shape == tvm_output_np.shape, ( f"Shape mismatch: PyTorch {pytorch_output_np.shape} vs TVM {tvm_output_np.shape}" ) tvm.testing.assert_allclose(pytorch_output_np, tvm_output_np, rtol=rtol, atol=atol) operator_basic_unary = [ (torch.abs, R.abs), (torch.acos, R.acos), (torch.acosh, R.acosh), (torch.asin, R.asin), (torch.asinh, R.asinh), (torch.atan, R.atan), (torch.atanh, R.atanh), (torch.bitwise_not, R.bitwise_not), (torch.ceil, R.ceil), (torch.cos, R.cos), (torch.cosh, R.cosh), (torch.erf, R.erf), (torch.exp, R.exp), (torch.floor, R.floor), (torch.ops.aten.gelu, R.nn.gelu), (torch.log, R.log), (torch.neg, R.negative), (torch.relu, R.nn.relu), (torch.round, R.round), (torch.rsqrt, R.rsqrt), (torch.sigmoid, R.sigmoid), (torch.sin, R.sin), (torch.sinh, R.sinh), (torch.sign, R.sign), (torch.sqrt, R.sqrt), (torch.tan, R.tan), (torch.tanh, R.tanh), (torch.trunc, R.trunc), ] @pytest.mark.parametrize("pytorch_op, relax_op", operator_basic_unary) def test_basic_unary_ops(pytorch_op, relax_op): example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) class UnaryOp(Module): def forward(self, input): return pytorch_op(input) @tvm.script.ir_module class expected: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = relax_op(input_1) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(UnaryOp(), example_args, {}, expected) operator_bool_unary = [ (torch.isinf, R.isinf), (torch.isnan, R.isnan), ] @pytest.mark.parametrize("pytorch_op, relax_op", operator_bool_unary) def test_bool_unary_ops(pytorch_op, relax_op): example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) class UnaryOp(Module): def forward(self, input): return pytorch_op(input) @tvm.script.ir_module class expected: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="bool") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="bool") = relax_op(input_1) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="bool")) = (lv,) R.output(gv) return gv verify_model(UnaryOp(), example_args, {}, expected) def test_sqrt_integer_input(): """Test that sqrt operation works with integer tensors by auto-converting to float.""" example_args = (torch.tensor([[4, 9, 16, 25]], dtype=torch.int64),) class SqrtIntModel(Module): def forward(self, input): return torch.sqrt(input) @tvm.script.ir_module class expected_int64: @R.function def main(input_1: R.Tensor((1, 4), dtype="int64")) -> R.Tuple( R.Tensor((1, 4), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 4), dtype="float32") = R.astype(input_1, dtype="float32") lv1: R.Tensor((1, 4), dtype="float32") = R.sqrt(lv) gv: R.Tuple(R.Tensor((1, 4), dtype="float32")) = (lv1,) R.output(gv) return gv verify_model(SqrtIntModel(), example_args, {}, expected_int64) example_args_int32 = (torch.tensor([[1, 4, 9]], dtype=torch.int32),) @tvm.script.ir_module class expected_int32: @R.function def main(input_1: R.Tensor((1, 3), dtype="int32")) -> R.Tuple( R.Tensor((1, 3), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3), dtype="float32") = R.astype(input_1, dtype="float32") lv1: R.Tensor((1, 3), dtype="float32") = R.sqrt(lv) gv: R.Tuple(R.Tensor((1, 3), dtype="float32")) = (lv1,) R.output(gv) return gv verify_model(SqrtIntModel(), example_args_int32, {}, expected_int32) def test_extended_unary_ops(): example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) # celu class Celu1(Module): def __init__(self): super().__init__() self.celu = torch.nn.CELU() def forward(self, input): return self.celu(input) class Celu2(Module): def forward(self, input): return torch.nn.functional.celu(input) # alpha * min(0, exp(x / alpha) - 1) + max(0, x) @tvm.script.ir_module class expected_celu: @R.function def main(input: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.exp(input) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.subtract( lv, R.const(1.0, "float32") ) lv2: R.Tensor((1, 3, 10, 10), dtype="bool") = R.greater( input, R.const(0.0, "float32") ) lv3: R.Tensor((1, 3, 10, 10), dtype="float32") = R.where(lv2, input, lv1) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv3,) R.output(gv) return gv verify_model(Celu1(), example_args, {}, expected_celu) verify_model(Celu2(), example_args, {}, expected_celu) # clamp class Clamp(Module): def forward(self, input): return torch.clamp(input, min=0.1, max=0.5) @tvm.script.ir_module class expected_clamp: @R.function def main( input: R.Tensor((1, 3, 10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.clip( input, R.prim_value(T.float64(0.10000000000000001)), R.prim_value(T.float64(0.5)), ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(Clamp(), example_args, {}, expected_clamp) class ClampMinOnly(Module): def forward(self, input): return torch.clamp(input, min=0.5, max=None) @tvm.script.ir_module class expected_clamp_min_only: @R.function def main( input: R.Tensor((1, 3, 10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.clip( input, R.prim_value(T.float64(0.5)), R.prim_value(T.float64("inf")) ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(ClampMinOnly(), example_args, {}, expected_clamp_min_only) class ClampTensors(Module): def forward(self, input): return torch.clamp(input, min=input, max=input) @tvm.script.ir_module class expected_clamp_tensors: @R.function def main( input: R.Tensor((1, 3, 10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.broadcast_to( input, R.shape([1, 3, 10, 10]) ) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.maximum(input, lv) lv2: R.Tensor((1, 3, 10, 10), dtype="float32") = R.broadcast_to( input, R.shape([1, 3, 10, 10]) ) lv3: R.Tensor((1, 3, 10, 10), dtype="float32") = R.minimum(lv1, lv2) lv4: R.Tensor((1, 3, 10, 10), dtype="float32") = R.clip( lv3, R.prim_value(T.float64("-inf")), R.prim_value(T.float64("inf")) ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv4,) R.output(gv) return gv verify_model(ClampTensors(), example_args, {}, expected_clamp_tensors) # dropout class Dropout1(Module): def __init__(self): super().__init__() self.dropout = torch.nn.Dropout(0.5) def forward(self, input): return self.dropout(input) class Dropout2(Module): def forward(self, input): return torch.dropout(input, 0.5, train=True) class Dropout3(Module): def forward(self, input): return torch.ops.aten.dropout_(input, 0.5, train=True) @tvm.script.ir_module class expected_dropout_for_1_2: @R.function def main(input: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): # block 0 with R.dataflow(): gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (input,) R.output(gv) return gv @tvm.script.ir_module class expected_dropout_for_3: @R.function def main(input: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.zeros( R.shape([1, 3, 10, 10]), dtype="float32" ) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.divide( lv, R.const(0.5, "float32") ) lv2: R.Tensor((1, 3, 10, 10), dtype="float32") = R.multiply(input, lv1) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv2,) R.output(gv) return gv verify_model(Dropout1(), example_args, {}, expected_dropout_for_1_2) verify_model(Dropout2(), example_args, {}, expected_dropout_for_1_2) verify_model(Dropout3(), example_args, {}, expected_dropout_for_3) # elu class Elu(Module): def __init__(self): super().__init__() self.elu = torch.nn.ELU() def forward(self, input): return self.elu(input) class Elu2(Module): def forward(self, input): return torch.nn.functional.elu(input) @tvm.script.ir_module class expected_elu: @R.function def main(input: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.exp(input) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.subtract( R.const(1.0, "float32"), lv ) lv2: R.Tensor((1, 3, 10, 10), dtype="float32") = R.nn.relu(lv1) lv3: R.Tensor((1, 3, 10, 10), dtype="float32") = R.multiply( R.const(-1.0, "float32"), lv2 ) lv4: R.Tensor((1, 3, 10, 10), dtype="float32") = R.nn.relu(input) lv5: R.Tensor((1, 3, 10, 10), dtype="float32") = R.add(lv3, lv4) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv5,) R.output(gv) return gv verify_model(Elu(), example_args, {}, expected_elu) verify_model(Elu2(), example_args, {}, expected_elu) # hardsigmoid class Hardsigmoid(torch.nn.Module): def __init__(self): super().__init__() self.hs = torch.nn.Hardsigmoid() def forward(self, input): return self.hs(input) class Hardsigmoid2(torch.nn.Module): def forward(self, input): return torch.nn.functional.hardsigmoid(input) @tvm.script.ir_module class expected_hardsigmoid: @R.function def main(inp_0: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.add( inp_0, R.const(3.0, "float32") ) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.clip( lv, R.prim_value(0), R.prim_value(T.float64("inf")) ) lv2: R.Tensor((1, 3, 10, 10), dtype="float32") = R.clip( lv1, R.prim_value(T.float64("-inf")), R.prim_value(6) ) lv3: R.Tensor((1, 3, 10, 10), dtype="float32") = R.divide( lv2, R.const(6.0, "float32") ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv3,) R.output(gv) return gv verify_model(Hardsigmoid(), example_args, {}, expected_hardsigmoid) verify_model(Hardsigmoid2(), example_args, {}, expected_hardsigmoid) # hardwish class Hardswish(torch.nn.Module): def __init__(self): super().__init__() self.hs = torch.nn.Hardswish() def forward(self, input): return self.hs(input) class Hardswish2(torch.nn.Module): def forward(self, input): return torch.nn.functional.hardswish(input) class Hardswish3(torch.nn.Module): def forward(self, input): return torch.ops.aten.hardswish_(input) @tvm.script.ir_module class expected_hardswish_for_1_2: @R.function def main(inp_0: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.add( inp_0, R.const(3.0, "float32") ) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.clip( lv, R.prim_value(0), R.prim_value(T.float64("inf")) ) lv2: R.Tensor((1, 3, 10, 10), dtype="float32") = R.clip( lv1, R.prim_value(T.float64("-inf")), R.prim_value(6) ) lv3: R.Tensor((1, 3, 10, 10), dtype="float32") = R.multiply(inp_0, lv2) lv4: R.Tensor((1, 3, 10, 10), dtype="float32") = R.divide( lv3, R.const(6.0, "float32") ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv4,) R.output(gv) return gv @tvm.script.ir_module class expected_hardswish_for_3: @R.function def main(input: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.add( input, R.const(3.0, "float32") ) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.clip( lv, R.prim_value(0), R.prim_value(T.float64("inf")) ) lv2: R.Tensor((1, 3, 10, 10), dtype="float32") = R.clip( lv1, R.prim_value(T.float64("-inf")), R.prim_value(6) ) lv3: R.Tensor((1, 3, 10, 10), dtype="float32") = R.multiply(input, lv2) lv4: R.Tensor((1, 3, 10, 10), dtype="float32") = R.divide( lv3, R.const(6.0, "float32") ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv4,) R.output(gv) return gv verify_model(Hardswish(), example_args, {}, expected_hardswish_for_1_2) verify_model(Hardswish2(), example_args, {}, expected_hardswish_for_1_2) verify_model(Hardswish3(), example_args, {}, expected_hardswish_for_3) # isfinite class IsFinite(Module): def forward(self, input): return torch.isfinite(input) @tvm.script.ir_module class expected_isfinite: @R.function def main(input: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="bool") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.abs(input) lv1: R.Tensor((1, 3, 10, 10), dtype="bool") = R.not_equal( lv, R.const(float("inf"), "float32") ) lv2: R.Tensor((1, 3, 10, 10), dtype="bool") = R.equal(input, input) lv3: R.Tensor((1, 3, 10, 10), dtype="bool") = R.multiply(lv2, lv1) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="bool")) = (lv3,) R.output(gv) return gv verify_model(IsFinite(), example_args, {}, expected_isfinite) # log2 class Log2(Module): def forward(self, x): return torch.log2(x) @tvm.script.ir_module class Expected_log2: @R.function def main(inp_0: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.log(inp_0) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.divide( lv, R.const(0.69314718246459961, "float32") ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv1,) R.output(gv) return gv verify_model(Log2(), example_args, {}, Expected_log2) # log10 class Log10(Module): def forward(self, x): return torch.log10(x) @tvm.script.ir_module class Expected_log10: @R.function def main( inp_0: R.Tensor((1, 3, 10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.log(inp_0) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.divide( lv, R.const(2.302585092994046, "float32") ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv1,) R.output(gv) return gv verify_model(Log10(), example_args, {}, Expected_log10) # log1p class Log1p(Module): def forward(self, x): return torch.log1p(x) @tvm.script.ir_module class Expected_log1p: @R.function def main( inp_0: R.Tensor((1, 3, 10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.log( R.add(inp_0, R.const(1, "float32")) ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(Log1p(), example_args, {}, Expected_log1p) # reciprocal class Reciprocal(Module): def forward(self, input): return torch.reciprocal(input) @tvm.script.ir_module class expected_reciprocal: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.divide( R.const(1.0, "float32"), input_1 ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(Reciprocal(), example_args, {}, expected_reciprocal) # Returns the maximum value of all elements in the input tensor. class MaxModel(Module): def forward(self, input): return torch.max(input) @tvm.script.ir_module class expected_max: @R.function def main(input: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((), dtype="float32") ): with R.dataflow(): lv: R.Tensor((), dtype="float32") = R.max(input, axis=None, keepdims=False) gv: R.Tuple(R.Tensor((), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(MaxModel(), example_args, {}, expected_max) # Returns the minimum value of all elements in the input tensor. class MinModel(Module): def forward(self, input): return torch.min(input) @tvm.script.ir_module class expected_min: @R.function def main(input: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((), dtype="float32") ): with R.dataflow(): lv: R.Tensor((), dtype="float32") = R.min(input, axis=None, keepdims=False) gv: R.Tuple(R.Tensor((), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(MinModel(), example_args, {}, expected_min) # relu6 class ReLU6_1(torch.nn.Module): def __init__(self): super().__init__() self.relu6 = torch.nn.ReLU6() def forward(self, x): return self.relu6(x) class ReLU6_2(torch.nn.Module): def forward(self, x): return torch.nn.functional.relu6(x) class ReLU6_3(torch.nn.Module): def forward(self, x): return torch.ops.aten.relu6_(x) @tvm.script.ir_module class expected_relu6_1: @R.function def main(x: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.clip( x, R.prim_value(T.float64(0.0)), R.prim_value(T.float64(6.0)) ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class expected_relu6_2: @R.function def main(x: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.nn.relu6(x) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class expected_relu6_3: @R.function def main(x: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.clip( x, R.prim_value(0), R.prim_value(6) ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(ReLU6_1(), example_args, {}, expected_relu6_1) verify_model(ReLU6_2(), example_args, {}, expected_relu6_2) verify_model(ReLU6_3(), example_args, {}, expected_relu6_3) # selu class SELU(Module): def forward(self, input): return torch.nn.functional.selu(input) @tvm.script.ir_module class expected_selu: @R.function def main(input: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.exp(input) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.subtract( R.const(1.0, "float32"), lv ) lv2: R.Tensor((1, 3, 10, 10), dtype="float32") = R.nn.relu(lv1) lv3: R.Tensor((1, 3, 10, 10), dtype="float32") = R.multiply( R.const(-1.6732631921768188, "float32"), lv2 ) lv4: R.Tensor((1, 3, 10, 10), dtype="float32") = R.nn.relu(input) lv5: R.Tensor((1, 3, 10, 10), dtype="float32") = R.add(lv3, lv4) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv5,) R.output(gv) return gv verify_model(SELU(), example_args, {}, expected_selu) # silu class SiLU(Module): def forward(self, input): return torch.nn.functional.silu(input) @tvm.script.ir_module class expected_silu: @R.function def main(input: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.sigmoid(input) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.multiply(input, lv) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv1,) R.output(gv) return gv verify_model(SiLU(), example_args, {}, expected_silu) # silu_ class SiLU_(Module): def forward(self, input): return torch.ops.aten.silu_(input) @tvm.script.ir_module class expected_silu_: @R.function def main(input: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.sigmoid(input) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.multiply(input, lv) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv1,) R.output(gv) return gv verify_model(SiLU_(), example_args, {}, expected_silu_) # square class Square(Module): def forward(self, input): return torch.square(input) @tvm.script.ir_module class expected_square: @R.function def main(input: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.power( input, R.const(2.0, "float32") ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(Square(), example_args, {}, expected_square) # relu_ class ReLU_(Module): def forward(self, input): return torch.relu_(input.clone()) @tvm.script.ir_module class expected_relu_: @R.function def main(input: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.nn.relu(input) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(ReLU_(), example_args, {}, expected_relu_) def test_hardtanh(): class Hardtanh(torch.nn.Module): def __init__(self): super().__init__() self.ht = torch.nn.Hardtanh() def forward(self, input): return self.ht(input) class Hardtanh2(torch.nn.Module): def forward(self, input): return torch.nn.functional.hardtanh(input) class Hardtanh3(torch.nn.Module): def forward(self, input): return torch.ops.aten.hardtanh_(input) @tvm.script.ir_module class expected_for_1_2: @R.function def main(inp_0: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.clip( inp_0, R.prim_value(T.float64(-1.0)), R.prim_value(T.float64(1.0)) ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(Hardtanh(), example_args, {}, expected_for_1_2) verify_model(Hardtanh2(), example_args, {}, expected_for_1_2) # In-place hardtanh_ yields the same program; mutation outputs are dropped. verify_model(Hardtanh3(), example_args, {}, expected_for_1_2) def test_softplus(): import torch from torch.nn import Module torch.set_grad_enabled(False) class Softplus0(torch.nn.Module): def __init__(self): super().__init__() self.softplus = torch.nn.Softplus(1.0, 20.0) def forward(self, x): return self.softplus(x) class Softplus1(Module): def forward(self, input): return torch.nn.functional.softplus(input, 1.0, 20.0) @tvm.script.ir_module class expected: @R.function def main(x: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.multiply( x, R.const(1.0, "float32") ) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.exp(lv) lv2: R.Tensor((1, 3, 10, 10), dtype="float32") = R.add(lv1, R.const(1.0, "float32")) lv3: R.Tensor((1, 3, 10, 10), dtype="float32") = R.log(lv2) lv4: R.Tensor((1, 3, 10, 10), dtype="float32") = R.divide( lv3, R.const(1.0, "float32") ) lv5: R.Tensor((1, 3, 10, 10), dtype="bool") = R.greater( lv, R.const(20.0, "float32") ) lv6: R.Tensor((1, 3, 10, 10), dtype="float32") = R.where(lv5, x, lv4) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv6,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(Softplus0(), example_args, {}, expected) verify_model(Softplus1(), example_args, {}, expected) def test_leakyrelu(): import torch from torch.nn import Module torch.set_grad_enabled(False) class LeakyReLU0(Module): def __init__(self): super().__init__() self.leakyrelu = torch.nn.LeakyReLU(0.02) def forward(self, input): return self.leakyrelu(input) class LeakyReLU1(Module): def forward(self, input): return torch.nn.functional.leaky_relu(input, 0.02) class LeakyReLU2(Module): def forward(self, input): return torch.ops.aten.leaky_relu_(input, 0.02) @tvm.script.ir_module class expected_for_1_2: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.nn.leakyrelu(input_1, alpha=0.02) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(LeakyReLU0(), example_args, {}, expected_for_1_2) verify_model(LeakyReLU1(), example_args, {}, expected_for_1_2) # In-place leaky_relu_ yields the same program; mutation outputs are dropped. verify_model(LeakyReLU2(), example_args, {}, expected_for_1_2) def test_logaddexp(): class LogAddExp(Module): def forward(self, input1, input2): return torch.logaddexp(input1, input2) @tvm.script.ir_module class expected: @R.function def main( input1: R.Tensor((1, 3, 10, 10), dtype="float32"), input2: R.Tensor((1, 3, 10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="bool") = R.greater_equal(input1, input2) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.where(lv, input1, input2) lv2: R.Tensor((1, 3, 10, 10), dtype="float32") = R.where(lv, input2, input1) lv3: R.Tensor((1, 3, 10, 10), dtype="float32") = R.abs(input1) lv4: R.Tensor((1, 3, 10, 10), dtype="bool") = R.not_equal( lv3, R.const(float("inf"), "float32") ) lv5: R.Tensor((1, 3, 10, 10), dtype="bool") = R.equal(input1, input1) lv6: R.Tensor((1, 3, 10, 10), dtype="bool") = R.multiply(lv5, lv4) lv7: R.Tensor((1, 3, 10, 10), dtype="bool") = R.logical_not(lv6) lv8: R.Tensor((1, 3, 10, 10), dtype="bool") = R.equal(input1, input2) lv9: R.Tensor((1, 3, 10, 10), dtype="bool") = R.logical_and(lv7, lv8) lv10: R.Tensor((1, 3, 10, 10), dtype="float32") = R.subtract(lv2, lv1) lv11: R.Tensor((1, 3, 10, 10), dtype="float32") = R.exp(lv10) lv12: R.Tensor((1, 3, 10, 10), dtype="float32") = R.add( lv11, R.const(1.0, "float32") ) lv13: R.Tensor((1, 3, 10, 10), dtype="float32") = R.log(lv12) lv14: R.Tensor((1, 3, 10, 10), dtype="float32") = R.add(lv1, lv13) lv15: R.Tensor((1, 3, 10, 10), dtype="float32") = R.where(lv9, input1, lv14) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv15,) R.output(gv) return gv example_args = ( torch.randn(1, 3, 10, 10, dtype=torch.float32), torch.randn(1, 3, 10, 10, dtype=torch.float32), ) verify_model(LogAddExp(), example_args, {}, expected) def test_atan2(): class Atan2(Module): def forward(self, lhs, rhs): return torch.atan2(lhs, rhs) @tvm.script.ir_module class expected: @R.function def main( lhs: R.Tensor((1, 3, 10, 10), dtype="float32"), rhs: R.Tensor((1, 3, 10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.atan2(lhs, rhs) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv example_args = ( torch.randn(1, 3, 10, 10, dtype=torch.float32), torch.randn(1, 3, 10, 10, dtype=torch.float32), ) verify_model(Atan2(), example_args, {}, expected) def test_logical_and(): class LogicalAnd(Module): def forward(self, lhs, rhs): return torch.logical_and(lhs, rhs) @tvm.script.ir_module class expected: @R.function def main( lhs: R.Tensor((1, 3, 10, 10), dtype="float32"), rhs: R.Tensor((1, 3, 10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="bool")): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="bool") = R.astype(lhs, dtype="bool") lv1: R.Tensor((1, 3, 10, 10), dtype="bool") = R.astype(rhs, dtype="bool") lv2: R.Tensor((1, 3, 10, 10), dtype="bool") = R.logical_and(lv, lv1) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="bool")) = (lv2,) R.output(gv) return gv example_args = ( torch.randn(1, 3, 10, 10, dtype=torch.float32), torch.randn(1, 3, 10, 10, dtype=torch.float32), ) verify_model(LogicalAnd(), example_args, {}, expected) def test_logical_not(): class LogicalNot(Module): def forward(self, input): return torch.logical_not(input) @tvm.script.ir_module class expected: @R.function def main(input: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="bool") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="bool") = R.astype(input, dtype="bool") lv1: R.Tensor((1, 3, 10, 10), dtype="bool") = R.logical_not(lv) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="bool")) = (lv1,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(LogicalNot(), example_args, {}, expected) def test_logical_or(): class LogicalOr(Module): def forward(self, lhs, rhs): return torch.logical_or(lhs, rhs) @tvm.script.ir_module class expected: @R.function def main( lhs: R.Tensor((1, 3, 10, 10), dtype="float32"), rhs: R.Tensor((1, 3, 10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="bool")): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="bool") = R.astype(lhs, dtype="bool") lv1: R.Tensor((1, 3, 10, 10), dtype="bool") = R.astype(rhs, dtype="bool") lv2: R.Tensor((1, 3, 10, 10), dtype="bool") = R.logical_or(lv, lv1) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="bool")) = (lv2,) R.output(gv) return gv example_args = ( torch.randn(1, 3, 10, 10, dtype=torch.float32), torch.randn(1, 3, 10, 10, dtype=torch.float32), ) verify_model(LogicalOr(), example_args, {}, expected) def test_logical_xor(): class LogicalXor(Module): def forward(self, lhs, rhs): return torch.logical_xor(lhs, rhs) @tvm.script.ir_module class expected: @R.function def main( lhs: R.Tensor((1, 3, 10, 10), dtype="float32"), rhs: R.Tensor((1, 3, 10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="bool")): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="bool") = R.astype(lhs, dtype="bool") lv1: R.Tensor((1, 3, 10, 10), dtype="bool") = R.astype(rhs, dtype="bool") lv2: R.Tensor((1, 3, 10, 10), dtype="bool") = R.logical_xor(lv, lv1) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="bool")) = (lv2,) R.output(gv) return gv example_args = ( torch.randn(1, 3, 10, 10, dtype=torch.float32), torch.randn(1, 3, 10, 10, dtype=torch.float32), ) verify_model(LogicalXor(), example_args, {}, expected) def test_pow_integer(): class Pow(Module): def forward(self, input): return input.pow(4) @tvm.script.ir_module class expected: @R.function def main(input: R.Tensor((4,), dtype="int64")) -> R.Tuple(R.Tensor((4,), dtype="int64")): # block 0 with R.dataflow(): lv: R.Tensor((4,), dtype="int64") = R.multiply(input, input) lv1: R.Tensor((4,), dtype="int64") = R.multiply(lv, input) lv2: R.Tensor((4,), dtype="int64") = R.multiply(lv1, input) gv: R.Tuple(R.Tensor((4,), dtype="int64")) = (lv2,) R.output(gv) return gv example_args = (torch.tensor([-1, 1, 2, 3], dtype=torch.int64),) verify_model(Pow(), example_args, {}, expected) def test_logsoftmax(): class LogSoftmax(Module): def __init__(self): super().__init__() self.lsm = torch.nn.LogSoftmax(dim=1) def forward(self, input): return self.lsm(input) class LogSoftmax2(Module): def forward(self, input): return torch.nn.functional.log_softmax(input, dim=1) @tvm.script.ir_module class expected1: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.nn.log_softmax(input_1, axis=1) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(LogSoftmax(), example_args, {}, expected1) verify_model(LogSoftmax2(), example_args, {}, expected1) def test_prelu(): class Prelu1(Module): def __init__(self, num_parameters=1, alpha=0.25): super().__init__() self.prelu = torch.nn.PReLU(num_parameters=num_parameters, init=alpha) def forward(self, x): return self.prelu(x) class Prelu2(torch.nn.Module): def __init__(self): super().__init__() self.alpha = torch.nn.Parameter(torch.tensor([0.25])) def forward(self, x): return torch.nn.functional.prelu(x, self.alpha) @tvm.script.ir_module class expected: @R.function def main(x: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 1, 1, 1), dtype="float32") = R.reshape( R.const([0.25], dtype="float32"), R.shape([1, 1, 1, 1]) ) lv1: R.Tensor((1, 3, 10, 10), dtype="bool") = R.greater(x, R.const(0.0, "float32")) lv2: R.Tensor((1, 3, 10, 10), dtype="float32") = R.multiply(lv, x) lv3: R.Tensor((1, 3, 10, 10), dtype="float32") = R.where(lv1, x, lv2) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv3,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(Prelu1(), example_args, {}, expected) verify_model(Prelu2(), example_args, {}, expected) def test_softmax(): class Softmax(Module): def __init__(self): super().__init__() self.sm = torch.nn.Softmax(dim=1) def forward(self, input): return self.sm(input) class Softmax2(Module): def forward(self, input): return torch.nn.functional.softmax(input, dim=1) @tvm.script.ir_module class expected1: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.nn.softmax(input_1, axis=1) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(Softmax(), example_args, {}, expected1) verify_model(Softmax2(), example_args, {}, expected1) def test_softsign(): class Softsign(Module): def __init__(self): super().__init__() self.ss = torch.nn.Softsign() def forward(self, input): return self.ss(input) class Softsign2(Module): def forward(self, input): return torch.nn.functional.softsign(input) @tvm.script.ir_module class expected_softsign: @R.function def main(input: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): with R.dataflow(): abs_val = R.abs(input) denom = R.add(abs_val, R.const(1.0, "float32")) result = R.divide(input, denom) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (result,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(Softsign(), example_args, {}, expected_softsign) verify_model(Softsign2(), example_args, {}, expected_softsign) def test_softshrink(): class Softshrink(Module): def __init__(self): super().__init__() self.softshrink = torch.nn.Softshrink(lambd=0.5) def forward(self, input): return self.softshrink(input) class Softshrink2(Module): def forward(self, input): return torch.nn.functional.softshrink(input, lambd=0.5) @tvm.script.ir_module class expected_softshrink: @R.function def main( input: R.Tensor((1, 3, 10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.abs(input) lv1: R.Tensor((1, 3, 10, 10), dtype="bool") = R.greater(lv, R.const(0.5, "float32")) lv2: R.Tensor((1, 3, 10, 10), dtype="float32") = R.sign(input) lv3: R.Tensor((1, 3, 10, 10), dtype="float32") = R.multiply( lv2, R.const(0.5, "float32") ) lv4: R.Tensor((1, 3, 10, 10), dtype="float32") = R.subtract(input, lv3) lv5: R.Tensor((1, 3, 10, 10), dtype="float32") = R.multiply( input, R.const(0.0, "float32") ) lv6: R.Tensor((1, 3, 10, 10), dtype="float32") = R.where(lv1, lv4, lv5) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv6,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(Softshrink(), example_args, {}, expected_softshrink) verify_model(Softshrink2(), example_args, {}, expected_softshrink) def test_tril_triu(): example_args = (torch.randn(10, 10, dtype=torch.float32),) class Tril(Module): def forward(self, input): return torch.tril(input, 1) @tvm.script.ir_module class expected_tril: @R.function def main(input: R.Tensor((10, 10), dtype="float32")) -> R.Tuple( R.Tensor((10, 10), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((10,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(10), R.prim_value(1), dtype="int64" ) lv1: R.Tensor((1, 10), dtype="int64") = R.expand_dims(lv, axis=[-2]) lv2: R.Tensor((10,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(10), R.prim_value(1), dtype="int64" ) lv3: R.Tensor((10, 1), dtype="int64") = R.expand_dims(lv2, axis=[-1]) lv4: R.Tensor((10, 10), dtype="int64") = R.subtract(lv1, lv3) lv5: R.Tensor((10, 10), dtype="bool") = R.less_equal(lv4, R.const(1, "int64")) lv6: R.Tensor((), dtype="float32") = R.const(0.0, "float32") lv7: R.Tensor((10, 10), dtype="float32") = R.where(lv5, input, lv6) gv: R.Tuple(R.Tensor((10, 10), dtype="float32")) = (lv7,) R.output(gv) return gv verify_model(Tril(), example_args, {}, expected_tril) class Triu(Module): def forward(self, input): return torch.triu(input, 1) @tvm.script.ir_module class expected_triu: @R.function def main(input: R.Tensor((10, 10), dtype="float32")) -> R.Tuple( R.Tensor((10, 10), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((10,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(10), R.prim_value(1), dtype="int64" ) lv1: R.Tensor((1, 10), dtype="int64") = R.expand_dims(lv, axis=[-2]) lv2: R.Tensor((10,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(10), R.prim_value(1), dtype="int64" ) lv3: R.Tensor((10, 1), dtype="int64") = R.expand_dims(lv2, axis=[-1]) lv4: R.Tensor((10, 10), dtype="int64") = R.subtract(lv1, lv3) lv5: R.Tensor((10, 10), dtype="bool") = R.greater_equal(lv4, R.const(1, "int64")) lv6: R.Tensor((), dtype="float32") = R.const(0.0, "float32") lv7: R.Tensor((10, 10), dtype="float32") = R.where(lv5, input, lv6) gv: R.Tuple(R.Tensor((10, 10), dtype="float32")) = (lv7,) R.output(gv) return gv verify_model(Triu(), example_args, {}, expected_triu) operator_binary_1 = [ (operator.add, R.add), (torch.ops.aten.add_, R.add), (torch.ops.aten.bitwise_or, R.bitwise_or), (torch.ops.aten.bitwise_or_, R.bitwise_or), (operator.sub, R.subtract), (operator.mul, R.multiply), (torch.ops.aten.mul_, R.multiply), (operator.truediv, R.divide), (operator.floordiv, R.floor_divide), (torch.ops.aten.fmod, R.mod), (operator.pow, R.power), (operator.mod, R.floor_mod), (operator.and_, R.bitwise_and), (operator.or_, R.bitwise_or), (operator.xor, R.bitwise_xor), ] @pytest.mark.parametrize("op, relax_op", operator_binary_1) def test_binary1(op, relax_op): example_args1 = ( torch.randn(10, 10, dtype=torch.float32), torch.randn(10, 10, dtype=torch.float32), ) example_args2 = (torch.randn(10, 10, dtype=torch.float32),) class Binary1(Module): def __init__(self, op): super().__init__() self.op = op def forward(self, lhs, rhs): return self.op(lhs, rhs) @tvm.script.ir_module class expected_binary1: @R.function def main( lhs: R.Tensor((10, 10), dtype="float32"), rhs: R.Tensor((10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((10, 10), dtype="float32") = relax_op(lhs, rhs) gv: R.Tuple(R.Tensor((10, 10), dtype="float32")) = (lv,) R.output(gv) return gv class Binary2(Module): def __init__(self, op): super().__init__() self.op = op def forward(self, lhs): return self.op(lhs, 1.0) @tvm.script.ir_module class expected_binary2: @R.function def main( lhs: R.Tensor((10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((10, 10), dtype="float32") = relax_op(lhs, R.const(1.0)) gv: R.Tuple(R.Tensor((10, 10), dtype="float32")) = (lv,) R.output(gv) return gv # In-place ops (add_, mul_, ...) produce the same Relax program as their # functional counterparts: mutation outputs are dropped by the importer. verify_model(Binary1(op), example_args1, {}, expected_binary1) verify_model(Binary2(op), example_args2, {}, expected_binary2) operator_binary_scalar = [ (torch.ops.aten.add.Scalar, R.add), (torch.ops.aten.bitwise_and.Scalar, R.bitwise_and), (torch.ops.aten.bitwise_or.Scalar, R.bitwise_or), (torch.ops.aten.bitwise_xor.Scalar, R.bitwise_xor), (torch.ops.aten.div.Scalar, R.divide), (torch.ops.aten.sub.Scalar, R.subtract), (torch.ops.aten.mul.Scalar, R.multiply), (torch.ops.aten.remainder.Scalar, R.floor_mod), ] @pytest.mark.parametrize("op, relax_op", operator_binary_scalar) def test_binary_scalar(op, relax_op): example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) class BinaryScalar(Module): def __init__(self, op): super().__init__() self.op = op def forward(self, lhs): return self.op(lhs, 1.0) @tvm.script.ir_module class expected_binary_scalar: @R.function def main( lhs: R.Tensor((1, 3, 10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = relax_op(lhs, R.const(1.0)) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(BinaryScalar(op), example_args, {}, expected_binary_scalar) operator_binary_promote = [ (operator.add, R.add), (operator.sub, R.subtract), (operator.mul, R.multiply), (operator.truediv, R.divide), (operator.pow, R.power), (operator.mod, R.floor_mod), ] @pytest.mark.parametrize("op, relax_op", operator_binary_promote) def test_binary_dtype_promotion(op, relax_op): """Ensure binary ops promote differing dtypes following PyTorch rules.""" class BinaryPromoteLHS(Module): def forward(self, x): arange_val = torch.arange(x.shape[1]) # int64 by default return op(x, arange_val) @tvm.script.ir_module class expected_promote_lhs: @R.function def main(x: R.Tensor((2, 3), dtype="float32")) -> R.Tuple( R.Tensor((2, 3), dtype="float32") ): with R.dataflow(): lv: R.Tensor((3,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(3), R.prim_value(1), dtype="int64" ) lv1: R.Tensor((3,), dtype="float32") = R.astype(lv, dtype="float32") lv2: R.Tensor((2, 3), dtype="float32") = relax_op(x, lv1) gv: R.Tuple(R.Tensor((2, 3), dtype="float32")) = (lv2,) R.output(gv) return gv class BinaryPromoteRHS(Module): def forward(self, x): arange_val = torch.arange(x.shape[1]) # int64 by default return op(arange_val, x) @tvm.script.ir_module class expected_promote_rhs: @R.function def main(x: R.Tensor((2, 3), dtype="float32")) -> R.Tuple( R.Tensor((2, 3), dtype="float32") ): with R.dataflow(): lv: R.Tensor((3,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(3), R.prim_value(1), dtype="int64" ) lv1: R.Tensor((3,), dtype="float32") = R.astype(lv, dtype="float32") lv2: R.Tensor((2, 3), dtype="float32") = relax_op(lv1, x) gv: R.Tuple(R.Tensor((2, 3), dtype="float32")) = (lv2,) R.output(gv) return gv example_args = (torch.randn(2, 3, dtype=torch.float32),) verify_model(BinaryPromoteLHS(), example_args, {}, expected_promote_lhs) verify_model(BinaryPromoteRHS(), example_args, {}, expected_promote_rhs) operator_binary_2 = [ (operator.eq, R.equal), (operator.ne, R.not_equal), (operator.lt, R.less), (operator.le, R.less_equal), (operator.gt, R.greater), (operator.ge, R.greater_equal), ] @pytest.mark.parametrize("op, relax_op", operator_binary_2) def test_binary2(op, relax_op): example_args1 = ( torch.randn(10, 10, dtype=torch.float32), torch.randn(10, 10, dtype=torch.float32), ) example_args2 = (torch.randn(10, 10, dtype=torch.float32),) class Binary1(Module): def __init__(self, op): super().__init__() self.op = op def forward(self, lhs, rhs): return self.op(lhs, rhs) @tvm.script.ir_module class expected_binary1: @R.function def main( lhs: R.Tensor((10, 10), dtype="float32"), rhs: R.Tensor((10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((10, 10), dtype="bool")): with R.dataflow(): lv: R.Tensor((10, 10), dtype="bool") = relax_op(lhs, rhs) gv: R.Tuple(R.Tensor((10, 10), dtype="bool")) = (lv,) R.output(gv) return gv class Binary2(Module): def __init__(self, op): super().__init__() self.op = op def forward(self, lhs): return self.op(lhs, 1.0) @tvm.script.ir_module class expected_binary2: @R.function def main( lhs: R.Tensor((10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((10, 10), dtype="bool")): with R.dataflow(): lv: R.Tensor((10, 10), dtype="bool") = relax_op(lhs, R.const(1.0)) gv: R.Tuple(R.Tensor((10, 10), dtype="bool")) = (lv,) R.output(gv) return gv verify_model(Binary1(op), example_args1, {}, expected_binary1) verify_model(Binary2(op), example_args2, {}, expected_binary2) def test_binary3(): example_args1 = ( torch.randn(10, 10, dtype=torch.float32), torch.randn(10, 10, dtype=torch.float32), ) example_args2 = (torch.randn(10, 10, dtype=torch.float32),) # Max class Max1(Module): def forward(self, x, y): return torch.max(x, y) @I.ir_module class expected_max1: @R.function def main( inp_0: R.Tensor((10, 10), dtype="float32"), inp_1: R.Tensor((10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((10, 10), dtype="float32") = R.maximum(inp_0, inp_1) gv: R.Tuple(R.Tensor((10, 10), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(Max1(), example_args1, {}, expected_max1) # Min class Min1(Module): def forward(self, x, y): return torch.min(x, y) @I.ir_module class expected_min1: @R.function def main( inp_0: R.Tensor((10, 10), dtype="float32"), inp_1: R.Tensor((10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((10, 10), dtype="float32") = R.minimum(inp_0, inp_1) gv: R.Tuple(R.Tensor((10, 10), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(Min1(), example_args1, {}, expected_min1) # RSub class RSub1(Module): def forward(self, x, y): return torch.rsub(x, y) class RSub2(Module): def forward(self, x): return torch.rsub(x, 5.0) @tvm.script.ir_module class expected_rsub1: @R.function def main( x: R.Tensor((10, 10), dtype="float32"), y: R.Tensor((10, 10), dtype="float32") ) -> R.Tuple(R.Tensor((10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((10, 10), dtype="float32") = R.subtract(y, x) gv: R.Tuple(R.Tensor((10, 10), dtype="float32")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class expected_rsub2: @R.function def main(x: R.Tensor((10, 10), dtype="float32")) -> R.Tuple( R.Tensor((10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((10, 10), dtype="float32") = R.subtract(R.const(5.0, "float32"), x) gv: R.Tuple(R.Tensor((10, 10), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(RSub1(), example_args1, {}, expected_rsub1) verify_model(RSub2(), example_args2, {}, expected_rsub2) # IsIn def test_isin(): class IsInModel(torch.nn.Module): def forward(self, x, test_elements): return torch.isin(x, test_elements) @tvm.script.ir_module class expected: @R.function def main( x: R.Tensor((10, 10), dtype="float32"), test_elements: R.Tensor((8,), dtype="float32") ) -> R.Tuple(R.Tensor((10, 10), dtype="bool")): with R.dataflow(): lv: R.Tensor((10, 10, 1), dtype="float32") = R.reshape(x, R.shape([10, 10, 1])) lv1: R.Tensor((10, 10, 8), dtype="bool") = R.equal(lv, test_elements) lv2: R.Tensor((10, 10, 8), dtype="int8") = R.astype(lv1, dtype="int8") lv3: R.Tensor((10, 10), dtype="int8") = R.max(lv2, axis=[-1], keepdims=False) lv4: R.Tensor((10, 10), dtype="bool") = R.astype(lv3, dtype="bool") gv: R.Tuple(R.Tensor((10, 10), dtype="bool")) = (lv4,) R.output(gv) return gv example_args = ( torch.randn(10, 10, dtype=torch.float32), torch.randn(8, dtype=torch.float32), ) verify_model(IsInModel(), example_args, {}, expected) def test_div_mode(): # Case 1: Basic division (no rounding mode) class DivModel(torch.nn.Module): def forward(self, a, b): return torch.div(a, b) @tvm.script.ir_module class expected_div: @R.function def main( a: R.Tensor((64, 64), dtype="float32"), b: R.Tensor((64,), dtype="float32") ) -> R.Tuple(R.Tensor((64, 64), dtype="float32")): with R.dataflow(): lv: R.Tensor((64, 64), dtype="float32") = R.divide(a, b) gv: R.Tuple(R.Tensor((64, 64), dtype="float32")) = (lv,) R.output(gv) return gv example_args = ( torch.randn(64, 64, dtype=torch.float32), torch.randn(64, dtype=torch.float32), ) verify_model(DivModel(), example_args, {}, expected_div) # Case 2: Division with trunc rounding class DivTruncModel(torch.nn.Module): def forward(self, a, b): return torch.div(a, b, rounding_mode="trunc") @tvm.script.ir_module class expected_div_trunc: @R.function def main( a: R.Tensor((64, 64), dtype="float32"), b: R.Tensor((64,), dtype="float32") ) -> R.Tuple(R.Tensor((64, 64), dtype="float32")): with R.dataflow(): lv: R.Tensor((64, 64), dtype="float32") = R.divide(a, b) lv1: R.Tensor((64, 64), dtype="float32") = R.trunc(lv) gv: R.Tuple(R.Tensor((64, 64), dtype="float32")) = (lv1,) R.output(gv) return gv verify_model(DivTruncModel(), example_args, {}, expected_div_trunc) # Case 3: Division with floor rounding class DivFloorModel(torch.nn.Module): def forward(self, a, b): return torch.div(a, b, rounding_mode="floor") @tvm.script.ir_module class expected_div_floor: @R.function def main( a: R.Tensor((64, 64), dtype="float32"), b: R.Tensor((64,), dtype="float32") ) -> R.Tuple(R.Tensor((64, 64), dtype="float32")): with R.dataflow(): lv: R.Tensor((64, 64), dtype="float32") = R.floor_divide(a, b) gv: R.Tuple(R.Tensor((64, 64), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(DivFloorModel(), example_args, {}, expected_div_floor) def test_batchnorm2d(): class BatchNorm2d1(Module): def __init__(self): super().__init__() self.bn = torch.nn.BatchNorm2d(3) def forward(self, input): return self.bn(input) @tvm.script.ir_module class expected1: @R.function def main( input_1: R.Tensor((1, 3, 10, 10), dtype="float32"), w1: R.Tensor((3,), dtype="float32"), w2: R.Tensor((3,), dtype="float32"), w3: R.Tensor((3,), dtype="float32"), w4: R.Tensor((3,), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")): # block 0 with R.dataflow(): lv: R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32"), R.Tensor((3,), dtype="float32"), R.Tensor((3,), dtype="float32"), ) = R.nn.batch_norm( input_1, w1, w2, w3, w4, axis=1, epsilon=1e-05, center=True, scale=True, momentum=0.1, training=False, ) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = lv[0] gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv1,) R.output(gv) return gv class BatchNorm2dCustom(Module): def __init__(self): super().__init__() self.bn = torch.nn.BatchNorm2d(3, eps=0.001, momentum=0.01) def forward(self, input): return self.bn(input) @tvm.script.ir_module class expected2: @R.function def main( input_1: R.Tensor((1, 3, 10, 10), dtype="float32"), w1: R.Tensor((3,), dtype="float32"), w2: R.Tensor((3,), dtype="float32"), w3: R.Tensor((3,), dtype="float32"), w4: R.Tensor((3,), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")): with R.dataflow(): lv: R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32"), R.Tensor((3,), dtype="float32"), R.Tensor((3,), dtype="float32"), ) = R.nn.batch_norm( input_1, w1, w2, w3, w4, axis=1, epsilon=0.001, center=True, scale=True, momentum=0.01, training=False, ) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = lv[0] gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) model_1 = BatchNorm2d1().eval() binding_1 = { "w1": model_1.bn.weight.detach().numpy(), "w2": model_1.bn.bias.detach().numpy(), "w3": model_1.bn.running_mean.detach().numpy(), "w4": model_1.bn.running_var.detach().numpy(), } verify_model(model_1, example_args, binding_1, expected1) model_2 = BatchNorm2dCustom().eval() binding_2 = { "w1": model_2.bn.weight.detach().numpy(), "w2": model_2.bn.bias.detach().numpy(), "w3": model_2.bn.running_mean.detach().numpy(), "w4": model_2.bn.running_var.detach().numpy(), } verify_model(model_2, example_args, binding_2, expected2) class BatchNorm2dTraining(Module): def __init__(self): super().__init__() self.bn = torch.nn.BatchNorm2d(3, track_running_stats=True) def forward(self, input): return self.bn(input) @tvm.script.ir_module class expected3: @R.function def main( input_1: R.Tensor((2, 3, 4, 4), dtype="float32"), w1: R.Tensor((3,), dtype="float32"), w2: R.Tensor((3,), dtype="float32"), w3: R.Tensor((3,), dtype="float32"), w4: R.Tensor((3,), dtype="float32"), ) -> R.Tuple(R.Tensor((2, 3, 4, 4), dtype="float32")): with R.dataflow(): lv: R.Tensor((), dtype="int64") = R.add(R.const(0, "int64"), R.const(1, "int64")) lv1: R.Tuple( R.Tensor((2, 3, 4, 4), dtype="float32"), R.Tensor((3,), dtype="float32"), R.Tensor((3,), dtype="float32"), ) = R.nn.batch_norm( input_1, w1, w2, w3, w4, axis=1, epsilon=1e-5, center=True, scale=True, momentum=0.1, training=True, ) lv2: R.Tensor((2, 3, 4, 4), dtype="float32") = lv1[0] lv3: R.Tensor((3,), dtype="float32") = lv1[1] lv4: R.Tensor((3,), dtype="float32") = R.zeros(R.shape([3]), dtype="float32") lv5: R.Tuple( R.Tensor((2, 3, 4, 4), dtype="float32"), R.Tensor((3,), dtype="float32"), R.Tensor((3,), dtype="float32"), R.Tensor((3,), dtype="float32"), R.Tensor((3,), dtype="float32"), ) = (lv2, lv3, lv4, lv4, lv4) lv6: R.Tensor((2, 3, 4, 4), dtype="float32") = lv5[0] lv7: R.Tensor((3,), dtype="float32") = lv5[3] lv8: R.Tensor((3,), dtype="float32") = lv5[4] gv: R.Tuple(R.Tensor((2, 3, 4, 4), dtype="float32")) = (lv6,) R.output(gv) return gv example_args_train = (torch.randn(2, 3, 4, 4, dtype=torch.float32),) model_3 = BatchNorm2dTraining() model_3.train() # Set to training mode binding_3 = { "w1": model_3.bn.weight.detach().numpy(), "w2": model_3.bn.bias.detach().numpy(), "w3": model_3.bn.running_mean.detach().numpy(), "w4": model_3.bn.running_var.detach().numpy(), } verify_model(model_3, example_args_train, binding_3, expected3) def test_adaptive_avgpool1d(): class AdaptiveAvgPool1d0(torch.nn.Module): def __init__(self): super().__init__() self.pool = torch.nn.AdaptiveAvgPool1d(output_size=5) def forward(self, input): return self.pool(input) class AdaptiveAvgPool1d1(torch.nn.Module): def forward(self, input): return torch.nn.functional.adaptive_avg_pool1d(input, output_size=5) @tvm.script.ir_module class expected1: @R.function def main(input_1: R.Tensor((1, 3, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 5), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 1, 10), dtype="float32") = R.expand_dims(input_1, axis=[-2]) lv1: R.Tensor((1, 3, 1, 5), dtype="float32") = R.nn.adaptive_avg_pool2d( lv, output_size=[1, 5], layout="NCHW" ) lv2: R.Tensor((1, 3, 5), dtype="float32") = R.squeeze(lv1, axis=[-2]) gv: R.Tuple(R.Tensor((1, 3, 5), dtype="float32")) = (lv2,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, dtype=torch.float32),) verify_model(AdaptiveAvgPool1d0(), example_args, {}, expected1) verify_model(AdaptiveAvgPool1d1(), example_args, {}, expected1) def test_adaptive_avgpool2d(): class AdaptiveAvgPool2d0(Module): def __init__(self): super().__init__() self.pool = torch.nn.AdaptiveAvgPool2d([10, 10]) def forward(self, input): return self.pool(input) class AdaptiveAvgPool2d1(Module): def forward(self, input): return torch.nn.functional.adaptive_avg_pool2d(input, [10, 10]) @tvm.script.ir_module class expected1: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.nn.adaptive_avg_pool2d( input_1, output_size=[10, 10], layout="NCHW", out_layout="NCHW" ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(AdaptiveAvgPool2d0(), example_args, {}, expected1) verify_model(AdaptiveAvgPool2d1(), example_args, {}, expected1) def test_adaptive_avgpool3d(): class AdaptiveAvgPool3d0(torch.nn.Module): def __init__(self): super().__init__() self.pool = torch.nn.AdaptiveAvgPool3d([4, 4, 4]) def forward(self, input): return self.pool(input) class AdaptiveAvgPool3d1(torch.nn.Module): def forward(self, input): return torch.nn.functional.adaptive_avg_pool3d(input, [4, 4, 4]) @tvm.script.ir_module class expected1: @R.function def main(input_1: R.Tensor((1, 3, 8, 8, 8), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 4, 4, 4), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 4, 4, 4), dtype="float32") = R.nn.adaptive_avg_pool3d( input_1, output_size=[4, 4, 4], layout="NCDHW", out_layout="NCDHW" ) gv: R.Tuple(R.Tensor((1, 3, 4, 4, 4), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 3, 8, 8, 8, dtype=torch.float32),) verify_model(AdaptiveAvgPool3d0(), example_args, {}, expected1) verify_model(AdaptiveAvgPool3d1(), example_args, {}, expected1) def test_addmm(): class Addmm1(Module): def __init__(self): super().__init__() def forward(self, x1, x2, x3): return torch.addmm(x1, x2, x3) class Addmm2(Module): def __init__(self): super().__init__() def forward(self, x1, x2, x3): return torch.addmm(x1, x2, x3, beta=0.8, alpha=0.5) @tvm.script.ir_module class expected1: @R.function def main( x1: R.Tensor((10, 10), dtype="float32"), x2: R.Tensor((10, 10), dtype="float32"), x3: R.Tensor((10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((10, 10), dtype="float32")): # block 0 with R.dataflow(): lv: R.Tensor((10, 10), dtype="float32") = R.matmul(x2, x3, out_dtype="float32") lv1: R.Tensor((10, 10), dtype="float32") = R.add(x1, lv) gv: R.Tuple(R.Tensor((10, 10), dtype="float32")) = (lv1,) R.output(gv) return gv @tvm.script.ir_module class expected2: @R.function def main( x1: R.Tensor((10, 10), dtype="float32"), x2: R.Tensor((10, 10), dtype="float32"), x3: R.Tensor((10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((10, 10), dtype="float32")): # block 0 with R.dataflow(): lv: R.Tensor((10, 10), dtype="float32") = R.matmul(x2, x3, out_dtype="float32") lv1: R.Tensor((10, 10), dtype="float32") = R.multiply(lv, R.const(0.5, "float32")) lv2: R.Tensor((10, 10), dtype="float32") = R.multiply(x1, R.const(0.8, "float32")) lv3: R.Tensor((10, 10), dtype="float32") = R.add(lv2, lv1) gv: R.Tuple(R.Tensor((10, 10), dtype="float32")) = (lv3,) R.output(gv) return gv example_args = ( torch.randn(10, 10, dtype=torch.float32), torch.randn(10, 10, dtype=torch.float32), torch.randn(10, 10, dtype=torch.float32), ) verify_model(Addmm1(), example_args, {}, expected1) verify_model(Addmm2(), example_args, {}, expected2) def test_sparse_addmm(): class SparseAddmm1(Module): def __init__(self): super().__init__() def forward(self, x1, x2, x3): return torch.sparse.addmm(x1, x2, x3) class SparseAddmm2(Module): def __init__(self): super().__init__() def forward(self, x1, x2, x3): return torch.sparse.addmm(x1, x2, x3, beta=0.8, alpha=0.5) @tvm.script.ir_module class expected1: @R.function def main( x1: R.Tensor((10, 10), dtype="float32"), x2: R.Tensor((10, 10), dtype="float32"), x3: R.Tensor((10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((10, 10), dtype="float32") = R.matmul(x2, x3, out_dtype="float32") lv1: R.Tensor((10, 10), dtype="float32") = R.add(x1, lv) gv: R.Tuple(R.Tensor((10, 10), dtype="float32")) = (lv1,) R.output(gv) return gv @tvm.script.ir_module class expected2: @R.function def main( x1: R.Tensor((10, 10), dtype="float32"), x2: R.Tensor((10, 10), dtype="float32"), x3: R.Tensor((10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((10, 10), dtype="float32") = R.matmul(x2, x3, out_dtype="float32") lv1: R.Tensor((10, 10), dtype="float32") = R.multiply(lv, R.const(0.5, "float32")) lv2: R.Tensor((10, 10), dtype="float32") = R.multiply(x1, R.const(0.8, "float32")) lv3: R.Tensor((10, 10), dtype="float32") = R.add(lv2, lv1) gv: R.Tuple(R.Tensor((10, 10), dtype="float32")) = (lv3,) R.output(gv) return gv example_args = ( torch.randn(10, 10, dtype=torch.float32), torch.randn(10, 10, dtype=torch.float32), torch.randn(10, 10, dtype=torch.float32), ) verify_model(SparseAddmm1(), example_args, {}, expected1) verify_model(SparseAddmm2(), example_args, {}, expected2) def test_avg_pool1d(): class AvgPool1d1(Module): def __init__(self): super().__init__() self.pool = torch.nn.AvgPool1d(kernel_size=1) def forward(self, input): return self.pool(input) @tvm.script.ir_module class expected1: @R.function def main(input: R.Tensor((1, 3, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 1, 10), dtype="float32") = R.expand_dims(input, axis=[-2]) lv1: R.Tensor((1, 3, 1, 10), dtype="float32") = R.nn.avg_pool2d( lv, pool_size=[1, 1], strides=[1, 1], dilation=[1, 1], padding=[0, 0, 0, 0], ceil_mode=False, count_include_pad=True, layout="NCHW", out_layout="NCHW", ) lv2: R.Tensor((1, 3, 10), dtype="float32") = R.squeeze(lv1, axis=[-2]) gv: R.Tuple(R.Tensor((1, 3, 10), dtype="float32")) = (lv2,) R.output(gv) return gv class AvgPool1d2(Module): def __init__(self): super().__init__() self.pool = torch.nn.AvgPool1d(kernel_size=3, stride=2, padding=1, ceil_mode=True) def forward(self, input): return self.pool(input) class AvgPool1d3(Module): def forward(self, input): return torch.nn.functional.avg_pool1d( input, kernel_size=3, stride=2, padding=1, ceil_mode=True ) @tvm.script.ir_module class expected2: @R.function def main(input: R.Tensor((1, 3, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 6), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 1, 10), dtype="float32") = R.expand_dims(input, axis=[-2]) lv1: R.Tensor((1, 3, 1, 6), dtype="float32") = R.nn.avg_pool2d( lv, pool_size=[1, 3], strides=[1, 2], dilation=[1, 1], padding=[0, 1, 0, 1], ceil_mode=True, count_include_pad=True, layout="NCHW", out_layout="NCHW", ) lv2: R.Tensor((1, 3, 6), dtype="float32") = R.squeeze(lv1, axis=[-2]) gv: R.Tuple(R.Tensor((1, 3, 6), dtype="float32")) = (lv2,) R.output(gv) return gv class AvgPool1d4(Module): def forward(self, input): return torch.nn.functional.avg_pool1d(input, kernel_size=2, stride=2, padding=0) @tvm.script.ir_module class expected3: @R.function def main(input: R.Tensor((1, 3, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 5), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 1, 10), dtype="float32") = R.expand_dims(input, axis=[-2]) lv1: R.Tensor((1, 3, 1, 5), dtype="float32") = R.nn.avg_pool2d( lv, pool_size=[1, 2], strides=[1, 2], dilation=[1, 1], padding=[0, 0, 0, 0], ceil_mode=False, count_include_pad=True, layout="NCHW", out_layout="NCHW", ) lv2: R.Tensor((1, 3, 5), dtype="float32") = R.squeeze(lv1, axis=[-2]) gv: R.Tuple(R.Tensor((1, 3, 5), dtype="float32")) = (lv2,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, dtype=torch.float32),) verify_model(AvgPool1d1(), example_args, {}, expected1) verify_model(AvgPool1d2(), example_args, {}, expected2) verify_model(AvgPool1d3(), example_args, {}, expected2) verify_model(AvgPool1d4(), example_args, {}, expected3) def test_avg_pool2d(): class AvgPool2d1(Module): def __init__(self): super().__init__() self.pool = torch.nn.AvgPool2d(kernel_size=[1, 1]) def forward(self, input): return self.pool(input) @tvm.script.ir_module class expected1: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.nn.avg_pool2d( input_1, pool_size=[1, 1], strides=[1, 1], dilation=[1, 1], padding=[0, 0, 0, 0], count_include_pad=True, layout="NCHW", out_layout="NCHW", ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv class AvgPool2d2(Module): def __init__(self): super().__init__() self.pool = torch.nn.AvgPool2d(kernel_size=[4, 4], stride=2, padding=2, ceil_mode=True) def forward(self, input): return self.pool(input) class AvgPool2d3(Module): def forward(self, input): return torch.nn.functional.avg_pool2d( input, kernel_size=[4, 4], stride=2, padding=2, ceil_mode=True ) @tvm.script.ir_module class expected2: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")): with R.dataflow(): lv = R.nn.avg_pool2d( input_1, pool_size=[4, 4], strides=[2, 2], dilation=[1, 1], padding=[2, 2, 2, 2], ceil_mode=True, count_include_pad=True, layout="NCHW", out_layout="NCHW", ) gv = (lv,) R.output(gv) return gv class AvgPool2d4(Module): def forward(self, input): return torch.nn.functional.avg_pool2d(input, kernel_size=[2, 1], divisor_override=2) @tvm.script.ir_module class expected4: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")): with R.dataflow(): lv = R.nn.avg_pool2d( input_1, pool_size=[2, 1], strides=[2, 1], dilation=[1, 1], padding=[0, 0, 0, 0], ceil_mode=False, count_include_pad=True, layout="NCHW", out_layout="NCHW", ) gv = (lv,) R.output(gv) return gv class AvgPool2d5(Module): def forward(self, input): return torch.nn.functional.avg_pool2d( input, kernel_size=[2, 1], divisor_override=2, count_include_pad=False ) @tvm.script.ir_module class expected5: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")): with R.dataflow(): lv = R.nn.avg_pool2d( input_1, pool_size=[2, 1], strides=[2, 1], dilation=[1, 1], padding=[0, 0, 0, 0], ceil_mode=False, count_include_pad=False, layout="NCHW", out_layout="NCHW", ) gv = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(AvgPool2d1(), example_args, {}, expected1) verify_model(AvgPool2d2(), example_args, {}, expected2) verify_model(AvgPool2d3(), example_args, {}, expected2) verify_model(AvgPool2d4(), example_args, {}, expected4) verify_model(AvgPool2d5(), example_args, {}, expected5) def test_avg_pool3d(): class AvgPool3d1(Module): def __init__(self): super().__init__() self.pool = torch.nn.AvgPool3d(kernel_size=1) def forward(self, input): return self.pool(input) @tvm.script.ir_module class expected1: @R.function def main(input_1: R.Tensor((1, 3, 8, 8, 8), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 8, 8, 8), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 8, 8, 8), dtype="float32") = R.nn.avg_pool3d( input_1, pool_size=[1, 1, 1], strides=[1, 1, 1], dilation=[1, 1, 1], padding=[0, 0, 0, 0, 0, 0], ceil_mode=False, count_include_pad=True, layout="NCDHW", out_layout="NCDHW", ) gv: R.Tuple(R.Tensor((1, 3, 8, 8, 8), dtype="float32")) = (lv,) R.output(gv) return gv class AvgPool3d2(Module): def __init__(self): super().__init__() self.pool = torch.nn.AvgPool3d(kernel_size=3, stride=2, padding=1, ceil_mode=True) def forward(self, input): return self.pool(input) class AvgPool3d3(Module): def forward(self, input): return torch.nn.functional.avg_pool3d( input, kernel_size=3, stride=2, padding=1, ceil_mode=True ) @tvm.script.ir_module class expected2: @R.function def main(input_1: R.Tensor((1, 3, 8, 8, 8), dtype="float32")): with R.dataflow(): lv = R.nn.avg_pool3d( input_1, pool_size=[3, 3, 3], strides=[2, 2, 2], dilation=[1, 1, 1], padding=[1, 1, 1, 1, 1, 1], ceil_mode=True, count_include_pad=True, layout="NCDHW", out_layout="NCDHW", ) gv = (lv,) R.output(gv) return gv class AvgPool3d4(Module): def forward(self, input): return torch.nn.functional.avg_pool3d(input, kernel_size=[2, 1, 2], stride=[2, 1, 2]) @tvm.script.ir_module class expected3: @R.function def main(input_1: R.Tensor((1, 3, 8, 8, 8), dtype="float32")): with R.dataflow(): lv = R.nn.avg_pool3d( input_1, pool_size=[2, 1, 2], strides=[2, 1, 2], dilation=[1, 1, 1], padding=[0, 0, 0, 0, 0, 0], ceil_mode=False, count_include_pad=True, layout="NCDHW", out_layout="NCDHW", ) gv = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 3, 8, 8, 8, dtype=torch.float32),) verify_model(AvgPool3d1(), example_args, {}, expected1) verify_model(AvgPool3d2(), example_args, {}, expected2) verify_model(AvgPool3d3(), example_args, {}, expected2) verify_model(AvgPool3d4(), example_args, {}, expected3) def test_baddbmm(): class BAddBMM1(Module): def __init__(self): super().__init__() def forward(self, c, x, y): return torch.baddbmm(c, x, y) @tvm.script.ir_module class Expected1: @R.function def main( inp_0: R.Tensor((4, 128, 512), dtype="float32"), inp_1: R.Tensor((4, 128, 256), dtype="float32"), inp_2: R.Tensor((4, 256, 512), dtype="float32"), ) -> R.Tuple(R.Tensor((4, 128, 512), dtype="float32")): with R.dataflow(): lv: R.Tensor((4, 128, 512), dtype="float32") = R.matmul( inp_1, inp_2, out_dtype="float32" ) lv1: R.Tensor((4, 128, 512), dtype="float32") = R.add(inp_0, lv) gv: R.Tuple(R.Tensor((4, 128, 512), dtype="float32")) = (lv1,) R.output(gv) return gv class BAddBMM2(Module): def __init__(self): super().__init__() def forward(self, c, x, y): return torch.baddbmm(c, x, y, alpha=2, beta=0) @tvm.script.ir_module class Expected2: @R.function def main( inp_0: R.Tensor((4, 128, 512), dtype="float32"), inp_1: R.Tensor((4, 128, 256), dtype="float32"), inp_2: R.Tensor((4, 256, 512), dtype="float32"), ) -> R.Tuple(R.Tensor((4, 128, 512), dtype="float32")): with R.dataflow(): lv: R.Tensor((4, 128, 512), dtype="float32") = R.matmul( inp_1, inp_2, out_dtype="float32" ) lv1: R.Tensor((4, 128, 512), dtype="float32") = R.multiply( lv, R.const(2, "float32") ) gv: R.Tuple(R.Tensor((4, 128, 512), dtype="float32")) = (lv1,) R.output(gv) return gv class BAddBMM3(Module): def __init__(self): super().__init__() def forward(self, c, x, y): return torch.baddbmm(c, x, y, alpha=2, beta=3) @tvm.script.ir_module class Expected3: @R.function def main( inp_0: R.Tensor((4, 128, 512), dtype="float32"), inp_1: R.Tensor((4, 128, 256), dtype="float32"), inp_2: R.Tensor((4, 256, 512), dtype="float32"), ) -> R.Tuple(R.Tensor((4, 128, 512), dtype="float32")): with R.dataflow(): lv: R.Tensor((4, 128, 512), dtype="float32") = R.matmul( inp_1, inp_2, out_dtype="float32" ) lv1: R.Tensor((4, 128, 512), dtype="float32") = R.multiply( lv, R.const(2, "float32") ) lv2: R.Tensor((4, 128, 512), dtype="float32") = R.multiply( inp_0, R.const(3, "float32") ) lv3: R.Tensor((4, 128, 512), dtype="float32") = R.add(lv2, lv1) gv: R.Tuple(R.Tensor((4, 128, 512), dtype="float32")) = (lv3,) R.output(gv) return gv example_args = ( torch.randn(4, 128, 512, dtype=torch.float32), torch.randn(4, 128, 256, dtype=torch.float32), torch.randn(4, 256, 512, dtype=torch.float32), ) verify_model( BAddBMM1(), example_args, {}, Expected1, run_ep_decomposition=True, ) verify_model( BAddBMM2(), example_args, {}, Expected2, run_ep_decomposition=True, ) verify_model( BAddBMM3(), example_args, {}, Expected3, run_ep_decomposition=True, ) def test_bmm(): class BMM(Module): def __init__(self): super().__init__() def forward(self, x, y): return torch.bmm(x, y) @tvm.script.ir_module class Expected: @R.function def main( input_1: R.Tensor((4, 128, 256), dtype="float32"), input_2: R.Tensor((4, 256, 512), dtype="float32"), ) -> R.Tuple(R.Tensor((4, 128, 512), dtype="float32")): # block 0 with R.dataflow(): lv: R.Tensor((4, 128, 512), dtype="float32") = R.matmul( input_1, input_2, out_dtype="float32" ) gv: R.Tuple(R.Tensor((4, 128, 512), dtype="float32")) = (lv,) R.output(gv) return gv example_args = ( torch.randn(4, 128, 256, dtype=torch.float32), torch.randn(4, 256, 512, dtype=torch.float32), ) verify_model( BMM(), example_args, {}, Expected, run_ep_decomposition=True, ) def test_conv_transpose1d(): class ConvTranspose1d1(Module): def __init__(self): super().__init__() self.conv = torch.nn.ConvTranspose1d(6, 6, 3, bias=True) def forward(self, input): return self.conv(input) class ConvTranspose1d1Func(Module): def __init__(self): super().__init__() self.weight = torch.randn(size=[6, 6, 3]) self.bias = torch.randn(size=[6]) def forward(self, input): return torch.nn.functional.conv_transpose1d(input, self.weight, self.bias) @tvm.script.ir_module class expected1: @R.function def main( input_1: R.Tensor((1, 6, 4), dtype="float32"), w1: R.Tensor((6, 6, 3), dtype="float32"), w2: R.Tensor((6,), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 6, 6), dtype="float32")): # block 0 with R.dataflow(): lv1: R.Tensor((1, 6, 6), dtype="float32") = R.nn.conv1d_transpose( input_1, w1, strides=[1], padding=[0, 0], output_padding=[0], dilation=[1], data_layout="NCW", kernel_layout="IOW", out_layout="NCW", out_dtype="float32", ) lv2: R.Tensor((1, 6, 1)) = R.reshape(w2, [1, 6, 1]) lv3: R.Tensor((1, 6, 6), dtype="float32") = R.add(lv1, lv2) gv: R.Tuple(R.Tensor((1, 6, 6), dtype="float32")) = (lv3,) R.output(gv) return gv class ConvTranspose1d2(Module): def __init__(self): super().__init__() self.conv = torch.nn.ConvTranspose1d(6, 6, 3, bias=False) def forward(self, input): return self.conv(input) @tvm.script.ir_module class expected2: @R.function def main( input_1: R.Tensor((1, 6, 4), dtype="float32"), w1: R.Tensor((6, 6, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 6, 6), dtype="float32")): # block 0 with R.dataflow(): lv1: R.Tensor((1, 6, 6), dtype="float32") = R.nn.conv1d_transpose( input_1, w1, strides=[1], padding=[0, 0], output_padding=[0], dilation=[1], data_layout="NCW", kernel_layout="IOW", out_layout="NCW", out_dtype="float32", ) gv: R.Tuple(R.Tensor((1, 6, 6), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = (torch.randn(1, 6, 4, dtype=torch.float32),) model = ConvTranspose1d1() binding = {"w1": model.conv.weight.detach().numpy(), "w2": model.conv.bias.detach().numpy()} verify_model(model, example_args, binding, expected1) model = ConvTranspose1d1Func() binding = {"w1": model.weight.detach().numpy(), "w2": model.bias.detach().numpy()} verify_model(model, example_args, binding, expected1) model = ConvTranspose1d2() binding = {"w1": model.conv.weight.detach().numpy()} verify_model(model, example_args, binding, expected2) def test_conv_transpose2d(): class ConvTranspose2d1(Module): def __init__(self): super().__init__() self.conv = torch.nn.ConvTranspose2d(3, 3, 7, bias=True) def forward(self, input): return self.conv(input) class ConvTranspose2d1Func(Module): def __init__(self): super().__init__() self.weight = torch.randn(size=[3, 3, 7, 7]) self.bias = torch.randn(size=[3]) def forward(self, input): return torch.nn.functional.conv_transpose2d(input, self.weight, self.bias) @tvm.script.ir_module class expected1: @R.function def main( input_1: R.Tensor((1, 3, 10, 10), dtype="float32"), w1: R.Tensor((3, 3, 7, 7), dtype="float32"), w2: R.Tensor((3,), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 16, 16), dtype="float32")): # block 0 with R.dataflow(): lv1: R.Tensor((1, 3, 16, 16), dtype="float32") = R.nn.conv2d_transpose( input_1, w1, strides=[1, 1], padding=[0, 0, 0, 0], output_padding=[0, 0], dilation=[1, 1], data_layout="NCHW", kernel_layout="IOHW", out_layout="NCHW", out_dtype="float32", ) lv2: R.Tensor((1, 3, 1, 1)) = R.reshape(w2, [1, 3, 1, 1]) lv3: R.Tensor((1, 3, 16, 16), dtype="float32") = R.add(lv1, lv2) gv: R.Tuple(R.Tensor((1, 3, 16, 16), dtype="float32")) = (lv3,) R.output(gv) return gv class ConvTranspose2d2(Module): def __init__(self): super().__init__() self.conv = torch.nn.ConvTranspose2d(3, 3, 7, bias=False) def forward(self, input): return self.conv(input) @tvm.script.ir_module class expected2: @R.function def main( input_1: R.Tensor((1, 3, 10, 10), dtype="float32"), w1: R.Tensor((3, 3, 7, 7), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 16, 16), dtype="float32")): # block 0 with R.dataflow(): lv1: R.Tensor((1, 3, 16, 16), dtype="float32") = R.nn.conv2d_transpose( input_1, w1, strides=[1, 1], padding=[0, 0, 0, 0], output_padding=[0, 0], dilation=[1, 1], data_layout="NCHW", kernel_layout="IOHW", out_layout="NCHW", out_dtype="float32", ) gv: R.Tuple(R.Tensor((1, 3, 16, 16), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) model = ConvTranspose2d1() binding = {"w1": model.conv.weight.detach().numpy(), "w2": model.conv.bias.detach().numpy()} verify_model(model, example_args, binding, expected1) model = ConvTranspose2d1Func() binding = {"w1": model.weight.detach().numpy(), "w2": model.bias.detach().numpy()} verify_model(model, example_args, binding, expected1) model = ConvTranspose2d2() binding = {"w1": model.conv.weight.detach().numpy()} verify_model(model, example_args, binding, expected2) def test_conv1d(): class Conv1D1(Module): def __init__(self): super().__init__() self.conv = torch.nn.Conv1d(3, 6, 7, bias=True) def forward(self, input): return self.conv(input) class Conv1D1Func(Module): def __init__(self): super().__init__() self.weight = torch.randn(size=[6, 3, 7]) self.bias = torch.randn(size=[6]) def forward(self, input): return torch.nn.functional.conv1d(input, self.weight, self.bias) @tvm.script.ir_module class expected1: @R.function def main( w1: R.Tensor((6, 3, 7), dtype="float32"), w2: R.Tensor((6,), dtype="float32"), input_1: R.Tensor((1, 3, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 6, 4), dtype="float32")): # block 0 with R.dataflow(): lv1: R.Tensor((1, 6, 4), dtype="float32") = R.nn.conv1d( input_1, w1, strides=[1], padding=[0, 0], dilation=[1], data_layout="NCW", kernel_layout="OIW", out_layout="NCW", out_dtype="float32", ) lv2: R.Tensor((1, 6, 1), dtype="float32") = R.reshape(w2, [1, 6, 1]) lv3: R.Tensor((1, 6, 4), dtype="float32") = R.add(lv1, lv2) gv: R.Tuple(R.Tensor((1, 6, 4), dtype="float32")) = (lv3,) R.output(gv) return gv class Conv1D2(Module): def __init__(self): super().__init__() self.conv = torch.nn.Conv1d(3, 6, 7, bias=False) def forward(self, input): return self.conv(input) @tvm.script.ir_module class expected2: @R.function def main( w1: R.Tensor((6, 3, 7), dtype="float32"), input_1: R.Tensor((1, 3, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 6, 4), dtype="float32")): # block 0 with R.dataflow(): lv1: R.Tensor((1, 6, 4), dtype="float32") = R.nn.conv1d( input_1, w1, strides=[1], padding=[0, 0], dilation=[1], data_layout="NCW", kernel_layout="OIW", out_layout="NCW", out_dtype="float32", ) gv: R.Tuple(R.Tensor((1, 6, 4), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, dtype=torch.float32),) model = Conv1D1() binding = {"w1": model.conv.weight.detach().numpy(), "w2": model.conv.bias.detach().numpy()} verify_model(model, example_args, binding, expected1) model = Conv1D1Func() binding = {"w1": model.weight.detach().numpy(), "w2": model.bias.detach().numpy()} verify_model(model, example_args, binding, expected1) model = Conv1D2() binding = {"w1": model.conv.weight.detach().numpy()} verify_model(model, example_args, binding, expected2) def test_conv2d(): class Conv2D1(Module): def __init__(self): super().__init__() self.conv = torch.nn.Conv2d(3, 6, 7, bias=True) def forward(self, input): return self.conv(input) class Conv2D1Func(Module): def __init__(self): super().__init__() self.weight = torch.randn(size=[6, 3, 7, 7]) self.bias = torch.randn(size=[6]) def forward(self, input): return torch.nn.functional.conv2d(input, self.weight, self.bias) @tvm.script.ir_module class expected1: @R.function def main( input_1: R.Tensor((1, 3, 10, 10), dtype="float32"), w1: R.Tensor((6, 3, 7, 7), dtype="float32"), w2: R.Tensor((6,), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 6, 4, 4), dtype="float32")): # block 0 with R.dataflow(): lv1: R.Tensor((1, 6, 4, 4), dtype="float32") = R.nn.conv2d( input_1, w1, strides=[1, 1], padding=[0, 0, 0, 0], dilation=[1, 1], data_layout="NCHW", kernel_layout="OIHW", out_layout="NCHW", out_dtype="float32", ) lv2: R.Tensor((1, 6, 1, 1)) = R.reshape(w2, [1, 6, 1, 1]) lv3: R.Tensor((1, 6, 4, 4), dtype="float32") = R.add(lv1, lv2) gv: R.Tuple(R.Tensor((1, 6, 4, 4), dtype="float32")) = (lv3,) R.output(gv) return gv class Conv2D2(Module): def __init__(self): super().__init__() self.conv = torch.nn.Conv2d(3, 6, 7, bias=False) def forward(self, input): return self.conv(input) @tvm.script.ir_module class expected2: @R.function def main( input_1: R.Tensor((1, 3, 10, 10), dtype="float32"), w1: R.Tensor((6, 3, 7, 7), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 6, 4, 4), dtype="float32")): # block 0 with R.dataflow(): lv1: R.Tensor((1, 6, 4, 4), dtype="float32") = R.nn.conv2d( input_1, w1, strides=[1, 1], padding=[0, 0, 0, 0], dilation=[1, 1], data_layout="NCHW", kernel_layout="OIHW", out_layout="NCHW", out_dtype="float32", ) gv: R.Tuple(R.Tensor((1, 6, 4, 4), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) model = Conv2D1() binding = {"w1": model.conv.weight.detach().numpy(), "w2": model.conv.bias.detach().numpy()} verify_model(model, example_args, binding, expected1) model = Conv2D1Func() binding = {"w1": model.weight.numpy(), "w2": model.bias.numpy()} verify_model(model, example_args, binding, expected1) model = Conv2D2() binding = {"w1": model.conv.weight.detach().numpy()} verify_model(model, example_args, binding, expected2) def test_conv3d(): class Conv3D1(Module): def __init__(self): super().__init__() self.conv = torch.nn.Conv3d(3, 6, 7, bias=True) def forward(self, input): return self.conv(input) class Conv3D1Func(Module): def __init__(self): super().__init__() self.weight = torch.randn(size=[6, 3, 7, 7, 7]) self.bias = torch.randn(size=[6]) def forward(self, input): return torch.nn.functional.conv3d(input, self.weight, self.bias) @tvm.script.ir_module class expected1: @R.function def main( input_1: R.Tensor((1, 3, 10, 10, 10), dtype="float32"), w1: R.Tensor((6, 3, 7, 7, 7), dtype="float32"), w2: R.Tensor((6,), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 6, 4, 4, 4), dtype="float32")): # block 0 with R.dataflow(): lv1: R.Tensor((1, 6, 4, 4, 4), dtype="float32") = R.nn.conv3d( input_1, w1, strides=[1], padding=[0, 0, 0], dilation=[1], data_layout="NCDHW", kernel_layout="OIDHW", out_layout="NCDHW", out_dtype="float32", ) lv2: R.Tensor((1, 6, 1, 1, 1)) = R.reshape(w2, [1, 6, 1, 1, 1]) lv3: R.Tensor((1, 6, 4, 4, 4), dtype="float32") = R.add(lv1, lv2) gv: R.Tuple(R.Tensor((1, 6, 4, 4, 4), dtype="float32")) = (lv3,) R.output(gv) return gv class Conv3D2(Module): def __init__(self): super().__init__() self.conv = torch.nn.Conv3d(3, 6, 7, bias=False) def forward(self, input): return self.conv(input) @tvm.script.ir_module class expected2: @R.function def main( input_1: R.Tensor((1, 3, 10, 10, 10), dtype="float32"), w1: R.Tensor((6, 3, 7, 7, 7), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 6, 4, 4, 4), dtype="float32")): # block 0 with R.dataflow(): lv1: R.Tensor((1, 6, 4, 4, 4), dtype="float32") = R.nn.conv3d( input_1, w1, strides=[1], padding=[0, 0, 0], dilation=[1], data_layout="NCDHW", kernel_layout="OIDHW", out_layout="NCDHW", out_dtype="float32", ) gv: R.Tuple(R.Tensor((1, 6, 4, 4, 4), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, 10, dtype=torch.float32),) model = Conv3D1() binding = {"w1": model.conv.weight.detach().numpy(), "w2": model.conv.bias.detach().numpy()} verify_model(model, example_args, binding, expected1) model = Conv3D1Func() binding = {"w1": model.weight.detach().numpy(), "w2": model.bias.detach().numpy()} verify_model(model, example_args, binding, expected1) model = Conv3D2() binding = {"w1": model.conv.weight.detach().numpy()} verify_model(model, example_args, binding, expected2) def test_pad(): class PadModel(torch.nn.Module): def __init__(self, pad, mode="constant", value=0.0): super().__init__() self.pad = pad self.mode = mode self.value = value def forward(self, x): if self.mode == "constant": return torch.nn.functional.pad(x, self.pad, mode=self.mode, value=self.value) else: return torch.nn.functional.pad(x, self.pad, mode=self.mode) @tvm.script.ir_module class expected_constant: @R.function def main(x: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 14, 12), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 14, 12), dtype="float32") = R.nn.pad( x, pad_width=[0, 0, 0, 0, 2, 2, 1, 1], pad_mode="constant", pad_value=0.0, ) gv: R.Tuple(R.Tensor((1, 3, 14, 12), dtype="float32")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class expected_reflect: @R.function def main(x: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 14, 12), dtype="float32") ): with R.dataflow(): lv: R.Tensor((14,), dtype="int64") = R.arange( R.prim_value(-2), R.prim_value(12), R.prim_value(1), dtype="int64" ) lv1: R.Tensor((14,), dtype="int64") = R.abs(lv) lv2: R.Tensor((14,), dtype="int64") = R.subtract(R.const(9, "int64"), lv1) lv3: R.Tensor((14,), dtype="int64") = R.abs(lv2) lv4: R.Tensor((14,), dtype="int64") = R.subtract(R.const(9, "int64"), lv3) lv5: R.Tensor((1, 3, 14, 10), dtype="float32") = R.take(x, lv4, axis=2, mode="fast") lv6: R.Tensor((12,), dtype="int64") = R.arange( R.prim_value(-1), R.prim_value(11), R.prim_value(1), dtype="int64" ) lv7: R.Tensor((12,), dtype="int64") = R.abs(lv6) lv8: R.Tensor((12,), dtype="int64") = R.subtract(R.const(9, "int64"), lv7) lv9: R.Tensor((12,), dtype="int64") = R.abs(lv8) lv10: R.Tensor((12,), dtype="int64") = R.subtract(R.const(9, "int64"), lv9) lv11: R.Tensor((1, 3, 14, 12), dtype="float32") = R.take( lv5, lv10, axis=3, mode="fast" ) gv: R.Tuple(R.Tensor((1, 3, 14, 12), dtype="float32")) = (lv11,) R.output(gv) return gv @tvm.script.ir_module class expected_replicate: @R.function def main(x: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 14, 12), dtype="float32") ): with R.dataflow(): lv: R.Tensor((14,), dtype="int64") = R.arange( R.prim_value(-2), R.prim_value(12), R.prim_value(1), dtype="int64" ) lv1: R.Tensor((14,), dtype="int64") = R.clip(lv, R.prim_value(0), R.prim_value(9)) lv2: R.Tensor((1, 3, 14, 10), dtype="float32") = R.take(x, lv1, axis=2, mode="fast") lv3: R.Tensor((12,), dtype="int64") = R.arange( R.prim_value(-1), R.prim_value(11), R.prim_value(1), dtype="int64" ) lv4: R.Tensor((12,), dtype="int64") = R.clip(lv3, R.prim_value(0), R.prim_value(9)) lv5: R.Tensor((1, 3, 14, 12), dtype="float32") = R.take( lv2, lv4, axis=3, mode="fast" ) gv: R.Tuple(R.Tensor((1, 3, 14, 12), dtype="float32")) = (lv5,) R.output(gv) return gv @tvm.script.ir_module class expected_circular: @R.function def main(x: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 14, 12), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 14, 12), dtype="float32") = R.zeros( R.shape([1, 3, 14, 12]), dtype="float32" ) lv1: R.Tensor((1, 3, 14, 10), dtype="float32") = R.strided_slice( lv, (R.prim_value(3),), (R.prim_value(1),), (R.prim_value(11),), (R.prim_value(1),), assume_inbound=False, ) lv2: R.Tensor((1, 3, 10, 10), dtype="float32") = R.strided_slice( x, (R.prim_value(3),), (R.prim_value(0),), (R.prim_value(10),), (R.prim_value(1),), assume_inbound=False, ) lv3: R.Tensor((1, 3, 10, 10), dtype="float32") = R.strided_slice( lv1, (R.prim_value(2),), (R.prim_value(2),), (R.prim_value(12),), (R.prim_value(1),), assume_inbound=False, ) lv4: R.Tensor((1, 3, 10, 10), dtype="float32") = R.strided_slice( lv2, (R.prim_value(2),), (R.prim_value(0),), (R.prim_value(10),), (R.prim_value(1),), assume_inbound=False, ) lv5: R.Tensor((1, 3, 10, 10), dtype="float32") = R.broadcast_to( lv4, R.shape([1, 3, 10, 10]) ) lv6: R.Tensor((1, 3, 14, 10), dtype="float32") = R.strided_slice( lv, (R.prim_value(3),), (R.prim_value(1),), (R.prim_value(11),), (R.prim_value(1),), assume_inbound=False, ) lv7: R.Tensor((1, 3, 14, 10), dtype="float32") = R.slice_scatter( lv6, lv5, R.prim_value(2), R.prim_value(12), R.prim_value(1), axis=2 ) lv8: R.Tensor((1, 3, 14, 12), dtype="float32") = R.slice_scatter( lv, lv7, R.prim_value(1), R.prim_value(11), R.prim_value(1), axis=3 ) lv9: R.Tensor((1, 3, 14, 1), dtype="float32") = R.strided_slice( lv8, (R.prim_value(3),), (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(1),), assume_inbound=False, ) lv10: R.Tensor((1, 3, 14, 1), dtype="float32") = R.strided_slice( lv8, (R.prim_value(3),), (R.prim_value(10),), (R.prim_value(11),), (R.prim_value(1),), assume_inbound=False, ) lv11: R.Tensor((1, 3, 14, 1), dtype="float32") = R.broadcast_to( lv10, R.shape([1, 3, 14, 1]) ) lv12: R.Tensor((1, 3, 14, 12), dtype="float32") = R.slice_scatter( lv8, lv11, R.prim_value(0), R.prim_value(1), R.prim_value(1), axis=3 ) lv13: R.Tensor((1, 3, 14, 1), dtype="float32") = R.strided_slice( lv12, (R.prim_value(3),), (R.prim_value(11),), (R.prim_value(12),), (R.prim_value(1),), assume_inbound=False, ) lv14: R.Tensor((1, 3, 14, 1), dtype="float32") = R.strided_slice( lv12, (R.prim_value(3),), (R.prim_value(1),), (R.prim_value(2),), (R.prim_value(1),), assume_inbound=False, ) lv15: R.Tensor((1, 3, 14, 1), dtype="float32") = R.broadcast_to( lv14, R.shape([1, 3, 14, 1]) ) lv16: R.Tensor((1, 3, 14, 12), dtype="float32") = R.slice_scatter( lv12, lv15, R.prim_value(11), R.prim_value(12), R.prim_value(1), axis=3 ) lv17: R.Tensor((1, 3, 2, 12), dtype="float32") = R.strided_slice( lv16, (R.prim_value(2),), (R.prim_value(0),), (R.prim_value(2),), (R.prim_value(1),), assume_inbound=False, ) lv18: R.Tensor((1, 3, 2, 12), dtype="float32") = R.strided_slice( lv16, (R.prim_value(2),), (R.prim_value(10),), (R.prim_value(12),), (R.prim_value(1),), assume_inbound=False, ) lv19: R.Tensor((1, 3, 2, 12), dtype="float32") = R.broadcast_to( lv18, R.shape([1, 3, 2, 12]) ) lv20: R.Tensor((1, 3, 14, 12), dtype="float32") = R.slice_scatter( lv16, lv19, R.prim_value(0), R.prim_value(2), R.prim_value(1), axis=2 ) lv21: R.Tensor((1, 3, 2, 12), dtype="float32") = R.strided_slice( lv20, (R.prim_value(2),), (R.prim_value(12),), (R.prim_value(14),), (R.prim_value(1),), assume_inbound=False, ) lv22: R.Tensor((1, 3, 2, 12), dtype="float32") = R.strided_slice( lv20, (R.prim_value(2),), (R.prim_value(2),), (R.prim_value(4),), (R.prim_value(1),), assume_inbound=False, ) lv23: R.Tensor((1, 3, 2, 12), dtype="float32") = R.broadcast_to( lv22, R.shape([1, 3, 2, 12]) ) lv24: R.Tensor((1, 3, 14, 12), dtype="float32") = R.slice_scatter( lv20, lv23, R.prim_value(12), R.prim_value(14), R.prim_value(1), axis=2 ) gv: R.Tuple(R.Tensor((1, 3, 14, 12), dtype="float32")) = (lv24,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(PadModel(pad=[1, 1, 2, 2]), example_args, {}, expected_constant) verify_model( PadModel(pad=[1, 1, 2, 2], mode="reflect"), example_args, {}, expected_reflect, run_ep_decomposition=True, ) verify_model( PadModel(pad=[1, 1, 2, 2], mode="replicate"), example_args, {}, expected_replicate, run_ep_decomposition=True, ) verify_model( PadModel(pad=[1, 1, 2, 2], mode="circular"), example_args, {}, expected_circular, run_ep_decomposition=True, ) def test_pixel_shuffle(): class PixelShuffle1(torch.nn.Module): def __init__(self, upscale_factor=2): super().__init__() self.pixel_shuffle = torch.nn.PixelShuffle(upscale_factor) def forward(self, x): return self.pixel_shuffle(x) class PixelShuffle2(torch.nn.Module): def __init__(self, upscale_factor=2): super().__init__() self.upscale_factor = upscale_factor def forward(self, x): return torch.nn.functional.pixel_shuffle(x, self.upscale_factor) @tvm.script.ir_module class expected: @R.function def main(x: R.Tensor((1, 8, 10, 15), dtype="float32")) -> R.Tuple( R.Tensor((1, 2, 20, 30), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 2, 2, 2, 10, 15), dtype="float32") = R.reshape( x, R.shape([1, 2, 2, 2, 10, 15]) ) lv1: R.Tensor((1, 2, 10, 2, 15, 2), dtype="float32") = R.permute_dims( lv, axes=[0, 1, 4, 2, 5, 3] ) lv2: R.Tensor((1, 2, 20, 30), dtype="float32") = R.reshape( lv1, R.shape([1, 2, 20, 30]) ) gv: R.Tuple(R.Tensor((1, 2, 20, 30), dtype="float32")) = (lv2,) R.output(gv) return gv example_args = (torch.randn(1, 8, 10, 15, dtype=torch.float32),) verify_model(PixelShuffle1(upscale_factor=2), example_args, {}, expected) verify_model(PixelShuffle2(upscale_factor=2), example_args, {}, expected) def test_einsum(): class Einsum1(Module): def __init__(self): super().__init__() def forward(self, x): return torch.einsum("ii", x) class Einsum2(Module): def __init__(self): super().__init__() def forward(self, x, y): return torch.einsum("i,j->ij", x, y) @tvm.script.ir_module class Expected1: @R.function def main(inp_0: R.Tensor((4, 4), dtype="float32")) -> R.Tuple( R.Tensor((), dtype="float32") ): with R.dataflow(): lv: R.Tensor((), dtype="float32") = R.einsum((inp_0,), subscripts="ii") gv: R.Tuple(R.Tensor((), dtype="float32")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class Expected2: @R.function def main( inp_0: R.Tensor((5,), dtype="float32"), inp_1: R.Tensor((4,), dtype="float32") ) -> R.Tuple(R.Tensor((5, 4), dtype="float32")): with R.dataflow(): lv: R.Tensor((5, 4), dtype="float32") = R.einsum( (inp_0, inp_1), subscripts="i,j->ij" ) gv: R.Tuple(R.Tensor((5, 4), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(4, 4, dtype=torch.float32),) verify_model(Einsum1(), example_args, {}, Expected1, run_ep_decomposition=False) example_args = (torch.randn(5, dtype=torch.float32), torch.randn(4, dtype=torch.float32)) verify_model(Einsum2(), example_args, {}, Expected2, run_ep_decomposition=False) def test_outer(): class Outer(torch.nn.Module): def forward(self, x, y): return torch.outer(x, y) @tvm.script.ir_module class expected: @R.function def main(x: R.Tensor((3,), dtype="float32"), y: R.Tensor((4,), dtype="float32")) -> R.Tuple( R.Tensor((3, 4), dtype="float32") ): with R.dataflow(): lv: R.Tensor((3, 1), dtype="float32") = R.reshape(x, R.shape([3, 1])) lv1: R.Tensor((3, 4), dtype="float32") = R.multiply(lv, y) gv: R.Tuple(R.Tensor((3, 4), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = ( torch.randn(3, dtype=torch.float32), torch.randn(4, dtype=torch.float32), ) verify_model(Outer(), example_args, {}, expected) def test_embedding(): class Embedding(Module): def __init__(self): super().__init__() self.embedding = torch.nn.Embedding(10, 3) def forward(self, input): return self.embedding(input) @tvm.script.ir_module class expected1: @R.function def main( input_1: R.Tensor((4,), dtype="int64"), w1: R.Tensor((10, 3), dtype="float32") ) -> R.Tuple(R.Tensor((4, 3), dtype="float32")): # block 0 with R.dataflow(): lv: R.Tensor((4,), dtype="int32") = R.astype(input_1, dtype="int32") lv1: R.Tensor((4, 3), dtype="float32") = R.take(w1, lv, axis=0) gv: R.Tuple(R.Tensor((4, 3), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = (torch.randint(low=-int(1e5), high=int(1e5), size=(4,), dtype=torch.int64),) model = Embedding() binding = {"w1": model.embedding.weight.detach().numpy()} verify_model(model, example_args, binding, expected1) def test_groupnorm(): import torch from torch.nn import Module torch.set_grad_enabled(False) torch.random.manual_seed(0) class GroupNorm(Module): def __init__(self): super().__init__() self.gn = torch.nn.GroupNorm(3, 3) def forward(self, input): return self.gn(input) @tvm.script.ir_module class expected1: @R.function def main( input_1: R.Tensor((1, 3, 10, 10), dtype="float32"), w1: R.Tensor((3,), dtype="float32"), w2: R.Tensor((3,), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.nn.group_norm( input_1, w1, w2, num_groups=3, channel_axis=1, axes=[2, 3], epsilon=1.0000000000000001e-05, center=True, scale=True, ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) model = GroupNorm() binding = { "w1": model.gn.weight.detach().numpy(), "w2": model.gn.bias.detach().numpy(), } verify_model(model, example_args, binding, expected1) def test_instancenorm2d(): torch.set_grad_enabled(False) torch.random.manual_seed(0) class InstanceNorm2d(Module): def __init__(self): super().__init__() self.gn = torch.nn.InstanceNorm2d(3) def forward(self, input): return self.gn(input) @tvm.script.ir_module class expected1: @R.function def main( input_1: R.Tensor((1, 3, 10, 10), dtype="float32"), w1: R.Tensor((3,), dtype="float32"), w2: R.Tensor((3,), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.nn.instance_norm( input_1, w1, w2, channel_axis=1, axes=[0, 2, 3], epsilon=1e-05, center=True, scale=True, ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) model = InstanceNorm2d() binding = { "w1": torch.ones(3).detach().numpy(), "w2": torch.zeros(3).detach().numpy(), } verify_model(model, example_args, binding, expected1) def test_layernorm(): class LayerNorm(Module): def __init__(self): super().__init__() self.ln = torch.nn.LayerNorm((10, 10)) def forward(self, input): return self.ln(input) @tvm.script.ir_module class expected1: @R.function def main( input_1: R.Tensor((1, 3, 10, 10), dtype="float32"), w1: R.Tensor((10, 10), dtype="float32"), w2: R.Tensor((10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.nn.layer_norm( input_1, w1, w2, axes=[-2, -1], epsilon=1e-05, center=True, scale=True, ) gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) model = LayerNorm() binding = { "w1": model.ln.weight.detach().numpy(), "w2": model.ln.bias.detach().numpy(), } verify_model(LayerNorm(), example_args, binding, expected1) def test_linear(): class Dense1(Module): def __init__(self): super().__init__() self.linear = torch.nn.Linear(10, 7, bias=True) def forward(self, input): return self.linear(input) class Dense1Func(Module): def __init__(self): super().__init__() self.weight = torch.randn(size=[7, 10]) self.bias = torch.randn(size=[7]) def forward(self, input): return torch.nn.functional.linear(input, self.weight, self.bias) @tvm.script.ir_module class expected1: @R.function def main( w1: R.Tensor((7, 10), dtype="float32"), w2: R.Tensor((7,), dtype="float32"), input_1: R.Tensor((1, 3, 10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 7), dtype="float32")): # block 0 with R.dataflow(): lv: R.Tensor((30, 10), dtype="float32") = R.reshape(input_1, R.shape([30, 10])) lv1: R.Tensor((10, 7), dtype="float32") = R.permute_dims(w1, axes=[1, 0]) lv2: R.Tensor((30, 7), dtype="float32") = R.matmul(lv, lv1, out_dtype="float32") lv3: R.Tensor((30, 7), dtype="float32") = R.add(w2, lv2) lv4: R.Tensor((1, 3, 10, 7), dtype="float32") = R.reshape( lv3, R.shape([1, 3, 10, 7]) ) gv: R.Tuple(R.Tensor((1, 3, 10, 7), dtype="float32")) = (lv4,) R.output(gv) return gv class Dense2(Module): def __init__(self): super().__init__() self.linear = torch.nn.Linear(10, 7, bias=False) def forward(self, input): return self.linear(input) @tvm.script.ir_module class expected2: @R.function def main( w1: R.Tensor((7, 10), dtype="float32"), input_1: R.Tensor((1, 3, 10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 10, 7), dtype="float32")): # block 0 with R.dataflow(): lv: R.Tensor((10, 7), dtype="float32") = R.permute_dims(w1, axes=[1, 0]) lv1: R.Tensor((30, 10), dtype="float32") = R.reshape(input_1, R.shape([30, 10])) lv2: R.Tensor((30, 7), dtype="float32") = R.matmul(lv1, lv, out_dtype="float32") lv3: R.Tensor((1, 3, 10, 7), dtype="float32") = R.reshape( lv2, R.shape([1, 3, 10, 7]) ) gv: R.Tuple(R.Tensor((1, 3, 10, 7), dtype="float32")) = (lv3,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) model = Dense1() binding = {"w1": model.linear.weight.detach().numpy(), "w2": model.linear.bias.detach().numpy()} verify_model(model, example_args, binding, expected1) model = Dense1Func() binding = {"w1": model.weight.detach().numpy(), "w2": model.bias.detach().numpy()} verify_model(model, example_args, binding, expected1) model = Dense2() binding = {"w1": model.linear.weight.detach().numpy()} verify_model(model, example_args, binding, expected2) def test_maxpool1d(): class MaxPool1d(Module): def __init__(self): super().__init__() self.pool = torch.nn.MaxPool1d(kernel_size=2) def forward(self, input): return self.pool(input) class MaxPool1d_functional(Module): def __init__(self): super().__init__() def forward(self, input): return torch.nn.functional.max_pool1d(input, kernel_size=2) class MaxPool1d2(Module): def __init__(self): super().__init__() self.pool = torch.nn.MaxPool1d(kernel_size=3, stride=2) def forward(self, input): return self.pool(input) @tvm.script.ir_module class expected1: @R.function def main(input_1: R.Tensor((1, 3, 8), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 4), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 1, 8), dtype="float32") = R.expand_dims(input_1, axis=[-2]) lv1: R.Tensor((1, 3, 1, 4), dtype="float32") = R.nn.max_pool2d( lv, pool_size=[1, 2], strides=[1, 2], dilation=[1, 1], padding=[0, 0, 0, 0], layout="NCHW", out_layout="NCHW", ) lv2: R.Tensor((1, 3, 1, 4), dtype="float32") = R.zeros_like(lv1) lv3: R.Tuple( R.Tensor((1, 3, 1, 4), dtype="float32"), R.Tensor((1, 3, 1, 4), dtype="float32"), ) = (lv1, lv2) lv4: R.Tensor((1, 3, 1, 4), dtype="float32") = lv3[0] lv5: R.Tensor((1, 3, 4), dtype="float32") = R.squeeze(lv4, axis=[-2]) gv: R.Tuple(R.Tensor((1, 3, 4), dtype="float32")) = (lv5,) R.output(gv) return gv @tvm.script.ir_module class expected2: @R.function def main(input_1: R.Tensor((1, 3, 8), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 4), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 1, 8), dtype="float32") = R.expand_dims(input_1, axis=[-2]) lv1: R.Tensor((1, 3, 1, 4), dtype="float32") = R.nn.max_pool2d( lv, pool_size=[1, 2], strides=[1, 2], dilation=[1, 1], padding=[0, 0, 0, 0], layout="NCHW", out_layout="NCHW", ) lv2: R.Tensor((1, 3, 1, 4), dtype="float32") = R.zeros_like(lv1) lv3: R.Tuple( R.Tensor((1, 3, 1, 4), dtype="float32"), R.Tensor((1, 3, 1, 4), dtype="float32"), ) = (lv1, lv2) lv4: R.Tensor((1, 3, 1, 4), dtype="float32") = lv3[0] lv5: R.Tensor((1, 3, 4), dtype="float32") = R.squeeze(lv4, axis=[-2]) gv: R.Tuple(R.Tensor((1, 3, 4), dtype="float32")) = (lv5,) R.output(gv) return gv @tvm.script.ir_module class expected3: @R.function def main(input_1: R.Tensor((1, 3, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 4), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 1, 10), dtype="float32") = R.expand_dims(input_1, axis=[-2]) lv1: R.Tensor((1, 3, 1, 4), dtype="float32") = R.nn.max_pool2d( lv, pool_size=[1, 3], strides=[1, 2], dilation=[1, 1], padding=[0, 0, 0, 0], layout="NCHW", out_layout="NCHW", ) lv2: R.Tensor((1, 3, 1, 4), dtype="float32") = R.zeros_like(lv1) lv3: R.Tuple( R.Tensor((1, 3, 1, 4), dtype="float32"), R.Tensor((1, 3, 1, 4), dtype="float32"), ) = (lv1, lv2) lv4: R.Tensor((1, 3, 1, 4), dtype="float32") = lv3[0] lv5: R.Tensor((1, 3, 4), dtype="float32") = R.squeeze(lv4, axis=[-2]) gv: R.Tuple(R.Tensor((1, 3, 4), dtype="float32")) = (lv5,) R.output(gv) return gv # Example inputs example_args1 = (torch.randn(1, 3, 8, dtype=torch.float32),) example_args2 = (torch.randn(1, 3, 8, dtype=torch.float32),) example_args3 = (torch.randn(1, 3, 10, dtype=torch.float32),) # Verify the models verify_model(MaxPool1d(), example_args1, {}, expected1) verify_model(MaxPool1d_functional(), example_args2, {}, expected2) verify_model(MaxPool1d2(), example_args3, {}, expected3) def test_maxpool2d(): class MaxPool2d(Module): def __init__(self): super().__init__() self.pool = torch.nn.MaxPool2d(kernel_size=[1, 1]) def forward(self, input): return self.pool(input) class MaxPool2d_functional(Module): def __init__(self): super().__init__() def forward(self, input): return torch.nn.functional.max_pool2d(input, kernel_size=[1, 1]) @tvm.script.ir_module class expected1: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.nn.max_pool2d( input_1, pool_size=[1, 1], strides=[1, 1], dilation=[1, 1], padding=[0, 0, 0, 0], layout="NCHW", out_layout="NCHW", ) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.zeros_like(lv) lv2: R.Tuple( R.Tensor((1, 3, 10, 10), dtype="float32"), R.Tensor((1, 3, 10, 10), dtype="float32"), ) = (lv, lv1) lv3: R.Tensor((1, 3, 10, 10), dtype="float32") = lv2[0] gv: R.Tuple(R.Tensor((1, 3, 10, 10), dtype="float32")) = (lv3,) R.output(gv) return gv class MaxPool2d2(Module): def __init__(self): super().__init__() self.pool = torch.nn.MaxPool2d(kernel_size=[2, 2], dilation=[2, 3]) def forward(self, input): return self.pool(input) @tvm.script.ir_module class expected2: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 4, 4), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 4, 4), dtype="float32") = R.nn.max_pool2d( input_1, pool_size=[2, 2], strides=[2, 2], dilation=[2, 3], padding=[0, 0, 0, 0], layout="NCHW", out_layout="NCHW", ) lv1: R.Tensor((1, 3, 4, 4), dtype="float32") = R.zeros_like(lv) lv2: R.Tuple( R.Tensor((1, 3, 4, 4), dtype="float32"), R.Tensor((1, 3, 4, 4), dtype="float32") ) = (lv, lv1) lv3: R.Tensor((1, 3, 4, 4), dtype="float32") = lv2[0] gv: R.Tuple(R.Tensor((1, 3, 4, 4), dtype="float32")) = (lv3,) R.output(gv) return gv class MaxPool2d3(Module): def __init__(self): super().__init__() self.pool = torch.nn.MaxPool2d(kernel_size=[4, 4], padding=2, stride=2) def forward(self, input): return self.pool(input) @tvm.script.ir_module class expected3: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 6, 6), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 6, 6), dtype="float32") = R.nn.max_pool2d( input_1, pool_size=[4, 4], strides=[2, 2], dilation=[1, 1], padding=[2, 2, 2, 2], layout="NCHW", out_layout="NCHW", ) lv1: R.Tensor((1, 3, 6, 6), dtype="float32") = R.zeros_like(lv) lv2: R.Tuple( R.Tensor((1, 3, 6, 6), dtype="float32"), R.Tensor((1, 3, 6, 6), dtype="float32") ) = (lv, lv1) lv3: R.Tensor((1, 3, 6, 6), dtype="float32") = lv2[0] gv: R.Tuple(R.Tensor((1, 3, 6, 6), dtype="float32")) = (lv3,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(MaxPool2d(), example_args, {}, expected1) verify_model(MaxPool2d_functional(), example_args, {}, expected1) verify_model(MaxPool2d2(), example_args, {}, expected2) verify_model(MaxPool2d3(), example_args, {}, expected3) def test_maxpool3d(): class MaxPool3d(Module): def __init__(self): super().__init__() self.pool = torch.nn.MaxPool3d(kernel_size=[1, 1, 1]) def forward(self, input): return self.pool(input) class MaxPool3d_functional(Module): def __init__(self): super().__init__() def forward(self, input): return torch.nn.functional.max_pool3d(input, kernel_size=[1, 1, 1]) @tvm.script.ir_module class expected1: @R.function def main(input_1: R.Tensor((1, 3, 4, 4, 4), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 4, 4, 4), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 4, 4, 4), dtype="float32") = R.nn.max_pool3d( input_1, pool_size=[1, 1, 1], strides=[1, 1, 1], dilation=[1, 1, 1], padding=[0, 0, 0, 0, 0, 0], layout="NCDHW", out_layout="NCDHW", ) lv1: R.Tensor((1, 3, 4, 4, 4), dtype="float32") = R.zeros_like(lv) lv2: R.Tuple( R.Tensor((1, 3, 4, 4, 4), dtype="float32"), R.Tensor((1, 3, 4, 4, 4), dtype="float32"), ) = (lv, lv1) lv3: R.Tensor((1, 3, 4, 4, 4), dtype="float32") = lv2[0] gv: R.Tuple(R.Tensor((1, 3, 4, 4, 4), dtype="float32")) = (lv3,) R.output(gv) return gv class MaxPool3d2(Module): def __init__(self): super().__init__() self.pool = torch.nn.MaxPool3d(kernel_size=[2, 2, 2], dilation=[2, 2, 2]) def forward(self, input): return self.pool(input) @tvm.script.ir_module class expected2: @R.function def main(input_1: R.Tensor((1, 3, 8, 8, 8), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 3, 3, 3), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 3, 3, 3), dtype="float32") = R.nn.max_pool3d( input_1, pool_size=[2, 2, 2], strides=[2, 2, 2], dilation=[2, 2, 2], padding=[0, 0, 0, 0, 0, 0], layout="NCDHW", out_layout="NCDHW", ) lv1: R.Tensor((1, 3, 3, 3, 3), dtype="float32") = R.zeros_like(lv) lv2: R.Tuple( R.Tensor((1, 3, 3, 3, 3), dtype="float32"), R.Tensor((1, 3, 3, 3, 3), dtype="float32"), ) = (lv, lv1) lv3: R.Tensor((1, 3, 3, 3, 3), dtype="float32") = lv2[0] gv: R.Tuple(R.Tensor((1, 3, 3, 3, 3), dtype="float32")) = (lv3,) R.output(gv) return gv class MaxPool3d3(Module): def __init__(self): super().__init__() self.pool = torch.nn.MaxPool3d(kernel_size=[3, 3, 3], padding=1, stride=2) def forward(self, input): return self.pool(input) @tvm.script.ir_module class expected3: @R.function def main(input_1: R.Tensor((1, 3, 10, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 5, 5, 5), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 5, 5, 5), dtype="float32") = R.nn.max_pool3d( input_1, pool_size=[3, 3, 3], strides=[2, 2, 2], dilation=[1, 1, 1], padding=[1, 1, 1, 1, 1, 1], layout="NCDHW", out_layout="NCDHW", ) lv1: R.Tensor((1, 3, 5, 5, 5), dtype="float32") = R.zeros_like(lv) lv2: R.Tuple( R.Tensor((1, 3, 5, 5, 5), dtype="float32"), R.Tensor((1, 3, 5, 5, 5), dtype="float32"), ) = (lv, lv1) lv3: R.Tensor((1, 3, 5, 5, 5), dtype="float32") = lv2[0] gv: R.Tuple(R.Tensor((1, 3, 5, 5, 5), dtype="float32")) = (lv3,) R.output(gv) return gv # Example input tensors example_args1 = (torch.randn(1, 3, 4, 4, 4, dtype=torch.float32),) example_args2 = (torch.randn(1, 3, 8, 8, 8, dtype=torch.float32),) example_args3 = (torch.randn(1, 3, 10, 10, 10, dtype=torch.float32),) # Verify the models with expected IR modules verify_model(MaxPool3d(), example_args1, {}, expected1) verify_model(MaxPool3d_functional(), example_args1, {}, expected1) verify_model(MaxPool3d2(), example_args2, {}, expected2) verify_model(MaxPool3d3(), example_args3, {}, expected3) def test_scaled_dot_product_attention(): class Attention1(Module): def forward(self, q, k, v): return torch.nn.functional.scaled_dot_product_attention(q, k, v) @I.ir_module class Expected1: @R.function def main( q: R.Tensor((32, 8, 128, 64), dtype="float32"), k: R.Tensor((32, 8, 128, 64), dtype="float32"), v: R.Tensor((32, 8, 128, 64), dtype="float32"), ) -> R.Tuple(R.Tensor((32, 8, 128, 64), dtype="float32")): with R.dataflow(): lv: R.Tensor((32, 128, 8, 64), dtype="float32") = R.permute_dims( q, axes=[0, 2, 1, 3] ) lv1: R.Tensor((32, 128, 8, 64), dtype="float32") = R.permute_dims( k, axes=[0, 2, 1, 3] ) lv2: R.Tensor((32, 128, 8, 64), dtype="float32") = R.permute_dims( v, axes=[0, 2, 1, 3] ) lv3: R.Tensor((32, 128, 8, 64), dtype="float32") = R.nn.attention( lv, lv1, lv2, scale=None, causal_mask=None, window_size=None ) lv4: R.Tensor((32, 8, 128, 64), dtype="float32") = R.permute_dims( lv3, axes=[0, 2, 1, 3] ) gv: R.Tuple(R.Tensor((32, 8, 128, 64), dtype="float32")) = (lv4,) R.output(gv) return gv class Attention2(Module): def forward(self, q, k, v, mask): return torch.nn.functional.scaled_dot_product_attention(q, k, v, mask) @I.ir_module class Expected2: @R.function def main( q: R.Tensor((32, 8, 128, 64), dtype="float32"), k: R.Tensor((32, 8, 128, 64), dtype="float32"), v: R.Tensor((32, 8, 128, 64), dtype="float32"), mask: R.Tensor((32, 8, 128, 128), dtype="float32"), ) -> R.Tuple(R.Tensor((32, 8, 128, 64), dtype="float32")): with R.dataflow(): lv: R.Tensor((32, 128, 8, 64), dtype="float32") = R.permute_dims( q, axes=[0, 2, 1, 3] ) lv1: R.Tensor((32, 128, 8, 64), dtype="float32") = R.permute_dims( k, axes=[0, 2, 1, 3] ) lv2: R.Tensor((32, 128, 8, 64), dtype="float32") = R.permute_dims( v, axes=[0, 2, 1, 3] ) lv3: R.Tensor((32, 128, 8, 64), dtype="float32") = R.nn.attention_bias( lv, lv1, lv2, mask, scale=None, causal_mask=None, window_size=None ) lv4: R.Tensor((32, 8, 128, 64), dtype="float32") = R.permute_dims( lv3, axes=[0, 2, 1, 3] ) gv: R.Tuple(R.Tensor((32, 8, 128, 64), dtype="float32")) = (lv4,) R.output(gv) return gv verify_model( Attention1(), ( torch.randn(32, 8, 128, 64, dtype=torch.float32), torch.randn(32, 8, 128, 64, dtype=torch.float32), torch.randn(32, 8, 128, 64, dtype=torch.float32), ), {}, Expected1, run_ep_decomposition=False, ) verify_model( Attention2(), ( torch.randn(32, 8, 128, 64, dtype=torch.float32), torch.randn(32, 8, 128, 64, dtype=torch.float32), torch.randn(32, 8, 128, 64, dtype=torch.float32), torch.randn(32, 8, 128, 128, dtype=torch.float32), ), {}, Expected2, run_ep_decomposition=False, ) # Test 2D input (seq_len, head_dim) - bug fix for #18441 class Attention2D(Module): def forward(self, x): return torch.nn.functional.scaled_dot_product_attention(x, x, x, is_causal=False) @I.ir_module class Expected2D: @R.function def main( x: R.Tensor((8, 32), dtype="float32"), ) -> R.Tuple(R.Tensor((8, 32), dtype="float32")): with R.dataflow(): # Expand to add batch dimension for query, key, value separately # (8, 32) -> (1, 8, 32) lv: R.Tensor((1, 8, 32), dtype="float32") = R.expand_dims(x, axis=[0]) lv1: R.Tensor((1, 8, 32), dtype="float32") = R.expand_dims(x, axis=[0]) lv2: R.Tensor((1, 8, 32), dtype="float32") = R.expand_dims(x, axis=[0]) # Expand to add num_heads dimension: (1, 8, 32) -> (1, 1, 8, 32) lv3: R.Tensor((1, 1, 8, 32), dtype="float32") = R.expand_dims(lv, axis=[1]) lv4: R.Tensor((1, 1, 8, 32), dtype="float32") = R.expand_dims(lv1, axis=[1]) lv5: R.Tensor((1, 1, 8, 32), dtype="float32") = R.expand_dims(lv2, axis=[1]) # Attention operation: (1, 1, 8, 32) -> (1, 1, 8, 32) lv6: R.Tensor((1, 1, 8, 32), dtype="float32") = R.nn.attention( lv3, lv4, lv5, scale=None, causal_mask=None, window_size=None ) # Squeeze batch and num_heads dimensions: (1, 1, 8, 32) -> (8, 32) lv7: R.Tensor((8, 32), dtype="float32") = R.squeeze(lv6, axis=[0, 1]) gv: R.Tuple(R.Tensor((8, 32), dtype="float32")) = (lv7,) R.output(gv) return gv verify_model( Attention2D(), (torch.randn(8, 32, dtype=torch.float32),), {}, Expected2D, run_ep_decomposition=False, ) def test_unbind(): class Unbind1(Module): def forward(self, data): return torch.unbind(data) @tvm.script.ir_module class expected1: @R.function def main(data: R.Tensor((3, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((3, 10, 10), dtype="float32"), R.Tensor((3, 10, 10), dtype="float32"), R.Tensor((3, 10, 10), dtype="float32"), ): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.strided_slice( data, (R.prim_value(0),), (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(1),), assume_inbound=False, ) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.strided_slice( data, (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(2),), (R.prim_value(1),), assume_inbound=False, ) lv2: R.Tensor((1, 3, 10, 10), dtype="float32") = R.strided_slice( data, (R.prim_value(0),), (R.prim_value(2),), (R.prim_value(3),), (R.prim_value(1),), assume_inbound=False, ) lv3: R.Tensor((3, 10, 10), dtype="float32") = R.squeeze(lv, axis=[0]) lv4: R.Tensor((3, 10, 10), dtype="float32") = R.squeeze(lv1, axis=[0]) lv5: R.Tensor((3, 10, 10), dtype="float32") = R.squeeze(lv2, axis=[0]) gv: R.Tuple( R.Tensor((3, 10, 10), dtype="float32"), R.Tensor((3, 10, 10), dtype="float32"), R.Tensor((3, 10, 10), dtype="float32"), ) = (lv3, lv4, lv5) R.output(gv) return gv class Unbind2(Module): def forward(self, data): return torch.unbind(data, dim=1) @tvm.script.ir_module class expected2: @R.function def main(data: R.Tensor((3, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((3, 10, 10), dtype="float32"), R.Tensor((3, 10, 10), dtype="float32"), R.Tensor((3, 10, 10), dtype="float32"), ): # block 0 with R.dataflow(): lv: R.Tensor((3, 1, 10, 10), dtype="float32") = R.strided_slice( data, (R.prim_value(1),), (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(1),), assume_inbound=False, ) lv1: R.Tensor((3, 1, 10, 10), dtype="float32") = R.strided_slice( data, (R.prim_value(1),), (R.prim_value(1),), (R.prim_value(2),), (R.prim_value(1),), assume_inbound=False, ) lv2: R.Tensor((3, 1, 10, 10), dtype="float32") = R.strided_slice( data, (R.prim_value(1),), (R.prim_value(2),), (R.prim_value(3),), (R.prim_value(1),), assume_inbound=False, ) lv3: R.Tensor((3, 10, 10), dtype="float32") = R.squeeze(lv, axis=[1]) lv4: R.Tensor((3, 10, 10), dtype="float32") = R.squeeze(lv1, axis=[1]) lv5: R.Tensor((3, 10, 10), dtype="float32") = R.squeeze(lv2, axis=[1]) gv: R.Tuple( R.Tensor((3, 10, 10), dtype="float32"), R.Tensor((3, 10, 10), dtype="float32"), R.Tensor((3, 10, 10), dtype="float32"), ) = (lv3, lv4, lv5) R.output(gv) return gv @tvm.script.ir_module class expected3: @R.function def main(data: R.Tensor((3, 1, 3), dtype="float32")) -> R.Tuple( R.Tensor((3, 3), dtype="float32") ): with R.dataflow(): lv: R.Tensor((3, 1, 3), dtype="float32") = R.strided_slice( data, (R.prim_value(1),), (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(1),), assume_inbound=False, ) lv1: R.Tensor((3, 3), dtype="float32") = R.squeeze(lv, axis=[1]) gv: R.Tuple(R.Tensor((3, 3), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = (torch.randn(3, 3, 10, 10, dtype=torch.float32),) verify_model(Unbind1(), example_args, {}, expected1) verify_model(Unbind2(), example_args, {}, expected2) single_dim_args = (torch.randn(3, 1, 3, dtype=torch.float32),) verify_model(Unbind2(), single_dim_args, {}, expected3) def test_interpolate(): class InterpolateBilinear(Module): def forward(self, input): return torch.nn.functional.interpolate(input, (224, 224), mode="bilinear") @tvm.script.ir_module class expected_bilinear: @R.function def main(input: R.Tensor((1, 3, 112, 112), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 224, 224), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 224, 224), dtype="float32") = R.image.resize2d( input, R.shape([224, 224]), roi=[T.float32(0.0), T.float32(0.0), T.float32(0.0), T.float32(0.0)], layout="NCHW", method="linear", coordinate_transformation_mode="half_pixel", rounding_method="round", cubic_alpha=-0.75, cubic_exclude=0, extrapolation_value=0.0, ) gv: R.Tuple(R.Tensor((1, 3, 224, 224), dtype="float32")) = (lv,) R.output(gv) return gv class InterpolateNearest(Module): def forward(self, input): return torch.nn.functional.interpolate(input, (224, 224), mode="nearest") @tvm.script.ir_module class expected_nearest: @R.function def main(input: R.Tensor((1, 3, 112, 112), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 224, 224), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 224, 224), dtype="float32") = R.image.resize2d( input, R.shape([224, 224]), roi=[T.float32(0.0), T.float32(0.0), T.float32(0.0), T.float32(0.0)], layout="NCHW", method="nearest_neighbor", coordinate_transformation_mode="half_pixel", rounding_method="round", cubic_alpha=-0.75, cubic_exclude=0, extrapolation_value=0.0, ) gv: R.Tuple(R.Tensor((1, 3, 224, 224), dtype="float32")) = (lv,) R.output(gv) return gv class InterpolateBicubic(Module): def forward(self, input): return torch.nn.functional.interpolate(input, (224, 224), mode="bicubic") @I.ir_module class expected_bicubic: @R.function def main(input: R.Tensor((1, 3, 112, 112), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 224, 224), dtype="float32") ): with R.dataflow(): lv: R.Tensor((224,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(224), R.prim_value(1), dtype="int64" ) lv1: R.Tensor((224,), dtype="float32") = R.astype(lv, dtype="float32") lv2: R.Tensor((224,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(224), R.prim_value(1), dtype="int64" ) lv3: R.Tensor((224,), dtype="float32") = R.astype(lv2, dtype="float32") lv4: R.Tensor((224,), dtype="float32") = R.add(lv3, R.const(0.5, "float32")) lv5: R.Tensor((224,), dtype="float32") = R.multiply(lv4, R.const(0.5, "float32")) lv6: R.Tensor((224,), dtype="float32") = R.subtract(lv5, R.const(0.5, "float32")) lv7: R.Tensor((224,), dtype="float32") = R.add(lv1, R.const(0.5, "float32")) lv8: R.Tensor((224,), dtype="float32") = R.multiply(lv7, R.const(0.5, "float32")) lv9: R.Tensor((224,), dtype="float32") = R.subtract(lv8, R.const(0.5, "float32")) lv10: R.Tensor((224, 1), dtype="float32") = R.expand_dims(lv9, axis=[-1]) lv11: R.Tensor((224,), dtype="float32") = R.floor(lv6) lv12: R.Tensor((224, 1), dtype="float32") = R.floor(lv10) lv13: R.Tensor((224, 1), dtype="float32") = R.subtract(lv10, lv12) lv14: R.Tensor((224, 1), dtype="float32") = R.clip( lv13, R.prim_value(T.float64(0.0)), R.prim_value(T.float64(1.0)) ) lv15: R.Tensor((224,), dtype="float32") = R.subtract(lv6, lv11) lv16: R.Tensor((224,), dtype="float32") = R.clip( lv15, R.prim_value(T.float64(0.0)), R.prim_value(T.float64(1.0)) ) lv17: R.Tensor((224,), dtype="int64") = R.astype(lv11, dtype="int64") lv18: R.Tensor((224, 1), dtype="int64") = R.astype(lv12, dtype="int64") lv19: R.Tensor((224, 1), dtype="int64") = R.subtract(lv18, R.const(1, "int64")) lv20: R.Tensor((224, 1), dtype="int64") = R.add(lv18, R.const(1, "int64")) lv21: R.Tensor((224, 1), dtype="int64") = R.add(lv18, R.const(2, "int64")) lv22: R.Tensor((224,), dtype="int64") = R.subtract(lv17, R.const(1, "int64")) lv23: R.Tensor((224,), dtype="int64") = R.add(lv17, R.const(1, "int64")) lv24: R.Tensor((224,), dtype="int64") = R.add(lv17, R.const(2, "int64")) lv25: R.Tensor((224,), dtype="float32") = R.subtract(R.const(1.0, "float32"), lv16) lv26: R.Tensor((448,), dtype="float32") = R.concat((lv16, lv25), axis=0) lv27: R.Tensor((2, 224), dtype="float32") = R.reshape(lv26, R.shape([2, 224])) lv28: R.Tensor((224,), dtype="float32") = R.add(lv16, R.const(1.0, "float32")) lv29: R.Tensor((224,), dtype="float32") = R.subtract(R.const(2.0, "float32"), lv16) lv30: R.Tensor((448,), dtype="float32") = R.concat((lv28, lv29), axis=0) lv31: R.Tensor((2, 224), dtype="float32") = R.reshape(lv30, R.shape([2, 224])) lv32: R.Tensor((2, 224), dtype="float32") = R.multiply( lv31, R.const(-0.75, "float32") ) lv33: R.Tensor((2, 224), dtype="float32") = R.subtract( lv32, R.const(-3.75, "float32") ) lv34: R.Tensor((2, 224), dtype="float32") = R.multiply(lv33, lv31) lv35: R.Tensor((2, 224), dtype="float32") = R.add(lv34, R.const(-6.0, "float32")) lv36: R.Tensor((2, 224), dtype="float32") = R.multiply(lv35, lv31) lv37: R.Tensor((2, 224), dtype="float32") = R.subtract( lv36, R.const(-3.0, "float32") ) lv38: R.Tensor((2, 224), dtype="float32") = R.multiply( lv27, R.const(1.25, "float32") ) lv39: R.Tensor((2, 224), dtype="float32") = R.subtract( lv38, R.const(2.25, "float32") ) lv40: R.Tensor((2, 224), dtype="float32") = R.multiply(lv39, lv27) lv41: R.Tensor((2, 224), dtype="float32") = R.multiply(lv40, lv27) lv42: R.Tensor((2, 224), dtype="float32") = R.add(lv41, R.const(1.0, "float32")) lv43: R.Tensor((1, 224), dtype="float32") = R.strided_slice( lv37, (R.prim_value(0),), (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(1),), assume_inbound=False, ) lv44: R.Tensor((1, 224), dtype="float32") = R.strided_slice( lv37, (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(2),), (R.prim_value(1),), assume_inbound=False, ) lv45: R.Tensor((224,), dtype="float32") = R.squeeze(lv43, axis=[0]) lv46: R.Tensor((224,), dtype="float32") = R.squeeze(lv44, axis=[0]) lv47: R.Tensor((1, 224), dtype="float32") = R.strided_slice( lv42, (R.prim_value(0),), (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(1),), assume_inbound=False, ) lv48: R.Tensor((1, 224), dtype="float32") = R.strided_slice( lv42, (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(2),), (R.prim_value(1),), assume_inbound=False, ) lv49: R.Tensor((224,), dtype="float32") = R.squeeze(lv47, axis=[0]) lv50: R.Tensor((224,), dtype="float32") = R.squeeze(lv48, axis=[0]) lv51: R.Tensor((224, 1), dtype="float32") = R.subtract( R.const(1.0, "float32"), lv14 ) lv52: R.Tensor((448, 1), dtype="float32") = R.concat((lv14, lv51), axis=0) lv53: R.Tensor((2, 224, 1), dtype="float32") = R.reshape(lv52, R.shape([2, 224, 1])) lv54: R.Tensor((224, 1), dtype="float32") = R.add(lv14, R.const(1.0, "float32")) lv55: R.Tensor((224, 1), dtype="float32") = R.subtract( R.const(2.0, "float32"), lv14 ) lv56: R.Tensor((448, 1), dtype="float32") = R.concat((lv54, lv55), axis=0) lv57: R.Tensor((2, 224, 1), dtype="float32") = R.reshape(lv56, R.shape([2, 224, 1])) lv58: R.Tensor((2, 224, 1), dtype="float32") = R.multiply( lv57, R.const(-0.75, "float32") ) lv59: R.Tensor((2, 224, 1), dtype="float32") = R.subtract( lv58, R.const(-3.75, "float32") ) lv60: R.Tensor((2, 224, 1), dtype="float32") = R.multiply(lv59, lv57) lv61: R.Tensor((2, 224, 1), dtype="float32") = R.add(lv60, R.const(-6.0, "float32")) lv62: R.Tensor((2, 224, 1), dtype="float32") = R.multiply(lv61, lv57) lv63: R.Tensor((2, 224, 1), dtype="float32") = R.subtract( lv62, R.const(-3.0, "float32") ) lv64: R.Tensor((2, 224, 1), dtype="float32") = R.multiply( lv53, R.const(1.25, "float32") ) lv65: R.Tensor((2, 224, 1), dtype="float32") = R.subtract( lv64, R.const(2.25, "float32") ) lv66: R.Tensor((2, 224, 1), dtype="float32") = R.multiply(lv65, lv53) lv67: R.Tensor((2, 224, 1), dtype="float32") = R.multiply(lv66, lv53) lv68: R.Tensor((2, 224, 1), dtype="float32") = R.add(lv67, R.const(1.0, "float32")) lv69: R.Tensor((1, 224, 1), dtype="float32") = R.strided_slice( lv63, (R.prim_value(0),), (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(1),), assume_inbound=False, ) lv70: R.Tensor((1, 224, 1), dtype="float32") = R.strided_slice( lv63, (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(2),), (R.prim_value(1),), assume_inbound=False, ) lv71: R.Tensor((224, 1), dtype="float32") = R.squeeze(lv69, axis=[0]) lv72: R.Tensor((224, 1), dtype="float32") = R.squeeze(lv70, axis=[0]) lv73: R.Tensor((1, 224, 1), dtype="float32") = R.strided_slice( lv68, (R.prim_value(0),), (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(1),), assume_inbound=False, ) lv74: R.Tensor((1, 224, 1), dtype="float32") = R.strided_slice( lv68, (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(2),), (R.prim_value(1),), assume_inbound=False, ) lv75: R.Tensor((224, 1), dtype="float32") = R.squeeze(lv73, axis=[0]) lv76: R.Tensor((224, 1), dtype="float32") = R.squeeze(lv74, axis=[0]) lv77: R.Tensor((224, 1), dtype="int64") = R.clip( lv19, R.prim_value(0), R.prim_value(111) ) lv78: R.Tensor((224,), dtype="int64") = R.clip( lv22, R.prim_value(0), R.prim_value(111) ) lv79: R.Tensor((1, 3, 112, 224), dtype="float32") = R.take( input, lv78, axis=3, mode="fast" ) lv80: R.Tensor((224,), dtype="int64") = R.squeeze(lv77, axis=None) lv81: R.Tensor((1, 3, 224, 224), dtype="float32") = R.take( lv79, lv80, axis=2, mode="fast" ) lv82: R.Tensor((224, 1), dtype="int64") = R.clip( lv19, R.prim_value(0), R.prim_value(111) ) lv83: R.Tensor((224,), dtype="int64") = R.clip( lv17, R.prim_value(0), R.prim_value(111) ) lv84: R.Tensor((1, 3, 112, 224), dtype="float32") = R.take( input, lv83, axis=3, mode="fast" ) lv85: R.Tensor((224,), dtype="int64") = R.squeeze(lv82, axis=None) lv86: R.Tensor((1, 3, 224, 224), dtype="float32") = R.take( lv84, lv85, axis=2, mode="fast" ) lv87: R.Tensor((224, 1), dtype="int64") = R.clip( lv19, R.prim_value(0), R.prim_value(111) ) lv88: R.Tensor((224,), dtype="int64") = R.clip( lv23, R.prim_value(0), R.prim_value(111) ) lv89: R.Tensor((1, 3, 112, 224), dtype="float32") = R.take( input, lv88, axis=3, mode="fast" ) lv90: R.Tensor((224,), dtype="int64") = R.squeeze(lv87, axis=None) lv91: R.Tensor((1, 3, 224, 224), dtype="float32") = R.take( lv89, lv90, axis=2, mode="fast" ) lv92: R.Tensor((224, 1), dtype="int64") = R.clip( lv19, R.prim_value(0), R.prim_value(111) ) lv93: R.Tensor((224,), dtype="int64") = R.clip( lv24, R.prim_value(0), R.prim_value(111) ) lv94: R.Tensor((1, 3, 112, 224), dtype="float32") = R.take( input, lv93, axis=3, mode="fast" ) lv95: R.Tensor((224,), dtype="int64") = R.squeeze(lv92, axis=None) lv96: R.Tensor((1, 3, 224, 224), dtype="float32") = R.take( lv94, lv95, axis=2, mode="fast" ) lv97: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv81, lv45) lv98: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv86, lv49) lv99: R.Tensor((1, 3, 224, 224), dtype="float32") = R.add(lv97, lv98) lv100: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv91, lv50) lv101: R.Tensor((1, 3, 224, 224), dtype="float32") = R.add(lv99, lv100) lv102: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv96, lv46) lv103: R.Tensor((1, 3, 224, 224), dtype="float32") = R.add(lv101, lv102) lv104: R.Tensor((224, 1), dtype="int64") = R.clip( lv18, R.prim_value(0), R.prim_value(111) ) lv105: R.Tensor((224,), dtype="int64") = R.clip( lv22, R.prim_value(0), R.prim_value(111) ) lv106: R.Tensor((1, 3, 112, 224), dtype="float32") = R.take( input, lv105, axis=3, mode="fast" ) lv107: R.Tensor((224,), dtype="int64") = R.squeeze(lv104, axis=None) lv108: R.Tensor((1, 3, 224, 224), dtype="float32") = R.take( lv106, lv107, axis=2, mode="fast" ) lv109: R.Tensor((224, 1), dtype="int64") = R.clip( lv18, R.prim_value(0), R.prim_value(111) ) lv110: R.Tensor((224,), dtype="int64") = R.clip( lv17, R.prim_value(0), R.prim_value(111) ) lv111: R.Tensor((1, 3, 112, 224), dtype="float32") = R.take( input, lv110, axis=3, mode="fast" ) lv112: R.Tensor((224,), dtype="int64") = R.squeeze(lv109, axis=None) lv113: R.Tensor((1, 3, 224, 224), dtype="float32") = R.take( lv111, lv112, axis=2, mode="fast" ) lv114: R.Tensor((224, 1), dtype="int64") = R.clip( lv18, R.prim_value(0), R.prim_value(111) ) lv115: R.Tensor((224,), dtype="int64") = R.clip( lv23, R.prim_value(0), R.prim_value(111) ) lv116: R.Tensor((1, 3, 112, 224), dtype="float32") = R.take( input, lv115, axis=3, mode="fast" ) lv117: R.Tensor((224,), dtype="int64") = R.squeeze(lv114, axis=None) lv118: R.Tensor((1, 3, 224, 224), dtype="float32") = R.take( lv116, lv117, axis=2, mode="fast" ) lv119: R.Tensor((224, 1), dtype="int64") = R.clip( lv18, R.prim_value(0), R.prim_value(111) ) lv120: R.Tensor((224,), dtype="int64") = R.clip( lv24, R.prim_value(0), R.prim_value(111) ) lv121: R.Tensor((1, 3, 112, 224), dtype="float32") = R.take( input, lv120, axis=3, mode="fast" ) lv122: R.Tensor((224,), dtype="int64") = R.squeeze(lv119, axis=None) lv123: R.Tensor((1, 3, 224, 224), dtype="float32") = R.take( lv121, lv122, axis=2, mode="fast" ) lv124: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv108, lv45) lv125: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv113, lv49) lv126: R.Tensor((1, 3, 224, 224), dtype="float32") = R.add(lv124, lv125) lv127: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv118, lv50) lv128: R.Tensor((1, 3, 224, 224), dtype="float32") = R.add(lv126, lv127) lv129: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv123, lv46) lv130: R.Tensor((1, 3, 224, 224), dtype="float32") = R.add(lv128, lv129) lv131: R.Tensor((224, 1), dtype="int64") = R.clip( lv20, R.prim_value(0), R.prim_value(111) ) lv132: R.Tensor((224,), dtype="int64") = R.clip( lv22, R.prim_value(0), R.prim_value(111) ) lv133: R.Tensor((1, 3, 112, 224), dtype="float32") = R.take( input, lv132, axis=3, mode="fast" ) lv134: R.Tensor((224,), dtype="int64") = R.squeeze(lv131, axis=None) lv135: R.Tensor((1, 3, 224, 224), dtype="float32") = R.take( lv133, lv134, axis=2, mode="fast" ) lv136: R.Tensor((224, 1), dtype="int64") = R.clip( lv20, R.prim_value(0), R.prim_value(111) ) lv137: R.Tensor((224,), dtype="int64") = R.clip( lv17, R.prim_value(0), R.prim_value(111) ) lv138: R.Tensor((1, 3, 112, 224), dtype="float32") = R.take( input, lv137, axis=3, mode="fast" ) lv139: R.Tensor((224,), dtype="int64") = R.squeeze(lv136, axis=None) lv140: R.Tensor((1, 3, 224, 224), dtype="float32") = R.take( lv138, lv139, axis=2, mode="fast" ) lv141: R.Tensor((224, 1), dtype="int64") = R.clip( lv20, R.prim_value(0), R.prim_value(111) ) lv142: R.Tensor((224,), dtype="int64") = R.clip( lv23, R.prim_value(0), R.prim_value(111) ) lv143: R.Tensor((1, 3, 112, 224), dtype="float32") = R.take( input, lv142, axis=3, mode="fast" ) lv144: R.Tensor((224,), dtype="int64") = R.squeeze(lv141, axis=None) lv145: R.Tensor((1, 3, 224, 224), dtype="float32") = R.take( lv143, lv144, axis=2, mode="fast" ) lv146: R.Tensor((224, 1), dtype="int64") = R.clip( lv20, R.prim_value(0), R.prim_value(111) ) lv147: R.Tensor((224,), dtype="int64") = R.clip( lv24, R.prim_value(0), R.prim_value(111) ) lv148: R.Tensor((1, 3, 112, 224), dtype="float32") = R.take( input, lv147, axis=3, mode="fast" ) lv149: R.Tensor((224,), dtype="int64") = R.squeeze(lv146, axis=None) lv150: R.Tensor((1, 3, 224, 224), dtype="float32") = R.take( lv148, lv149, axis=2, mode="fast" ) lv151: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv135, lv45) lv152: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv140, lv49) lv153: R.Tensor((1, 3, 224, 224), dtype="float32") = R.add(lv151, lv152) lv154: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv145, lv50) lv155: R.Tensor((1, 3, 224, 224), dtype="float32") = R.add(lv153, lv154) lv156: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv150, lv46) lv157: R.Tensor((1, 3, 224, 224), dtype="float32") = R.add(lv155, lv156) lv158: R.Tensor((224, 1), dtype="int64") = R.clip( lv21, R.prim_value(0), R.prim_value(111) ) lv159: R.Tensor((224,), dtype="int64") = R.clip( lv22, R.prim_value(0), R.prim_value(111) ) lv160: R.Tensor((1, 3, 112, 224), dtype="float32") = R.take( input, lv159, axis=3, mode="fast" ) lv161: R.Tensor((224,), dtype="int64") = R.squeeze(lv158, axis=None) lv162: R.Tensor((1, 3, 224, 224), dtype="float32") = R.take( lv160, lv161, axis=2, mode="fast" ) lv163: R.Tensor((224, 1), dtype="int64") = R.clip( lv21, R.prim_value(0), R.prim_value(111) ) lv164: R.Tensor((224,), dtype="int64") = R.clip( lv17, R.prim_value(0), R.prim_value(111) ) lv165: R.Tensor((1, 3, 112, 224), dtype="float32") = R.take( input, lv164, axis=3, mode="fast" ) lv166: R.Tensor((224,), dtype="int64") = R.squeeze(lv163, axis=None) lv167: R.Tensor((1, 3, 224, 224), dtype="float32") = R.take( lv165, lv166, axis=2, mode="fast" ) lv168: R.Tensor((224, 1), dtype="int64") = R.clip( lv21, R.prim_value(0), R.prim_value(111) ) lv169: R.Tensor((224,), dtype="int64") = R.clip( lv23, R.prim_value(0), R.prim_value(111) ) lv170: R.Tensor((1, 3, 112, 224), dtype="float32") = R.take( input, lv169, axis=3, mode="fast" ) lv171: R.Tensor((224,), dtype="int64") = R.squeeze(lv168, axis=None) lv172: R.Tensor((1, 3, 224, 224), dtype="float32") = R.take( lv170, lv171, axis=2, mode="fast" ) lv173: R.Tensor((224, 1), dtype="int64") = R.clip( lv21, R.prim_value(0), R.prim_value(111) ) lv174: R.Tensor((224,), dtype="int64") = R.clip( lv24, R.prim_value(0), R.prim_value(111) ) lv175: R.Tensor((1, 3, 112, 224), dtype="float32") = R.take( input, lv174, axis=3, mode="fast" ) lv176: R.Tensor((224,), dtype="int64") = R.squeeze(lv173, axis=None) lv177: R.Tensor((1, 3, 224, 224), dtype="float32") = R.take( lv175, lv176, axis=2, mode="fast" ) lv178: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv162, lv45) lv179: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv167, lv49) lv180: R.Tensor((1, 3, 224, 224), dtype="float32") = R.add(lv178, lv179) lv181: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv172, lv50) lv182: R.Tensor((1, 3, 224, 224), dtype="float32") = R.add(lv180, lv181) lv183: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv177, lv46) lv184: R.Tensor((1, 3, 224, 224), dtype="float32") = R.add(lv182, lv183) lv185: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv103, lv71) lv186: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv130, lv75) lv187: R.Tensor((1, 3, 224, 224), dtype="float32") = R.add(lv185, lv186) lv188: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv157, lv76) lv189: R.Tensor((1, 3, 224, 224), dtype="float32") = R.add(lv187, lv188) lv190: R.Tensor((1, 3, 224, 224), dtype="float32") = R.multiply(lv184, lv72) lv191: R.Tensor((1, 3, 224, 224), dtype="float32") = R.add(lv189, lv190) gv: R.Tuple(R.Tensor((1, 3, 224, 224), dtype="float32")) = (lv191,) R.output(gv) return gv example_args = (torch.randn(1, 3, 112, 112, dtype=torch.float32),) verify_model(InterpolateBilinear(), example_args, {}, expected_bilinear) verify_model(InterpolateNearest(), example_args, {}, expected_nearest) verify_model(InterpolateBicubic(), example_args, {}, expected_bicubic) def test_interpolate_antialiased(): """Test bilinear interpolation with antialiasing enabled.""" class InterpolateBilinearAA(Module): def forward(self, input): return torch.nn.functional.interpolate( input, size=(64, 64), mode="bilinear", align_corners=False, antialias=True ) @tvm.script.ir_module class expected_bilinear_aa: @R.function def main(input: R.Tensor((1, 3, 32, 32), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 64, 64), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 64, 64), dtype="float32") = R.image.resize2d( input, R.shape([64, 64]), roi=[T.float32(0.0), T.float32(0.0), T.float32(0.0), T.float32(0.0)], layout="NCHW", method="linear", coordinate_transformation_mode="half_pixel", rounding_method="round", cubic_alpha=-0.75, cubic_exclude=0, extrapolation_value=0.0, ) gv: R.Tuple(R.Tensor((1, 3, 64, 64), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 3, 32, 32, dtype=torch.float32),) verify_model(InterpolateBilinearAA(), example_args, {}, expected_bilinear_aa) def test_mean(): class Mean(Module): def forward(self, input): return input.mean(-1) class MeanKeepDim(Module): def forward(self, input: torch.Tensor): return input.mean(-1, keepdim=True) class MeanWithoutDim(Module): def forward(self, input: torch.Tensor): return input.mean() @I.ir_module class Expected1: @R.function def main(inp_0: R.Tensor((256, 256), dtype="float32")) -> R.Tuple( R.Tensor((256,), dtype="float32") ): with R.dataflow(): lv: R.Tensor((256,), dtype="float32") = R.mean(inp_0, axis=[-1], keepdims=False) gv: R.Tuple(R.Tensor((256,), dtype="float32")) = (lv,) R.output(gv) return gv @I.ir_module class Expected2: @R.function def main(inp_0: R.Tensor((256, 256), dtype="float32")) -> R.Tuple( R.Tensor((256, 1), dtype="float32") ): with R.dataflow(): lv: R.Tensor((256, 1), dtype="float32") = R.mean(inp_0, axis=[-1], keepdims=True) gv: R.Tuple(R.Tensor((256, 1), dtype="float32")) = (lv,) R.output(gv) return gv @I.ir_module class Expected3: @R.function def main(inp_0: R.Tensor((256, 256), dtype="float32")) -> R.Tuple( R.Tensor((), dtype="float32") ): with R.dataflow(): lv: R.Tensor((), dtype="float32") = R.mean(inp_0, axis=None, keepdims=False) gv: R.Tuple(R.Tensor((), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(256, 256, dtype=torch.float32),) verify_model(Mean(), example_args, {}, Expected1) verify_model(MeanKeepDim(), example_args, {}, Expected2) verify_model(MeanWithoutDim(), example_args, {}, Expected3) def test_median(): class Median(Module): def forward(self, input): return input.median(-1) class MedianKeepDim(Module): def forward(self, input): return input.median(-1, keepdim=True) class MedianWithoutDim(Module): def forward(self, input): return input.median() @I.ir_module class Expected1: @R.function def main(inp_0: R.Tensor((256, 256), dtype="float32")) -> R.Tuple( R.Tensor((256,), dtype="float32"), R.Tensor((256,), dtype="int64") ): with R.dataflow(): lv: R.Tuple(R.Tensor((256,), dtype="float32"), R.Tensor((256,), dtype="int64")) = ( R.median(inp_0, axis=[-1], keepdims=False) ) lv1: R.Tensor((256,), dtype="float32") = lv[0] lv2: R.Tensor((256,), dtype="int64") = lv[1] gv: R.Tuple(R.Tensor((256,), dtype="float32"), R.Tensor((256,), dtype="int64")) = ( lv1, lv2, ) R.output(gv) return gv @I.ir_module class Expected2: @R.function def main(inp_0: R.Tensor((256, 256), dtype="float32")) -> R.Tuple( R.Tensor((256, 1), dtype="float32"), R.Tensor((256, 1), dtype="int64") ): with R.dataflow(): lv: R.Tuple( R.Tensor((256, 1), dtype="float32"), R.Tensor((256, 1), dtype="int64") ) = R.median(inp_0, axis=[-1], keepdims=True) lv1: R.Tensor((256, 1), dtype="float32") = lv[0] lv2: R.Tensor((256, 1), dtype="int64") = lv[1] gv: R.Tuple( R.Tensor((256, 1), dtype="float32"), R.Tensor((256, 1), dtype="int64") ) = (lv1, lv2) R.output(gv) return gv @I.ir_module class Expected3: @R.function def main(inp_0: R.Tensor((256, 256), dtype="float32")) -> R.Tuple( R.Tensor((), dtype="float32") ): with R.dataflow(): lv: R.Tensor((), dtype="float32") = R.median(inp_0, axis=None, keepdims=False) gv: R.Tuple(R.Tensor((), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(256, 256, dtype=torch.float32),) verify_model(Median(), example_args, {}, Expected1) verify_model(MedianKeepDim(), example_args, {}, Expected2) verify_model(MedianWithoutDim(), example_args, {}, Expected3) def test_sum(): class Sum(Module): def forward(self, x): return torch.sum(x, (2, 1)) class SumKeepDim(Module): def forward(self, x): return torch.sum(x, (2, 1), keepdim=True) class SumWithoutDim(Module): def forward(self, x): return torch.sum(x) @tvm.script.ir_module class expected1: @R.function def main(inp_0: R.Tensor((1, 2, 3, 4), dtype="float32")) -> R.Tuple( R.Tensor((1, 4), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 4), dtype="float32") = R.sum(inp_0, axis=[2, 1], keepdims=False) gv: R.Tuple(R.Tensor((1, 4), dtype="float32")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class expected2: @R.function def main(inp_0: R.Tensor((1, 2, 3, 4), dtype="float32")) -> R.Tuple( R.Tensor((1, 1, 1, 4), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 1, 1, 4), dtype="float32") = R.sum( inp_0, axis=[2, 1], keepdims=True ) gv: R.Tuple(R.Tensor((1, 1, 1, 4), dtype="float32")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class expected3: @R.function def main(inp_0: R.Tensor((1, 2, 3, 4), dtype="float32")) -> R.Tuple( R.Tensor((), dtype="float32") ): with R.dataflow(): lv: R.Tensor((), dtype="float32") = R.sum(inp_0, axis=None, keepdims=False) gv: R.Tuple(R.Tensor((), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 2, 3, 4, dtype=torch.float32),) verify_model(Sum(), example_args, {}, expected1) verify_model(SumKeepDim(), example_args, {}, expected2) verify_model(SumWithoutDim(), example_args, {}, expected3) def test_argmax_argmin(): example_args = (torch.randn(256, 256, dtype=torch.float32),) class Argmax1(Module): def __init__(self) -> None: super().__init__() def forward(self, input): return torch.argmax(input, dim=-1) class Argmax2(Module): def __init__(self) -> None: super().__init__() def forward(self, input): return torch.argmax(input, dim=-1, keepdim=True) @tvm.script.ir_module class expected_argmax1: @R.function def main(inp_0: R.Tensor((256, 256), dtype="float32")) -> R.Tuple( R.Tensor((256,), dtype="int64") ): with R.dataflow(): lv: R.Tensor((256,), dtype="int64") = R.argmax(inp_0, axis=-1, keepdims=False) gv: R.Tuple(R.Tensor((256,), dtype="int64")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class expected_argmax2: @R.function def main(inp_0: R.Tensor((256, 256), dtype="float32")) -> R.Tuple( R.Tensor((256, 1), dtype="int64") ): with R.dataflow(): lv: R.Tensor((256, 1), dtype="int64") = R.argmax(inp_0, axis=-1, keepdims=True) gv: R.Tuple(R.Tensor((256, 1), dtype="int64")) = (lv,) R.output(gv) return gv verify_model(Argmax1(), example_args, {}, expected_argmax1) verify_model(Argmax2(), example_args, {}, expected_argmax2) class Argmin1(Module): def __init__(self) -> None: super().__init__() def forward(self, input): return torch.argmin(input) class Argmin2(Module): def __init__(self) -> None: super().__init__() def forward(self, input): return torch.argmin(input, keepdim=True) @tvm.script.ir_module class expected_argmin1: @R.function def main(inp_0: R.Tensor((256, 256), dtype="float32")) -> R.Tuple( R.Tensor((), dtype="int64") ): with R.dataflow(): lv: R.Tensor((), dtype="int64") = R.argmin(inp_0, axis=None, keepdims=False) gv: R.Tuple(R.Tensor((), dtype="int64")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class expected_argmin2: @R.function def main(inp_0: R.Tensor((256, 256), dtype="float32")) -> R.Tuple( R.Tensor((1, 1), dtype="int64") ): with R.dataflow(): lv: R.Tensor((1, 1), dtype="int64") = R.argmin(inp_0, axis=None, keepdims=True) gv: R.Tuple(R.Tensor((1, 1), dtype="int64")) = (lv,) R.output(gv) return gv verify_model(Argmin1(), example_args, {}, expected_argmin1) verify_model(Argmin2(), example_args, {}, expected_argmin2) def test_cat_concat(): class Cat0(Module): def forward(self, x, y): return torch.cat((x, y)) class Cat1(Module): def forward(self, x, y): return torch.cat((x, y), dim=1) class Cat2(Module): def forward(self, x, y): return torch.cat((x, y), 1) class Cat3(Module): def forward(self, x, y): return torch.concat((x, y), dim=0) @I.ir_module class Expected1: @R.function def main( inp_0: R.Tensor((2, 3), dtype="float32"), inp_1: R.Tensor((2, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((4, 3), dtype="float32")): with R.dataflow(): lv: R.Tensor((4, 3), dtype="float32") = R.concat((inp_0, inp_1), axis=0) gv: R.Tuple(R.Tensor((4, 3), dtype="float32")) = (lv,) R.output(gv) return gv @I.ir_module class Expected2: @R.function def main( inp_0: R.Tensor((2, 3), dtype="float32"), inp_1: R.Tensor((2, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((2, 6), dtype="float32")): with R.dataflow(): lv: R.Tensor((2, 6), dtype="float32") = R.concat((inp_0, inp_1), axis=1) gv: R.Tuple(R.Tensor((2, 6), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(2, 3, dtype=torch.float32), torch.randn(2, 3, dtype=torch.float32)) verify_model(Cat0(), example_args, {}, Expected1) verify_model(Cat1(), example_args, {}, Expected2) verify_model(Cat2(), example_args, {}, Expected2) verify_model(Cat3(), example_args, {}, Expected1) def test_cumsum(): class Cumsum(Module): def forward(self, input): return torch.cumsum(input, dim=1, dtype=torch.int32) @tvm.script.ir_module class expected1: @R.function def main(input_1: R.Tensor((1, 2, 3, 4), dtype="float32")) -> R.Tuple( R.Tensor((1, 2, 3, 4), dtype="int32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 2, 3, 4), dtype="int32") = R.cumsum(input_1, axis=1, dtype="int32") gv: R.Tuple(R.Tensor((1, 2, 3, 4), dtype="int32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 2, 3, 4, dtype=torch.float32),) verify_model(Cumsum(), example_args, {}, expected1) def test_expand(): class Expand1(Module): def forward(self, x): return x.expand(4, 2, 3, 4) class Expand2(Module): def forward(self, x): return x.expand(4, -1, -1, 4) @tvm.script.ir_module class expected1: @R.function def main(x: R.Tensor((1, 2, 3, 4), dtype="float32")) -> R.Tuple( R.Tensor((4, 2, 3, 4), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((4, 2, 3, 4), dtype="float32") = R.broadcast_to(x, (4, 2, 3, 4)) gv: R.Tuple(R.Tensor((4, 2, 3, 4), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 2, 3, 4, dtype=torch.float32),) verify_model(Expand1(), example_args, {}, expected1) verify_model(Expand2(), example_args, {}, expected1) def test_flatten(): class Flatten(Module): def __init__(self): super().__init__() self.f = torch.nn.Flatten(2, -1) def forward(self, input): return self.f(input) @tvm.script.ir_module class expected1: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 100), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 100), dtype="float32") = R.reshape(input_1, (1, 3, 100)) gv: R.Tuple(R.Tensor((1, 3, 100), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(Flatten(), example_args, {}, expected1) def test_meshgrid(): class Meshgrid1(Module): def forward(self, input1, input2): return torch.meshgrid((input1, input2), indexing="ij") class Meshgrid2(Module): def forward(self, input1, input2): return torch.meshgrid((input1, input2), indexing="xy") @tvm.script.ir_module class expected1: @R.function def main( input1: R.Tensor((3,), dtype="float32"), input2: R.Tensor((3,), dtype="float32") ) -> R.Tuple(R.Tensor((3, 3), dtype="float32"), R.Tensor((3, 3), dtype="float32")): with R.dataflow(): lv: R.Tensor((3, 1), dtype="float32") = R.reshape(input1, R.shape([3, 1])) lv1: R.Tensor((3, 3), dtype="float32") = R.broadcast_to(lv, R.shape([3, 3])) lv2: R.Tensor((1, 3), dtype="float32") = R.reshape(input2, R.shape([1, 3])) lv3: R.Tensor((3, 3), dtype="float32") = R.broadcast_to(lv2, R.shape([3, 3])) gv: R.Tuple( R.Tensor((3, 3), dtype="float32"), R.Tensor((3, 3), dtype="float32") ) = (lv1, lv3) R.output(gv) return gv @tvm.script.ir_module class expected2: @R.function def main( input1: R.Tensor((3,), dtype="float32"), input2: R.Tensor((3,), dtype="float32") ) -> R.Tuple(R.Tensor((3, 3), dtype="float32"), R.Tensor((3, 3), dtype="float32")): with R.dataflow(): lv: R.Tensor((3, 1), dtype="float32") = R.reshape(input2, R.shape([3, 1])) lv1: R.Tensor((3, 3), dtype="float32") = R.broadcast_to(lv, R.shape([3, 3])) lv2: R.Tensor((1, 3), dtype="float32") = R.reshape(input1, R.shape([1, 3])) lv3: R.Tensor((3, 3), dtype="float32") = R.broadcast_to(lv2, R.shape([3, 3])) gv: R.Tuple( R.Tensor((3, 3), dtype="float32"), R.Tensor((3, 3), dtype="float32") ) = (lv3, lv1) R.output(gv) return gv example_args = ( torch.randn(3, dtype=torch.float32), torch.randn(3, dtype=torch.float32), ) verify_model(Meshgrid1(), example_args, {}, expected1) verify_model(Meshgrid2(), example_args, {}, expected2) def test_permute(): class Permute1(Module): def forward(self, x): return x.permute(0, 3, 2, 1) class Permute2(Module): def forward(self, x): return torch.permute(x, (0, 3, 2, 1)) @tvm.script.ir_module class expected1: @R.function def main(x: R.Tensor((1, 2, 3, 4), dtype="float32")) -> R.Tuple( R.Tensor((1, 4, 3, 2), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 4, 3, 2), dtype="float32") = R.permute_dims(x, axes=[0, 3, 2, 1]) gv: R.Tuple(R.Tensor((1, 4, 3, 2), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 2, 3, 4, dtype=torch.float32),) verify_model(Permute1(), example_args, {}, expected1) verify_model(Permute2(), example_args, {}, expected1) def test_repeat(): class Tile1(Module): def forward(self, x: torch.Tensor): return x.repeat(2) class Tile2(Module): def forward(self, x: torch.Tensor): return x.repeat(4, 2) @tvm.script.ir_module class expected1: @R.function def main(x: R.Tensor((3,), dtype="float32")) -> R.Tuple(R.Tensor((6,), dtype="float32")): # block 0 with R.dataflow(): lv: R.Tensor((6,), dtype="float32") = R.tile(x, 2) gv: R.Tuple(R.Tensor((6,), dtype="float32")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class expected2: @R.function def main(x: R.Tensor((1, 3), dtype="float32")) -> R.Tuple( R.Tensor((4, 6), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((4, 6), dtype="float32") = R.tile(x, [4, 2]) gv: R.Tuple(R.Tensor((4, 6), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(3, dtype=torch.float32),) verify_model(Tile1(), example_args, {}, expected1) example_args = (torch.randn(1, 3, dtype=torch.float32),) verify_model(Tile2(), example_args, {}, expected2) example_args = (torch.randn(1, 3, dtype=torch.float32),) verify_model(Tile2(), example_args, {}, expected2) def test_reshape(): class Reshape(Module): def forward(self, x): return x.reshape(2, 12) @tvm.script.ir_module class expected1: @R.function def main(x: R.Tensor((1, 2, 3, 4), dtype="float32")) -> R.Tuple( R.Tensor((2, 12), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((2, 12), dtype="float32") = R.reshape(x, (2, 12)) gv: R.Tuple(R.Tensor((2, 12), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 2, 3, 4, dtype=torch.float32),) verify_model(Reshape(), example_args, {}, expected1) def test_reshape_as(): class ReshapeAs(Module): def forward(self, x: torch.Tensor, y: torch.Tensor): return x.reshape_as(y) @tvm.script.ir_module class expected1: @R.function def main( x: R.Tensor((1, 2, 3, 4), dtype="float32"), y: R.Tensor((2, 12), dtype="float32"), ) -> R.Tuple(R.Tensor((2, 12), dtype="float32")): # block 0 with R.dataflow(): lv: R.Tensor((2, 12), dtype="float32") = R.reshape(x, (2, 12)) gv: R.Tuple(R.Tensor((2, 12), dtype="float32")) = (lv,) R.output(gv) return gv example_args = ( torch.randn(1, 2, 3, 4, dtype=torch.float32), torch.randn(2, 12, dtype=torch.float32), ) verify_model(ReshapeAs(), example_args, {}, expected1) def test_roll(): class Roll1(Module): def forward(self, x): return torch.roll(x, 1) class Roll2(Module): def forward(self, x): return torch.roll(x, -1, 0) class Roll3(Module): def forward(self, x): return torch.roll(x, shifts=(2, 1), dims=(0, 1)) # Test case 1: torch.roll(x, 1) @I.ir_module class Expected1: @R.function def main(x: R.Tensor((4, 2), dtype="int64")) -> R.Tuple(R.Tensor((4, 2), dtype="int64")): with R.dataflow(): lv: R.Tensor((8,), dtype="int64") = R.reshape(x, R.shape([8])) lv1: R.Tensor((8,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(8), R.prim_value(1), dtype="int64" ) lv2: R.Tensor((8,), dtype="int64") = R.add(lv1, R.const(7, "int64")) lv3: R.Tensor((8,), dtype="int64") = R.mod(lv2, R.const(8, "int64")) lv4: R.Tensor((8,), dtype="int64") = R.take(lv, lv3, axis=0, mode="fast") lv5: R.Tensor((4, 2), dtype="int64") = R.reshape(lv4, R.shape([4, 2])) gv: R.Tuple(R.Tensor((4, 2), dtype="int64")) = (lv5,) R.output(gv) return gv # Test case 2: torch.roll(x, -1, 0) @I.ir_module class Expected2: @R.function def main(x: R.Tensor((4, 2), dtype="int64")) -> R.Tuple(R.Tensor((4, 2), dtype="int64")): with R.dataflow(): lv: R.Tensor((4,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(4), R.prim_value(1), dtype="int64" ) lv1: R.Tensor((4,), dtype="int64") = R.add(lv, R.const(1, "int64")) lv2: R.Tensor((4,), dtype="int64") = R.mod(lv1, R.const(4, "int64")) lv3: R.Tensor((4, 2), dtype="int64") = R.take(x, lv2, axis=0, mode="fast") gv: R.Tuple(R.Tensor((4, 2), dtype="int64")) = (lv3,) R.output(gv) return gv # Test case 3: torch.roll(x, shifts=(2,1), dims=(0,1)) @I.ir_module class Expected3: @R.function def main(x: R.Tensor((4, 2), dtype="int64")) -> R.Tuple(R.Tensor((4, 2), dtype="int64")): with R.dataflow(): # First roll along dim=0 with shift=2 lv: R.Tensor((4,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(4), R.prim_value(1), dtype="int64" ) lv1: R.Tensor((4,), dtype="int64") = R.add(lv, R.const(2, "int64")) lv2: R.Tensor((4,), dtype="int64") = R.mod(lv1, R.const(4, "int64")) lv3: R.Tensor((4, 2), dtype="int64") = R.take(x, lv2, axis=0, mode="fast") # Second roll along dim=1 with shift=1 lv4: R.Tensor((2,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(2), R.prim_value(1), dtype="int64" ) lv5: R.Tensor((2,), dtype="int64") = R.add(lv4, R.const(1, "int64")) lv6: R.Tensor((2,), dtype="int64") = R.mod(lv5, R.const(2, "int64")) lv7: R.Tensor((4, 2), dtype="int64") = R.take(lv3, lv6, axis=1, mode="fast") gv: R.Tuple(R.Tensor((4, 2), dtype="int64")) = (lv7,) R.output(gv) return gv # Test inputs example_input = torch.randint(0, 10, (4, 2), dtype=torch.int64) # Run verification for each case verify_model(Roll1(), (example_input,), {}, Expected1) verify_model(Roll2(), (example_input,), {}, Expected2) verify_model(Roll3(), (example_input,), {}, Expected3) def test_select_slice(): class Slice1(Module): def forward(self, x): return x[0, 1::2, :, :3] @tvm.script.ir_module class expected1: @R.function def main(x: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 10, 3), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((3, 10, 10), dtype="float32") = R.take( x, R.const(0, "int64"), axis=0, mode="fast" ) lv1: R.Tensor((1, 10, 10), dtype="float32") = R.strided_slice( lv, (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(9223372036854775807),), (R.prim_value(2),), assume_inbound=False, ) lv2: R.Tensor((1, 10, 3), dtype="float32") = R.strided_slice( lv1, (R.prim_value(2),), (R.prim_value(0),), (R.prim_value(3),), (R.prim_value(1),), assume_inbound=False, ) gv: R.Tuple(R.Tensor((1, 10, 3), dtype="float32")) = (lv2,) R.output(gv) return gv class Slice2(Module): def forward(self, x): return x[:, None, None, :, None] @I.ir_module class expected2: @R.function def main(x: R.Tensor((8, 16), dtype="float32")) -> R.Tuple( R.Tensor((8, 1, 1, 16, 1), dtype="float32") ): with R.dataflow(): lv: R.Tensor((8, 1, 16), dtype="float32") = R.expand_dims(x, axis=[1]) lv1: R.Tensor((8, 1, 1, 16), dtype="float32") = R.expand_dims(lv, axis=[2]) lv2: R.Tensor((8, 1, 1, 16, 1), dtype="float32") = R.expand_dims(lv1, axis=[4]) gv: R.Tuple(R.Tensor((8, 1, 1, 16, 1), dtype="float32")) = (lv2,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(Slice1(), example_args, {}, expected1) example_args = (torch.randn(8, 16, dtype=torch.float32),) verify_model(Slice2(), example_args, {}, expected2) def test_slice_scatter(): class SliceScatter1(Module): def forward(self, input, src): return torch.slice_scatter(input, src, dim=1, start=1, end=7, step=2) @tvm.script.ir_module class expected1: @R.function def main( a: R.Tensor((8, 8, 10, 10), dtype="float32"), b: R.Tensor((8, 3, 10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((8, 8, 10, 10), dtype="float32")): with R.dataflow(): lv: R.Tensor((8, 8, 10, 10), dtype="float32") = R.slice_scatter( a, b, R.prim_value(1), R.prim_value(7), R.prim_value(2), axis=1 ) gv: R.Tuple(R.Tensor((8, 8, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv class SliceScatter2(Module): def forward(self, input, src): return torch.slice_scatter(input, src, dim=0, start=0, end=6, step=1) @I.ir_module class expected2: @R.function def main( a: R.Tensor((8, 16), dtype="float32"), b: R.Tensor((6, 16), dtype="float32") ) -> R.Tuple(R.Tensor((8, 16), dtype="float32")): with R.dataflow(): lv: R.Tensor((8, 16), dtype="float32") = R.slice_scatter( a, b, R.prim_value(0), R.prim_value(6), R.prim_value(1), axis=0 ) gv: R.Tuple(R.Tensor((8, 16), dtype="float32")) = (lv,) R.output(gv) return gv class SliceScatterNegative(Module): def forward(self, input, src): return torch.slice_scatter(input, src, dim=1, start=0, end=-2, step=1) @tvm.script.ir_module class expected_slice_scatter: @R.function def main( a: R.Tensor((2, 5), dtype="float32"), b: R.Tensor((2, 3), dtype="float32") ) -> R.Tuple(R.Tensor((2, 5), dtype="float32")): with R.dataflow(): lv: R.Tensor((2, 5), dtype="float32") = R.slice_scatter( a, b, R.prim_value(0), R.prim_value(3), R.prim_value(1), axis=1 ) gv: R.Tuple(R.Tensor((2, 5), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(8, 8, 10, 10, dtype=torch.float32), torch.randn(8, 3, 10, 10)) verify_model(SliceScatter1(), example_args, {}, expected1) example_args = (torch.randn(8, 16, dtype=torch.float32), torch.randn(6, 16)) verify_model(SliceScatter2(), example_args, {}, expected2) example_args = (torch.randn(2, 5, dtype=torch.float32), torch.randn(2, 3, dtype=torch.float32)) verify_model(SliceScatterNegative(), example_args, {}, expected_slice_scatter) def test_slice_with_symbolic_end(): """_slice correctly handles symbolic end values from dynamic shapes.""" class SliceIdentityModel(torch.nn.Module): def forward(self, x): # x[:, :x.size(1)] is an identity slice that torch.export emits # as slice(x, 1, 0, sym_size_int(x, 1), 1) with dynamic shapes. seq_len = x.size(1) return x[:, :seq_len] + 0.0 # +0.0 to ensure output is a new tensor # The identity slice is elided; only x + 0.0 remains. @I.ir_module class ExpectedIdentity: @R.function def main(x: R.Tensor(("s0", "s1", 4), dtype="float32")) -> R.Tuple( R.Tensor(("s0", "s1", 4), dtype="float32") ): s0 = T.int64() s1 = T.int64() R.func_attr({"tir_var_lower_bound": {"s27": 2, "s77": 2}}) with R.dataflow(): lv: R.Tensor((s0, s1, 4), dtype="float32") = R.add(x, R.const(0.0, "float32")) gv: R.Tuple(R.Tensor((s0, s1, 4), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(2, 8, 4, dtype=torch.float32),) batch = torch.export.Dim("batch", min=2) seq = torch.export.Dim("seq", min=2) dynamic_shapes = {"x": {0: batch, 1: seq}} verify_model( SliceIdentityModel(), example_args, {}, ExpectedIdentity, dynamic_shapes=dynamic_shapes, map_free_vars=True, ) class SliceStaticModel(torch.nn.Module): def forward(self, x): # A non-identity static slice return x[:, :3] @tvm.script.ir_module class ExpectedStatic: @R.function def main(x: R.Tensor((2, 8, 4), dtype="float32")) -> R.Tuple( R.Tensor((2, 3, 4), dtype="float32") ): with R.dataflow(): lv: R.Tensor((2, 3, 4), dtype="float32") = R.strided_slice( x, axes=[1], begin=[0], end=[3], strides=[1], ) gv: R.Tuple(R.Tensor((2, 3, 4), dtype="float32")) = (lv,) R.output(gv) return gv example_args_static = (torch.randn(2, 8, 4, dtype=torch.float32),) verify_model(SliceStaticModel(), example_args_static, {}, ExpectedStatic) def test_split(): class Chunk(Module): def forward(self, input): return torch.chunk(input, 3, dim=1) @tvm.script.ir_module class Expected: @R.function def main(input: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 1, 10, 10), dtype="float32"), R.Tensor((1, 1, 10, 10), dtype="float32"), R.Tensor((1, 1, 10, 10), dtype="float32"), ): # block 0 with R.dataflow(): lv: R.Tuple( R.Tensor((1, 1, 10, 10), dtype="float32"), R.Tensor((1, 1, 10, 10), dtype="float32"), R.Tensor((1, 1, 10, 10), dtype="float32"), ) = R.split(input, indices_or_sections=[1, 2], axis=1) lv1: R.Tensor((1, 1, 10, 10), dtype="float32") = lv[0] lv2: R.Tensor((1, 1, 10, 10), dtype="float32") = lv[1] lv3: R.Tensor((1, 1, 10, 10), dtype="float32") = lv[2] gv: R.Tuple( R.Tensor((1, 1, 10, 10), dtype="float32"), R.Tensor((1, 1, 10, 10), dtype="float32"), R.Tensor((1, 1, 10, 10), dtype="float32"), ) = (lv1, lv2, lv3) R.output(gv) return gv class Unbind1(Module): def forward(self, data): return torch.unbind(data) @tvm.script.ir_module class expected1: @R.function def main(data: R.Tensor((3, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((3, 10, 10), dtype="float32"), R.Tensor((3, 10, 10), dtype="float32"), R.Tensor((3, 10, 10), dtype="float32"), ): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10), dtype="float32") = R.strided_slice( data, (R.prim_value(0),), (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(1),), assume_inbound=False, ) lv1: R.Tensor((1, 3, 10, 10), dtype="float32") = R.strided_slice( data, (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(2),), (R.prim_value(1),), assume_inbound=False, ) lv2: R.Tensor((1, 3, 10, 10), dtype="float32") = R.strided_slice( data, (R.prim_value(0),), (R.prim_value(2),), (R.prim_value(3),), (R.prim_value(1),), assume_inbound=False, ) lv3: R.Tensor((3, 10, 10), dtype="float32") = R.squeeze(lv, axis=[0]) lv4: R.Tensor((3, 10, 10), dtype="float32") = R.squeeze(lv1, axis=[0]) lv5: R.Tensor((3, 10, 10), dtype="float32") = R.squeeze(lv2, axis=[0]) gv: R.Tuple( R.Tensor((3, 10, 10), dtype="float32"), R.Tensor((3, 10, 10), dtype="float32"), R.Tensor((3, 10, 10), dtype="float32"), ) = (lv3, lv4, lv5) R.output(gv) return gv class Unbind2(Module): def forward(self, data): return torch.unbind(data, dim=1) @tvm.script.ir_module class expected2: @R.function def main(data: R.Tensor((3, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((3, 10, 10), dtype="float32"), R.Tensor((3, 10, 10), dtype="float32"), R.Tensor((3, 10, 10), dtype="float32"), ): # block 0 with R.dataflow(): lv: R.Tensor((3, 1, 10, 10), dtype="float32") = R.strided_slice( data, (R.prim_value(1),), (R.prim_value(0),), (R.prim_value(1),), (R.prim_value(1),), assume_inbound=False, ) lv1: R.Tensor((3, 1, 10, 10), dtype="float32") = R.strided_slice( data, (R.prim_value(1),), (R.prim_value(1),), (R.prim_value(2),), (R.prim_value(1),), assume_inbound=False, ) lv2: R.Tensor((3, 1, 10, 10), dtype="float32") = R.strided_slice( data, (R.prim_value(1),), (R.prim_value(2),), (R.prim_value(3),), (R.prim_value(1),), assume_inbound=False, ) lv3: R.Tensor((3, 10, 10), dtype="float32") = R.squeeze(lv, axis=[1]) lv4: R.Tensor((3, 10, 10), dtype="float32") = R.squeeze(lv1, axis=[1]) lv5: R.Tensor((3, 10, 10), dtype="float32") = R.squeeze(lv2, axis=[1]) gv: R.Tuple( R.Tensor((3, 10, 10), dtype="float32"), R.Tensor((3, 10, 10), dtype="float32"), R.Tensor((3, 10, 10), dtype="float32"), ) = (lv3, lv4, lv5) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(Chunk(), example_args, {}, Expected) example_args = (torch.randn(3, 3, 10, 10, dtype=torch.float32),) verify_model(Unbind1(), example_args, {}, expected1) verify_model(Unbind2(), example_args, {}, expected2) def test_squeeze(): class Squeeze1(Module): def forward(self, input): return input.squeeze(1) @tvm.script.ir_module class Expected1: @R.function def main(inp_0: R.Tensor((3, 1, 4, 1), dtype="float32")) -> R.Tuple( R.Tensor((3, 4, 1), dtype="float32") ): with R.dataflow(): lv: R.Tensor((3, 4, 1), dtype="float32") = R.squeeze(inp_0, axis=[1]) gv: R.Tuple(R.Tensor((3, 4, 1), dtype="float32")) = (lv,) R.output(gv) return gv class Squeeze2(Module): def forward(self, input): return input.squeeze() @tvm.script.ir_module class Expected2: @R.function def main(input: R.Tensor((3, 1, 4, 1), dtype="float32")) -> R.Tuple( R.Tensor((3, 4), dtype="float32") ): with R.dataflow(): lv: R.Tensor((3, 4), dtype="float32") = R.squeeze(input, axis=[0, 1, 2, 3]) gv: R.Tuple(R.Tensor((3, 4), dtype="float32")) = (lv,) R.output(gv) return gv class Squeeze3(Module): def forward(self, input): return input.squeeze(2) @I.ir_module class Expected3: @R.function def main(inp_0: R.Tensor((3, 1, 4, 1), dtype="float32")) -> R.Tuple( R.Tensor((3, 1, 4, 1), dtype="float32") ): with R.dataflow(): lv: R.Tensor((3, 1, 4, 1), dtype="float32") = R.squeeze(inp_0, axis=[2]) gv: R.Tuple(R.Tensor((3, 1, 4, 1), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(3, 1, 4, 1, dtype=torch.float32),) verify_model(Squeeze1(), example_args, {}, Expected1) verify_model(Squeeze2(), example_args, {}, Expected2) verify_model(Squeeze3(), example_args, {}, Expected3) def test_stack(): class Stack0(Module): def forward(self, x, y): return torch.stack((x, y)) # default dim=0 class Stack1(Module): def forward(self, x, y): return torch.stack((x, y), dim=1) class Stack2(Module): def forward(self, x, y): return torch.stack((x, y), 1) # positional dim class Stack3(Module): def forward(self, x, y): return torch.stack((x, y), dim=-1) # negative dim @I.ir_module class Expected0: @R.function def main( x: R.Tensor((2, 3), dtype="float32"), y: R.Tensor((2, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((2, 2, 3), dtype="float32")): with R.dataflow(): lv: R.Tensor((4, 3), dtype="float32") = R.concat((x, y), axis=0) lv1: R.Tensor((2, 2, 3), dtype="float32") = R.reshape(lv, R.shape([2, 2, 3])) gv: R.Tuple(R.Tensor((2, 2, 3), dtype="float32")) = (lv1,) R.output(gv) return gv @I.ir_module class Expected1: @R.function def main( x: R.Tensor((2, 3), dtype="float32"), y: R.Tensor((2, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((2, 2, 3), dtype="float32")): with R.dataflow(): lv: R.Tensor((2, 6), dtype="float32") = R.concat((x, y), axis=1) lv1: R.Tensor((2, 2, 3), dtype="float32") = R.reshape(lv, R.shape([2, 2, 3])) gv: R.Tuple(R.Tensor((2, 2, 3), dtype="float32")) = (lv1,) R.output(gv) return gv @I.ir_module class Expected3: @R.function def main( x: R.Tensor((2, 3), dtype="float32"), y: R.Tensor((2, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((2, 3, 2), dtype="float32")): with R.dataflow(): lv: R.Tensor((2, 3, 1), dtype="float32") = R.expand_dims(x, axis=[2]) lv1: R.Tensor((2, 3, 1), dtype="float32") = R.expand_dims(y, axis=[2]) lv2: R.Tensor((2, 3, 2), dtype="float32") = R.concat((lv, lv1), axis=-1) gv: R.Tuple(R.Tensor((2, 3, 2), dtype="float32")) = (lv2,) R.output(gv) return gv example_args = (torch.randn(2, 3, dtype=torch.float32), torch.randn(2, 3, dtype=torch.float32)) verify_model(Stack0(), example_args, {}, Expected0) verify_model(Stack1(), example_args, {}, Expected1) verify_model(Stack2(), example_args, {}, Expected1) verify_model(Stack3(), example_args, {}, Expected3) def test_tile(): class Tile1(Module): def forward(self, x): return x.tile((2,)) class Tile2(Module): def forward(self, x): return x.tile(4, 2) class Tile3(Module): def forward(self, x): return torch.tile(x, (4, 2)) @tvm.script.ir_module class expected1: @R.function def main(x: R.Tensor((1, 3), dtype="float32")) -> R.Tuple( R.Tensor((1, 6), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 6), dtype="float32") = R.tile(x, repeats=[1, 2]) gv: R.Tuple(R.Tensor((1, 6), dtype="float32")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class expected2: @R.function def main(x: R.Tensor((1, 3), dtype="float32")) -> R.Tuple( R.Tensor((4, 6), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((4, 6), dtype="float32") = R.tile(x, repeats=[4, 2]) gv: R.Tuple(R.Tensor((4, 6), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 3, dtype=torch.float32),) verify_model(Tile1(), example_args, {}, expected1) verify_model(Tile2(), example_args, {}, expected2) verify_model(Tile3(), example_args, {}, expected2) def test_transpose(): class Transpose(Module): def forward(self, x): return x.transpose(1, 3) @tvm.script.ir_module class expected1: @R.function def main(x: R.Tensor((1, 2, 3, 4), dtype="float32")) -> R.Tuple( R.Tensor((1, 4, 3, 2), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 4, 3, 2), dtype="float32") = R.permute_dims(x, axes=[0, 3, 2, 1]) gv: R.Tuple(R.Tensor((1, 4, 3, 2), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 2, 3, 4, dtype=torch.float32),) verify_model(Transpose(), example_args, {}, expected1) def test_unsqueeze(): class Unsqueeze1(Module): def forward(self, input): return input.unsqueeze(1) @tvm.script.ir_module class expected1: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 1, 3, 10, 10), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 1, 3, 10, 10), dtype="float32") = R.expand_dims(input_1, 1) gv: R.Tuple(R.Tensor((1, 1, 3, 10, 10), dtype="float32")) = (lv,) R.output(gv) return gv class Unsqueeze2(Module): def forward(self, input): return input.unsqueeze(-1) @tvm.script.ir_module class expected2: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 10, 10, 1), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 3, 10, 10, 1), dtype="float32") = R.expand_dims(input_1, -1) gv: R.Tuple(R.Tensor((1, 3, 10, 10, 1), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) verify_model(Unsqueeze1(), example_args, {}, expected1) verify_model(Unsqueeze2(), example_args, {}, expected2) def test_view(): class View(Module): def forward(self, x): return x.view(2, 12) @tvm.script.ir_module class expected1: @R.function def main(x: R.Tensor((1, 2, 3, 4), dtype="float32")) -> R.Tuple( R.Tensor((2, 12), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((2, 12), dtype="float32") = R.reshape(x, (2, 12)) gv: R.Tuple(R.Tensor((2, 12), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 2, 3, 4, dtype=torch.float32),) verify_model(View(), example_args, {}, expected1) def test_as_strided(): class AsStrided(Module): def forward(self, x): return torch.ops.aten.as_strided.default(x, (3, 2, 2), (4, 2, 1)) @tvm.script.ir_module class Expected: @R.function def main(x: R.Tensor((2, 2, 3), dtype="float32")) -> R.Tuple( R.Tensor((3, 2, 2), dtype="float32") ): with R.dataflow(): lv: R.Tensor((3, 2, 2), dtype="float32") = R.reshape(x, (3, 2, 2)) gv: R.Tuple(R.Tensor((3, 2, 2), dtype="float32")) = (lv,) R.output(gv) return gv class AsStridedNonContiguous(Module): def forward(self, x): return torch.ops.aten.as_strided.default(x, (2, 2, 2), (6, 3, 1)) class AsStridedWithStorageOffset(Module): def forward(self, x): return torch.ops.aten.as_strided.default(x, (2, 2), (2, 1), 1) example_args = (torch.randn(2, 2, 3, dtype=torch.float32),) verify_model(AsStrided(), example_args, {}, Expected) exported = export(AsStridedNonContiguous(), args=example_args) with pytest.raises(AssertionError, match="non-contiguous stride"): from_exported_program(exported) example_args = (torch.randn(2, 2, dtype=torch.float32),) exported = export(AsStridedWithStorageOffset(), args=example_args) with pytest.raises(AssertionError, match="storage_offset"): from_exported_program(exported) def test_arange(): class Arange(Module): def forward(self, input): return torch.arange(0, 20, dtype=torch.int32) @tvm.script.ir_module class Expected: @R.function def main(input: R.Tensor((10, 10), dtype="float32")) -> R.Tuple( R.Tensor((20,), dtype="int32") ): with R.dataflow(): lv: R.Tensor((20,), dtype="int32") = R.arange(0, 20, 1, dtype="int32") gv: R.Tuple(R.Tensor((20,), dtype="int32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(10, 10, dtype=torch.float32),) verify_model(Arange(), example_args, {}, Expected) def test_hamming_window(): class HammingWindow(Module): def forward(self, input): return torch.hamming_window(20, True, dtype=torch.float32) @tvm.script.ir_module class Expected: @R.function def main(input: R.Tensor((10, 10), dtype="float32")) -> R.Tuple( R.Tensor((20,), dtype="float32") ): with R.dataflow(): lv: R.Tensor((20,), dtype="float32") = R.hamming_window( R.prim_value(20), R.prim_value(True), R.prim_value(T.float64(0.54000000000000004)), R.prim_value(T.float64(0.46000000000000002)), dtype="float32", ) gv: R.Tuple(R.Tensor((20,), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(10, 10, dtype=torch.float32),) verify_model(HammingWindow(), example_args, {}, Expected) def test_contiguous(): class Contiguous(Module): def forward(self, input): return input.contiguous() @tvm.script.ir_module class Expected: @R.function def main( input: R.Tensor((10, 10), dtype="float32"), ) -> R.Tuple(R.Tensor((10, 10), dtype="float32")): with R.dataflow(): gv: R.Tuple(R.Tensor((10, 10), dtype="float32")) = (input,) R.output(gv) return gv example_args = (torch.randn(10, 10, dtype=torch.float32),) verify_model(Contiguous(), example_args, {}, Expected) def test_clone(): class Clone(Module): def forward(self, input): return torch.clone(input) @tvm.script.ir_module class Expected: @R.function def main(input: R.Tensor((10, 10), dtype="float32")) -> R.Tuple( R.Tensor((10, 10), dtype="float32") ): with R.dataflow(): gv: R.Tuple(R.Tensor((10, 10), dtype="float32")) = (input,) R.output(gv) return gv example_args = (torch.randn(10, 10, dtype=torch.float32),) verify_model(Clone(), example_args, {}, Expected) def test_empty(): class Empty(Module): def forward(self, input): return torch.empty((10, 10), dtype=torch.float32) @tvm.script.ir_module class Expected: @R.function def main(input: R.Tensor((10, 10), dtype="float32")) -> R.Tuple( R.Tensor((10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((10, 10), dtype="float32") = R.zeros( R.shape([10, 10]), dtype="float32" ) gv: R.Tuple(R.Tensor((10, 10), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(10, 10, dtype=torch.float32),) verify_model(Empty(), example_args, {}, Expected) def test_empty_without_dtype(): class EmptyWithoutDtype(Module): def forward(self, input): return torch.empty((5, 5)) @tvm.script.ir_module class Expected: @R.function def main(input: R.Tensor((10, 10), dtype="float32")) -> R.Tuple( R.Tensor((5, 5), dtype="float32") ): with R.dataflow(): lv: R.Tensor((5, 5), dtype="float32") = R.zeros(R.shape([5, 5]), dtype="float32") gv: R.Tuple(R.Tensor((5, 5), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(10, 10, dtype=torch.float32),) verify_model(EmptyWithoutDtype(), example_args, {}, Expected) def test_fill(): class Fill(Module): def forward(self, input: torch.Tensor): return torch.fill(input, 1.5) @tvm.script.ir_module class Expected: @R.function def main(input: R.Tensor((10, 10), dtype="float32")) -> R.Tuple( R.Tensor((10, 10), dtype="float32") ): with R.dataflow(): lv: R.Tensor((10, 10), dtype="float32") = R.full_like( input, R.const(1.5, "float32") ) gv: R.Tuple(R.Tensor((10, 10), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(10, 10, dtype=torch.float32),) verify_model(Fill(), example_args, {}, Expected) def test_fill_inplace(): class FillInplace(Module): def forward(self, input: torch.Tensor): input.fill_(42.0) return input @tvm.script.ir_module class Expected: @R.function def main(input: R.Tensor((2, 3), dtype="float32")) -> R.Tuple( R.Tensor((2, 3), dtype="float32") ): with R.dataflow(): lv: R.Tensor((2, 3), dtype="float32") = R.full_like(input, R.const(42.0, "float32")) gv: R.Tuple(R.Tensor((2, 3), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(2, 3, dtype=torch.float32),) verify_model(FillInplace(), example_args, {}, Expected) def test_masked_fill(): class Masked_Fill(Module): def forward(self, input: torch.Tensor, mask: torch.Tensor): return torch.masked_fill(input, mask, 0) @tvm.script.ir_module class Expected: @R.function def main( input: R.Tensor((128, 128), dtype="float32"), mask: R.Tensor((128, 128), dtype="bool") ) -> R.Tuple(R.Tensor((128, 128), dtype="float32")): with R.dataflow(): lv: R.Tensor((), dtype="float32") = R.const(0.0, "float32") lv1: R.Tensor((128, 128), dtype="float32") = R.where(mask, lv, input) gv: R.Tuple(R.Tensor((128, 128), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = ( torch.randn(128, 128, dtype=torch.float32), torch.testing.make_tensor((128, 128), dtype=torch.bool, device="cpu"), ) verify_model(Masked_Fill(), example_args, {}, Expected) def test_masked_fill_inplace(): class Masked_Fill_Inplace(Module): def forward(self, input: torch.Tensor, mask: torch.Tensor): return input.masked_fill_(mask, 1.5) @tvm.script.ir_module class Expected: @R.function def main( input: R.Tensor((128, 128), dtype="float32"), mask: R.Tensor((128, 128), dtype="bool") ) -> R.Tuple(R.Tensor((128, 128), dtype="float32")): with R.dataflow(): lv: R.Tensor((), dtype="float32") = R.const(1.5, "float32") lv1: R.Tensor((128, 128), dtype="float32") = R.where(mask, lv, input) gv: R.Tuple(R.Tensor((128, 128), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = ( torch.randn(128, 128, dtype=torch.float32), torch.testing.make_tensor((128, 128), dtype=torch.bool, device="cpu"), ) verify_model(Masked_Fill_Inplace(), example_args, {}, Expected) def test_masked_select(): class MaskedSelect(Module): def forward(self, data: torch.Tensor, mask: torch.Tensor): return torch.masked_select(data, mask) @tvm.script.ir_module class Expected: @R.function def main( data: R.Tensor((2, 3), dtype="float32"), mask: R.Tensor((2, 3), dtype="bool") ) -> R.Tuple(R.Tensor(dtype="float32", ndim=1)): R.func_attr({"tir_var_lower_bound": {"u0": 0}, "tir_var_upper_bound": {"u0": 6}}) u0 = T.int64() with R.dataflow(): lv: R.Tensor((6,), dtype="float32") = R.reshape(data, R.shape([6])) lv1: R.Tensor((6,), dtype="bool") = R.reshape(mask, R.shape([6])) lv2: R.Tensor(dtype="int64", ndim=2) = R.nonzero(lv1) lv3: R.Tensor((1, u0), dtype="int64") = R.match_cast( lv2, R.Tensor((1, u0), dtype="int64") ) lv4: R.Tensor((u0,), dtype="int64") = R.squeeze(lv3, axis=[0]) lv5: R.Tensor((u0,), dtype="float32") = R.take(lv, lv4, axis=0, mode="fast") lv6: R.Tensor((), dtype="bool") = R.const(True, "bool") lv7: R.Tensor((), dtype="bool") = R.const(True, "bool") gv: R.Tuple(R.Tensor((u0,), dtype="float32")) = (lv5,) R.output(gv) return gv example_args = ( torch.randn(2, 3, dtype=torch.float32), torch.tensor([[True, False, True], [False, True, False]]), ) verify_model(MaskedSelect(), example_args, {}, Expected) @pytest.mark.skipif(not tvm.testing.device_enabled("llvm"), reason="llvm not enabled") def test_masked_select_numerically(): class MaskedSelect(Module): def forward(self, data: torch.Tensor, mask: torch.Tensor): return torch.masked_select(data, mask) example_args = ( torch.tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.float32), torch.tensor([[True, False, True], [False, True, False]]), ) verify_model_numerically(MaskedSelect(), example_args) def test_new_ones(): class NewOnes(Module): def forward(self, x): return x.new_ones(1, 2, 3) @tvm.script.ir_module class expected1: @R.function def main(x: R.Tensor((1, 2, 3), dtype="float32")) -> R.Tuple( R.Tensor((1, 2, 3), dtype="float32") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 2, 3), dtype="float32") = R.full( (1, 2, 3), R.const(1, "float32"), dtype="float32" ) gv: R.Tuple(R.Tensor((1, 2, 3), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 2, 3, dtype=torch.float32),) verify_model(NewOnes(), example_args, {}, expected1) def test_new_zeros(): class NewZeros(torch.nn.Module): def forward(self, x): return x.new_zeros(1, 128, 128) @tvm.script.ir_module class expected1: @R.function def main(x: R.Tensor((1, 128, 128), dtype="float32")) -> R.Tuple( R.Tensor((1, 128, 128), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 128, 128), dtype="float32") = R.full( R.shape([1, 128, 128]), R.const(0, "float32"), dtype="float32" ) gv: R.Tuple(R.Tensor((1, 128, 128), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, 128, 128, dtype=torch.float32),) verify_model(NewZeros(), example_args, {}, expected1) def test_copy(): class CopyBroadcast(Module): def forward(self, x, src): x.copy_(src) return x @tvm.script.ir_module class expected_copy: @R.function def main(x: R.Tensor((2, 3), dtype="float32"), src: R.Tensor((), dtype="int64")) -> R.Tuple( R.Tensor((2, 3), dtype="float32") ): with R.dataflow(): lv: R.Tensor((), dtype="float32") = R.astype(src, dtype="float32") lv1: R.Tensor((2, 3), dtype="float32") = R.broadcast_to(lv, (2, 3)) gv: R.Tuple(R.Tensor((2, 3), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = (torch.zeros(2, 3, dtype=torch.float32), torch.tensor(1, dtype=torch.int64)) verify_model(CopyBroadcast(), example_args, {}, expected_copy) def test_to_copy(): # float class ToFloat(Module): def forward(self, x): return x.float() @tvm.script.ir_module class expected_float: @R.function def main(x: R.Tensor((1, 2, 3, 4), dtype="float32")) -> R.Tuple( R.Tensor((1, 2, 3, 4), dtype="float32") ): # block 0 with R.dataflow(): gv: R.Tuple(R.Tensor((1, 2, 3, 4), dtype="float32")) = (x,) R.output(gv) return gv # half class ToHalf(Module): def forward(self, x): return x.half() @tvm.script.ir_module class expected_half: @R.function def main(x: R.Tensor((1, 2, 3, 4), dtype="float32")) -> R.Tuple( R.Tensor((1, 2, 3, 4), dtype="float16") ): # block 0 with R.dataflow(): lv: R.Tensor((1, 2, 3, 4), dtype="float16") = R.astype(x, dtype="float16") gv: R.Tuple(R.Tensor((1, 2, 3, 4), dtype="float16")) = (lv,) R.output(gv) return gv # type class Type(Module): def forward(self, x): return x.type(torch.float32) @tvm.script.ir_module class expected_type: @R.function def main(x: R.Tensor((1, 2, 3, 4), dtype="float32")) -> R.Tuple( R.Tensor((1, 2, 3, 4), dtype="float32") ): # block 0 with R.dataflow(): gv: R.Tuple(R.Tensor((1, 2, 3, 4), dtype="float32")) = (x,) R.output(gv) return gv class To1(Module): def forward(self, input): return input.to(torch.float16) @I.ir_module class expected_to1: @R.function def main(input: R.Tensor((1, 2, 3, 4), dtype="float32")) -> R.Tuple( R.Tensor((1, 2, 3, 4), dtype="float16") ): with R.dataflow(): lv: R.Tensor((1, 2, 3, 4), dtype="float16") = R.astype(input, dtype="float16") gv: R.Tuple(R.Tensor((1, 2, 3, 4), dtype="float16")) = (lv,) R.output(gv) return gv class To2(Module): def forward(self, input): return input.to("cpu") @I.ir_module class expected_to2: @R.function def main(input: R.Tensor((1, 2, 3, 4), dtype="float32")) -> R.Tuple( R.Tensor((1, 2, 3, 4), dtype="float32") ): with R.dataflow(): gv: R.Tuple(R.Tensor((1, 2, 3, 4), dtype="float32")) = (input,) R.output(gv) return gv example_args = (torch.randn(1, 2, 3, 4, dtype=torch.float32),) verify_model(ToFloat(), example_args, {}, expected_float) verify_model(ToHalf(), example_args, {}, expected_half) verify_model(Type(), example_args, {}, expected_type) verify_model(To1(), example_args, {}, expected_to1) verify_model(To2(), example_args, {}, expected_to2) def test_keep_params(): class Conv2D1(Module): def __init__(self): super().__init__() self.conv = torch.nn.Conv2d(3, 6, 7, bias=True) def forward(self, input): return self.conv(input) @tvm.script.ir_module class expected1: @R.function def main( input_1: R.Tensor((1, 3, 10, 10), dtype="float32"), conv_weight: R.Tensor((6, 3, 7, 7), dtype="float32"), conv_bias: R.Tensor((6,), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 6, 4, 4), dtype="float32")): R.func_attr({"num_input": 1}) # block 0 with R.dataflow(): lv1: R.Tensor((1, 6, 4, 4), dtype="float32") = R.nn.conv2d( input_1, conv_weight, strides=[1, 1], padding=[0, 0, 0, 0], dilation=[1, 1], data_layout="NCHW", kernel_layout="OIHW", out_layout="NCHW", out_dtype="float32", ) lv2: R.Tensor((1, 6, 1, 1), dtype="float32") = R.reshape(conv_bias, [1, 6, 1, 1]) lv3: R.Tensor((1, 6, 4, 4), dtype="float32") = R.add(lv1, lv2) gv: R.Tuple(R.Tensor((1, 6, 4, 4), dtype="float32")) = (lv3,) R.output(gv) return gv from tvm.relax.frontend import detach_params example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) model = Conv2D1() exported_program = torch.export.export(model, example_args) mod = from_exported_program(exported_program, keep_params_as_input=True) mod, params = detach_params(mod) tvm.ir.assert_structural_equal(mod, expected1) func = mod["main"] params = params["main"] assert len(params) == len(func.params) - 1 for param_var, param_tensor in zip(func.params[1:], params): assert tuple(x.value for x in param_var.ty.shape.values) == param_tensor.shape assert param_var.ty.dtype == param_tensor.dtype tvm.testing.assert_allclose(params[0].numpy(), model.conv.weight.detach().detach().numpy()) tvm.testing.assert_allclose(params[1].numpy(), model.conv.bias.detach().detach().numpy()) def test_unwrap_unit_return_tuple(): class Identity(Module): def __init__(self): super().__init__() def forward(self, x): return (x,) @tvm.script.ir_module class Expected: @R.function def main(inp_0: R.Tensor((256, 256), dtype="float32")) -> R.Tensor( (256, 256), dtype="float32" ): with R.dataflow(): gv: R.Tensor((256, 256), dtype="float32") = inp_0 R.output(gv) return gv example_args = (torch.randn(256, 256, dtype=torch.float32),) verify_model(Identity(), example_args, {}, Expected, unwrap_unit_return_tuple=True) def test_no_bind_return_tuple(): class Identity(Module): def __init__(self): super().__init__() def forward(self, x, y): return (x, y) @tvm.script.ir_module class Expected: @R.function def main( inp_0: R.Tensor((256, 256), dtype="float32"), inp_1: R.Tensor((256, 256), dtype="float32"), ) -> R.Tuple(R.Tensor((256, 256), dtype="float32"), R.Tensor((256, 256), dtype="float32")): with R.dataflow(): gv: R.Tensor((256, 256), dtype="float32") = inp_0 gv1: R.Tensor((256, 256), dtype="float32") = inp_1 R.output(gv, gv1) return (gv, gv1) example_args = ( torch.randn(256, 256, dtype=torch.float32), torch.randn(256, 256, dtype=torch.float32), ) verify_model(Identity(), example_args, {}, Expected, no_bind_return_tuple=True) def test_register_buffer(): class ModelWithBuffer(torch.nn.Module): def __init__(self): super().__init__() self.register_buffer("my_buffer", torch.randn(3, 4), persistent=False) def forward(self, x): return x + self.my_buffer example_args = (torch.randn(2, 3, 4),) ep = export(ModelWithBuffer(), args=example_args) # Just verify that import works. from_exported_program(ep) def test_custom_op(): class AddOp(Module): def forward(self, x, y): return torch.ops.aten.add.Tensor(x, y) @tvm.script.ir_module class Expected: @R.function def main( x: R.Tensor((5,), dtype="float32"), y: R.Tensor((5,), dtype="float32"), ) -> R.Tuple(R.Tensor((5,), dtype="float32")): with R.dataflow(): lv: R.Tensor((5,), dtype="float32") = R.subtract(x, y) gv: R.Tuple(R.Tensor((5,), dtype="float32")) = (lv,) R.output(gv) return gv from tvm.relax.frontend.torch.exported_program_translator import ( ExportedProgramImporter, ) def custom_add_converter(node: torch.fx.Node, self: ExportedProgramImporter) -> relax.Var: x = self.env[node.args[0]] y = self.env[node.args[1]] return self.block_builder.emit(R.subtract(x, y)) example_args = (torch.randn(5, dtype=torch.float32), torch.randn(5, dtype=torch.float32)) verify_model( AddOp(), example_args, {}, Expected, custom_convert_map={"add.Tensor": custom_add_converter} ) def test_empty_like(): class EmptyLike(Module): def forward(self, data): return torch.empty_like(data) @tvm.script.ir_module class Expected: @R.function def main( data: R.Tensor((5,), dtype="float32"), ) -> R.Tuple(R.Tensor((5,), dtype="float32")): with R.dataflow(): lv: R.Tensor((5,), dtype="float32") = R.zeros(R.shape([5]), dtype="float32") gv: R.Tuple(R.Tensor((5,), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(5, dtype=torch.float32),) verify_model(EmptyLike(), example_args, {}, Expected) def test_one_hot(): class OneHot(Module): def forward(self, indices): return torch.nn.functional.one_hot(indices, num_classes=10) @tvm.script.ir_module class Expected: @R.function def main( indices: R.Tensor((5,), dtype="int64"), ) -> R.Tuple(R.Tensor((5, 10), dtype="int64")): with R.dataflow(): lv: R.Tensor((10,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(10), R.prim_value(1), dtype="int64" ) lv1: R.Tensor((5, 1), dtype="int64") = R.expand_dims(indices, axis=[-1]) lv2: R.Tensor((5, 10), dtype="bool") = R.equal(lv1, lv) lv3: R.Tensor((5, 10), dtype="int64") = R.astype(lv2, dtype="int64") gv: R.Tuple(R.Tensor((5, 10), dtype="int64")) = (lv3,) R.output(gv) return gv example_args = (torch.randint(0, 10, (5,), dtype=torch.int64),) verify_model(OneHot(), example_args, {}, Expected) def test_ones_like(): class OnesLike(Module): def forward(self, input): return torch.ones_like(input) @tvm.script.ir_module class Expected: @R.function def main(input: R.Tensor((128, 128), dtype="float32")) -> R.Tuple( R.Tensor((128, 128), dtype="float32") ): with R.dataflow(): lv: R.Tensor((128, 128), dtype="float32") = R.full_like(input, R.const(1, "int32")) gv: R.Tuple(R.Tensor((128, 128), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.rand(128, 128, dtype=torch.float32),) verify_model(OnesLike(), example_args, {}, Expected) def test_zero_inplace(): class ZeroInplace(Module): def forward(self, input): return input.zero_() @tvm.script.ir_module class Expected: @R.function def main(input: R.Tensor((128, 128), dtype="float32")) -> R.Tuple( R.Tensor((128, 128), dtype="float32") ): with R.dataflow(): lv: R.Tensor((128, 128), dtype="float32") = R.full_like(input, R.const(0, "int32")) gv: R.Tuple(R.Tensor((128, 128), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.rand(128, 128, dtype=torch.float32),) verify_model(ZeroInplace(), example_args, {}, Expected) def test_zeros(): class Zeros(Module): def forward(self, input): return torch.zeros(5, 2) @tvm.script.ir_module class Expected: @R.function def main(input: R.Tensor((128, 128), dtype="float32")) -> R.Tuple( R.Tensor((5, 2), dtype="float32") ): with R.dataflow(): lv: R.Tensor((5, 2), dtype="float32") = R.full( R.shape([5, 2]), R.const(0.0, "float32"), dtype="float32" ) gv: R.Tuple(R.Tensor((5, 2), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.rand(128, 128, dtype=torch.float32),) verify_model(Zeros(), example_args, {}, Expected) def test_zeros_like(): class ZerosLike(Module): def forward(self, input): return torch.zeros_like(input) @tvm.script.ir_module class Expected: @R.function def main(input: R.Tensor((128, 128), dtype="float32")) -> R.Tuple( R.Tensor((128, 128), dtype="float32") ): with R.dataflow(): lv: R.Tensor((128, 128), dtype="float32") = R.full_like(input, R.const(0, "int32")) gv: R.Tuple(R.Tensor((128, 128), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.rand(128, 128, dtype=torch.float32),) verify_model(ZerosLike(), example_args, {}, Expected) def test_randn(): class Randn(Module): def forward(self, input): return input + torch.randn(5, 3) example_args = (torch.rand(5, 3, dtype=torch.float32),) exported_program = export(Randn(), args=example_args) mod = from_exported_program(exported_program) func = mod["main"] ret_ty = func.ret_ty assert ret_ty.fields[0].shape[0] == 5 assert ret_ty.fields[0].shape[1] == 3 assert ret_ty.fields[0].dtype == "float32" def test_randn_like(): class RandnLike(Module): def forward(self, input): return input + torch.randn_like(input) example_args = (torch.rand(4, 6, dtype=torch.float32),) exported_program = export(RandnLike(), args=example_args) mod = from_exported_program(exported_program) func = mod["main"] ret_ty = func.ret_ty assert ret_ty.fields[0].shape[0] == 4 assert ret_ty.fields[0].shape[1] == 6 assert ret_ty.fields[0].dtype == "float32" def test_type_as(): class TypeAs(Module): def forward(self, input, other): return input.type_as(other) @tvm.script.ir_module class Expected: @R.function def main( input: R.Tensor((128, 128), dtype="float32"), other: R.Tensor((128, 128), dtype="float16"), ) -> R.Tuple(R.Tensor((128, 128), dtype="float16")): with R.dataflow(): lv: R.Tensor((128, 128), dtype="float16") = R.astype(input, dtype="float16") gv: R.Tuple(R.Tensor((128, 128), dtype="float16")) = (lv,) R.output(gv) return gv example_args = ( torch.rand(128, 128, dtype=torch.float32), torch.rand(128, 128, dtype=torch.float16), ) verify_model(TypeAs(), example_args, {}, Expected) def test_select(): class Select(Module): def forward(self, input): return torch.select(input, 0, 1) @tvm.script.ir_module class Expected: @R.function def main( inp_0: R.Tensor((2, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((3,), dtype="float32")): with R.dataflow(): lv: R.Tensor((3,), dtype="float32") = R.take(inp_0, R.const(1, "int64"), axis=0) gv: R.Tuple(R.Tensor((3,), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(2, 3, dtype=torch.float32),) verify_model(Select(), example_args, {}, Expected) def test_unflatten(): class Unflatten(Module): def forward(self, input): return torch.ops.aten.unflatten(input, 1, (3, 5)) class Unflatten1(Module): def forward(self, input): return torch.ops.aten.unflatten(input, -2, (3, 5)) @tvm.script.ir_module class Expected: @R.function def main( inp_0: R.Tensor((2, 15, 7), dtype="float32"), ) -> R.Tuple(R.Tensor((2, 3, 5, 7), dtype="float32")): with R.dataflow(): lv: R.Tensor((2, 3, 5, 7), dtype="float32") = R.reshape(inp_0, [2, 3, 5, 7]) gv: R.Tuple(R.Tensor((2, 3, 5, 7), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(2, 15, 7, dtype=torch.float32),) verify_model(Unflatten(), example_args, {}, Expected) verify_model(Unflatten1(), example_args, {}, Expected) def test_gather(): class Gather0(Module): def forward(self, data, indices): return torch.gather(data, 0, indices) class Gather1(Module): def forward(self, data, indices): return torch.gather(data, 1, indices) class Gather2(Module): def forward(self, data, indices): return torch.gather(data, -1, indices) class Gather3(Module): def forward(self, data, indices): return torch.gather(data, -2, indices) @tvm.script.ir_module class Expected0: @R.function def main( inp_0: R.Tensor((2, 3), dtype="float32"), inp_1: R.Tensor((2, 3), dtype="int64"), ) -> R.Tuple(R.Tensor((2, 3), dtype="float32")): with R.dataflow(): lv: R.Tensor((2, 3), dtype="float32") = R.gather_elements(inp_0, inp_1, axis=0) gv: R.Tuple(R.Tensor((2, 3), dtype="float32")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class Expected1: @R.function def main( inp_0: R.Tensor((2, 3), dtype="float32"), inp_1: R.Tensor((2, 3), dtype="int64"), ) -> R.Tuple(R.Tensor((2, 3), dtype="float32")): with R.dataflow(): lv: R.Tensor((2, 3), dtype="float32") = R.gather_elements(inp_0, inp_1, axis=1) gv: R.Tuple(R.Tensor((2, 3), dtype="float32")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class Expected2: @R.function def main( inp_0: R.Tensor((2, 3), dtype="float32"), inp_1: R.Tensor((2, 3), dtype="int64"), ) -> R.Tuple(R.Tensor((2, 3), dtype="float32")): with R.dataflow(): lv: R.Tensor((2, 3), dtype="float32") = R.gather_elements(inp_0, inp_1, axis=-1) gv: R.Tuple(R.Tensor((2, 3), dtype="float32")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class Expected3: @R.function def main( inp_0: R.Tensor((2, 3), dtype="float32"), inp_1: R.Tensor((2, 3), dtype="int64"), ) -> R.Tuple(R.Tensor((2, 3), dtype="float32")): with R.dataflow(): lv: R.Tensor((2, 3), dtype="float32") = R.gather_elements(inp_0, inp_1, axis=-2) gv: R.Tuple(R.Tensor((2, 3), dtype="float32")) = (lv,) R.output(gv) return gv example_args = ( torch.randn(2, 3, dtype=torch.float32), torch.randint(0, 3, (2, 3), dtype=torch.int64), ) verify_model(Gather0(), example_args, {}, Expected0) verify_model(Gather1(), example_args, {}, Expected1) verify_model(Gather2(), example_args, {}, Expected2) verify_model(Gather3(), example_args, {}, Expected3) def test_index_put(): # Test case 1: 1D input class IndexPut1D(Module): def forward(self, data, indices_0, values): indices_tuple = (indices_0,) return data.index_put_(indices_tuple, values, accumulate=False) example_args_1d = ( torch.randn(64, dtype=torch.float32), torch.randint(0, 64, (128,), dtype=torch.int64), torch.randn(128, dtype=torch.float32), ) @I.ir_module class Expected1D: @R.function def main( data: R.Tensor((64,), dtype="float32"), indices_0: R.Tensor((128,), dtype="int64"), values: R.Tensor((128,), dtype="float32"), ) -> R.Tuple(R.Tensor((64,), dtype="float32")): with R.dataflow(): lv: R.Tensor((64,), dtype="float32") = R.index_put( data, (indices_0,), values, accumulate=False ) gv: R.Tuple(R.Tensor((64,), dtype="float32")) = (lv,) R.output(gv) return gv # Test case 2: 2D input class IndexPut2D(Module): def forward(self, data, indices_0, indices_1, values): indices_tuple = (indices_0, indices_1) return data.index_put_(indices_tuple, values, accumulate=False) example_args_2d = ( torch.randn(32, 64, dtype=torch.float32), torch.randint(0, 32, (128,), dtype=torch.int64), torch.randint(0, 64, (128,), dtype=torch.int64), torch.randn(128, dtype=torch.float32), ) @I.ir_module class Expected2D: @R.function def main( data: R.Tensor((32, 64), dtype="float32"), indices_0: R.Tensor((128,), dtype="int64"), indices_1: R.Tensor((128,), dtype="int64"), values: R.Tensor((128,), dtype="float32"), ) -> R.Tuple(R.Tensor((32, 64), dtype="float32")): with R.dataflow(): lv: R.Tensor((32, 64), dtype="float32") = R.index_put( data, (indices_0, indices_1), values, accumulate=False ) gv: R.Tuple(R.Tensor((32, 64), dtype="float32")) = (lv,) R.output(gv) return gv # Test case 3: 3D input class IndexPut3D(Module): def forward(self, data, indices_0, indices_1, indices_2, values): indices_tuple = (indices_0, indices_1, indices_2) return data.index_put_(indices_tuple, values, accumulate=False) example_args_3d = ( torch.randn(16, 32, 64, dtype=torch.float32), torch.randint(0, 16, (128,), dtype=torch.int64), torch.randint(0, 32, (128,), dtype=torch.int64), torch.randint(0, 64, (128,), dtype=torch.int64), torch.randn(128, dtype=torch.float32), ) @I.ir_module class Expected3D: @R.function def main( data: R.Tensor((16, 32, 64), dtype="float32"), indices_0: R.Tensor((128,), dtype="int64"), indices_1: R.Tensor((128,), dtype="int64"), indices_2: R.Tensor((128,), dtype="int64"), values: R.Tensor((128,), dtype="float32"), ) -> R.Tuple(R.Tensor((16, 32, 64), dtype="float32")): with R.dataflow(): lv: R.Tensor((16, 32, 64), dtype="float32") = R.index_put( data, (indices_0, indices_1, indices_2), values, accumulate=False ) gv: R.Tuple(R.Tensor((16, 32, 64), dtype="float32")) = (lv,) R.output(gv) return gv # Test case 4: 4D input class IndexPut4D(Module): def forward(self, data, indices_0, indices_1, indices_2, indices_3, values): indices_tuple = (indices_0, indices_1, indices_2, indices_3) return data.index_put_(indices_tuple, values, accumulate=False) example_args_4d = ( torch.randn(8, 16, 32, 64, dtype=torch.float32), torch.randint(0, 8, (128,), dtype=torch.int64), torch.randint(0, 16, (128,), dtype=torch.int64), torch.randint(0, 32, (128,), dtype=torch.int64), torch.randint(0, 64, (128,), dtype=torch.int64), torch.randn(128, dtype=torch.float32), ) @I.ir_module class Expected4D: @R.function def main( data: R.Tensor((8, 16, 32, 64), dtype="float32"), indices_0: R.Tensor((128,), dtype="int64"), indices_1: R.Tensor((128,), dtype="int64"), indices_2: R.Tensor((128,), dtype="int64"), indices_3: R.Tensor((128,), dtype="int64"), values: R.Tensor((128,), dtype="float32"), ) -> R.Tuple(R.Tensor((8, 16, 32, 64), dtype="float32")): with R.dataflow(): lv: R.Tensor((8, 16, 32, 64), dtype="float32") = R.index_put( data, (indices_0, indices_1, indices_2, indices_3), values, accumulate=False, ) gv: R.Tuple(R.Tensor((8, 16, 32, 64), dtype="float32")) = (lv,) R.output(gv) return gv # Test case 5: 5D input class IndexPut5D(Module): def forward(self, data, indices_0, indices_1, indices_2, indices_3, indices_4, values): indices_tuple = (indices_0, indices_1, indices_2, indices_3, indices_4) return data.index_put_(indices_tuple, values, accumulate=False) example_args_5d = ( torch.randn(4, 8, 16, 32, 64, dtype=torch.float32), torch.randint(0, 4, (128,), dtype=torch.int64), torch.randint(0, 8, (128,), dtype=torch.int64), torch.randint(0, 16, (128,), dtype=torch.int64), torch.randint(0, 32, (128,), dtype=torch.int64), torch.randint(0, 64, (128,), dtype=torch.int64), torch.randn(128, dtype=torch.float32), ) @I.ir_module class Expected5D: @R.function def main( data: R.Tensor((4, 8, 16, 32, 64), dtype="float32"), indices_0: R.Tensor((128,), dtype="int64"), indices_1: R.Tensor((128,), dtype="int64"), indices_2: R.Tensor((128,), dtype="int64"), indices_3: R.Tensor((128,), dtype="int64"), indices_4: R.Tensor((128,), dtype="int64"), values: R.Tensor((128,), dtype="float32"), ) -> R.Tuple(R.Tensor((4, 8, 16, 32, 64), dtype="float32")): with R.dataflow(): lv: R.Tensor((4, 8, 16, 32, 64), dtype="float32") = R.index_put( data, (indices_0, indices_1, indices_2, indices_3, indices_4), values, accumulate=False, ) gv: R.Tuple(R.Tensor((4, 8, 16, 32, 64), dtype="float32")) = (lv,) R.output(gv) return gv # Test case 6: 2D input with multi-dimensional index (broadcasting) # This tests the multi-dimensional index support with broadcasting class IndexPutBroadcast1D(Module): def forward(self, data, indices_1): indices_0 = torch.arange(data.shape[0]).unsqueeze(1) values = torch.ones(data.shape[0], len(indices_1), dtype=data.dtype) return data.index_put_((indices_0, indices_1), values, accumulate=False) example_args_broadcast1 = ( torch.randn(32, 64, dtype=torch.float32), torch.randint(0, 64, (10,), dtype=torch.int64), ) @I.ir_module class ExpectedBroadcast1D: @R.function def main( data: R.Tensor((32, 64), dtype="float32"), indices_1: R.Tensor((10,), dtype="int64"), ) -> R.Tuple(R.Tensor((32, 64), dtype="float32")): with R.dataflow(): lv: R.Tensor((32,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(32), R.prim_value(1), dtype="int64" ) lv1: R.Tensor((32, 1), dtype="int64") = R.expand_dims(lv, axis=[1]) lv2: R.Tensor((32, 10), dtype="float32") = R.full( R.shape([32, 10]), R.const(1.0, "float32"), dtype="float32" ) lv3: R.Tensor((32, 64), dtype="float32") = R.index_put( data, (lv1, indices_1), lv2, accumulate=False ) gv: R.Tuple(R.Tensor((32, 64), dtype="float32")) = (lv3,) R.output(gv) return gv # Test case 7: 2D input with multi-dimensional index (second position) class IndexPutBroadcast2D(Module): def forward(self, data, indices_0): indices_1 = torch.arange(data.shape[1]).unsqueeze(1) values = torch.ones(len(indices_0), data.shape[1], dtype=data.dtype) return data.index_put_((indices_0, indices_1), values, accumulate=False) example_args_broadcast2 = ( torch.randn(32, 64, dtype=torch.float32), torch.randint(0, 32, (10,), dtype=torch.int64), ) @I.ir_module class ExpectedBroadcast2D: @R.function def main( data: R.Tensor((32, 64), dtype="float32"), indices_0: R.Tensor((10,), dtype="int64"), ) -> R.Tuple(R.Tensor((32, 64), dtype="float32")): with R.dataflow(): lv: R.Tensor((64,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(64), R.prim_value(1), dtype="int64" ) lv1: R.Tensor((64, 1), dtype="int64") = R.expand_dims(lv, axis=[1]) lv2: R.Tensor((10, 64), dtype="float32") = R.full( R.shape([10, 64]), R.const(1.0, "float32"), dtype="float32" ) lv3: R.Tensor((32, 64), dtype="float32") = R.index_put( data, (indices_0, lv1), lv2, accumulate=False ) gv: R.Tuple(R.Tensor((32, 64), dtype="float32")) = (lv3,) R.output(gv) return gv # Test case 8: 3D input with mixed 1D and 2D indices class IndexPutBroadcast3D(Module): def forward(self, data, indices_1): indices_0 = torch.arange(data.shape[0]).unsqueeze(1) indices_2 = torch.arange(data.shape[2]).unsqueeze(1) values = torch.ones(data.shape[0], len(indices_1), data.shape[2], dtype=data.dtype) return data.index_put_((indices_0, indices_1, indices_2), values, accumulate=False) example_args_broadcast3d = ( torch.randn(16, 32, 64, dtype=torch.float32), torch.randint(0, 32, (10,), dtype=torch.int64), ) @I.ir_module class ExpectedBroadcast3D: @R.function def main( data: R.Tensor((16, 32, 64), dtype="float32"), indices_1: R.Tensor((10,), dtype="int64"), ) -> R.Tuple(R.Tensor((16, 32, 64), dtype="float32")): with R.dataflow(): lv: R.Tensor((16,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(16), R.prim_value(1), dtype="int64" ) lv1: R.Tensor((16, 1), dtype="int64") = R.expand_dims(lv, axis=[1]) lv2: R.Tensor((64,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(64), R.prim_value(1), dtype="int64" ) lv3: R.Tensor((64, 1), dtype="int64") = R.expand_dims(lv2, axis=[1]) lv4: R.Tensor((16, 10, 64), dtype="float32") = R.full( R.shape([16, 10, 64]), R.const(1.0, "float32"), dtype="float32" ) lv5: R.Tensor((16, 32, 64), dtype="float32") = R.index_put( data, (lv1, indices_1, lv3), lv4, accumulate=False ) gv: R.Tuple(R.Tensor((16, 32, 64), dtype="float32")) = (lv5,) R.output(gv) return gv # Test case 9: batched indexing with slice (e.g., M[:, rows, cols] = x) class IndexPutBatchedWithNone(Module): def forward(self, x): B = x.size(0) M = torch.zeros(B, 11, 11) rows = torch.arange(10) cols = rows + 1 M[:, rows, cols] = x # Batched index assignment return M example_args_batched_none = (torch.randn(2, 10, dtype=torch.float32),) @I.ir_module class ExpectedBatchedWithNone: @R.function def main(x: R.Tensor((2, 10), dtype="float32")) -> R.Tuple( R.Tensor((2, 11, 11), dtype="float32") ): with R.dataflow(): lv: R.Tensor((2, 11, 11), dtype="float32") = R.full( R.shape([2, 11, 11]), R.const(0.0, "float32"), dtype="float32" ) lv1: R.Tensor((10,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(10), R.prim_value(1), dtype="int64" ) lv2: R.Tensor((10,), dtype="int64") = R.add(lv1, R.const(1, "int64")) lv3: R.Tensor((2,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(2), R.prim_value(1), dtype="int64" ) lv4: R.Tensor((2, 1), dtype="int64") = R.reshape(lv3, R.shape([2, 1])) lv5: R.Tensor((2, 11, 11), dtype="float32") = R.index_put( lv, (lv4, lv1, lv2), x, accumulate=False ) gv: R.Tuple(R.Tensor((2, 11, 11), dtype="float32")) = (lv5,) R.output(gv) return gv # Run verification for each case verify_model(IndexPut1D(), example_args_1d, {}, Expected1D) verify_model(IndexPut2D(), example_args_2d, {}, Expected2D) verify_model(IndexPut3D(), example_args_3d, {}, Expected3D) verify_model(IndexPut4D(), example_args_4d, {}, Expected4D) verify_model(IndexPut5D(), example_args_5d, {}, Expected5D) verify_model(IndexPutBroadcast1D(), example_args_broadcast1, {}, ExpectedBroadcast1D) verify_model(IndexPutBroadcast2D(), example_args_broadcast2, {}, ExpectedBroadcast2D) verify_model(IndexPutBroadcast3D(), example_args_broadcast3d, {}, ExpectedBroadcast3D) verify_model(IndexPutBatchedWithNone(), example_args_batched_none, {}, ExpectedBatchedWithNone) def test_index_put_with_tuple_output(): class IndexPutTupleOutput(Module): def forward(self, x, buf, idx): values = x buf[..., idx, idx] = values return x[..., 1], buf example_args = ( torch.ones(2, 3, 5, dtype=torch.float32), torch.zeros(2, 3, 5, 5, dtype=torch.float32), torch.tensor([0, 1, 2, 3, 4], dtype=torch.int64), ) exported_program = export(IndexPutTupleOutput(), args=example_args) mod = from_exported_program(exported_program) ret_ty = mod["main"].ret_ty assert isinstance(ret_ty, relax.TupleType) tensor_fields = [f for f in ret_ty.fields if isinstance(f, relax.TensorType)] assert len(tensor_fields) >= 2 assert any( len(f.shape) == 4 and int(f.shape[-2]) == 5 and int(f.shape[-1]) == 5 for f in tensor_fields ) def test_m4d_diag_index_put_tuple_output_regression(): class M4D(Module): def forward(self, x): b, k, n = 2, 3, 5 buf = x.new_zeros(b, k, n, n) idx = torch.arange(n, device=x.device) diag = buf[..., idx, idx] diag = torch.nn.functional.elu(diag) + 1.0 + 1e-8 buf[..., idx, idx] = diag return x[..., :1], buf ex_in = torch.zeros(2, 3, 5, dtype=torch.float32) exported_program = export(M4D().eval(), args=(ex_in,)) exported_targets = [str(getattr(n, "target", "")) for n in exported_program.graph.nodes] assert any("index_put" in target for target in exported_targets) # Regression focus: importing this graph should not segfault at Tuple construction. mod = from_exported_program(exported_program) ret_ty = mod["main"].ret_ty assert isinstance(ret_ty, relax.TupleType) tensor_fields = [f for f in ret_ty.fields if isinstance(f, relax.TensorType)] assert len(tensor_fields) >= 2 # x: (2, 3, 5) → x[..., :1]: (2, 3, 1) assert any(len(f.shape) == 3 and int(f.shape[-1]) == 1 for f in tensor_fields) # buf: (2, 3, 5, 5) → 4-D with spatial dims 5x5 assert any( len(f.shape) == 4 and int(f.shape[-2]) == 5 and int(f.shape[-1]) == 5 for f in tensor_fields ) def test_index_put_mutation_through_alias_regression(): class IndexPutAlias(Module): def forward(self, x, idx, values): y = torch.ops.aten.alias.default(x) y[idx] = values return x, y example_args = ( torch.zeros(5, dtype=torch.float32), torch.tensor([1, 3], dtype=torch.int64), torch.tensor([2.0, 4.0], dtype=torch.float32), ) @I.ir_module class Expected: @R.function def main( x: R.Tensor((5,), dtype="float32"), idx: R.Tensor((2,), dtype="int64"), values: R.Tensor((2,), dtype="float32"), ) -> R.Tuple( R.Tensor((5,), dtype="float32"), R.Tensor((5,), dtype="float32"), ): with R.dataflow(): lv: R.Tensor((5,), dtype="float32") = R.index_put( x, (idx,), values, accumulate=False ) # Mutation outputs introduced by functionalization are dropped; # only the user outputs (x, y) remain. gv: R.Tuple( R.Tensor((5,), dtype="float32"), R.Tensor((5,), dtype="float32"), ) = ( lv, lv, ) R.output(gv) return gv verify_model(IndexPutAlias(), example_args, {}, Expected) def test_flip(): class Flip0(Module): def forward(self, data): return torch.flip(data, [0]) class Flip1(Module): def forward(self, data): return torch.flip(data, [1]) @tvm.script.ir_module class Expected0: @R.function def main( inp_0: R.Tensor((2, 2), dtype="float32"), ) -> R.Tuple(R.Tensor((2, 2), dtype="float32")): with R.dataflow(): lv: R.Tensor((2, 2), dtype="float32") = R.flip(inp_0, axis=0) gv: R.Tuple(R.Tensor((2, 2), dtype="float32")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class Expected1: @R.function def main( inp_0: R.Tensor((2, 2), dtype="float32"), ) -> R.Tuple(R.Tensor((2, 2), dtype="float32")): with R.dataflow(): lv: R.Tensor((2, 2), dtype="float32") = R.flip(inp_0, axis=1) gv: R.Tuple(R.Tensor((2, 2), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(2, 2, dtype=torch.float32),) verify_model(Flip0(), example_args, {}, Expected0) verify_model(Flip1(), example_args, {}, Expected1) def test_flip_multi_axis(): class FlipMulti(Module): def forward(self, data): return torch.flip(data, [0, 1]) class FlipNegMulti(Module): def forward(self, data): return torch.flip(data, dims=[-1, -2]) @tvm.script.ir_module class ExpectedMulti: @R.function def main( inp_0: R.Tensor((2, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((2, 3), dtype="float32")): with R.dataflow(): lv: R.Tensor((2, 3), dtype="float32") = R.flip(inp_0, axis=0) lv1: R.Tensor((2, 3), dtype="float32") = R.flip(lv, axis=1) gv: R.Tuple(R.Tensor((2, 3), dtype="float32")) = (lv1,) R.output(gv) return gv @tvm.script.ir_module class ExpectedNegMulti: @R.function def main( inp_0: R.Tensor((2, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((2, 3), dtype="float32")): with R.dataflow(): lv: R.Tensor((2, 3), dtype="float32") = R.flip(inp_0, axis=-1) lv1: R.Tensor((2, 3), dtype="float32") = R.flip(lv, axis=-2) gv: R.Tuple(R.Tensor((2, 3), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = (torch.randn(2, 3, dtype=torch.float32),) verify_model(FlipMulti(), example_args, {}, ExpectedMulti) verify_model(FlipNegMulti(), example_args, {}, ExpectedNegMulti) def test_take(): class Take(Module): def forward(self, data, indices): return torch.take(data, indices) @tvm.script.ir_module class Expected: @R.function def main( data: R.Tensor((5,), dtype="float32"), indices: R.Tensor((3,), dtype="int64"), ) -> R.Tuple(R.Tensor((3,), dtype="float32")): with R.dataflow(): lv: R.Tensor((5,), dtype="float32") = R.reshape(data, R.shape([5])) lv1: R.Tensor((3,), dtype="float32") = R.take(lv, indices, axis=0, mode="fast") gv: R.Tuple(R.Tensor((3,), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = ( torch.randn(5, dtype=torch.float32), torch.randint(0, 5, (3,), dtype=torch.int64), ) verify_model(Take(), example_args, {}, Expected) def test_any(): class AnyAten(torch.nn.Module): def forward(self, x): return torch.ops.aten.any(x, dim=1) @tvm.script.ir_module class Expected: @R.function def main( x: R.Tensor((2, 3), dtype="bool"), ) -> R.Tuple(R.Tensor((2,), dtype="bool")): with R.dataflow(): lv: R.Tensor((2, 3), dtype="int8") = relax.op.astype(x, dtype="int8") lv2: R.Tensor((2,), dtype="int8") = relax.op.max(lv, axis=1, keepdims=False) lv3: R.Tensor((2,), dtype="bool") = relax.op.astype(lv2, dtype="bool") gv: R.Tuple(R.Tensor((2,), dtype="bool")) = (lv3,) R.output(gv) return gv example_args = (torch.tensor([[0, 0, 0], [0, 1, 0]], dtype=torch.bool),) verify_model(AnyAten(), example_args, {}, Expected) def test_std(): # torch.std(x) defaults to correction=1 (Bessel); decomposes to var.correction + sqrt. class Std(Module): def forward(self, x): return torch.std(x) @tvm.script.ir_module class Expected: @R.function def main( x: R.Tensor((5, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((), dtype="float32")): with R.dataflow(): lv: R.Tensor((), dtype="float32") = R.variance(x, axis=None, keepdims=False) lv1: R.Tensor((), dtype="float32") = R.multiply(lv, R.const(15.0 / 14.0, "float32")) lv2: R.Tensor((), dtype="float32") = R.sqrt(lv1) gv: R.Tuple(R.Tensor((), dtype="float32")) = (lv2,) R.output(gv) return gv example_args = (torch.randn(5, 3, dtype=torch.float32),) verify_model(Std(), example_args, {}, Expected) def test_var(): # torch.var(x) defaults to correction=1 (Bessel). class Var(Module): def forward(self, x): return torch.var(x) @tvm.script.ir_module class Expected: @R.function def main( x: R.Tensor((5, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((), dtype="float32")): with R.dataflow(): lv: R.Tensor((), dtype="float32") = R.variance(x, axis=None, keepdims=False) lv1: R.Tensor((), dtype="float32") = R.multiply(lv, R.const(15.0 / 14.0, "float32")) gv: R.Tuple(R.Tensor((), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = (torch.randn(5, 3, dtype=torch.float32),) verify_model(Var(), example_args, {}, Expected) def test_var_correction(): class VarCorrection2(Module): def forward(self, x): return torch.var(x, dim=-1, correction=2) class VarCorrection0(Module): def forward(self, x): return torch.var(x, dim=1, correction=0) @tvm.script.ir_module class Expected2: @R.function def main( x: R.Tensor((2, 5), dtype="float32"), ) -> R.Tuple(R.Tensor((2,), dtype="float32")): with R.dataflow(): lv: R.Tensor((2,), dtype="float32") = R.variance(x, axis=[-1], keepdims=False) lv1: R.Tensor((2,), dtype="float32") = R.multiply(lv, R.const(5.0 / 3.0, "float32")) gv: R.Tuple(R.Tensor((2,), dtype="float32")) = (lv1,) R.output(gv) return gv @tvm.script.ir_module class Expected0: @R.function def main( x: R.Tensor((2, 5), dtype="float32"), ) -> R.Tuple(R.Tensor((2,), dtype="float32")): with R.dataflow(): lv: R.Tensor((2,), dtype="float32") = R.variance(x, axis=[1], keepdims=False) gv: R.Tuple(R.Tensor((2,), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(2, 5, dtype=torch.float32),) verify_model(VarCorrection2(), example_args, {}, Expected2) verify_model(VarCorrection0(), example_args, {}, Expected0) @pytest.mark.parametrize( "torch_dtype,relax_dtype", [(torch.float32, "float32"), (torch.bool, "bool")], ) def test_prod(torch_dtype, relax_dtype): class Prod(Module): def forward(self, x): return torch.prod(x, dtype=torch_dtype) @tvm.script.ir_module class Expected: @R.function def main( x: R.Tensor((5, 3), dtype=relax_dtype), ) -> R.Tuple(R.Tensor((), dtype=relax_dtype)): with R.dataflow(): lv: R.Tensor((), dtype=relax_dtype) = R.prod(x, axis=None, keepdims=False) gv: R.Tuple(R.Tensor((), dtype=relax_dtype)) = (lv,) R.output(gv) return gv example_args = (torch.ones(5, 3, dtype=torch_dtype),) verify_model(Prod(), example_args, {}, Expected) def test_cumprod(): class Cumprod(Module): def forward(self, x): return torch.cumprod(x, 0) @tvm.script.ir_module class Expected: @R.function def main( inp_0: R.Tensor((5, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((5, 3), dtype="float32")): with R.dataflow(): lv: R.Tensor((5, 3), dtype="float32") = R.cumprod(inp_0, axis=0, exclusive=False) gv: R.Tuple(R.Tensor((5, 3), dtype="float32")) = (lv,) R.output(gv) return gv example_input = torch.randn(5, 3, dtype=torch.float32) verify_model(Cumprod(), (example_input,), {}, Expected) def test_where(): class Where(Module): def forward(self, condition, x, y): return torch.where(condition, x, y) @tvm.script.ir_module class Expected: @R.function def main( inp_0: R.Tensor((5, 3), dtype="bool"), inp_1: R.Tensor((5, 3), dtype="float32"), inp_2: R.Tensor((5, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((5, 3), dtype="float32")): with R.dataflow(): lv: R.Tensor((5, 3), dtype="float32") = R.where(inp_0, inp_1, inp_2) gv: R.Tuple(R.Tensor((5, 3), dtype="float32")) = (lv,) R.output(gv) return gv condition = torch.testing.make_tensor((5, 3), dtype=torch.bool, device="cpu") x = torch.randn(5, 3, dtype=torch.float32) y = torch.randn(5, 3, dtype=torch.float32) verify_model(Where(), (condition, x, y), {}, Expected) def test_bucketize(): class Bucketize(Module): def forward(self, input_tensor, boundaries): return torch.bucketize(input_tensor, boundaries) @tvm.script.ir_module class Expected: @R.function def main( input: R.Tensor((20,), dtype="int64"), boundaries: R.Tensor((10,), dtype="int64") ) -> R.Tuple(R.Tensor((20,), dtype="int64")): with R.dataflow(): lv: R.Tensor((20,), dtype="int64") = R.bucketize( input, boundaries, out_int32=False, right=False ) gv: R.Tuple(R.Tensor((20,), dtype="int64")) = (lv,) R.output(gv) return gv input_tensor = torch.arange(0, 20) boundaries = torch.arange(0, 20, 2) verify_model(Bucketize(), (input_tensor, boundaries), {}, Expected) @pytest.mark.parametrize("right", [False, True]) @pytest.mark.parametrize("out_int32", [False, True]) def test_bucketize_numerically(right, out_int32): class Bucketize(Module): def forward(self, input_tensor, boundaries): return torch.bucketize(input_tensor, boundaries, right=right, out_int32=out_int32) input_tensor = torch.tensor([-0.5, 0.0, 0.5, 1.0, 2.0, 2.5], dtype=torch.float32) boundaries = torch.tensor([0.0, 1.0, 2.0], dtype=torch.float32) verify_model_numerically(Bucketize(), (input_tensor, boundaries)) def test_argsort(): class Argsort(Module): def forward(self, x): return torch.argsort(x, dim=1, descending=True) @tvm.script.ir_module class Expected: @R.function def main(x: R.Tensor((5, 3), dtype="float32")) -> R.Tuple(R.Tensor((5, 3), dtype="int32")): with R.dataflow(): lv: R.Tensor((5, 3), dtype="int32") = R.argsort( x, axis=1, descending=True, dtype="int32" ) lv1: R.Tensor((5, 3), dtype="float32") = R.gather_elements(x, lv, axis=1) lv2: R.Tuple(R.Tensor((5, 3), dtype="float32"), R.Tensor((5, 3), dtype="int32")) = ( lv1, lv, ) lv3: R.Tensor((5, 3), dtype="int32") = lv2[1] gv: R.Tuple(R.Tensor((5, 3), dtype="int32")) = (lv3,) R.output(gv) return gv example_args = (torch.randn(5, 3, dtype=torch.float32),) verify_model(Argsort(), example_args, {}, Expected) def test_topk(): class Topk(Module): def forward(self, x): return torch.topk(x, k=2, dim=1, largest=True, sorted=True) @tvm.script.ir_module class Expected: @R.function def main(x: R.Tensor((5, 3), dtype="float32")) -> R.Tuple( R.Tensor((5, 2), dtype="float32"), R.Tensor((5, 2), dtype="int64") ): with R.dataflow(): lv: R.Tuple(R.Tensor((5, 2), dtype="float32"), R.Tensor((5, 2), dtype="int64")) = ( R.topk(x, k=2, axis=1, ret_type="both", largest=True, dtype="int64") ) lv1: R.Tensor((5, 2), dtype="float32") = lv[0] lv2: R.Tensor((5, 2), dtype="int64") = lv[1] gv: R.Tuple(R.Tensor((5, 2), dtype="float32"), R.Tensor((5, 2), dtype="int64")) = ( lv1, lv2, ) R.output(gv) return gv example_args = (torch.randn(5, 3, dtype=torch.float32),) verify_model(Topk(), example_args, {}, Expected) def test_dynamic_shape(): class DynamicModel(torch.nn.Module): def forward(self, x1, x2): return torch.ops.aten.add.Tensor(x1, x2) @I.ir_module class Expected: @R.function def main( lhs: R.Tensor(("s0", 4), dtype="float32"), rhs: R.Tensor(("s0", 4), dtype="float32"), ) -> R.Tuple(R.Tensor(("s0", 4), dtype="float32")): s0 = T.int64() R.func_attr({"tir_var_lower_bound": {"s24": 0}}) with R.dataflow(): lv: R.Tensor((s0, 4), dtype="float32") = R.add(lhs, rhs) gv: R.Tuple(R.Tensor((s0, 4), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(2, 4), torch.randn(2, 4)) batch = torch.export.Dim("batch") dynamic_shapes = {"x1": {0: batch}, "x2": {0: batch}} verify_model( DynamicModel(), example_args, {}, Expected, dynamic_shapes=dynamic_shapes, run_ep_decomposition=True, map_free_vars=True, ) def test_broadcast_to(): class BroadcastTo(Module): def forward(self, x): return torch.broadcast_to(x, (5, 3)) @tvm.script.ir_module class Expected: @R.function def main(x: R.Tensor((5, 1), dtype="float32")) -> R.Tuple( R.Tensor((5, 3), dtype="float32") ): with R.dataflow(): lv: R.Tensor((5, 3), dtype="float32") = R.broadcast_to(x, R.shape([5, 3])) gv: R.Tuple(R.Tensor((5, 3), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(5, 1, dtype=torch.float32),) verify_model(BroadcastTo(), example_args, {}, Expected) def test_narrow(): class Narrow(Module): def forward(self, x): return torch.narrow(x, 1, 0, 2) @tvm.script.ir_module class Expected: @R.function def main(x: R.Tensor((5, 3), dtype="float32")) -> R.Tuple( R.Tensor((5, 2), dtype="float32") ): with R.dataflow(): lv: R.Tensor((5, 2), dtype="float32") = R.strided_slice( x, (R.prim_value(1),), (R.prim_value(0),), (R.prim_value(2),), (R.prim_value(1),), assume_inbound=False, ) gv: R.Tuple(R.Tensor((5, 2), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(5, 3, dtype=torch.float32),) verify_model(Narrow(), example_args, {}, Expected) def test_item(): class Item(Module): def forward(self, x): return x.item() @tvm.script.ir_module class Expected: @R.function def main(input: R.Tensor((1,), dtype="float32")) -> R.Tuple(R.Tensor((), dtype="float32")): with R.dataflow(): lv: R.Tensor((), dtype="float32") = R.take(input, R.const(0, "int64"), axis=0) gv: R.Tuple(R.Tensor((), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(1, dtype=torch.float32),) verify_model(Item(), example_args, {}, Expected) def test_norm(): class Norm(Module): def __init__(self, p, dim=None, keepdim=False): super().__init__() self.p = p self.dim = dim self.keepdim = keepdim def forward(self, x): return torch.norm(x, p=self.p, dim=self.dim, keepdim=self.keepdim) @tvm.script.ir_module class Expected1: @R.function def main( inp_0: R.Tensor((1, 3, 5, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((), dtype="float32")): with R.dataflow(): lv: R.Tensor((), dtype="float32") = R.max(R.abs(inp_0), axis=None, keepdims=False) gv: R.Tuple(R.Tensor((), dtype="float32")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class Expected2: @R.function def main( inp_0: R.Tensor((1, 3, 5, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((), dtype="float32")): with R.dataflow(): lv: R.Tensor((), dtype="float32") = R.min(R.abs(inp_0), axis=None, keepdims=False) gv: R.Tuple(R.Tensor((), dtype="float32")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class Expected3: @R.function def main( inp_0: R.Tensor((1, 3, 5, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((), dtype="float32")): with R.dataflow(): lv: R.Tensor((1, 3, 5, 3), dtype="float32") = R.abs(inp_0) lv1: R.Tensor((1, 3, 5, 3), dtype="float32") = R.power(lv, R.const(2, "float32")) lv2: R.Tensor((), dtype="float32") = R.sum(lv1, axis=None, keepdims=False) lv3: R.Tensor((), dtype="float32") = R.power(lv2, R.const(0.5, "float32")) gv: R.Tuple(R.Tensor((), dtype="float32")) = (lv3,) R.output(gv) return gv @tvm.script.ir_module class Expected4: @R.function def main( inp_0: R.Tensor((1, 3, 5, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((), dtype="float32")): with R.dataflow(): lv: R.Tensor((1, 3, 5, 3), dtype="float32") = R.abs(inp_0) lv1: R.Tensor((1, 3, 5, 3), dtype="float32") = R.power(lv, R.const(1.0, "float32")) lv2: R.Tensor((), dtype="float32") = R.sum(lv1, axis=None, keepdims=False) lv3: R.Tensor((), dtype="float32") = R.power(lv2, R.const(1.0, "float32")) gv: R.Tuple(R.Tensor((), dtype="float32")) = (lv3,) R.output(gv) return gv @tvm.script.ir_module class Expected5: @R.function def main( inp_0: R.Tensor((1, 3, 5, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 1, 1, 1), dtype="float32")): with R.dataflow(): lv: R.Tensor((1, 3, 5, 3), dtype="float32") = R.abs(inp_0) lv1: R.Tensor((1, 3, 5, 3), dtype="float32") = R.power(lv, R.const(-4.0, "float32")) lv2: R.Tensor((1, 1, 1, 1), dtype="float32") = R.sum(lv1, axis=None, keepdims=True) lv3: R.Tensor((1, 1, 1, 1), dtype="float32") = R.power( lv2, R.const(-0.25, "float32") ) gv: R.Tuple(R.Tensor((1, 1, 1, 1), dtype="float32")) = (lv3,) R.output(gv) return gv @tvm.script.ir_module class Expected6: @R.function def main( inp_0: R.Tensor((1, 3, 5, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 1, 1, 1), dtype="float32")): with R.dataflow(): lv: R.Tensor((1, 3, 5, 3), dtype="float32") = R.abs(inp_0) lv1: R.Tensor((1, 3, 5, 3), dtype="float32") = R.power(lv, R.const(0.5, "float32")) lv2: R.Tensor((1, 1, 1, 1), dtype="float32") = R.sum(lv1, axis=None, keepdims=True) lv3: R.Tensor((1, 1, 1, 1), dtype="float32") = R.power(lv2, R.const(2.0, "float32")) gv: R.Tuple(R.Tensor((1, 1, 1, 1), dtype="float32")) = (lv3,) R.output(gv) return gv norms = [ ((float("inf"), None, False), Expected1), ((float("-inf"), None, False), Expected2), ((float(2), None, False), Expected3), ((1.0, None, False), Expected4), ((float(-4), None, True), Expected5), ((0.5, None, True), Expected6), ] example_args = (torch.randn(1, 3, 5, 3, dtype=torch.float32),) for (p, dim, keepdim), expected in norms: verify_model(Norm(p, dim=dim, keepdim=keepdim), example_args, {}, expected) def test_eye(): import pytest class Eye1(Module): def forward(self, input): return torch.eye(3, 5, dtype=torch.float32) @tvm.script.ir_module class Expected1: @R.function def main(input: R.Tensor((3, 5), dtype="float32")) -> R.Tuple( R.Tensor((3, 5), dtype="float32") ): with R.dataflow(): lv: R.Tensor((3,), dtype="uint8") = R.arange( R.prim_value(0), R.prim_value(3), R.prim_value(1), dtype="uint8" ) lv1: R.Tensor((5,), dtype="uint8") = R.arange( R.prim_value(0), R.prim_value(5), R.prim_value(1), dtype="uint8" ) lv2: R.Tensor((3, 1), dtype="uint8") = R.expand_dims(lv, axis=[-1]) lv3: R.Tensor((3, 5), dtype="bool") = R.equal(lv2, lv1) lv4: R.Tensor((3, 5), dtype="float32") = R.astype(lv3, dtype="float32") gv: R.Tuple(R.Tensor((3, 5), dtype="float32")) = (lv4,) R.output(gv) return gv class Eye2(Module): def forward(self, input): return torch.eye(5, dtype=torch.float32) @tvm.script.ir_module class Expected2: @R.function def main(input: R.Tensor((5,), dtype="float32")) -> R.Tuple( R.Tensor((5, 5), dtype="float32") ): with R.dataflow(): lv: R.Tensor((5,), dtype="uint8") = R.arange( R.prim_value(0), R.prim_value(5), R.prim_value(1), dtype="uint8" ) lv1: R.Tensor((5,), dtype="uint8") = R.arange( R.prim_value(0), R.prim_value(5), R.prim_value(1), dtype="uint8" ) lv2: R.Tensor((5, 1), dtype="uint8") = R.expand_dims(lv, axis=[-1]) lv3: R.Tensor((5, 5), dtype="bool") = R.equal(lv2, lv1) lv4: R.Tensor((5, 5), dtype="float32") = R.astype(lv3, dtype="float32") gv: R.Tuple(R.Tensor((5, 5), dtype="float32")) = (lv4,) R.output(gv) return gv example_args1 = (torch.randn(3, 5, dtype=torch.float32),) verify_model(Eye1(), example_args1, {}, Expected1) example_args2 = (torch.randn(5, dtype=torch.float32),) verify_model(Eye2(), example_args2, {}, Expected2) def test_cross_entropy(): class CrossEntropyModule(Module): def __init__(self): super().__init__() self.criterion = nn.CrossEntropyLoss() self.target = torch.tensor([0, 1, 2, 1]) def forward(self, x): return self.criterion(x, self.target) @tvm.script.ir_module class Expected1: @R.function def main(x: R.Tensor((4, 3), dtype="float32")) -> R.Tuple(R.Tensor((), dtype="float32")): with R.dataflow(): lv: R.Tensor((4, 3), dtype="float32") = R.nn.log_softmax(x, axis=1) lv1: R.Tensor((4,), dtype="bool") = R.not_equal( R.const([0, 1, 2, 1], dtype="int64"), R.const(-100, "int64") ) lv2: R.Tensor((), dtype="int64") = R.const(0, "int64") lv3: R.Tensor((4,), dtype="int64") = R.where( lv1, R.const([0, 1, 2, 1], dtype="int64"), lv2 ) lv4: R.Tensor((4, 1), dtype="int64") = R.expand_dims(lv3, axis=[1]) lv5: R.Tensor((4, 1), dtype="float32") = R.gather_elements(lv, lv4, axis=1) lv6: R.Tensor((4,), dtype="float32") = R.squeeze(lv5, axis=[1]) lv7: R.Tensor((4,), dtype="float32") = R.negative(lv6) lv8: R.Tensor((4,), dtype="bool") = R.not_equal( R.const([0, 1, 2, 1], dtype="int64"), R.const(-100, "int64") ) lv9: R.Tensor((), dtype="float32") = R.const(0.0, "float32") lv10: R.Tensor((4,), dtype="float32") = R.where(lv8, lv7, lv9) lv11: R.Tensor((4,), dtype="bool") = R.not_equal( R.const([0, 1, 2, 1], dtype="int64"), R.const(-100, "int64") ) lv12: R.Tensor((4,), dtype="int64") = R.astype(lv11, dtype="int64") lv13: R.Tensor((), dtype="int64") = R.sum(lv12, axis=None, keepdims=False) lv14: R.Tensor((), dtype="float32") = R.astype(lv13, dtype="float32") lv15: R.Tensor((), dtype="float32") = R.sum(lv10, axis=None, keepdims=False) lv16: R.Tensor((), dtype="float32") = R.divide(lv15, lv14) gv: R.Tuple(R.Tensor((), dtype="float32")) = (lv16,) R.output(gv) return gv example_args1 = (torch.randn(4, 3, dtype=torch.float32),) verify_model(CrossEntropyModule(), example_args1, {}, Expected1) def test_linspace(): class Linspace(Module): def forward(self, input): return torch.linspace(0, 1, steps=9, dtype=torch.float32) @tvm.script.ir_module class Expected: @R.function def main(input: R.Tensor((9, 9), dtype="float32")) -> R.Tuple( R.Tensor((9,), dtype="float32") ): with R.dataflow(): lv: R.Tensor((9,), dtype="int64") = R.arange( R.prim_value(0), R.prim_value(9), R.prim_value(1), dtype="int64" ) lv1: R.Tensor((9,), dtype="bool") = R.less(lv, R.const(4, "int64")) lv2: R.Tensor((9,), dtype="float32") = R.astype(lv, dtype="float32") lv3: R.Tensor((9,), dtype="float32") = R.multiply(lv2, R.const(0.125, "float32")) lv4: R.Tensor((9,), dtype="float32") = R.add(lv3, R.const(0.0, "float32")) lv5: R.Tensor((9,), dtype="int64") = R.subtract(R.const(8, "int64"), lv) lv6: R.Tensor((9,), dtype="float32") = R.astype(lv5, dtype="float32") lv7: R.Tensor((9,), dtype="float32") = R.multiply(lv6, R.const(0.125, "float32")) lv8: R.Tensor((9,), dtype="float32") = R.subtract(R.const(1.0, "float32"), lv7) lv9: R.Tensor((9,), dtype="float32") = R.where(lv1, lv4, lv8) gv: R.Tuple(R.Tensor((9,), dtype="float32")) = (lv9,) R.output(gv) return gv example_args = (torch.randn(9, 9, dtype=torch.float32),) verify_model(Linspace(), example_args, {}, Expected) @pytest.mark.parametrize( "torch_dtype, relax_dtype", [ (torch.float32, "float32"), (torch.float16, "float16"), (torch.bfloat16, "bfloat16"), (torch.int64, "int64"), (torch.int32, "int32"), (torch.bool, "bool"), ], ) def test_dtypes(torch_dtype, relax_dtype): example_args = ( torch.testing.make_tensor((10, 10), dtype=torch_dtype, device="cpu", low=0, high=10), torch.testing.make_tensor((10, 10), dtype=torch_dtype, device="cpu", low=0, high=10), ) class Model(Module): def forward(self, lhs: torch.Tensor, rhs: torch.Tensor): return torch.ops.aten.add(lhs, rhs) @tvm.script.ir_module class Expected: @R.function def main( lhs: R.Tensor((10, 10), dtype=relax_dtype), rhs: R.Tensor((10, 10), dtype=relax_dtype), ) -> R.Tuple(R.Tensor((10, 10), dtype=relax_dtype)): with R.dataflow(): lv: R.Tensor((10, 10), dtype=relax_dtype) = relax.op.add(lhs, rhs) gv: R.Tuple(R.Tensor((10, 10), dtype=relax_dtype)) = (lv,) R.output(gv) return gv verify_model(Model(), example_args, {}, Expected) def test_mm(): class MatrixMultiply(Module): def forward(self, a, b): return torch.mm(a, b) example_args = ( torch.randn(2, 3, dtype=torch.float32), torch.randn(3, 4, dtype=torch.float32), ) @tvm.script.ir_module class Expected: @R.function def main( a: R.Tensor((2, 3), dtype="float32"), b: R.Tensor((3, 4), dtype="float32"), ) -> R.Tuple(R.Tensor((2, 4), dtype="float32")): with R.dataflow(): lv: R.Tensor((2, 4), dtype="float32") = R.matmul(a, b, out_dtype="float32") gv: R.Tuple(R.Tensor((2, 4), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(MatrixMultiply(), example_args, {}, Expected) def test_sparse_mm(): class SparseMatrixMultiply(Module): def forward(self, sparse_input, dense_input): return torch.sparse.mm(sparse_input, dense_input) indices = torch.tensor([[0, 1, 2], [2, 0, 1]]) values = torch.tensor([1.0, 2.0, 3.0], dtype=torch.float32) sparse_input = torch.sparse_coo_tensor(indices, values, size=(3, 100)) dense_input = torch.randn(100, 50, dtype=torch.float32) example_args = (sparse_input, dense_input) @tvm.script.ir_module class Expected: @R.function def main( sparse_input: R.Tensor((3, 100), dtype="float32"), dense_input: R.Tensor((100, 50), dtype="float32"), ) -> R.Tuple(R.Tensor((3, 50), dtype="float32")): with R.dataflow(): lv: R.Tensor((3, 50), dtype="float32") = R.full( R.shape([3, 50]), R.const(0.0, "float32"), dtype="float32" ) lv1: R.Tensor((3, 50), dtype="float32") = R.matmul( sparse_input, dense_input, out_dtype="float32" ) gv: R.Tuple(R.Tensor((3, 50), dtype="float32")) = (lv1,) R.output(gv) return gv verify_model(SparseMatrixMultiply(), example_args, {}, Expected) @pytest.mark.skipif(not env.has_llvm(), reason="need llvm") def test_lstm(): class LSTM(nn.Module): def __init__(self, input_size, hidden_size, batch_first, bidirectional): super().__init__() self.lstm = nn.LSTM( input_size=input_size, hidden_size=hidden_size, num_layers=1, batch_first=batch_first, bidirectional=bidirectional, ) def forward(self, x): y, _ = self.lstm(x) return y # Unidirectional LSTM with batch_first=True torch.manual_seed(42) x = torch.randn(2, 3, 4, dtype=torch.float32) verify_model_numerically(LSTM(4, 8, batch_first=True, bidirectional=False), (x,)) # Unidirectional LSTM with batch_first=False torch.manual_seed(43) x2 = torch.randn(4, 2, 3, dtype=torch.float32) verify_model_numerically(LSTM(3, 6, batch_first=False, bidirectional=False), (x2,)) # Bidirectional LSTM with batch_first=True torch.manual_seed(44) x3 = torch.randn(2, 3, 4, dtype=torch.float32) verify_model_numerically(LSTM(4, 8, batch_first=True, bidirectional=True), (x3,)) # Bidirectional LSTM with batch_first=False torch.manual_seed(45) x4 = torch.randn(4, 2, 3, dtype=torch.float32) verify_model_numerically(LSTM(3, 6, batch_first=False, bidirectional=True), (x4,)) def test_tensor_none_tuple(): example_args = (torch.tensor([1.0, 2.0, 3.0]),) class TensorNoneModel(Module): def forward(self, x): return x + 1, None @tvm.script.ir_module class Expected: @R.function def main(x: R.Tensor((3,), dtype="float32")) -> R.Tuple( R.Tensor((3,), dtype="float32"), R.Any ): with R.dataflow(): lv: R.Tensor((3,), dtype="float32") = R.add(x, R.const(1.0, "float32")) gv: R.Tuple(R.Tensor((3,), dtype="float32"), R.Any) = (lv, R.null_value()) R.output(gv) return gv verify_model(TensorNoneModel(), example_args, {}, Expected) def test_gru(): class BasicGRU(nn.Module): def __init__(self): super().__init__() self.gru = nn.GRU( input_size=4, hidden_size=8, num_layers=1, batch_first=True, bidirectional=False, ) def forward(self, x): y, _ = self.gru(x) return y torch.manual_seed(42) x = torch.randn(2, 3, 4, dtype=torch.float32) model = BasicGRU() with torch.no_grad(): pytorch_output = model(x) exported_program = export(model, args=(x,)) mod = from_exported_program(exported_program) target = tvm.target.Target("llvm") ex = relax.build(mod, target) vm = relax.VirtualMachine(ex, tvm.cpu()) x_tvm = tvm.runtime.tensor(x.numpy()) tvm_output = vm["main"](x_tvm) if hasattr(tvm_output, "numpy"): tvm_output_np = tvm_output.numpy() else: tvm_output_np = tvm_output[0].numpy() assert pytorch_output.shape == tvm_output_np.shape, ( f"Shape mismatch: PyTorch {pytorch_output.shape} vs TVM {tvm_output_np.shape}" ) tvm.testing.assert_allclose(pytorch_output.numpy(), tvm_output_np, rtol=1e-4, atol=1e-5) class SeqFirstGRU(nn.Module): def __init__(self): super().__init__() self.gru = nn.GRU( input_size=3, hidden_size=6, num_layers=1, batch_first=False, bidirectional=False, ) def forward(self, x): y, _ = self.gru(x) return y torch.manual_seed(43) x2 = torch.randn(4, 2, 3, dtype=torch.float32) model2 = SeqFirstGRU() with torch.no_grad(): pytorch_output2 = model2(x2) exported_program2 = export(model2, args=(x2,)) mod2 = from_exported_program(exported_program2) ex2 = relax.build(mod2, target) vm2 = relax.VirtualMachine(ex2, tvm.cpu()) x2_tvm = tvm.runtime.tensor(x2.numpy()) tvm_output2 = vm2["main"](x2_tvm) if hasattr(tvm_output2, "numpy"): tvm_output2_np = tvm_output2.numpy() else: tvm_output2_np = tvm_output2[0].numpy() assert pytorch_output2.shape == tvm_output2_np.shape tvm.testing.assert_allclose(pytorch_output2.numpy(), tvm_output2_np, rtol=1e-4, atol=1e-5) # Test bidirectional GRU with batch_first=True class BidirectionalGRU(nn.Module): def __init__(self): super().__init__() self.gru = nn.GRU( input_size=4, hidden_size=5, num_layers=1, batch_first=True, bidirectional=True, ) def forward(self, x): y, _ = self.gru(x) return y torch.manual_seed(44) x3 = torch.randn(2, 3, 4, dtype=torch.float32) model3 = BidirectionalGRU() with torch.no_grad(): pytorch_output3 = model3(x3) # Verify output shape is correct (hidden_size * 2 due to bidirectional) assert pytorch_output3.shape == ( 2, 3, 10, ), f"Expected shape (2, 3, 10), got {pytorch_output3.shape}" exported_program3 = export(model3, args=(x3,)) mod3 = from_exported_program(exported_program3) ex3 = relax.build(mod3, target) vm3 = relax.VirtualMachine(ex3, tvm.cpu()) x3_tvm = tvm.runtime.tensor(x3.numpy()) tvm_output3 = vm3["main"](x3_tvm) if hasattr(tvm_output3, "numpy"): tvm_output3_np = tvm_output3.numpy() else: tvm_output3_np = tvm_output3[0].numpy() assert pytorch_output3.shape == tvm_output3_np.shape, ( f"Shape mismatch: PyTorch {pytorch_output3.shape} vs TVM {tvm_output3_np.shape}" ) tvm.testing.assert_allclose(pytorch_output3.numpy(), tvm_output3_np, rtol=1e-4, atol=1e-5) # Test bidirectional GRU with batch_first=False class SeqFirstBidirectionalGRU(nn.Module): def __init__(self): super().__init__() self.gru = nn.GRU( input_size=3, hidden_size=4, num_layers=1, batch_first=False, bidirectional=True, ) def forward(self, x): y, _ = self.gru(x) return y torch.manual_seed(45) x4 = torch.randn(4, 2, 3, dtype=torch.float32) # (seq_len, batch, input_size) model4 = SeqFirstBidirectionalGRU() with torch.no_grad(): pytorch_output4 = model4(x4) # Verify output shape (seq_len, batch, hidden_size * 2) assert pytorch_output4.shape == ( 4, 2, 8, ), f"Expected shape (4, 2, 8), got {pytorch_output4.shape}" exported_program4 = export(model4, args=(x4,)) mod4 = from_exported_program(exported_program4) ex4 = relax.build(mod4, target) vm4 = relax.VirtualMachine(ex4, tvm.cpu()) x4_tvm = tvm.runtime.tensor(x4.numpy()) tvm_output4 = vm4["main"](x4_tvm) if hasattr(tvm_output4, "numpy"): tvm_output4_np = tvm_output4.numpy() else: tvm_output4_np = tvm_output4[0].numpy() assert pytorch_output4.shape == tvm_output4_np.shape tvm.testing.assert_allclose(pytorch_output4.numpy(), tvm_output4_np, rtol=1e-4, atol=1e-5) @pytest.mark.skipif(not env.has_llvm(), reason="need llvm") def test_rnn_tanh(): target = tvm.target.Target("llvm") def _check(rnn_kwargs, x_shape, seed): class RNNWithState(nn.Module): def __init__(self): super().__init__() self.rnn = nn.RNN(nonlinearity="tanh", num_layers=1, **rnn_kwargs) def forward(self, x): output, h_n = self.rnn(x) return output, h_n torch.manual_seed(seed) x = torch.randn(*x_shape, dtype=torch.float32) model = RNNWithState() with torch.no_grad(): pt_out, pt_hn = model(x) exported_program = export(model, args=(x,)) mod = from_exported_program(exported_program, run_ep_decomposition=False) ex = relax.build(mod, target) vm = relax.VirtualMachine(ex, tvm.cpu()) tvm_outputs = vm["main"](tvm.runtime.tensor(x.numpy())) tvm_out_np = tvm_outputs[0].numpy() tvm_hn_np = tvm_outputs[1].numpy() assert pt_out.shape == tvm_out_np.shape, ( f"output shape mismatch: PyTorch {tuple(pt_out.shape)} vs TVM {tvm_out_np.shape}" ) assert pt_hn.shape == tvm_hn_np.shape, ( f"h_n shape mismatch: PyTorch {tuple(pt_hn.shape)} vs TVM {tvm_hn_np.shape}" ) tvm.testing.assert_allclose(pt_out.numpy(), tvm_out_np, rtol=1e-4, atol=1e-5) tvm.testing.assert_allclose(pt_hn.numpy(), tvm_hn_np, rtol=1e-4, atol=1e-5) # batch_first, unidirectional _check( {"input_size": 4, "hidden_size": 8, "batch_first": True, "bidirectional": False}, (2, 3, 4), seed=42, ) # seq-first (batch_first=False), unidirectional _check( {"input_size": 3, "hidden_size": 6, "batch_first": False, "bidirectional": False}, (4, 2, 3), seed=43, ) # bidirectional, batch_first _check( {"input_size": 4, "hidden_size": 8, "batch_first": True, "bidirectional": True}, (2, 3, 4), seed=44, ) def test_dynamic_shape_with_range_constraints(): class DynamicModel(torch.nn.Module): def forward(self, x1, x2): return torch.ops.aten.add.Tensor(x1, x2) @I.ir_module class Expected: @R.function def main( x1: R.Tensor(("s0", 4), dtype="float32"), x2: R.Tensor(("s0", 4), dtype="float32") ) -> R.Tuple(R.Tensor(("s0", 4), dtype="float32")): s0 = T.int64() R.func_attr({"tir_var_lower_bound": {"s24": 1}, "tir_var_upper_bound": {"s24": 64}}) with R.dataflow(): lv: R.Tensor((s0, 4), dtype="float32") = R.add(x1, x2) gv: R.Tuple(R.Tensor((s0, 4), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(8, 4), torch.randn(8, 4)) batch = torch.export.Dim("batch", min=1, max=64) dynamic_shapes = {"x1": {0: batch}, "x2": {0: batch}} verify_model( DynamicModel(), example_args, {}, Expected, dynamic_shapes=dynamic_shapes, map_free_vars=True, ) def test_dynamic_shape_with_addition_constraints(): class ConcatModel(torch.nn.Module): def forward(self, x, y): return torch.cat([x, y], dim=0) @I.ir_module class Expected: @R.function def main( x: R.Tensor(("s0", 4), dtype="float32"), y: R.Tensor(("s0___1", 4), dtype="float32") ) -> R.Tuple(R.Tensor(("s0 + s0___1", 4), dtype="float32")): s0 = T.int64() s0___1 = T.int64() R.func_attr( { "tir_var_lower_bound": {"s77": 1, "s77___1": 2}, "tir_var_upper_bound": {"s77": 64, "s77___1": 65}, } ) with R.dataflow(): lv: R.Tensor((s0 + s0___1, 4), dtype="float32") = R.concat((x, y), axis=0) gv: R.Tuple(R.Tensor((s0 + s0___1, 4), dtype="float32")) = (lv,) R.output(gv) return gv batch = torch.export.Dim("batch", min=1, max=64) example_args = (torch.randn(8, 4), torch.randn(9, 4)) dynamic_shapes = {"x": {0: batch}, "y": {0: batch + 1}} verify_model( ConcatModel(), example_args, {}, Expected, dynamic_shapes=dynamic_shapes, map_free_vars=True ) def test_dynamic_shape_with_subtraction_constraints(): class ConcatModel(torch.nn.Module): def forward(self, x, y): return torch.cat([x, y], dim=0) @I.ir_module class Expected: @R.function def main( x: R.Tensor(("s0___1", 4), dtype="float32"), y: R.Tensor(("s0", 4), dtype="float32") ) -> R.Tuple(R.Tensor(("s0___1 + s0", 4), dtype="float32")): s0___1 = T.int64() s0 = T.int64() R.func_attr( { "tir_var_lower_bound": {"s17": 0, "s17___1": 1}, "tir_var_upper_bound": {"s17": 63, "s17___1": 64}, } ) with R.dataflow(): lv: R.Tensor((s0___1 + s0, 4), dtype="float32") = R.concat((x, y), axis=0) gv: R.Tuple(R.Tensor((s0___1 + s0, 4), dtype="float32")) = (lv,) R.output(gv) return gv batch = torch.export.Dim("batch", min=1, max=64) example_args = (torch.randn(8, 4), torch.randn(7, 4)) dynamic_shapes = {"x": {0: batch}, "y": {0: batch - 1}} verify_model( ConcatModel(), example_args, {}, Expected, dynamic_shapes=dynamic_shapes, map_free_vars=True ) def test_dynamic_shape_with_multiplication_constraints(): class ConcatModel(torch.nn.Module): def forward(self, x, y): return torch.cat([x, y], dim=0) @I.ir_module class Expected: @R.function def main( x: R.Tensor(("s0", 4), dtype="float32"), y: R.Tensor(("s0_2", 4), dtype="float32") ) -> R.Tuple(R.Tensor(("s0 + s0_2", 4), dtype="float32")): s0 = T.int64() s0_2 = T.int64() R.func_attr( { "tir_var_lower_bound": {"s77": 1, "s77_2": 2}, "tir_var_upper_bound": {"s77": 64, "s77_2": 128}, } ) with R.dataflow(): lv: R.Tensor((s0 + s0_2, 4), dtype="float32") = R.concat((x, y), axis=0) gv: R.Tuple(R.Tensor((s0 + s0_2, 4), dtype="float32")) = (lv,) R.output(gv) return gv batch = torch.export.Dim("batch", min=1, max=64) example_args = (torch.randn(8, 4), torch.randn(16, 4)) dynamic_shapes = {"x": {0: batch}, "y": {0: batch * 2}} verify_model( ConcatModel(), example_args, {}, Expected, dynamic_shapes=dynamic_shapes, map_free_vars=True ) def test_dynamic_shape_with_unbounded_constraints(): class DynamicModel(torch.nn.Module): def forward(self, x): return torch.ops.aten.add.Tensor(x, x) @I.ir_module class Expected: @R.function def main(x: R.Tensor(("s0", 4), dtype="float32")) -> R.Tuple( R.Tensor(("s0", 4), dtype="float32") ): s0 = T.int64() R.func_attr({"tir_var_lower_bound": {"s77": 2}}) with R.dataflow(): lv: R.Tensor((s0, 4), dtype="float32") = R.add(x, x) gv: R.Tuple(R.Tensor((s0, 4), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(8, 4),) batch = torch.export.Dim("batch", min=2) dynamic_shapes = {"x": {0: batch}} verify_model( DynamicModel(), example_args, {}, Expected, dynamic_shapes=dynamic_shapes, map_free_vars=True, ) def test_sym_size_int(): class SymSizeInt(Module): def __init__(self, dim): super().__init__() self.dim = dim def forward(self, x): # TODO(@mshr-h): `torch.ops.aten.sym_size.int(x, self.dim)` would be ideal, but currently # the ep frontend is not able to handle it. return torch.add(x[0], torch.ops.aten.sym_size.int(x, self.dim)) @I.ir_module class Expected1: @R.function def main(x: R.Tensor((1, 3, 4), dtype="float32")) -> R.Tuple( R.Tensor((3, 4), dtype="float32") ): with R.dataflow(): lv: R.Tensor((3, 4), dtype="float32") = R.take( x, R.const(0, "int64"), axis=0, mode="fast" ) lv1: R.Tensor((3, 4), dtype="float32") = R.add(lv, R.const(3.0, "float32")) gv: R.Tuple(R.Tensor((3, 4), dtype="float32")) = (lv1,) R.output(gv) return gv example_args_1 = (torch.randn(1, 3, 4),) verify_model(SymSizeInt(dim=1), example_args_1, {}, Expected1) verify_model(SymSizeInt(dim=-2), example_args_1, {}, Expected1) class SymSizeIntDynamic(Module): def __init__(self, dim): super().__init__() self.dim = dim def forward(self, x): shape_dim = torch.ops.aten.sym_size.int(x, self.dim) return x.reshape(shape_dim, -1) @I.ir_module class Expected2: @R.function def main(x: R.Tensor(("s0", 3, 4), dtype="float32")) -> R.Tuple( R.Tensor(("s0", 12), dtype="float32") ): s0 = T.int64() R.func_attr({"tir_var_lower_bound": {"s77": 0}}) with R.dataflow(): lv: R.Tensor((s0, 12), dtype="float32") = R.reshape(x, R.shape([s0, 12])) gv: R.Tuple(R.Tensor((s0, 12), dtype="float32")) = (lv,) R.output(gv) return gv example_args_2 = (torch.randn(2, 3, 4),) dynamic_shapes = {"x": {0: torch.export.Dim("dim")}} verify_model( SymSizeIntDynamic(dim=0), example_args_2, {}, Expected2, dynamic_shapes=dynamic_shapes, map_free_vars=True, ) def test_exponential(): class Exponential(Module): def forward(self, x): return x.exponential_() @I.ir_module class Expected: @R.function def main(x: R.Tensor((4, 8), dtype="float32")) -> R.Tuple( R.Tensor((4, 8), dtype="float32") ): with R.dataflow(): lv: R.Tensor((4, 8), dtype="float32") = R.zeros_like(x) gv: R.Tuple(R.Tensor((4, 8), dtype="float32")) = (lv,) R.output(gv) return gv example_args = (torch.randn(4, 8, dtype=torch.float32),) verify_model(Exponential(), example_args, {}, Expected) def test_max_dim(): class MaxDim1(Module): def forward(self, x): return torch.max(x, dim=1) class MaxDim2(Module): def forward(self, x): return torch.max(x, dim=1, keepdim=True) @I.ir_module class expected1: @R.function def main(x: R.Tensor((4, 8, 16), dtype="float32")) -> R.Tuple( R.Tensor((4, 16), dtype="float32"), R.Tensor((4, 16), dtype="int64") ): with R.dataflow(): lv: R.Tuple( R.Tensor((4, 1, 16), dtype="float32"), R.Tensor((4, 1, 16), dtype="int64") ) = R.topk(x, k=1, axis=1, ret_type="both", largest=True, dtype="int64") lv1: R.Tensor((4, 1, 16), dtype="float32") = lv[0] lv2: R.Tensor((4, 16), dtype="float32") = R.squeeze(lv1, axis=[1]) lv3: R.Tensor((4, 1, 16), dtype="int64") = lv[1] lv4: R.Tensor((4, 16), dtype="int64") = R.squeeze(lv3, axis=[1]) lv5: R.Tuple( R.Tensor((4, 16), dtype="float32"), R.Tensor((4, 16), dtype="int64") ) = (lv2, lv4) lv6: R.Tensor((4, 16), dtype="float32") = lv5[0] lv7: R.Tensor((4, 16), dtype="int64") = lv5[1] gv: R.Tuple( R.Tensor((4, 16), dtype="float32"), R.Tensor((4, 16), dtype="int64") ) = (lv6, lv7) R.output(gv) return gv @I.ir_module class expected2: @R.function def main(x: R.Tensor((4, 8, 16), dtype="float32")) -> R.Tuple( R.Tensor((4, 1, 16), dtype="float32"), R.Tensor((4, 1, 16), dtype="int64") ): with R.dataflow(): lv: R.Tuple( R.Tensor((4, 1, 16), dtype="float32"), R.Tensor((4, 1, 16), dtype="int64") ) = R.topk(x, k=1, axis=1, ret_type="both", largest=True, dtype="int64") lv1: R.Tensor((4, 1, 16), dtype="float32") = lv[0] lv2: R.Tensor((4, 1, 16), dtype="int64") = lv[1] lv3: R.Tuple( R.Tensor((4, 1, 16), dtype="float32"), R.Tensor((4, 1, 16), dtype="int64") ) = (lv1, lv2) lv4: R.Tensor((4, 1, 16), dtype="float32") = lv3[0] lv5: R.Tensor((4, 1, 16), dtype="int64") = lv3[1] gv: R.Tuple( R.Tensor((4, 1, 16), dtype="float32"), R.Tensor((4, 1, 16), dtype="int64") ) = (lv4, lv5) R.output(gv) return gv example_args = (torch.randn(4, 8, 16, dtype=torch.float32),) verify_model(MaxDim1(), example_args, {}, expected1) verify_model(MaxDim2(), example_args, {}, expected2) def test_alias(): class Alias(Module): def forward(self, x): return torch.ops.aten.alias(x) @I.ir_module class Expected: @R.function def main(x: R.Tensor((4, 8), dtype="float32")) -> R.Tuple( R.Tensor((4, 8), dtype="float32") ): with R.dataflow(): gv: R.Tuple(R.Tensor((4, 8), dtype="float32")) = (x,) R.output(gv) return gv example_args = (torch.randn(4, 8, dtype=torch.float32),) verify_model(Alias(), example_args, {}, Expected) def test_scatter_value(): class ScatterValue(Module): def forward(self, x, index): return x.scatter(1, index, 0.5) @I.ir_module class Expected: @R.function def main( x: R.Tensor((4, 8), dtype="float32"), index: R.Tensor((4, 2), dtype="int64"), ) -> R.Tuple(R.Tensor((4, 8), dtype="float32")): with R.dataflow(): lv: R.Tensor((4, 2), dtype="float32") = R.broadcast_to( R.const(0.5, "float32"), R.shape([4, 2]) ) lv1: R.Tensor((4, 8), dtype="float32") = R.scatter_elements(x, index, lv, axis=1) gv: R.Tuple(R.Tensor((4, 8), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = ( torch.randn(4, 8, dtype=torch.float32), torch.randint(0, 8, (4, 2), dtype=torch.int64), ) verify_model(ScatterValue(), example_args, {}, Expected) def test_grid_sample(): class GridSample(Module): def forward(self, input, grid): return torch.nn.functional.grid_sample( input, grid, mode="bilinear", padding_mode="zeros", align_corners=True ) @tvm.script.ir_module class expected: @R.function def main( input_1: R.Tensor((1, 3, 4, 4), dtype="float32"), grid: R.Tensor((1, 2, 2, 2), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 3, 2, 2), dtype="float32")): with R.dataflow(): lv: R.Tensor((1, 3, 2, 2), dtype="float32") = R.image.grid_sample( input_1, grid, method="bilinear", layout="NCHW", padding_mode="zeros", align_corners=True, ) gv: R.Tuple(R.Tensor((1, 3, 2, 2), dtype="float32")) = (lv,) R.output(gv) return gv example_args = ( torch.randn(1, 3, 4, 4, dtype=torch.float32), torch.randn(1, 2, 2, 2, dtype=torch.float32), ) verify_model(GridSample(), example_args, {}, expected) def test_torchvision_roi_align(): torchvision = pytest.importorskip("torchvision") class ROIAlign(Module): def forward(self, input, rois): return torchvision.ops.roi_align( input, rois, output_size=(3, 3), spatial_scale=1.0, sampling_ratio=2, aligned=False, ) @tvm.script.ir_module class expected: @R.function def main( input_1: R.Tensor((1, 3, 8, 8), dtype="float32"), rois: R.Tensor((2, 5), dtype="float32"), ) -> R.Tuple(R.Tensor((2, 3, 3, 3), dtype="float32")): with R.dataflow(): lv: R.Tensor((2, 3, 3, 3), dtype="float32") = R.vision.roi_align( input_1, rois, pooled_size=(3, 3), spatial_scale=1.0, sample_ratio=2, layout="NCHW", mode="avg", ) gv: R.Tuple(R.Tensor((2, 3, 3, 3), dtype="float32")) = (lv,) R.output(gv) return gv example_args = ( torch.randn(1, 3, 8, 8, dtype=torch.float32), torch.tensor([[0.0, 1.0, 1.0, 6.0, 6.0], [0.0, 0.5, 0.5, 7.0, 7.0]], dtype=torch.float32), ) verify_model(ROIAlign(), example_args, {}, expected) def test_torchvision_roi_align_aligned(): torchvision = pytest.importorskip("torchvision") class ROIAlign(Module): def forward(self, input, rois): return torchvision.ops.roi_align( input, rois, output_size=(1, 1), spatial_scale=1.0, sampling_ratio=2, aligned=True, ) example_args = ( torch.arange(16, dtype=torch.float32).reshape(1, 1, 4, 4), torch.tensor([[0.0, 1.0, 1.0, 1.2, 1.2]], dtype=torch.float32), ) verify_model_numerically(ROIAlign(), example_args, rtol=1e-5, atol=1e-5) def test_upsample_nearest2d(): class UpsampleNearest2dScale(Module): def forward(self, input): return torch.nn.functional.interpolate(input, scale_factor=2.0, mode="nearest") class UpsampleNearest2dSize(Module): def forward(self, input): return torch.nn.functional.interpolate(input, size=(20, 20), mode="nearest") example_args = (torch.randn(1, 3, 10, 10, dtype=torch.float32),) @tvm.script.ir_module class expected_scale: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 20, 20), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 20, 20), dtype="float32") = R.image.resize2d( input_1, size=(20, 20), layout="NCHW", method="nearest_neighbor", coordinate_transformation_mode="half_pixel", ) gv: R.Tuple(R.Tensor((1, 3, 20, 20), dtype="float32")) = (lv,) R.output(gv) return gv @tvm.script.ir_module class expected_size: @R.function def main(input_1: R.Tensor((1, 3, 10, 10), dtype="float32")) -> R.Tuple( R.Tensor((1, 3, 20, 20), dtype="float32") ): with R.dataflow(): lv: R.Tensor((1, 3, 20, 20), dtype="float32") = R.image.resize2d( input_1, size=(20, 20), layout="NCHW", method="nearest_neighbor", coordinate_transformation_mode="half_pixel", ) gv: R.Tuple(R.Tensor((1, 3, 20, 20), dtype="float32")) = (lv,) R.output(gv) return gv verify_model(UpsampleNearest2dScale(), example_args, {}, expected_scale) verify_model(UpsampleNearest2dSize(), example_args, {}, expected_size) def test_from_exported_program_sparse_csr_buffer(): class SparseCsrBufferModule(nn.Module): def __init__(self): super().__init__() crow_indices = torch.tensor([0, 1, 2], dtype=torch.int64) col_indices = torch.tensor([0, 1], dtype=torch.int64) values = torch.tensor([1.0, 1.0], dtype=torch.float32, requires_grad=True) csr_tensor = torch.sparse_csr_tensor( crow_indices, col_indices, values, dtype=torch.float32 ) self.register_buffer("csr_tensor", csr_tensor) self.csr_tensor.requires_grad_(True) def forward(self, x): csr2 = self.csr_tensor.to_sparse(layout=torch.sparse_csr) y = torch.matmul(csr2, x) return y.sum() model = SparseCsrBufferModule().eval() x = torch.ones((2, 1), dtype=torch.float32) exported_program = export(model, (x,)) mod = from_exported_program(exported_program) assert isinstance(mod, tvm.IRModule) def test_cond_basic(): """Basic data-dependent cond with runtime predicate.""" class CondModel(Module): def forward(self, x): def true_fn(x): return x.cos() def false_fn(x): return x.sin() return torch.cond(x.sum() > 0, true_fn, false_fn, (x,)) @tvm.script.ir_module class expected: @R.function def cond_true_branch_0( x: R.Tensor((3, 4), dtype="float32"), ) -> R.Tensor((3, 4), dtype="float32"): gv: R.Tensor((3, 4), dtype="float32") = R.cos(x) gv1: R.Tensor((3, 4), dtype="float32") = gv return gv1 @R.function def cond_false_branch_1( x: R.Tensor((3, 4), dtype="float32"), ) -> R.Tensor((3, 4), dtype="float32"): gv: R.Tensor((3, 4), dtype="float32") = R.sin(x) gv1: R.Tensor((3, 4), dtype="float32") = gv return gv1 @R.function def main( x: R.Tensor((3, 4), dtype="float32"), ) -> R.Tuple(R.Tensor((3, 4), dtype="float32")): cls = expected gv: R.Tensor((), dtype="float32") = R.sum(x, axis=None, keepdims=False) gv1: R.Tensor((), dtype="bool") = R.greater(gv, R.const(0.0, "float32")) if gv1: gv2: R.Tensor((3, 4), dtype="float32") = cls.cond_true_branch_0(x) cond_result: R.Tensor((3, 4), dtype="float32") = gv2 else: gv3: R.Tensor((3, 4), dtype="float32") = cls.cond_false_branch_1(x) cond_result: R.Tensor((3, 4), dtype="float32") = gv3 return (cond_result,) verify_model(CondModel(), (torch.randn(3, 4),), {}, expected, map_free_vars=True) def test_cond_shape_predicate(): """Cond with a shape-derived predicate and dynamic shapes.""" class CondShapeModel(Module): def forward(self, x): def true_fn(x): return x + 1.0 def false_fn(x): return x - 1.0 return torch.cond(x.shape[0] > 4, true_fn, false_fn, (x,)) @tvm.script.ir_module class expected: @R.function def cond_true_branch_0( x: R.Tensor(("s77", 4), dtype="float32"), ) -> R.Tensor(("s77", 4), dtype="float32"): s77 = T.int64() gv: R.Tensor((s77, 4), dtype="float32") = R.add(x, R.const(1.0, "float32")) gv1: R.Tensor((s77, 4), dtype="float32") = gv return gv1 @R.function def cond_false_branch_1( x: R.Tensor(("s77", 4), dtype="float32"), ) -> R.Tensor(("s77", 4), dtype="float32"): s77 = T.int64() gv: R.Tensor((s77, 4), dtype="float32") = R.subtract(x, R.const(1.0, "float32")) gv1: R.Tensor((s77, 4), dtype="float32") = gv return gv1 @R.function def main( x: R.Tensor(("s77", 4), dtype="float32"), ) -> R.Tuple(R.Tensor(("s77", 4), dtype="float32")): s77 = T.int64() R.func_attr({"tir_var_lower_bound": {"s77": 1}}) cls = expected gv: R.Tensor((), dtype="bool") = R.const(True, "bool") if gv: gv1: R.Tensor((s77, 4), dtype="float32") = cls.cond_true_branch_0(x) cond_result: R.Tensor((s77, 4), dtype="float32") = gv1 else: gv2: R.Tensor((s77, 4), dtype="float32") = cls.cond_false_branch_1(x) cond_result: R.Tensor((s77, 4), dtype="float32") = gv2 return (cond_result,) batch = torch.export.Dim("batch", min=1) verify_model( CondShapeModel(), (torch.randn(3, 4),), {}, expected, dynamic_shapes={"x": {0: batch}}, map_free_vars=True, ) def test_cond_tuple_output(): """Cond where both branches return a tuple.""" class CondTupleModel(Module): def forward(self, x): def true_fn(x): return (x.cos(), x.sin()) def false_fn(x): return (x.sin(), x.cos()) return torch.cond(x.sum() > 0, true_fn, false_fn, (x,)) @tvm.script.ir_module class expected: @R.function def cond_true_branch_0( x: R.Tensor((3, 4), dtype="float32"), ) -> R.Tuple(R.Tensor((3, 4), dtype="float32"), R.Tensor((3, 4), dtype="float32")): gv: R.Tensor((3, 4), dtype="float32") = R.cos(x) gv1: R.Tensor((3, 4), dtype="float32") = R.sin(x) gv2: R.Tensor((3, 4), dtype="float32") = gv gv3: R.Tensor((3, 4), dtype="float32") = gv1 return (gv2, gv3) @R.function def cond_false_branch_1( x: R.Tensor((3, 4), dtype="float32"), ) -> R.Tuple(R.Tensor((3, 4), dtype="float32"), R.Tensor((3, 4), dtype="float32")): gv: R.Tensor((3, 4), dtype="float32") = R.sin(x) gv1: R.Tensor((3, 4), dtype="float32") = R.cos(x) gv2: R.Tensor((3, 4), dtype="float32") = gv gv3: R.Tensor((3, 4), dtype="float32") = gv1 return (gv2, gv3) @R.function def main( x: R.Tensor((3, 4), dtype="float32"), ) -> R.Tuple(R.Tensor((3, 4), dtype="float32"), R.Tensor((3, 4), dtype="float32")): cls = expected gv: R.Tensor((), dtype="float32") = R.sum(x, axis=None, keepdims=False) gv1: R.Tensor((), dtype="bool") = R.greater(gv, R.const(0.0, "float32")) if gv1: gv2: R.Tuple( R.Tensor((3, 4), dtype="float32"), R.Tensor((3, 4), dtype="float32"), ) = cls.cond_true_branch_0(x) cond_result: R.Tuple( R.Tensor((3, 4), dtype="float32"), R.Tensor((3, 4), dtype="float32"), ) = gv2 else: gv3: R.Tuple( R.Tensor((3, 4), dtype="float32"), R.Tensor((3, 4), dtype="float32"), ) = cls.cond_false_branch_1(x) cond_result: R.Tuple( R.Tensor((3, 4), dtype="float32"), R.Tensor((3, 4), dtype="float32"), ) = gv3 gv4: R.Tensor((3, 4), dtype="float32") = cond_result[0] gv5: R.Tensor((3, 4), dtype="float32") = cond_result[1] return (gv4, gv5) verify_model(CondTupleModel(), (torch.randn(3, 4),), {}, expected, map_free_vars=True) def test_cond_nested(): """Nested cond: a cond inside one of the branches.""" class CondNestedModel(Module): def forward(self, x): def true_fn(x): def inner_true(x): return x * 2.0 def inner_false(x): return x * 3.0 return torch.cond(x.sum() > 1, inner_true, inner_false, (x,)) def false_fn(x): return x - 1.0 return torch.cond(x.sum() > 0, true_fn, false_fn, (x,)) example_args = (torch.randn(3, 4),) exported_program = export(CondNestedModel(), args=example_args) mod = from_exported_program(exported_program) assert isinstance(mod, tvm.IRModule) # Should have at least 4 branch functions (2 outer + 2 inner) plus main func_names = [gv.name_hint for gv in mod.get_global_vars()] branch_funcs = [n for n in func_names if n != "main"] assert len(branch_funcs) >= 4, ( f"Expected at least 4 branch functions for nested cond, got {branch_funcs}" ) # Verify no duplicate function names assert len(set(branch_funcs)) == len(branch_funcs), ( f"Duplicate branch function names: {branch_funcs}" ) def test_affine_grid(): class AffineGrid(Module): def forward(self, theta): return torch.nn.functional.affine_grid(theta, [1, 3, 16, 16], align_corners=True) @tvm.script.ir_module class expected: @R.function def main( theta: R.Tensor((1, 2, 3), dtype="float32"), ) -> R.Tuple(R.Tensor((1, 16, 16, 2), dtype="float32")): with R.dataflow(): lv: R.Tensor((1, 2, 16, 16), dtype="float32") = R.image.affine_grid( theta, size=(16, 16) ) lv1: R.Tensor((1, 16, 16, 2), dtype="float32") = R.permute_dims( lv, axes=[0, 2, 3, 1] ) gv: R.Tuple(R.Tensor((1, 16, 16, 2), dtype="float32")) = (lv1,) R.output(gv) return gv example_args = (torch.randn(1, 2, 3, dtype=torch.float32),) # Disable decomposition to keep aten.affine_grid_generator as a single op verify_model(AffineGrid(), example_args, {}, expected, run_ep_decomposition=False) def test_affine_grid_numerically(): """Verify affine_grid numerical correctness: PyTorch vs TVM via our converter.""" class AffineGrid(Module): def forward(self, theta): return torch.nn.functional.affine_grid(theta, [2, 3, 8, 12], align_corners=True) model = AffineGrid() example_args = (torch.randn(2, 2, 3, dtype=torch.float32),) with torch.no_grad(): pytorch_output = model(*example_args) exported_program = export(model, args=example_args) mod = from_exported_program(exported_program, run_ep_decomposition=False) exe = tvm.compile(mod, target="llvm") vm = relax.VirtualMachine(exe, tvm.cpu()) tvm_args = [tvm.runtime.tensor(arg.numpy()) for arg in example_args] tvm_output = vm["main"](*tvm_args) tvm_output_np = tvm_output[0].numpy() tvm.testing.assert_allclose(tvm_output_np, pytorch_output.numpy(), rtol=1e-5, atol=1e-5) if __name__ == "__main__": tvm.testing.main()