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