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 #ifndef ANDROID_FRAMEWORK_ML_NN_RUNTIME_TEST_FUZZING_RANDOM_GRAPH_GENERATOR_H 18 #define ANDROID_FRAMEWORK_ML_NN_RUNTIME_TEST_FUZZING_RANDOM_GRAPH_GENERATOR_H 19 20 #include <string> 21 #include <vector> 22 23 #include "TestNeuralNetworksWrapper.h" 24 #include "fuzzing/RandomVariable.h" 25 26 namespace android { 27 namespace nn { 28 namespace fuzzing_test { 29 30 using test_wrapper::Type; 31 using OperandBuffer = std::vector<int32_t>; 32 33 struct OperandSignature; 34 struct OperationSignature; 35 class OperationManager; 36 37 enum class RandomOperandType { INPUT = 0, OUTPUT = 1, INTERNAL = 2, CONST = 3 }; 38 39 struct RandomOperand { 40 RandomOperandType type; 41 Type dataType; 42 float scale = 0.0f; 43 int32_t zeroPoint = 0; 44 std::vector<RandomVariable> dimensions; 45 OperandBuffer buffer; 46 std::vector<RandomVariable> randomBuffer; 47 48 // The finalizer will be invoked after RandomVariableNetwork::freeze(). 49 // Operand buffer will be set during this step (if not set before). 50 std::function<void(RandomOperand*)> finalizer = nullptr; 51 52 // The index of the operand in the model as returned from model->addOperand(...). 53 int32_t opIndex = -1; 54 // The index of the input/output as specified in model->identifyInputsAndOutputs(...). 55 int32_t ioIndex = -1; 56 57 // If set true, this operand will be ignored during the accuracy checking step. 58 bool doNotCheckAccuracy = false; 59 60 // If set true, this operand will not be connected to another operation, e.g. if this operand is 61 // an operation output, then it will not be used as an input to another operation, and will 62 // eventually end up being a model output. 63 bool doNotConnect = false; 64 65 RandomOperand(const OperandSignature& op, Type dataType, uint32_t rank); 66 67 // Resize the underlying operand buffer. 68 template <typename T> resizeBufferRandomOperand69 void resizeBuffer(uint32_t len) { 70 constexpr size_t valueSize = sizeof(OperandBuffer::value_type); 71 uint32_t bufferSize = (sizeof(T) * len + valueSize - 1) / valueSize; 72 buffer.resize(bufferSize); 73 } 74 75 // Get the operand value as the specified type. The caller is reponsible for making sure that 76 // the index is not out of range. 77 template <typename T> 78 T& value(uint32_t index = 0) { 79 return reinterpret_cast<T*>(buffer.data())[index]; 80 } 81 template <> 82 RandomVariable& value<RandomVariable>(uint32_t index) { 83 return randomBuffer[index]; 84 } 85 86 // The caller is reponsible for making sure that the operand is indeed a scalar. 87 template <typename T> setScalarValueRandomOperand88 void setScalarValue(const T& val) { 89 resizeBuffer<T>(/*len=*/1); 90 value<T>() = val; 91 } 92 93 // Check if a directed edge between [other -> this] is valid. If yes, add the edge. 94 // Where "this" must be of type INPUT and "other" must be of type OUTPUT. 95 bool createEdgeIfValid(const RandomOperand& other) const; 96 97 // The followings are only intended to be used after RandomVariableNetwork::freeze(). 98 std::vector<uint32_t> getDimensions() const; 99 uint32_t getNumberOfElements() const; 100 size_t getBufferSize() const; 101 }; 102 103 struct RandomOperation { 104 ANeuralNetworksOperationType opType; 105 std::vector<std::shared_ptr<RandomOperand>> inputs; 106 std::vector<std::shared_ptr<RandomOperand>> outputs; 107 std::function<void(RandomOperation*)> finalizer = nullptr; 108 RandomOperation(const OperationSignature& operation); 109 }; 110 111 // TODO: Consider relative bias and mse on floating point data types? 112 struct AccuracyCriterion { 113 // We expect the driver results to be unbiased. 114 // Formula: abs(sum_{i}(diff)) <= bias, where 115 // * fixed point: diff = actual - expected 116 // * floating point: diff = (actual - expected) / max(1, abs(expected)) 117 float bias = std::numeric_limits<float>::max(); 118 119 // Set the threshold on Mean Square Error (MSE). 120 // Formula: sum_{i}(diff ^ 2) / sum(1) <= mse 121 float mse = std::numeric_limits<float>::max(); 122 123 // We also set accuracy thresholds on each element to detect any particular edge cases that may 124 // be shadowed in bias or MSE. We use the similar approach as our CTS unit tests, but with much 125 // relaxed criterion. 126 // Formula: abs(actual - expected) <= atol + rtol * abs(expected) 127 // where atol stands for Absolute TOLerance and rtol for Relative TOLerance. 128 float atol = 0.0f; 129 float rtol = 0.0f; 130 }; 131 132 struct AccuracyCriteria { 133 AccuracyCriterion float32; 134 AccuracyCriterion float16; 135 AccuracyCriterion int32; 136 AccuracyCriterion quant8Asymm; 137 AccuracyCriterion quant8Symm; 138 AccuracyCriterion quant16Asymm; 139 AccuracyCriterion quant16Symm; 140 }; 141 142 // The main interface of the random graph generator. 143 class RandomGraph { 144 public: 145 RandomGraph() = default; 146 147 // Generate a random graph with numOperations and dimensionRange from a seed. 148 bool generate(uint32_t seed, uint32_t numOperations, uint32_t dimensionRange); 149 150 // Create a NDK model from the random graph. 151 void createModel(test_wrapper::Model* model); 152 153 // Set the input/output buffers to an NDK execution object. The input buffer resides in 154 // RandomOperand.buffer, the output buffer is either provided by "buffers" argument, or set 155 // buffers to nullptr to use RandomOperand.buffer to record reference result. 156 void createRequest(test_wrapper::Execution* execution, 157 std::vector<OperandBuffer>* buffers = nullptr); 158 159 // Check if the results in buffers meet the given accuracy criteria. 160 void checkResults(const std::vector<OperandBuffer>& buffers, 161 const AccuracyCriteria& criteria) const; 162 163 // Dump the generated random graph to a spec file for debugging and visualization purpose. 164 void dumpSpecFile(std::string filename, std::string testname); 165 getOperations()166 const std::vector<RandomOperation>& getOperations() const { return mOperations; } 167 168 private: 169 // Generate the graph structure. 170 bool generateGraph(uint32_t numOperations); 171 172 // Fill in random values for dimensions, constants, and inputs. 173 bool generateValue(); 174 175 std::vector<RandomOperation> mOperations; 176 std::vector<std::shared_ptr<RandomOperand>> mOperands; 177 }; 178 179 } // namespace fuzzing_test 180 } // namespace nn 181 } // namespace android 182 183 #endif // ANDROID_FRAMEWORK_ML_NN_RUNTIME_TEST_FUZZING_RANDOM_GRAPH_GENERATOR_H 184