Skip to content

Commit b608f79

Browse files
authored
[QNN-EP] Enable largest attr and remove sorted check for topk. (microsoft#26150)
### Description Enable largest attr and remove sorted check for QNN topk op builder. ### Motivation and Context Signed-off-by: Mu-Chein Hsu <quic_muchhsu@quicinc.com>
1 parent b7ae53f commit b608f79

File tree

2 files changed

+96
-15
lines changed

2 files changed

+96
-15
lines changed

onnxruntime/core/providers/qnn/builder/opbuilder/topk.cc

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,6 @@ Status TopKOpBuilder::ExplictOpCheck(QnnModelWrapper& qnn_model_wrapper, const N
6060
if (!qnn_model_wrapper.IsConstantInput(input_1)) {
6161
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "The number of top elements to retrieve must be specified as constant input.");
6262
}
63-
NodeAttrHelper node_helper(node_unit);
64-
auto largest = node_helper.Get("largest", 1);
65-
auto sorted = node_helper.Get("sorted", 1);
66-
if (0 == sorted) {
67-
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "QNN TopK output is always sorted");
68-
}
69-
if (0 == largest) {
70-
return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "QNN TopK output is always largest values");
71-
}
7263

7364
return Status::OK();
7465
}
@@ -147,6 +138,16 @@ Status TopKOpBuilder::ProcessAttributesAndOutputs(QnnModelWrapper& qnn_model_wra
147138
qnn_model_wrapper.AddParamWrapper(std::move(k_param));
148139
std::vector<std::string> param_tensor_names{k_param_name};
149140

141+
// Add largest to TopK attr
142+
uint8_t largest = static_cast<uint8_t>(NodeAttrHelper(node_unit).Get("largest", 1));
143+
Qnn_Scalar_t qnn_largest_k = QNN_SCALAR_INIT;
144+
qnn_largest_k.dataType = QNN_DATATYPE_BOOL_8;
145+
qnn_largest_k.bool8Value = largest;
146+
QnnParamWrapper k_largest(node_unit.Index(), node_unit.Name(), QNN_OP_TOP_K_PARAM_LARGEST, qnn_largest_k);
147+
std::string k_largest_name = k_largest.GetParamTensorName();
148+
qnn_model_wrapper.AddParamWrapper(std::move(k_largest));
149+
param_tensor_names.push_back(k_largest_name);
150+
150151
// HTP only supports TopK at the last axis, and thus check whether extra Transpose is required.
151152
TensorInfo input_info = {};
152153
ORT_RETURN_IF_ERROR(qnn_model_wrapper.GetTensorInfo(node_unit.Inputs()[0], input_info));

onnxruntime/test/providers/qnn/topk_op_test.cc

Lines changed: 86 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,19 @@ static void RunTopKTestOnCPU(const TestInputDef<DataType>& input_def,
4141
const TestInputDef<int64_t>& k_def,
4242
const std::vector<ONNX_NAMESPACE::AttributeProto>& attrs,
4343
ExpectedEPNodeAssignment expected_ep_assignment,
44-
int opset = 19) {
44+
int opset = 19,
45+
bool verify_outputs = true) {
4546
ProviderOptions provider_options;
4647

4748
provider_options["backend_type"] = "cpu";
4849

4950
RunQnnModelTest(BuildTopKTestCase<DataType>(input_def, k_def, attrs),
5051
provider_options,
5152
opset,
52-
expected_ep_assignment);
53+
expected_ep_assignment,
54+
/*fp32_abs_err*/ 1e-5f,
55+
/*log_severity*/ logging::Severity::kERROR,
56+
/*verify_outputs*/ verify_outputs);
5357
}
5458

5559
//
@@ -72,12 +76,13 @@ TEST_F(QnnCPUBackendTests, TopK_NonLastAxis) {
7276
ExpectedEPNodeAssignment::All);
7377
}
7478

75-
// Test that TopK that returns the top k minimum values is not supported by QNN EP.
76-
TEST_F(QnnCPUBackendTests, TopK_MinValues_Unsupported) {
79+
// Test that TopK with an axis attribute that is not the last dimension. Set largest to 0.
80+
TEST_F(QnnCPUBackendTests, TopK_NonLastAxis_Largest_0) {
7781
RunTopKTestOnCPU<float>(TestInputDef<float>({1, 3, 4, 4}, false, GetFloatDataInRange(-10.0f, 10.0f, 48)),
7882
TestInputDef<int64_t>({1}, true /* is_initializer */, {2}),
79-
{utils::MakeAttribute("largest", static_cast<int64_t>(0))},
80-
ExpectedEPNodeAssignment::None); // Should not be assigned to QNN EP.
83+
{utils::MakeAttribute("axis", static_cast<int64_t>(1)),
84+
utils::MakeAttribute("largest", static_cast<int64_t>(0))},
85+
ExpectedEPNodeAssignment::All);
8186
}
8287

8388
// Test TopK on CPU backend: top 2 largest floats from last axis
@@ -88,6 +93,25 @@ TEST_F(QnnCPUBackendTests, TopK_LargestFloats_LastAxis) {
8893
ExpectedEPNodeAssignment::All);
8994
}
9095

96+
// Test TopK on CPU backend: top 2 largest floats from last axis. Largest to 0.
97+
TEST_F(QnnCPUBackendTests, TopK_LargestFloats_LastAxis_Largest_0) {
98+
RunTopKTestOnCPU<float>(TestInputDef<float>({1, 3, 4, 4}, false, GetFloatDataInRange(-10.0f, 10.0f, 48)),
99+
TestInputDef<int64_t>({1}, true /* is_initializer */, {2}),
100+
{utils::MakeAttribute("largest", static_cast<int64_t>(0))}, // Attributes
101+
ExpectedEPNodeAssignment::All);
102+
}
103+
104+
// Test TopK on CPU backend: top 2 largest floats from last axis. Set largest to 0. Set sorted to false.
105+
TEST_F(QnnCPUBackendTests, TopK_LargestFloats_LastAxis_Largest_0_Sorted) {
106+
RunTopKTestOnCPU<float>(TestInputDef<float>({1, 3, 4, 4}, false, GetFloatDataInRange(-10.0f, 10.0f, 48)),
107+
TestInputDef<int64_t>({1}, true /* is_initializer */, {2}),
108+
{utils::MakeAttribute("largest", static_cast<int64_t>(0)),
109+
utils::MakeAttribute("sorted", static_cast<int64_t>(0))}, // Attributes
110+
ExpectedEPNodeAssignment::All,
111+
/*opset*/ 19,
112+
/*verify_outputs*/ false); // Disable verify output. Unsorted doesn't guarantee output sequence
113+
}
114+
91115
// Test TopK on CPU backend: top 2 largest int32s from last axis
92116
TEST_F(QnnCPUBackendTests, TopK_LargestInt32s_LastAxis) {
93117
std::vector<int32_t> input_data = {-6, -5, -4, -3, -2, 0, 1, 2, 3, 4, 5, 6};
@@ -97,6 +121,27 @@ TEST_F(QnnCPUBackendTests, TopK_LargestInt32s_LastAxis) {
97121
ExpectedEPNodeAssignment::All);
98122
}
99123

124+
// Test TopK on CPU backend: top 2 largest int32s from last axis. Set largest to 0.
125+
TEST_F(QnnCPUBackendTests, TopK_LargestInt32s_LastAxis_Largest_0) {
126+
std::vector<int32_t> input_data = {-6, -5, -4, -3, -2, 0, 1, 2, 3, 4, 5, 6};
127+
RunTopKTestOnCPU<int32_t>(TestInputDef<int32_t>({1, 2, 2, 3}, false, input_data),
128+
TestInputDef<int64_t>({1}, true /* is_initializer */, {2}),
129+
{utils::MakeAttribute("largest", static_cast<int64_t>(0))}, // Attributes
130+
ExpectedEPNodeAssignment::All);
131+
}
132+
133+
// Test TopK on CPU backend: top 2 largest int32s from last axis. Set largest to 0. Set sorted to false.
134+
TEST_F(QnnCPUBackendTests, TopK_LargestInt32s_LastAxis_Largest_0_Sorted) {
135+
std::vector<int32_t> input_data = {-6, -5, -4, -3, -2, 0, 1, 2, 3, 4, 5, 6};
136+
RunTopKTestOnCPU<int32_t>(TestInputDef<int32_t>({1, 2, 2, 3}, false, input_data),
137+
TestInputDef<int64_t>({1}, true /* is_initializer */, {2}),
138+
{utils::MakeAttribute("largest", static_cast<int64_t>(0)),
139+
utils::MakeAttribute("sorted", static_cast<int64_t>(0))}, // Attributes
140+
ExpectedEPNodeAssignment::All,
141+
/*opset*/ 19,
142+
/*verify_outputs*/ false); // Disable verify output. Unsorted doesn't guarantee output sequence
143+
}
144+
100145
#if defined(__aarch64__) || defined(_M_ARM64) || defined(__linux__)
101146
//
102147
// HTP tests:
@@ -166,6 +211,14 @@ TEST_F(QnnHTPBackendTests, TopK_LargestFloats_U8_LastAxis) {
166211
ExpectedEPNodeAssignment::All);
167212
}
168213

214+
// Test 8-bit QDQ TopK on HTP backend: top 2 largest floats from last axis. Set largest to 0.
215+
TEST_F(QnnHTPBackendTests, TopK_LargestFloats_U8_LastAxis_Largest_0) {
216+
RunQDQTopKTestOnHTP<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, GetFloatDataInRange(-10.0f, 10.0f, 48)),
217+
TestInputDef<int64_t>({1}, true /* is_initializer */, {2}),
218+
{utils::MakeAttribute("largest", static_cast<int64_t>(0))}, // Attributes
219+
ExpectedEPNodeAssignment::All);
220+
}
221+
169222
// Test 8-bit QDQ TopK on HTP backend: non-last axis
170223
TEST_F(QnnHTPBackendTests, TopK_U8_NonLastAxis) {
171224
RunQDQTopKTestOnHTP<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, GetFloatDataInRange(-10.0f, 10.0f, 48)),
@@ -174,6 +227,15 @@ TEST_F(QnnHTPBackendTests, TopK_U8_NonLastAxis) {
174227
ExpectedEPNodeAssignment::All);
175228
}
176229

230+
// Test 8-bit QDQ TopK on HTP backend: non-last axis. Set largest to 0.
231+
TEST_F(QnnHTPBackendTests, TopK_U8_NonLastAxis_Largest_0) {
232+
RunQDQTopKTestOnHTP<uint8_t>(TestInputDef<float>({1, 3, 4, 4}, false, GetFloatDataInRange(-10.0f, 10.0f, 48)),
233+
TestInputDef<int64_t>({1}, true /* is_initializer */, {2}),
234+
{utils::MakeAttribute("axis", static_cast<int64_t>(1)),
235+
utils::MakeAttribute("largest", static_cast<int64_t>(0))}, // Attributes
236+
ExpectedEPNodeAssignment::All);
237+
}
238+
177239
// Test 16-bit QDQ TopK on HTP backend: top 2 largest floats from last axis
178240
TEST_F(QnnHTPBackendTests, TopK_LargestFloats_U16_LastAxis) {
179241
RunQDQTopKTestOnHTP<uint16_t>(TestInputDef<float>({1, 3, 4, 4}, false, GetFloatDataInRange(-20.0f, 20.0f, 48)),
@@ -183,6 +245,15 @@ TEST_F(QnnHTPBackendTests, TopK_LargestFloats_U16_LastAxis) {
183245
21); // opset
184246
}
185247

248+
// Test 16-bit QDQ TopK on HTP backend: top 2 largest floats from last axis. Set largest to 0.
249+
TEST_F(QnnHTPBackendTests, TopK_LargestFloats_U16_LastAxis_Largest_0) {
250+
RunQDQTopKTestOnHTP<uint16_t>(TestInputDef<float>({1, 3, 4, 4}, false, GetFloatDataInRange(-20.0f, 20.0f, 48)),
251+
TestInputDef<int64_t>({1}, true /* is_initializer */, {2}),
252+
{utils::MakeAttribute("largest", static_cast<int64_t>(0))}, // Attributes
253+
ExpectedEPNodeAssignment::All,
254+
21); // opset
255+
}
256+
186257
// Test 16-bit QDQ TopK on HTP backend: non-last axis
187258
TEST_F(QnnHTPBackendTests, TopK_U16_NonLastAxis) {
188259
RunQDQTopKTestOnHTP<uint16_t>(TestInputDef<float>({1, 3, 4, 4}, false, GetFloatDataInRange(-20.0f, 20.0f, 48)),
@@ -192,6 +263,15 @@ TEST_F(QnnHTPBackendTests, TopK_U16_NonLastAxis) {
192263
21); // opset
193264
}
194265

266+
// Test 16-bit QDQ TopK on HTP backend: non-last axis. Set largest to 0.
267+
TEST_F(QnnHTPBackendTests, TopK_U16_NonLastAxis_Largest_0) {
268+
RunQDQTopKTestOnHTP<uint16_t>(TestInputDef<float>({1, 3, 4, 4}, false, GetFloatDataInRange(-20.0f, 20.0f, 48)),
269+
TestInputDef<int64_t>({1}, true /* is_initializer */, {2}),
270+
{utils::MakeAttribute("axis", static_cast<int64_t>(1)),
271+
utils::MakeAttribute("largest", static_cast<int64_t>(0))}, // Attributes
272+
ExpectedEPNodeAssignment::All,
273+
21); // opset
274+
}
195275
#endif // defined(__aarch64__) || defined(_M_ARM64) || defined(__linux__)
196276
} // namespace test
197277
} // namespace onnxruntime

0 commit comments

Comments
 (0)