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_FRAMEWORKS_ML_NN_RUNTIME_TEST_FUZZING_RANDOM_GRAPH_GENERATOR_H 18 #define ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_FUZZING_RANDOM_GRAPH_GENERATOR_H 19 20 #include <memory> 21 #include <string> 22 #include <vector> 23 24 #include "TestHarness.h" 25 #include "TestNeuralNetworksWrapper.h" 26 #include "fuzzing/RandomVariable.h" 27 28 namespace android { 29 namespace nn { 30 namespace fuzzing_test { 31 32 using OperandBuffer = std::vector<int32_t>; 33 34 struct OperandSignature; 35 struct OperationSignature; 36 class OperationManager; 37 38 enum class RandomOperandType { INPUT = 0, OUTPUT = 1, INTERNAL = 2, CONST = 3, NO_VALUE = 4 }; 39 40 struct RandomOperand { 41 // Describes the properties of the values of an operand. For operation inputs, this specifies 42 // what is required; for outputs, this specifies what is guaranteed. 43 // The graph generation algorithm will use this information to decide whether to wire an output 44 // to an input or not. 45 enum ValueProperty : int { 46 NON_ZERO = 1 << 0, 47 NON_NEGATIVE = 1 << 1, 48 }; 49 50 RandomOperandType type; 51 int valueProperties = 0; 52 test_helper::TestOperandType dataType; 53 float scale = 0.0f; 54 int32_t zeroPoint = 0; 55 std::vector<RandomVariable> dimensions; 56 OperandBuffer buffer; 57 std::vector<RandomVariable> randomBuffer; 58 59 // The finalizer will be invoked after RandomVariableNetwork::freeze(). 60 // Operand buffer will be set during this step (if not set before). 61 std::function<void(RandomOperand*)> finalizer = nullptr; 62 63 // The index of the operand in the model as returned from model->addOperand(...). 64 int32_t opIndex = -1; 65 // The index of the input/output as specified in model->identifyInputsAndOutputs(...). 66 int32_t ioIndex = -1; 67 68 // If set true, this operand will be ignored during the accuracy checking step. 69 bool doNotCheckAccuracy = false; 70 71 // If set true, this operand will not be connected to another operation, e.g. if this operand is 72 // an operation output, then it will not be used as an input to another operation, and will 73 // eventually end up being a model output. 74 bool doNotConnect = false; 75 76 RandomOperand(const OperandSignature& op, test_helper::TestOperandType dataType, uint32_t rank); 77 78 // Resize the underlying operand buffer. 79 template <typename T> resizeBufferRandomOperand80 void resizeBuffer(uint32_t len) { 81 constexpr size_t valueSize = sizeof(OperandBuffer::value_type); 82 uint32_t bufferSize = (sizeof(T) * len + valueSize - 1) / valueSize; 83 buffer.resize(bufferSize); 84 } 85 86 // Get the operand value as the specified type. The caller is reponsible for making sure that 87 // the index is not out of range. 88 template <typename T> 89 T& value(uint32_t index = 0) { 90 return reinterpret_cast<T*>(buffer.data())[index]; 91 } 92 template <> 93 RandomVariable& value<RandomVariable>(uint32_t index) { 94 return randomBuffer[index]; 95 } 96 97 // The caller is reponsible for making sure that the operand is indeed a scalar. 98 template <typename T> setScalarValueRandomOperand99 void setScalarValue(const T& val) { 100 resizeBuffer<T>(/*len=*/1); 101 value<T>() = val; 102 } 103 104 // Check if a directed edge between [other -> this] is valid. If yes, add the edge. 105 // Where "this" must be of type INPUT and "other" must be of type OUTPUT. 106 bool createEdgeIfValid(const RandomOperand& other) const; 107 108 // The followings are only intended to be used after RandomVariableNetwork::freeze(). 109 std::vector<uint32_t> getDimensions() const; 110 uint32_t getNumberOfElements() const; 111 size_t getBufferSize() const; 112 }; 113 114 struct RandomOperation { 115 test_helper::TestOperationType opType; 116 std::vector<std::shared_ptr<RandomOperand>> inputs; 117 std::vector<std::shared_ptr<RandomOperand>> outputs; 118 std::function<void(RandomOperation*)> finalizer = nullptr; 119 RandomOperation(const OperationSignature& operation); 120 }; 121 122 // The main interface of the random graph generator. 123 class RandomGraph { 124 public: 125 RandomGraph() = default; 126 127 // Generate a random graph with numOperations and dimensionRange from a seed. 128 bool generate(uint32_t seed, uint32_t numOperations, uint32_t dimensionRange); 129 130 // Create a test model of the generated graph. The operands will always have fully-specified 131 // dimensions. The output buffers are only allocated but not initialized. 132 test_helper::TestModel createTestModel(); 133 getOperations()134 const std::vector<RandomOperation>& getOperations() const { return mOperations; } 135 136 private: 137 // Generate the graph structure. 138 bool generateGraph(uint32_t numOperations); 139 140 // Fill in random values for dimensions, constants, and inputs. 141 bool generateValue(); 142 143 std::vector<RandomOperation> mOperations; 144 std::vector<std::shared_ptr<RandomOperand>> mOperands; 145 }; 146 147 } // namespace fuzzing_test 148 } // namespace nn 149 } // namespace android 150 151 #endif // ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_FUZZING_RANDOM_GRAPH_GENERATOR_H 152