11#
2- # Copyright 2024 NXP
2+ # Copyright 2024-2025 NXP
33#
44# License: LA_OPT_NXP_Software_License
55# See the LICENSE_LA_OPT_NXP_Software_License for more details.
66#
77
88import numpy as np
99
10- from executorch .backends .nxp .backend .ir .lib .tflite .TensorType import TensorType
11- from executorch .backends .nxp .backend .ir import logger
1210from executorch .backends .nxp .backend .ir .converter .builder .model_builder import ModelBuilder
1311from executorch .backends .nxp .backend .ir .converter .conversion import translator
14- from executorch .backends .nxp .backend .ir .converter .conversion .common import OpsList , try_get_input
15- from executorch .backends .nxp .backend .ir .converter .tensor_utils import tensor_has_data
12+ from executorch .backends .nxp .backend .ir .converter .conversion .common import OpsList
1613from executorch .backends .nxp .backend .ir .tensor_formatting import TensorFormat
1714from executorch .backends .nxp .backend .ir .tflite_generator import tflite_model
18- from executorch .backends .nxp .backend .ir .tflite_generator .meta .types import name_for_type
1915
2016
2117def convert_axes_from_attribute (t_op : tflite_model .Operator , builder : ModelBuilder , axes : list [int ] | None ):
@@ -38,88 +34,6 @@ def convert_axes_from_attribute(t_op: tflite_model.Operator, builder: ModelBuild
3834 t_op .tmp_inputs .append (axes_tensor )
3935
4036
41- def convert_axes_from_input_tensor (t_op : tflite_model .Operator , builder : ModelBuilder , inspector : ONNXModelInspector ,
42- ops : OpsList , noop_with_empty_axes : int , op_type : str ):
43- """ Verify the `axes` tensor (on input index 1) of the `t_op`, which is expected to represent an ONNX reduction
44- operator.
45- """
46- x = t_op .tmp_inputs [0 ]
47- rank = x .rank
48-
49- if axes_tensor := try_get_input (t_op , 1 ):
50-
51- # ONNX uses int64, while TFLite requires int32 for the `axes` tensor.
52- if axes_tensor .type != TensorType .INT64 :
53- logger .e (logger .Code .INVALID_ONNX_OPERATOR ,
54- f'ONNX `{ op_type } ` has `axes` of type `{ name_for_type (axes_tensor .type )} `, instead of INT64.' )
55-
56- # Try to get the inferred data for the `axes` input.
57- if (axes_data := inspector .try_get_inferred_tensor_data (axes_tensor .name )) is not None :
58- # The `axes` were inferred during shape inference.
59- logger .d (f'Using inferred data for the `axes` input tensor of ONNX `{ op_type } `.' )
60-
61- # Create a new tensor, in case the original `axes` tensor is used by multiple ops.
62- axes_tensor = builder .create_tensor_for_data (axes_data .astype (np .int32 ), 'axes' )
63-
64- # Make sure the `axes` are int32.
65- if tensor_has_data (axes_tensor ):
66- # Cast the `axes` to int32 statically.
67- axes_tensor .tmp_buffer .data = axes_tensor .tmp_buffer .data .astype (np .int32 )
68- axes_tensor .type = TensorType .INT32
69-
70- else :
71- # The `axes` are dynamic and there is no inferred data for them. The shape inference is not possible in
72- # this case, so it must have been skipped. If the `axes` are empty at runtime, ONNX will reduce over
73- # all dimensions, whereas TFLite will not reduce at all. So the behavior is different, and it depends
74- # on runtime data. Conversion could be implemented by adding multiple extra operators.
75- # I don't thing that completely prohibiting the conversion here is ideal, since the issue arises only in
76- # an edge case, which is hopefully not very common. Just print a warning message for now.
77- logger .w (f'Conversion of ONNX `{ op_type } ` with a dynamic `axes` input will not be correct, if the `axes`'
78- 'are empty at runtime!' )
79-
80- # Insert a `Cast` op, to make the `axes` int32.
81- cast_op = builder .create_cast_before (t_op , 1 , TensorType .INT32 )
82- ops .add_pre (cast_op )
83-
84- # For future references. Following code only cares about the final axes tensor.
85- axes_tensor = cast_op .tmp_outputs [0 ]
86-
87- # Assign the new `axes_tensor` to the ReduceX operator.
88- t_op .tmp_inputs [1 ] = axes_tensor
89-
90- else :
91- # No axes specified.
92-
93- if noop_with_empty_axes == 1 :
94- # ONNXRT: According to the documentation, the operator should do nothing in this situation. But that's
95- # not what happens in ONNX Runtime. ORT seems to simply ignore the `noop_with_empty_axes` attribute.
96- # https://github.com/microsoft/onnxruntime/issues/19147
97- # For now, exit with error. If later ORT adds support for this attribute, simply uncomment the
98- # following code.
99-
100- # if self.builder.operator_can_be_skipped(t_op, self.inspector):
101- # # Skip the operator.
102- # self.builder.redirect_tensor(t_op.tmp_outputs[0], t_op.tmp_inputs[0])
103- # return []
104- #
105- # else:
106- # # Return an operator which does nothing.
107- # self.builder.turn_operator_to_identity(t_op)
108- # return [t_op]
109-
110- logger .e (logger .Code .INVALID_ONNX_OPERATOR ,
111- f'ONNX `{ op_type } ` has `noop_with_empty_axes` == 1 and the `axes` are not specified, which'
112- ' indicates that the operator should do nothing. This is however not supported by ONNX'
113- ' Runtime, and therefore the conversion is also not supported.' )
114-
115- else :
116- # Default is to reduce all axes.
117- axes_tensor = builder .create_tensor_for_data (np .arange (rank ).astype (np .int32 ), 'axes' )
118-
119- t_op .tmp_inputs [1 :] = [] # If the optional input was passed with name "", remove it.
120- t_op .tmp_inputs .append (axes_tensor )
121-
122-
12337def ensure_reduce_transposition (builder , ops : OpsList ):
12438 """
12539 Ensure transposition of ReduceX operator is defined correctly based on tensor format.
0 commit comments