# Copyright (c) Qualcomm Innovation Center, Inc. # All rights reserved # # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. import io import json import subprocess import sys import tempfile import unittest from multiprocessing.connection import Listener from pathlib import Path import torch from executorch.backends.qualcomm._passes.qnn_pass_manager import ( get_capture_program_passes, QnnPassManager, ) from executorch.backends.qualcomm._passes.utils import ( get_passes_dependency_for_capture_program, ) from executorch.backends.qualcomm.tests.utils import ( generate_context_binary, ModuleQConfig, QnnTool, QuantDtype, TestQNN, validate_context_binary, ) from executorch.backends.qualcomm.utils.constants import ( QCOM_ANNOTATION, QCOM_MODULE, QCOM_PASS_ACTIVATE_KEY, QCOM_PASS_ARGS_KWARGS_DEFAULTS_KEY, QCOM_QUANT_ATTRS_MAP, QCOM_QUANT_DTYPE, QCOM_SAMPLE_INPUTS, ) from executorch.backends.qualcomm.utils.utils import ( capture_program, dump_context_from_pte, from_context_binary, generate_htp_compiler_spec, generate_multi_graph_program, generate_qnn_executorch_compiler_spec, PyQnnManagerAdaptor, skip_annotation, to_edge_transform_and_lower_to_qnn, update_spill_fill_size, ) from executorch.examples.models.llama.llama_transformer import MOEFeedForward from executorch.examples.models.llama.model_args import ModelArgs from executorch.examples.qualcomm.utils import ( make_quantizer, setup_common_args_and_variables, ) from executorch.backends.qualcomm.tests.models import * # noqa: F403 import os import random from collections import defaultdict from typing import List from executorch.backends.qualcomm._passes import ( ExpandBroadcastTensorShape, FoldQDQ, TagQuantIO, ) from executorch.backends.qualcomm.builders.node_visitor import get_node_visitors from executorch.backends.qualcomm.debugger.utils import DrawGraph from executorch.examples.models.deeplab_v3 import DeepLabV3ResNet101Model from executorch.examples.models.edsr import EdsrModel from executorch.examples.models.inception_v3 import InceptionV3Model from executorch.examples.models.inception_v4 import InceptionV4Model # from executorch.examples.models.mobilebert import MobileBertModelExample from executorch.examples.models.mobilenet_v2 import MV2Model from executorch.examples.models.mobilenet_v3 import MV3Model from executorch.examples.models.torchvision_vit.model import TorchVisionViTModel from executorch.examples.models.wav2letter import Wav2LetterModel from executorch.exir import to_edge from executorch.exir.backend.backend_api import disable_validation class TestQNNFloatingPointOperator(TestQNN): # TODO: refactor to support different backends def setUp(self): TestQNN.atol = 1e-1 TestQNN.rtol = 1e-1 backend_options = generate_htp_compiler_spec(use_fp16=True) TestQNN.compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, debug=False, saver=False, online_prepare=TestQNN.online_prepare, dump_intermediate_outputs=TestQNN.dump_intermediate_outputs, profile=TestQNN.enable_profile, shared_buffer=TestQNN.shared_buffer, ) def test_qnn_backend_abs(self): module = Abs() # noqa: F405 sample_input = (torch.randn(1, 2, 3, 4),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_adaptive_avg_pool2d(self): module = AdaptiveAvgPool2D() # noqa: F405 sample_input = (torch.randn(1, 512, 7, 7),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_amax(self): modules = [AMax(dim=1, keepdim=False), AMax(dim=1, keepdim=True)] # noqa: F405 sample_input = (torch.randn(4, 4),) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_any(self): modules = [Any(), Any(dim=[0, 1]), Any(dim=1, keepdim=True)] # noqa: F405 sample_input = (torch.randn(3, 3, 3) > 0,) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_arange(self): modules = [ Arange(start=1, end=11, step=1, dtype=torch.int32), # noqa: F405 ] sample_input = (torch.randn(10),) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_argmin(self): module = Argmin() # noqa: F405 sample_input = (torch.randn(16, 3, 4, 4),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_avg_pool2d(self): module = AvgPoolModule() # noqa: F405 sample_input = (torch.randn(1, 3, 2, 2),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_batch_norm(self): modules = [BatchNorm(32), BatchNorm(32, False)] # noqa: F405 sample_input = (torch.randn([4, 32, 16, 16]),) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_bmm(self): module = Bmm() # noqa: F405 torch.manual_seed(8) sample_input = (torch.randn([4, 8, 32]), torch.randn([4, 32, 8])) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_cast(self): module = Cast() # noqa: F405 sample_input = (10 * torch.rand((9, 4, 5, 3)),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_cat(self): modules = [Cat2(), Cat3(), Cat4()] # noqa: F405 sample_input = (torch.randn(1, 1, 2, 2), torch.randn(1, 1, 4, 2)) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_cdist(self): module = CDist() # noqa: F405 sample_input = ( torch.randn(1, 125, 256), torch.randn(1, 2048, 256), ) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_chunk_single(self): module = Chunk() # noqa: F405 sample_input = (torch.randn(1, 1, 4, 3),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_clamp(self): modules = [Clamp(), ClampMin(1e-10), ClampMax(1e10)] # noqa: F405 sample_input = (torch.randn((9, 4, 5, 3)),) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv1d(self): modules = [Conv1dSequential(), Conv1dSequential(bias=False)] # noqa: F405 sample_input = (torch.randn([1, 1, 3]),) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d(self): modules = [Conv2dSequential(), Conv2dSequential(bias=False)] # noqa: F405 sample_input = (torch.randn([1, 1, 3, 3]),) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_channel_last(self): modules = [ Conv2dSequential(channel_last=True), # noqa: F405 Conv2dSequential(bias=False, channel_last=True), # noqa: F405 ] sample_input = (torch.randn([1, 1, 3, 3]),) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv_transpose1d(self): modules = [ ConvTranspose1dSingle(), # noqa: F405 ConvTranspose1dSingle(bias=False), # noqa: F405 ] sample_input = (torch.randn([1, 1, 3]),) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv_transpose2d(self): modules = [ ConvTranspose2dSingle(), # noqa: F405 ConvTranspose2dSingle(bias=False), # noqa: F405 ] sample_input = (torch.randn([1, 1, 3, 3]),) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_cos(self): module = Cos() # noqa: F405 sample_input = (torch.randn(2, 5, 1, 3),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_cumsum(self): module = CumSum() # noqa: F405 sample_input = (torch.randn(4),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_einsum_outer_product(self): module = EinsumOuterProduct() # noqa: F405 x = torch.randn(5) y = torch.randn(4) sample_input = ( x, y, ) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_einsum_bilinear(self): module = EinsumBilinear() # noqa: F405 bn = torch.randn(2, 5) anm = torch.randn(3, 5, 4) bm = torch.randn(2, 4) sample_input = ( bn, anm, bm, ) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_element_wise_add(self): test_comb = [ { QCOM_MODULE: [Add()], # noqa: F405 QCOM_SAMPLE_INPUTS: [ (torch.randn(2, 5, 1, 3), torch.randn(2, 5, 1, 3)), (torch.randn([2, 5, 1, 3]), torch.randn([4, 1])), ], }, { QCOM_MODULE: [AddConstantFloat()], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, ] index = 0 for comb in test_comb: for module in comb[QCOM_MODULE]: for sample_input in comb[QCOM_SAMPLE_INPUTS]: with self.subTest(i=index): self.lower_module_and_test_output(module, sample_input) index += 1 def test_qnn_backend_element_wise_and(self): module = And(torch.tensor(1.7), torch.tensor(0.2)) # noqa: F405 sample_input = ( torch.tensor([1, 0, 1, 0], dtype=torch.bool), torch.tensor([1, 1, 0, 0], dtype=torch.bool), ) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_element_wise_ceil(self): module = Ceil() # noqa: F405 sample_input = (torch.randn([2, 5, 1, 3]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_element_wise_div(self): eps = 1e-03 torch.manual_seed(8) test_comb = [ { QCOM_MODULE: [Div()], # noqa: F405 QCOM_SAMPLE_INPUTS: [ (torch.randn(2, 5, 1, 3), eps + torch.randn(2, 5, 1, 3)), (torch.randn([2, 5, 1, 3]), eps + torch.randn([4, 1])), ], }, { QCOM_MODULE: [DivConstantFloat()], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, ] index = 0 for comb in test_comb: for module in comb[QCOM_MODULE]: for sample_input in comb[QCOM_SAMPLE_INPUTS]: with self.subTest(i=index): self.lower_module_and_test_output(module, sample_input) index += 1 def test_qnn_backend_element_wise_mul(self): test_comb = [ { QCOM_MODULE: [Mul()], # noqa: F405 QCOM_SAMPLE_INPUTS: [ (torch.randn(2, 5, 1, 3), torch.randn(2, 5, 1, 3)), (torch.randn([2, 5, 1, 3]), torch.randn([4, 1])), ], }, { QCOM_MODULE: [MulConstantFloat()], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, { QCOM_MODULE: [MulScalar()], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, ] index = 0 for comb in test_comb: for module in comb[QCOM_MODULE]: for sample_input in comb[QCOM_SAMPLE_INPUTS]: with self.subTest(i=index): self.lower_module_and_test_output(module, sample_input) index += 1 def test_qnn_backend_element_wise_or(self): test_comb = [ { QCOM_MODULE: OrBitWise( # noqa: F405 torch.tensor(1.7), torch.tensor(0.2) ), QCOM_SAMPLE_INPUTS: ( torch.tensor([1, 0, 1, 0], dtype=torch.bool), torch.tensor([1, 1, 0, 0], dtype=torch.bool), ), }, { QCOM_MODULE: OrOperator( # noqa: F405 torch.tensor(1.5), torch.tensor(-1.2) ), QCOM_SAMPLE_INPUTS: ( torch.full((3, 3), 1).triu(), torch.full((3, 3), 1).tril(diagonal=0), ), }, ] for i, test in enumerate(test_comb): with self.subTest(i=i): self.lower_module_and_test_output( test[QCOM_MODULE], test[QCOM_SAMPLE_INPUTS] ) def test_qnn_backend_element_wise_sqrt(self): modules = [Sqrt(), SqrtConstant()] # noqa: F405 for i, module in enumerate(modules): sample_input = (torch.rand([3, 1]),) with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_element_wise_sub(self): test_comb = [ { QCOM_MODULE: [Sub()], # noqa: F405 QCOM_SAMPLE_INPUTS: [ (torch.randn(2, 5, 1, 3), torch.randn(2, 5, 1, 3)), (torch.randn([2, 5, 1, 3]), torch.randn([4, 1])), ], }, { QCOM_MODULE: [SubConstantFloat()], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, ] index = 0 for comb in test_comb: for module in comb[QCOM_MODULE]: for sample_input in comb[QCOM_SAMPLE_INPUTS]: with self.subTest(i=index): self.lower_module_and_test_output(module, sample_input) index += 1 @unittest.expectedFailure def test_qnn_backend_elu(self): module = Elu() # noqa: F405 sample_input = (torch.randn(2, 5, 1, 3),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_embedding(self): module = Embedding() # noqa: F405 sample_input = (torch.Tensor([[1, 2, 4, 5], [4, 3, 2, 9]]).to(torch.int32),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_equal(self): test_comb = [ { QCOM_MODULE: Equal(), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4), torch.randn(2, 3, 4)), }, { QCOM_MODULE: EqualConstant(0.5), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4),), }, ] for i, test in enumerate(test_comb): with self.subTest(i=i): self.lower_module_and_test_output( test[QCOM_MODULE], test[QCOM_SAMPLE_INPUTS] ) def test_qnn_backend_expand(self): modules = [ExpandAs(), ExpandCopy()] # noqa: F405 sample_inputs = [ (torch.randn([3, 1]),), (torch.randn([4]),), ] passes_job = get_capture_program_passes() passes_job[ExpandBroadcastTensorShape][QCOM_PASS_ACTIVATE_KEY] = True index = 0 for module in modules: for sample_input in sample_inputs: with self.subTest(i=index): self.lower_module_and_test_output( module, sample_input, passes_job=passes_job ) index += 1 def test_qnn_backend_expm1(self): sample_input = (torch.randn(3, 4, 5),) module = ExpM1() # noqa: F405 self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_full(self): shape = (1, 2, 3, 4) module = Full(0.5, shape) # noqa: F405 sample_input = (torch.randn(shape),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_full_like(self): module = FullLike(0.5) # noqa: F405 sample_input = (torch.randn(1, 2, 3, 4),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_gelu(self): module = Gelu() # noqa: F405 sample_input = (torch.randn(2, 5, 1, 3),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_greater_equal(self): test_comb = [ { QCOM_MODULE: GreaterEqual(), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4), torch.randn(2, 3, 4)), }, { QCOM_MODULE: GreaterEqualConstant(0.5), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4),), }, ] for i, test in enumerate(test_comb): with self.subTest(i=i): self.lower_module_and_test_output( test[QCOM_MODULE], test[QCOM_SAMPLE_INPUTS] ) def test_qnn_backend_greater_than(self): test_comb = [ { QCOM_MODULE: GreaterThan(), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4), torch.randn(2, 3, 4)), }, { QCOM_MODULE: GreaterThanConstant(0.5), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4),), }, ] for i, test in enumerate(test_comb): with self.subTest(i=i): self.lower_module_and_test_output( test[QCOM_MODULE], test[QCOM_SAMPLE_INPUTS] ) def test_qnn_backend_group_norm(self): modules = [GroupNorm(), GroupNorm(bias=False)] # noqa: F405 sample_input = (torch.randn(3, 32, 56, 56),) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_hardsigmoid(self): module = HardSigmoid() # noqa: F405 sample_input = (torch.randn(2, 5, 1, 3),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_hardswish(self): module = HardSwish() # noqa: F405 sample_input = (torch.randn(2, 5, 1, 3),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_hardtanh(self): module = HardTanh() # noqa: F405 sample_input = (torch.randn([2, 5, 1, 3]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_index(self): modules = [Index(0), Index(1), Index(2)] # noqa: F405 sample_input = (torch.randn([8, 172, 64]),) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_index_put(self): module = IndexPut() # noqa: F405 sample_input = ( torch.tensor([2], dtype=torch.int32), torch.randn([1, 1, 12, 64]), ) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_instance_norm_2d(self): modules = [InstanceNorm2d(32), InstanceNorm2d(32, affine=False)] # noqa: F405 sample_input = (torch.randn([4, 32, 16, 16]),) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_interpolate_bilinear_2d(self): module = ResizeBilinear2D() # noqa: F405 sample_input = (torch.randn(2, 3, 4, 5),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_interpolate_nearest_2d(self): module = ResizeNearest2D() # noqa: F405 sample_input = (torch.randn(2, 3, 4, 5),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_up_sampling_nearest_2d_with_scale_factor(self): module = UpsampleNearest2D(scale_factor=2) # noqa: F405 sample_input = (torch.randn(1, 16, 72, 104),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_up_sampling_nearest_2d_with_size(self): module = UpsampleNearest2D(sizes=(144, 208)) # noqa: F405 sample_input = (torch.randn(1, 16, 72, 104),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_layer_norm(self): modules = [LayerNorm(), LayerNorm(bias=False)] # noqa: F405 sample_input = (torch.randn(196, 768),) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_leaky_relu(self): test_comb = [ { QCOM_MODULE: [LeakyReLUDefault()], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, { QCOM_MODULE: [LeakyReLUCustom(0.05, inplace=False)], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, { QCOM_MODULE: [LeakyReLUCustom(0.05, inplace=True)], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, ] index = 0 for comb in test_comb: for module in comb[QCOM_MODULE]: for sample_input in comb[QCOM_SAMPLE_INPUTS]: with self.subTest(i=index): self.lower_module_and_test_output(module, sample_input) index += 1 def test_qnn_backend_less_equal(self): test_comb = [ { QCOM_MODULE: LessEqual(), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4), torch.randn(2, 3, 4)), }, { QCOM_MODULE: LessEqualConstant(0.5), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4),), }, ] for i, test in enumerate(test_comb): with self.subTest(i=i): self.lower_module_and_test_output( test[QCOM_MODULE], test[QCOM_SAMPLE_INPUTS] ) def test_qnn_backend_less_than(self): test_comb = [ { QCOM_MODULE: LessThan(), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4), torch.randn(2, 3, 4)), }, { QCOM_MODULE: LessThanConstant(0.5), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4),), }, ] for i, test in enumerate(test_comb): with self.subTest(i=i): self.lower_module_and_test_output( test[QCOM_MODULE], test[QCOM_SAMPLE_INPUTS] ) def test_qnn_backend_linalg_vector_norm(self): modules = [ LinalgVectorNorm(), # noqa: F405 LinalgVectorNorm(ord=3.5), # noqa: F405 LinalgVectorNorm(dim=1), # noqa: F405 LinalgVectorNorm(dim=1, keepdim=True), # noqa: F405 ] sample_input = (torch.randn([3, 3]),) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_linear(self): module = Linear() # noqa: F405 sample_input = (torch.randn([3, 4]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_log(self): module = Log() # noqa: F405 sample_input = (torch.rand([1, 2, 3, 4]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_logical_not(self): module = LogicalNot() # noqa: F405 sample_input = (torch.rand([1, 2, 3, 4]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_log_softmax(self): module = LogSoftmax() # noqa: F405 sample_input = (torch.randn([1, 4, 8, 8]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_maximum(self): module = Maximum() # noqa: F405 sample_input = (torch.randn(1, 2, 3, 4), torch.randn(2, 3, 4)) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_max_pool2d(self): module = MaxPool2d() # noqa: F405 sample_input = (torch.randn(4, 3, 24, 24),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_mean_dim(self): modules = [MeanWKeppDim(), MeanWOKeppDim()] # noqa: F405 sample_input = (torch.randn([2, 5, 1, 3]),) for i, module in enumerate(modules): with self.subTest(i=i): self.lower_module_and_test_output(module, sample_input) @unittest.skip("failed to lower in QNN 2.26") def test_qnn_backend_mha(self): module = MultiheadAttention() # noqa: F405 sample_input = (torch.randn(1, 197, 96),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_minimum(self): module = Minimum() # noqa: F405 sample_input = (torch.randn(1, 2, 3, 4), torch.randn(2, 3, 4)) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_neg(self): module = Neg() # noqa: F405 sample_input = (torch.randn(1, 4, 16, 16),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_not_equal(self): test_comb = [ { QCOM_MODULE: NotEqual(), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4), torch.randn(2, 3, 4)), }, { QCOM_MODULE: NotEqualConstant(0.5), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4),), }, ] for i, test in enumerate(test_comb): with self.subTest(i=i): self.lower_module_and_test_output( test[QCOM_MODULE], test[QCOM_SAMPLE_INPUTS] ) def test_qnn_backend_pad(self): module = Pad() # noqa: F405 sample_input = (torch.randn([1, 8, 128]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_pixel_shuffle(self): module = PixelShuffle(2) # noqa: F405 sample_input = (torch.ones([2, 4, 3, 3]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_pixel_unshuffle(self): module = PixelUnshuffle(2) # noqa: F405 sample_input = (torch.ones([2, 2, 6, 6]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_pow_tensor_scalar(self): module = PowTensorScalar() # noqa: F405 sample_input = (torch.rand([2, 4, 3, 3]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_prelu(self): test_comb = [ { QCOM_MODULE: [PReLUDefault()], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, { QCOM_MODULE: [PReLUPerChannel(5)], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, ] index = 0 for comb in test_comb: for module in comb[QCOM_MODULE]: for sample_input in comb[QCOM_SAMPLE_INPUTS]: with self.subTest(i=index): self.lower_module_and_test_output(module, sample_input) index += 1 def test_qnn_backend_relu(self): module = Relu() # noqa: F405 sample_input = (torch.randn([2, 5, 1, 3]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_repeat(self): module = Repeat() # noqa: F405 sample_input = (torch.randn([2, 2, 2, 2]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_reshape(self): module = Reshape() # noqa: F405 sample_input = (torch.randn([3, 4]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_rms_norm(self): module = RmsNorm() # noqa: F405 sample_input = (torch.abs(torch.randn([1, 1, 1, 4])),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_rsqrt(self): module = Rsqrt() # noqa: F405 sample_input = (torch.abs(torch.randn([3, 4])),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_sdpa(self): module = ScaledDotProductAttention() # noqa: F405 mask = torch.tril(torch.randn(1, 1, 100, 100)) mask[mask == 0] = float("-inf") sample_input = ( torch.randn(1, 4, 100, 64), torch.randn(1, 4, 100, 64), torch.randn(1, 4, 100, 64), mask, ) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_sigmoid(self): module = Sigmoid() # noqa: F405 sample_input = (torch.randn([1, 3, 3, 3]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_sin(self): module = Sin() # noqa: F405 sample_input = (torch.randn(2, 5, 1, 3),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_select_copy(self): module = SelectCopy() # noqa: F405 sample_input = (torch.randn([1, 3, 3, 3]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_slice_copy(self): modules = [SliceCopy(), SliceCopyWithStep()] # noqa: F405 sample_input = ( torch.randn([1, 512]), torch.randn([1, 8]), ) for module in modules: self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_stack(self): module = Stack() # noqa: F405 sample_input = ( torch.randn([1, 2, 3, 4]), torch.randn([1, 2, 3, 4]), torch.randn([1, 2, 3, 4]), ) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_softmax(self): module = Softmax() # noqa: F405 sample_input = (torch.randn([1, 4, 8, 8]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_squared_relu(self): module = SquaredReLU() # noqa: F405 sample_input = (torch.randn([2, 5, 1, 3]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_squeeze(self): module = Squeeze() # noqa: F405 sample_input = (torch.randn([1, 3, 3]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_sum_int_list(self): module = SumIntList() # noqa: F405 sample_input = (torch.randn([1, 4, 8, 8]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_tanh(self): module = Tanh() # noqa: F405 sample_input = (torch.randn(2, 5, 1, 3),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_unbind(self): module = Unbind() # noqa: F405 sample_input = (torch.randn([3, 3]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_unsqueeze(self): module = Unsqueeze() # noqa: F405 sample_input = (torch.randn([1, 3, 3]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_view(self): module = View() # noqa: F405 sample_input = (torch.randn([1, 8, 512]), torch.randn([1, 2, 8, 256])) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_where(self): modules = [ Where(), # noqa: F405 WhereConstant(torch.randn(3, 2), torch.randn(3, 2)), # noqa: F405 WhereConstantOther(), # noqa: F405 WhereConstantAll(), # noqa: F405 WhereConstantInf(), # noqa: F405 ] sample_inputs = [ (torch.randn(3, 2), torch.randn(3, 2), torch.randn(3, 2)), (torch.randn(3, 2),), (torch.randn(3, 2),), (torch.randn(3, 2),), (torch.randn(30, 20),), ] for i, module in enumerate(modules): self.lower_module_and_test_output(module, sample_inputs[i]) class TestQNNFloatingPointModel(TestQNN): # TODO: refactor to support different backends def setUp(self): TestQNN.atol = 1e-1 TestQNN.rtol = 1e-1 backend_options = generate_htp_compiler_spec(use_fp16=True) TestQNN.compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, debug=False, saver=False, online_prepare=TestQNN.online_prepare, dump_intermediate_outputs=TestQNN.dump_intermediate_outputs, profile=TestQNN.enable_profile, shared_buffer=TestQNN.shared_buffer, ) def test_qnn_backend_argmin_view_squeeze_conv2d(self): module = ArgminViewSqueezeConv2D() # noqa: F405 sample_input = (torch.randn(32), torch.randn(32, 3, 32, 32)) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_chunk_add(self): module = ChunkAdd() # noqa: F405 torch.manual_seed(8) sample_input = (torch.randn(1, 2, 4, 2),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv1d_relu_log_softmax(self): module = Conv1dReluLogSoftmax() # noqa: F405 sample_input = (torch.rand(1, 2, 28),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_argmin(self): module = Conv2dArgmin() # noqa: F405 sample_input = (torch.randn(16, 3, 4, 4),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_avg_pool2d(self): module = Conv2dAvgPool2d() # noqa: F405 sample_input = (torch.randn(16, 3, 16, 16),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_bn_hardtanh_mean(self): module = Conv2dBnHardtanhMean() # noqa: F405 sample_input = (torch.randn(1, 1, 6, 6),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_cat(self): module = Conv2dCat() # noqa: F405 sample_input = (torch.randn(1, 3, 5, 5), torch.randn(1, 3, 5, 5)) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_down_up_sample(self): module = Conv2dDownUpSample() # noqa: F405 sample_input = (torch.randn(1, 16, 224, 224),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_max_pool2d(self): module = Conv2dMaxPool2d() # noqa: F405 sample_input = (torch.rand(1, 2, 14, 14),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_sum_reduce_dim(self): module = Conv2dSumReduceDim() # noqa: F405 sample_input = (torch.randn([1, 1, 3, 3]),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_topk(self): module = Conv2dTopK() # noqa: F405 sample_input = (torch.randn(1, 3, 32, 32),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_einsum_outer_product_relu(self): module = EinsumOuterProductRelu() # noqa: F405 x = torch.randn(5) y = torch.randn(4) sample_input = ( x, y, ) self.lower_module_and_test_output(module, sample_input) @unittest.skip("Fail because of bad accuracy") def test_qnn_backend_moe_feed_forward(self): args = ModelArgs() args.dim = 32 args.n_heads = 8 args.n_layers = 2 self.head_dim = args.dim // args.n_heads module = MOEFeedForward(args) # noqa: F405 sample_input = ( torch.randint(low=0, high=100, size=(1, 32), dtype=torch.float32), ) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_pixel_unshuffle_math_equivalent(self): module = PixelUnshuffleMathEquivalent(2) # noqa: F405 sample_input = (torch.rand(2, 2, 6, 6),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_residual_block(self): module = ResidualBlockModule() # noqa: F405 sample_input = (torch.randn(1, 32, 28, 28),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_simple_model(self): module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_topk_and_index(self): module = TopKandIndex() # noqa: F405 sample_input = (torch.randn(3, 10),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_view_permute_matmul(self): module = ViewPermuteMatMul() # noqa: F405 torch.manual_seed(8) sample_input = (torch.randn([1, 8, 512]), torch.randn([1, 2, 8, 256])) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_zero_dim_tensor(self): module = ZeroDimTensor() # noqa: F405 sample_input = (torch.randn(1, 256, 125),) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_example_models(self): # TODO Fix MobileBertModelExample and TorchVisionViTModel instances = [ DeepLabV3ResNet101Model(), EdsrModel(), InceptionV3Model(), InceptionV4Model(), # The module of llama is changing frequently. Reopen it when it's stable MV2Model(), MV3Model(), # Fail during lowering Reopen once resolved # MobileBertModelExample(), # TorchVisionViTModel(), Wav2LetterModel(), ] expected_partitions = [ 1, 1, 1, 1, 1, 1, # 1, # 1, 1, ] # TODO: Due to trigger maximum recursion depth exceeded, need to check it. disable_validation() for i, instance in enumerate(instances): with self.subTest(i=i): module = instance.get_eager_model().eval() sample_input = instance.get_example_inputs() self.lower_module_and_test_output( module, sample_input, expected_partitions=expected_partitions[i], assert_output_equal=False, ) class TestQNNQuantizedOperator(TestQNN): # TODO: refactor to support different backends def setUp(self): TestQNN.atol = 1e-1 TestQNN.rtol = 1 backend_options = generate_htp_compiler_spec(use_fp16=False) TestQNN.compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, debug=False, saver=False, online_prepare=TestQNN.online_prepare, dump_intermediate_outputs=TestQNN.dump_intermediate_outputs, profile=TestQNN.enable_profile, shared_buffer=TestQNN.shared_buffer, ) def test_qnn_backend_16a4w_conv2d(self): modules = [Conv2dSingle(), Conv2dSingle(bias=False)] # noqa: F405 sample_input = (torch.randn([1, 1, 3, 3]),) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module( module, sample_input, quant_dtype=QuantDtype.use_16a4w ) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_16a4w_conv2d_qat(self): modules = [Conv2dSingle(), Conv2dSingle(bias=False)] # noqa: F405 sample_input = (torch.randn([1, 1, 3, 3]),) for i, module in enumerate(modules): with self.subTest(i=i): prepared = self.get_prepared_qat_module(module, sample_input) converted = self.get_converted_sgd_trained_module( module, prepared, sample_input ) self.lower_module_and_test_output(converted, sample_input) def test_qnn_backend_16a4w_layer_norm(self): module = LayerNorm() # noqa: F405 sample_input = (torch.randn(196, 768),) module = self.get_qdq_module( module, sample_input, quant_dtype=QuantDtype.use_16a4w, ) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_16a4w_linear(self): module = Linear() # noqa: F405 sample_input = (torch.randn([3, 4]),) module = self.get_qdq_module( module, sample_input, quant_dtype=QuantDtype.use_16a4w, ) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_16a4w_per_channel_linear(self): module = Linear(use_bias=False) # noqa: F405 sample_input = (torch.randn([3, 4]),) module = self.get_qdq_module( module, sample_input, is_linear_per_channel=True, quant_dtype=QuantDtype.use_16a4w, ) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_16a4w_per_channel_linear_with_bias(self): module = Linear() # noqa: F405 sample_input = (torch.randn([3, 4]),) module = self.get_qdq_module( module, sample_input, is_linear_per_channel=True, quant_dtype=QuantDtype.use_16a4w, ) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_abs(self): module = Abs() # noqa: F405 sample_input = (torch.randn(1, 2, 3, 4),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_adaptive_avg_pool2d(self): module = AdaptiveAvgPool2D() # noqa: F405 sample_input = (torch.randn(1, 512, 7, 7),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_amax(self): modules = [AMax(dim=1, keepdim=False), AMax(dim=1, keepdim=True)] # noqa: F405 sample_input = (torch.randn(4, 4),) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_any(self): modules = [Any(), Any(dim=[0, 1]), Any(dim=1, keepdim=True)] # noqa: F405 sample_input = (torch.randn(3, 3, 3) > 0,) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input, bypass_check=True) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_arange(self): modules = [ Arange(start=1, end=6, step=0.5, dtype=torch.float32), # noqa: F405 ] sample_input = (torch.randn(10),) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_argmin(self): module = Argmin() # noqa: F405 sample_input = (torch.randn(16, 3, 4, 4),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_avg_pool2d(self): module = AvgPoolModule() # noqa: F405 sample_input = (torch.randn(1, 3, 2, 2),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_batch_norm(self): modules = [BatchNorm(32), BatchNorm(32, False)] # noqa: F405 sample_input = (torch.randn([4, 32, 16, 16]),) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_bmm(self): module = Bmm() # noqa: F405 torch.manual_seed(8) sample_input = (torch.randn([4, 8, 32]), torch.randn([4, 32, 8])) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_cat(self): modules = [Cat2(), Cat3(), Cat4()] # noqa: F405 sample_input = (torch.randn(1, 1, 2, 2), torch.randn(1, 1, 4, 2)) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_cdist(self): module = CDist() # noqa: F405 sample_input = ( torch.randn(1, 125, 256), torch.randn(1, 2048, 256), ) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_chunk_single(self): module = Chunk() # noqa: F405 sample_input = (torch.randn(1, 1, 4, 3),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_clamp(self): modules = [Clamp(), ClampMin(1e-10), ClampMax(1e10)] # noqa: F405 sample_input = (torch.randn((9, 4, 5, 3)),) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv1d(self): modules = [Conv1dSequential(), Conv1dSequential(bias=False)] # noqa: F405 sample_input = (torch.randn([1, 1, 3]),) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d(self): modules = [Conv2dSequential(), Conv2dSequential(bias=False)] # noqa: F405 sample_input = (torch.randn([1, 1, 3, 3]),) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_block(self): import numpy as np np.random.seed(1) o_ch, i_ch, kernel, padding = 32, 512, (1, 1), 0 input = ( torch.from_numpy(np.random.uniform(-3, 3, size=(1, 1, 32, i_ch))) .to(torch.float) .permute(0, 3, 1, 2) ) weight = ( torch.from_numpy(np.random.uniform(-3, 3, size=(1, 1, i_ch, o_ch))) .to(torch.float) .permute(3, 2, 0, 1) ) modules = [ Conv2dSingle( # noqa: F405 bias=False, in_channel=i_ch, out_channel=o_ch, kernel_size=kernel, padding=padding, ), Conv2dSingle( # noqa: F405 in_channel=i_ch, out_channel=o_ch, kernel_size=kernel, padding=padding, ), ] for module in modules: module.conv.weight = torch.nn.Parameter(weight) sample_input = (input,) for i, module in enumerate(modules): with self.subTest(i=i): # update block size for convolution weight (OIHW) # channel dimension(O) is defaultly sliced in QNN # divide dimension(I) into 4 groups module = self.get_qdq_module( module, sample_input, quant_dtype=QuantDtype.use_16a4w_block, block_size_map={"conv2d": (1, 128, 1, 1)}, ) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_channel_last(self): modules = [ Conv2dSequential(channel_last=True), # noqa: F405 Conv2dSequential(bias=False, channel_last=True), # noqa: F405 ] sample_input = (torch.randn([1, 1, 3, 3]),) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv_transpose1d(self): modules = [ ConvTranspose1dSingle(), # noqa: F405 ConvTranspose1dSingle(bias=False), # noqa: F405 ] sample_input = (torch.randn([1, 1, 3]),) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv_transpose2d(self): modules = [ ConvTranspose2dSingle(), # noqa: F405 ConvTranspose2dSingle(bias=False), # noqa: F405 ] # noqa: F405 sample_input = (torch.randn([1, 1, 3, 3]),) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_cos(self): module = Cos() # noqa: F405 sample_input = (torch.randn(2, 5, 1, 3),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_cumsum(self): module = CumSum() # noqa: F405 sample_input = (torch.randn(4),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_einsum_outer_product(self): module = EinsumOuterProduct() # noqa: F405 x = torch.randn(5) y = torch.randn(4) sample_input = ( x, y, ) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_einsum_bilinear(self): module = EinsumBilinear() # noqa: F405 bn = torch.randn(2, 5) anm = torch.randn(3, 5, 4) bm = torch.randn(2, 4) sample_input = ( bn, anm, bm, ) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_element_wise_add(self): test_comb = [ { QCOM_MODULE: [Add()], # noqa: F405 QCOM_SAMPLE_INPUTS: [ (torch.randn(2, 5, 1, 3), torch.randn(2, 5, 1, 3)), (torch.randn([2, 5, 1, 3]), torch.randn([4, 1])), ], }, { QCOM_MODULE: [AddConstantFloat(), AddConstantLong()], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, ] index = 0 for comb in test_comb: for module in comb[QCOM_MODULE]: for sample_input in comb[QCOM_SAMPLE_INPUTS]: with self.subTest(i=index): gm = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(gm, sample_input) index += 1 def test_qnn_backend_element_wise_and(self): module = And(torch.tensor(1.7), torch.tensor(0.2)) # noqa: F405 sample_input = ( torch.tensor([1, 0, 1, 0], dtype=torch.bool), torch.tensor([1, 1, 0, 0], dtype=torch.bool), ) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_element_wise_ceil(self): module = Ceil() # noqa: F405 sample_input = (torch.randn([2, 5, 1, 3]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_element_wise_div(self): eps = 1e-03 torch.manual_seed(8) test_comb = [ { QCOM_MODULE: [Div()], # noqa: F405 QCOM_SAMPLE_INPUTS: [ (torch.randn(2, 5, 1, 3), eps + torch.randn(2, 5, 1, 3)), (torch.randn([2, 5, 1, 3]), eps + torch.randn([4, 1])), ], }, { QCOM_MODULE: [DivConstantFloat(), DivConstantLong()], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, ] index = 0 for comb in test_comb: for module in comb[QCOM_MODULE]: for sample_input in comb[QCOM_SAMPLE_INPUTS]: with self.subTest(i=index): gm = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(gm, sample_input) index += 1 def test_qnn_backend_element_wise_mul(self): test_comb = [ { QCOM_MODULE: [Mul()], # noqa: F405 QCOM_SAMPLE_INPUTS: [ (torch.randn(2, 5, 1, 3), torch.randn(2, 5, 1, 3)), (torch.randn([2, 5, 1, 3]), torch.randn([4, 1])), ], }, { QCOM_MODULE: [MulConstantFloat(), MulConstantLong()], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, { QCOM_MODULE: [MulScalar()], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, ] index = 0 for comb in test_comb: for module in comb[QCOM_MODULE]: for sample_input in comb[QCOM_SAMPLE_INPUTS]: with self.subTest(i=index): gm = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(gm, sample_input) index += 1 def test_qnn_backend_element_wise_or(self): test_comb = [ { QCOM_MODULE: OrBitWise( # noqa: F405 torch.tensor(1.7), torch.tensor(0.2) ), QCOM_SAMPLE_INPUTS: ( torch.tensor([1, 0, 1, 0], dtype=torch.bool), torch.tensor([1, 1, 0, 0], dtype=torch.bool), ), }, { QCOM_MODULE: OrOperator( # noqa: F405 torch.tensor(1.5), torch.tensor(-1.2) ), QCOM_SAMPLE_INPUTS: ( torch.full((3, 3), 1).triu(), torch.full((3, 3), 1).tril(diagonal=0), ), }, ] for i, test in enumerate(test_comb): with self.subTest(i=i): module = self.get_qdq_module( test[QCOM_MODULE], test[QCOM_SAMPLE_INPUTS] ) self.lower_module_and_test_output(module, test[QCOM_SAMPLE_INPUTS]) def test_qnn_backend_element_wise_sqrt(self): modules = [Sqrt(), SqrtConstant()] # noqa: F405 for i, module in enumerate(modules): sample_input = (torch.rand([3, 1]),) with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_element_wise_sub(self): test_comb = [ { QCOM_MODULE: [Sub()], # noqa: F405 QCOM_SAMPLE_INPUTS: [ (torch.randn(2, 5, 1, 3), torch.randn(2, 5, 1, 3)), (torch.randn([2, 5, 1, 3]), torch.randn([4, 1])), ], }, { QCOM_MODULE: [SubConstantFloat(), SubConstantLong()], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, ] index = 0 for comb in test_comb: for module in comb[QCOM_MODULE]: for sample_input in comb[QCOM_SAMPLE_INPUTS]: with self.subTest(i=index): gm = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(gm, sample_input) index += 1 def test_qnn_backend_elu(self): module = Elu() # noqa: F405 sample_input = (torch.randn(2, 5, 1, 3),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_embedding(self): module = Embedding() # noqa: F405 sample_input = (torch.Tensor([[1, 2, 4, 5], [4, 3, 2, 9]]).to(torch.int32),) quant_dtype = [QuantDtype.use_8a8w, QuantDtype.use_16a4w] for i, qdtype in enumerate(quant_dtype): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input, quant_dtype=qdtype) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_equal(self): test_comb = [ { QCOM_MODULE: Equal(), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4), torch.randn(2, 3, 4)), }, { QCOM_MODULE: EqualConstant(0.5), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4),), }, ] for i, test in enumerate(test_comb): with self.subTest(i=i): module = self.get_qdq_module( test[QCOM_MODULE], test[QCOM_SAMPLE_INPUTS] ) self.lower_module_and_test_output(module, test[QCOM_SAMPLE_INPUTS]) def test_qnn_backend_expand(self): modules = [ExpandAs(), ExpandCopy()] # noqa: F405 sample_inputs = [ (torch.randn([3, 1]),), (torch.randn([4]),), ] passes_job = get_capture_program_passes() passes_job[ExpandBroadcastTensorShape][QCOM_PASS_ACTIVATE_KEY] = True index = 0 for module in modules: for sample_input in sample_inputs: with self.subTest(i=index): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output( module, sample_input, passes_job=passes_job ) index += 1 def test_qnn_backend_expm1(self): sample_input = (torch.randn(3, 4, 5),) module = ExpM1() # noqa: F405 module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_full(self): shape = (1, 2, 3, 4) module = Full(0.5, shape) # noqa: F405 sample_input = (torch.randn(shape),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_full_like(self): module = FullLike(0.5) # noqa: F405 sample_input = (torch.randn(1, 2, 3, 4),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_gelu(self): module = Gelu() # noqa: F405 sample_input = (torch.randn(2, 5, 1, 3),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_greater_equal(self): test_comb = [ { QCOM_MODULE: GreaterEqual(), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4), torch.randn(2, 3, 4)), }, { QCOM_MODULE: GreaterEqualConstant(0.5), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4),), }, ] for i, test in enumerate(test_comb): with self.subTest(i=i): module = self.get_qdq_module( test[QCOM_MODULE], test[QCOM_SAMPLE_INPUTS] ) self.lower_module_and_test_output(module, test[QCOM_SAMPLE_INPUTS]) def test_qnn_backend_greater_than(self): test_comb = [ { QCOM_MODULE: GreaterThan(), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4), torch.randn(2, 3, 4)), }, { QCOM_MODULE: GreaterThanConstant(0.5), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4),), }, ] for i, test in enumerate(test_comb): with self.subTest(i=i): module = self.get_qdq_module( test[QCOM_MODULE], test[QCOM_SAMPLE_INPUTS] ) self.lower_module_and_test_output(module, test[QCOM_SAMPLE_INPUTS]) def test_qnn_backend_group_norm(self): modules = [GroupNorm(), GroupNorm(bias=False)] # noqa: F405 sample_input = (torch.randn(3, 32, 56, 56),) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_hardsigmoid(self): module = HardSigmoid() # noqa: F405 sample_input = (torch.randn(2, 5, 1, 3),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_hardswish(self): module = HardSwish() # noqa: F405 sample_input = (torch.randn(2, 5, 1, 3),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_hardtanh(self): module = HardTanh() # noqa: F405 sample_input = (torch.randn([2, 5, 1, 3]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_index(self): modules = [Index(0), Index(1), Index(2)] # noqa: F405 sample_input = (torch.randn([8, 172, 64]),) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_index_put(self): module = IndexPut() # noqa: F405 sample_input = ( torch.tensor([2], dtype=torch.int32), torch.randn([1, 1, 12, 64]), ) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_instance_norm_2d(self): modules = [InstanceNorm2d(32), InstanceNorm2d(32, affine=False)] # noqa: F405 sample_input = (torch.randn([4, 32, 16, 16]),) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_interpolate_bilinear_2d(self): module = ResizeBilinear2D() # noqa: F405 sample_input = (torch.randn(2, 3, 4, 5),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_interpolate_nearest_2d(self): module = ResizeNearest2D() # noqa: F405 sample_input = (torch.randn(2, 3, 4, 5),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_up_sampling_nearest_2d_with_scale_factor(self): module = UpsampleNearest2D(scale_factor=2) # noqa: F405 sample_input = (torch.randn(1, 16, 72, 104),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_up_sampling_nearest_2d_with_size(self): module = UpsampleNearest2D(sizes=(144, 208)) # noqa: F405 sample_input = (torch.randn(1, 16, 72, 104),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_layer_norm(self): modules = [LayerNorm(), LayerNorm(bias=False)] # noqa: F405 sample_input = (torch.randn(196, 768),) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_leaky_relu(self): test_comb = [ { QCOM_MODULE: [LeakyReLUDefault()], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, { QCOM_MODULE: [LeakyReLUCustom(0.05)], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, { QCOM_MODULE: [LeakyReLUCustom(0.05, inplace=True)], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, ] index = 0 for comb in test_comb: for module in comb[QCOM_MODULE]: for sample_input in comb[QCOM_SAMPLE_INPUTS]: with self.subTest(i=index): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) index += 1 def test_qnn_backend_less_equal(self): test_comb = [ { QCOM_MODULE: LessEqual(), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4), torch.randn(2, 3, 4)), }, { QCOM_MODULE: LessEqualConstant(0.5), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4),), }, ] for i, test in enumerate(test_comb): with self.subTest(i=i): module = self.get_qdq_module( test[QCOM_MODULE], test[QCOM_SAMPLE_INPUTS] ) self.lower_module_and_test_output(module, test[QCOM_SAMPLE_INPUTS]) def test_qnn_backend_less_than(self): test_comb = [ { QCOM_MODULE: LessThan(), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4), torch.randn(2, 3, 4)), }, { QCOM_MODULE: LessThanConstant(0.5), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4),), }, ] for i, test in enumerate(test_comb): with self.subTest(i=i): module = self.get_qdq_module( test[QCOM_MODULE], test[QCOM_SAMPLE_INPUTS] ) self.lower_module_and_test_output(module, test[QCOM_SAMPLE_INPUTS]) def test_qnn_backend_linalg_vector_norm(self): modules = [ LinalgVectorNorm(), # noqa: F405 LinalgVectorNorm(ord=3.5), # noqa: F405 LinalgVectorNorm(dim=1), # noqa: F405 LinalgVectorNorm(dim=1, keepdim=True), # noqa: F405 ] sample_input = (torch.randn([3, 3]),) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_linear(self): module = Linear() # noqa: F405 sample_input = (torch.randn([3, 4]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_linear_qat(self): """ Prototype to test qat model """ module = Linear() # noqa: F405 sample_input = (torch.randn([3, 4]),) prepared = self.get_prepared_qat_module(module, sample_input) module = self.get_converted_sgd_trained_module(module, prepared, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_log(self): module = Log() # noqa: F405 sample_input = (torch.rand([1, 2, 3, 4]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_logical_not(self): module = LogicalNot() # noqa: F405 sample_input = (torch.rand([1, 2, 3, 4]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_log_softmax(self): module = LogSoftmax() # noqa: F405 sample_input = (torch.randn([1, 4, 8, 8]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_maximum(self): module = Maximum() # noqa: F405 sample_input = (torch.randn(1, 2, 3, 4), torch.randn(2, 3, 4)) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_max_pool2d(self): module = MaxPool2d() # noqa: F405 sample_input = (torch.randn(4, 3, 24, 24),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_mean_dim(self): modules = [MeanWKeppDim(), MeanWOKeppDim()] # noqa: F405 sample_input = (torch.randn([2, 5, 1, 3]),) for i, module in enumerate(modules): with self.subTest(i=i): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_mha(self): module = MultiheadAttention() # noqa: F405 sample_input = (torch.randn(1, 197, 96),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_minimum(self): module = Minimum() # noqa: F405 sample_input = (torch.randn(1, 2, 3, 4), torch.randn(2, 3, 4)) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_neg(self): module = Neg() # noqa: F405 sample_input = (torch.randn(1, 4, 16, 16),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_not_equal(self): test_comb = [ { QCOM_MODULE: NotEqual(), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4), torch.randn(2, 3, 4)), }, { QCOM_MODULE: NotEqualConstant(0.5), # noqa: F405 QCOM_SAMPLE_INPUTS: (torch.randn(1, 2, 3, 4),), }, ] for i, test in enumerate(test_comb): with self.subTest(i=i): module = self.get_qdq_module( test[QCOM_MODULE], test[QCOM_SAMPLE_INPUTS] ) self.lower_module_and_test_output(module, test[QCOM_SAMPLE_INPUTS]) def test_qnn_backend_pad(self): module = Pad() # noqa: F405 sample_input = (torch.randn([1, 8, 128]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_pixel_shuffle(self): module = PixelShuffle(2) # noqa: F405 sample_input = (torch.ones([2, 4, 3, 3]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_pixel_unshuffle(self): module = PixelUnshuffle(2) # noqa: F405 sample_input = (torch.ones([2, 2, 6, 6]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_pow_tensor_scalar(self): module = PowTensorScalar() # noqa: F405 sample_input = (torch.rand([2, 4, 3, 3]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_prelu(self): test_comb = [ { QCOM_MODULE: [PReLUDefault()], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, { QCOM_MODULE: [PReLUPerChannel(5)], # noqa: F405 QCOM_SAMPLE_INPUTS: [(torch.randn(2, 5, 1, 3),)], }, ] index = 0 for comb in test_comb: for module in comb[QCOM_MODULE]: for sample_input in comb[QCOM_SAMPLE_INPUTS]: with self.subTest(i=index): module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) index += 1 def test_qnn_backend_relu(self): module = Relu() # noqa: F405 sample_input = (torch.randn([2, 5, 1, 3]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_repeat(self): module = Repeat() # noqa: F405 sample_input = (torch.randn([2, 2, 2, 2]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_reshape(self): module = Reshape() # noqa: F405 sample_input = (torch.randn([3, 4]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_rms_norm(self): module = RmsNorm() # noqa: F405 sample_input = (torch.abs(torch.randn([1, 1, 1, 4])),) module = self.get_qdq_module( module, sample_input, quant_dtype=QuantDtype.use_16a4w ) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_rsqrt(self): module = Rsqrt() # noqa: F405 sample_input = (torch.abs(torch.randn([3, 4])),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_sdpa(self): module = ScaledDotProductAttention() # noqa: F405 mask = torch.tril(torch.randn(1, 1, 100, 100)) mask[mask == 0] = torch.finfo(torch.float32).min sample_input = ( torch.randn(1, 4, 100, 64), torch.randn(1, 4, 100, 64), torch.randn(1, 4, 100, 64), mask, ) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_select_copy(self): module = SelectCopy() # noqa: F405 sample_input = (torch.randn([1, 3, 3, 3]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_sigmoid(self): module = Sigmoid() # noqa: F405 sample_input = (torch.randn([1, 3, 3, 3]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_sin(self): module = Sin() # noqa: F405 sample_input = (torch.randn(2, 5, 1, 3),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_slice_copy(self): modules = [SliceCopy(), SliceCopyWithStep()] # noqa: F405 sample_input = ( torch.randn([1, 512]), torch.randn([1, 8]), ) for module in modules: module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_softmax(self): module = Softmax() # noqa: F405 sample_input = (torch.randn([1, 4, 8, 8]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_squared_relu(self): module = SquaredReLU() # noqa: F405 sample_input = (torch.randn([2, 5, 1, 3]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_squeeze(self): module = Squeeze() # noqa: F405 sample_input = (torch.randn([1, 3, 3]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_stack(self): module = Stack() # noqa: F405 sample_input = ( torch.randn([1, 2, 3, 4]), torch.randn([1, 2, 3, 4]), torch.randn([1, 2, 3, 4]), ) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_sum_int_list(self): module = SumIntList() # noqa: F405 sample_input = (torch.randn([1, 4, 8, 8]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_tanh(self): module = Tanh() # noqa: F405 sample_input = (torch.randn(2, 5, 1, 3),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_unbind(self): module = Unbind() # noqa: F405 sample_input = (torch.randn([3, 3]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_unsqueeze(self): module = Unsqueeze() # noqa: F405 sample_input = (torch.randn([1, 3, 3]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_view(self): module = View() # noqa: F405 sample_input = (torch.randn([1, 8, 512]), torch.randn([1, 2, 8, 256])) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_where(self): modules = [ Where(), # noqa: F405 WhereConstant(torch.randn(3, 2), torch.randn(3, 2)), # noqa: F405 WhereConstantOther(), # noqa: F405 WhereConstantAll(), # noqa: F405 WhereConstantInf(), # noqa: F405 ] sample_inputs = [ (torch.randn(3, 2), torch.randn(3, 2), torch.randn(3, 2)), (torch.randn(3, 2),), (torch.randn(3, 2),), (torch.randn(3, 2),), (torch.randn(30, 20),), ] for i, module in enumerate(modules): module = self.get_qdq_module(module, sample_inputs[i]) self.lower_module_and_test_output(module, sample_inputs[i]) class TestQNNQuantizedModel(TestQNN): # TODO: refactor to support different backends def setUp(self): TestQNN.atol = 1e-1 TestQNN.rtol = 1 backend_options = generate_htp_compiler_spec(use_fp16=False) TestQNN.compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, debug=False, saver=False, online_prepare=TestQNN.online_prepare, dump_intermediate_outputs=TestQNN.dump_intermediate_outputs, profile=TestQNN.enable_profile, shared_buffer=TestQNN.shared_buffer, ) def test_qnn_backend_argmin_view_squeeze_conv2d(self): module = ArgminViewSqueezeConv2D() # noqa: F405 sample_input = (torch.randn(32), torch.randn(32, 3, 32, 32)) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_chunk_add(self): module = ChunkAdd() # noqa: F405 torch.manual_seed(8) sample_input = (torch.randn(1, 1, 4, 2),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv1d_relu_log_softmax(self): module = Conv1dReluLogSoftmax() # noqa: F405 sample_input = (torch.rand(1, 2, 28),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_argmin(self): module = Conv2dArgmin() # noqa: F405 sample_input = (torch.randn(16, 3, 4, 4),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_avg_pool2d(self): module = Conv2dAvgPool2d() # noqa: F405 sample_input = (torch.randn(16, 3, 16, 16),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_bn_hardtanh_mean(self): module = Conv2dBnHardtanhMean() # noqa: F405 sample_input = (torch.randn(1, 1, 6, 6),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_cat(self): module = Conv2dCat() # noqa: F405 sample_input = (torch.randn(1, 3, 5, 5), torch.randn(1, 3, 5, 5)) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_down_up_sample(self): module = Conv2dDownUpSample() # noqa: F405 sample_input = (torch.randn(1, 16, 224, 224),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_max_pool2d(self): module = Conv2dMaxPool2d() # noqa: F405 sample_input = (torch.rand(1, 2, 14, 14),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_sum_reduce_dim(self): module = Conv2dSumReduceDim() # noqa: F405 sample_input = (torch.randn([1, 1, 3, 3]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_conv2d_topk(self): module = Conv2dTopK() # noqa: F405 sample_input = (torch.randn(1, 3, 32, 32),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_einsum_outer_product_relu(self): module = EinsumOuterProductRelu() # noqa: F405 x = torch.randn(5) y = torch.randn(4) sample_input = ( x, y, ) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) @unittest.skip("UT pass before QNN 2.26, segfault during partitioner") def test_qnn_backend_moe_feed_forward(self): args = ModelArgs() args.dim = 32 args.n_heads = 8 args.n_layers = 2 self.head_dim = args.dim // args.n_heads module = MOEFeedForward(args) # noqa: F405 sample_input = ( torch.randint(low=0, high=100, size=(1, 32), dtype=torch.float32), ) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_pixel_unshuffle_math_equivalent(self): module = PixelUnshuffleMathEquivalent(2) # noqa: F405 sample_input = (torch.rand(2, 2, 6, 6),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_residual_block(self): module = ResidualBlockModule() # noqa: F405 sample_input = (torch.randn(1, 32, 28, 28),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_simple_model(self): module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_submodules(self): module = SimpleSubModules() # noqa: F405 sample_input = ( torch.rand(1, 3, 8, 8), torch.rand(1, 3, 8, 8), torch.rand(1, 3, 8, 8), torch.rand(1, 3, 8, 8), ) from executorch.backends.qualcomm.quantizer.quantizer import ( get_submodule_type_predicate, ) submodule_qconfig_list = [ ( get_submodule_type_predicate("Add"), ModuleQConfig(QuantDtype.use_16a16w), ) # noqa: F405 ] module = self.get_qdq_module( module, sample_input, submodule_qconfig_list=submodule_qconfig_list, ) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_topk_and_index(self): module = TopKandIndex() # noqa: F405 sample_input = (torch.randn(3, 10),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_view_permute_matmul(self): module = ViewPermuteMatMul() # noqa: F405 torch.manual_seed(8) sample_input = (torch.randn([1, 8, 512]), torch.randn([1, 2, 8, 256])) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_zero_dim_tensor(self): module = ZeroDimTensor() # noqa: F405 sample_input = (torch.randn(1, 256, 125),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) def test_qnn_backend_example_models(self): instances = [ { QCOM_MODULE: DeepLabV3ResNet101Model(), QCOM_ANNOTATION: (), QCOM_QUANT_DTYPE: QuantDtype.use_8a8w, }, { QCOM_MODULE: EdsrModel(), QCOM_ANNOTATION: (), QCOM_QUANT_DTYPE: QuantDtype.use_8a8w, }, { QCOM_MODULE: InceptionV3Model(), QCOM_ANNOTATION: (), QCOM_QUANT_DTYPE: QuantDtype.use_8a8w, }, { QCOM_MODULE: InceptionV4Model(), QCOM_ANNOTATION: (), QCOM_QUANT_DTYPE: QuantDtype.use_8a8w, }, # The module of llama is changing frequently. Reopen it when it's stable { QCOM_MODULE: MV2Model(), QCOM_ANNOTATION: (), QCOM_QUANT_DTYPE: QuantDtype.use_8a8w, }, { QCOM_MODULE: MV3Model(), QCOM_ANNOTATION: (), QCOM_QUANT_DTYPE: QuantDtype.use_8a8w, }, # only works on QNN 2.12 so far # { 'module': MobileBertModelExample(), 'annotation': (), QCOM_QUANT_DTYPE: QuantDtype.use_8a8w }, { QCOM_MODULE: TorchVisionViTModel(), QCOM_ANNOTATION: (), QCOM_QUANT_DTYPE: QuantDtype.use_8a8w, }, { QCOM_MODULE: Wav2LetterModel(), QCOM_ANNOTATION: (), QCOM_QUANT_DTYPE: QuantDtype.use_8a8w, }, ] expected_partitions = [ 1, 1, 1, 1, 1, 1, # For MobileBertModelExample # 1, 1, 1, ] # TODO: Due to trigger maximum recursion depth exceeded, need to check it. disable_validation() for i, instance in enumerate(instances): with self.subTest(i=i): module = instance[QCOM_MODULE].get_eager_model().eval() sample_input = instance[QCOM_MODULE].get_example_inputs() module = self.get_qdq_module( module, sample_input, custom_quant_annotations=instance[QCOM_ANNOTATION], quant_dtype=instance[QCOM_QUANT_DTYPE], ) self.lower_module_and_test_output( module, sample_input, expected_partitions=expected_partitions[i], assert_output_equal=False, ) class TestQNNFloatingPointUtils(TestQNN): # TODO: refactor to support different backends def setUp(self): TestQNN.atol = 1e-1 TestQNN.rtol = 1e-1 backend_options = generate_htp_compiler_spec(use_fp16=True) TestQNN.compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, debug=False, saver=False, ) def test_qnn_backend_dump_intermediate_outputs(self): backend_options = generate_htp_compiler_spec(use_fp16=True) TestQNN.compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, dump_intermediate_outputs=True, ) module = Relu() # noqa: F405 sample_input = (torch.randn([2, 5, 1, 3]),) self.lower_module_and_test_output( module, sample_input, expected_partitions=1, expected_intermediate_events=3, ) def test_qnn_backend_skip_node_id(self): module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) self.lower_module_and_test_output( module, sample_input, expected_partitions=3, skip_node_id_set={"aten_add_tensor", "aten_mean_dim"}, ) def test_qnn_backend_skip_node_op(self): module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) self.lower_module_and_test_output( module, sample_input, expected_partitions=2, skip_node_op_set={"aten.add.Tensor"}, ) @unittest.expectedFailure def test_qnn_backend_spill_fill_buffer_size(self): # TODO: Fix self.assertNotEqual(0, max_sf_size) module = LargeTensorLinear() # noqa: F405 sample_input = (torch.randn(1, 256, 512),) backend_options = generate_htp_compiler_spec( use_fp16=True, use_multi_contexts=True, ) compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, ) edge_prog = to_edge_transform_and_lower_to_qnn( module, sample_input, compiler_specs ) max_sf_size = update_spill_fill_size(edge_prog.exported_program()) self.assertNotEqual(0, max_sf_size) def test_qnn_backend_multi_contexts(self): module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) backend_options = generate_htp_compiler_spec( use_fp16=True, use_dlbc=True, use_multi_contexts=True, ) compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, ) pass_jobs = get_capture_program_passes() split_graph_pass, setting = self.split_graph(4) pass_jobs[split_graph_pass] = setting dep_table = get_passes_dependency_for_capture_program() dep_table[split_graph_pass] = [FoldQDQ] edge_prog = to_edge_transform_and_lower_to_qnn( module, sample_input, compiler_specs, dep_table=dep_table, passes_job=pass_jobs, ) update_spill_fill_size(edge_prog.exported_program()) exec_prog = edge_prog.to_executorch() self.verify_output(module, sample_input, exec_prog) def test_qnn_backend_multi_contexts_composite(self): backend_options = generate_htp_compiler_spec( use_fp16=True, use_dlbc=True, use_multi_contexts=True, ) compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, ) module = CompositeDelegateModule( # noqa: F405 compiler_specs=compiler_specs, to_edge_transform_and_lower_method=to_edge_transform_and_lower_to_qnn, ) sample_input = module.get_random_input() edge_prog = to_edge( torch.export.export(module, sample_input, strict=True), ) update_spill_fill_size(edge_prog.exported_program()) exec_prog = edge_prog.to_executorch() self.verify_output(module.get_reference_module(), sample_input, exec_prog) def test_qnn_backend_multi_graphs(self): if self.enable_x86_64: self.skipTest("weight sharing is not supported on host machine") seq_conv = Conv2dSequential() # noqa: F405 # weight sharing modules = [seq_conv, seq_conv.second] sample_inputs = [(torch.randn([1, 1, 3, 3]),), (torch.randn([1, 3, 3, 3]),)] graph_names = ["seq_conv", "single_conv"] backend_options = generate_htp_compiler_spec( use_fp16=True, ) compiler_specs = [ generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, multiple_graphs=True, weight_sharing=True, graph_name=graph_name, ) for graph_name in graph_names ] edge_progs = [ to_edge_transform_and_lower_to_qnn(module, sample_input, compiler_spec) for module, sample_input, compiler_spec in zip( modules, sample_inputs, compiler_specs ) ] prog_mgr, _ = generate_multi_graph_program( compiler_specs=compiler_specs[0], processed_bytes=[ edge_prog.exported_program().graph_module.lowered_module_0.processed_bytes for edge_prog in edge_progs ], ) for index, module in enumerate(modules): self.verify_output( module=module, sample_inputs=sample_inputs[index], executorch_prog=prog_mgr, method_index=index, ) def test_qnn_backend_profile_op(self): TestQNN.enable_profile = True backend_options = generate_htp_compiler_spec(use_fp16=True) TestQNN.compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, profile=True, ) module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) self.lower_module_and_test_output( module, sample_input, expected_partitions=1, expected_profile_events=30, ) def test_qnn_backend_shared_buffer(self): TestQNN.shared_buffer = True backend_options = generate_htp_compiler_spec( use_fp16=True, ) TestQNN.compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, shared_buffer=True, ) module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) self.lower_module_and_test_output( module, sample_input, expected_partitions=1, ) def test_qnn_backend_online_prepare(self): if self.enable_x86_64: self.skipTest("online prepare is not supported on host machine") backend_options = generate_htp_compiler_spec(use_fp16=True) TestQNN.compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, online_prepare=True, ) module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) self.lower_module_and_test_output(module, sample_input) @unittest.expectedFailure def test_qnn_backend_context_direct(self): # TODO: Fix QNN tools pairs with np 2.x with tempfile.TemporaryDirectory() as tmp_dir: module = ContextBinaryExample() # noqa: F405 generate_context_binary( module=module, inputs=module.example_inputs(), quantized=False, artifact_dir=tmp_dir, ) ctx_path = f"{tmp_dir}/model_ctx.bin" bundle_program = from_context_binary(ctx_path, "ctx_loader") self.verify_output( module, tuple( torch.randn(size=v.shape, dtype=v.dtype) for v in bundle_program["inputs"].values() ), bundle_program["edge_program_manager"].to_executorch(), ) def test_qnn_backend_context_extraction(self): module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) backend_options = generate_htp_compiler_spec(use_fp16=True) # Validate dlc compiler_spec = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, online_prepare=True, ) with tempfile.TemporaryDirectory() as tmp_dir: edge_prog_mgr = to_edge_transform_and_lower_to_qnn( module, sample_input, compiler_spec ).to_executorch() pte_path = f"{tmp_dir}/model.pte" with open(pte_path, "wb") as f: edge_prog_mgr.write_to_file(f) dump_context_from_pte(pte_path) qnn_tool = QnnTool(tmp_dir, pte_path, sample_input) qnn_tool.qnn_context_binary_generator() qnn_tool.qnn_net_run() compiler_specs = [ self.compiler_specs, ] validators = [validate_context_binary] for compiler_spec, validate in zip(compiler_specs, validators): edge_prog_mgr = to_edge_transform_and_lower_to_qnn( module, sample_input, compiler_spec ) lowered_module = edge_prog_mgr.exported_program().graph_module._modules[ "lowered_module_0" ] qnn_mgr = PyQnnManagerAdaptor.QnnManager( lowered_module.compile_specs[0].value ) qnn_mgr.Init() binary = qnn_mgr.StripProtocol(lowered_module.processed_bytes) validate(binary) def test_qnn_backend_dump_context_from_pte(self): module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) backend_options = generate_htp_compiler_spec(use_fp16=True) # Validate dlc compiler_spec = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, online_prepare=True, ) with tempfile.TemporaryDirectory() as tmp_dir: edge_prog_mgr = to_edge_transform_and_lower_to_qnn( module, sample_input, compiler_spec ).to_executorch() pte_path = f"{tmp_dir}/model.pte" with open(pte_path, "wb") as f: edge_prog_mgr.write_to_file(f) dump_context_from_pte(pte_path) qnn_tool = QnnTool(tmp_dir, pte_path, sample_input) qnn_tool.qnn_context_binary_generator() qnn_tool.qnn_net_run() compiler_specs = [ self.compiler_specs, ] validators = [validate_context_binary] for compiler_spec, validate in zip(compiler_specs, validators): edge_prog_mgr = to_edge_transform_and_lower_to_qnn( module, sample_input, compiler_spec ).to_executorch() with tempfile.TemporaryDirectory() as tmp_dir: pte_path = f"{tmp_dir}/model.pte" with open(pte_path, "wb") as f: edge_prog_mgr.write_to_file(f) dump_context_from_pte(pte_path) binary_name = f"{tmp_dir}/forward_0.bin" self.assertTrue(os.path.isfile(binary_name)) with open(binary_name, "rb") as f: stripped_binary = f.read() validate(stripped_binary) def test_qnn_backend_draw_graph(self): golden_data = """digraph test { rankdir=TB input_0_x_0 [label=<
name: input_0_x_0
data_type: Qnn_DataType_t.QNN_DATATYPE_FLOAT_32
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_APP_WRITE
dims: [1, 28, 28, 32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_UNDEFINED
> color=black fillcolor=transparent shape=box style=rounded] p_conv2_weight_0 [label=<
name: p_conv2_weight_0
data_type: Qnn_DataType_t.QNN_DATATYPE_FLOAT_32
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_STATIC
dims: [3, 3, 32, 32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_UNDEFINED
> color=black fillcolor=transparent shape=box style=rounded] p_conv2_bias_0 [label=<
name: p_conv2_bias_0
data_type: Qnn_DataType_t.QNN_DATATYPE_FLOAT_32
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_STATIC
dims: [32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_UNDEFINED
> color=black fillcolor=transparent shape=box style=rounded] aten_convolution_default_1_0 [label=<
name: aten_convolution_default_1_0
data_type: Qnn_DataType_t.QNN_DATATYPE_FLOAT_32
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_NATIVE
dims: [1, 28, 28, 32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_UNDEFINED
> color=black fillcolor=transparent shape=box style=rounded] aten_relu_default_0 [label=<
name: aten_relu_default_0
data_type: Qnn_DataType_t.QNN_DATATYPE_FLOAT_32
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_NATIVE
dims: [1, 28, 28, 32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_UNDEFINED
> color=black fillcolor=transparent shape=box style=rounded] aten_relu_default_1_0 [label=<
name: aten_relu_default_1_0
data_type: Qnn_DataType_t.QNN_DATATYPE_FLOAT_32
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_NATIVE
dims: [1, 28, 28, 32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_UNDEFINED
> color=black fillcolor=transparent shape=box style=rounded] output_aten_add_tensor_0 [label=<
name: output_aten_add_tensor_0
data_type: Qnn_DataType_t.QNN_DATATYPE_FLOAT_32
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_APP_READ
dims: [1, 28, 28, 32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_UNDEFINED
> color=black fillcolor=transparent shape=box style=rounded] p_conv1_weight_0 [label=<
name: p_conv1_weight_0
data_type: Qnn_DataType_t.QNN_DATATYPE_FLOAT_32
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_STATIC
dims: [3, 3, 32, 32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_UNDEFINED
> color=black fillcolor=transparent shape=box style=rounded] p_conv1_bias_0 [label=<
name: p_conv1_bias_0
data_type: Qnn_DataType_t.QNN_DATATYPE_FLOAT_32
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_STATIC
dims: [32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_UNDEFINED
> color=black fillcolor=transparent shape=box style=rounded] aten_convolution_default_0 [label=<
name: aten_convolution_default_0
data_type: Qnn_DataType_t.QNN_DATATYPE_FLOAT_32
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_NATIVE
dims: [1, 28, 28, 32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_UNDEFINED
> color=black fillcolor=transparent shape=box style=rounded] input_0_x_0 -> aten_convolution_default_1_0 p_conv2_weight_0 -> aten_convolution_default_1_0 p_conv2_bias_0 -> aten_convolution_default_1_0 aten_convolution_default_0 -> aten_relu_default_0 input_0_x_0 -> aten_convolution_default_0 p_conv1_weight_0 -> aten_convolution_default_0 p_conv1_bias_0 -> aten_convolution_default_0 aten_convolution_default_1_0 -> aten_relu_default_1_0 aten_relu_default_0 -> output_aten_add_tensor_0 aten_relu_default_1_0 -> output_aten_add_tensor_0 } """ module = DrawGraphModel() # noqa: F405 sample_input = (torch.randn(1, 32, 28, 28),) # TODO: Figure out how to get the original graph module with the to_edge_transform_and_lowering_to_qnn API delegated_program = capture_program(module, sample_input) """ This piece of code simulates the behavior of the final preprocessing step to obtain the op wrapper list. In practice, users need to set a breakpoint in the preprocessing step and use the DrawGraph tool to visualize the graph. """ graph_module = QnnPassManager().transform_for_preprocess_pipeline( delegated_program.exported_program ) nodes_to_wrappers = defaultdict(dict) node_visitors = get_node_visitors( delegated_program.exported_program, enable_tensor_dump=False ) py_op_wrapper_list = [] for node in graph_module.graph.nodes: if node.op == "call_function": if node.target.__name__ in node_visitors: py_op_wrapper = node_visitors[node.target.__name__].define_node( node, nodes_to_wrappers ) if py_op_wrapper is not None: if isinstance(py_op_wrapper, List): py_op_wrapper_list.extend(py_op_wrapper) else: py_op_wrapper_list.append(py_op_wrapper) elif node.op in [ "get_attr", "placeholder", "output", ]: continue # random py_op_wrapper_list to check it's correctness random.shuffle(py_op_wrapper_list) DrawGraph("test", ".", py_op_wrapper_list, dot_string=True) test_file = os.path.join(".", "test.dot") with open(test_file, "r") as test: test_data = test.read() assert sorted(golden_data.split()) == sorted( test_data.split() ), "Generated .dot file does not match the golden file." class TestQNNQuantizedUtils(TestQNN): # TODO: refactor to support different backends def setUp(self): TestQNN.atol = 1e-1 TestQNN.rtol = 1 backend_options = generate_htp_compiler_spec(use_fp16=False) TestQNN.compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, debug=False, saver=False, ) def test_qnn_backend_dump_intermediate_outputs(self): backend_options = generate_htp_compiler_spec(use_fp16=False) TestQNN.compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, dump_intermediate_outputs=True, ) module = Relu() # noqa: F405 sample_input = (torch.randn([2, 5, 1, 3]),) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output( module, sample_input, expected_partitions=1, expected_intermediate_events=5, ) def test_qnn_backend_dynamic_shape(self): from executorch.backends.qualcomm._passes.build_quant_io import BuildQuantIo from executorch.backends.qualcomm.utils.constants import ( QCOM_DTYPE, QCOM_QUANT_ATTRS, ) from executorch.exir.capture._config import ExecutorchBackendConfig module = Add() # noqa: F405 last_dim = torch.export.Dim("last_dim", min=1, max=8) dynamic_shapes = {"x": {3: last_dim}, "y": {3: last_dim}} # the tracing input in dynamic dimension should have maximun expected # value for QNN to be setup correctly input_shape = (1, 2, 3, last_dim.max) sample_input = ( torch.randint(0, 2, input_shape, dtype=torch.float), torch.randint(0, 2, input_shape, dtype=torch.float), ) module = self.get_qdq_module( module, sample_input, quant_dtype=QuantDtype.use_16a16w, dynamic_shapes=dynamic_shapes, ) # only few ops with 16bit are supported with dynamic shape now # strip unsupported quantize / dequantize ops generated in preprocess pass_jobs = get_capture_program_passes() pass_jobs[TagQuantIO][QCOM_PASS_ACTIVATE_KEY] = True pass_jobs[TagQuantIO][QCOM_PASS_ARGS_KWARGS_DEFAULTS_KEY][ "get_quant_io_dtype_fn" ] = lambda n: ( torch.uint16 if any(name in n.name for name in ["x", "y", "add"]) else None ) edge_prog_mgr = to_edge_transform_and_lower_to_qnn( module, sample_input, self.compiler_specs, dynamic_shapes=dynamic_shapes, passes_job=pass_jobs, ) # collect encodings for ios input_encodings, output_encodings = [], [] for n in edge_prog_mgr.exported_program().graph.nodes: if n.op == "placeholder": input_encodings.append(n.meta[QCOM_QUANT_ATTRS]) input_encodings[-1][QCOM_DTYPE] = torch.uint16 elif n.op == "output": output_encodings = n.meta[QCOM_QUANT_ATTRS_MAP].values() for output_encoding in output_encodings: output_encoding[QCOM_DTYPE] = torch.uint16 exec_prog = edge_prog_mgr.to_executorch( ExecutorchBackendConfig(passes=[BuildQuantIo()]) ) for dim in range(last_dim.min, last_dim.max + 1): with self.subTest(i=dim): input_shape = (1, 2, 3, dim) sample_input = (torch.rand(input_shape), torch.rand(input_shape)) self.verify_output( module, sample_input, exec_prog, input_encodings=tuple(input_encodings), output_encodings=tuple(output_encodings), check_io_shape=True, ) def test_qnn_backend_skip_node_id_partitioner(self): module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output( module, sample_input, expected_partitions=3, skip_node_id_set={"aten_add_tensor", "aten_mean_dim"}, ) def test_qnn_backend_skip_node_id_quantizer(self): module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) # define compile specs backend_options = generate_htp_compiler_spec( use_fp16=False, ) compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, ) # define quantizer quantizer = make_quantizer() # define calibration method def calibrator(gm): gm(*sample_input) # get partially lowererd graph module graph_module, edge_prog_mgrs = skip_annotation( nn_module=module, quantizer=quantizer, compiler_specs=compiler_specs, sample_input=sample_input, calibration_cb=calibrator, fp_node_id_set={"conv2d"}, ) self.assertEqual(len(edge_prog_mgrs), 1) # lower all graph again, the skipped operators will be left in CPU exec_prog = to_edge( torch.export.export(graph_module, sample_input, strict=True), ).to_executorch() self.verify_output(module, sample_input, exec_prog) def test_qnn_backend_skip_node_op_partitioner(self): module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output( module, sample_input, expected_partitions=2, skip_node_op_set={"aten.add.Tensor"}, ) def test_qnn_backend_skip_node_op_quantizer(self): module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) # define compile specs backend_options = generate_htp_compiler_spec( use_fp16=False, ) compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, ) # define quantizer quantizer = make_quantizer() # define calibration method def calibrator(gm): gm(*sample_input) # get partially lowererd graph module graph_module, edge_prog_mgrs = skip_annotation( nn_module=module, quantizer=quantizer, compiler_specs=compiler_specs, sample_input=sample_input, calibration_cb=calibrator, fp_node_op_set={torch.ops.aten.add.Tensor}, ) self.assertEqual(len(edge_prog_mgrs), 2) # lower all graph again, the skipped operators will be left in CPU exec_prog = exec_prog = to_edge( torch.export.export(graph_module, sample_input, strict=True), ).to_executorch() self.verify_output(module, sample_input, exec_prog) @unittest.expectedFailure def test_qnn_backend_spill_fill_buffer_size(self): # TODO: Fix self.assertNotEqual(0, max_sf_size) module = LargeTensorLinear() # noqa: F405 sample_input = (torch.randn(1, 256, 512),) module = self.get_qdq_module(module, sample_input) backend_options = generate_htp_compiler_spec( use_fp16=False, use_multi_contexts=True, ) compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, ) edge_prog = to_edge_transform_and_lower_to_qnn( module, sample_input, compiler_specs ) max_sf_size = update_spill_fill_size(edge_prog.exported_program()) self.assertNotEqual(0, max_sf_size) def test_qnn_backend_graph_level_mixed_precision(self): module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) # define compile spec backend_options = generate_htp_compiler_spec( use_fp16=False, ) compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, ) # define quantizer quantizer = make_quantizer() # define calibration method def calibrator(gm): gm(*sample_input) # get partially lowererd graph module graph_module, edge_prog_mgrs = skip_annotation( nn_module=module, quantizer=quantizer, compiler_specs=compiler_specs, sample_input=sample_input, calibration_cb=calibrator, fp_node_id_set={"add", "mean"}, fallback_to_cpu=False, ) self.assertEqual(len(edge_prog_mgrs), 5) # lower all graph again, the skipped operators will be delegated with fp16 exec_prog = to_edge( torch.export.export(graph_module, sample_input, strict=True), ).to_executorch() self.verify_output(module, sample_input, exec_prog) def test_qnn_backend_multi_contexts(self): module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) module = self.get_qdq_module(module, sample_input) backend_options = generate_htp_compiler_spec( use_fp16=False, use_dlbc=True, use_multi_contexts=True, ) compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, ) pass_jobs = get_capture_program_passes() split_graph_pass, setting = self.split_graph(4) pass_jobs[split_graph_pass] = setting dep_table = get_passes_dependency_for_capture_program() dep_table[split_graph_pass] = [FoldQDQ] edge_prog = to_edge_transform_and_lower_to_qnn( module, sample_input, compiler_specs, dep_table=dep_table, passes_job=pass_jobs, ) update_spill_fill_size(edge_prog.exported_program()) exec_prog = edge_prog.to_executorch() self.verify_output(module, sample_input, exec_prog) def test_qnn_backend_multi_contexts_composite(self): backend_options = generate_htp_compiler_spec( use_fp16=False, use_dlbc=True, use_multi_contexts=True, ) compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, ) module = CompositeDelegateModule( # noqa: F405 compiler_specs=compiler_specs, to_edge_transform_and_lower_method=to_edge_transform_and_lower_to_qnn, quantize_method=self.get_qdq_module, ) sample_input = module.get_random_input() edge_prog = to_edge( torch.export.export(module, sample_input, strict=True), ) update_spill_fill_size(edge_prog.exported_program()) exec_prog = edge_prog.to_executorch() self.verify_output(module.get_reference_module(), sample_input, exec_prog) def test_qnn_backend_multi_graphs(self): if self.enable_x86_64: self.skipTest("weight sharing is not supported on host machine") seq_conv = Conv2dSequential() # noqa: F405 # weight sharing modules = [seq_conv, seq_conv.second] sample_inputs = [(torch.randn([1, 1, 3, 3]),), (torch.randn([1, 3, 3, 3]),)] graph_names = ["seq_conv", "single_conv"] backend_options = generate_htp_compiler_spec( use_fp16=False, ) compiler_specs = [ generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, multiple_graphs=True, weight_sharing=True, graph_name=graph_name, ) for graph_name in graph_names ] edge_progs = [ to_edge_transform_and_lower_to_qnn( self.get_qdq_module(module, sample_input), sample_input, compiler_spec ) for module, sample_input, compiler_spec in zip( modules, sample_inputs, compiler_specs ) ] prog_mgr, _ = generate_multi_graph_program( compiler_specs=compiler_specs[0], processed_bytes=[ edge_prog.exported_program().graph_module.lowered_module_0.processed_bytes for edge_prog in edge_progs ], ) for index, module in enumerate(modules): self.verify_output( module=module, sample_inputs=sample_inputs[index], executorch_prog=prog_mgr, method_index=index, ) def test_qnn_backend_profile_op(self): TestQNN.enable_profile = True backend_options = generate_htp_compiler_spec(use_fp16=False) TestQNN.compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, profile=True, ) module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output( module, sample_input, expected_partitions=1, expected_profile_events=30, ) def test_qnn_backend_shared_buffer(self): TestQNN.shared_buffer = True backend_options = generate_htp_compiler_spec( use_fp16=False, ) TestQNN.compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, shared_buffer=True, ) module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output( module, sample_input, expected_partitions=1, ) def test_qnn_backend_online_prepare(self): if self.enable_x86_64: self.skipTest("online prepare is not supported on host machine") backend_options = generate_htp_compiler_spec(use_fp16=False) TestQNN.compiler_specs = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, online_prepare=True, ) module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) module = self.get_qdq_module(module, sample_input) self.lower_module_and_test_output(module, sample_input) @unittest.expectedFailure def test_qnn_backend_context_direct(self): # TODO: Fix QNN tools pairs with np 2.x with tempfile.TemporaryDirectory() as tmp_dir: module = ContextBinaryExample() # noqa: F405 generate_context_binary( module=module, inputs=module.example_inputs(), quantized=True, artifact_dir=tmp_dir, ) ctx_path = f"{tmp_dir}/model_ctx.bin" bundle_program = from_context_binary(ctx_path, "ctx_loader") self.verify_output( module, tuple( torch.randn(size=v.shape, dtype=v.dtype) for v in bundle_program["inputs"].values() ), bundle_program["edge_program_manager"].to_executorch(), ) def test_qnn_backend_context_extraction(self): module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) module = self.get_qdq_module(module, sample_input) backend_options = generate_htp_compiler_spec(use_fp16=False) # Validate dlc compiler_spec = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, online_prepare=True, ) with tempfile.TemporaryDirectory() as tmp_dir: edge_prog_mgr = to_edge_transform_and_lower_to_qnn( module, sample_input, compiler_spec ).to_executorch() pte_path = f"{tmp_dir}/model.pte" with open(pte_path, "wb") as f: edge_prog_mgr.write_to_file(f) dump_context_from_pte(pte_path) qnn_tool = QnnTool(tmp_dir, pte_path, sample_input) qnn_tool.qnn_context_binary_generator() qnn_tool.qnn_net_run() compiler_specs = [ self.compiler_specs, ] validators = [validate_context_binary] for compiler_spec, validate in zip(compiler_specs, validators): edge_prog_mgr = to_edge_transform_and_lower_to_qnn( module, sample_input, compiler_spec ) lowered_module = edge_prog_mgr.exported_program().graph_module._modules[ "lowered_module_0" ] qnn_mgr = PyQnnManagerAdaptor.QnnManager( lowered_module.compile_specs[0].value ) qnn_mgr.Init() binary = qnn_mgr.StripProtocol(lowered_module.processed_bytes) validate(binary) def test_qnn_backend_dump_context_from_pte(self): module = SimpleModel() # noqa: F405 sample_input = (torch.ones(1, 32, 28, 28), torch.ones(1, 32, 28, 28)) module = self.get_qdq_module(module, sample_input) backend_options = generate_htp_compiler_spec(use_fp16=True) # Validate dlc compiler_spec = generate_qnn_executorch_compiler_spec( soc_model=self.chipset_table[TestQNN.model], backend_options=backend_options, online_prepare=True, ) with tempfile.TemporaryDirectory() as tmp_dir: edge_prog_mgr = to_edge_transform_and_lower_to_qnn( module, sample_input, compiler_spec ).to_executorch() pte_path = f"{tmp_dir}/model.pte" with open(pte_path, "wb") as f: edge_prog_mgr.write_to_file(f) dump_context_from_pte(pte_path) qnn_tool = QnnTool(tmp_dir, pte_path, sample_input) qnn_tool.qnn_context_binary_generator() qnn_tool.qnn_net_run() compiler_specs = [ self.compiler_specs, ] validators = [validate_context_binary] for compiler_spec, validate in zip(compiler_specs, validators): edge_prog_mgr = to_edge_transform_and_lower_to_qnn( module, sample_input, compiler_spec ).to_executorch() with tempfile.TemporaryDirectory() as tmp_dir: pte_path = f"{tmp_dir}/model.pte" with open(pte_path, "wb") as f: edge_prog_mgr.write_to_file(f) dump_context_from_pte(pte_path) binary_name = f"{tmp_dir}/forward_0.bin" self.assertTrue(os.path.isfile(binary_name)) with open(binary_name, "rb") as f: stripped_binary = f.read() validate(stripped_binary) def test_qnn_backend_draw_graph(self): golden_data = """digraph test { rankdir=TB aten_convolution_default_1_0 [label=<
name: aten_convolution_default_1_0
data_type: Qnn_DataType_t.QNN_DATATYPE_UFIXED_POINT_8
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_NATIVE
dims: [1, 28, 28, 32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_SCALE_OFFSET
> color=black fillcolor=transparent shape=box style=rounded] aten_relu_default_1_0 [label=<
name: aten_relu_default_1_0
data_type: Qnn_DataType_t.QNN_DATATYPE_UFIXED_POINT_8
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_NATIVE
dims: [1, 28, 28, 32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_SCALE_OFFSET
> color=black fillcolor=transparent shape=box style=rounded] quantized_decomposed_quantize_per_tensor_default_0 [label=<
name: quantized_decomposed_quantize_per_tensor_default_0
data_type: Qnn_DataType_t.QNN_DATATYPE_UFIXED_POINT_8
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_NATIVE
dims: [1, 32, 28, 28]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_SCALE_OFFSET
> color=black fillcolor=transparent shape=box style=rounded] b__frozen_param2_0 [label=<
name: b__frozen_param2_0
data_type: Qnn_DataType_t.QNN_DATATYPE_SFIXED_POINT_8
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_STATIC
dims: [3, 3, 32, 32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_AXIS_SCALE_OFFSET
> color=black fillcolor=transparent shape=box style=rounded] b__frozen_param3_0 [label=<
name: b__frozen_param3_0
data_type: Qnn_DataType_t.QNN_DATATYPE_SFIXED_POINT_32
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_STATIC
dims: [32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_AXIS_SCALE_OFFSET
> color=black fillcolor=transparent shape=box style=rounded] b__frozen_param0_0 [label=<
name: b__frozen_param0_0
data_type: Qnn_DataType_t.QNN_DATATYPE_SFIXED_POINT_8
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_STATIC
dims: [3, 3, 32, 32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_AXIS_SCALE_OFFSET
> color=black fillcolor=transparent shape=box style=rounded] b__frozen_param1_0 [label=<
name: b__frozen_param1_0
data_type: Qnn_DataType_t.QNN_DATATYPE_SFIXED_POINT_32
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_STATIC
dims: [32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_AXIS_SCALE_OFFSET
> color=black fillcolor=transparent shape=box style=rounded] aten_convolution_default_0 [label=<
name: aten_convolution_default_0
data_type: Qnn_DataType_t.QNN_DATATYPE_UFIXED_POINT_8
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_NATIVE
dims: [1, 28, 28, 32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_SCALE_OFFSET
> color=black fillcolor=transparent shape=box style=rounded] aten_relu_default_0 [label=<
name: aten_relu_default_0
data_type: Qnn_DataType_t.QNN_DATATYPE_UFIXED_POINT_8
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_NATIVE
dims: [1, 28, 28, 32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_SCALE_OFFSET
> color=black fillcolor=transparent shape=box style=rounded] aten_add_tensor_0 [label=<
name: aten_add_tensor_0
data_type: Qnn_DataType_t.QNN_DATATYPE_UFIXED_POINT_8
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_NATIVE
dims: [1, 28, 28, 32]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_SCALE_OFFSET
> color=black fillcolor=transparent shape=box style=rounded] input_0_x_0 [label=<
name: input_0_x_0
data_type: Qnn_DataType_t.QNN_DATATYPE_FLOAT_32
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_APP_WRITE
dims: [1, 32, 28, 28]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_UNDEFINED
> color=black fillcolor=transparent shape=box style=rounded] output_quantized_decomposed_dequantize_per_tensor_tensor_0 [label=<
name: output_quantized_decomposed_dequantize_per_tensor_tensor_0
data_type: Qnn_DataType_t.QNN_DATATYPE_FLOAT_32
tensor_type: Qnn_TensorType_t.QNN_TENSOR_TYPE_APP_READ
dims: [1, 32, 28, 28]
quantization_encoding: Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_UNDEFINED
> color=black fillcolor=transparent shape=box style=rounded] quantized_decomposed_quantize_per_tensor_default_0 -> aten_convolution_default_1_0 input_0_x_0 -> quantized_decomposed_quantize_per_tensor_default_0 b__frozen_param2_0 -> aten_convolution_default_1_0 b__frozen_param3_0 -> aten_convolution_default_1_0 aten_convolution_default_1_0 -> aten_relu_default_1_0 quantized_decomposed_quantize_per_tensor_default_0 -> aten_convolution_default_0 b__frozen_param0_0 -> aten_convolution_default_0 b__frozen_param1_0 -> aten_convolution_default_0 aten_convolution_default_0 -> aten_relu_default_0 aten_relu_default_0 -> aten_add_tensor_0 aten_relu_default_1_0 -> aten_add_tensor_0 aten_add_tensor_0 -> output_quantized_decomposed_dequantize_per_tensor_tensor_0 }""" module = DrawGraphModel() # noqa: F405 sample_input = (torch.randn(1, 32, 28, 28),) module = self.get_qdq_module(module, sample_input) # TODO: Figure out how to get the original graph module with to_edge_transform_and_lowering_to_qnn delegated_program = capture_program(module, sample_input) """ This piece of code simulates the behavior of the final preprocessing step to obtain the op wrapper list. In practice, users need to set a breakpoint in the preprocessing step and use the DrawGraph tool to visualize the graph. """ graph_module = QnnPassManager().transform_for_preprocess_pipeline( delegated_program.exported_program ) nodes_to_wrappers = defaultdict(dict) node_visitors = get_node_visitors( delegated_program.exported_program, enable_tensor_dump=False ) py_op_wrapper_list = [] for node in graph_module.graph.nodes: if node.op == "call_function": if node.target.__name__ in node_visitors: py_op_wrapper = node_visitors[node.target.__name__].define_node( node, nodes_to_wrappers ) if py_op_wrapper is not None: if isinstance(py_op_wrapper, List): py_op_wrapper_list.extend(py_op_wrapper) else: py_op_wrapper_list.append(py_op_wrapper) elif node.op in [ "get_attr", "placeholder", "output", ]: continue # random py_op_wrapper_list to check it's correctness random.shuffle(py_op_wrapper_list) DrawGraph("test", ".", py_op_wrapper_list, dot_string=True) test_file = os.path.join(".", "test.dot") with open(test_file, "r") as test: test_data = test.read() assert sorted(golden_data.split()) == sorted( test_data.split() ), "Generated .dot file does not match the golden file." class TestExampleLLMScript(TestQNN): def required_envs(self, conditions=None) -> bool: conditions = [] if conditions is None else conditions return all( [ self.executorch_root, self.artifact_dir, *conditions, ] ) def test_llama3_2_1b(self): if not self.required_envs(): self.skipTest("missing required envs") assert ( self.llama_artifacts is not None ), "Please provide path to llama artifacts" prompt = "What is the meaning of life?" cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/oss_scripts/llama/llama.py", "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--model", self.model, "--checkpoint", f"{self.llama_artifacts}/consolidated.00.pth", "--params", f"{self.llama_artifacts}/params.json", "--tokenizer_model", f"{self.llama_artifacts}/tokenizer.model", "--ip", self.ip, "--port", str(self.port), "--prompt", f"{prompt}", "--ptq", "16a4w", "--temperature", "0", "--llama_model", "llama3_2", "--model_mode", "hybrid", "--prefill_ar_len", "32", "--max_seq_len", "512", "--num_sharding", "4", ] if self.compile_only: cmds.extend(["--compile_only"]) elif self.device: cmds.extend(["--device", self.device]) if self.host: cmds.extend(["--host", self.host]) elif self.enable_x86_64: cmds.extend(["--enable_x86_64"]) if self.pre_gen_pte: cmds.extend(["--pre_gen_pte", self.pre_gen_pte]) golden_start_with = "<|begin_of_text|><|start_header_id|>user<|end_header_id|>" p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: if not self.compile_only: model_out = msg["result"][0] self.assertTrue( model_out.startswith(golden_start_with), f"Expected Output: {golden_start_with}. Actual Output: {model_out}", ) # x86 does not allow weight sharing, so we don't check pte size. # Inference speed on x86 is slow, so we only check when running on Android if not self.enable_x86_64: pte_size = msg["pte_size"] self.assertLessEqual(pte_size, 1300000000) if not self.compile_only and not self.enable_x86_64: self.assertGreaterEqual(msg["inference_speed"], 66) # Lanai def test_llama_stories_110m(self): if not self.required_envs(): self.skipTest("missing required envs") assert ( self.llama_artifacts is not None ), "Please provide path to llama artifacts" prompt = "Once" cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/oss_scripts/llama/llama.py", "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--model", self.model, "--checkpoint", f"{self.llama_artifacts}/stories110M.pt", "--params", f"{self.llama_artifacts}/params.json", "--tokenizer_model", f"{self.llama_artifacts}/tokenizer.model", "--tokenizer_bin", f"{self.llama_artifacts}/tokenizer.bin", "--ip", self.ip, "--port", str(self.port), "--prompt", f"{prompt}", "--ptq", "16a4w", "--temperature", "0", "--llama_model", "stories110m", "--model_mode", "hybrid", "--prefill_ar_len", "32", "--max_seq_len", "128", ] if self.compile_only: cmds.extend(["--compile_only"]) elif self.device: cmds.extend(["--device", self.device]) if self.host: cmds.extend(["--host", self.host]) elif self.enable_x86_64: cmds.extend(["--enable_x86_64"]) if self.pre_gen_pte: cmds.extend(["--pre_gen_pte", self.pre_gen_pte]) golden_start_with = "Once upon a time," p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: if not self.compile_only: model_out = msg["result"][0] self.assertTrue( model_out.startswith(golden_start_with), f"Expected Output: {golden_start_with}. Actual Output: {model_out}", ) # x86 does not allow weight sharing, so we don't check pte size if not self.enable_x86_64: pte_size = msg["pte_size"] self.assertLessEqual(pte_size, 130000000) if not self.compile_only and not self.enable_x86_64: self.assertGreaterEqual(msg["inference_speed"], 220) # Lanai class TestExampleOssScript(TestQNN): def required_envs(self, conditions=None) -> bool: conditions = [] if conditions is None else conditions return all( [ self.executorch_root, self.artifact_dir, *conditions, ] ) def test_conv_former(self): if not self.required_envs([self.image_dataset]): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/oss_scripts/conv_former.py", "--dataset", self.image_dataset, "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["top_1"], 60) self.assertGreaterEqual(msg["top_5"], 80) def test_dino_v2(self): if not self.required_envs([self.image_dataset]): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/oss_scripts/dino_v2.py", "--dataset", self.image_dataset, "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["top_1"], 70) self.assertGreaterEqual(msg["top_5"], 85) def test_efficientSAM(self): if not self.required_envs( [self.image_dataset, self.pretrained_weight, self.oss_repo] ): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/oss_scripts/efficientSAM/efficientSAM.py", "--dataset", self.image_dataset, "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--oss_repo", self.oss_repo, "--pretrained_weight", self.pretrained_weight, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["MIoU"], 0.55) def test_esrgan(self): if not self.required_envs(): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/oss_scripts/esrgan.py", "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--default_dataset", "--oss_repo", self.oss_repo, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["PSNR"], 24) self.assertGreaterEqual(msg["SSIM"], 0.8) def test_fastvit(self): if not self.required_envs( [self.image_dataset, self.pretrained_weight, self.oss_repo] ): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/oss_scripts/fastvit.py", "--dataset", self.image_dataset, "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--oss_repo", self.oss_repo, "--pretrained_weight", self.pretrained_weight, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["top_1"], 60) self.assertGreaterEqual(msg["top_5"], 80) def test_fbnet(self): if not self.required_envs([self.image_dataset]): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/oss_scripts/fbnet.py", "--dataset", self.image_dataset, "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["top_1"], 60) self.assertGreaterEqual(msg["top_5"], 90) def test_gMLP(self): if not self.required_envs([self.image_dataset]): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/oss_scripts/gMLP_image_classification.py", "--dataset", self.image_dataset, "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["top_1"], 60) self.assertGreaterEqual(msg["top_5"], 90) def test_regnet(self): if not self.required_envs([self.image_dataset]): self.skipTest("missing required envs") weights = ["regnet_y_400mf", "regnet_x_400mf"] cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/oss_scripts/regnet.py", "--dataset", self.image_dataset, "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) for weight in weights: p = subprocess.Popen( cmds + ["--weights", weight], stdout=subprocess.DEVNULL ) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["top_1"], 60) self.assertGreaterEqual(msg["top_5"], 85) def test_retinanet(self): if not self.required_envs([self.image_dataset]): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/oss_scripts/retinanet.py", "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--dataset", self.image_dataset, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["mAP"], 0.6) def test_squeezenet(self): if not self.required_envs([self.image_dataset]): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/oss_scripts/squeezenet.py", "--dataset", self.image_dataset, "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["top_1"], 45) self.assertGreaterEqual(msg["top_5"], 70) def test_ssd300_vgg16(self): if not self.required_envs([self.pretrained_weight, self.oss_repo]): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/oss_scripts/ssd300_vgg16.py", "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--oss_repo", self.oss_repo, "--pretrained_weight", self.pretrained_weight, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["mAP"], 0.70) class TestExampleQaihubScript(TestQNN): def required_envs(self, conditions=None) -> bool: conditions = [] if conditions is None else conditions return all( [ self.executorch_root, self.artifact_dir, *conditions, ] ) def test_utils_export(self): with tempfile.TemporaryDirectory() as tmp_dir: module = ContextBinaryExample() # noqa: F405 generate_context_binary( module=module, inputs=module.example_inputs(), quantized=True, artifact_dir=tmp_dir, ) ctx_path = f"{tmp_dir}/model_ctx.bin" fpath = f"{self.executorch_root}/examples/qualcomm/qaihub_scripts/utils/export.py" # do compilation compile_cmds = [ "python", fpath, "compile", "-a", ctx_path, "-m", self.model, "-l", "False", "-b", self.build_folder, "-o", f"{tmp_dir}/output_pte", ] compile_process = subprocess.Popen( compile_cmds, stdout=subprocess.DEVNULL, cwd=self.executorch_root ) output_pte_dir = f"{tmp_dir}/output_pte/model_ctx" compile_process.communicate() # check artifacts are correctly generated self.assertTrue( all( [ Path(output_pte_dir).exists(), Path(f"{output_pte_dir}/model_ctx.json").exists(), Path(f"{output_pte_dir}/model_ctx.svg").exists(), ] ) ) # prepare input files input_list, inputs = [], module.example_inputs() for name, tensor in inputs.items(): tensor_path = f"{output_pte_dir}/{name}.pt" torch.save(tensor, tensor_path) input_list.append(tensor_path) # do execution output_data_dir = f"{tmp_dir}/output_data" execute_cmds = [ "python", fpath, "execute", "-p", output_pte_dir, "-i", *input_list, "-s", self.device, "-z", "-b", self.build_folder, "-o", output_data_dir, ] if self.host is not None: execute_cmds.append(f"-H {self.host}") execute_process = subprocess.Popen(execute_cmds, cwd=self.executorch_root) execute_process.communicate() # read outputs with open(f"{output_pte_dir}/model_ctx.json", "r") as f: graph_info = json.load(f) device_output = [] for output in graph_info["outputs"]: with open(f"{output_data_dir}/{output['name']}.pt", "rb") as f: buffer = io.BytesIO(f.read()) device_output.append(torch.load(buffer, weights_only=False)) # validate outputs golden_output = module.forward(inputs["x"], inputs["y"]) self.atol, self.rtol = 1e-1, 1 self._assert_outputs_equal(golden_output, device_output) def test_llama2_7b(self): if not self.required_envs(): self.skipTest("missing required envs") prompt = "Explain the rules of baseball" cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/qaihub_scripts/llama/llama2/qaihub_llama2_7b.py", "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--tokenizer_bin", f"{self.artifact_dir}/tokenizer.bin", "--context_binaries", f"{self.artifact_dir}", "--ip", self.ip, "--port", str(self.port), "--prompt", f"{prompt}", ] if self.host: cmds.extend(["--host", self.host]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: model_out = msg["result"] self.assertTrue(model_out.startswith(prompt)) def test_llama3_8b(self): if not self.required_envs(): self.skipTest("missing required envs") prompt = "Explain the rules of baseball" cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/qaihub_scripts/llama/llama3/qaihub_llama3_8b.py", "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--tokenizer_model", f"{self.artifact_dir}/tokenizer.model", "--context_binaries", f"{self.artifact_dir}", "--ip", self.ip, "--port", str(self.port), "--prompt", f"{prompt}", ] if self.host: cmds.extend(["--host", self.host]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: model_out = msg["result"] expected_result = ( "<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n\n" + prompt + "<|eot_id|><|start_header_id|>assistant<|end_header_id|>" ) self.assertTrue(model_out.startswith(expected_result)) def test_stable_diffusion(self): if not self.required_envs(): self.skipTest("missing required envs") prompt = "a photo of an astronaut riding a horse on mars" cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/qaihub_scripts/stable_diffusion/qaihub_stable_diffusion.py", "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--text_encoder_bin", f"{self.artifact_dir}/text_encoder.serialized.bin", "--unet_bin", f"{self.artifact_dir}/unet.serialized.bin", "--vae_bin", f"{self.artifact_dir}/vae.serialized.bin", "--vocab_json", f"{self.artifact_dir}/vocab.json", "--num_time_steps", "20", "--ip", self.ip, "--port", str(self.port), "--prompt", f"{prompt}", "--fix_latents", ] if self.host: cmds.extend(["--host", self.host]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: # For the default settings and prompt, the expected results will be {PSNR: 23.258, SSIM: 0.852} self.assertGreaterEqual(msg["PSNR"], 20) self.assertGreaterEqual(msg["SSIM"], 0.8) class TestExampleScript(TestQNN): def required_envs(self, conditions=None) -> bool: conditions = [] if conditions is None else conditions return all( [ self.executorch_root, self.artifact_dir, *conditions, ] ) def test_mobilenet_v2(self): if not self.required_envs([self.image_dataset]): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/scripts/mobilenet_v2.py", "--dataset", self.image_dataset, "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) if self.shared_buffer: cmds.extend(["--shared_buffer"]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["top_1"], 60) self.assertGreaterEqual(msg["top_5"], 80) def test_mobilenet_v3(self): if not self.required_envs([self.image_dataset]): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/scripts/mobilenet_v3.py", "--dataset", self.image_dataset, "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) if self.shared_buffer: cmds.extend(["--shared_buffer"]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["top_1"], 60) self.assertGreaterEqual(msg["top_5"], 80) def test_inception_v3(self): if not self.required_envs([self.image_dataset]): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/scripts/inception_v3.py", "--dataset", self.image_dataset, "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) if self.shared_buffer: cmds.extend(["--shared_buffer"]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["top_1"], 60) self.assertGreaterEqual(msg["top_5"], 80) def test_inception_v4(self): if not self.required_envs([self.image_dataset]): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/scripts/inception_v4.py", "--dataset", self.image_dataset, "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) if self.shared_buffer: cmds.extend(["--shared_buffer"]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["top_1"], 60) self.assertGreaterEqual(msg["top_5"], 80) def test_vit(self): if not self.required_envs([self.image_dataset]): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/scripts/torchvision_vit.py", "--dataset", self.image_dataset, "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) if self.shared_buffer: cmds.extend(["--shared_buffer"]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["top_1"], 65) self.assertGreaterEqual(msg["top_5"], 90) def test_edsr(self): if not self.required_envs(): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/scripts/edsr.py", "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--default_dataset", "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) if self.shared_buffer: cmds.extend(["--shared_buffer"]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["PSNR"], 25) self.assertGreaterEqual(msg["SSIM"], 0.8) def test_deeplab_v3(self): if not self.required_envs(): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/scripts/deeplab_v3.py", "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--download", "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) if self.shared_buffer: cmds.extend(["--shared_buffer"]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertGreaterEqual(msg["PA"], 0.85) self.assertGreaterEqual(msg["MPA"], 0.70) self.assertGreaterEqual(msg["MIoU"], 0.55) @unittest.skip("dynamic shape inputs appear in recent torch.export.export") def test_mobilebert(self): if not self.required_envs([self.pretrained_weight]): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/scripts/mobilebert_fine_tune.py", "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--pretrained_weight", self.pretrained_weight, "--ip", self.ip, "--port", str(self.port), "--use_fp16", ] if self.host: cmds.extend(["--host", self.host]) if self.shared_buffer: cmds.extend(["--shared_buffer"]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: cpu, htp = msg["CPU"], msg["HTP"] for k, v in cpu.items(): self.assertLessEqual(abs(v[0] - htp[k][0]), 2) @unittest.skip("eagar mode fake quant works well, need further investigation") def test_ptq_mobilebert(self): if not self.required_envs([self.pretrained_weight]): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/scripts/mobilebert_fine_tune.py", "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--pretrained_weight", self.pretrained_weight, "--ptq", "16a16w", "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) if self.shared_buffer: cmds.extend(["--shared_buffer"]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: cpu, htp = msg["CPU"], msg["HTP"] for k, v in cpu.items(): self.assertLessEqual(abs(v[0] - htp[k][0]), 5) @unittest.skip("encountered undefined symbol in mainline, reopen once resolved") def test_wav2letter(self): if not self.required_envs([self.pretrained_weight]): self.skipTest("missing required envs") cmds = [ "python", f"{self.executorch_root}/examples/qualcomm/scripts/wav2letter.py", "--artifact", self.artifact_dir, "--build_folder", self.build_folder, "--device", self.device, "--model", self.model, "--pretrained_weight", self.pretrained_weight, "--ip", self.ip, "--port", str(self.port), ] if self.host: cmds.extend(["--host", self.host]) if self.shared_buffer: cmds.extend(["--shared_buffer"]) p = subprocess.Popen(cmds, stdout=subprocess.DEVNULL) with Listener((self.ip, self.port)) as listener: conn = listener.accept() p.communicate() msg = json.loads(conn.recv()) if "Error" in msg: self.fail(msg["Error"]) else: self.assertLessEqual(msg["wer"], 0.5) self.assertLessEqual(msg["cer"], 0.25) def test_export_example(self): if not self.required_envs([self.model_name]): self.skipTest("missing required envs") with tempfile.TemporaryDirectory() as tmp_dir: cmds = [ "python", "qualcomm/scripts/export_example.py", "--model_name", self.model_name, "--output_folder", "{}/".format(tmp_dir), "--generate_etrecord", ] p = subprocess.Popen( cmds, stdout=subprocess.DEVNULL, cwd=f"{self.executorch_root}/examples" ) p.communicate() self.assertTrue( Path("{0}/{1}.pte".format(tmp_dir, self.model_name)).exists() ) def setup_environment(): parser = setup_common_args_and_variables() parser.add_argument( "-r", "--executorch_root", help="Root location of current repo", type=str, ) parser.add_argument( "-a", "--artifact_dir", help="Location for putting generated artifacts", type=str, ) parser.add_argument( "-i", "--image_dataset", help="Location for imagenet dataset", type=str, ) parser.add_argument( "-p", "--pretrained_weight", help="Location for pretrained weighting", default="", type=str, ) parser.add_argument( "-n", "--model_name", help="Input the model to export", type=str, ) parser.add_argument( "-P", "--enable_profile", help="Profile the performance of each operator with kProfileDetailed profile level", action="store_true", ) parser.add_argument( "-e", "--error_only", help="Emit log only when error happened", action="store_true", ) parser.add_argument( "--oss_repo", help="Path to open source software model repository", type=str, ) parser.add_argument( "--pre_gen_pte", help="Run the pre-generated pte in the given directory.", type=str, ) parser.add_argument( "--llama_artifacts", help="A folder that contains: weight, tokenizer, and params.", type=str, ) args, ns_args = parser.parse_known_args(namespace=unittest) TestQNN.host = args.host TestQNN.device = args.device TestQNN.model = args.model TestQNN.build_folder = args.build_folder TestQNN.executorch_root = args.executorch_root TestQNN.artifact_dir = args.artifact_dir TestQNN.image_dataset = args.image_dataset TestQNN.pretrained_weight = args.pretrained_weight TestQNN.model_name = args.model_name TestQNN.online_prepare = args.online_prepare TestQNN.enable_profile = args.enable_profile TestQNN.error_only = args.error_only TestQNN.oss_repo = args.oss_repo TestQNN.shared_buffer = args.shared_buffer TestQNN.enable_x86_64 = args.enable_x86_64 TestQNN.dump_intermediate_outputs = args.dump_intermediate_outputs TestQNN.compile_only = args.compile_only TestQNN.pre_gen_pte = args.pre_gen_pte TestQNN.llama_artifacts = args.llama_artifacts return sys.argv[:1] + ns_args if __name__ == "__main__": ut_args = setup_environment() unittest.main(argv=ut_args)