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.
55
66import numpy as np
77import pytest
88import torch
9-
109from executorch .backends .nxp .backend .edge_program_converter import (
1110 EdgeProgramToIRConverter ,
1211)
1716 ToChannelFirstPreprocess ,
1817 ToChannelLastPreprocess ,
1918)
19+ from executorch .backends .nxp .tests .graph_verifier import (
20+ BaseGraphVerifier ,
21+ NonDelegatedNode ,
22+ )
23+
24+ from executorch .backends .nxp .tests .nsys_testing import (
25+ lower_run_compare ,
26+ RandomDatasetCreator ,
27+ )
2028
2129from executorch .exir .dialects ._ops import ops as exir_ops
2230from torch .export import ExportedProgram
@@ -29,7 +37,7 @@ def reseed_model_per_test_run():
2937 np .random .seed (23 )
3038
3139
32- class ConvBlocksWithAbs (torch .nn .Module ):
40+ class ConvBlocksWithAbsModule (torch .nn .Module ):
3341 def __init__ (self , conv_in_channels : int = 3 ):
3442 super ().__init__ ()
3543 self .block1 = torch .nn .Sequential (
@@ -56,36 +64,112 @@ def forward(self, x):
5664 return self .block2 (x )
5765
5866
59- class Abs (torch .nn .Module ):
67+ class AbsModule (torch .nn .Module ):
6068 def __init__ (self ):
6169 super ().__init__ ()
6270
6371 def forward (self , x ):
6472 return x .abs ()
6573
6674
67- def test_conv_abs (mocker , use_qat , input_shape : tuple [int ] = (1 , 3 , 112 , 112 )):
68- model = ConvBlocksWithAbs (conv_in_channels = input_shape [1 ])
75+ class TestAbsLegacyNeutronFlow :
76+ def test_conv_abs (
77+ self , mocker , use_qat , input_shape : tuple [int , ...] = (1 , 3 , 112 , 112 )
78+ ):
79+ model = ConvBlocksWithAbsModule (conv_in_channels = input_shape [1 ])
6980
70- converter_spy = mocker .spy (EdgeProgramToIRConverter , "convert_program" )
81+ converter_spy = mocker .spy (EdgeProgramToIRConverter , "convert_program" )
7182
72- quantized_program = to_quantized_edge_program (
73- model , input_shape , use_qat = use_qat , use_neutron_for_format_conversion = False
74- ).exported_program ()
83+ quantized_program = to_quantized_edge_program (
84+ model ,
85+ input_shape ,
86+ use_qat = use_qat ,
87+ use_neutron_for_format_conversion = False ,
88+ use_new_flow_neutron_c = False ,
89+ ).exported_program ()
7590
76- tflite_flatbuffers_model , io_formats = converter_spy .spy_return
77- exported_program : ExportedProgram = converter_spy .call_args .args [1 ]
91+ tflite_flatbuffers_model , io_formats = converter_spy .spy_return
92+ exported_program : ExportedProgram = converter_spy .call_args .args [1 ]
7893
79- assert not graph_contains_any_of_ops (
80- graph = quantized_program .graph , ops = [exir_ops .edge .aten .abs .default ]
81- )
94+ assert not graph_contains_any_of_ops (
95+ graph = quantized_program .graph , ops = [exir_ops .edge .aten .abs .default ]
96+ )
8297
83- input_data = (np .random .random (input_shape ) * 50 ).astype (np .int8 )
84- convert_run_compare (
85- exported_program ,
86- tfl_model = tflite_flatbuffers_model ,
87- tflite_input_preprocess = ToChannelLastPreprocess (),
88- tflite_output_preprocess = ToChannelFirstPreprocess (),
89- input_data = input_data ,
90- atol = 1.0 ,
91- )
98+ input_data = (np .random .random (input_shape ) * 50 ).astype (np .int8 )
99+ convert_run_compare (
100+ exported_program ,
101+ tfl_model = tflite_flatbuffers_model ,
102+ tflite_input_preprocess = ToChannelLastPreprocess (),
103+ tflite_output_preprocess = ToChannelFirstPreprocess (),
104+ input_data = input_data ,
105+ atol = 1.0 ,
106+ )
107+
108+
109+ class TestAbsNewNeutronFlow :
110+ @staticmethod
111+ def _get_dataset_creator ():
112+ # to test `abs` reliably, we need to include negative values
113+ low = - 255.0
114+ high = 255.0
115+
116+ dataset = RandomDatasetCreator (low = low , high = high )
117+ return dataset
118+
119+ def test__basic_nsys_inference (self ):
120+ input_shape = (2 , 3 , 6 , 7 )
121+ model = AbsModule ()
122+ graph_verifier = BaseGraphVerifier (
123+ exp_num_delegate_call_nodes = 1 , # Delegated Abs.
124+ exp_non_delegated_nodes = [],
125+ )
126+
127+ dataset_creator = self ._get_dataset_creator ()
128+ lower_run_compare (
129+ model ,
130+ input_shape ,
131+ graph_verifier ,
132+ dataset_creator ,
133+ use_new_flow_neutron_c = True ,
134+ )
135+
136+ def test__basic_nsys_inference__big (self ):
137+ # some operators have delegation requirement that size must be < 4096
138+ input_shape = (4097 , 1 )
139+ model = AbsModule ()
140+ graph_verifier = BaseGraphVerifier (
141+ exp_num_delegate_call_nodes = 1 , # Delegated Abs.
142+ exp_non_delegated_nodes = [],
143+ )
144+
145+ dataset_creator = self ._get_dataset_creator ()
146+ lower_run_compare (
147+ model ,
148+ input_shape ,
149+ graph_verifier ,
150+ dataset_creator ,
151+ use_new_flow_neutron_c = True ,
152+ )
153+
154+ def test_basic_nsys_inference__with_conv (self ):
155+ input_shape = (2 , 3 , 6 , 7 )
156+ in_channels = input_shape [1 ]
157+ model = ConvBlocksWithAbsModule (conv_in_channels = in_channels )
158+ graph_verifier = BaseGraphVerifier (
159+ exp_num_delegate_call_nodes = 1 , # Delegated `Abs` + `Relu` in one partition
160+ exp_non_delegated_nodes = [
161+ NonDelegatedNode ("aten_convolution_default" , 2 ),
162+ NonDelegatedNode (
163+ "aten_relu_default" , 1 # One `Relu` ends up in partition with `Abs`
164+ ),
165+ ],
166+ )
167+
168+ dataset_creator = self ._get_dataset_creator ()
169+ lower_run_compare (
170+ model ,
171+ input_shape ,
172+ graph_verifier ,
173+ dataset_creator ,
174+ use_new_flow_neutron_c = True ,
175+ )
0 commit comments