1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <algorithm>
18 #include <utility>
19 #include <vector>
20
21 #include "fuzzing/operation_signatures/OperationSignatureUtils.h"
22
23 namespace android {
24 namespace nn {
25 namespace fuzzing_test {
26
embeddingLookupConstructor(TestOperandType,uint32_t rank,RandomOperation * op)27 static void embeddingLookupConstructor(TestOperandType, uint32_t rank, RandomOperation* op) {
28 setFreeDimensions(op->inputs[0], /*rank=*/1);
29 setFreeDimensions(op->inputs[1], rank);
30 op->outputs[0]->dimensions.resize(rank);
31 op->outputs[0]->dimensions[0] = op->inputs[0]->dimensions[0];
32 for (uint32_t i = 1; i < rank; i++) {
33 op->outputs[0]->dimensions[i] = op->inputs[1]->dimensions[i];
34 }
35 setSameQuantization(op->outputs[0], op->inputs[1]);
36 }
37
embeddingLookupFinalizer(RandomOperation * op)38 static void embeddingLookupFinalizer(RandomOperation* op) {
39 uint32_t dimValue = op->inputs[1]->dimensions[0].getValue();
40 uint32_t numElements = op->inputs[0]->getNumberOfElements();
41 for (uint32_t i = 0; i < numElements; i++) {
42 // The index values must be in the range of [0, input1_dim0).
43 op->inputs[0]->value<int32_t>(i) = getUniform<int32_t>(0, dimValue - 1);
44 }
45 }
46
47 #define DEFINE_EMBEDDING_LOOKUP_SIGNATURE(ver, ...) \
48 DEFINE_OPERATION_SIGNATURE(EMBEDDING_LOOKUP_##ver){ \
49 .opType = TestOperationType::EMBEDDING_LOOKUP, \
50 .supportedDataTypes = {__VA_ARGS__}, \
51 .supportedRanks = {2, 3, 4}, \
52 .version = TestHalVersion::ver, \
53 .inputs = {PARAMETER_NONE(TestOperandType::TENSOR_INT32), INPUT_DEFAULT}, \
54 .outputs = {OUTPUT_DEFAULT}, \
55 .constructor = embeddingLookupConstructor, \
56 .finalizer = embeddingLookupFinalizer};
57
58 DEFINE_EMBEDDING_LOOKUP_SIGNATURE(V1_0, TestOperandType::TENSOR_FLOAT32);
59 DEFINE_EMBEDDING_LOOKUP_SIGNATURE(V1_2, TestOperandType::TENSOR_INT32,
60 TestOperandType::TENSOR_QUANT8_ASYMM);
61 DEFINE_EMBEDDING_LOOKUP_SIGNATURE(V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED,
62 TestOperandType::TENSOR_FLOAT16);
63
hashtableLookupConstructor(TestOperandType,uint32_t rank,RandomOperation * op)64 static void hashtableLookupConstructor(TestOperandType, uint32_t rank, RandomOperation* op) {
65 op->inputs[0]->dimensions = {RandomVariableType::FREE};
66 op->inputs[1]->dimensions = {RandomVariableType::FREE};
67 op->inputs[2]->dimensions.resize(rank);
68 op->outputs[0]->dimensions.resize(rank);
69 op->inputs[2]->dimensions[0] = op->inputs[1]->dimensions[0];
70 op->outputs[0]->dimensions[0] = op->inputs[0]->dimensions[0];
71 for (uint32_t i = 1; i < rank; i++) {
72 op->inputs[2]->dimensions[i] = RandomVariableType::FREE;
73 op->outputs[0]->dimensions[i] = op->inputs[2]->dimensions[i];
74 }
75 setSameQuantization(op->outputs[0], op->inputs[2]);
76 op->outputs[1]->dimensions = {op->inputs[0]->dimensions[0]};
77 }
78
hashtableLookupFinalizer(RandomOperation * op)79 static void hashtableLookupFinalizer(RandomOperation* op) {
80 // Generate values for keys. The keys tensor must be sorted in ascending order.
81 uint32_t n = op->inputs[1]->getNumberOfElements();
82 int32_t val = 0;
83 for (uint32_t i = 0; i < n; i++) {
84 op->inputs[1]->value<int32_t>(i) = val;
85 val += getUniform<int32_t>(1, 2);
86 }
87 // Generate values for lookups.
88 uint32_t k = op->inputs[0]->getNumberOfElements();
89 for (uint32_t i = 0; i < k; i++) {
90 op->inputs[0]->value<int32_t>(i) = getUniform<int32_t>(0, val);
91 }
92 }
93
94 // The hits tensor in HASHTABLE_LOOKUP.
95 static const OperandSignature hitsTensor_HASHTABLE_LOOKUP = {
96 .type = RandomOperandType::OUTPUT,
__anonbfbd3ebb0102() 97 .constructor = [](TestOperandType, uint32_t, RandomOperand* op) {
98 op->dataType = TestOperandType::TENSOR_QUANT8_ASYMM;
99 op->scale = 1.0f;
100 op->zeroPoint = 0;
101 }};
102
DEFINE_OPERATION_SIGNATURE(HASHTABLE_LOOKUP_V1_0)103 DEFINE_OPERATION_SIGNATURE(HASHTABLE_LOOKUP_V1_0){
104 .opType = TestOperationType::HASHTABLE_LOOKUP,
105 .supportedDataTypes = {TestOperandType::TENSOR_FLOAT32, TestOperandType::TENSOR_INT32,
106 TestOperandType::TENSOR_QUANT8_ASYMM},
107 .supportedRanks = {2, 3, 4},
108 .version = TestHalVersion::V1_0,
109 .inputs = {PARAMETER_NONE(TestOperandType::TENSOR_INT32),
110 PARAMETER_NONE(TestOperandType::TENSOR_INT32), INPUT_DEFAULT},
111 .outputs = {OUTPUT_DEFAULT, hitsTensor_HASHTABLE_LOOKUP},
112 .constructor = hashtableLookupConstructor,
113 .finalizer = hashtableLookupFinalizer};
114
gatherConstructor(TestOperandType,uint32_t rank,RandomOperation * op)115 static void gatherConstructor(TestOperandType, uint32_t rank, RandomOperation* op) {
116 // Generate value for "axis" scalar.
117 int32_t axis = getUniform<int32_t>(-rank, rank - 1);
118 op->inputs[1]->setScalarValue<int32_t>(axis);
119 if (axis < 0) axis += rank;
120
121 // Set dimensions for input and indices tensor.
122 uint32_t indRank = getUniform<uint32_t>(1, 5);
123 setFreeDimensions(op->inputs[0], rank);
124 setFreeDimensions(op->inputs[2], indRank);
125
126 for (uint32_t i = 0; i < static_cast<uint32_t>(axis); i++) {
127 op->outputs[0]->dimensions.push_back(op->inputs[0]->dimensions[i]);
128 }
129 for (uint32_t i = 0; i < indRank; i++) {
130 op->outputs[0]->dimensions.push_back(op->inputs[2]->dimensions[i]);
131 }
132 for (uint32_t i = axis + 1; i < rank; i++) {
133 op->outputs[0]->dimensions.push_back(op->inputs[0]->dimensions[i]);
134 }
135 setSameQuantization(op->outputs[0], op->inputs[0]);
136 }
137
gatherFinalizer(RandomOperation * op)138 static void gatherFinalizer(RandomOperation* op) {
139 int32_t axis = op->inputs[1]->value<int32_t>();
140 if (axis < 0) axis += op->inputs[0]->dimensions.size();
141 uint32_t dimValue = op->inputs[0]->dimensions[axis].getValue();
142 uint32_t numElements = op->inputs[2]->getNumberOfElements();
143 for (uint32_t i = 0; i < numElements; i++) {
144 // The index values must be in the range of [0, dimValue).
145 op->inputs[2]->value<int32_t>(i) = getUniform<int32_t>(0, dimValue - 1);
146 }
147 }
148
149 #define DEFINE_GATHER_SIGNATURE(ver, ...) \
150 DEFINE_OPERATION_SIGNATURE(GATHER_##ver){ \
151 .opType = TestOperationType::GATHER, \
152 .supportedDataTypes = {__VA_ARGS__}, \
153 .supportedRanks = {1, 2, 3, 4, 5}, \
154 .version = TestHalVersion::ver, \
155 .inputs = {INPUT_DEFAULT, PARAMETER_NONE(TestOperandType::INT32), \
156 PARAMETER_NONE(TestOperandType::TENSOR_INT32)}, \
157 .outputs = {OUTPUT_DEFAULT}, \
158 .constructor = gatherConstructor, \
159 .finalizer = gatherFinalizer};
160
161 DEFINE_GATHER_SIGNATURE(V1_2, TestOperandType::TENSOR_FLOAT32, TestOperandType::TENSOR_FLOAT16,
162 TestOperandType::TENSOR_INT32, TestOperandType::TENSOR_QUANT8_ASYMM);
163 DEFINE_GATHER_SIGNATURE(V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED);
164
selectConstructor(TestOperandType,uint32_t rank,RandomOperation * op)165 static void selectConstructor(TestOperandType, uint32_t rank, RandomOperation* op) {
166 setFreeDimensions(op->inputs[0], rank);
167 op->inputs[1]->dimensions = op->inputs[0]->dimensions;
168 op->inputs[2]->dimensions = op->inputs[0]->dimensions;
169 op->outputs[0]->dimensions = op->inputs[0]->dimensions;
170 setSameQuantization(op->inputs[2], op->inputs[1]);
171 setSameQuantization(op->outputs[0], op->inputs[1]);
172 }
173
174 #define DEFINE_SELECT_SIGNATURE(ver, ...) \
175 DEFINE_OPERATION_SIGNATURE(SELECT_##ver){ \
176 .opType = TestOperationType::SELECT, \
177 .supportedDataTypes = {__VA_ARGS__}, \
178 .supportedRanks = {1, 2, 3, 4}, \
179 .version = TestHalVersion::ver, \
180 .inputs = {INPUT_TYPED(TestOperandType::TENSOR_BOOL8), INPUT_DEFAULT, INPUT_DEFAULT}, \
181 .outputs = {OUTPUT_DEFAULT}, \
182 .constructor = selectConstructor};
183
184 DEFINE_SELECT_SIGNATURE(V1_2, TestOperandType::TENSOR_FLOAT32, TestOperandType::TENSOR_FLOAT16,
185 TestOperandType::TENSOR_INT32, TestOperandType::TENSOR_QUANT8_ASYMM);
186 DEFINE_SELECT_SIGNATURE(V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED);
187
topKConstructor(TestOperandType,uint32_t rank,RandomOperation * op)188 static void topKConstructor(TestOperandType, uint32_t rank, RandomOperation* op) {
189 setFreeDimensions(op->inputs[0], rank);
190 op->outputs[0]->dimensions.resize(rank);
191 op->outputs[1]->dimensions.resize(rank);
192 for (uint32_t i = 0; i < rank - 1; i++) {
193 op->outputs[0]->dimensions[i] = op->inputs[0]->dimensions[i];
194 op->outputs[1]->dimensions[i] = op->inputs[0]->dimensions[i];
195 }
196
197 // K must be in the range of [1, depth].
198 auto k = op->inputs[1]->value<RandomVariable>();
199 k.setRange(1, kInvalidValue);
200 op->inputs[0]->dimensions.back().setGreaterEqual(k);
201
202 op->outputs[0]->dimensions.back() = k;
203 op->outputs[1]->dimensions.back() = k;
204 setSameQuantization(op->outputs[0], op->inputs[0]);
205
206 // As the sorting is not required to be stable, we should not check the second output (indices).
207 op->outputs[1]->doNotCheckAccuracy = true;
208 op->outputs[1]->doNotConnect = true;
209 }
210
211 #define DEFINE_TOPK_SIGNATURE(ver, ...) \
212 DEFINE_OPERATION_SIGNATURE(TOPK_V2_##ver){ \
213 .opType = TestOperationType::TOPK_V2, \
214 .supportedDataTypes = {__VA_ARGS__}, \
215 .supportedRanks = {1, 2, 3, 4}, \
216 .version = TestHalVersion::ver, \
217 .inputs = {INPUT_DEFAULT, RANDOM_INT_FREE}, \
218 .outputs = {OUTPUT_DEFAULT, OUTPUT_TYPED(TestOperandType::TENSOR_INT32)}, \
219 .constructor = topKConstructor};
220
221 DEFINE_TOPK_SIGNATURE(V1_2, TestOperandType::TENSOR_FLOAT32, TestOperandType::TENSOR_FLOAT16,
222 TestOperandType::TENSOR_INT32, TestOperandType::TENSOR_QUANT8_ASYMM);
223 DEFINE_TOPK_SIGNATURE(V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED);
224
sliceConstructor(TestOperandType,uint32_t rank,RandomOperation * op)225 static void sliceConstructor(TestOperandType, uint32_t rank, RandomOperation* op) {
226 op->inputs[1]->dimensions = {rank};
227 op->inputs[2]->dimensions = {rank};
228 setFreeDimensions(op->inputs[0], rank);
229 setFreeDimensions(op->outputs[0], rank);
230 // The axis size of output must be less than or equal to input.
231 for (uint32_t i = 0; i < rank; i++) {
232 op->inputs[0]->dimensions[i].setGreaterEqual(op->outputs[0]->dimensions[i]);
233 }
234 setSameQuantization(op->outputs[0], op->inputs[0]);
235 }
236
sliceFinalizer(RandomOperation * op)237 static void sliceFinalizer(RandomOperation* op) {
238 uint32_t rank = op->inputs[0]->dimensions.size();
239 int32_t* begin = reinterpret_cast<int32_t*>(op->inputs[1]->buffer.data());
240 int32_t* size = reinterpret_cast<int32_t*>(op->inputs[2]->buffer.data());
241
242 NN_FUZZER_CHECK(op->inputs[1]->buffer.size() >= rank)
243 << "input[1] buffer size " << op->inputs[1]->buffer.size() << " is smaller than rank "
244 << rank;
245
246 NN_FUZZER_CHECK(op->inputs[2]->buffer.size() >= rank)
247 << "input[1] buffer size " << op->inputs[2]->buffer.size() << " is smaller than rank "
248 << rank;
249
250 for (uint32_t i = 0; i < rank; i++) {
251 int32_t inputSize = op->inputs[0]->dimensions[i].getValue();
252 int32_t outputSize = op->outputs[0]->dimensions[i].getValue();
253 // Randomly choose a valid begin index for each axis.
254 begin[i] = getUniform<int32_t>(0, inputSize - outputSize);
255 size[i] = outputSize;
256 }
257 }
258
259 #define DEFINE_SLICE_SIGNATURE(ver, ...) \
260 DEFINE_OPERATION_SIGNATURE(SLICE_##ver){ \
261 .opType = TestOperationType::SLICE, \
262 .supportedDataTypes = {__VA_ARGS__}, \
263 .supportedRanks = {1, 2, 3, 4}, \
264 .version = TestHalVersion::ver, \
265 .inputs = {INPUT_DEFAULT, PARAMETER_NONE(TestOperandType::TENSOR_INT32), \
266 PARAMETER_NONE(TestOperandType::TENSOR_INT32)}, \
267 .outputs = {OUTPUT_DEFAULT}, \
268 .constructor = sliceConstructor, \
269 .finalizer = sliceFinalizer};
270
271 DEFINE_SLICE_SIGNATURE(V1_2, TestOperandType::TENSOR_FLOAT32, TestOperandType::TENSOR_FLOAT16,
272 TestOperandType::TENSOR_INT32, TestOperandType::TENSOR_QUANT8_ASYMM);
273 DEFINE_SLICE_SIGNATURE(V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED);
274
convertToBitMask(const std::vector<bool> & flags)275 inline int32_t convertToBitMask(const std::vector<bool>& flags) {
276 int32_t mask = 0, bit = 1;
277 for (bool flag : flags) {
278 if (flag) mask |= bit;
279 bit <<= 1;
280 }
281 return mask;
282 }
283
stridedSliceConstructor(TestOperandType,uint32_t rank,RandomOperation * op)284 static void stridedSliceConstructor(TestOperandType, uint32_t rank, RandomOperation* op) {
285 op->inputs[1]->dimensions = {rank};
286 op->inputs[2]->dimensions = {rank};
287 op->inputs[3]->dimensions = {rank};
288 op->inputs[3]->resizeBuffer<int32_t>(rank);
289 setFreeDimensions(op->inputs[0], rank);
290 std::vector<bool> shrinkMask(rank, false);
291 for (uint32_t i = 0; i < rank; i++) {
292 shrinkMask[i] = getBernoulli(0.2f);
293 int32_t stride = getUniform<int32_t>(1, 3);
294 op->inputs[3]->value<int32_t>(i) = stride;
295 if (!shrinkMask[i]) {
296 op->outputs[0]->dimensions.push_back(RandomVariableType::FREE);
297 auto maxOut = (op->inputs[0]->dimensions[i] + (stride - 1)) / stride;
298 maxOut.setGreaterEqual(op->outputs[0]->dimensions.back());
299 }
300 }
301 setSameQuantization(op->outputs[0], op->inputs[0]);
302 op->inputs[6]->setScalarValue<int32_t>(convertToBitMask(shrinkMask));
303 }
304
stridedSliceFinalizer(RandomOperation * op)305 static void stridedSliceFinalizer(RandomOperation* op) {
306 uint32_t rank = op->inputs[0]->dimensions.size();
307 int32_t* begin = reinterpret_cast<int32_t*>(op->inputs[1]->buffer.data());
308 int32_t* end = reinterpret_cast<int32_t*>(op->inputs[2]->buffer.data());
309 std::vector<bool> beginMask(rank, false), endMask(rank, false);
310
311 NN_FUZZER_CHECK(op->inputs[1]->buffer.size() >= rank)
312 << "input[1] buffer size " << op->inputs[1]->buffer.size() << " is smaller than rank "
313 << rank;
314
315 NN_FUZZER_CHECK(op->inputs[2]->buffer.size() >= rank)
316 << "input[1] buffer size " << op->inputs[2]->buffer.size() << " is smaller than rank "
317 << rank;
318
319 int32_t shrinkMask = op->inputs[6]->value<int32_t>();
320 for (uint32_t i = 0, o = 0; i < rank; i++) {
321 int32_t inputSize = op->inputs[0]->dimensions[i].getValue();
322 int32_t stride = op->inputs[3]->value<int32_t>(i);
323 bool shrink = shrinkMask & (1 << i);
324 if (!shrink) {
325 int32_t outputSize = op->outputs[0]->dimensions[o++].getValue();
326 int32_t maxStart = inputSize - (outputSize - 1) * stride - 1;
327 begin[i] = getUniform<int32_t>(0, maxStart);
328
329 int32_t minEnd = begin[i] + (outputSize - 1) * stride + 1;
330 int32_t maxEnd = std::min(begin[i] + outputSize * stride, inputSize);
331 end[i] = getUniform<int32_t>(minEnd, maxEnd);
332
333 // Switch to masked begin/end.
334 beginMask[i] = (begin[i] == 0 && getBernoulli(0.2f));
335 endMask[i] = (end[i] == 0 && getBernoulli(0.2f));
336
337 // When begin or end mask is set, begin[i] or end[i] is ignored and can have any
338 // arbitrary value.
339 if (beginMask[i]) begin[i] = getUniform<int32_t>(-inputSize, inputSize - 1);
340 if (endMask[i]) end[i] = getUniform<int32_t>(-inputSize, inputSize - 1);
341
342 // Switch to negative stride.
343 if (getBernoulli(0.2f)) {
344 op->inputs[3]->value<int32_t>(i) = -stride;
345 std::swap(begin[i], end[i]);
346 std::swap(beginMask[i], endMask[i]);
347 begin[i]--;
348 end[i]--;
349 // end = -1 will be interpreted to inputSize - 1 if not setting endMask.
350 if (end[i] < 0) endMask[i] = true;
351 }
352 } else {
353 // When shrink mask is set, the begin and end must define a slice of size 1, e.g.
354 // begin[i] = x, end[i] = x + 1.
355 begin[i] = getUniform<int32_t>(0, inputSize - 1);
356 end[i] = begin[i] + 1;
357 }
358 }
359 op->inputs[4]->setScalarValue<int32_t>(convertToBitMask(beginMask));
360 op->inputs[5]->setScalarValue<int32_t>(convertToBitMask(endMask));
361 }
362
DEFINE_OPERATION_SIGNATURE(STRIDED_SLICE_V1_1)363 DEFINE_OPERATION_SIGNATURE(STRIDED_SLICE_V1_1){
364 .opType = TestOperationType::STRIDED_SLICE,
365 .supportedDataTypes = {TestOperandType::TENSOR_FLOAT32,
366 TestOperandType::TENSOR_QUANT8_ASYMM},
367 .supportedRanks = {1, 2, 3, 4},
368 .version = TestHalVersion::V1_1,
369 .inputs = {INPUT_DEFAULT, PARAMETER_NONE(TestOperandType::TENSOR_INT32),
370 PARAMETER_NONE(TestOperandType::TENSOR_INT32),
371 PARAMETER_NONE(TestOperandType::TENSOR_INT32),
372 PARAMETER_CHOICE(TestOperandType::INT32, 0),
373 PARAMETER_CHOICE(TestOperandType::INT32, 0),
374 PARAMETER_CHOICE(TestOperandType::INT32, 0)},
375 .outputs = {OUTPUT_DEFAULT},
376 .constructor = stridedSliceConstructor,
377 .finalizer = stridedSliceFinalizer};
378
DEFINE_OPERATION_SIGNATURE(STRIDED_SLICE_V1_2)379 DEFINE_OPERATION_SIGNATURE(STRIDED_SLICE_V1_2){
380 .opType = TestOperationType::STRIDED_SLICE,
381 .supportedDataTypes = {TestOperandType::TENSOR_FLOAT16},
382 .supportedRanks = {1, 2, 3, 4},
383 .version = TestHalVersion::V1_2,
384 .inputs = {INPUT_DEFAULT, PARAMETER_NONE(TestOperandType::TENSOR_INT32),
385 PARAMETER_NONE(TestOperandType::TENSOR_INT32),
386 PARAMETER_NONE(TestOperandType::TENSOR_INT32),
387 PARAMETER_NONE(TestOperandType::INT32), PARAMETER_NONE(TestOperandType::INT32),
388 PARAMETER_NONE(TestOperandType::INT32)},
389 .outputs = {OUTPUT_DEFAULT},
390 .constructor = stridedSliceConstructor,
391 .finalizer = stridedSliceFinalizer};
392
DEFINE_OPERATION_SIGNATURE(STRIDED_SLICE_V1_3)393 DEFINE_OPERATION_SIGNATURE(STRIDED_SLICE_V1_3){
394 .opType = TestOperationType::STRIDED_SLICE,
395 .supportedDataTypes = {TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED},
396 .supportedRanks = {1, 2, 3, 4},
397 .version = TestHalVersion::V1_3,
398 .inputs = {INPUT_DEFAULT, PARAMETER_NONE(TestOperandType::TENSOR_INT32),
399 PARAMETER_NONE(TestOperandType::TENSOR_INT32),
400 PARAMETER_NONE(TestOperandType::TENSOR_INT32),
401 PARAMETER_NONE(TestOperandType::INT32), PARAMETER_NONE(TestOperandType::INT32),
402 PARAMETER_NONE(TestOperandType::INT32)},
403 .outputs = {OUTPUT_DEFAULT},
404 .constructor = stridedSliceConstructor,
405 .finalizer = stridedSliceFinalizer};
406
407 } // namespace fuzzing_test
408 } // namespace nn
409 } // namespace android
410