3838from ng_model_gym .usecases .nfru .model .nfru_v1_nn import ( # type: ignore[import-not-found,import-untyped]
3939 NFRUAutoEncoder ,
4040)
41+ from torch .export import Dim
4142from torchao .quantization .pt2e import (
4243 allow_exported_model_train_eval ,
4344 move_exported_model_to_eval ,
4748input_t = Tuple [torch .Tensor ] # Input x
4849
4950pytestmark = skip_if_frozen_release ("NFRU" )
51+ _NFRU_HEIGHT = 2 * Dim ("_nfru_height" , min = 16 , max = 135 )
52+ _NFRU_WIDTH = 2 * Dim ("_nfru_width" , min = 16 , max = 240 )
53+ _NFRU_DYNAMIC_SHAPES = ({2 : _NFRU_HEIGHT , 3 : _NFRU_WIDTH },)
5054
5155
5256def nfru () -> NFRUAutoEncoder :
@@ -73,7 +77,7 @@ def nfru() -> NFRUAutoEncoder:
7377 return model
7478
7579
76- def prequantized_nfru (inputs : input_t ) -> torch .fx .GraphModule :
80+ def prequantized_nfru (inputs : input_t , dynamic_shapes = None ) -> torch .fx .GraphModule :
7781 weights = hf_hub_download ( # nosec B615
7882 repo_id = "Arm/neural-frame-rate-upscaling" ,
7983 filename = "nfru_v1_int8.pt" ,
@@ -88,7 +92,9 @@ def prequantized_nfru(inputs: input_t) -> torch.fx.GraphModule:
8892 assert all (key .startswith (prefix ) for key in checkpoint )
8993 state_dict = {key .removeprefix (prefix ): value for key , value in checkpoint .items ()}
9094
91- exported = torch .export .export (nfru ().eval (), inputs , strict = True ).module ()
95+ exported = torch .export .export (
96+ nfru ().eval (), inputs , dynamic_shapes = dynamic_shapes , strict = True
97+ ).module ()
9298 quantizer = TOSAQuantizer (TosaSpecification .create_from_string ("TOSA-1.0+INT" ))
9399 quantizer .set_global (
94100 get_symmetric_quantization_config (is_per_channel = False , is_qat = True )
@@ -141,7 +147,10 @@ def random_inputs(memory_format: torch.memory_format = torch.channels_last):
141147
142148
143149def _set_nfru_calibration_samples (pipeline ):
144- return pipeline .set_quantization_calibration (iter_nfru_test_calibration_samples ())
150+ return pipeline .set_quantization_calibration (
151+ iter_nfru_test_calibration_samples (),
152+ dynamic_shapes = _NFRU_DYNAMIC_SHAPES ,
153+ )
145154
146155
147156@common .parametrize ("use_real_data" , input_test_data )
@@ -306,3 +315,182 @@ def test_nfru_vgf_quant_a16w8(use_real_data):
306315 if use_real_data :
307316 _set_nfru_calibration_samples (pipeline )
308317 pipeline .run ()
318+
319+
320+ @common .parametrize ("use_real_data" , input_test_data )
321+ def test_nfru_tosa_FP_dynamic_shapes (use_real_data ):
322+ pipeline = TosaPipelineFP [input_t ](
323+ nfru ().eval (),
324+ example_inputs () if use_real_data else random_inputs (),
325+ aten_op = [],
326+ exir_op = [],
327+ dynamic_shapes = _NFRU_DYNAMIC_SHAPES ,
328+ run_on_tosa_ref_model = False ,
329+ )
330+ pipeline .run ()
331+
332+
333+ @common .parametrize ("is_qat" , is_qat_test_data )
334+ @common .parametrize ("use_real_data" , input_test_data )
335+ def test_nfru_tosa_INT_dynamic_shapes (use_real_data , is_qat ):
336+ pipeline_kwargs = (
337+ {
338+ "per_channel_quantization" : False ,
339+ "use_to_edge_transform_and_lower" : True ,
340+ "frobenius_threshold" : None ,
341+ "cosine_threshold" : None ,
342+ }
343+ if is_qat
344+ else {}
345+ )
346+ pipeline = TosaPipelineINT [input_t ](
347+ nfru ().eval (),
348+ example_inputs () if use_real_data else random_inputs (),
349+ aten_op = [],
350+ exir_op = [],
351+ atol = 0.2 ,
352+ qtol = 2 if use_real_data else 1 ,
353+ is_qat = is_qat ,
354+ dynamic_shapes = _NFRU_DYNAMIC_SHAPES ,
355+ run_on_tosa_ref_model = False ,
356+ ** pipeline_kwargs ,
357+ )
358+ if use_real_data :
359+ _set_nfru_calibration_samples (pipeline )
360+ pipeline .run ()
361+
362+
363+ @common .parametrize ("use_real_data" , input_test_data )
364+ def test_nfru_prequantized_tosa_INT_dynamic_shapes (use_real_data ):
365+ inputs = example_inputs () if use_real_data else random_inputs ()
366+ pipeline = TosaPipelineINT [input_t ](
367+ prequantized_nfru (inputs , _NFRU_DYNAMIC_SHAPES ),
368+ inputs ,
369+ aten_op = [],
370+ exir_op = [],
371+ atol = 0.2 ,
372+ qtol = 2 if use_real_data else 1 ,
373+ dynamic_shapes = _NFRU_DYNAMIC_SHAPES ,
374+ run_on_tosa_ref_model = False ,
375+ )
376+ pipeline .pop_stage ("quantize" )
377+ pipeline .pop_stage ("check.quant_nodes" )
378+ pipeline .add_stage_after (
379+ "export" ,
380+ pipeline .tester .check ,
381+ ["torch.ops.quantized_decomposed.dequantize_per_tensor.default" ],
382+ suffix = "prequant_nodes" ,
383+ )
384+ pipeline .run ()
385+
386+
387+ @common .parametrize ("use_real_data" , input_test_data )
388+ def test_nfru_tosa_INT_a16w8_dynamic_shapes (use_real_data ):
389+ pipeline = TosaPipelineINT [input_t ](
390+ nfru ().eval (),
391+ example_inputs () if use_real_data else random_inputs (),
392+ aten_op = [],
393+ exir_op = [],
394+ tosa_extensions = ["int16" ],
395+ atol = 0.1 ,
396+ dynamic_shapes = _NFRU_DYNAMIC_SHAPES ,
397+ run_on_tosa_ref_model = False ,
398+ )
399+ if use_real_data :
400+ _set_nfru_calibration_samples (pipeline )
401+ pipeline .run ()
402+
403+
404+ @common .SkipIfNoModelConverter
405+ @common .parametrize ("use_real_data" , input_test_data )
406+ def test_nfru_vgf_no_quant_dynamic_shapes (use_real_data ):
407+ pipeline = VgfPipeline [input_t ](
408+ nfru ().eval (),
409+ example_inputs () if use_real_data else random_inputs (),
410+ aten_op = [],
411+ exir_op = [],
412+ tosa_version = "TOSA-1.0+FP" ,
413+ quantize = False ,
414+ dynamic_shapes = _NFRU_DYNAMIC_SHAPES ,
415+ run_on_vulkan_runtime = False ,
416+ )
417+ pipeline .run ()
418+
419+
420+ @common .SkipIfNoModelConverter
421+ @common .parametrize ("is_qat" , is_qat_test_data )
422+ @common .parametrize ("use_real_data" , input_test_data )
423+ def test_nfru_vgf_quant_dynamic_shapes (use_real_data , is_qat ):
424+ pipeline_kwargs = (
425+ {
426+ "per_channel_quantization" : False ,
427+ "use_to_edge_transform_and_lower" : True ,
428+ }
429+ if is_qat
430+ else {}
431+ )
432+ pipeline = VgfPipeline [input_t ](
433+ nfru ().eval (),
434+ example_inputs () if use_real_data else random_inputs (),
435+ aten_op = [],
436+ exir_op = [],
437+ tosa_version = "TOSA-1.0+INT" ,
438+ symmetric_io_quantization = True ,
439+ quantize = True ,
440+ is_qat = is_qat ,
441+ atol = 0.2 ,
442+ qtol = 2 if use_real_data else 1 ,
443+ dynamic_shapes = _NFRU_DYNAMIC_SHAPES ,
444+ run_on_vulkan_runtime = False ,
445+ ** pipeline_kwargs ,
446+ )
447+ if use_real_data :
448+ _set_nfru_calibration_samples (pipeline )
449+ pipeline .run ()
450+
451+
452+ @common .SkipIfNoModelConverter
453+ @common .parametrize ("use_real_data" , input_test_data )
454+ def test_nfru_prequantized_vgf_INT_dynamic_shapes (use_real_data ):
455+ inputs = example_inputs () if use_real_data else random_inputs ()
456+ pipeline = VgfPipeline [input_t ](
457+ prequantized_nfru (inputs , _NFRU_DYNAMIC_SHAPES ),
458+ inputs ,
459+ aten_op = [],
460+ exir_op = [],
461+ tosa_version = "TOSA-1.0+INT" ,
462+ quantize = True ,
463+ atol = 0.2 ,
464+ qtol = 2 if use_real_data else 1 ,
465+ dynamic_shapes = _NFRU_DYNAMIC_SHAPES ,
466+ run_on_vulkan_runtime = False ,
467+ )
468+ pipeline .pop_stage ("quantize" )
469+ pipeline .pop_stage ("check.quant_nodes" )
470+ pipeline .add_stage_after (
471+ "export" ,
472+ pipeline .tester .check ,
473+ ["torch.ops.quantized_decomposed.dequantize_per_tensor.default" ],
474+ suffix = "prequant_nodes" ,
475+ )
476+ pipeline .run ()
477+
478+
479+ @common .SkipIfNoModelConverter
480+ @common .parametrize ("use_real_data" , input_test_data )
481+ def test_nfru_vgf_quant_a16w8_dynamic_shapes (use_real_data ):
482+ pipeline = VgfPipeline [input_t ](
483+ nfru ().eval (),
484+ example_inputs () if use_real_data else random_inputs (),
485+ aten_op = [],
486+ exir_op = [],
487+ tosa_version = "TOSA-1.0+INT" ,
488+ tosa_extensions = ["int16" ],
489+ symmetric_io_quantization = True ,
490+ atol = 0.2 ,
491+ dynamic_shapes = _NFRU_DYNAMIC_SHAPES ,
492+ run_on_vulkan_runtime = False ,
493+ )
494+ if use_real_data :
495+ _set_nfru_calibration_samples (pipeline )
496+ pipeline .run ()
0 commit comments