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 
19 #include "fuzzing/operation_signatures/OperationSignatureUtils.h"
20 
21 namespace android {
22 namespace nn {
23 namespace fuzzing_test {
24 
broadcastOpConstructor(TestOperandType dataType,uint32_t rank,RandomOperation * op)25 static void broadcastOpConstructor(TestOperandType dataType, uint32_t rank, RandomOperation* op) {
26     const uint32_t rank2 = getUniform(1u, rank), rankDiff = rank - rank2;
27     op->inputs[0]->dimensions.resize(rank);
28     op->inputs[1]->dimensions.resize(rank2);
29     op->outputs[0]->dimensions.resize(rank);
30     for (uint32_t i = 0; i < rank; i++) {
31         op->outputs[0]->dimensions[i] = RandomVariableType::FREE;
32         if (i < rankDiff) {
33             op->inputs[0]->dimensions[i] = op->outputs[0]->dimensions[i];
34         } else {
35             if (getBernoulli(0.5f)) {
36                 // No broadcast on this dimension.
37                 op->inputs[0]->dimensions[i] = op->outputs[0]->dimensions[i];
38                 op->inputs[1]->dimensions[i - rankDiff] = op->outputs[0]->dimensions[i];
39             } else if (getBernoulli(0.5f)) {
40                 // input0 broadcast on this dimension.
41                 op->inputs[0]->dimensions[i] = 1;
42                 op->inputs[1]->dimensions[i - rankDiff] = op->outputs[0]->dimensions[i];
43             } else {
44                 // input1 broadcast on this dimension.
45                 op->inputs[0]->dimensions[i] = op->outputs[0]->dimensions[i];
46                 op->inputs[1]->dimensions[i - rankDiff] = 1;
47             }
48         }
49     }
50     if (getBernoulli(0.5f)) {
51         // Swap the dimensions to test the case that inpuy 1 has a larger rank than input0.
52         op->inputs[0]->dimensions.swap(op->inputs[1]->dimensions);
53     }
54 
55     // MUL requires output.scale > input0.scale * input1.scale.
56     if (isQuantizedType(dataType) && op->opType == TestOperationType::MUL) {
57         float minScale = op->inputs[0]->scale * op->inputs[1]->scale;
58         op->outputs[0]->scale = getUniform(minScale, minScale * 5);
59     }
60 
61     // DIV and POW may produce Inf output values. We should not connect this output tensor to the
62     // input of another operation.
63     if (op->opType == TestOperationType::DIV || op->opType == TestOperationType::POW) {
64         op->outputs[0]->doNotConnect = true;
65     }
66 
67     // For ADD/MUL/SUB/DIV with TENSOR_INT32 tensors, the activation must be "NONE".
68     if ((op->opType == TestOperationType::ADD || op->opType == TestOperationType::MUL ||
69          op->opType == TestOperationType::SUB || op->opType == TestOperationType::DIV) &&
70         dataType == TestOperandType::TENSOR_INT32) {
71         op->inputs[2]->setScalarValue(0);
72     }
73 
74     if (op->opType == TestOperationType::DIV) {
75         op->inputs[1]->valueProperties = RandomOperand::NON_ZERO;
76     }
77 
78     if (op->opType == TestOperationType::POW) {
79         op->inputs[0]->valueProperties = RandomOperand::NON_NEGATIVE;
80     }
81 }
82 
83 // For broadcast operations with fused activation.
84 #define DEFINE_BROADCAST_WITH_ACT_SIGNATURE(op, ver, ...)                     \
85     DEFINE_OPERATION_SIGNATURE(op##_##ver){                                   \
86             .opType = TestOperationType::op,                                  \
87             .supportedDataTypes = {__VA_ARGS__},                              \
88             .supportedRanks = {1, 2, 3, 4},                                   \
89             .version = TestHalVersion::ver,                                   \
90             .inputs = {INPUT_DEFAULT, INPUT_DEFAULT,                          \
91                        PARAMETER_CHOICE(TestOperandType::INT32, 0, 1, 2, 3)}, \
92             .outputs = {OUTPUT_DEFAULT},                                      \
93             .constructor = broadcastOpConstructor};
94 
95 // Arithmetic with activation.
96 DEFINE_BROADCAST_WITH_ACT_SIGNATURE(ADD, V1_0, TestOperandType::TENSOR_FLOAT32,
97                                     TestOperandType::TENSOR_QUANT8_ASYMM);
98 DEFINE_BROADCAST_WITH_ACT_SIGNATURE(MUL, V1_0, TestOperandType::TENSOR_FLOAT32,
99                                     TestOperandType::TENSOR_QUANT8_ASYMM);
100 DEFINE_BROADCAST_WITH_ACT_SIGNATURE(SUB, V1_1, TestOperandType::TENSOR_FLOAT32);
101 DEFINE_BROADCAST_WITH_ACT_SIGNATURE(DIV, V1_1, TestOperandType::TENSOR_FLOAT32);
102 DEFINE_BROADCAST_WITH_ACT_SIGNATURE(ADD, V1_2, TestOperandType::TENSOR_FLOAT16);
103 DEFINE_BROADCAST_WITH_ACT_SIGNATURE(MUL, V1_2, TestOperandType::TENSOR_FLOAT16);
104 DEFINE_BROADCAST_WITH_ACT_SIGNATURE(SUB, V1_2, TestOperandType::TENSOR_FLOAT16,
105                                     TestOperandType::TENSOR_QUANT8_ASYMM);
106 DEFINE_BROADCAST_WITH_ACT_SIGNATURE(DIV, V1_2, TestOperandType::TENSOR_FLOAT16);
107 DEFINE_BROADCAST_WITH_ACT_SIGNATURE(ADD, V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED,
108                                     TestOperandType::TENSOR_INT32);
109 DEFINE_BROADCAST_WITH_ACT_SIGNATURE(MUL, V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED,
110                                     TestOperandType::TENSOR_INT32);
111 DEFINE_BROADCAST_WITH_ACT_SIGNATURE(SUB, V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED,
112                                     TestOperandType::TENSOR_INT32);
113 DEFINE_BROADCAST_WITH_ACT_SIGNATURE(DIV, V1_3, TestOperandType::TENSOR_INT32);
114 
115 // For broadcast ops with output of the same data type as inputs.
116 #define DEFINE_BROADCAST_SIGNATURE(op, ver, ...)                                     \
117     DEFINE_OPERATION_SIGNATURE(op##_##ver){.opType = TestOperationType::op,          \
118                                            .supportedDataTypes = {__VA_ARGS__},      \
119                                            .supportedRanks = {1, 2, 3, 4, 5},        \
120                                            .version = TestHalVersion::ver,           \
121                                            .inputs = {INPUT_DEFAULT, INPUT_DEFAULT}, \
122                                            .outputs = {OUTPUT_DEFAULT},              \
123                                            .constructor = broadcastOpConstructor};
124 
125 // Arithmetic without activation.
126 DEFINE_BROADCAST_SIGNATURE(POW, V1_2, TestOperandType::TENSOR_FLOAT32,
127                            TestOperandType::TENSOR_FLOAT16);
128 DEFINE_BROADCAST_SIGNATURE(PRELU, V1_2, TestOperandType::TENSOR_FLOAT32,
129                            TestOperandType::TENSOR_FLOAT16, TestOperandType::TENSOR_QUANT8_ASYMM);
130 DEFINE_BROADCAST_SIGNATURE(MAXIMUM, V1_2, TestOperandType::TENSOR_FLOAT32,
131                            TestOperandType::TENSOR_FLOAT16, TestOperandType::TENSOR_QUANT8_ASYMM,
132                            TestOperandType::TENSOR_INT32);
133 DEFINE_BROADCAST_SIGNATURE(MINIMUM, V1_2, TestOperandType::TENSOR_FLOAT32,
134                            TestOperandType::TENSOR_FLOAT16, TestOperandType::TENSOR_QUANT8_ASYMM,
135                            TestOperandType::TENSOR_INT32);
136 DEFINE_BROADCAST_SIGNATURE(PRELU, V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED);
137 DEFINE_BROADCAST_SIGNATURE(MAXIMUM, V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED);
138 DEFINE_BROADCAST_SIGNATURE(MINIMUM, V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED);
139 
140 // Logical
141 DEFINE_BROADCAST_SIGNATURE(LOGICAL_AND, V1_2, TestOperandType::TENSOR_BOOL8);
142 DEFINE_BROADCAST_SIGNATURE(LOGICAL_OR, V1_2, TestOperandType::TENSOR_BOOL8);
143 
144 // Comparisons
145 #define DEFINE_COMPARISON_SIGNATURE(op, ver, ...)                     \
146     DEFINE_OPERATION_SIGNATURE(op##_##ver){                           \
147             .opType = TestOperationType::op,                          \
148             .supportedDataTypes = {__VA_ARGS__},                      \
149             .supportedRanks = {1, 2, 3, 4},                           \
150             .version = TestHalVersion::ver,                           \
151             .inputs = {INPUT_DEFAULT, INPUT_DEFAULT},                 \
152             .outputs = {OUTPUT_TYPED(TestOperandType::TENSOR_BOOL8)}, \
153             .constructor = broadcastOpConstructor};
154 
155 DEFINE_COMPARISON_SIGNATURE(EQUAL, V1_2, TestOperandType::TENSOR_FLOAT32,
156                             TestOperandType::TENSOR_FLOAT16, TestOperandType::TENSOR_INT32,
157                             TestOperandType::TENSOR_QUANT8_ASYMM, TestOperandType::TENSOR_BOOL8);
158 DEFINE_COMPARISON_SIGNATURE(GREATER, V1_2, TestOperandType::TENSOR_FLOAT32,
159                             TestOperandType::TENSOR_FLOAT16, TestOperandType::TENSOR_INT32,
160                             TestOperandType::TENSOR_QUANT8_ASYMM);
161 DEFINE_COMPARISON_SIGNATURE(GREATER_EQUAL, V1_2, TestOperandType::TENSOR_FLOAT32,
162                             TestOperandType::TENSOR_FLOAT16, TestOperandType::TENSOR_INT32,
163                             TestOperandType::TENSOR_QUANT8_ASYMM);
164 DEFINE_COMPARISON_SIGNATURE(LESS, V1_2, TestOperandType::TENSOR_FLOAT32,
165                             TestOperandType::TENSOR_FLOAT16, TestOperandType::TENSOR_INT32,
166                             TestOperandType::TENSOR_QUANT8_ASYMM);
167 DEFINE_COMPARISON_SIGNATURE(LESS_EQUAL, V1_2, TestOperandType::TENSOR_FLOAT32,
168                             TestOperandType::TENSOR_FLOAT16, TestOperandType::TENSOR_INT32,
169                             TestOperandType::TENSOR_QUANT8_ASYMM);
170 DEFINE_COMPARISON_SIGNATURE(NOT_EQUAL, V1_2, TestOperandType::TENSOR_FLOAT32,
171                             TestOperandType::TENSOR_FLOAT16, TestOperandType::TENSOR_INT32,
172                             TestOperandType::TENSOR_QUANT8_ASYMM, TestOperandType::TENSOR_BOOL8);
173 DEFINE_COMPARISON_SIGNATURE(EQUAL, V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED);
174 DEFINE_COMPARISON_SIGNATURE(GREATER, V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED);
175 DEFINE_COMPARISON_SIGNATURE(GREATER_EQUAL, V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED);
176 DEFINE_COMPARISON_SIGNATURE(LESS, V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED);
177 DEFINE_COMPARISON_SIGNATURE(LESS_EQUAL, V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED);
178 DEFINE_COMPARISON_SIGNATURE(NOT_EQUAL, V1_3, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED);
179 
180 }  // namespace fuzzing_test
181 }  // namespace nn
182 }  // namespace android
183