Skip to content

Commit e6db65f

Browse files
committed
feat: added support for conv2d using new NeutronC flow
1 parent f4ac60f commit e6db65f

3 files changed

Lines changed: 1579 additions & 478 deletions

File tree

backends/nxp/backend/ir/converter/node_converters/ops_converters/convolution_converter.py

Lines changed: 122 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -48,70 +48,166 @@
4848
from 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+
5165
class ConvolutionConverter(NodeConverter):
66+
@staticmethod
67+
def _is_supported_on_target_new_flow(
68+
node: Node,
69+
parameters_mapping: dict[str, Parameter],
70+
) -> bool:
71+
(
72+
inp_node,
73+
w_node,
74+
b_node,
75+
stride,
76+
padding,
77+
dilation,
78+
transposed,
79+
_,
80+
groups,
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 = (
135+
inp_node.meta["val"].shape if hasattr(inp_node, "meta") else inp_node.shape
136+
)
137+
inp_channels = inp_node_shape[1]
138+
139+
if kernel_h * kernel_w * inp_channels > 65535:
140+
return False
141+
142+
return True
143+
52144
@staticmethod
53145
def _is_supported_on_target(
54146
node: Node,
55147
neutron_target_spec: NeutronTargetSpec,
56148
parameters_mapping: dict[str, Parameter],
57149
custom_delegation_options: CustomDelegationOptions,
58150
) -> bool:
151+
if custom_delegation_options.use_new_flow_neutron_c:
152+
return ConvolutionConverter._is_supported_on_target_new_flow(
153+
node, parameters_mapping
154+
)
155+
59156
num_macs = neutron_target_spec.get_num_macs()
60157
node_t_params = get_node_tensor_params(node)
61-
weights = node.args[1]
62-
conv_params = ConvParameters(
63-
*ConvolutionConverter._get_convolution_arguments(node)
158+
_, w_node, _, stride, padding, dilation, transposed, _, groups = (
159+
ConvolutionConverter._get_convolution_arguments(node)
64160
)
65161

66162
if node_t_params["batch_size"] != 1:
67163
# Only batch size 1 is supported on neutron.
68164
return False
69165

70-
if conv_params.transposed:
166+
if transposed:
71167
# TransposeConv2d with groups > 1 is not supported
72168
# TODO: split into multiple convs with groups = 1
73-
if conv_params.groups > 1:
169+
if groups > 1:
74170
return False
75-
if not node_is_effectively_static_tensor(weights, parameters_mapping):
171+
if not node_is_effectively_static_tensor(w_node, parameters_mapping):
76172
# Only supported if the weights are static, because TFLite `TransposeConv` uses permuted
77173
# weights. In case the weights are dynamic, a Transpose operator would have to be added, which
78174
# is not supported on Neutron.
79175
return False
80176
# neutron-library/src/utils/NeutronLibraryInterrogation.cpp#876 TransposeConv2DKernelKind
81177
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"]
178+
dilation != [1, 1]
179+
or padding[0] != 0
180+
or padding[1] >= node_t_params["kernel_width"]
85181
or (
86-
conv_params.padding[1] != 0 and node_t_params["inp_height"] != 1
182+
padding[1] != 0 and node_t_params["inp_height"] != 1
87183
) # Slice added by explicit padding
88-
or conv_params.stride[0] != 1
184+
or stride[0] != 1
89185
or (
90186
(
91-
conv_params.stride[1] != node_t_params["kernel_width"] / 2
187+
stride[1] != node_t_params["kernel_width"] / 2
92188
or node_t_params["out_height"] != 1
93189
)
94-
and conv_params.stride[1] != node_t_params["kernel_width"]
190+
and stride[1] != node_t_params["kernel_width"]
95191
)
96-
or conv_params.stride[1] % 2 != 0
192+
or stride[1] % 2 != 0
97193
or node_t_params["inp_channels"] % num_macs != 0
98194
or node_t_params["out_channels"] % num_macs != 0
99195
or node_t_params["kernel_width"] % 2 != 0
100196
or node_t_params["kernel_height"] != 1
101197
):
102198
return False
103-
elif conv_params.groups == 1: # Regular convolution.
199+
elif groups == 1: # Regular convolution.
104200
pass
105201
elif conv_utils.group_conv_convertible_as_depthwise(
106-
node, conv_params.groups
202+
node, groups
107203
): # Depthwise convolution.
108204
# Only supported if the weights are static, because TFLite `DepthwiseConv2D` uses permuted
109205
# weights. In case the weights are dynamic, a Transpose operator would have to be added, which
110206
# is not supported on Neutron.
111-
if not node_is_effectively_static_tensor(weights, parameters_mapping):
207+
if not node_is_effectively_static_tensor(w_node, parameters_mapping):
112208
return False
113209
elif conv_utils.group_conv_convertible_into_multiple_convolutions(
114-
node, conv_params.groups
210+
node, groups
115211
): # Separable conv.
116212
# Requires addition of `Split` and `Concatenation` operators, which are not supported on Neutron.
117213
return False
@@ -149,10 +245,6 @@ def _is_supported_in_IR(
149245

150246
return True
151247

152-
Stride = Padding = Dilation = OutPadding = list[int]
153-
Transposed = bool
154-
Groups = int
155-
156248
def _compute_slicing_params(
157249
self, output_shape, explicit_padding
158250
) -> tuple[list[int], list[int]]:
@@ -170,14 +262,14 @@ def _compute_slicing_params(
170262
@staticmethod
171263
def _get_convolution_arguments(
172264
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 = (
265+
) -> ConvolutionArgs:
266+
x, w, b, stride, padding, dilation, transposed, out_padding, groups = (
178267
conv_node.args
179268
)
180269
return (
270+
x,
271+
w,
272+
b,
181273
list(stride),
182274
list(padding),
183275
list(dilation),
@@ -380,16 +472,8 @@ def _convert_2d_conv(
380472

381473
elif conv_utils.group_conv_convertible_into_multiple_convolutions(
382474
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-
)
475+
):
476+
raise RuntimeError("NXP backend: Group convolution was not decomposed.")
393477

394478
else:
395479
# Convert to regular `Conv2D`.
@@ -419,7 +503,7 @@ def _convert_2d_conv(
419503
def convert(self, node: Node):
420504
self.assert_convertible(node)
421505

422-
stride, padding, dilation, transposed, out_padding, groups = (
506+
_, _, _, stride, padding, dilation, transposed, out_padding, groups = (
423507
self._get_convolution_arguments(node)
424508
)
425509

0 commit comments

Comments
 (0)