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 "Converter.h"
18
19 #include <android-base/logging.h>
20 #include <nnapi/TypeUtils.h>
21
22 #include <algorithm>
23 #include <random>
24 #include <utility>
25 #include <vector>
26
27 namespace android::nn::fuzz {
28 namespace {
29
30 using namespace test_helper;
31
32 constexpr uint32_t kMaxSize = 65536;
33
convert(android_nn_fuzz::OperandType type)34 TestOperandType convert(android_nn_fuzz::OperandType type) {
35 return static_cast<TestOperandType>(type);
36 }
37
convert(android_nn_fuzz::OperationType type)38 TestOperationType convert(android_nn_fuzz::OperationType type) {
39 return static_cast<TestOperationType>(type);
40 }
41
convert(android_nn_fuzz::OperandLifeTime lifetime)42 TestOperandLifeTime convert(android_nn_fuzz::OperandLifeTime lifetime) {
43 return static_cast<TestOperandLifeTime>(lifetime);
44 }
45
convert(const android_nn_fuzz::Scales & scales)46 std::vector<float> convert(const android_nn_fuzz::Scales& scales) {
47 const auto& repeatedScale = scales.scale();
48 return std::vector<float>(repeatedScale.begin(), repeatedScale.end());
49 }
50
convert(const android_nn_fuzz::SymmPerChannelQuantParams & params)51 TestSymmPerChannelQuantParams convert(const android_nn_fuzz::SymmPerChannelQuantParams& params) {
52 std::vector<float> scales = convert(params.scales());
53 const uint32_t channelDim = params.channel_dim();
54 return {.scales = std::move(scales), .channelDim = channelDim};
55 }
56
convert(const android_nn_fuzz::Dimensions & dimensions)57 std::vector<uint32_t> convert(const android_nn_fuzz::Dimensions& dimensions) {
58 const auto& repeatedDimension = dimensions.dimension();
59 return std::vector<uint32_t>(repeatedDimension.begin(), repeatedDimension.end());
60 }
61
convert(size_t size,const android_nn_fuzz::Buffer & buffer)62 TestBuffer convert(size_t size, const android_nn_fuzz::Buffer& buffer) {
63 if (size == 0) {
64 return TestBuffer();
65 }
66 const uint32_t randomSeed = buffer.random_seed();
67 std::default_random_engine generator{randomSeed};
68 return TestBuffer::createRandom(size % kMaxSize, &generator);
69 }
70
convert(const android_nn_fuzz::Operand & operand)71 TestOperand convert(const android_nn_fuzz::Operand& operand) {
72 const TestOperandType type = convert(operand.type());
73 std::vector<uint32_t> dimensions = convert(operand.dimensions());
74 const float scale = operand.scale();
75 const int32_t zeroPoint = operand.zero_point();
76 const TestOperandLifeTime lifetime = convert(operand.lifetime());
77 auto channelQuant = convert(operand.channel_quant());
78
79 const bool isIgnored = false;
80 const auto opType = static_cast<OperandType>(type);
81 const size_t size = getNonExtensionSize(opType, dimensions).value_or(0);
82 const bool makeEmpty = (lifetime == TestOperandLifeTime::NO_VALUE ||
83 lifetime == TestOperandLifeTime::TEMPORARY_VARIABLE);
84 const size_t bufferSize = makeEmpty ? 0 : size;
85 TestBuffer data = convert(bufferSize, operand.data());
86
87 return {.type = type,
88 .dimensions = std::move(dimensions),
89 .numberOfConsumers = 0,
90 .scale = scale,
91 .zeroPoint = zeroPoint,
92 .lifetime = lifetime,
93 .channelQuant = std::move(channelQuant),
94 .isIgnored = isIgnored,
95 .data = std::move(data)};
96 }
97
convert(const android_nn_fuzz::Operands & operands)98 std::vector<TestOperand> convert(const android_nn_fuzz::Operands& operands) {
99 std::vector<TestOperand> testOperands;
100 testOperands.reserve(operands.operand_size());
101 const auto& repeatedOperand = operands.operand();
102 std::transform(repeatedOperand.begin(), repeatedOperand.end(), std::back_inserter(testOperands),
103 [](const auto& operand) { return convert(operand); });
104 return testOperands;
105 }
106
convert(const android_nn_fuzz::Indexes & indexes)107 std::vector<uint32_t> convert(const android_nn_fuzz::Indexes& indexes) {
108 const auto& repeatedIndex = indexes.index();
109 return std::vector<uint32_t>(repeatedIndex.begin(), repeatedIndex.end());
110 }
111
convert(const android_nn_fuzz::Operation & operation)112 TestOperation convert(const android_nn_fuzz::Operation& operation) {
113 const TestOperationType type = convert(operation.type());
114 std::vector<uint32_t> inputs = convert(operation.inputs());
115 std::vector<uint32_t> outputs = convert(operation.outputs());
116 return {.type = type, .inputs = std::move(inputs), .outputs = std::move(outputs)};
117 }
118
convert(const android_nn_fuzz::Operations & operations)119 std::vector<TestOperation> convert(const android_nn_fuzz::Operations& operations) {
120 std::vector<TestOperation> testOperations;
121 testOperations.reserve(operations.operation_size());
122 const auto& repeatedOperation = operations.operation();
123 std::transform(repeatedOperation.begin(), repeatedOperation.end(),
124 std::back_inserter(testOperations),
125 [](const auto& operation) { return convert(operation); });
126 return testOperations;
127 }
128
calculateNumberOfConsumers(const std::vector<TestOperation> & operations,std::vector<TestOperand> * operands)129 void calculateNumberOfConsumers(const std::vector<TestOperation>& operations,
130 std::vector<TestOperand>* operands) {
131 CHECK(operands != nullptr);
132 const auto addConsumer = [operands](uint32_t operand) {
133 if (operand < operands->size()) {
134 operands->at(operand).numberOfConsumers++;
135 }
136 };
137 const auto addAllConsumers = [&addConsumer](const TestOperation& operation) {
138 std::for_each(operation.inputs.begin(), operation.inputs.end(), addConsumer);
139 };
140 std::for_each(operations.begin(), operations.end(), addAllConsumers);
141 }
142
convert(const android_nn_fuzz::Model & model)143 TestModel convert(const android_nn_fuzz::Model& model) {
144 std::vector<TestOperand> operands = convert(model.operands());
145 std::vector<TestOperation> operations = convert(model.operations());
146 std::vector<uint32_t> inputIndexes = convert(model.input_indexes());
147 std::vector<uint32_t> outputIndexes = convert(model.output_indexes());
148 const bool isRelaxed = model.is_relaxed();
149
150 // Calculate number of consumers.
151 calculateNumberOfConsumers(operations, &operands);
152
153 return {.main = {.operands = std::move(operands),
154 .operations = std::move(operations),
155 .inputIndexes = std::move(inputIndexes),
156 .outputIndexes = std::move(outputIndexes)},
157 .isRelaxed = isRelaxed};
158 }
159
160 } // anonymous namespace
161
convertToTestModel(const android_nn_fuzz::Test & model)162 TestModel convertToTestModel(const android_nn_fuzz::Test& model) {
163 return convert(model.model());
164 }
165
166 } // namespace android::nn::fuzz
167