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 "RandomGraphGeneratorUtils.h"
18
19 #include <algorithm>
20 #include <iomanip>
21 #include <memory>
22 #include <sstream>
23 #include <string>
24
25 #include "RandomGraphGenerator.h"
26 #include "RandomVariable.h"
27
28 namespace android {
29 namespace nn {
30 namespace fuzzing_test {
31
32 std::mt19937 RandomNumberGenerator::generator;
33
getElapsedTime()34 std::string Logger::getElapsedTime() {
35 auto end = std::chrono::high_resolution_clock::now();
36 int ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - mStart).count();
37 int hour = ms / 3600000;
38 int minutes = (ms % 3600000) / 60000;
39 int seconds = (ms % 60000) / 1000;
40 int milli = ms % 1000;
41 std::ostringstream oss;
42 oss << std::setfill('0') << std::setw(2) << hour << ":" << std::setw(2) << minutes << ":"
43 << std::setw(2) << seconds << "." << std::setw(3) << milli << " ";
44 return oss.str();
45 }
46
SpecWriter(std::string filename,std::string testname)47 SpecWriter::SpecWriter(std::string filename, std::string testname) : os(filename) {
48 if (os.is_open() && testname != "") {
49 os << "# Generated from " << testname << ". Do not edit.\n\n";
50 }
51 }
52
53 // Main entrance of dumping the spec file.
54 // Check nn/tools/test_generator/README.md for guide on spec file syntax.
dump(const std::vector<RandomOperation> & operations,const std::vector<std::shared_ptr<RandomOperand>> & operands)55 void SpecWriter::dump(const std::vector<RandomOperation>& operations,
56 const std::vector<std::shared_ptr<RandomOperand>>& operands) {
57 // RandomGraphGenerator does not support dynamic output shape.
58 os << "Configuration.test_dynamic_output_shape = False\n\n";
59
60 // Dump model operands.
61 os << "# Model operands\n";
62 for (auto& operand : operands) dump(operand);
63
64 // Dump model operations.
65 os << "\n# Model operations\nmodel = Model()\n";
66 for (const auto& operation : operations) dump(operation);
67
68 // Dump input/output buffers.
69 os << "\n# Example\n";
70 os << "Example({\n";
71 for (auto& operand : operands) {
72 if (operand->type == RandomOperandType::CONST ||
73 operand->type == RandomOperandType::INTERNAL) {
74 continue;
75 }
76 os << " op" << operand->opIndex << ": [";
77 dump(operand->dataType, reinterpret_cast<uint8_t*>(operand->buffer.data()),
78 operand->getNumberOfElements());
79 os << "],\n";
80 }
81 os << "})\n";
82 }
83
84 // Dump an operand buffer.
dump(test_wrapper::Type type,const uint8_t * buffer,uint32_t length)85 void SpecWriter::dump(test_wrapper::Type type, const uint8_t* buffer, uint32_t length) {
86 if (buffer == nullptr) return;
87 switch (type) {
88 case test_wrapper::Type::FLOAT32:
89 case test_wrapper::Type::TENSOR_FLOAT32:
90 dump(reinterpret_cast<const float*>(buffer), length);
91 break;
92 case test_wrapper::Type::INT32:
93 case test_wrapper::Type::TENSOR_INT32:
94 dump(reinterpret_cast<const int32_t*>(buffer), length);
95 break;
96 case test_wrapper::Type::TENSOR_QUANT8_ASYMM:
97 dump(reinterpret_cast<const uint8_t*>(buffer), length);
98 break;
99 case test_wrapper::Type::TENSOR_QUANT8_SYMM:
100 dump(reinterpret_cast<const int8_t*>(buffer), length);
101 break;
102 case test_wrapper::Type::TENSOR_QUANT16_ASYMM:
103 dump(reinterpret_cast<const uint16_t*>(buffer), length);
104 break;
105 case test_wrapper::Type::TENSOR_QUANT16_SYMM:
106 dump(reinterpret_cast<const int16_t*>(buffer), length);
107 break;
108 case test_wrapper::Type::BOOL:
109 case test_wrapper::Type::TENSOR_BOOL8:
110 dump(reinterpret_cast<const bool8*>(buffer), length);
111 break;
112 case test_wrapper::Type::FLOAT16:
113 case test_wrapper::Type::TENSOR_FLOAT16:
114 dump(reinterpret_cast<const _Float16*>(buffer), length);
115 break;
116 default:
117 NN_FUZZER_CHECK(false) << "Unknown type when dumping the buffer";
118 }
119 }
120
121 // Dump dimensions with curly braces.
dump(const std::vector<RandomVariable> & dimensions)122 void SpecWriter::dump(const std::vector<RandomVariable>& dimensions) {
123 os << "{" << joinStr(", ", dimensions, [](const RandomVariable& var) {
124 return std::to_string(var.getValue());
125 }) << "}";
126 }
127
128 // Dump a model operand.
129 // e.g. op0 = Input("op0", "TENSOR_FLOAT32", "{1, 2, 6, 1}")
130 // e.g. op1 = Parameter("op1", "INT32", "{}", [2])
dump(const std::shared_ptr<RandomOperand> & op)131 void SpecWriter::dump(const std::shared_ptr<RandomOperand>& op) {
132 os << "op" << op->opIndex << " = " << toString(op->type) << "(\"op" << op->opIndex << "\", \""
133 << toString(op->dataType) << "\", \"";
134 dump(op->dimensions);
135 if (op->scale != 0.0f || op->zeroPoint != 0) {
136 os << ", " << op->scale << "f, " << op->zeroPoint;
137 }
138 os << "\"";
139 if (op->type == RandomOperandType::CONST) {
140 os << ", [";
141 dump(op->dataType, reinterpret_cast<uint8_t*>(op->buffer.data()),
142 op->getNumberOfElements());
143 os << "]";
144 }
145 os << ")\n";
146 }
147
148 // Dump a model operation.
149 // e.g. model = model.Operation("CONV_2D", op0, op1, op2, op3, op4, op5, op6).To(op7)
dump(const RandomOperation & op)150 void SpecWriter::dump(const RandomOperation& op) {
151 auto getOperandStr = [](std::shared_ptr<RandomOperand> op) {
152 return "op" + std::to_string(op->opIndex);
153 };
154 os << "model = model.Operation(\"" << kOperationNames[op.opType] << "\", "
155 << joinStr(", ", op.inputs, getOperandStr) << ").To("
156 << joinStr(", ", op.outputs, getOperandStr) << ")\n";
157 }
158
159 } // namespace fuzzing_test
160 } // namespace nn
161 } // namespace android
162