1 /* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #include "tensorflow/lite/delegates/xnnpack/quantized_reduce_tester.h"
17
18 #include <array>
19 #include <cstdint>
20 #include <functional>
21 #include <numeric>
22 #include <random>
23 #include <vector>
24
25 #include <gtest/gtest.h>
26 #include "flatbuffers/flatbuffers.h" // from @flatbuffers
27 #include "tensorflow/lite/interpreter.h"
28 #include "tensorflow/lite/kernels/register.h"
29 #include "tensorflow/lite/model.h"
30 #include "tensorflow/lite/schema/schema_conversion_utils.h"
31 #include "tensorflow/lite/schema/schema_generated.h"
32 #include "tensorflow/lite/version.h"
33
34 namespace tflite {
35 namespace xnnpack {
36
37 template <class T>
Test(Interpreter * delegate_interpreter,Interpreter * default_interpreter) const38 void QuantizedReduceTester::Test(Interpreter* delegate_interpreter,
39 Interpreter* default_interpreter) const {
40 std::random_device random_device;
41 auto rng = std::mt19937(random_device());
42 auto input_rng = std::bind(
43 std::uniform_int_distribution<int32_t>(std::numeric_limits<T>::min(),
44 std::numeric_limits<T>::max()),
45 std::ref(rng));
46
47 T* default_input_data = default_interpreter->typed_input_tensor<T>(0);
48 std::generate(default_input_data, default_input_data + InputSize(),
49 std::ref(input_rng));
50
51 T* delegate_input_data = delegate_interpreter->typed_input_tensor<T>(0);
52 std::copy(default_input_data, default_input_data + InputSize(),
53 delegate_input_data);
54
55 ASSERT_EQ(default_interpreter->Invoke(), kTfLiteOk);
56 ASSERT_EQ(delegate_interpreter->Invoke(), kTfLiteOk);
57
58 T* default_output_data = default_interpreter->typed_output_tensor<T>(0);
59 T* delegate_output_data = delegate_interpreter->typed_output_tensor<T>(0);
60
61 const int32_t output_size = OutputSize();
62 for (size_t i = 0; i < output_size; i++) {
63 ASSERT_LE(std::abs(static_cast<int32_t>(default_output_data[i]) -
64 static_cast<int32_t>(delegate_output_data[i])),
65 1)
66 << "default " << static_cast<int32_t>(default_output_data[i])
67 << ", delegate " << static_cast<int32_t>(delegate_output_data[i])
68 << " at index " << i << " / " << output_size;
69 }
70 }
71
Test(tflite::BuiltinOperator reduce_op,TfLiteDelegate * delegate) const72 void QuantizedReduceTester::Test(tflite::BuiltinOperator reduce_op,
73 TfLiteDelegate* delegate) const {
74 std::vector<char> buffer = CreateTfLiteModel(reduce_op);
75 const Model* model = GetModel(buffer.data());
76
77 std::unique_ptr<Interpreter> delegate_interpreter;
78 ASSERT_EQ(
79 InterpreterBuilder(
80 model,
81 ::tflite::ops::builtin::BuiltinOpResolverWithoutDefaultDelegates())(
82 &delegate_interpreter),
83 kTfLiteOk);
84 std::unique_ptr<Interpreter> default_interpreter;
85 ASSERT_EQ(
86 InterpreterBuilder(
87 model,
88 ::tflite::ops::builtin::BuiltinOpResolverWithoutDefaultDelegates())(
89 &default_interpreter),
90 kTfLiteOk);
91
92 ASSERT_TRUE(delegate_interpreter);
93 ASSERT_TRUE(default_interpreter);
94
95 ASSERT_EQ(delegate_interpreter->inputs().size(), 1);
96 ASSERT_EQ(default_interpreter->inputs().size(), 1);
97
98 ASSERT_EQ(delegate_interpreter->outputs().size(), 1);
99 ASSERT_EQ(default_interpreter->outputs().size(), 1);
100
101 ASSERT_EQ(delegate_interpreter->AllocateTensors(), kTfLiteOk);
102 ASSERT_EQ(default_interpreter->AllocateTensors(), kTfLiteOk);
103
104 ASSERT_EQ(delegate_interpreter->ModifyGraphWithDelegate(delegate), kTfLiteOk);
105
106 if (Unsigned()) {
107 Test<uint8_t>(delegate_interpreter.get(), default_interpreter.get());
108 } else {
109 Test<int8_t>(delegate_interpreter.get(), default_interpreter.get());
110 }
111 }
112
CreateTfLiteModel(tflite::BuiltinOperator reduce_op) const113 std::vector<char> QuantizedReduceTester::CreateTfLiteModel(
114 tflite::BuiltinOperator reduce_op) const {
115 flatbuffers::FlatBufferBuilder builder;
116 flatbuffers::Offset<OperatorCode> operator_code =
117 CreateOperatorCode(builder, reduce_op);
118
119 const std::array<flatbuffers::Offset<Buffer>, 2> buffers{{
120 CreateBuffer(builder, builder.CreateVector({})),
121 CreateBuffer(builder, builder.CreateVector(
122 reinterpret_cast<const uint8_t*>(Axes().data()),
123 sizeof(int32_t) * Axes().size())),
124 }};
125
126 const std::vector<int32_t> output_shape = OutputShape();
127 const std::array<int32_t, 1> axes_shape{
128 {static_cast<int32_t>(Axes().size())}};
129 const std::array<flatbuffers::Offset<Tensor>, 3> tensors{{
130 CreateTensor(builder,
131 builder.CreateVector<int32_t>(InputShape().data(),
132 InputShape().size()),
133 Unsigned() ? TensorType_UINT8 : TensorType_INT8,
134 /*buffer=*/0, /*name=*/0,
135 CreateQuantizationParameters(
136 builder, /*min=*/0, /*max=*/0,
137 builder.CreateVector<float>({InputScale()}),
138 builder.CreateVector<int64_t>({InputZeroPoint()}))),
139 CreateTensor(
140 builder,
141 builder.CreateVector<int32_t>(axes_shape.data(), axes_shape.size()),
142 TensorType_INT32, /*buffer=*/1),
143 CreateTensor(builder,
144 builder.CreateVector<int32_t>(output_shape.data(),
145 output_shape.size()),
146 Unsigned() ? TensorType_UINT8 : TensorType_INT8,
147 /*buffer=*/0, /*name=*/0,
148 CreateQuantizationParameters(
149 builder, /*min=*/0, /*max=*/0,
150 builder.CreateVector<float>({OutputScale()}),
151 builder.CreateVector<int64_t>({OutputZeroPoint()}))),
152 }};
153
154 const flatbuffers::Offset<ReducerOptions> reducer_options =
155 CreateReducerOptions(builder, KeepDims());
156
157 const std::array<int32_t, 2> op_inputs{{0, 1}};
158 const std::array<int32_t, 1> op_outputs{{2}};
159 flatbuffers::Offset<Operator> op = CreateOperator(
160 builder, /*opcode_index=*/0,
161 builder.CreateVector<int32_t>(op_inputs.data(), op_inputs.size()),
162 builder.CreateVector<int32_t>(op_outputs.data(), op_outputs.size()),
163 tflite::BuiltinOptions_ReducerOptions, reducer_options.Union());
164
165 const std::array<int32_t, 1> subgraph_inputs{{0}};
166 const std::array<int32_t, 1> subgraph_outputs{{2}};
167 flatbuffers::Offset<SubGraph> subgraph = CreateSubGraph(
168 builder, builder.CreateVector(tensors.data(), tensors.size()),
169 builder.CreateVector<int32_t>(subgraph_inputs.data(),
170 subgraph_inputs.size()),
171 builder.CreateVector<int32_t>(subgraph_outputs.data(),
172 subgraph_outputs.size()),
173 builder.CreateVector(&op, 1));
174
175 flatbuffers::Offset<flatbuffers::String> description =
176 builder.CreateString("Quantized Reduce model");
177
178 flatbuffers::Offset<Model> model_buffer = CreateModel(
179 builder, TFLITE_SCHEMA_VERSION, builder.CreateVector(&operator_code, 1),
180 builder.CreateVector(&subgraph, 1), description,
181 builder.CreateVector(buffers.data(), buffers.size()));
182
183 builder.Finish(model_buffer);
184
185 return std::vector<char>(builder.GetBufferPointer(),
186 builder.GetBufferPointer() + builder.GetSize());
187 }
188
ComputeSize(const std::vector<int32_t> & shape)189 int32_t QuantizedReduceTester::ComputeSize(const std::vector<int32_t>& shape) {
190 return std::accumulate(shape.cbegin(), shape.cend(), 1,
191 std::multiplies<int32_t>());
192 }
193
194 } // namespace xnnpack
195 } // namespace tflite
196