• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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