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 DetailedGraphVerifier
20+
21+ from executorch .backends .nxp .tests .nsys_testing import (
22+ lower_run_compare ,
23+ RandomDatasetCreator ,
24+ )
25+ from executorch .backends .nxp .tests .ops_aliases import Abs , Convolution , Relu
2026
2127from executorch .exir .dialects ._ops import ops as exir_ops
2228from torch .export import ExportedProgram
@@ -29,7 +35,7 @@ def reseed_model_per_test_run():
2935 np .random .seed (23 )
3036
3137
32- class ConvBlocksWithAbs (torch .nn .Module ):
38+ class ConvBlocksWithAbsModule (torch .nn .Module ):
3339 def __init__ (self , conv_in_channels : int = 3 ):
3440 super ().__init__ ()
3541 self .block1 = torch .nn .Sequential (
@@ -56,36 +62,108 @@ def forward(self, x):
5662 return self .block2 (x )
5763
5864
59- class Abs (torch .nn .Module ):
65+ class AbsModule (torch .nn .Module ):
6066 def __init__ (self ):
6167 super ().__init__ ()
6268
6369 def forward (self , x ):
6470 return x .abs ()
6571
6672
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 ])
73+ class TestAbsLegacyNeutronFlow :
74+ def test_conv_abs (
75+ self , mocker , use_qat , input_shape : tuple [int , ...] = (1 , 3 , 112 , 112 )
76+ ):
77+ model = ConvBlocksWithAbsModule (conv_in_channels = input_shape [1 ])
6978
70- converter_spy = mocker .spy (EdgeProgramToIRConverter , "convert_program" )
79+ converter_spy = mocker .spy (EdgeProgramToIRConverter , "convert_program" )
7180
72- quantized_program = to_quantized_edge_program (
73- model , input_shape , use_qat = use_qat , use_neutron_for_format_conversion = False
74- ).exported_program ()
81+ quantized_program = to_quantized_edge_program (
82+ model ,
83+ input_shape ,
84+ use_qat = use_qat ,
85+ use_neutron_for_format_conversion = False ,
86+ use_new_flow_neutron_c = False ,
87+ ).exported_program ()
7588
76- tflite_flatbuffers_model , io_formats = converter_spy .spy_return
77- exported_program : ExportedProgram = converter_spy .call_args .args [1 ]
89+ tflite_flatbuffers_model , io_formats = converter_spy .spy_return
90+ exported_program : ExportedProgram = converter_spy .call_args .args [1 ]
7891
79- assert not graph_contains_any_of_ops (
80- graph = quantized_program .graph , ops = [exir_ops .edge .aten .abs .default ]
81- )
92+ assert not graph_contains_any_of_ops (
93+ graph = quantized_program .graph , ops = [exir_ops .edge .aten .abs .default ]
94+ )
95+
96+ input_data = (np .random .random (input_shape ) * 50 ).astype (np .int8 )
97+ convert_run_compare (
98+ exported_program ,
99+ tfl_model = tflite_flatbuffers_model ,
100+ tflite_input_preprocess = ToChannelLastPreprocess (),
101+ tflite_output_preprocess = ToChannelFirstPreprocess (),
102+ input_data = input_data ,
103+ atol = 1.0 ,
104+ )
105+
106+
107+ class TestAbsNewNeutronFlow :
108+ @staticmethod
109+ def _get_dataset_creator ():
110+ # to test `abs` reliably, we need to include negative values
111+ low = - 255.0
112+ high = 255.0
113+
114+ dataset = RandomDatasetCreator (low = low , high = high )
115+ return dataset
116+
117+ def test__basic_nsys_inference (self , mocker ):
118+ input_shape = (2 , 3 , 6 , 7 )
119+ model = AbsModule ()
120+ graph_verifier = DetailedGraphVerifier (
121+ mocker , expected_delegated_ops = {Abs : 1 }, expected_non_delegated_ops = {}
122+ )
82123
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- )
124+ dataset_creator = self ._get_dataset_creator ()
125+ lower_run_compare (
126+ model ,
127+ input_shape ,
128+ graph_verifier ,
129+ dataset_creator ,
130+ use_new_flow_neutron_c = True ,
131+ )
132+
133+ def test__basic_nsys_inference__big (self , mocker ):
134+ # some operators have delegation requirement that size must be < 4096
135+ input_shape = (4097 , 1 )
136+ model = AbsModule ()
137+ graph_verifier = DetailedGraphVerifier (
138+ mocker , expected_delegated_ops = {Abs : 1 }, expected_non_delegated_ops = {}
139+ )
140+
141+ dataset_creator = self ._get_dataset_creator ()
142+ lower_run_compare (
143+ model ,
144+ input_shape ,
145+ graph_verifier ,
146+ dataset_creator ,
147+ use_new_flow_neutron_c = True ,
148+ )
149+
150+ def test_basic_nsys_inference__with_conv (self , mocker ):
151+ input_shape = (2 , 3 , 6 , 7 )
152+ in_channels = input_shape [1 ]
153+ model = ConvBlocksWithAbsModule (conv_in_channels = in_channels )
154+
155+ # one `relu` ends up in the same delegated partition as `abs`
156+ graph_verifier = DetailedGraphVerifier (
157+ mocker ,
158+ expected_delegated_ops = {Abs : 1 , Relu : 1 },
159+ expected_non_delegated_ops = {Relu : 1 , Convolution : 2 },
160+ )
161+
162+ dataset_creator = self ._get_dataset_creator ()
163+ lower_run_compare (
164+ model ,
165+ input_shape ,
166+ graph_verifier ,
167+ dataset_creator ,
168+ use_new_flow_neutron_c = True ,
169+ )
0 commit comments