88from executorch .backends .nxp .backend .custom_delegation_options import (
99 CustomDelegationOptions ,
1010)
11- from executorch .backends .nxp .backend .data_format import NXP_NODE_FORMAT
12- from executorch .backends .nxp .backend .edge_helper import previous_non_qdq_node
1311from executorch .backends .nxp .backend .ir .converter .conversion import translator
14- from executorch .backends .nxp .backend .ir .converter .conversion .translator import (
15- apply_permutation_to ,
16- create_channels_first_to_channels_last_permutation ,
17- )
1812from executorch .backends .nxp .backend .ir .converter .node_converter import (
1913 _is_dequant_node ,
2014 _is_quant_node ,
2519)
2620from executorch .backends .nxp .backend .neutron_target_spec import NeutronTargetSpec
2721from torch .fx import Node
28- from torch .fx .passes .infra .partitioner import Partition
2922from torch .nn import Parameter
3023
3124
@@ -83,56 +76,12 @@ def _is_supported_on_target(
8376 parameters_mapping : dict [str , Parameter ],
8477 custom_delegation_options : CustomDelegationOptions ,
8578 ) -> bool :
86- if custom_delegation_options .force_delegate_cat :
87- return True
88-
89- dim = CatConverter ._get_normalized_dim (node )
90-
91- # Neutron requires the channels to be a multiple of `num_macs`. The channels could either be the second or the
92- # last dimension, depending on the formats of the node.
93- if node .meta [NXP_NODE_FORMAT ].is_channels_first ():
94- # During conversion to IR, the shape will be permuted to channels last, and the dimension on index
95- # `1` will end up being the channels (last dim in NHWC).
96- channels_index = 1
97- to_nhwc_perm = create_channels_first_to_channels_last_permutation (
98- len (node .meta ["val" ].shape ), True
99- )
100- dim = to_nhwc_perm .index (
101- dim
102- ) # Make sure the dim points to the NHWC dimension.
103- else :
104- # The shape will not be permuted during conversion, so the channels will remain the last dimension.
105- channels_index = - 1
106-
107- input_channels = [
108- _get_shape (input_ )[channels_index ] for input_ in node .all_input_nodes
109- ]
110- output_channels = _get_shape (node )[channels_index ]
111-
112- num_macs = neutron_target_spec .get_num_macs ()
113- input_shapes = [_get_shape (input_ ) for input_ in node .all_input_nodes ]
114- if any ((input_channel % num_macs ) != 0 for input_channel in input_channels ):
115- # neutron-library/src/utils/NeutronLibraryInterrogation.cpp#1492
116-
117- # If all input shapes are equal, the neutron is able to pad the last dimension of the inputs.
118- if not (
119- input_shapes .count (input_shapes [0 ]) == len (input_shapes )
120- and dim == len (input_shapes [0 ]) - 1
121- ):
122- return False
123-
124- if (output_channels % num_macs ) != 0 :
125- # neutron-library/src/utils/NeutronLibraryInterrogation.cpp#1493
126-
127- # If all input shapes are equal, the neutron is able to pad the last dimension of the output.
128- if not (
129- input_shapes .count (input_shapes [0 ]) == len (input_shapes )
130- and dim == len (input_shapes [0 ]) - 1
131- ):
132- return False
133-
134- if len (node .all_input_nodes ) < 2 : # Not supported on Neutron
135- # TODO Try to skip the operator if this case is realistic.
79+ # `cat` uses a list of inputs as its first argument, so the indices are tuples of (0, i).
80+ input_indices = [(0 , i ) for i in range (len (node .args [0 ]))]
81+ supported_types = [torch .int8 , torch .uint8 ]
82+ if not NodeConverter .uses_quantization_type_for_io (
83+ node , supported_types , input_indices = input_indices , output_indices = [0 ]
84+ ):
13685 return False
13786
13887 return True
@@ -150,50 +99,14 @@ def _is_supported_in_IR(
15099
151100 return True
152101
153- @classmethod
154- def supports_partitioning_result (
155- cls ,
156- node : Node ,
157- partition_list : list [Partition ],
158- custom_delegation_options : CustomDelegationOptions ,
159- neutron_target_spec : NeutronTargetSpec ,
160- parameters_mapping : dict [str , Parameter ],
161- ) -> bool :
162- # There is a bug in the NeutronConverter, where if none of the input dimensions before the one referenced by
163- # `dim` are `!= 1`, the `Concat` is not delegated.
164- # This only happens when the inputs to the `Concat` are model inputs, and not outputs of other
165- # operators.
166- cat_partition = [p for p in partition_list if node in p .nodes ][0 ]
167- cat_inputs = map (previous_non_qdq_node , node .args [0 ])
168-
169- if not all (
170- input_ .op == "call_function" and input_ in cat_partition .nodes
171- for input_ in cat_inputs
172- ):
173- # Some inputs of the `cat` are NOT in the same partition as `cat`.
174- dim = CatConverter ._get_normalized_dim (node )
175- input_shapes = [list (n .meta ["val" ].shape ) for n in node .args [0 ]]
176- if node .meta [NXP_NODE_FORMAT ].is_channels_first ():
177- # Transform the shapes to channels last.
178- to_nhwc_perm = create_channels_first_to_channels_last_permutation (
179- len (node .meta ["val" ].shape ), True
180- )
181- input_shapes = [
182- apply_permutation_to (shape , to_nhwc_perm ) for shape in input_shapes
183- ]
184-
185- # Transform the `dim` to refer to a channels last dimension.
186- dim = to_nhwc_perm .index (dim )
187-
188- for input_shape in input_shapes :
189- if not any (d != 1 for d in input_shape [:dim ]):
190- # Do not delegate if there are no "non-1" dimensions in the shape before the `dim` dimension.
191- return False
192-
193- return True
194-
195102 def convert (self , node : Node ):
196- """Convert the 'aten.cat' operator to TFLite 'Concatenation'."""
103+ """Convert the 'aten.cat' operator to NeutronIR 'Concatenation'.
104+ The ExecuTorch schema is:
105+ cat(
106+ Tensor[] tensors,
107+ int dim=0
108+ ) -> Tensor
109+ """
197110 self .assert_convertible (node )
198111
199112 t_op = self ._create_tflite_op_with_io_tensors (node )
@@ -205,5 +118,5 @@ def convert(self, node: Node):
205118 t_op .tmp_inputs [0 ].rank
206119 )[dim ]
207120
208- t_op .builtin_options = Concatenation (dim )
121+ t_op .builtin_options = Concatenation (int ( dim ) )
209122 self .builder .append_operators ([t_op ])
0 commit comments