From 857cb68df6ef7a4b963275a8cd5ecbe4112d4a92 Mon Sep 17 00:00:00 2001 From: Vaclav Novak Date: Wed, 15 Jul 2026 08:49:45 +0200 Subject: [PATCH] feat: added support for `aten.conv_transpose2d` using new MLIR flow --- .../ops_converters/convolution_converter.py | 203 +-- .../node_converters/shared/conv_utils.py | 46 +- .../node_converter/test_conv_converter.py | 1494 +++++++---------- backends/nxp/tests/models.py | 31 + .../nxp/tests/test_convert_1d_conv_to_2d.py | 15 +- 5 files changed, 815 insertions(+), 974 deletions(-) diff --git a/backends/nxp/backend/ir/converter/node_converters/ops_converters/convolution_converter.py b/backends/nxp/backend/ir/converter/node_converters/ops_converters/convolution_converter.py index bdd71c6da1a..a55d0316f90 100644 --- a/backends/nxp/backend/ir/converter/node_converters/ops_converters/convolution_converter.py +++ b/backends/nxp/backend/ir/converter/node_converters/ops_converters/convolution_converter.py @@ -58,29 +58,15 @@ Stride = Padding = Dilation = OutPadding = list[int] Transposed = bool Groups = int -ConvolutionArgs = tuple[ - Node, Node, Node | None, Stride, Padding, Dilation, Transposed, OutPadding, Groups -] @requires_channels_first_format class ConvolutionConverter(NodeConverter): @staticmethod - def _is_supported_on_target_regular_conv( - node: Node, - parameters_mapping: dict[str, Parameter], + def _is_conv_quant_supported( + node: Node, parameters_mapping: dict[str, Parameter] ) -> bool: - ( - inp_node, - w_node, - b_node, - stride, - _, - dilation, - _, - _, - _, - ) = ConvolutionConverter._get_convolution_arguments(node) + conv_params = ConvolutionConverter._get_conv_params(node) # Input must be INT8/UINT8 # Output must be INT8/UINT8 @@ -98,7 +84,7 @@ def _is_supported_on_target_regular_conv( return False # Bias must be INT32 - if b_node is not None: + if conv_params.bias_node is not None: b_supported_types = [torch.int32] if not NodeConverter.uses_quantization_type_for_io( node, b_supported_types, [2], [] @@ -106,39 +92,68 @@ def _is_supported_on_target_regular_conv( return False # Weights must be constant - if not node_is_effectively_static_tensor(w_node, parameters_mapping): + if not node_is_effectively_static_tensor( + conv_params.weight_node, parameters_mapping + ): return False # Bias must be constant (if present) - if b_node is not None and not node_is_effectively_static_tensor( - b_node, parameters_mapping + if conv_params.bias_node is not None and not node_is_effectively_static_tensor( + conv_params.bias_node, parameters_mapping ): return False + return True + + @staticmethod + def _is_supported_on_target_regular_conv( + node: Node, + neutron_target_spec: NeutronTargetSpec, + parameters_mapping: dict[str, Parameter], + ) -> bool: + # Check the quantization of inputs is supported + if not ConvolutionConverter._is_conv_quant_supported(node, parameters_mapping): + return False + + op_args = ConvolutionConverter._get_conv_params(node) + node_params = get_node_tensor_params(node) + # kernelH <= 4096, kernelW <= 4096 # strideH <= 4096, strideW <= 4096 # dilationH <= 4096, dilationW <= 4096 - w_node_shape = w_node.meta["val"].shape - - kernel_h = w_node_shape[2] - kernel_w = w_node_shape[3] - stride_h = stride[0] - stride_w = stride[1] - dilation_h = dilation[0] - dilation_w = dilation[1] + kernel_h = node_params["kernel_height"] + kernel_w = node_params["kernel_width"] + stride_h = op_args.stride[0] + stride_w = op_args.stride[1] + dilation_h = op_args.dilation[0] + dilation_w = op_args.dilation[1] dim_sizes = [kernel_h, kernel_w, stride_h, stride_w, dilation_h, dilation_w] if any(dim > 4096 for dim in dim_sizes): return False - # kernelH * kernelW * inpC <= 65535 - inp_node_shape = inp_node.meta["val"].shape - inp_channels = ( - inp_node_shape[1] if len(inp_node_shape) == 4 else inp_node_shape[0] - ) + # The following checks are mentioned in Neutron docs, however they cause some models + # to be non-delegable. Discussion with Neutron team is pending, and because the convolutions seem + # to work even without this constraint, this code is commented out for now + # to boost the models' performance. + # padT < kernelH, padB < kernelH, padL < kernelW, padR < kernelW + + # padding_h = op_args.padding[0] + # padding_w = op_args.padding[1] + # if padding_h >= kernel_h or padding_w >= kernel_w: + # return False - if kernel_h * kernel_w * inp_channels > 65535: + # kernelH * kernelW * ROUND_CEIL(inpC, NUM_MACS) <= 65535 + inp_channels = node_params["inp_channels"] + num_macs = neutron_target_spec.get_num_macs() + + if ( + kernel_h + * kernel_w + * ConvolutionConverter._round_ceil(inp_channels, num_macs) + > 65535 + ): return False return True @@ -149,48 +164,51 @@ def _is_supported_on_target_transp_conv( neutron_target_spec: NeutronTargetSpec, parameters_mapping: dict[str, Parameter], ) -> bool: - # TODO: EIEX-894 update the requirements of delegation for new Neutron flow - _, w_node, _, stride, padding, dilation, transposed, _, groups = ( - ConvolutionConverter._get_convolution_arguments(node) - ) - - num_macs = neutron_target_spec.get_num_macs() - node_t_params = get_node_tensor_params(node) - - if node_t_params["batch_size"] != 1: - # Only TransposeConv2d with batch size = 1 is supported on neutron. + # Check the quantization of inputs is supported + if not ConvolutionConverter._is_conv_quant_supported(node, parameters_mapping): return False + op_args = ConvolutionConverter._get_conv_params(node) + node_params = get_node_tensor_params(node) + # TransposeConv2d with groups > 1 is not supported # TODO: split into multiple convs with groups = 1 - if groups > 1: + if op_args.groups > 1: return False - if not node_is_effectively_static_tensor(w_node, parameters_mapping): - # Only supported if the weights are static, because TFLite `TransposeConv` uses permuted - # weights. In case the weights are dynamic, a Transpose operator would have to be added, which - # is not supported on Neutron. + + # kernelH <= 4096, kernelW <= 4096 + kernel_h = node_params["kernel_height"] + kernel_w = node_params["kernel_width"] + + dim_sizes = [kernel_h, kernel_w] + if any(dim > 4096 for dim in dim_sizes): return False - # neutron-library/src/utils/NeutronLibraryInterrogation.cpp#876 TransposeConv2DKernelKind + + # strideH <= kernelH, strideW <= kernelW + stride_h = op_args.stride[0] + stride_w = op_args.stride[1] + if stride_h > kernel_h or stride_w > kernel_w: + return False + + # strideH <= 2, strideW <= 2 + if stride_h > 2 or stride_w > 2: + return False + + # padT < kernelH, padB < kernelH, padL < kernelW, padR < kernelW + padding_h = op_args.padding[0] + padding_w = op_args.padding[1] + if padding_h >= kernel_h or padding_w >= kernel_w: + return False + + # kernelH * kernelW * ceil(inpC, NUM_MACS) <= 65535 + inp_channels = node_params["inp_channels"] + num_macs = neutron_target_spec.get_num_macs() + if ( - dilation != [1, 1] - or padding[0] != 0 - or padding[1] >= node_t_params["kernel_width"] - or ( - padding[1] != 0 and node_t_params["inp_height"] != 1 - ) # Slice added by explicit padding - or stride[0] != 1 - or ( - ( - stride[1] != node_t_params["kernel_width"] / 2 - or node_t_params["out_height"] != 1 - ) - and stride[1] != node_t_params["kernel_width"] - ) - or stride[1] % 2 != 0 - or node_t_params["inp_channels"] % num_macs != 0 - or node_t_params["out_channels"] % num_macs != 0 - or node_t_params["kernel_width"] % 2 != 0 - or node_t_params["kernel_height"] != 1 + kernel_h + * kernel_w + * ConvolutionConverter._round_ceil(inp_channels, num_macs) + > 65535 ): return False @@ -203,16 +221,16 @@ def _is_supported_on_target( parameters_mapping: dict[str, Parameter], custom_delegation_options: CustomDelegationOptions, ) -> bool: - is_transposed = (ConvolutionConverter._get_convolution_arguments(node))[6] + conv_params = ConvolutionConverter._get_conv_params(node) - if is_transposed: + if conv_params.transposed: return ConvolutionConverter._is_supported_on_target_transp_conv( node, neutron_target_spec, parameters_mapping ) else: return ConvolutionConverter._is_supported_on_target_regular_conv( - node, parameters_mapping + node, neutron_target_spec, parameters_mapping ) @staticmethod @@ -244,6 +262,10 @@ def _is_supported_in_IR( return True + @staticmethod + def _round_ceil(x, n): + return ((x + n - 1) // n) * n + def _compute_slicing_params( self, output_shape, explicit_padding ) -> tuple[list[int], list[int]]: @@ -259,22 +281,20 @@ def _compute_slicing_params( return begins, sizes @staticmethod - def _get_convolution_arguments( + def _get_conv_params( conv_node: Node, - ) -> ConvolutionArgs: + ) -> ConvParameters: x, w, b, stride, padding, dilation, transposed, out_padding, groups = ( conv_node.args ) - return ( - x, - w, - b, - list(stride), - list(padding), - list(dilation), - transposed, - list(out_padding), - groups, + + stride = list(stride) + padding = list(padding) + dilation = list(dilation) + out_padding = None if out_padding is None else list(out_padding) + + return ConvParameters( + x, w, b, stride, padding, dilation, transposed, out_padding, groups ) # noinspection PyPep8Naming @@ -415,8 +435,10 @@ def _convert_transpose_conv( return conversion_result def _convert_2d_conv( - self, t_op: tflite_model.Operator, conv_params: ConvParameters + self, torch_node: Node, t_op: tflite_model.Operator ) -> list[tflite_model.Operator]: + conv_params = self._get_conv_params(torch_node) + if conv_params.transposed: t_op.builtin_options = transpose_conv_options.TransposeConv() if conv_utils.group_conv_convertible_into_multiple_convolutions( @@ -502,18 +524,11 @@ def _convert_2d_conv( def convert(self, node: Node): self.assert_convertible(node) - _, _, _, stride, padding, dilation, transposed, out_padding, groups = ( - self._get_convolution_arguments(node) - ) - t_op = self._create_tflite_op_with_io_tensors(node) - conv_params = ConvParameters( - stride, padding, dilation, transposed, out_padding, groups - ) rank = t_op.tmp_inputs[1].shape.len() if rank == 4: # Conv2D - ops_to_add = self._convert_2d_conv(t_op, conv_params) + ops_to_add = self._convert_2d_conv(node, t_op) else: raise NotImplementedError( f"{rank - 2}D convolution is not supported." diff --git a/backends/nxp/backend/ir/converter/node_converters/shared/conv_utils.py b/backends/nxp/backend/ir/converter/node_converters/shared/conv_utils.py index 2012ecc8640..e23d587b66f 100755 --- a/backends/nxp/backend/ir/converter/node_converters/shared/conv_utils.py +++ b/backends/nxp/backend/ir/converter/node_converters/shared/conv_utils.py @@ -1,4 +1,4 @@ -# Copyright 2023-2025 NXP +# Copyright 2023-2026 NXP # # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. @@ -13,11 +13,14 @@ @dataclass class ConvParameters: + input_node: Node + weight_node: Node + bias_node: Node | None stride: list[int] padding: list[int] dilation: list[int] transposed: bool - out_padding: list[int] + out_padding: list[int] | None # only meaningful for transposed conv groups: int @@ -40,22 +43,37 @@ def _get_IO_channels(node: Node | tflite_model.Operator) -> (int, int): def get_node_tensor_params(node: Node) -> dict: node_tensor_params = {} + # handle input tensor parameters input_tensor = node.args[0] - assert len(input_tensor.meta["val"].shape) in [3, 4], "Supports only Conv 1D, 2D." - node_tensor_params["batch_size"] = input_tensor.meta["val"].shape[0] - node_tensor_params["inp_channels"] = input_tensor.meta["val"].shape[1] - node_tensor_params["inp_height"] = input_tensor.meta["val"].shape[2] - if len(input_tensor.meta["val"].shape) == 4: - node_tensor_params["inp_width"] = input_tensor.meta["val"].shape[3] + inp_shape = input_tensor.meta["val"].shape + assert len(inp_shape) in [ + 3, + 4, + ], "Supports only forward Conv 2D with possible implicit batch." + + node_tensor_params["inp_height"] = inp_shape[2] + node_tensor_params["inp_width"] = inp_shape[3] + if len(inp_shape) == 4: + node_tensor_params["inp_channels"] = inp_shape[1] + node_tensor_params["batch_size"] = inp_shape[0] + else: + node_tensor_params["inp_channels"] = inp_shape[0] + node_tensor_params["batch_size"] = 1 + # handle weight tensor parameters weights = node.args[1] - node_tensor_params["out_channels"] = node.meta["val"].shape[1] - node_tensor_params["out_height"] = node.meta["val"].shape[2] - if len(node.meta["val"].shape) == 4: - node_tensor_params["out_width"] = node.meta["val"].shape[3] node_tensor_params["kernel_height"] = weights.meta["val"].shape[2] - if len(weights.meta["val"].shape) == 4: - node_tensor_params["kernel_width"] = weights.meta["val"].shape[3] + node_tensor_params["kernel_width"] = weights.meta["val"].shape[3] + + # handle output tensor parameters + out_shape = node.meta["val"].shape + + node_tensor_params["out_height"] = out_shape[2] + node_tensor_params["out_width"] = out_shape[3] + if len(out_shape) == 4: + node_tensor_params["out_channels"] = out_shape[1] + else: + node_tensor_params["out_channels"] = out_shape[0] return node_tensor_params diff --git a/backends/nxp/tests/ir/converter/node_converter/test_conv_converter.py b/backends/nxp/tests/ir/converter/node_converter/test_conv_converter.py index 1705dae85bb..5dc3b33e6dc 100644 --- a/backends/nxp/tests/ir/converter/node_converter/test_conv_converter.py +++ b/backends/nxp/tests/ir/converter/node_converter/test_conv_converter.py @@ -8,19 +8,13 @@ import torch from executorch.backends.nxp.tests.dataset_creator import RandomDatasetCreator from executorch.backends.nxp.tests.executorch_pipeline import to_quantized_edge_program -from executorch.backends.nxp.tests.executors import ( - convert_run_compare, - EdgeProgramToIRConverter, - ExportedProgram, - graph_contains_any_of_ops, - ToChannelFirstPreprocess, - ToChannelLastPreprocess, -) +from executorch.backends.nxp.tests.executors import graph_contains_any_of_ops from executorch.backends.nxp.tests.graph_verifier import DetailedGraphVerifier -from executorch.backends.nxp.tests.models import Conv2dModule +from executorch.backends.nxp.tests.models import Conv2dModule, Conv2dTransposedModule from executorch.backends.nxp.tests.nsys_testing import ( AllCloseOutputComparator, lower_run_compare, + ReferenceModel, ) from executorch.backends.nxp.tests.ops_aliases import ( Convolution, @@ -36,148 +30,531 @@ def reseed_model_per_test_run(): np.random.seed(23) -class TestTransposedConvFromLegacyFlow: +def assert_delegated_and_correct( + model, + input_shape, + mocker, + request, + use_qat, + exp_delegated_ops=None, + exp_non_delegated_ops=None, + et_ref_model=ReferenceModel.QUANTIZED_EXECUTORCH_CPP, +): + if exp_delegated_ops is None: + exp_delegated_ops = {Convolution: 1} + if exp_non_delegated_ops is None: + exp_non_delegated_ops = {} + + graph_verifier = DetailedGraphVerifier( + mocker, + expected_delegated_ops=exp_delegated_ops, + expected_non_delegated_ops=exp_non_delegated_ops, + ) + dataset = RandomDatasetCreator(low=-1.0, high=1.0) + + # Use quantized dataset and allow single bit error. + remove_quant_io_ops = True + comparator = AllCloseOutputComparator(atol=1) + + lower_run_compare( + model, + input_shape, + graph_verifier, + request, + dataset, + comparator, + use_qat=use_qat, + remove_quant_io_ops=remove_quant_io_ops, + reference_model=et_ref_model, + ) + + +def assert_not_delegated(model, input_shape, use_qat): + delegated_ep = to_quantized_edge_program( + model, + input_shape, + use_qat=use_qat, + ).exported_program() + + # Make sure the `convolution` was NOT delegated. + assert not graph_contains_any_of_ops(delegated_ep.graph, [ExecutorchDelegateCall]) + assert graph_contains_any_of_ops(delegated_ep.graph, [Convolution]) + + +def _conv_id(ins, oc, ks=3, s=2, d=1, p=0, op=0, b=True, g=1): + return ( + f"ins={ins}, " + f"oc={oc}, " + f"ks={ks}, " + f"s={s}, " + f"d={d}, " + f"p={p}, " + f"op={op}, " + f"b={b}, " + f"g={g}" + ) + + +class TestTrConv: @pytest.mark.parametrize( - "model, input_shape", + "input_shape, out_channels", [ pytest.param( - torch.nn.ConvTranspose2d(8, 16, (1, 4), stride=(1, 2)), - (1, 8, 1, 16), - id="In ch 8, out ch 16, kernel (1, 4), stride (1, 2)", + ins := (1, 8, 16, 24), + oc := 8, + id=f"basic inference: {_conv_id(ins, oc)}", ), pytest.param( - torch.nn.ConvTranspose2d(64, 64, (1, 2), stride=(1, 2)), - (1, 64, 3, 12), - id="In ch 64, out ch 64, kernel (1, 2), stride (1, 2)", + ins := (8, 16, 8, 32), + oc := 16, + id=f"basic inference: {_conv_id(ins, oc)}", ), pytest.param( - torch.nn.ConvTranspose2d(16, 40, (1, 4), stride=(1, 2), padding=(0, 1)), - (1, 16, 1, 27), - id="In ch 16, out ch 40, kernel (1, 4), stride (1, 2), padding (0, 1)", + ins := (16, 8, 32, 64), + oc := 32, + id=f"basic inference: {_conv_id(ins, oc)}", + marks=pytest.mark.xfail(reason="AIR-14853", strict=True), ), pytest.param( - torch.nn.ConvTranspose2d(8, 16, (1, 4), stride=(1, 2), padding=(0, 1)), - (1, 8, 1, 16), - id="In ch 8, out ch 16, kernel (1, 4), stride (1, 2), padding (0, 1)", + ins := (1, 8, 32, 64), + oc := 16, + id=f"basic inference: {_conv_id(ins, oc)}", ), pytest.param( - torch.nn.ConvTranspose2d( - 8, 16, (1, 4), stride=(1, 2), output_padding=(0, 1) - ), - (1, 8, 1, 16), - id="In ch 8, out ch 16, kernel (1, 8), stride (1, 2), output_padding (0, 1)", + ins := (1, 32, 48, 8), + oc := 24, + id=f"basic inference: {_conv_id(ins, oc)}", ), + ], + ) + def test__tr_basic(self, input_shape, out_channels, use_qat, request, mocker): + in_channels = input_shape[1] + model = Conv2dTransposedModule( + in_channels=in_channels, out_channels=out_channels + ) + + assert_delegated_and_correct(model, input_shape, mocker, request, use_qat) + + @pytest.mark.parametrize( + "input_shape, out_channels, use_et_ref_model", + [ pytest.param( - torch.nn.ConvTranspose2d(16, 16, (1, 4), stride=(1, 2)), - (1, 16, 1, 16), - id="In ch 16, out ch 16, kernel (1, 4), stride (1, 2)", + ins := (1, 3, 7, 14), + oc := 3, + use_et_ref_model := True, + id=f"ET reference model used: {use_et_ref_model}, unusual shape inference: " + + _conv_id(ins, oc), ), pytest.param( - torch.nn.ConvTranspose2d(8, 16, (1, 4), stride=(1, 2), bias=False), - (1, 8, 1, 16), - id="In ch 8, out ch 16, kernel (1, 4), stride (1, 2), no bias", + ins := (2, 3, 13, 27), + oc := 7, + use_et_ref_model := True, + id=f"ET reference model used: {use_et_ref_model}, unusual shape inference: " + + _conv_id(ins, oc), ), pytest.param( - torch.nn.ConvTranspose2d( - 8, 16, (1, 4), stride=(1, 2), padding=(0, 1), bias=False - ), - (1, 8, 1, 16), - id="In ch 8, out ch 16, kernel (1, 4), stride (1, 2)," - "padding (0, 1), no bias", + ins := (3, 7, 3, 14), + oc := 4, + use_et_ref_model := True, + id=f"ET reference model used: {use_et_ref_model}, unusual shape inference: " + + _conv_id(ins, oc), + ), + pytest.param( + ins := (1, 9, 9, 13), + oc := 1, + use_et_ref_model := False, + id=f"ET reference model used: {use_et_ref_model}, unusual shape inference: " + + _conv_id(ins, oc), + ), + pytest.param( + ins := (7, 7, 7, 7), + oc := 10, + use_et_ref_model := True, + id=f"ET reference model used: {use_et_ref_model}, unusual shape inference: " + + _conv_id(ins, oc), + ), + pytest.param( + ins := (4, 21, 13, 17), + oc := 27, + use_et_ref_model := True, + id=f"ET reference model used: {use_et_ref_model}, unusual shape inference: " + + _conv_id(ins, oc), ), ], ) - def test_conv_transpose2d_conversion__quantized( - self, mocker, model: torch.nn.Module, input_shape, use_qat + def test__tr_unusual( + self, input_shape, out_channels, use_et_ref_model, use_qat, request, mocker ): - converter_spy = mocker.spy(EdgeProgramToIRConverter, "convert_program") - - edge_program = to_quantized_edge_program( - model, input_shape, use_qat=use_qat, use_neutron_for_format_conversion=False - ).exported_program() + in_channels = input_shape[1] + model = Conv2dTransposedModule( + in_channels=in_channels, out_channels=out_channels + ) - # Make sure the `TransposeConv` was delegated. - assert not graph_contains_any_of_ops( - graph=edge_program.graph, ops=[Convolution] + # Running `conv_transpose2d` with `output_channels = 1` produces errors in Executorch. The issue has been reported: + # https://github.com/pytorch/executorch/issues/20804 + ref_model = ( + ReferenceModel.QUANTIZED_EXECUTORCH_CPP + if use_et_ref_model + else ReferenceModel.QUANTIZED_EDGE_PYTHON ) - assert graph_contains_any_of_ops( - graph=edge_program.graph, ops=[ExecutorchDelegateCall] + + assert_delegated_and_correct( + model, + input_shape, + mocker, + request, + use_qat, + exp_delegated_ops={Convolution: 1}, + exp_non_delegated_ops={}, + et_ref_model=ref_model, ) - # Capture generated model - tflite_flatbuffers_model, *_ = converter_spy.spy_return + @pytest.mark.parametrize( + "input_shape, out_channels", + [ + pytest.param( + ins := (21, 4, 7), + oc := 45, + id=f"`conv2d_transpose` implicit batch: {_conv_id(ins, oc)}", + ), + ], + ) + def test__tr_impl_b(self, input_shape, out_channels, use_qat, mocker, request): + in_channels = input_shape[0] - # Capture converted program - exported_program: ExportedProgram = converter_spy.call_args.args[1] + model = Conv2dTransposedModule( + in_channels=in_channels, out_channels=out_channels + ) - input_data = (np.random.random(input_shape).astype(np.float32) * 50).astype( - np.int8 + # `view_copy` is inserted to convert to explicit batch + assert_delegated_and_correct( + model, + input_shape, + mocker, + request, + use_qat, + exp_delegated_ops={Convolution: 1, ViewCopy: 2}, + exp_non_delegated_ops={}, ) - convert_run_compare( - exported_program, - tflite_input_preprocess=ToChannelLastPreprocess(), - tfl_model=tflite_flatbuffers_model, - tflite_output_preprocess=ToChannelFirstPreprocess(), - input_data=input_data, - atol=1.0, + @pytest.mark.parametrize( + "input_shape, out_channels, kernel_size, stride, dilation, padding", + [ + pytest.param( + ins := (2, 3, 1, 8500), + oc := 7, + ks := (1, 4096), + s := 1, + d := 1, + p := 0, + id=f"bounds of kernel width: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p)}", + marks=pytest.mark.xfail(reason="AIR-14853", strict=True), + ), + pytest.param( + ins := (3, 3, 8500, 1), + oc := 9, + ks := (4096, 1), + s := 1, + d := 1, + p := 0, + id=f"bounds of kernel height: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p)}", + marks=pytest.mark.xfail(reason="AIR-14853", strict=True), + ), + pytest.param( + ins := (3, 3, 5, 7), + oc := 9, + ks := (2, 1), + s := (2, 1), + d := 1, + p := 0, + id=f"bounds of stride height - kernel height: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p)}", + ), + pytest.param( + ins := (3, 3, 5, 7), + oc := 9, + ks := (1, 2), + s := (1, 2), + d := 1, + p := 0, + id=f"bounds of stride width - kernel width: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p)}", + ), + pytest.param( + ins := (3, 3, 5, 7), + oc := 9, + ks := (3, 3), + s := 1, + d := 1, + p := (2, 1), + id=f"bounds of padding height - kernel height: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p)}", + ), + pytest.param( + ins := (3, 3, 5, 7), + oc := 9, + ks := (3, 3), + s := 1, + d := 1, + p := (1, 2), + id=f"bounds of padding width - kernel width: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p)}", + ), + pytest.param( + ins := (1, 20, 16, 24), + oc := 2, + ks := (16, 24), + s := 1, + d := 1, + p := 1, + id=f"(almost) bounds of kernel_h * kernel_w * round_ceil(input_channels, num_macs): {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p)}", + ), + ], + ) + def test__tr_big( + self, + input_shape, + out_channels, + kernel_size, + stride, + dilation, + padding, + use_qat, + request, + mocker, + ): + model = Conv2dTransposedModule( + in_channels=input_shape[1], + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + dilation=dilation, + padding=padding, ) + assert_delegated_and_correct(model, input_shape, mocker, request, use_qat) + @pytest.mark.parametrize( - "model, input_shape", + "input_shape, out_channels, kernel_size, stride, dilation, padding, output_padding, bias", [ pytest.param( - torch.nn.ConvTranspose2d(8, 16, (1, 4), stride=(1, 2), dilation=(1, 2)), - (1, 8, 1, 16), - id="Dilation != (1, 1)", + ins := (1, 8, 32, 32), + oc := 7, + ks := (5, 3), + s := (2, 1), + d := (1, 2), + p := (2, 1), + op := (0, 1), + b := True, + id=f"some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b, op=op)}", + marks=pytest.mark.xfail(reason="AIR-14852", strict=True), ), pytest.param( - torch.nn.ConvTranspose2d(6, 16, (1, 4), stride=(1, 2)), - (1, 6, 1, 16), - id="In channels % num_macs != 0", + ins := (2, 7, 31, 17), + oc := 9, + ks := (7, 7), + s := (2, 2), + d := (6, 5), + p := (5, 4), + op := (2, 1), + b := False, + id=f"some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b, op=op)}", + marks=pytest.mark.xfail(reason="AIR-14853", strict=True), ), pytest.param( - torch.nn.ConvTranspose2d(8, 16, (1, 4), stride=(1, 2)), - (1, 8, 4, 16), - id="Out height != 1, stride width != kernel width", + ins := (2, 12, 28, 28), + oc := 11, + ks := (3, 5), + s := (2, 2), + d := (2, 2), + p := (1, 2), + op := (1, 1), + b := True, + id=f"some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b, op=op)}", + marks=pytest.mark.xfail(reason="AIR-14852", strict=True), ), pytest.param( - torch.nn.ConvTranspose2d(8, 16, (2, 4), stride=(1, 2), padding=(0, 1)), - (1, 8, 1, 16), - id="Out height != 1, stride width != kernel width", + ins := (3, 2, 40, 20), + oc := 13, + ks := (1, 5), + s := (1, 2), + d := (3, 1), + p := (0, 4), + op := (1, 1), + b := False, + id=f"some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b, op=op)}", ), pytest.param( - torch.nn.ConvTranspose2d(8, 16, (1, 5), stride=(1, 4)), - (1, 8, 1, 16), - id="Stride width != kernel width / 2, stride width != kernel width", + ins := (4, 6, 30, 30), + oc := 5, + ks := (3, 3), + s := (2, 2), + d := (3, 3), + p := (2, 2), + op := (2, 2), + b := True, + id=f"some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b, op=op)}", + marks=pytest.mark.xfail(reason="AIR-14852", strict=True), ), pytest.param( - torch.nn.ConvTranspose2d(16, 12, (1, 4), stride=(3, 3)), - (1, 16, 1, 16), - id="Out channels % num_macs != 0", + ins := (3, 12, 7, 7), + oc := 7, + ks := (5, 5), + s := (1, 2), + d := (1, 3), + p := (2, 4), + op := (0, 2), + b := False, + id=f"some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b, op=op)}", + marks=pytest.mark.xfail(reason="AIR-14852", strict=True), ), pytest.param( - torch.nn.ConvTranspose2d(64, 64, (1, 4), stride=(1, 2)), - (1, 64, 3, 12), - id="Out height != 1, stride width != kernel width", + ins := (1, 4, 15, 15), + oc := 9, + ks := (2, 2), + s := (2, 2), + d := (2, 2), + p := (1, 1), + op := (1, 1), + b := True, + id=f"some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b, op=op)}", + marks=pytest.mark.xfail(reason="AIR-14852", strict=True), + ), + ], + ) + def test__tr_misc_arg( + self, + input_shape, + out_channels, + kernel_size, + stride, + dilation, + padding, + output_padding, + bias, + use_qat, + request, + mocker, + ): + model = Conv2dTransposedModule( + in_channels=input_shape[1], + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + dilation=dilation, + padding=padding, + output_padding=output_padding, + bias=bias, + ) + + assert_delegated_and_correct(model, input_shape, mocker, request, use_qat) + + @pytest.mark.parametrize( + "input_shape, out_channels, kernel_size, stride, padding, groups", + [ + pytest.param( + ins := (3, 7, 5000, 11), + oc := 7, + ks := (4097, 1), + s := 1, + p := 0, + g := 1, + id=f"kernel height too big: {_conv_id(ins, oc, ks=ks, s=s, p=p)}", ), pytest.param( - torch.nn.ConvTranspose2d(16, 40, (1, 4), stride=(1, 4), padding=(0, 1)), - (1, 16, 4, 27), - id="Padding width != 1 and input height != 1", + ins := (3, 7, 13, 5000), + oc := 9, + ks := (1, 4097), + s := 1, + p := 0, + g := 1, + id=f"kernel width too big: {_conv_id(ins, oc, ks=ks, s=s, p=p)}", + ), + pytest.param( + ins := (3, 7, 9, 11), + oc := 11, + ks := 1, + s := (2, 1), + p := 0, + g := 1, + id=f"stride height > kernel height: {_conv_id(ins, oc, ks=ks, s=s, p=p)}", + ), + pytest.param( + ins := (3, 7, 13, 11), + oc := 5, + ks := 1, + s := (1, 2), + p := 0, + g := 1, + id=f"stride width > kernel width: {_conv_id(ins, oc, ks=ks, s=s, p=p)}", + ), + pytest.param( + ins := (3, 7, 13, 11), + oc := 7, + ks := 3, + s := (3, 1), + p := 0, + g := 1, + id=f"stride height too big: {_conv_id(ins, oc, ks=ks, s=s, p=p)}", + ), + pytest.param( + ins := (3, 7, 13, 11), + oc := 7, + ks := 3, + s := (1, 3), + p := 0, + g := 1, + id=f"stride width too big: {_conv_id(ins, oc, ks=ks, s=s, p=p)}", + ), + pytest.param( + ins := (3, 7, 9, 11), + oc := 7, + ks := 3, + s := 1, + p := (3, 1), + g := 1, + id=f"padding height >= kernel height: {_conv_id(ins, oc, ks=ks, s=s, p=p)}", + ), + pytest.param( + ins := (3, 7, 9, 11), + oc := 7, + ks := 3, + s := 1, + p := (1, 3), + g := 1, + id=f"padding width >= kernel width: {_conv_id(ins, oc, ks=ks, s=s, p=p)}", + ), + pytest.param( + ins := (3, 113, 123, 133), + oc := 11, + ks := (41, 15), + s := 1, + p := 0, + g := 1, + id=f"kernel_h * kernel_w * round_ceil(input_channels, num_macs) too big: {_conv_id(ins, oc, ks=ks, s=s, p=p)}", + ), + pytest.param( + ins := (3, 9, 11, 13), + oc := 3, + ks := 3, + s := 1, + p := 0, + g := 3, + id=f"groups > 1: {_conv_id(ins, oc, ks=ks, s=s, p=p, g=g)}", ), ], ) - def test_conv_transpose2d_non_delegated_conversion__quantized( - self, model: torch.nn.Module, input_shape, use_qat + def test__tr_no_deleg( + self, input_shape, out_channels, kernel_size, stride, padding, groups, use_qat ): - edge_program = to_quantized_edge_program( - model, input_shape, use_qat=use_qat - ).exported_program() + in_channels = input_shape[1] - nodes = list(edge_program.graph.nodes) - assert len(nodes) == 15 - assert nodes[11].target == Convolution # TransposeConv not delegated. + model = Conv2dTransposedModule( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + groups=groups, + ) + + assert_not_delegated(model, input_shape, use_qat) class TestConv: @@ -194,351 +571,152 @@ def _conv_id(ins, oc, ks=3, s=2, d=1, p=0, b=True, g=1): f"g={g}" ) - @staticmethod - def assert_delegated_and_correct(model, input_shape, mocker, request, use_qat): - graph_verifier = DetailedGraphVerifier( - mocker, - expected_delegated_ops={Convolution: 1}, - expected_non_delegated_ops={}, - ) - dataset = RandomDatasetCreator(low=-1, high=1) - - # Use quantized dataset and allow single bit error. - remove_quant_io_ops = True - comparator = AllCloseOutputComparator(atol=1) - - lower_run_compare( - model, - input_shape, - graph_verifier, - request, - dataset, - comparator, - use_qat=use_qat, - remove_quant_io_ops=remove_quant_io_ops, - ) - - @staticmethod - def assert_not_delegated(model, input_shape, use_qat): - delegated_ep = to_quantized_edge_program( - model, - input_shape, - use_qat=use_qat, - ).exported_program() - - # Make sure the `convolution` was NOT delegated. - assert not graph_contains_any_of_ops( - delegated_ep.graph, [ExecutorchDelegateCall] - ) - assert graph_contains_any_of_ops(delegated_ep.graph, [Convolution]) - @pytest.mark.parametrize( - "input_shape, out_channels, is_qat", + "input_shape, out_channels", [ pytest.param( ins := (1, 8, 16, 24), oc := 8, - qat := True, - id=f"qat={qat}, basic inference: " + _conv_id(ins, oc), - ), - pytest.param( - ins := (1, 8, 16, 24), - oc := 8, - qat := False, - id=f"qat={qat}, basic inference: " + _conv_id(ins, oc), + id="basic inference: " + _conv_id(ins, oc), ), pytest.param( ins := (8, 16, 8, 32), oc := 16, - qat := True, - id=f"qat={qat}, basic inference: " + _conv_id(ins, oc), - ), - pytest.param( - ins := (8, 16, 8, 32), - oc := 16, - qat := False, - id=f"qat={qat}, basic inference: " + _conv_id(ins, oc), + id="basic inference: " + _conv_id(ins, oc), ), pytest.param( ins := (16, 8, 32, 64), oc := 32, - qat := True, - id=f"qat={qat}, basic inference: " + _conv_id(ins, oc), - ), - pytest.param( - ins := (16, 8, 32, 64), - oc := 32, - qat := False, - id=f"qat={qat}, basic inference: " + _conv_id(ins, oc), + id="basic inference: " + _conv_id(ins, oc), ), pytest.param( ins := (1, 8, 32, 64), oc := 16, - qat := True, - id=f"qat={qat}, basic inference: " + _conv_id(ins, oc), - ), - pytest.param( - ins := (1, 8, 32, 64), - oc := 16, - qat := False, - id=f"qat={qat}, basic inference: " + _conv_id(ins, oc), - ), - pytest.param( - ins := (1, 32, 48, 8), - oc := 24, - qat := True, - id=f"qat={qat}, basic inference: " + _conv_id(ins, oc), + id="basic inference: " + _conv_id(ins, oc), ), pytest.param( ins := (1, 32, 48, 8), oc := 24, - qat := False, - id=f"qat={qat}, basic inference: " + _conv_id(ins, oc), + id="basic inference: " + _conv_id(ins, oc), ), ], ) - def test__basic_nsys_inference( - self, input_shape, out_channels, is_qat, request, mocker - ): + def test__fwd_basic(self, input_shape, out_channels, use_qat, request, mocker): in_channels = input_shape[1] model = Conv2dModule(in_channels=in_channels, out_channels=out_channels) - self.assert_delegated_and_correct(model, input_shape, mocker, request, is_qat) + assert_delegated_and_correct(model, input_shape, mocker, request, use_qat) @pytest.mark.parametrize( - "input_shape, is_qat", + "input_shape", [ pytest.param( ins := (1, 8, 16, 24), - qat := True, - id=f"qat={qat}, basic inference, depthwise: " - + _conv_id(ins, ins[1], g=ins[1]), - ), - pytest.param( - ins := (1, 8, 16, 24), - qat := False, - id=f"qat={qat}, basic inference, depthwise: " - + _conv_id(ins, ins[1], g=ins[1]), - ), - pytest.param( - ins := (8, 16, 8, 32), - qat := True, - id=f"qat={qat}, basic inference, depthwise: " - + _conv_id(ins, ins[1], g=ins[1]), + id="basic inference, depthwise: " + _conv_id(ins, ins[1], g=ins[1]), ), pytest.param( ins := (8, 16, 8, 32), - qat := False, - id=f"qat={qat}, basic inference, depthwise: " - + _conv_id(ins, ins[1], g=ins[1]), + id="basic inference, depthwise: " + _conv_id(ins, ins[1], g=ins[1]), ), pytest.param( ins := (16, 8, 32, 64), - qat := True, - id=f"qat={qat}, basic inference, depthwise: " - + _conv_id(ins, ins[1], g=ins[1]), - ), - pytest.param( - ins := (16, 8, 32, 64), - qat := False, - id=f"qat={qat}, basic inference, depthwise: " - + _conv_id(ins, ins[1], g=ins[1]), + id="basic inference, depthwise: " + _conv_id(ins, ins[1], g=ins[1]), ), pytest.param( ins := (1, 16, 32, 64), - qat := True, - id=f"qat={qat}, basic inference, depthwise: " - + _conv_id(ins, ins[1], g=ins[1]), - ), - pytest.param( - ins := (1, 16, 32, 64), - qat := False, - id=f"qat={qat}, basic inference, depthwise: " - + _conv_id(ins, ins[1], g=ins[1]), + id="basic inference, depthwise: " + _conv_id(ins, ins[1], g=ins[1]), ), pytest.param( ins := (1, 32, 48, 8), - qat := True, - id=f"qat={qat}, basic inference, depthwise: " - + _conv_id(ins, ins[1], g=ins[1]), - ), - pytest.param( - ins := (1, 32, 48, 8), - qat := False, - id=f"qat={qat}, basic inference, depthwise: " - + _conv_id(ins, ins[1], g=ins[1]), + id="basic inference, depthwise: " + _conv_id(ins, ins[1], g=ins[1]), ), ], ) - def test__depthwise(self, input_shape, is_qat, request, mocker): + def test__d_fwd_basic(self, input_shape, use_qat, request, mocker): out_channels = input_shape[1] group = input_shape[1] model = Conv2dModule( in_channels=input_shape[1], out_channels=out_channels, group=group ) - self.assert_delegated_and_correct(model, input_shape, mocker, request, is_qat) + assert_delegated_and_correct(model, input_shape, mocker, request, use_qat) @pytest.mark.parametrize( - "input_shape, out_channels, is_qat", + "input_shape, out_channels", [ pytest.param( ins := (1, 3, 7, 14), oc := 3, - qat := True, - id=f"qat={qat}, unusual shape inference: " + _conv_id(ins, oc), - ), - pytest.param( - ins := (1, 3, 7, 14), - oc := 3, - qat := False, - id=f"qat={qat}, unusual shape inference: " + _conv_id(ins, oc), - ), - pytest.param( - ins := (2, 3, 13, 27), - oc := 7, - qat := True, - id=f"qat={qat}, unusual shape inference: " + _conv_id(ins, oc), + id="unusual shape inference: " + _conv_id(ins, oc), ), pytest.param( ins := (2, 3, 13, 27), oc := 7, - qat := False, - id=f"qat={qat}, unusual shape inference: " + _conv_id(ins, oc), + id="unusual shape inference: " + _conv_id(ins, oc), ), pytest.param( ins := (3, 7, 3, 14), oc := 4, - qat := True, - id=f"qat={qat}, unusual shape inference: " + _conv_id(ins, oc), - ), - pytest.param( - ins := (3, 7, 3, 14), - oc := 4, - qat := False, - id=f"qat={qat}, unusual shape inference: " + _conv_id(ins, oc), + id="unusual shape inference: " + _conv_id(ins, oc), ), pytest.param( ins := (1, 7, 7, 21), oc := 1, - qat := True, - id=f"qat={qat}, unusual shape inference: " + _conv_id(ins, oc), - ), - pytest.param( - ins := (1, 7, 7, 21), - oc := 1, - qat := False, - id=f"qat={qat}, unusual shape inference: " + _conv_id(ins, oc), + id="unusual shape inference: " + _conv_id(ins, oc), ), pytest.param( ins := (7, 7, 7, 7), oc := 10, - qat := True, - id=f"qat={qat}, unusual shape inference: " + _conv_id(ins, oc), - ), - pytest.param( - ins := (7, 7, 7, 7), - oc := 10, - qat := False, - id=f"qat={qat}, unusual shape inference: " + _conv_id(ins, oc), + id="unusual shape inference: " + _conv_id(ins, oc), ), pytest.param( ins := (4, 21, 13, 17), oc := 27, - qat := True, - id=f"qat={qat}, unusual shape inference: " + _conv_id(ins, oc), - ), - pytest.param( - ins := (4, 21, 13, 17), - oc := 27, - qat := False, - id=f"qat={qat}, unusual shape inference: " + _conv_id(ins, oc), + id="unusual shape inference: " + _conv_id(ins, oc), ), ], ) - def test__unusual_shapes(self, input_shape, out_channels, is_qat, request, mocker): + def test__fwd_unusual(self, input_shape, out_channels, use_qat, request, mocker): model = Conv2dModule(in_channels=input_shape[1], out_channels=out_channels) - self.assert_delegated_and_correct(model, input_shape, mocker, request, is_qat) + assert_delegated_and_correct(model, input_shape, mocker, request, use_qat) @pytest.mark.parametrize( - "input_shape, is_qat", + "input_shape", [ pytest.param( ins := (1, 3, 7, 14), - qat := True, - id=f"qat={qat}, unusual shape inference, depthwise: " - + _conv_id(ins, ins[1], g=ins[1]), - ), - pytest.param( - ins := (1, 3, 7, 14), - qat := False, - id=f"qat={qat}, unusual shape inference, depthwise: " + id="unusual shape inference, depthwise: " + _conv_id(ins, ins[1], g=ins[1]), ), pytest.param( ins := (2, 3, 13, 27), - qat := True, - id=f"qat={qat}, unusual shape inference, depthwise: " - + _conv_id(ins, ins[1], g=ins[1]), - ), - pytest.param( - ins := (2, 3, 13, 27), - qat := False, - id=f"qat={qat}, unusual shape inference, depthwise: " - + _conv_id(ins, ins[1], g=ins[1]), - ), - pytest.param( - ins := (3, 7, 3, 14), - qat := True, - id=f"qat={qat}, unusual shape inference, depthwise: " + id="unusual shape inference, depthwise: " + _conv_id(ins, ins[1], g=ins[1]), ), pytest.param( ins := (3, 7, 3, 14), - qat := False, - id=f"qat={qat}, unusual shape inference, depthwise: " + id="unusual shape inference, depthwise: " + _conv_id(ins, ins[1], g=ins[1]), ), pytest.param( ins := (1, 7, 7, 21), - qat := True, - id=f"qat={qat}, unusual shape inference, depthwise: " - + _conv_id(ins, ins[1], g=ins[1]), - ), - pytest.param( - ins := (1, 7, 7, 21), - qat := False, - id=f"qat={qat}, unusual shape inference, depthwise: " + id="unusual shape inference, depthwise: " + _conv_id(ins, ins[1], g=ins[1]), ), pytest.param( ins := (7, 7, 7, 7), - qat := True, - id=f"qat={qat}, unusual shape inference, depthwise: " - + _conv_id(ins, ins[1], g=ins[1]), - ), - pytest.param( - ins := (7, 7, 7, 7), - qat := False, - id=f"qat={qat}, unusual shape inference, depthwise: " + id="unusual shape inference, depthwise: " + _conv_id(ins, ins[1], g=ins[1]), ), pytest.param( ins := (4, 21, 13, 17), - qat := True, - id=f"qat={qat}, unusual shape inference, depthwise: " - + _conv_id(ins, ins[1], g=ins[1]), - ), - pytest.param( - ins := (4, 21, 13, 17), - qat := False, - id=f"qat={qat}, unusual shape inference, depthwise: " + id="unusual shape inference, depthwise: " + _conv_id(ins, ins[1], g=ins[1]), ), ], ) - def test__depthwise__unusual_shapes(self, input_shape, is_qat, request, mocker): + def test__d_fwd_unusual(self, input_shape, use_qat, request, mocker): out_channels = input_shape[1] group = input_shape[1] @@ -546,51 +724,36 @@ def test__depthwise__unusual_shapes(self, input_shape, is_qat, request, mocker): in_channels=input_shape[1], out_channels=out_channels, group=group ) - self.assert_delegated_and_correct(model, input_shape, mocker, request, is_qat) + assert_delegated_and_correct(model, input_shape, mocker, request, use_qat) @pytest.mark.parametrize( - "input_shape, out_channels, is_qat", + "input_shape, out_channels", [ pytest.param( ins := (21, 4, 7), oc := 45, - qat := True, - id=f"qat={qat}, `conv2d` implicit batch: " + _conv_id(ins, oc), - ), - pytest.param( - ins := (21, 4, 7), - oc := 45, - qat := False, - id=f"qat={qat}, `conv2d` implicit batch: " + _conv_id(ins, oc), + id="`conv2d` implicit batch: " + _conv_id(ins, oc), ), ], ) - def test__implicit_batch(self, input_shape, out_channels, is_qat, mocker, request): + def test__fwd_impl_b(self, input_shape, out_channels, use_qat, mocker, request): in_channels = input_shape[0] model = Conv2dModule(in_channels=in_channels, out_channels=out_channels) # `view_copy` is inserted to convert to explicit batch - graph_verifier = DetailedGraphVerifier( - mocker, - expected_delegated_ops={Convolution: 1, ViewCopy: 2}, - expected_non_delegated_ops={}, - ) - dataset = RandomDatasetCreator(low=-256, high=256) - comparator = AllCloseOutputComparator(atol=1) - - lower_run_compare( + assert_delegated_and_correct( model, input_shape, - graph_verifier, + mocker, request, - dataset, - comparator, - use_qat=is_qat, + use_qat, + exp_delegated_ops={Convolution: 1, ViewCopy: 2}, + exp_non_delegated_ops={}, ) @pytest.mark.parametrize( - "input_shape, out_channels, kernel_size, stride, dilation, is_qat", + "input_shape, out_channels, kernel_size, stride, dilation", [ pytest.param( ins := (2, 3, 1, 4100), @@ -598,17 +761,7 @@ def test__implicit_batch(self, input_shape, out_channels, is_qat, mocker, reques ks := (1, 4096), s := 1, d := 1, - qat := True, - id=f"qat={qat}, bounds of kernel width: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", - ), - pytest.param( - ins := (2, 3, 1, 4100), - oc := 7, - ks := (1, 4096), - s := 1, - d := 1, - qat := False, - id=f"qat={qat}, bounds of kernel width: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", + id=f"bounds of kernel width: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", ), pytest.param( ins := (3, 3, 4100, 1), @@ -616,17 +769,7 @@ def test__implicit_batch(self, input_shape, out_channels, is_qat, mocker, reques ks := (4096, 1), s := 1, d := 1, - qat := True, - id=f"qat={qat}, bounds of kernel height: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", - ), - pytest.param( - ins := (3, 3, 4100, 1), - oc := 9, - ks := (4096, 1), - s := 1, - d := 1, - qat := False, - id=f"qat={qat}, bounds of kernel height: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", + id=f"bounds of kernel height: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", ), pytest.param( ins := (4, 3, 3, 8500), @@ -634,26 +777,7 @@ def test__implicit_batch(self, input_shape, out_channels, is_qat, mocker, reques ks := 3, s := (1, 4096), d := 1, - qat := True, - id=f"qat={qat}, bounds of stride width: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", - ), - pytest.param( - ins := (4, 3, 3, 8500), - oc := 5, - ks := 3, - s := (1, 4096), - d := 1, - qat := False, - id=f"qat={qat}, bounds of stride width: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", - ), - pytest.param( - ins := (2, 3, 8500, 3), - oc := 11, - ks := 3, - s := (4096, 1), - d := 1, - qat := True, - id=f"qat={qat}, bounds of stride height: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", + id=f"bounds of stride width: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", ), pytest.param( ins := (2, 3, 8500, 3), @@ -661,17 +785,7 @@ def test__implicit_batch(self, input_shape, out_channels, is_qat, mocker, reques ks := 3, s := (4096, 1), d := 1, - qat := False, - id=f"qat={qat}, bounds of stride height: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", - ), - pytest.param( - ins := (3, 3, 3, 8500), - oc := 9, - ks := 3, - s := 1, - d := (1, 4096), - qat := True, - id=f"qat={qat}, bounds of dilation width: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", + id=f"bounds of stride height: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", ), pytest.param( ins := (3, 3, 3, 8500), @@ -679,8 +793,7 @@ def test__implicit_batch(self, input_shape, out_channels, is_qat, mocker, reques ks := 3, s := 1, d := (1, 4096), - qat := False, - id=f"qat={qat}, bounds of dilation width: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", + id=f"bounds of dilation width: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", ), pytest.param( ins := (4, 3, 8500, 3), @@ -688,30 +801,7 @@ def test__implicit_batch(self, input_shape, out_channels, is_qat, mocker, reques ks := 3, s := 1, d := (4096, 1), - qat := True, - id=f"qat={qat}, bounds of dilation height: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", - ), - pytest.param( - ins := (4, 3, 8500, 3), - oc := 7, - ks := 3, - s := 1, - d := (4096, 1), - qat := False, - id=f"qat={qat}, bounds of dilation height: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", - ), - pytest.param( - ins := (2, 80, 35, 34), - oc := 13, - ks := (32, 24), - s := 1, - d := 1, - qat := True, - id=f"qat={qat}, bounds of kernel_h * kernel_w * input_channels: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", - marks=pytest.mark.xfail( - reason="AIR-14679", - strict=True, - ), + id=f"bounds of dilation height: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", ), pytest.param( ins := (2, 80, 35, 34), @@ -719,8 +809,7 @@ def test__implicit_batch(self, input_shape, out_channels, is_qat, mocker, reques ks := (32, 24), s := 1, d := 1, - qat := False, - id=f"qat={qat}, bounds of kernel_h * kernel_w * input_channels: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", + id=f"bounds of kernel_h * kernel_w * round_ceil(input_channels, num_macs): {_conv_id(ins, oc, ks=ks, s=s, d=d)}", marks=pytest.mark.xfail( reason="AIR-14679", strict=True, @@ -728,14 +817,14 @@ def test__implicit_batch(self, input_shape, out_channels, is_qat, mocker, reques ), ], ) - def test__big( + def test__fwd_big( self, input_shape, out_channels, kernel_size, stride, dilation, - is_qat, + use_qat, request, mocker, ): @@ -747,127 +836,64 @@ def test__big( dilation=dilation, ) - self.assert_delegated_and_correct(model, input_shape, mocker, request, is_qat) + assert_delegated_and_correct(model, input_shape, mocker, request, use_qat) @pytest.mark.parametrize( - "input_shape, kernel_size, stride, dilation, is_qat", + "input_shape, kernel_size, stride, dilation", [ pytest.param( ins := (2, 3, 1, 4100), ks := (1, 4096), s := 1, d := 1, - qat := True, - id=f"qat={qat}, bounds of kernel width: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", - ), - pytest.param( - ins := (2, 3, 1, 4100), - ks := (1, 4096), - s := 1, - d := 1, - qat := False, - id=f"qat={qat}, bounds of kernel width: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", - ), - pytest.param( - ins := (3, 3, 4100, 1), - ks := (4096, 1), - s := 1, - d := 1, - qat := True, - id=f"qat={qat}, bounds of kernel height: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", + id=f"bounds of kernel width: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", ), pytest.param( ins := (3, 3, 4100, 1), ks := (4096, 1), s := 1, d := 1, - qat := False, - id=f"qat={qat}, bounds of kernel height: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", - ), - pytest.param( - ins := (2, 3, 3, 8500), - ks := 3, - s := (1, 4096), - d := 1, - qat := True, - id=f"qat={qat}, bounds of stride width: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", + id=f"bounds of kernel height: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", ), pytest.param( ins := (2, 3, 3, 8500), ks := 3, s := (1, 4096), d := 1, - qat := False, - id=f"qat={qat}, bounds of stride width: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", - ), - pytest.param( - ins := (4, 3, 8500, 3), - ks := 3, - s := (4096, 1), - d := 1, - qat := True, - id=f"qat={qat}, bounds of stride height: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", + id=f"bounds of stride width: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", ), pytest.param( ins := (4, 3, 8500, 3), ks := 3, s := (4096, 1), d := 1, - qat := False, - id=f"qat={qat}, bounds of stride height: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", + id=f"bounds of stride height: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", ), pytest.param( ins := (4, 3, 3, 8500), ks := 3, s := 1, d := (1, 4096), - qat := True, - id=f"qat={qat}, bounds of dilation width: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", - ), - pytest.param( - ins := (4, 3, 3, 8500), - ks := 3, - s := 1, - d := (1, 4096), - qat := False, - id=f"qat={qat}, bounds of dilation width: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", - ), - pytest.param( - ins := (3, 3, 8500, 3), - ks := 3, - s := 1, - d := (4096, 1), - qat := True, - id=f"qat={qat}, bounds of dilation height: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", + id=f"bounds of dilation width: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", ), pytest.param( ins := (3, 3, 8500, 3), ks := 3, s := 1, d := (4096, 1), - qat := False, - id=f"qat={qat}, bounds of dilation height: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", - ), - pytest.param( - ins := (2, 80, 35, 34), - ks := (32, 24), - s := 1, - d := 1, - qat := True, - id=f"qat={qat}, bounds of kernel_h * kernel_w * input_channels: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", + id=f"bounds of dilation height: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", ), pytest.param( ins := (2, 80, 35, 34), ks := (32, 24), s := 1, d := 1, - qat := False, - id=f"qat={qat}, bounds of kernel_h * kernel_w * input_channels: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", + id=f"bounds of kernel_h * kernel_w * round_ceil(input_channels, num_macs): {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", ), ], ) - def test__depthwise__big( - self, input_shape, kernel_size, stride, dilation, is_qat, request, mocker + def test__d_fwd_big( + self, input_shape, kernel_size, stride, dilation, use_qat, request, mocker ): out_channels = input_shape[1] group = input_shape[1] @@ -881,10 +907,10 @@ def test__depthwise__big( group=group, ) - self.assert_delegated_and_correct(model, input_shape, mocker, request, is_qat) + assert_delegated_and_correct(model, input_shape, mocker, request, use_qat) @pytest.mark.parametrize( - "input_shape, out_channels, kernel_size, stride, dilation, padding, bias, is_qat", + "input_shape, out_channels, kernel_size, stride, dilation, padding, bias", [ pytest.param( ins := (1, 8, 32, 32), @@ -894,30 +920,7 @@ def test__depthwise__big( d := (1, 2), p := (2, 1), b := True, - qat := True, - id=f"qat={qat}, some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", - ), - pytest.param( - ins := (1, 8, 32, 32), - oc := 7, - ks := (5, 3), - s := (2, 1), - d := (1, 2), - p := (2, 1), - b := True, - qat := False, - id=f"qat={qat}, some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", - ), - pytest.param( - ins := (2, 7, 31, 17), - oc := 9, - ks := (7, 7), - s := (3, 2), - d := (2, 1), - p := (3, 3), - b := False, - qat := True, - id=f"qat={qat}, some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", + id=f"some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", ), pytest.param( ins := (2, 7, 31, 17), @@ -927,19 +930,7 @@ def test__depthwise__big( d := (2, 1), p := (3, 3), b := False, - qat := False, - id=f"qat={qat}, some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", - ), - pytest.param( - ins := (2, 12, 28, 28), - oc := 11, - ks := (3, 5), - s := (2, 2), - d := (2, 2), - p := (1, 2), - b := True, - qat := True, - id=f"qat={qat}, some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", + id=f"some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", ), pytest.param( ins := (2, 12, 28, 28), @@ -949,19 +940,7 @@ def test__depthwise__big( d := (2, 2), p := (1, 2), b := True, - qat := False, - id=f"qat={qat}, some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", - ), - pytest.param( - ins := (3, 2, 40, 20), - oc := 13, - ks := (1, 5), - s := (1, 2), - d := (3, 1), - p := (0, 2), - b := False, - qat := True, - id=f"qat={qat}, some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", + id=f"some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", ), pytest.param( ins := (3, 2, 40, 20), @@ -971,19 +950,7 @@ def test__depthwise__big( d := (3, 1), p := (0, 2), b := False, - qat := False, - id=f"qat={qat}, some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", - ), - pytest.param( - ins := (4, 6, 30, 30), - oc := 5, - ks := (3, 3), - s := (2, 2), - d := (1, 1), - p := (2, 2), - b := True, - qat := True, - id=f"qat={qat}, some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", + id=f"some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", ), pytest.param( ins := (4, 6, 30, 30), @@ -993,19 +960,7 @@ def test__depthwise__big( d := (1, 1), p := (2, 2), b := True, - qat := False, - id=f"qat={qat}, some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", - ), - pytest.param( - ins := (3, 12, 7, 7), - oc := 7, - ks := (5, 5), - s := (1, 3), - d := (1, 2), - p := (2, 4), - b := False, - qat := True, - id=f"qat={qat}, some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", + id=f"some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", ), pytest.param( ins := (3, 12, 7, 7), @@ -1015,8 +970,7 @@ def test__depthwise__big( d := (1, 2), p := (2, 4), b := False, - qat := False, - id=f"qat={qat}, some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", + id=f"some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", ), pytest.param( ins := (1, 4, 15, 15), @@ -1026,23 +980,11 @@ def test__depthwise__big( d := (2, 2), p := (1, 1), b := True, - qat := True, - id=f"qat={qat}, some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", - ), - pytest.param( - ins := (1, 4, 15, 15), - oc := 9, - ks := (2, 2), - s := (2, 2), - d := (2, 2), - p := (1, 1), - b := True, - qat := False, - id=f"qat={qat}, some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", + id=f"some params not default: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p, b=b)}", ), ], ) - def test__non_default_params( + def test__fwd_misc_arg( self, input_shape, out_channels, @@ -1051,7 +993,7 @@ def test__non_default_params( dilation, padding, bias, - is_qat, + use_qat, request, mocker, ): @@ -1065,10 +1007,10 @@ def test__non_default_params( bias=bias, ) - self.assert_delegated_and_correct(model, input_shape, mocker, request, is_qat) + assert_delegated_and_correct(model, input_shape, mocker, request, use_qat) @pytest.mark.parametrize( - "input_shape, kernel_size, stride, dilation, padding, bias, is_qat", + "input_shape, kernel_size, stride, dilation, padding, bias", [ pytest.param( ins := (1, 8, 32, 32), @@ -1077,28 +1019,7 @@ def test__non_default_params( d := (1, 2), p := (2, 1), b := True, - qat := True, - id=f"qat={qat}, some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", - ), - pytest.param( - ins := (1, 8, 32, 32), - ks := (5, 3), - s := (2, 1), - d := (1, 2), - p := (2, 1), - b := True, - qat := False, - id=f"qat={qat}, some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", - ), - pytest.param( - ins := (3, 7, 31, 17), - ks := (7, 7), - s := (3, 2), - d := (2, 1), - p := (3, 3), - b := False, - qat := True, - id=f"qat={qat}, some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", + id=f"some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", ), pytest.param( ins := (3, 7, 31, 17), @@ -1107,18 +1028,7 @@ def test__non_default_params( d := (2, 1), p := (3, 3), b := False, - qat := False, - id=f"qat={qat}, some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", - ), - pytest.param( - ins := (2, 12, 28, 28), - ks := (3, 5), - s := (2, 2), - d := (2, 2), - p := (1, 2), - b := True, - qat := True, - id=f"qat={qat}, some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", + id=f"some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", ), pytest.param( ins := (2, 12, 28, 28), @@ -1127,18 +1037,7 @@ def test__non_default_params( d := (2, 2), p := (1, 2), b := True, - qat := False, - id=f"qat={qat}, some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", - ), - pytest.param( - ins := (3, 2, 40, 20), - ks := (1, 5), - s := (1, 2), - d := (3, 1), - p := (0, 2), - b := False, - qat := True, - id=f"qat={qat}, some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", + id=f"some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", ), pytest.param( ins := (3, 2, 40, 20), @@ -1147,18 +1046,7 @@ def test__non_default_params( d := (3, 1), p := (0, 2), b := False, - qat := False, - id=f"qat={qat}, some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", - ), - pytest.param( - ins := (4, 6, 30, 30), - ks := (3, 3), - s := (2, 2), - d := (1, 1), - p := (2, 2), - b := True, - qat := True, - id=f"qat={qat}, some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", + id=f"some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", ), pytest.param( ins := (4, 6, 30, 30), @@ -1167,18 +1055,7 @@ def test__non_default_params( d := (1, 1), p := (2, 2), b := True, - qat := False, - id=f"qat={qat}, some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", - ), - pytest.param( - ins := (3, 12, 7, 7), - ks := (5, 5), - s := (1, 3), - d := (1, 2), - p := (2, 4), - b := False, - qat := True, - id=f"qat={qat}, some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", + id=f"some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", ), pytest.param( ins := (3, 12, 7, 7), @@ -1187,18 +1064,7 @@ def test__non_default_params( d := (1, 2), p := (2, 4), b := False, - qat := False, - id=f"qat={qat}, some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", - ), - pytest.param( - ins := (1, 4, 15, 15), - ks := (2, 2), - s := (2, 2), - d := (2, 2), - p := (1, 1), - b := True, - qat := True, - id=f"qat={qat}, some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", + id=f"some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", ), pytest.param( ins := (1, 4, 15, 15), @@ -1207,12 +1073,11 @@ def test__non_default_params( d := (2, 2), p := (1, 1), b := True, - qat := False, - id=f"qat={qat}, some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", + id=f"some params not default: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, p=p, b=b, g=ins[1])}", ), ], ) - def test__depthwise__non_default_params( + def test__d_fwd_misc_arg( self, input_shape, kernel_size, @@ -1220,7 +1085,7 @@ def test__depthwise__non_default_params( dilation, padding, bias, - is_qat, + use_qat, request, mocker, ): @@ -1238,10 +1103,10 @@ def test__depthwise__non_default_params( group=group, ) - self.assert_delegated_and_correct(model, input_shape, mocker, request, is_qat) + assert_delegated_and_correct(model, input_shape, mocker, request, use_qat) @pytest.mark.parametrize( - "input_shape, out_channels, kernel_size, stride, dilation, is_qat", + "input_shape, out_channels, kernel_size, stride, dilation, padding", [ pytest.param( ins := (3, 7, 5000, 11), @@ -1249,17 +1114,8 @@ def test__depthwise__non_default_params( ks := (4097, 1), s := 1, d := 1, - qat := True, - id=f"qat={qat}, kernel height too big: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", - ), - pytest.param( - ins := (3, 7, 5000, 11), - oc := 7, - ks := (4097, 1), - s := 1, - d := 1, - qat := False, - id=f"qat={qat}, kernel height too big: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", + p := 0, + id=f"kernel height too big: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p)}", ), pytest.param( ins := (3, 7, 13, 5000), @@ -1267,26 +1123,8 @@ def test__depthwise__non_default_params( ks := (1, 4097), s := 1, d := 1, - qat := True, - id=f"qat={qat}, kernel width too big: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", - ), - pytest.param( - ins := (3, 7, 13, 5000), - oc := 9, - ks := (1, 4097), - s := 1, - d := 1, - qat := False, - id=f"qat={qat}, kernel width too big: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", - ), - pytest.param( - ins := (3, 7, 5000, 11), - oc := 11, - ks := 3, - s := (4097, 1), - d := 1, - qat := True, - id=f"qat={qat}, stride height too big: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", + p := 0, + id=f"kernel width too big: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p)}", ), pytest.param( ins := (3, 7, 5000, 11), @@ -1294,17 +1132,8 @@ def test__depthwise__non_default_params( ks := 3, s := (4097, 1), d := 1, - qat := False, - id=f"qat={qat}, stride height too big: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", - ), - pytest.param( - ins := (3, 7, 13, 5000), - oc := 5, - ks := 3, - s := (1, 4097), - d := 1, - qat := True, - id=f"qat={qat}, stride width too big: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", + p := 0, + id=f"stride height too big: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p)}", ), pytest.param( ins := (3, 7, 13, 5000), @@ -1312,35 +1141,38 @@ def test__depthwise__non_default_params( ks := 3, s := (1, 4097), d := 1, - qat := False, - id=f"qat={qat}, stride width too big: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", - ), - pytest.param( - ins := (3, 7, 8500, 11), - oc := 7, - ks := 3, - s := 1, - d := (4097, 1), - qat := True, - id=f"qat={qat}, dilation height too big: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", - ), + p := 0, + id=f"stride width too big: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p)}", + ), + # The following two cases are delegable for now, the discussion about + # the validity of such cases is being discussed with Neutron team. + # See more in `convolution_converter`. + # pytest.param( + # ins := (3, 7, 13, 11), + # oc := 5, + # ks := (3, 1), + # s := 1, + # d := 1, + # p := (3, 1), + # id=f"padding height >= kernel height: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p)}", + # ), + # pytest.param( + # ins := (3, 7, 13, 11), + # oc := 5, + # ks := (1, 3), + # s := 1, + # d := 1, + # p := (1, 3), + # id=f"padding width >= kernel width: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p)}", + # ), pytest.param( ins := (3, 7, 8500, 11), oc := 7, ks := 3, s := 1, d := (4097, 1), - qat := False, - id=f"qat={qat}, dilation height too big: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", - ), - pytest.param( - ins := (3, 7, 13, 8500), - oc := 9, - ks := 3, - s := 1, - d := (1, 4097), - qat := True, - id=f"qat={qat}, dilation width too big: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", + p := 0, + id=f"dilation height too big: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p)}", ), pytest.param( ins := (3, 7, 13, 8500), @@ -1348,17 +1180,8 @@ def test__depthwise__non_default_params( ks := 3, s := 1, d := (1, 4097), - qat := False, - id=f"qat={qat}, dilation width too big: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", - ), - pytest.param( - ins := (3, 113, 123, 133), - oc := 11, - ks := (41, 15), - s := 1, - d := 1, - qat := True, - id=f"qat={qat}, kernel_h * kernel_w * input_channels too big: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", + p := 0, + id=f"dilation width too big: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p)}", ), pytest.param( ins := (3, 113, 123, 133), @@ -1366,13 +1189,13 @@ def test__depthwise__non_default_params( ks := (41, 15), s := 1, d := 1, - qat := False, - id=f"qat={qat}, kernel_h * kernel_w * input_channels too big: {_conv_id(ins, oc, ks=ks, s=s, d=d)}", + p := 0, + id=f"kernel_h * kernel_w * round_ceil(input_channels, num_macs) too big: {_conv_id(ins, oc, ks=ks, s=s, d=d, p=p)}", ), ], ) - def test__non_delegation( - self, input_shape, out_channels, kernel_size, stride, dilation, is_qat + def test__fwd_no_deleg( + self, input_shape, out_channels, kernel_size, stride, dilation, padding, use_qat ): in_channels = input_shape[1] @@ -1382,129 +1205,93 @@ def test__non_delegation( kernel_size=kernel_size, stride=stride, dilation=dilation, + padding=padding, ) - self.assert_not_delegated(model, input_shape, is_qat) + assert_not_delegated(model, input_shape, use_qat) @pytest.mark.parametrize( - "input_shape, kernel_size, stride, dilation, is_qat", + "input_shape, kernel_size, stride, dilation, padding", [ pytest.param( ins := (3, 7, 5000, 11), ks := (4097, 1), s := 1, d := 1, - qat := True, - id=f"qat={qat}, kernel height too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", - ), - pytest.param( - ins := (3, 7, 5000, 11), - ks := (4097, 1), - s := 1, - d := 1, - qat := False, - id=f"qat={qat}, kernel height too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", - ), - pytest.param( - ins := (3, 7, 13, 5000), - ks := (1, 4097), - s := 1, - d := 1, - qat := True, - id=f"qat={qat}, kernel width too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", + p := 0, + id=f"kernel height too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1], p=p)}", ), pytest.param( ins := (3, 7, 13, 5000), ks := (1, 4097), s := 1, d := 1, - qat := False, - id=f"qat={qat}, kernel width too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", + p := 0, + id=f"kernel width too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1], p=p)}", ), pytest.param( ins := (3, 7, 5000, 11), ks := 3, s := (4097, 1), d := 1, - qat := True, - id=f"qat={qat}, stride height too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", - ), - pytest.param( - ins := (3, 7, 5000, 11), - ks := 3, - s := (4097, 1), - d := 1, - qat := False, - id=f"qat={qat}, stride height too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", - ), - pytest.param( - ins := (3, 7, 13, 5000), - ks := 3, - s := (1, 4097), - d := 1, - qat := True, - id=f"qat={qat}, stride width too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", + p := 0, + id=f"stride height too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1], p=p)}", ), pytest.param( ins := (3, 7, 13, 5000), ks := 3, s := (1, 4097), d := 1, - qat := False, - id=f"qat={qat}, stride width too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", - ), - pytest.param( - ins := (3, 7, 8500, 11), - ks := 3, - s := 1, - d := (4097, 1), - qat := True, - id=f"qat={qat}, dilation height too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", - ), + p := 0, + id=f"stride width too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1], p=p)}", + ), + # The following two cases are delegable for now, the discussion about + # the validity of such cases is being discussed with Neutron team. + # See more in `convolution_converter`. + # pytest.param( + # ins := (3, 7, 13, 11), + # ks := (3, 1), + # s := 1, + # d := 1, + # p := (3, 1), + # id=f"padding height >= kernel height, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1], p=p)}", + # ), + # pytest.param( + # ins := (3, 7, 13, 11), + # ks := (1, 3), + # s := 1, + # d := 1, + # p := (1, 3), + # id=f"padding width >= kernel width, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1], p=p)}", + # ), pytest.param( ins := (3, 7, 8500, 11), ks := 3, s := 1, d := (4097, 1), - qat := False, - id=f"qat={qat}, dilation height too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", - ), - pytest.param( - ins := (3, 7, 13, 8500), - ks := 3, - s := 1, - d := (1, 4097), - qat := True, - id=f"qat={qat}, dilation width too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", + p := 0, + id=f"dilation height too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1], p=p)}", ), pytest.param( ins := (3, 7, 13, 8500), ks := 3, s := 1, d := (1, 4097), - qat := False, - id=f"qat={qat}, dilation width too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", - ), - pytest.param( - ins := (3, 113, 123, 133), - ks := (41, 15), - s := 1, - d := 1, - qat := True, - id=f"qat={qat}, kernel_h * kernel_w * input_channels too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", + p := 0, + id=f"dilation width too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1], p=p)}", ), pytest.param( ins := (3, 113, 123, 133), ks := (41, 15), s := 1, d := 1, - qat := False, - id=f"qat={qat}, kernel_h * kernel_w * input_channels too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1])}", + p := 0, + id=f"kernel_h * kernel_w * round_ceil(input_channels, num_macs) too big, depthwise: {_conv_id(ins, ins[1], ks=ks, s=s, d=d, g=ins[1], p=p)}", ), ], ) - def test__non_delegation_depthwise( - self, input_shape, kernel_size, stride, dilation, is_qat + def test__d_fwd_no_deleg( + self, input_shape, kernel_size, stride, dilation, padding, use_qat ): out_channels = input_shape[1] group = input_shape[1] @@ -1516,6 +1303,7 @@ def test__non_delegation_depthwise( stride=stride, dilation=dilation, group=group, + padding=padding, ) - self.assert_not_delegated(model, input_shape, is_qat) + assert_not_delegated(model, input_shape, use_qat) diff --git a/backends/nxp/tests/models.py b/backends/nxp/tests/models.py index a00ef602395..13071ca5234 100644 --- a/backends/nxp/tests/models.py +++ b/backends/nxp/tests/models.py @@ -100,6 +100,37 @@ def forward(self, x): return self.conv(x) +class Conv2dTransposedModule(torch.nn.Module): + def __init__( + self, + bias: bool = True, + dilation: Union[int, tuple[int, int]] = 1, + in_channels: int = 4, + kernel_size: Union[int, tuple[int, int]] = 3, + out_channels: int = 8, + padding: Union[int, Collection[int]] = 0, + output_padding: Union[int, tuple[int, int]] = 0, + stride: Union[int, tuple[int, int]] = 2, + groups: int = 1, + ): + super().__init__() + + self.conv_transp = torch.nn.ConvTranspose2d( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + stride=stride, + padding=padding, + output_padding=output_padding, + dilation=dilation, + bias=bias, + groups=groups, + ) + + def forward(self, x): + return self.conv_transp(x) + + class Conv3dModule(torch.nn.Module): def __init__( self, diff --git a/backends/nxp/tests/test_convert_1d_conv_to_2d.py b/backends/nxp/tests/test_convert_1d_conv_to_2d.py index 7737b98625b..b7db6fbc46e 100644 --- a/backends/nxp/tests/test_convert_1d_conv_to_2d.py +++ b/backends/nxp/tests/test_convert_1d_conv_to_2d.py @@ -253,7 +253,7 @@ def test_convert_conv_1d_transp_to_conv2d_transp( [ pytest.param(3, 1, 1, 1, 1, True, id="All default, except for padding = 1."), pytest.param(1, 1, 0, 1, 1, True, id="kernel_size = 1"), - pytest.param(3, 2, 5, 1, 1, True, id="stride = 2"), + pytest.param(3, 2, 2, 1, 1, True, id="stride = 2"), pytest.param(3, 1, 2, 2, 1, True, id="dilation = 2"), pytest.param(3, 1, 1, 1, 1, False, id="bias = False, padding = 1"), ], @@ -323,18 +323,7 @@ def test_convert_conv_1d_to_conv2d_full_pipeline( [ pytest.param(2, 2, 0, 0, 1, True, 1, id="All default."), pytest.param(4, 2, 1, 0, 1, True, 1, id="kernel_size = 4 (and padding = 1)"), - pytest.param(4, 4, 0, 0, 1, True, 1, id="stride = 4 (and kernel_size = 4)"), - pytest.param( - 4, - 4, - 1, - 2, - 1, - True, - 1, - id="output_padding = 2 (and kernel_size = 4, stride = 4, padding = 1)", - marks=pytest.mark.skip(reason="Neutron Converter hangs (AIR-14771)."), - ), + pytest.param(2, 2, 0, 1, 1, True, 1, id="output_padding = 1"), pytest.param(2, 2, 0, 0, 1, False, 1, id="bias=False"), ], )