4848from torch .nn import Parameter
4949
5050
51+ # The arguments of the conv are:
52+ # convolution(
53+ # Tensor input, Tensor weight, Tensor? bias,
54+ # SymInt[] stride, SymInt[] padding, SymInt[] dilation,
55+ # bool transposed, SymInt[] output_padding, SymInt groups
56+ # ) -> Tensor
57+ Stride = Padding = Dilation = OutPadding = list [int ]
58+ Transposed = bool
59+ Groups = int
60+ ConvolutionArgs = tuple [
61+ Node , Node , Node | None , Stride , Padding , Dilation , Transposed , OutPadding , Groups
62+ ]
63+
64+
5165class ConvolutionConverter (NodeConverter ):
5266 @staticmethod
53- def _is_supported_on_target (
67+ def _is_supported_on_target_regular_conv (
68+ node : Node ,
69+ parameters_mapping : dict [str , Parameter ],
70+ ) -> bool :
71+ (
72+ inp_node ,
73+ w_node ,
74+ b_node ,
75+ stride ,
76+ _ ,
77+ dilation ,
78+ _ ,
79+ _ ,
80+ _ ,
81+ ) = ConvolutionConverter ._get_convolution_arguments (node )
82+
83+ # Input must be INT8/UINT8
84+ # Output must be INT8/UINT8
85+ inp_out_supported_types = [torch .int8 , torch .uint8 ]
86+ if not NodeConverter .uses_quantization_type_for_io (
87+ node , inp_out_supported_types , [0 ], [0 ]
88+ ):
89+ return False
90+
91+ # Weights must be INT8
92+ w_supported_types = [torch .int8 ]
93+ if not NodeConverter .uses_quantization_type_for_io (
94+ node , w_supported_types , [1 ], []
95+ ):
96+ return False
97+
98+ # Bias must be INT32
99+ if b_node is not None :
100+ b_supported_types = [torch .int32 ]
101+ if not NodeConverter .uses_quantization_type_for_io (
102+ node , b_supported_types , [2 ], []
103+ ):
104+ return False
105+
106+ # Weights must be constant
107+ if not node_is_effectively_static_tensor (w_node , parameters_mapping ):
108+ return False
109+
110+ # Bias must be constant (if present)
111+ if b_node is not None and not node_is_effectively_static_tensor (
112+ b_node , parameters_mapping
113+ ):
114+ return False
115+
116+ # kernelH <= 4096, kernelW <= 4096
117+ # strideH <= 4096, strideW <= 4096
118+ # dilationH <= 4096, dilationW <= 4096
119+ w_node_shape = w_node .meta ["val" ].shape
120+
121+ kernel_h = w_node_shape [2 ]
122+ kernel_w = w_node_shape [3 ]
123+ stride_h = stride [0 ]
124+ stride_w = stride [1 ]
125+ dilation_h = dilation [0 ]
126+ dilation_w = dilation [1 ]
127+
128+ dim_sizes = [kernel_h , kernel_w , stride_h , stride_w , dilation_h , dilation_w ]
129+
130+ if any (dim > 4096 for dim in dim_sizes ):
131+ return False
132+
133+ # kernelH * kernelW * inpC <= 65535
134+ inp_node_shape = inp_node .meta ["val" ].shape
135+ inp_channels = (
136+ inp_node_shape [1 ] if len (inp_node_shape ) == 4 else inp_node_shape [0 ]
137+ )
138+
139+ if kernel_h * kernel_w * inp_channels > 65535 :
140+ return False
141+
142+ return True
143+
144+ @staticmethod
145+ def _is_supported_on_target_transp_conv (
54146 node : Node ,
55147 neutron_target_spec : NeutronTargetSpec ,
56148 parameters_mapping : dict [str , Parameter ],
57- custom_delegation_options : CustomDelegationOptions ,
58149 ) -> bool :
150+ # TODO: EIEX-894 update the requirements of delegation for new Neutron flow
151+ _ , w_node , _ , stride , padding , dilation , transposed , _ , groups = (
152+ ConvolutionConverter ._get_convolution_arguments (node )
153+ )
154+
59155 num_macs = neutron_target_spec .get_num_macs ()
60156 node_t_params = get_node_tensor_params (node )
61- weights = node .args [1 ]
62- conv_params = ConvParameters (
63- * ConvolutionConverter ._get_convolution_arguments (node )
64- )
65157
66158 if node_t_params ["batch_size" ] != 1 :
67- # Only batch size 1 is supported on neutron.
159+ # Only TransposeConv2d with batch size = 1 is supported on neutron.
68160 return False
69161
70- if conv_params .transposed :
71- # TransposeConv2d with groups > 1 is not supported
72- # TODO: split into multiple convs with groups = 1
73- if conv_params .groups > 1 :
74- return False
75- if not node_is_effectively_static_tensor (weights , parameters_mapping ):
76- # Only supported if the weights are static, because TFLite `TransposeConv` uses permuted
77- # weights. In case the weights are dynamic, a Transpose operator would have to be added, which
78- # is not supported on Neutron.
79- return False
80- # neutron-library/src/utils/NeutronLibraryInterrogation.cpp#876 TransposeConv2DKernelKind
81- if (
82- conv_params .dilation != [1 , 1 ]
83- or conv_params .padding [0 ] != 0
84- or conv_params .padding [1 ] >= node_t_params ["kernel_width" ]
85- or (
86- conv_params .padding [1 ] != 0 and node_t_params ["inp_height" ] != 1
87- ) # Slice added by explicit padding
88- or conv_params .stride [0 ] != 1
89- or (
90- (
91- conv_params .stride [1 ] != node_t_params ["kernel_width" ] / 2
92- or node_t_params ["out_height" ] != 1
93- )
94- and conv_params .stride [1 ] != node_t_params ["kernel_width" ]
95- )
96- or conv_params .stride [1 ] % 2 != 0
97- or node_t_params ["inp_channels" ] % num_macs != 0
98- or node_t_params ["out_channels" ] % num_macs != 0
99- or node_t_params ["kernel_width" ] % 2 != 0
100- or node_t_params ["kernel_height" ] != 1
101- ):
102- return False
103- elif conv_params .groups == 1 : # Regular convolution.
104- pass
105- elif conv_utils .group_conv_convertible_as_depthwise (
106- node , conv_params .groups
107- ): # Depthwise convolution.
108- # Only supported if the weights are static, because TFLite `DepthwiseConv2D` uses permuted
162+ # TransposeConv2d with groups > 1 is not supported
163+ # TODO: split into multiple convs with groups = 1
164+ if groups > 1 :
165+ return False
166+ if not node_is_effectively_static_tensor (w_node , parameters_mapping ):
167+ # Only supported if the weights are static, because TFLite `TransposeConv` uses permuted
109168 # weights. In case the weights are dynamic, a Transpose operator would have to be added, which
110169 # is not supported on Neutron.
111- if not node_is_effectively_static_tensor (weights , parameters_mapping ):
112- return False
113- elif conv_utils .group_conv_convertible_into_multiple_convolutions (
114- node , conv_params .groups
115- ): # Separable conv.
116- # Requires addition of `Split` and `Concatenation` operators, which are not supported on Neutron.
117170 return False
118- else : # Unexpected case (should never happen).
171+ # neutron-library/src/utils/NeutronLibraryInterrogation.cpp#876 TransposeConv2DKernelKind
172+ if (
173+ dilation != [1 , 1 ]
174+ or padding [0 ] != 0
175+ or padding [1 ] >= node_t_params ["kernel_width" ]
176+ or (
177+ padding [1 ] != 0 and node_t_params ["inp_height" ] != 1
178+ ) # Slice added by explicit padding
179+ or stride [0 ] != 1
180+ or (
181+ (
182+ stride [1 ] != node_t_params ["kernel_width" ] / 2
183+ or node_t_params ["out_height" ] != 1
184+ )
185+ and stride [1 ] != node_t_params ["kernel_width" ]
186+ )
187+ or stride [1 ] % 2 != 0
188+ or node_t_params ["inp_channels" ] % num_macs != 0
189+ or node_t_params ["out_channels" ] % num_macs != 0
190+ or node_t_params ["kernel_width" ] % 2 != 0
191+ or node_t_params ["kernel_height" ] != 1
192+ ):
119193 return False
120194
121195 return True
122196
197+ @staticmethod
198+ def _is_supported_on_target (
199+ node : Node ,
200+ neutron_target_spec : NeutronTargetSpec ,
201+ parameters_mapping : dict [str , Parameter ],
202+ custom_delegation_options : CustomDelegationOptions ,
203+ ) -> bool :
204+ is_transposed = (ConvolutionConverter ._get_convolution_arguments (node ))[6 ]
205+
206+ if is_transposed :
207+ return ConvolutionConverter ._is_supported_on_target_transp_conv (
208+ node , neutron_target_spec , parameters_mapping
209+ )
210+
211+ else :
212+ return ConvolutionConverter ._is_supported_on_target_regular_conv (
213+ node , parameters_mapping
214+ )
215+
123216 @staticmethod
124217 def _is_supported_in_IR (
125218 node : Node ,
@@ -149,10 +242,6 @@ def _is_supported_in_IR(
149242
150243 return True
151244
152- Stride = Padding = Dilation = OutPadding = list [int ]
153- Transposed = bool
154- Groups = int
155-
156245 def _compute_slicing_params (
157246 self , output_shape , explicit_padding
158247 ) -> tuple [list [int ], list [int ]]:
@@ -170,14 +259,14 @@ def _compute_slicing_params(
170259 @staticmethod
171260 def _get_convolution_arguments (
172261 conv_node : Node ,
173- ) -> (Stride , Padding , Dilation , Transposed , OutPadding , Groups ):
174- # The arguments of the conv are:
175- # [x, w, b, stride, padding, dilation, transposed, output padding, groups]
176- # https://github.com/pytorch/pytorch/blob/v2.6.0/aten/src/ATen/native/Convolution.cpp#L286-L291
177- _ , _ , _ , stride , padding , dilation , transposed , out_padding , groups = (
262+ ) -> ConvolutionArgs :
263+ x , w , b , stride , padding , dilation , transposed , out_padding , groups = (
178264 conv_node .args
179265 )
180266 return (
267+ x ,
268+ w ,
269+ b ,
181270 list (stride ),
182271 list (padding ),
183272 list (dilation ),
@@ -380,16 +469,8 @@ def _convert_2d_conv(
380469
381470 elif conv_utils .group_conv_convertible_into_multiple_convolutions (
382471 t_op , conv_params .groups
383- ): # Convert to separated `Conv2D`.
384- t_op .builtin_options = conv_2d_options .Conv2D ()
385-
386- return conv_utils .create_separated_convolutions_based_on_group (
387- t_op ,
388- conv_params ,
389- self .builder ,
390- self ._convert_unpadded_2D ,
391- conv_utils .conv_op_factory ,
392- )
472+ ):
473+ raise RuntimeError ("NXP backend: Group convolution was not decomposed." )
393474
394475 else :
395476 # Convert to regular `Conv2D`.
@@ -419,7 +500,7 @@ def _convert_2d_conv(
419500 def convert (self , node : Node ):
420501 self .assert_convertible (node )
421502
422- stride , padding , dilation , transposed , out_padding , groups = (
503+ _ , _ , _ , stride , padding , dilation , transposed , out_padding , groups = (
423504 self ._get_convolution_arguments (node )
424505 )
425506
0 commit comments