diff --git a/.github/workflows/pull.yml b/.github/workflows/pull.yml index 01d3b7b7c94..ddad7eebf61 100644 --- a/.github/workflows/pull.yml +++ b/.github/workflows/pull.yml @@ -1427,7 +1427,7 @@ jobs: docker-image: ci-image:executorch-ubuntu-22.04-clang12 submodules: 'recursive' ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - timeout: 120 + timeout: 150 script: | set -eux diff --git a/backends/nxp/backend/ir/converter/node_converter.py b/backends/nxp/backend/ir/converter/node_converter.py index 3f22d198469..4c71dab1285 100755 --- a/backends/nxp/backend/ir/converter/node_converter.py +++ b/backends/nxp/backend/ir/converter/node_converter.py @@ -6,6 +6,7 @@ import logging import operator from abc import ABC, abstractmethod +from math import prod from typing import Callable import torch @@ -465,29 +466,30 @@ def uses_shape_broadcasting(node: Node) -> bool: ) @staticmethod - def at_least_one_input_shape_matches_the_output_shape(node: Node) -> bool: - """Determine if given PyTorch fx Node uses at least one input shape broadcasting for it's input nodes or not. + def inputs_satisfy_broadcast_condition(node: Node) -> bool: + """Determine if given PyTorch fx Node has inputs that satisfy broadcasting conditions for Neutron or not. :param node: PyTorch fx Node with 'all_input_nodes' initialized. - :return: True, if at least one input has the same shape as the output node. + :return: True, if at least one input has the same number of elements as the output node. False otherwise. """ if node.all_input_nodes is None: logger.e( logger.Code.INTERNAL_ERROR, - "node_converter.at_least_one_input_shape_matches_the_output_shape(): 'all_input_nodes' are None!", + "node_converter.inputs_satisfy_broadcast_condition(): 'all_input_nodes' are None!", ) if len(node.all_input_nodes) == 0: logger.e( logger.Code.INTERNAL_ERROR, - "node_converter.at_least_one_input_shape_matches_the_output_shape(): Operator has no inputs!", + "node_converter.inputs_satisfy_broadcast_condition(): Operator has no inputs!", ) output_shape = node.meta["val"].shape + num_elements = prod(output_shape) return any( - input_tensor.meta["val"].shape == output_shape + prod(input_tensor.meta["val"].shape) == num_elements for input_tensor in node.all_input_nodes ) diff --git a/backends/nxp/backend/ir/converter/node_converters/ops_converters/add_tensor_converter.py b/backends/nxp/backend/ir/converter/node_converters/ops_converters/add_tensor_converter.py index fbbd906da17..f38f250113f 100644 --- a/backends/nxp/backend/ir/converter/node_converters/ops_converters/add_tensor_converter.py +++ b/backends/nxp/backend/ir/converter/node_converters/ops_converters/add_tensor_converter.py @@ -26,7 +26,7 @@ def _is_supported_on_target( parameters_mapping: dict[str, Parameter], custom_delegation_options: CustomDelegationOptions, ) -> bool: - if not NodeConverter.at_least_one_input_shape_matches_the_output_shape(node): + if not NodeConverter.inputs_satisfy_broadcast_condition(node): return False supported_types = [torch.int8, torch.uint8] diff --git a/backends/nxp/backend/ir/converter/node_converters/ops_converters/mul_tensor_converter.py b/backends/nxp/backend/ir/converter/node_converters/ops_converters/mul_tensor_converter.py index f1438b1fed5..d87d2d638f1 100644 --- a/backends/nxp/backend/ir/converter/node_converters/ops_converters/mul_tensor_converter.py +++ b/backends/nxp/backend/ir/converter/node_converters/ops_converters/mul_tensor_converter.py @@ -26,7 +26,7 @@ def _is_supported_on_target( parameters_mapping: dict[str, Parameter], custom_delegation_options: CustomDelegationOptions, ) -> bool: - if not NodeConverter.at_least_one_input_shape_matches_the_output_shape(node): + if not NodeConverter.inputs_satisfy_broadcast_condition(node): return False supported_types = [torch.int8, torch.uint8] diff --git a/backends/nxp/backend/ir/converter/node_converters/ops_converters/prelu_converter.py b/backends/nxp/backend/ir/converter/node_converters/ops_converters/prelu_converter.py index 003221a16fb..7f952755d72 100644 --- a/backends/nxp/backend/ir/converter/node_converters/ops_converters/prelu_converter.py +++ b/backends/nxp/backend/ir/converter/node_converters/ops_converters/prelu_converter.py @@ -3,7 +3,10 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. +import torch + from executorch.backends.nxp.backend.data_format import NXP_NODE_FORMAT +from executorch.backends.nxp.backend.edge_helper import input_rank from executorch.backends.nxp.backend.ir.converter.node_converter import ( CustomDelegationOptions, NodeConverter, @@ -24,38 +27,17 @@ def _is_supported_on_target( parameters_mapping: dict[str, Parameter], custom_delegation_options: CustomDelegationOptions, ) -> bool: - node_shape = node.meta["val"].shape - rank = len(node_shape) - - # According to Neutron spec., PReLU can be done only on 4D tensors - if rank != 4: - return False - - ch_idx, h_idx, w_idx = PReLUConverter._get_channel_spatial_indices(node) - # According to Neutron spec., size of channels must be divisible by num_macs. - num_macs = neutron_target_spec.get_num_macs() - if node_shape[ch_idx] % num_macs != 0: + if not NodeConverter.inputs_satisfy_broadcast_condition(node): return False - # According to Neutron spec., height * width cannot be greater than a given constant. - if node_shape[w_idx] * node_shape[h_idx] > 4096: + supported_types = [torch.int8, torch.uint8] + if not NodeConverter.uses_quantization_type_for_io( + node, supported_types, [0, 1], [0] + ): return False return True - @staticmethod - def _get_channel_spatial_indices(node: Node): - if node.meta[NXP_NODE_FORMAT].is_channels_first(): - ch_idx = 1 - h_idx = 2 - w_idx = 3 - else: - ch_idx = 3 - h_idx = 1 - w_idx = 2 - - return ch_idx, h_idx, w_idx - @staticmethod def _is_supported_in_IR( node: Node, @@ -65,6 +47,12 @@ def _is_supported_in_IR( if len(node.args) != 2: return False + if input_rank(node, 0) > 2 and tuple( + node.all_input_nodes[1].meta["val"].shape + ) != (1,): + if not node.meta[NXP_NODE_FORMAT].is_channels_first(): + # Should never happen. + return False return True def convert(self, node: Node): diff --git a/backends/nxp/backend/ir/converter/node_converters/ops_converters/sub_tensor_converter.py b/backends/nxp/backend/ir/converter/node_converters/ops_converters/sub_tensor_converter.py index 0551ae30956..d2f33454abc 100644 --- a/backends/nxp/backend/ir/converter/node_converters/ops_converters/sub_tensor_converter.py +++ b/backends/nxp/backend/ir/converter/node_converters/ops_converters/sub_tensor_converter.py @@ -26,7 +26,7 @@ def _is_supported_on_target( parameters_mapping: dict[str, Parameter], custom_delegation_options: CustomDelegationOptions, ) -> bool: - if not NodeConverter.at_least_one_input_shape_matches_the_output_shape(node): + if not NodeConverter.inputs_satisfy_broadcast_condition(node): return False supported_types = [torch.int8, torch.uint8] diff --git a/backends/nxp/backend/node_format_inference.py b/backends/nxp/backend/node_format_inference.py index 44e28ac748b..6933a373250 100644 --- a/backends/nxp/backend/node_format_inference.py +++ b/backends/nxp/backend/node_format_inference.py @@ -10,6 +10,7 @@ from executorch.backends.nxp.backend.data_format import DataFormat, NXP_NODE_FORMAT from executorch.backends.nxp.backend.edge_helper import ( + input_rank, is_channels_last_dim_order, try_get_arg, ) @@ -26,6 +27,7 @@ MaxPool2DWithIndices, MeanDim, PermuteCopy, + Prelu, QuantizePerTensor, SumDimIntList, UpsampleBilinear2D, @@ -54,6 +56,11 @@ class NodeFormatInference: UpsampleNearest2D: {"inputs": [0]}, } + ops_conditionally_with_channels_first_nodes = { + Prelu: {"inputs": [0]}, + torch.ops.aten.prelu.default: {"inputs": [0]}, + } + # A set of Edge Aten ops, which have the ability to change the format (for example - input nodes # are channels first but output is formatless). ops_that_can_change_tensor_format = { @@ -64,6 +71,8 @@ class NodeFormatInference: SumDimIntList, } + prelu_targets = [Prelu, torch.ops.aten.prelu.default] + _type_changed_during_last_run: bool # Mapping between Node and its ancestors (inputs) @@ -160,6 +169,13 @@ def _infer_format_of_nodes(self, node: Node): f"Node format inference for node type: {op_type} not found!" ) + elif node.target in self.prelu_targets: + num_parameters_shape = tuple(node.args[1].meta["val"].shape) + if input_rank(node, 0) > 2 and num_parameters_shape != (1,): + self._handle_node_which_uses_channels_first_format(node) + else: + self._handle_node_which_can_use_any_node_format(node) + elif node.op != "call_function" or ( hasattr(node, "target") and node.target in self._known_targets ): @@ -248,10 +264,14 @@ def _handle_node_which_uses_channels_first_format(self, node: Node): Function for assigning format to nodes that require channels first input (Conv, MaxPool etc.) """ op_type = self._get_node_op_type(node) + ops_using_channels_first_format = ( + self.ops_with_channels_first_nodes + | self.ops_conditionally_with_channels_first_nodes + ) for index, ancestor_node in enumerate(self._node_inputs[node]): # Go through input nodes and assign them correct format - if index in self.ops_with_channels_first_nodes[op_type]["inputs"]: + if index in ops_using_channels_first_format[op_type]["inputs"]: self._assign_format_to_node(ancestor_node, DataFormat.CHANNELS_FIRST) # We need to propagate channels first format up to already visited nodes diff --git a/backends/nxp/tests/ir/converter/node_converter/test_add_tensor_converter.py b/backends/nxp/tests/ir/converter/node_converter/test_add_tensor_converter.py index 2ec1c50cc01..2c89a2f5e51 100644 --- a/backends/nxp/tests/ir/converter/node_converter/test_add_tensor_converter.py +++ b/backends/nxp/tests/ir/converter/node_converter/test_add_tensor_converter.py @@ -115,6 +115,10 @@ def test__basic_nsys_inference_qat(self, mocker, request): [ModelInputSpec((1, 4, 8, 8)), ModelInputSpec((8, 8))], id="2 inputs 4D + 2D.", ), + pytest.param( + [ModelInputSpec((10,)), ModelInputSpec((1, 1))], + id="2 inputs 1D + 2D, num_elems of input == num_elems of output", + ), ], ) def test__broadcast(self, mocker, request, input_spec): diff --git a/backends/nxp/tests/ir/converter/node_converter/test_mul_tensor_converter.py b/backends/nxp/tests/ir/converter/node_converter/test_mul_tensor_converter.py index 5e807a7731f..718383284be 100644 --- a/backends/nxp/tests/ir/converter/node_converter/test_mul_tensor_converter.py +++ b/backends/nxp/tests/ir/converter/node_converter/test_mul_tensor_converter.py @@ -96,6 +96,10 @@ def test__basic_nsys_inference_qat(self, mocker, request, x_input_shape): pytest.param( [ModelInputSpec((4,)), ModelInputSpec((4, 4))], id="2 inputs 1D+2D." ), + pytest.param( + [ModelInputSpec((10,)), ModelInputSpec((1, 1))], + id="2 inputs 1D + 2D, num_elems of input == num_elems of output", + ), ], ) def test__broadcast(self, input_spec, mocker, request): diff --git a/backends/nxp/tests/ir/converter/node_converter/test_prelu_converter.py b/backends/nxp/tests/ir/converter/node_converter/test_prelu_converter.py index c5c7aa55b03..884e95ec20c 100644 --- a/backends/nxp/tests/ir/converter/node_converter/test_prelu_converter.py +++ b/backends/nxp/tests/ir/converter/node_converter/test_prelu_converter.py @@ -4,23 +4,41 @@ # LICENSE file in the root directory of this source tree. import numpy as np + +# noinspection PyUnusedImports import pytest import torch from executorch.backends.nxp.backend.edge_program_converter import ( EdgeProgramToIRConverter, ) -from executorch.backends.nxp.tests.executors import ( - convert_run_compare, - graph_contains_any_of_ops, +from executorch.backends.nxp.tests.dataset_creator import RandomDatasetCreator +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.model_output_comparator import ( + AllCloseOutputComparator, ) from executorch.backends.nxp.tests.models import ( + ConvPReLUModule, LinearPReLUModule, + PReLUModule, TwoPartitionPReLUModel, ) + +from executorch.backends.nxp.tests.nsys_testing import lower_run_compare +from executorch.backends.nxp.tests.ops_aliases import ( + AddMM, + Convolution, + ExecutorchDelegateCall, + GtScalar, + MulTensor, + PermuteCopy, + Prelu, + ViewCopy, + WhereSelf, +) from torch.export import ExportedProgram from executorch.backends.nxp.tests.use_qat import * # noqa F403 from executorch.backends.nxp.tests.executorch_pipeline import to_quantized_edge_program -from executorch.exir.dialects._ops import ops as exir_ops @pytest.fixture(autouse=True) @@ -29,123 +47,192 @@ def reseed_model_per_test_run(): np.random.seed(23) -# noinspection PyProtectedMember -ExecutorchDelegateCall = torch.ops.higher_order.executorch_call_delegate - - -@pytest.mark.parametrize( - "input_shape", - [ - pytest.param((1, 8, 24, 32), id="4D."), - ], -) -def test_prelu_with_linear_quant_conversion(mocker, input_shape): - converter_spy = mocker.spy(EdgeProgramToIRConverter, "convert_program") - - # Run conversion - channels = input_shape[-1] - edge_program = to_quantized_edge_program( - LinearPReLUModule(in_features=channels, out_features=channels), +class TestPreluConverter: + # noinspection PyMethodMayBeStatic + def assert_delegated( + self, + model, input_shape, - ).exported_program() - - # Capture generated entities - neutron_ir_model, *_ = converter_spy.spy_return - exported_program: ExportedProgram = converter_spy.call_args.args[1] - - # Check `prelu` was not decomposed into simpler edge operators - assert not graph_contains_any_of_ops( - exported_program.graph, + mocker, + request, + expected_delegated_ops=None, + use_qat=False, + ): + rank = len(input_shape) + graph_verifier = DetailedGraphVerifier( + mocker, + expected_delegated_ops=expected_delegated_ops + or { + Prelu: 1, + AddMM: 1, + PermuteCopy: 1, + ViewCopy: 0 if rank == 2 else 2, + }, + expected_non_delegated_ops={}, + ) + # Check that Prelu alpha non-single shapes weight was randomly initialized + if tuple(model.prelu.weight.shape) != (1,): + assert model.prelu.weight.data.std().item() > 0 + + # Cover also negative values to thoroughly test the operator. + dataset_creator = RandomDatasetCreator(low=-2, high=2) + comparator = AllCloseOutputComparator(atol=1) + + lower_run_compare( + model, + input_shape, + graph_verifier, + request, + dataset_creator, + output_comparator=comparator, + remove_quant_io_ops=True, + use_qat=use_qat, + ) + + @pytest.mark.parametrize( + "input_shape", [ - exir_ops.edge.aten.gt.Scalar, - exir_ops.edge.aten.mul.Tensor, - exir_ops.edge.aten.where.self, + pytest.param((1,), id="1D."), + pytest.param( + (36, 409), + id="2D incorrect results.", + marks=pytest.mark.xfail( + reason="AIR-14737: incorrect results", strict=True + ), + ), + pytest.param((7, 83), id="2D."), + pytest.param((7, 8, 12), id="3D."), + pytest.param( + (1, 43, 183), + id="3D incorrect results alt.", + marks=pytest.mark.xfail( + reason="AIR-14737: incorrect results", strict=True + ), + ), + pytest.param((1, 4, 7, 8), id="4D."), + pytest.param((1, 4, 3, 4, 14), id="5D."), ], ) - - assert graph_contains_any_of_ops( - exported_program.graph, - [exir_ops.edge.aten.prelu.default], - ) - - # Check `prelu` was delegated - assert not graph_contains_any_of_ops( - edge_program.graph, - [exir_ops.edge.aten.prelu.default], - ) - - input_data = ( - (2 * np.random.random(input_shape).astype(np.float32) - 1) * 50 - ).astype(np.int8) - - convert_run_compare(exported_program, input_data, tfl_model=neutron_ir_model) - - -@pytest.mark.parametrize( - "input_shape", - [ - pytest.param((1, 8, 24, 32), id="4D."), - ], -) -def test_prelu_2_partitions(mocker, input_shape): - # TODO (Martin) Add a channels last dim order variant of this test to verify correct partitioning. - # Run conversion - edge_program = to_quantized_edge_program( - TwoPartitionPReLUModel(), [input_shape, input_shape] - ).exported_program() - - # Check `prelu` was delegated - assert not graph_contains_any_of_ops( - edge_program.graph, - [exir_ops.edge.aten.prelu.default], + @pytest.mark.parametrize( + "num_parameters_channels", + [True, False], + ids=lambda ch: "Num parameters channels" if ch else "Num parameters 1", ) - - # Check there are two partitions - edge_nodes = list(edge_program.graph.nodes) - assert sum(n.target == ExecutorchDelegateCall for n in edge_nodes) == 2 - - -@pytest.mark.parametrize( - "input_shape", - [ - pytest.param((1,), id="1D not supported."), - pytest.param((1, 8), id="2D not supported."), - pytest.param((1, 8, 16), id="3D not supported."), - pytest.param((1, 8, 16, 32, 64), id="5D not supported."), - pytest.param((1, 8, 16, 31), id="channels must be divisible by NUM_MACS"), - pytest.param((1, 8, 1024, 8), id="width*height is too big (limit 4096)"), - ], -) -def test_prelu__no_delegation__unsupported_conversion(mocker, input_shape): - # Run conversion - channels = input_shape[-1] - edge_program = to_quantized_edge_program( - LinearPReLUModule(in_features=channels, out_features=channels), - input_shape, - ).exported_program() - - # Check `prelu` was not delegated (only `linear` was) - edge_nodes = list(edge_program.graph.nodes) - assert sum(n.target == ExecutorchDelegateCall for n in edge_nodes) == 1 - - # Check `prelu` was decomposed into simpler edge operators - assert graph_contains_any_of_ops( - edge_program.graph, + def test__basic_nsys_inference( + self, mocker, request, input_shape, num_parameters_channels + ): + channels = input_shape[-1] + # torch.nn.PReLU() has fixed number of channels to 1 for 1D tensor input + num_parameters = ( + input_shape[1] if num_parameters_channels and len(input_shape) > 1 else 1 + ) + model = LinearPReLUModule( + in_features=channels, out_features=channels, num_parameters=num_parameters + ) + + converter_spy = mocker.spy(EdgeProgramToIRConverter, "convert_program") + + self.assert_delegated(model, input_shape, mocker, request) + + # Capture generated entities + exported_program: ExportedProgram = converter_spy.call_args.args[1] + + # Check `prelu` was not decomposed into simpler edge operators + assert not graph_contains_any_of_ops( + exported_program.graph, + [ + GtScalar, + MulTensor, + WhereSelf, + ], + ) + + def test__basic_nsys_inference_qat(self, mocker, request): + input_shape = (2, 4, 6, 7) + channels = input_shape[-1] + model = LinearPReLUModule(in_features=channels, out_features=channels) + + self.assert_delegated(model, input_shape, mocker, request, use_qat=True) + + @pytest.mark.parametrize( + "input_shape", [ - exir_ops.edge.aten.gt.Scalar, + pytest.param((3,), id="1D."), + pytest.param((1, 4), id="2D."), + pytest.param((4, 7, 4), id="3D."), + pytest.param((1, 6, 4, 4), id="4D."), + pytest.param((2, 3, 8, 3, 11), id="5D."), ], ) - - assert graph_contains_any_of_ops( - edge_program.graph, - [ - exir_ops.edge.aten.mul.Tensor, - ], + @pytest.mark.parametrize( + "num_parameters_channels", + [True, False], + ids=lambda ch: "Num parameters channels" if ch else "Num parameters 1", ) - - assert graph_contains_any_of_ops( - edge_program.graph, + def test__single_prelu(self, mocker, request, input_shape, num_parameters_channels): + # torch.nn.PReLU() has fixed number of channels to 1 for 1D tensor input + num_parameters = ( + input_shape[1] if num_parameters_channels and len(input_shape) > 1 else 1 + ) + model = PReLUModule(num_parameters=num_parameters) + expected_delegated_ops = { + Prelu: 1, + } + + self.assert_delegated( + model, + input_shape, + mocker, + request, + expected_delegated_ops=expected_delegated_ops, + ) + + def test_prelu_2_partitions(self): + input_shape = (1, 8, 24, 32) + # Run conversion + edge_program = to_quantized_edge_program( + TwoPartitionPReLUModel(), [input_shape, input_shape] + ).exported_program() + + # Check `prelu` was delegated + assert not graph_contains_any_of_ops( + edge_program.graph, + [Prelu], + ) + + # Check there are two partitions + edge_nodes = list(edge_program.graph.nodes) + assert sum(n.target == ExecutorchDelegateCall for n in edge_nodes) == 2 + + @pytest.mark.parametrize( + "input_shape", [ - exir_ops.edge.aten.where.self, + pytest.param((1, 8, 4, 4), id="4D."), + pytest.param( + (1, 22, 76, 83), + id="4D incorrect results.", + marks=pytest.mark.xfail( + reason="AIR-14737: incorrect results", strict=True + ), + ), ], ) + @pytest.mark.parametrize( + "num_parameters_channels", + [True, False], + ids=lambda ch: "Num parameters channels" if ch else "Num parameters 1", + ) + def test__channels_first( + self, mocker, request, input_shape, num_parameters_channels + ): + channels = input_shape[1] + num_parameters = channels if num_parameters_channels else 1 + model = ConvPReLUModule(in_channels=channels, num_parameters=num_parameters) + expected_delegated_ops = { + Prelu: 1, + Convolution: 1, + } + + self.assert_delegated( + model, input_shape, mocker, request, expected_delegated_ops + ) diff --git a/backends/nxp/tests/ir/converter/node_converter/test_sub_tensor_converter.py b/backends/nxp/tests/ir/converter/node_converter/test_sub_tensor_converter.py index 7a8c1a33d6f..76817d53928 100644 --- a/backends/nxp/tests/ir/converter/node_converter/test_sub_tensor_converter.py +++ b/backends/nxp/tests/ir/converter/node_converter/test_sub_tensor_converter.py @@ -111,6 +111,10 @@ def test__basic_nsys_inference_qat(self, mocker, request): [ModelInputSpec((5, 3, 4)), ModelInputSpec((1, 3, 1))], id="2 inputs 3D.", ), + pytest.param( + [ModelInputSpec((10,)), ModelInputSpec((1, 1))], + id="2 inputs 1D + 2D, num_elems of input == num_elems of output", + ), ], ) def test__broadcast(self, mocker, request, input_spec): diff --git a/backends/nxp/tests/models.py b/backends/nxp/tests/models.py index a5049a4c6cc..a00ef602395 100644 --- a/backends/nxp/tests/models.py +++ b/backends/nxp/tests/models.py @@ -902,12 +902,42 @@ def __init__(self, in_features, out_features, num_parameters=1): self.linear = nn.Linear(in_features=in_features, out_features=out_features) self.prelu = torch.nn.PReLU(num_parameters) + # Initialize the weight alpha with random values to simulate learned model with non-constant data + torch.nn.init.uniform(self.prelu.weight, -1.0, 1.0) def forward(self, x): x = self.linear(x) return self.prelu(x) +class PReLUModule(torch.nn.Module): + def __init__(self, num_parameters=1): + super().__init__() + + self.prelu = torch.nn.PReLU(num_parameters) + # Initialize the weight alpha with random values to simulate learned model with non-constant data + torch.nn.init.uniform(self.prelu.weight, -1.0, 1.0) + + def forward(self, x): + return self.prelu(x) + + +class ConvPReLUModule(torch.nn.Module): + def __init__(self, in_channels, num_parameters=1): + super().__init__() + + self.conv = Conv2dModule( + in_channels=in_channels, out_channels=in_channels, stride=1, padding=1 + ) + self.prelu = torch.nn.PReLU(num_parameters) + # Initialize the weight alpha with random values to simulate learned model with non-constant data + torch.nn.init.uniform(self.prelu.weight, -1.0, 1.0) + + def forward(self, x): + x = self.conv(x) + return self.prelu(x) + + class TwoPartitionPReLUModel(torch.nn.Module): def __init__(self): super().__init__() diff --git a/backends/nxp/tests/ops_aliases.py b/backends/nxp/tests/ops_aliases.py index be5fc5e0be0..17833edb14b 100644 --- a/backends/nxp/tests/ops_aliases.py +++ b/backends/nxp/tests/ops_aliases.py @@ -29,6 +29,7 @@ ExecutorchDelegateCall = torch.ops.higher_order.executorch_call_delegate Exp = exir_ops.edge.aten.exp.default GetItem = operator.getitem +GtScalar = exir_ops.edge.aten.gt.Scalar HardTanh = exir_ops.edge.aten.hardtanh.default HardTanh_ = exir_ops.edge.aten.hardtanh_.default LeakyRelu = exir_ops.edge.aten.leaky_relu.default @@ -40,6 +41,7 @@ MulTensor = exir_ops.edge.aten.mul.Tensor Neg = exir_ops.edge.aten.neg.default PermuteCopy = exir_ops.edge.aten.permute_copy.default +Prelu = exir_ops.edge.aten.prelu.default QuantizePerChannel = exir_ops.edge.quantized_decomposed.quantize_per_channel.default QuantizePerTensor = exir_ops.edge.quantized_decomposed.quantize_per_tensor.default Relu = exir_ops.edge.aten.relu.default @@ -58,3 +60,4 @@ UpsampleBilinear2D = exir_ops.edge.aten.upsample_bilinear2d.vec UpsampleNearest2D = exir_ops.edge.aten.upsample_nearest2d.vec ViewCopy = exir_ops.edge.aten.view_copy.default +WhereSelf = exir_ops.edge.aten.where.self