Skip to content

Commit 2874dcb

Browse files
authored
Qualcomm AI Engine Direct - Adding QNN backend support for randn core ATen op (pytorch#19377)
### Summary Added support for the `randn` core ATen op using the existing [QNN backend implementation](https://docs.qualcomm.com/doc/80-63442-10/topic/MasterOpDef.html#randomnormallike) of `RandomNormalLike`. Note only `INT8` outputs are supported. Also fixed a minor bug in the `rand` op's implementation and test and removed the `FP` test as it doesn't serve a purpose since `rand` doesn't support `FP` outputs either. ### Test plan ``` python backends/qualcomm/tests/test_qnn_delegate.py -k TestQNNQuantizedOperator.test_qnn_backend_randn --model SM8750 --host aisw-vm15-labsd --device 545ee4aa --build_folder build-android ```
1 parent 0c9b0df commit 2874dcb

9 files changed

Lines changed: 127 additions & 23 deletions

File tree

backends/qualcomm/_passes/layout_transform.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ class LayoutTransform(ExportPass):
113113
exir_ops.edge.aten.neg.default,
114114
exir_ops.edge.aten.pow.Tensor_Scalar,
115115
exir_ops.edge.aten.prelu.default,
116+
exir_ops.edge.aten.rand.default,
117+
exir_ops.edge.aten.randn.default,
116118
exir_ops.edge.aten.reflection_pad1d.default,
117119
exir_ops.edge.aten.reflection_pad2d.default,
118120
exir_ops.edge.aten.repeat.default,

backends/qualcomm/builders/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ Please help update following table if you are contributing new operators:
368368
+ 🚫 = Deprecated, supported with other QNN Ops
369369

370370

371-
| Operators | HTP - 98/119 Enabled |
371+
| Operators | HTP - 99/120 Enabled |
372372
|-----------|---------|
373373
| Argmax | ✓ |
374374
| Argmin | ✓ |
@@ -457,7 +457,8 @@ Please help update following table if you are contributing new operators:
457457
| PoolMax2d | ✓ |
458458
| Prelu | ✓ |
459459
| Quantize | ✓ |
460-
| Rand | ✓ |
460+
| RandomUniformLike | ✓ |
461+
| RandomNormalLike | ✓ |
461462
| ReduceMax | ✓ |
462463
| ReduceMean | ✓ |
463464
| ReduceMin | ✓ |

backends/qualcomm/builders/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
op_prelu,
8282
op_quantize,
8383
op_rand,
84+
op_randn,
8485
op_relu,
8586
op_repeat,
8687
op_reshape,
@@ -194,6 +195,7 @@
194195
op_prelu,
195196
op_quantize,
196197
op_rand,
198+
op_randn,
197199
op_relu,
198200
op_repeat,
199201
op_reshape,
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Copyright (c) Qualcomm Innovation Center, Inc.
2+
# All rights reserved
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
from typing import Dict
7+
8+
import executorch.backends.qualcomm.python.PyQnnManagerAdaptor as PyQnnManager
9+
10+
import numpy as np
11+
import torch
12+
from executorch.backends.qualcomm.utils.constants import QCOM_DATA
13+
14+
from .node_visitor import NodeVisitor
15+
from .node_visitor_manager import register_node_visitor
16+
from .qnn_constants import OpRandomNormalLike, QNN_OP_PACKAGE_NAME_QTI_AISW
17+
18+
19+
@register_node_visitor
20+
class Randn(NodeVisitor):
21+
target = ["aten.randn.default", "aten.randn_like.default"]
22+
23+
def __init__(self, *args) -> None:
24+
super().__init__(*args)
25+
26+
def define_node(
27+
self,
28+
node: torch.fx.Node,
29+
nodes_to_wrappers: Dict[torch.fx.Node, PyQnnManager.TensorWrapper],
30+
) -> PyQnnManager.PyQnnOpWrapper:
31+
output_tensor = node.meta["val"]
32+
output_shape = list(output_tensor.shape)
33+
34+
shape_data = np.array(output_shape, dtype=np.uint32)
35+
shape_dims = [len(output_shape)]
36+
37+
shape_tensor_wrapper = PyQnnManager.TensorWrapper(
38+
f"{node.name}_shape",
39+
PyQnnManager.Qnn_TensorType_t.QNN_TENSOR_TYPE_STATIC,
40+
PyQnnManager.Qnn_DataType_t.QNN_DATATYPE_UINT_32,
41+
PyQnnManager.Qnn_QuantizationEncoding_t.QNN_QUANTIZATION_ENCODING_UNDEFINED,
42+
{},
43+
len(shape_dims),
44+
shape_dims,
45+
[],
46+
shape_data,
47+
True,
48+
)
49+
50+
output_tensor_wrapper = self.define_tensor(
51+
node,
52+
node,
53+
output_tensor,
54+
PyQnnManager.Qnn_TensorType_t.QNN_TENSOR_TYPE_NATIVE,
55+
nodes_to_wrappers,
56+
)
57+
58+
randn_op = PyQnnManager.PyQnnOpWrapper(
59+
node.name,
60+
QNN_OP_PACKAGE_NAME_QTI_AISW,
61+
OpRandomNormalLike.op_name,
62+
)
63+
64+
randn_op.AddInputTensors([shape_tensor_wrapper])
65+
randn_op.AddOutputTensors([output_tensor_wrapper])
66+
67+
randn_op.AddScalarParam(
68+
OpRandomNormalLike.param_mean,
69+
PyQnnManager.Qnn_DataType_t.QNN_DATATYPE_FLOAT_32,
70+
{QCOM_DATA: np.float32(0.0)},
71+
)
72+
73+
randn_op.AddScalarParam(
74+
OpRandomNormalLike.param_scale,
75+
PyQnnManager.Qnn_DataType_t.QNN_DATATYPE_FLOAT_32,
76+
{QCOM_DATA: np.float32(1.0)},
77+
)
78+
79+
return randn_op

backends/qualcomm/builders/qnn_constants.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,13 @@ class OpQuantize:
504504
op_name: str = "Quantize"
505505

506506

507+
@dataclass(init=False, frozen=True)
508+
class OpRandomNormalLike:
509+
op_name: str = "RandomNormalLike"
510+
param_mean: str = "mean"
511+
param_scale: str = "scale"
512+
513+
507514
@dataclass(init=False, frozen=True)
508515
class OpRandomUniformLike:
509516
op_name: str = "RandomUniformLike"

backends/qualcomm/quantizer/annotators/htp_rules.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,8 @@ class ColIm(GeneralOpDef):
345345
torch.ops.aten.zeros_like.default,
346346
torch.ops.aten.ones.default,
347347
torch.ops.aten.ones_like.default,
348+
torch.ops.aten.rand.default,
349+
torch.ops.aten.randn.default,
348350
],
349351
qnn_op=None,
350352
)

backends/qualcomm/quantizer/annotators/lpai_rules.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ class ColIm(GeneralOpDef):
272272
torch.ops.aten.zeros_like.default,
273273
torch.ops.aten.ones.default,
274274
torch.ops.aten.ones_like.default,
275+
torch.ops.aten.rand.default,
276+
torch.ops.aten.randn.default,
275277
],
276278
qnn_op=None,
277279
)

backends/qualcomm/tests/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1954,6 +1954,14 @@ def forward(self, x):
19541954
return torch.rand_like(x) + x
19551955

19561956

1957+
class Randn(torch.nn.Module):
1958+
def __init__(self):
1959+
super().__init__()
1960+
1961+
def forward(self, x):
1962+
return torch.randn_like(x) + x
1963+
1964+
19571965
class Reciprocal(torch.nn.Module):
19581966
def __init__(self):
19591967
super().__init__()

backends/qualcomm/tests/test_qnn_delegate.py

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1809,24 +1809,6 @@ def test_qnn_backend_prelu(self):
18091809
index += 1
18101810
self.lower_module_and_test_output(module, sample_input)
18111811

1812-
def test_qnn_backend_rand(self):
1813-
sample_inputs = [
1814-
(torch.randn(3, 4, 5),),
1815-
(torch.randn(2, 8),),
1816-
(
1817-
torch.randn(
1818-
10,
1819-
),
1820-
),
1821-
(torch.randn(1, 3, 32, 32),),
1822-
]
1823-
for i, sample_input in enumerate(sample_inputs):
1824-
with self.subTest(i=i):
1825-
module = Rand() # noqa: F405
1826-
self.lower_module_and_test_output(
1827-
module, sample_input, assert_output_equal=False
1828-
)
1829-
18301812
def test_qnn_backend_reciprocal(self):
18311813
module = Reciprocal() # noqa: F405
18321814
sample_input = (torch.randn([2, 2, 2, 2]),)
@@ -4566,6 +4548,7 @@ def test_qnn_backend_prelu(self):
45664548
self.lower_module_and_test_output(module, sample_input)
45674549

45684550
def test_qnn_backend_rand(self):
4551+
module = Rand() # noqa: F405
45694552
sample_inputs = [
45704553
(torch.randn(3, 4, 5),),
45714554
(torch.randn(2, 8),),
@@ -4578,10 +4561,28 @@ def test_qnn_backend_rand(self):
45784561
]
45794562
for i, sample_input in enumerate(sample_inputs):
45804563
with self.subTest(i=i):
4581-
module = Rand() # noqa: F405
4582-
module = self.get_qdq_module(module, sample_input)
4564+
qdq_module = self.get_qdq_module(module, sample_input)
4565+
self.lower_module_and_test_output(
4566+
qdq_module, sample_input, assert_output_equal=False
4567+
)
4568+
4569+
def test_qnn_backend_randn(self):
4570+
module = Randn() # noqa: F405
4571+
sample_inputs = [
4572+
(torch.randn(3, 4, 5),),
4573+
(torch.randn(2, 8),),
4574+
(
4575+
torch.randn(
4576+
10,
4577+
),
4578+
),
4579+
(torch.randn(1, 3, 32, 32),),
4580+
]
4581+
for i, sample_input in enumerate(sample_inputs):
4582+
with self.subTest(i=i):
4583+
qdq_module = self.get_qdq_module(module, sample_input)
45834584
self.lower_module_and_test_output(
4584-
module, sample_input, assert_output_equal=False
4585+
qdq_module, sample_input, assert_output_equal=False
45854586
)
45864587

45874588
def test_qnn_backend_reciprocal(self):

0 commit comments

Comments
 (0)