1- # Copyright 2025 NXP
1+ # Copyright 2025-2026 NXP
22#
33# This source code is licensed under the BSD-style license found in the
44# LICENSE file in the root directory of this source tree.
5+
56import numpy as np
67import pytest
78import torch
89
910from executorch .backends .nxp .backend .edge_program_converter import (
1011 EdgeProgramToIRConverter ,
1112)
12- from executorch .backends .nxp .tests .executorch_pipeline import to_quantized_edge_program
13+ from executorch .backends .nxp .tests .dataset_creator import RandomDatasetCreator
14+ from executorch .backends .nxp .tests .executorch_pipeline import (
15+ ModelInputSpec ,
16+ to_quantized_edge_program ,
17+ )
1318from executorch .backends .nxp .tests .executors import (
1419 convert_run_compare ,
20+ graph_contains_any_of_ops ,
1521 ToChannelFirstPreprocess ,
1622 ToChannelLastPreprocess ,
1723)
24+ from executorch .backends .nxp .tests .graph_verifier import DetailedGraphVerifier
1825from executorch .backends .nxp .tests .models import (
1926 AddTensorConvModule ,
2027 AddTensorModule ,
2128 AddTensorOneInputModule ,
2229)
30+ from executorch .backends .nxp .tests .nsys_testing import lower_run_compare
31+ from executorch .backends .nxp .tests .ops_aliases import (
32+ AddTensor ,
33+ Convolution ,
34+ ExecutorchDelegateCall ,
35+ )
2336from torch .export import ExportedProgram
2437from executorch .backends .nxp .tests .use_qat import * # noqa F403
2538
@@ -92,20 +105,26 @@ def test_add_tensor_one_input_quant_conversion(mocker, input_shape, use_qat):
92105
93106
94107@pytest .mark .parametrize (
95- "input_shape " ,
108+ "x_input_shape " ,
96109 [
97110 pytest .param ((1 , 4 , 8 , 8 ), id = "4D." ),
98111 pytest .param ((1 , 4 , 5 , 5 ), id = "4D, product of dims is not a multiple of 8." ),
99112 ],
100113)
101- def test_add_tensor_w_conv_quant_conversion (mocker , input_shape , use_qat ):
114+ def test_add_tensor_w_conv_quant_conversion (mocker , x_input_shape , use_qat ):
102115 model = AddTensorConvModule ()
103116
104117 converter_spy = mocker .spy (EdgeProgramToIRConverter , "convert_program" )
105118
119+ n , c , h , w = x_input_shape
120+ y_input_shape = (n , 8 , h , w )
121+
106122 # Run conversion
107123 _ = to_quantized_edge_program (
108- model , input_shape , use_qat = use_qat , use_neutron_for_format_conversion = False
124+ model ,
125+ [x_input_shape , y_input_shape ],
126+ use_qat = use_qat ,
127+ use_neutron_for_format_conversion = False ,
109128 )
110129
111130 # Capture generated model
@@ -114,7 +133,13 @@ def test_add_tensor_w_conv_quant_conversion(mocker, input_shape, use_qat):
114133 # Capture converted program
115134 exported_program : ExportedProgram = converter_spy .call_args .args [1 ]
116135
117- input_data = (np .random .random (input_shape ).astype (np .float32 ) * 50 ).astype (np .int8 )
136+ input_data_1 = (np .random .random (x_input_shape ).astype (np .float32 ) * 50 ).astype (
137+ np .int8
138+ )
139+ input_data_2 = (np .random .random (y_input_shape ).astype (np .float32 ) * 50 ).astype (
140+ np .int8
141+ )
142+ input_data = {0 : input_data_1 , 1 : input_data_2 }
118143
119144 convert_run_compare (
120145 exported_program ,
@@ -149,7 +174,7 @@ def test_add_tensor_broadcasting_unsupported_quant_conversion(
149174 nodes = list (edge_program .graph .nodes )
150175
151176 # Broadcast is not supported, node is not converted
152- assert nodes [6 ].target . __name__ == "aten.add.Tensor" # Add Tensor is not delegated.
177+ assert nodes [6 ].target == AddTensor # Add Tensor is not delegated.
153178
154179 # Capture converted program
155180 # exported_program: ExportedProgram = converter_spy.call_args.args[1]
@@ -159,3 +184,228 @@ def test_add_tensor_broadcasting_unsupported_quant_conversion(
159184 # input_data = {0: x_input_data, 1: y_input_data}
160185 #
161186 # convert_run_compare(exported_program, tfl_model=tflite_flatbuffers_model, input_data=input_data)
187+
188+
189+ class TestAddTensorNewNeutronFlow :
190+ @pytest .mark .parametrize (
191+ "x_input_shape" ,
192+ [
193+ pytest .param ((1 ,), id = "1D." ),
194+ pytest .param ((6 , 5 ), id = "2D." ),
195+ pytest .param ((1 , 4 , 7 ), id = "3D." ),
196+ pytest .param ((2 , 4 , 3 , 15 ), id = "4D." ),
197+ ],
198+ )
199+ def test__basic_nsys_inference (self , x_input_shape , mocker ):
200+ x_input_spec = ModelInputSpec (x_input_shape )
201+ model = AddTensorModule ()
202+ graph_verifier = DetailedGraphVerifier (
203+ mocker , expected_delegated_ops = {AddTensor : 1 }, expected_non_delegated_ops = {}
204+ )
205+ dataset_creator = RandomDatasetCreator (low = - 1.0 , high = 1.0 )
206+
207+ lower_run_compare (
208+ model ,
209+ [x_input_spec , x_input_spec ],
210+ graph_verifier ,
211+ dataset_creator ,
212+ use_new_flow_neutron_c = True ,
213+ )
214+
215+ @pytest .mark .xfail (strict = True , reason = "AIR-14602:incorrect results" )
216+ @pytest .mark .parametrize (
217+ "x_input_shape" ,
218+ [
219+ pytest .param ((6 , 82 ), id = "2D ." ),
220+ pytest .param ((1 , 68 , 7 ), id = "3D." ),
221+ pytest .param ((1 , 4 , 9 , 11 , 4 ), id = "5D." ),
222+ ],
223+ )
224+ def test__basic_nsys_inference_incorrect (self , x_input_shape , mocker ):
225+ x_input_spec = ModelInputSpec (x_input_shape )
226+ model = AddTensorModule ()
227+ graph_verifier = DetailedGraphVerifier (
228+ mocker , expected_delegated_ops = {AddTensor : 1 }, expected_non_delegated_ops = {}
229+ )
230+ dataset_creator = RandomDatasetCreator (low = - 1.0 , high = 1.0 )
231+
232+ lower_run_compare (
233+ model ,
234+ [x_input_spec , x_input_spec ],
235+ graph_verifier ,
236+ dataset_creator ,
237+ use_new_flow_neutron_c = True ,
238+ )
239+
240+ @pytest .mark .parametrize (
241+ "x_input_shape" ,
242+ [
243+ pytest .param ((1 ,), id = "1D." ),
244+ pytest .param ((6 , 5 ), id = "2D." ),
245+ pytest .param ((1 , 4 , 7 ), id = "3D." ),
246+ pytest .param ((2 , 4 , 3 , 15 ), id = "4D." ),
247+ # pytest.param((1, 4, 9, 11, 4), id="5D."),
248+ ],
249+ )
250+ def test__basic_nsys_inference_qat (self , x_input_shape , mocker ):
251+ x_input_spec = ModelInputSpec (x_input_shape )
252+ model = AddTensorModule ()
253+ graph_verifier = DetailedGraphVerifier (
254+ mocker , expected_delegated_ops = {AddTensor : 1 }, expected_non_delegated_ops = {}
255+ )
256+ dataset_creator = RandomDatasetCreator (low = - 1.0 , high = 1.0 )
257+
258+ lower_run_compare (
259+ model ,
260+ [x_input_spec , x_input_spec ],
261+ graph_verifier ,
262+ dataset_creator ,
263+ use_new_flow_neutron_c = True ,
264+ use_qat = True ,
265+ )
266+
267+ @pytest .mark .parametrize (
268+ "input_spec" ,
269+ [
270+ pytest .param (
271+ [ModelInputSpec ((4 , 6 )), ModelInputSpec ((1 , 6 ))], id = "2 inputs 2D."
272+ ),
273+ pytest .param (
274+ [ModelInputSpec ((5 , 3 , 4 )), ModelInputSpec ((1 , 3 , 1 ))],
275+ id = "2 inputs 3D." ,
276+ ),
277+ pytest .param (
278+ [ModelInputSpec ((4 ,)), ModelInputSpec ((4 , 4 ))], id = "2 inputs 1D + 2D."
279+ ),
280+ ],
281+ )
282+ def test__correct_broadcast (self , input_spec , mocker ):
283+ model = AddTensorModule ()
284+ graph_verifier = DetailedGraphVerifier (
285+ mocker , expected_delegated_ops = {AddTensor : 1 }, expected_non_delegated_ops = {}
286+ )
287+ dataset_creator = RandomDatasetCreator (low = - 1.0 , high = 1.0 )
288+
289+ lower_run_compare (
290+ model ,
291+ input_spec ,
292+ graph_verifier ,
293+ dataset_creator ,
294+ use_new_flow_neutron_c = True ,
295+ )
296+
297+ @pytest .mark .parametrize (
298+ "input_spec" ,
299+ [
300+ pytest .param (
301+ [ModelInputSpec ((4 , 1 )), ModelInputSpec ((1 , 6 ))], id = "2 inputs 2D."
302+ ),
303+ pytest .param (
304+ [ModelInputSpec ((1 , 3 , 4 )), ModelInputSpec ((5 , 3 , 1 ))],
305+ id = "2 inputs 3D." ,
306+ ),
307+ pytest .param (
308+ [ModelInputSpec ((6 , 4 )), ModelInputSpec ((6 , 6 , 1 ))],
309+ id = "2 inputs 2D + 3D." ,
310+ ),
311+ ],
312+ )
313+ def test__incorrect_broadcast (self , input_spec ):
314+ # Broadcast where at least one of the inputs is not equal to output is not supported
315+ model = AddTensorModule ()
316+
317+ delegated_ep = to_quantized_edge_program (
318+ model , input_spec , use_new_flow_neutron_c = True
319+ ).exported_program ()
320+
321+ # Make sure the `add.Tensor` was NOT delegated.
322+ assert not graph_contains_any_of_ops (
323+ delegated_ep .graph , [ExecutorchDelegateCall ]
324+ )
325+ assert graph_contains_any_of_ops (delegated_ep .graph , [AddTensor ])
326+
327+ @pytest .mark .xfail (strict = True , reason = "AIR-14602: incorrect results" )
328+ @pytest .mark .parametrize (
329+ "x_input_shape" ,
330+ [
331+ pytest .param (
332+ (1 , 4 , 5 , 5 ), id = "4D, product of dims is not a multiple of 8."
333+ ),
334+ ],
335+ )
336+ def test__w_conv (self , x_input_shape , mocker ):
337+ model = AddTensorConvModule ()
338+
339+ n , c , h , w = x_input_shape
340+ y_input_spec = ModelInputSpec ((n , 8 , h , w ))
341+ x_input_spec = ModelInputSpec (x_input_shape )
342+
343+ graph_verifier = DetailedGraphVerifier (
344+ mocker ,
345+ expected_delegated_ops = {AddTensor : 1 , Convolution : 1 },
346+ expected_non_delegated_ops = {},
347+ )
348+ dataset_creator = RandomDatasetCreator (low = - 1.0 , high = 1.0 )
349+
350+ lower_run_compare (
351+ model ,
352+ [x_input_spec , y_input_spec ],
353+ graph_verifier ,
354+ dataset_creator ,
355+ use_new_flow_neutron_c = True ,
356+ )
357+
358+ @pytest .mark .parametrize (
359+ "input_spec" ,
360+ [
361+ pytest .param (
362+ [ModelInputSpec ((1 , 4 , 5 , 5 )), ModelInputSpec ((1 , 8 , 5 , 1 ))],
363+ id = "2 inputs 4D + 4D." ,
364+ ),
365+ pytest .param (
366+ [ModelInputSpec ((1 , 4 , 4 , 10 )), ModelInputSpec ((1 , 1 , 4 , 1 ))],
367+ id = "2 inputs 4D + 4D." ,
368+ ),
369+ ],
370+ )
371+ def test__w_conv_correct_broadcast (self , input_spec , mocker ):
372+ model = AddTensorConvModule ()
373+
374+ graph_verifier = DetailedGraphVerifier (
375+ mocker ,
376+ expected_delegated_ops = {AddTensor : 1 , Convolution : 1 },
377+ expected_non_delegated_ops = {},
378+ )
379+ dataset_creator = RandomDatasetCreator (low = - 1.0 , high = 1.0 )
380+
381+ lower_run_compare (
382+ model ,
383+ input_spec ,
384+ graph_verifier ,
385+ dataset_creator ,
386+ use_new_flow_neutron_c = True ,
387+ )
388+
389+ @pytest .mark .parametrize (
390+ "input_spec" ,
391+ [
392+ pytest .param (
393+ [ModelInputSpec ((1 , 4 , 5 , 5 )), ModelInputSpec ((1 , 5 ))],
394+ id = "2 inputs 4D + 2D." ,
395+ ),
396+ pytest .param (
397+ [ModelInputSpec ((1 , 4 , 4 , 10 )), ModelInputSpec ((1 , 4 , 1 ))],
398+ id = "2 inputs 4D + 3D." ,
399+ ),
400+ ],
401+ )
402+ def test__w_conv_unsupported (self , input_spec ):
403+ model = AddTensorConvModule ()
404+
405+ delegated_ep = to_quantized_edge_program (
406+ model , input_spec , use_new_flow_neutron_c = True
407+ ).exported_program ()
408+
409+ # Make sure the `add.Tensor` was NOT delegated.
410+ assert graph_contains_any_of_ops (delegated_ep .graph , [ExecutorchDelegateCall ])
411+ assert graph_contains_any_of_ops (delegated_ep .graph , [AddTensor ])
0 commit comments