xref: /aosp_15_r20/external/tensorflow/tensorflow/tools/graph_transforms/quantize_nodes_test.cc (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1 /* Copyright 2015 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 #define EIGEN_USE_THREADS
17 
18 #include "tensorflow/cc/ops/const_op.h"
19 #include "tensorflow/cc/ops/image_ops.h"
20 #include "tensorflow/cc/ops/nn_ops.h"
21 #include "tensorflow/cc/ops/sendrecv_ops.h"
22 #include "tensorflow/cc/ops/standard_ops.h"
23 #include "tensorflow/core/framework/tensor_testutil.h"
24 #include "tensorflow/core/kernels/quantization_utils.h"
25 #include "tensorflow/core/lib/core/status_test_util.h"
26 #include "tensorflow/core/platform/test.h"
27 #include "tensorflow/core/platform/test_benchmark.h"
28 #include "tensorflow/core/public/session.h"
29 #include "tensorflow/tools/graph_transforms/transform_utils.h"
30 
31 namespace tensorflow {
32 namespace graph_transforms {
33 
34 // Declare here, so we don't need a public header.
35 Status QuantizeNodes(const GraphDef& input_graph_def,
36                      const TransformFuncContext& context,
37                      GraphDef* output_graph_def);
38 Status RemoveRedundantQuantizations(const GraphDef& input_graph_def,
39                                     const TransformFuncContext& context,
40                                     GraphDef* output_graph_def);
41 Status QuantizePlaceholders(const GraphDef& input_graph_def,
42                             const TransformFuncContext& context,
43                             GraphDef* output_graph_def);
44 Status ConvertFakeQuantsToRequantize(const GraphDef& input_graph_def,
45                                      const TransformFuncContext& context,
46                                      GraphDef* output_graph_def);
47 Status MergeAdjacentRequantizes(const GraphDef& input_graph_def,
48                                 const TransformFuncContext& context,
49                                 GraphDef* output_graph_def);
50 Status HoistFakeQuants(const GraphDef& input_graph_def,
51                        const TransformFuncContext& context,
52                        GraphDef* output_graph_def);
53 Status MergeDuplicateNodes(const GraphDef& input_graph_def,
54                            const TransformFuncContext& context,
55                            GraphDef* output_graph_def);
56 
57 class QuantizeNodesTest : public ::testing::Test {
58  protected:
TestTransformedVersusFloatGraph(const TransformFunc & transform_function,const GraphDef & float_graph_def,const std::vector<std::pair<string,Tensor>> & float_inputs,const std::vector<std::pair<string,Tensor>> & transformed_inputs,const std::vector<string> & output_names,const TransformFuncContext & in_context,double threshold,GraphDef * transformed_graph_def)59   void TestTransformedVersusFloatGraph(
60       const TransformFunc& transform_function, const GraphDef& float_graph_def,
61       const std::vector<std::pair<string, Tensor>>& float_inputs,
62       const std::vector<std::pair<string, Tensor>>& transformed_inputs,
63       const std::vector<string>& output_names,
64       const TransformFuncContext& in_context, double threshold,
65       GraphDef* transformed_graph_def) {
66     std::unique_ptr<Session> float_session(NewSession(SessionOptions()));
67     TF_ASSERT_OK(float_session->Create(float_graph_def));
68     std::vector<Tensor> float_outputs;
69     TF_ASSERT_OK(
70         float_session->Run(float_inputs, output_names, {}, &float_outputs));
71 
72     TransformFuncContext context(in_context);
73     std::vector<string> input_names;
74     for (const std::pair<const string&, const Tensor&> float_input :
75          float_inputs) {
76       context.input_names.push_back(float_input.first);
77     }
78 
79     context.output_names = output_names;
80     TF_ASSERT_OK(
81         transform_function(float_graph_def, context, transformed_graph_def));
82 
83     std::unique_ptr<Session> transformed_session(NewSession(SessionOptions()));
84     TF_ASSERT_OK(transformed_session->Create(*transformed_graph_def));
85     std::vector<Tensor> transformed_outputs;
86     TF_ASSERT_OK(transformed_session->Run(transformed_inputs, output_names, {},
87                                           &transformed_outputs));
88 
89     const int output_count = output_names.size();
90     EXPECT_EQ(output_count, float_outputs.size());
91     EXPECT_EQ(output_count, transformed_outputs.size());
92     for (int i = 0; i < output_count; ++i) {
93       test::ExpectTensorNear<float>(float_outputs[i], transformed_outputs[i],
94                                     threshold);
95     }
96   }
97 
TestQuantizedVersusFloatGraph(const GraphDef & float_graph_def,const std::vector<std::pair<string,Tensor>> & inputs,const std::vector<string> & output_names)98   void TestQuantizedVersusFloatGraph(
99       const GraphDef& float_graph_def,
100       const std::vector<std::pair<string, Tensor>>& inputs,
101       const std::vector<string>& output_names) {
102     GraphDef quantized_graph_def;
103     TestTransformedVersusFloatGraph(QuantizeNodes, float_graph_def, inputs,
104                                     inputs, output_names, {}, 1.0,
105                                     &quantized_graph_def);
106     // Reshape is not included here because it can be added as part of the
107     // quantization process.
108     const std::set<string> quantizable_ops = {
109         "Add",   "BiasAdd",        "Concat",  "Conv2D",  "MatMul", "Relu",
110         "Relu6", "ResizeBilinear", "AvgPool", "MaxPool", "Mul"};
111     for (const NodeDef& node : quantized_graph_def.node()) {
112       EXPECT_EQ(0, quantizable_ops.count(node.op()))
113           << "Found quantizable node " << node.op() << " for node named "
114           << node.name();
115     }
116   }
117 
TestGraphWithInputRange(const GraphDef & float_graph_def,const std::vector<std::pair<string,Tensor>> & float_inputs,const std::vector<string> & output_names,float range_min,float range_max)118   void TestGraphWithInputRange(
119       const GraphDef& float_graph_def,
120       const std::vector<std::pair<string, Tensor>>& float_inputs,
121       const std::vector<string>& output_names, float range_min,
122       float range_max) {
123     TransformFuncContext context;
124     context.params["input_min"] = {strings::StrCat(range_min)};
125     context.params["input_max"] = {strings::StrCat(range_max)};
126 
127     std::vector<std::pair<string, Tensor>> quantized_inputs;
128     for (const std::pair<string, Tensor>& float_input : float_inputs) {
129       const Tensor& float_tensor = float_input.second;
130       Tensor quantized_tensor(DT_QUINT8, float_tensor.shape());
131       FloatTensorToQuantizedInPlace<quint8>(float_tensor, range_min, range_max,
132                                             &quantized_tensor);
133       quantized_inputs.push_back({float_input.first, quantized_tensor});
134     }
135 
136     GraphDef quantized_graph_def;
137     TestTransformedVersusFloatGraph(
138         QuantizeNodes, float_graph_def, float_inputs, quantized_inputs,
139         output_names, context, 1.0, &quantized_graph_def);
140   }
141 
TestGraphWithFallbackRange(const GraphDef & float_graph_def,const std::vector<std::pair<string,Tensor>> & float_inputs,const std::vector<string> & output_names,float range_min,float range_max,GraphDef * quantized_graph_def)142   void TestGraphWithFallbackRange(
143       const GraphDef& float_graph_def,
144       const std::vector<std::pair<string, Tensor>>& float_inputs,
145       const std::vector<string>& output_names, float range_min, float range_max,
146       GraphDef* quantized_graph_def) {
147     TransformFuncContext context;
148     context.params["fallback_min"] = {strings::StrCat(range_min)};
149     context.params["fallback_max"] = {strings::StrCat(range_max)};
150     TestTransformedVersusFloatGraph(QuantizeNodes, float_graph_def,
151                                     float_inputs, float_inputs, output_names,
152                                     context, 2.0, quantized_graph_def);
153   }
154 
TestIgnoreOps(std::initializer_list<string> ops_to_ignore)155   void TestIgnoreOps(std::initializer_list<string> ops_to_ignore) {
156     auto root = tensorflow::Scope::NewRootScope();
157     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
158 
159     // A small helper to construct a Const op.
160     auto const_op = [&](const string& name, const TensorShape& shape,
161                         std::initializer_list<float> values) {
162       Tensor tensor(DT_FLOAT, shape);
163       test::FillValues<float>(&tensor, values);
164       return Const(root.WithOpName(name), Input::Initializer(tensor));
165     };
166 
167     // A simple graph with two different quantizable ops.
168     int m = 1;
169     int n = 1;
170     int k = 1;
171     Output a_op = const_op("a_op", {m, k}, {2});
172     Output b_op = const_op("b_op", {k, n}, {3});
173     Output c_op = const_op("c_op", {m, k}, {1});
174     Output d_op = const_op("d_op", {k, n}, {4});
175     Output mat_mul_op = MatMul(root.WithOpName("mat_mul_op"), a_op, b_op);
176     Output mul_op = Mul(root.WithOpName("mul"), c_op, d_op);
177 
178     GraphDef float_graph_def;
179     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
180 
181     TransformFuncContext context;
182     if (ops_to_ignore.size() > 0) {
183       context.params["ignore_op"] = ops_to_ignore;
184     }
185 
186     GraphDef quantized_graph_def;
187     TestTransformedVersusFloatGraph(QuantizeNodes, float_graph_def, {}, {},
188                                     {"mat_mul_op", "mul"}, context, 1.0,
189                                     &quantized_graph_def);
190 
191     // Make sure the quantized graph still contains the op that should have
192     // been ignored by QuantizeNodes.
193     for (const string& op_name : ops_to_ignore) {
194       bool exists_in_quantized_graph = false;
195       for (const NodeDef& node : quantized_graph_def.node()) {
196         if (node.op() == op_name) {
197           exists_in_quantized_graph = true;
198           break;
199         }
200       }
201       EXPECT_TRUE(exists_in_quantized_graph)
202           << "Op " << op_name
203           << " should not have been replace by a quantized version";
204     }
205   }
206 
TestQuantizeMatMul(int m,int n,int k,const std::vector<float> & a_values,const std::vector<float> & b_values)207   void TestQuantizeMatMul(int m, int n, int k,
208                           const std::vector<float>& a_values,
209                           const std::vector<float>& b_values) {
210     auto root = tensorflow::Scope::NewRootScope();
211     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
212 
213     Tensor a_tensor(DT_FLOAT, TensorShape({m, k}));
214     test::FillValues<float>(&a_tensor, a_values);
215     Output a_op = Const(root.WithOpName("a_op"), Input::Initializer(a_tensor));
216 
217     Tensor b_tensor(DT_FLOAT, TensorShape({k, n}));
218     test::FillValues<float>(&b_tensor, b_values);
219     Output b_op = Const(root.WithOpName("b_op"), Input::Initializer(b_tensor));
220 
221     Output mat_mul_op = MatMul(root.WithOpName("mat_mul_op"), a_op, b_op);
222 
223     GraphDef float_graph_def;
224     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
225 
226     TestQuantizedVersusFloatGraph(float_graph_def, {}, {"mat_mul_op"});
227   }
228 
TestQuantizeMatMulTiny()229   void TestQuantizeMatMulTiny() {
230     // These tests are added to test the generate case where
231     // min(matrix) == max(matrix), which used to cause problems.
232     TestQuantizeMatMul(1, 1, 1, {2}, {3});
233     TestQuantizeMatMul(1, 2, 1, {1}, {2, 3});
234     TestQuantizeMatMul(1, 1, 2, {1, 1}, {1, 1});
235     TestQuantizeMatMul(1, 1, 2, {0, 0}, {1, 1});
236     // The general case.
237     TestQuantizeMatMul(1, 1, 2, {1, 2}, {1, 2});
238   }
239 
TestQuantizeMatMulSmall()240   void TestQuantizeMatMulSmall() {
241     TestQuantizeMatMul(2, 4, 3, {1, 2, 3, 4, 5, 6},
242                        {7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18});
243   }
244 
TestQuantizeMul()245   void TestQuantizeMul() {
246     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
247 
248     std::vector<int64_t> x_shape({10, 100});
249     const size_t x_num_elements = TensorShape(x_shape).num_elements();
250     std::vector<float> x_values(x_num_elements);
251     for (int i = 0; i < x_num_elements; ++i) {
252       x_values[i] = (i % 256) / 256.0f;
253     }
254 
255     std::vector<int64_t> y_shape({100});
256     const size_t y_num_elements = TensorShape(y_shape).num_elements();
257     std::vector<float> y_values(y_num_elements);
258     for (int i = 0; i < y_num_elements; ++i) {
259       y_values[i] = ((i + 23) % 123) - 50;
260     }
261 
262     Scope root = Scope::NewRootScope();
263 
264     Tensor x_float_tensor(DT_FLOAT, TensorShape(x_shape));
265     test::FillValues<float>(&x_float_tensor, x_values);
266     Output x = Const(root.WithOpName("x"), Input::Initializer(x_float_tensor));
267 
268     Tensor y_float_tensor(DT_FLOAT, TensorShape(y_shape));
269     test::FillValues<float>(&y_float_tensor, y_values);
270     Output y = Const(root.WithOpName("y"), Input::Initializer(y_float_tensor));
271 
272     Mul mul = Mul(root.WithOpName("mul"), x, y);
273 
274     GraphDef float_graph_def;
275     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
276 
277     TestQuantizedVersusFloatGraph(float_graph_def, {}, {"mul"});
278   }
279 
TestQuantizeAdd()280   void TestQuantizeAdd() {
281     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
282 
283     std::vector<int64_t> x_shape({10, 100});
284     const size_t x_num_elements = TensorShape(x_shape).num_elements();
285     std::vector<float> x_values(x_num_elements);
286     for (int i = 0; i < x_num_elements; ++i) {
287       x_values[i] = (i % 256) / 256.0f;
288     }
289 
290     std::vector<int64_t> y_shape({100});
291     const size_t y_num_elements = TensorShape(y_shape).num_elements();
292     std::vector<float> y_values(y_num_elements);
293     for (int i = 0; i < y_num_elements; ++i) {
294       y_values[i] = ((i + 23) % 123) - 50;
295     }
296 
297     Scope root = Scope::NewRootScope();
298 
299     Tensor x_float_tensor(DT_FLOAT, TensorShape(x_shape));
300     test::FillValues<float>(&x_float_tensor, x_values);
301     Output x = Const(root.WithOpName("x"), Input::Initializer(x_float_tensor));
302 
303     Tensor y_float_tensor(DT_FLOAT, TensorShape(y_shape));
304     test::FillValues<float>(&y_float_tensor, y_values);
305     Output y = Const(root.WithOpName("y"), Input::Initializer(y_float_tensor));
306 
307     Add add = Add(root.WithOpName("add"), x, y);
308 
309     GraphDef float_graph_def;
310     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
311 
312     TestQuantizedVersusFloatGraph(float_graph_def, {}, {"add"});
313   }
314 
TestQuantizeConv2D(int depth,int input_width,int input_height,int input_batch_count,int filter_size,int filter_count,int stride,const string & padding,const std::vector<float> & input_values,const std::vector<float> & filter_values)315   void TestQuantizeConv2D(int depth, int input_width, int input_height,
316                           int input_batch_count, int filter_size,
317                           int filter_count, int stride, const string& padding,
318                           const std::vector<float>& input_values,
319                           const std::vector<float>& filter_values) {
320     auto root = tensorflow::Scope::NewRootScope();
321     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
322 
323     Tensor input_tensor(DT_FLOAT, TensorShape({input_batch_count, input_height,
324                                                input_width, depth}));
325     test::FillValues<float>(&input_tensor, input_values);
326     Output input_op =
327         Const(root.WithOpName("input_op"), Input::Initializer(input_tensor));
328 
329     Tensor filter_tensor(
330         DT_FLOAT, TensorShape({filter_size, filter_size, depth, filter_count}));
331     test::FillValues<float>(&filter_tensor, filter_values);
332     Output filter_op =
333         Const(root.WithOpName("filter_op"), Input::Initializer(filter_tensor));
334 
335     Output conv_op = Conv2D(root.WithOpName("conv_op"), input_op, filter_op,
336                             {1, stride, stride, 1}, padding);
337 
338     GraphDef float_graph_def;
339     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
340 
341     TestQuantizedVersusFloatGraph(float_graph_def, {}, {"conv_op"});
342   }
343 
TestQuantizeBiasAdd()344   void TestQuantizeBiasAdd() {
345     auto root = tensorflow::Scope::NewRootScope();
346     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
347 
348     Tensor input_tensor(DT_FLOAT, TensorShape({1, 1, 2, 6}));
349     test::FillIota<float>(&input_tensor, 1);
350     Output input_op =
351         Const(root.WithOpName("input_op"), Input::Initializer(input_tensor));
352 
353     Tensor offset_tensor(DT_FLOAT, TensorShape({6}));
354     test::FillIota<float>(&offset_tensor, 1);
355     Output offset_op =
356         Const(root.WithOpName("offset_op"), Input::Initializer(offset_tensor));
357 
358     Output bias_add_op =
359         BiasAdd(root.WithOpName("bias_add_op"), input_op, offset_op);
360 
361     GraphDef float_graph_def;
362     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
363 
364     TestQuantizedVersusFloatGraph(float_graph_def, {}, {"bias_add_op"});
365   }
366 
TestQuantizeConcat()367   void TestQuantizeConcat() {
368     auto root = tensorflow::Scope::NewRootScope();
369     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
370 
371     Tensor shape_tensor(DT_INT32, TensorShape({}));
372     test::FillValues<int32>(&shape_tensor, {0});
373     Output shape_op =
374         Const(root.WithOpName("shape_op"), Input::Initializer(shape_tensor));
375 
376     Tensor a_tensor(DT_FLOAT, TensorShape({2, 2, 3}));
377     test::FillValues<float>(&a_tensor, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12});
378     Output a_op = Const(root.WithOpName("a_op"), Input::Initializer(a_tensor));
379 
380     Tensor b_tensor(DT_FLOAT, TensorShape({2, 2, 3}));
381     test::FillValues<float>(&b_tensor,
382                             {13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24});
383     Output b_op = Const(root.WithOpName("b_op"), Input::Initializer(b_tensor));
384 
385     Output concat_op =
386         Concat(root.WithOpName("concat_op"), {a_op, b_op}, shape_op);
387 
388     GraphDef float_graph_def;
389     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
390 
391     TestQuantizedVersusFloatGraph(float_graph_def, {}, {"concat_op"});
392   }
393 
TestQuantizeRelu()394   void TestQuantizeRelu() {
395     auto root = tensorflow::Scope::NewRootScope();
396     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
397 
398     Tensor constant_tensor(DT_FLOAT, TensorShape({1, 2, 6, 1}));
399     test::FillValues<float>(&constant_tensor,
400                             {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12});
401     Output constant_op = Const(root.WithOpName("constant_op"),
402                                Input::Initializer(constant_tensor));
403 
404     Output relu_op = Relu(root.WithOpName("relu_op"), constant_op);
405 
406     GraphDef float_graph_def;
407     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
408 
409     TestQuantizedVersusFloatGraph(float_graph_def, {}, {"relu_op"});
410   }
411 
TestQuantizeRelu6()412   void TestQuantizeRelu6() {
413     auto root = tensorflow::Scope::NewRootScope();
414     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
415 
416     Tensor constant_tensor(DT_FLOAT, TensorShape({1, 2, 6, 1}));
417     test::FillValues<float>(&constant_tensor,
418                             {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12});
419     Output constant_op = Const(root.WithOpName("constant_op"),
420                                Input::Initializer(constant_tensor));
421 
422     Output relu6_op = Relu6(root.WithOpName("relu6_op"), constant_op);
423 
424     GraphDef float_graph_def;
425     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
426 
427     TestQuantizedVersusFloatGraph(float_graph_def, {}, {"relu6_op"});
428   }
429 
TestQuantizeMaxPool()430   void TestQuantizeMaxPool() {
431     auto root = tensorflow::Scope::NewRootScope();
432     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
433 
434     Tensor constant_tensor(DT_FLOAT, TensorShape({1, 2, 6, 1}));
435     test::FillValues<float>(&constant_tensor,
436                             {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12});
437     Output constant_op = Const(root.WithOpName("constant_op"),
438                                Input::Initializer(constant_tensor));
439 
440     Output max_pool_op = MaxPool(root.WithOpName("max_pool_op"), constant_op,
441                                  {1, 2, 2, 1}, {1, 1, 1, 1}, "SAME");
442 
443     GraphDef float_graph_def;
444     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
445 
446     TestQuantizedVersusFloatGraph(float_graph_def, {}, {"max_pool_op"});
447   }
448 
TestQuantizeAvgPool()449   void TestQuantizeAvgPool() {
450     auto root = tensorflow::Scope::NewRootScope();
451     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
452 
453     Tensor constant_tensor(DT_FLOAT, TensorShape({1, 2, 6, 1}));
454     test::FillValues<float>(&constant_tensor,
455                             {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12});
456     Output constant_op = Const(root.WithOpName("constant_op"),
457                                Input::Initializer(constant_tensor));
458 
459     Output avg_pool_op = AvgPool(root.WithOpName("avg_pool_op"), constant_op,
460                                  {1, 2, 2, 1}, {1, 1, 1, 1}, "SAME");
461 
462     GraphDef float_graph_def;
463     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
464 
465     TestQuantizedVersusFloatGraph(float_graph_def, {}, {"avg_pool_op"});
466   }
467 
TestQuantizeReshape()468   void TestQuantizeReshape() {
469     auto root = tensorflow::Scope::NewRootScope();
470     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
471 
472     Tensor constant_tensor(DT_FLOAT, TensorShape({4, 5}));
473     test::FillValues<float>(&constant_tensor,
474                             {1,  2,  3,  4,  5,  6,  7,  8,  9,  10,
475                              11, 12, 13, 14, 15, 16, 17, 18, 19, 20});
476     Output constant_op = Const(root.WithOpName("constant_op"),
477                                Input::Initializer(constant_tensor));
478 
479     Output reshape_op =
480         Reshape(root.WithOpName("reshape_op"), constant_op, {10, 2});
481 
482     GraphDef float_graph_def;
483     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
484 
485     TestQuantizedVersusFloatGraph(float_graph_def, {}, {"reshape_op"});
486   }
487 
TestRemoveRedundantQuantization()488   void TestRemoveRedundantQuantization() {
489     auto root = tensorflow::Scope::NewRootScope();
490     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
491 
492     Tensor quantized_tensor(DT_QUINT8, TensorShape({}));
493     test::FillValues<quint8>(&quantized_tensor, {0});
494     Output quantized_op = Const(root.WithOpName("quantized_op"),
495                                 Input::Initializer(quantized_tensor));
496 
497     Tensor quantized_min_tensor(DT_FLOAT, TensorShape({}));
498     test::FillValues<float>(&quantized_min_tensor, {2.0f});
499     Output quantized_min_op = Const(root.WithOpName("quantized_min_op"),
500                                     Input::Initializer(quantized_min_tensor));
501 
502     Tensor quantized_max_tensor(DT_FLOAT, TensorShape({}));
503     test::FillValues<float>(&quantized_max_tensor, {2.0f});
504     Output quantized_max_op = Const(root.WithOpName("quantized_max_op"),
505                                     Input::Initializer(quantized_min_tensor));
506 
507     Output dequantize_op =
508         Dequantize(root.WithOpName("dequantize_op"), quantized_op,
509                    quantized_min_op, quantized_max_op);
510 
511     Tensor dequantize_reshape_dims_tensor(DT_INT32, TensorShape({1}));
512     test::FillValues<int32>(&dequantize_reshape_dims_tensor, {-1});
513     Output dequantize_reshape_dims =
514         Const(root.WithOpName("dequantize_reshape_dims"),
515               Input::Initializer(dequantize_reshape_dims_tensor));
516 
517     Tensor dequantize_reduction_dims_tensor(DT_INT32, TensorShape({}));
518     test::FillValues<int32>(&dequantize_reduction_dims_tensor, {0});
519     Output dequantize_reduction_dims =
520         Const(root.WithOpName("dequantize_reduction_dims"),
521               Input::Initializer(dequantize_reduction_dims_tensor));
522 
523     Output dequantize_reshape = Reshape(root.WithOpName("dequantize_reshape"),
524                                         dequantize_op, dequantize_reshape_dims);
525 
526     Output dequantize_min =
527         Min(root.WithOpName("dequantize_min"), dequantize_reshape,
528             dequantize_reduction_dims, Min::Attrs().KeepDims(false));
529 
530     Output dequantize_max =
531         Max(root.WithOpName("dequantize_max"), dequantize_reshape,
532             dequantize_reduction_dims, Max::Attrs().KeepDims(false));
533 
534     QuantizeV2 quantize_op(root.WithOpName("quantize_op"), dequantize_op,
535                            dequantize_min, dequantize_max, DT_QUINT8,
536                            QuantizeV2::Attrs().Mode("MIN_FIRST"));
537 
538     Output final_dequantize =
539         Dequantize(root.WithOpName("final_dequantize"), quantize_op.output,
540                    quantize_op.output_min, quantize_op.output_max);
541 
542     GraphDef float_graph_def;
543     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
544 
545     GraphDef removed_graph_def;
546     TestTransformedVersusFloatGraph(
547         RemoveRedundantQuantizations, float_graph_def, {}, {},
548         {"final_dequantize"}, {}, 1.0, &removed_graph_def);
549 
550     std::map<string, const NodeDef*> node_map;
551     MapNamesToNodes(removed_graph_def, &node_map);
552     EXPECT_EQ(1, node_map.count("final_dequantize"));
553     EXPECT_EQ("quantized_op", node_map.at("final_dequantize")->input(0));
554   }
555 
TestRemoveRedundantQuantizationWithBiasAdd()556   void TestRemoveRedundantQuantizationWithBiasAdd() {
557     auto root = tensorflow::Scope::NewRootScope();
558     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
559 
560     Tensor quantized_tensor(DT_QUINT8, TensorShape({1, 6}));
561     test::FillValues<quint8>(&quantized_tensor, {0, 0, 0, 0, 0, 0});
562     Output quantized_op = Const(root.WithOpName("quantized_op"),
563                                 Input::Initializer(quantized_tensor));
564 
565     Tensor quantized_min_tensor(DT_FLOAT, TensorShape({}));
566     test::FillValues<float>(&quantized_min_tensor, {2.0f});
567     Output quantized_min_op = Const(root.WithOpName("quantized_min_op"),
568                                     Input::Initializer(quantized_min_tensor));
569 
570     Tensor quantized_max_tensor(DT_FLOAT, TensorShape({}));
571     test::FillValues<float>(&quantized_max_tensor, {2.0f});
572     Output quantized_max_op = Const(root.WithOpName("quantized_max_op"),
573                                     Input::Initializer(quantized_min_tensor));
574 
575     Tensor offset_tensor(DT_QUINT8, TensorShape({6}));
576     test::FillValues<quint8>(&offset_tensor, {1, 2, 3, 4, 5, 6});
577     Output offset_op =
578         Const(root.WithOpName("offset_op"), Input::Initializer(offset_tensor));
579 
580     Tensor offset_min_tensor(DT_FLOAT, TensorShape({}));
581     test::FillValues<float>(&offset_min_tensor, {0.0f});
582     Output offset_min_op = Const(root.WithOpName("offset_min_op"),
583                                  Input::Initializer(offset_min_tensor));
584 
585     Tensor offset_max_tensor(DT_FLOAT, TensorShape({}));
586     test::FillValues<float>(&offset_max_tensor, {255.0f});
587     Output offset_max_op = Const(root.WithOpName("offset_max_op"),
588                                  Input::Initializer(offset_max_tensor));
589 
590     QuantizedBiasAdd quantized_bias_add_op(
591         root.WithOpName("bias_add_op"), quantized_op, offset_op,
592         quantized_min_op, quantized_max_op, offset_min_op, offset_max_op,
593         DT_QINT32);
594 
595     RequantizationRange requantization_range_op(
596         root.WithOpName("requantization_range_op"),
597         quantized_bias_add_op.output, quantized_bias_add_op.min_out,
598         quantized_bias_add_op.max_out);
599 
600     Requantize requantize_op(
601         root.WithOpName("requantize_op"), quantized_bias_add_op.output,
602         quantized_bias_add_op.min_out, quantized_bias_add_op.max_out,
603         requantization_range_op.output_min, requantization_range_op.output_max,
604         DT_QUINT8);
605 
606     Output dequantize_op =
607         Dequantize(root.WithOpName("dequantize_op"), requantize_op.output,
608                    requantize_op.output_min, requantize_op.output_max);
609 
610     Tensor dequantize_reshape_dims_tensor(DT_INT32, TensorShape({1}));
611     test::FillValues<int32>(&dequantize_reshape_dims_tensor, {-1});
612     Output dequantize_reshape_dims =
613         Const(root.WithOpName("dequantize_reshape_dims"),
614               Input::Initializer(dequantize_reshape_dims_tensor));
615 
616     Tensor dequantize_reduction_dims_tensor(DT_INT32, TensorShape({}));
617     test::FillValues<int32>(&dequantize_reduction_dims_tensor, {0});
618     Output dequantize_reduction_dims =
619         Const(root.WithOpName("dequantize_reduction_dims"),
620               Input::Initializer(dequantize_reduction_dims_tensor));
621 
622     Output dequantize_reshape = Reshape(root.WithOpName("dequantize_reshape"),
623                                         dequantize_op, dequantize_reshape_dims);
624 
625     Output dequantize_min =
626         Min(root.WithOpName("dequantize_min"), dequantize_reshape,
627             dequantize_reduction_dims, Min::Attrs().KeepDims(false));
628 
629     Output dequantize_max =
630         Max(root.WithOpName("dequantize_max"), dequantize_reshape,
631             dequantize_reduction_dims, Max::Attrs().KeepDims(false));
632 
633     QuantizeV2 quantize_op(root.WithOpName("quantize_op"), dequantize_op,
634                            dequantize_min, dequantize_max, DT_QUINT8,
635                            QuantizeV2::Attrs().Mode("MIN_FIRST"));
636 
637     Output final_dequantize =
638         Dequantize(root.WithOpName("final_dequantize"), quantize_op.output,
639                    quantize_op.output_min, quantize_op.output_max);
640 
641     GraphDef float_graph_def;
642     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
643 
644     GraphDef removed_graph_def;
645     TestTransformedVersusFloatGraph(
646         RemoveRedundantQuantizations, float_graph_def, {}, {},
647         {"final_dequantize"}, {}, 1.0, &removed_graph_def);
648 
649     std::map<string, const NodeDef*> node_map;
650     MapNamesToNodes(removed_graph_def, &node_map);
651     EXPECT_EQ(1, node_map.count("final_dequantize"));
652     EXPECT_EQ("requantize_op", node_map.at("final_dequantize")->input(0));
653   }
654 
TestQuantizeResizeBilinear()655   void TestQuantizeResizeBilinear() {
656     auto root = tensorflow::Scope::NewRootScope();
657     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
658 
659     Tensor size_tensor(DT_INT32, TensorShape({2}));
660     test::FillValues<int32>(&size_tensor, {256, 256});
661 
662     Output constant_op = Const(root.WithOpName("size_tensor_op"),
663                                Input::Initializer(size_tensor));
664 
665     Output placeholder_op =
666         Placeholder(root.WithOpName("placeholder_op"), DT_FLOAT);
667 
668     Output resize_bilinear_op = ResizeBilinear(
669         root.WithOpName("resize_bilinear_op"), placeholder_op, constant_op);
670 
671     GraphDef float_graph_def;
672     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
673 
674     Tensor input_tensor(DT_FLOAT, {1, 128, 128, 3});
675     test::FillFn<float>(&input_tensor, [](int) { return 100.0f; });
676 
677     TestQuantizedVersusFloatGraph(float_graph_def,
678                                   {{"placeholder_op", input_tensor}},
679                                   {"resize_bilinear_op"});
680   }
681 
TestRemoveRedundantQuantizationWithMultipleOutputs()682   void TestRemoveRedundantQuantizationWithMultipleOutputs() {
683     auto root = tensorflow::Scope::NewRootScope();
684     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
685 
686     Tensor quantized_tensor(DT_QUINT8, TensorShape({1, 6}));
687     test::FillValues<quint8>(&quantized_tensor, {0, 0, 0, 0, 0, 0});
688     Output quantized_op = Const(root.WithOpName("quantized_op"),
689                                 Input::Initializer(quantized_tensor));
690 
691     Tensor quantized_min_tensor(DT_FLOAT, TensorShape({}));
692     test::FillValues<float>(&quantized_min_tensor, {2.0f});
693     Output quantized_min_op = Const(root.WithOpName("quantized_min_op"),
694                                     Input::Initializer(quantized_min_tensor));
695 
696     Tensor quantized_max_tensor(DT_FLOAT, TensorShape({}));
697     test::FillValues<float>(&quantized_max_tensor, {2.0f});
698     Output quantized_max_op = Const(root.WithOpName("quantized_max_op"),
699                                     Input::Initializer(quantized_min_tensor));
700 
701     Tensor offset_tensor(DT_QUINT8, TensorShape({6}));
702     test::FillValues<quint8>(&offset_tensor, {1, 2, 3, 4, 5, 6});
703     Output offset_op =
704         Const(root.WithOpName("offset_op"), Input::Initializer(offset_tensor));
705 
706     Tensor offset_min_tensor(DT_FLOAT, TensorShape({}));
707     test::FillValues<float>(&offset_min_tensor, {0.0f});
708     Output offset_min_op = Const(root.WithOpName("offset_min_op"),
709                                  Input::Initializer(offset_min_tensor));
710 
711     Tensor offset_max_tensor(DT_FLOAT, TensorShape({}));
712     test::FillValues<float>(&offset_max_tensor, {255.0f});
713     Output offset_max_op = Const(root.WithOpName("offset_max_op"),
714                                  Input::Initializer(offset_max_tensor));
715 
716     QuantizedBiasAdd quantized_bias_add_op(
717         root.WithOpName("bias_add_op"), quantized_op, offset_op,
718         quantized_min_op, quantized_max_op, offset_min_op, offset_max_op,
719         DT_QINT32);
720 
721     RequantizationRange requantization_range_op(
722         root.WithOpName("requantization_range_op"),
723         quantized_bias_add_op.output, quantized_bias_add_op.min_out,
724         quantized_bias_add_op.max_out);
725 
726     Requantize requantize_op(
727         root.WithOpName("requantize_op"), quantized_bias_add_op.output,
728         quantized_bias_add_op.min_out, quantized_bias_add_op.max_out,
729         requantization_range_op.output_min, requantization_range_op.output_max,
730         DT_QUINT8);
731 
732     Output dequantize_op =
733         Dequantize(root.WithOpName("dequantize_op"), requantize_op.output,
734                    requantize_op.output_min, requantize_op.output_max);
735 
736     Tensor dequantize_reshape_dims_tensor(DT_INT32, TensorShape({1}));
737     test::FillValues<int32>(&dequantize_reshape_dims_tensor, {-1});
738     Output dequantize_reshape_dims =
739         Const(root.WithOpName("dequantize_reshape_dims"),
740               Input::Initializer(dequantize_reshape_dims_tensor));
741 
742     Tensor dequantize_reduction_dims_tensor(DT_INT32, TensorShape({}));
743     test::FillValues<int32>(&dequantize_reduction_dims_tensor, {0});
744     Output dequantize_reduction_dims =
745         Const(root.WithOpName("dequantize_reduction_dims"),
746               Input::Initializer(dequantize_reduction_dims_tensor));
747 
748     Output dequantize_reshape = Reshape(root.WithOpName("dequantize_reshape"),
749                                         dequantize_op, dequantize_reshape_dims);
750 
751     Output dequantize_min =
752         Min(root.WithOpName("dequantize_min"), dequantize_reshape,
753             dequantize_reduction_dims, Min::Attrs().KeepDims(false));
754 
755     Output dequantize_max =
756         Max(root.WithOpName("dequantize_max"), dequantize_reshape,
757             dequantize_reduction_dims, Max::Attrs().KeepDims(false));
758 
759     QuantizeV2 quantize_op(root.WithOpName("quantize_op"), dequantize_op,
760                            dequantize_min, dequantize_max, DT_QUINT8,
761                            QuantizeV2::Attrs().Mode("MIN_FIRST"));
762 
763     Output final_dequantize =
764         Dequantize(root.WithOpName("final_dequantize"), quantize_op.output,
765                    quantize_op.output_min, quantize_op.output_max);
766 
767     Output relu_op = Relu(root.WithOpName("relu_op"), dequantize_op);
768 
769     GraphDef float_graph_def;
770     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
771 
772     GraphDef removed_graph_def;
773     TestTransformedVersusFloatGraph(
774         RemoveRedundantQuantizations, float_graph_def, {}, {},
775         {"final_dequantize", "relu_op"}, {}, 1.0, &removed_graph_def);
776 
777     std::map<string, int> op_type_count;
778     for (const NodeDef& node : removed_graph_def.node()) {
779       ++op_type_count[node.op()];
780     }
781     EXPECT_EQ(2, op_type_count["Dequantize"]);
782   }
783 
TestQuantizePlaceholders()784   void TestQuantizePlaceholders() {
785     auto root = tensorflow::Scope::NewRootScope();
786     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
787 
788     Output placeholder_op =
789         Placeholder(root.WithOpName("placeholder_op"), DT_FLOAT);
790 
791     Output relu_op = Relu(root.WithOpName("relu_op"), placeholder_op);
792 
793     GraphDef float_graph_def;
794     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
795 
796     TransformFuncContext context;
797     context.input_names = {"placeholder_op"};
798     context.output_names = {"relu_op"};
799     context.params = {{"input_min", {"-10.0"}}, {"input_max", {"10.0"}}};
800 
801     GraphDef quantized_graph_def;
802     TF_ASSERT_OK(
803         QuantizePlaceholders(float_graph_def, context, &quantized_graph_def));
804 
805     Tensor input_tensor(DT_FLOAT, {});
806     input_tensor.flat<float>()(0) = 5.0f;
807 
808     TestQuantizedVersusFloatGraph(
809         float_graph_def, {{"placeholder_op", input_tensor}}, {"relu_op"});
810 
811     std::map<string, const NodeDef*> node_map;
812     MapNamesToNodes(quantized_graph_def, &node_map);
813     EXPECT_NE("placeholder_op", node_map.at("relu_op")->input(0));
814     EXPECT_EQ("Placeholder", node_map.at("placeholder_op")->op());
815     EXPECT_EQ(DT_QUINT8,
816               node_map.at("placeholder_op")->attr().at("dtype").type());
817   }
818 
TestInputRange()819   void TestInputRange() {
820     auto root = tensorflow::Scope::NewRootScope();
821     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
822 
823     const int width = 100;
824 
825     Tensor a_data(DT_FLOAT, TensorShape({1, width}));
826     test::FillIota<float>(&a_data, 1.0f);
827     Output a_const = Const(root.WithOpName("a"), Input::Initializer(a_data));
828 
829     Output placeholder = Placeholder(root.WithOpName("placeholder"), DT_FLOAT);
830 
831     Output bias_add =
832         BiasAdd(root.WithOpName("bias_add"), a_const, placeholder);
833 
834     GraphDef graph_def;
835     TF_ASSERT_OK(root.ToGraphDef(&graph_def));
836 
837     Tensor placeholder_tensor(DT_FLOAT, TensorShape({width}));
838     test::FillIota<float>(&placeholder_tensor, 1.0f);
839 
840     TestGraphWithInputRange(graph_def, {{"placeholder", placeholder_tensor}},
841                             {"bias_add"}, 0.0f, 100.0f);
842   }
843 
TestFallbackRange()844   void TestFallbackRange() {
845     auto root = tensorflow::Scope::NewRootScope();
846     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
847 
848     const int width = 100;
849 
850     Tensor a_data(DT_FLOAT, TensorShape({1, width}));
851     test::FillIota<float>(&a_data, 1.0f);
852     Output a_const = Const(root.WithOpName("a"), Input::Initializer(a_data));
853 
854     Output placeholder = Placeholder(root.WithOpName("placeholder"), DT_FLOAT);
855 
856     Output bias_add =
857         BiasAdd(root.WithOpName("bias_add"), a_const, placeholder);
858 
859     GraphDef graph_def;
860     TF_ASSERT_OK(root.ToGraphDef(&graph_def));
861 
862     Tensor placeholder_tensor(DT_FLOAT, TensorShape({width}));
863     test::FillIota<float>(&placeholder_tensor, 1.0f);
864 
865     GraphDef quantized_graph_def;
866     TestGraphWithFallbackRange(graph_def, {{"placeholder", placeholder_tensor}},
867                                {"bias_add"}, 0.0f, 200.0f,
868                                &quantized_graph_def);
869 
870     for (const NodeDef& node : quantized_graph_def.node()) {
871       EXPECT_NE("RequantizationRange", node.op());
872     }
873   }
874 
TestConvertFakeQuantsToRequantize()875   void TestConvertFakeQuantsToRequantize() {
876     auto root = tensorflow::Scope::NewRootScope();
877     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
878 
879     Tensor input_tensor(DT_FLOAT, TensorShape({1, 1, 2, 6}));
880     test::FillIota<float>(&input_tensor, 1);
881     Output input_op =
882         Const(root.WithOpName("input_op"), Input::Initializer(input_tensor));
883 
884     Tensor offset_tensor(DT_FLOAT, TensorShape({6}));
885     test::FillIota<float>(&offset_tensor, 1);
886     Output offset_op =
887         Const(root.WithOpName("offset_op"), Input::Initializer(offset_tensor));
888 
889     Output bias_add_op =
890         BiasAdd(root.WithOpName("bias_add_op"), input_op, offset_op);
891 
892     Tensor fake_quant_min_tensor(DT_FLOAT, TensorShape({}));
893     test::FillValues<float>(&fake_quant_min_tensor, {0.0f});
894     Output fake_quant_min_op = Const(root.WithOpName("fake_quant_min_op"),
895                                      Input::Initializer(fake_quant_min_tensor));
896 
897     Tensor fake_quant_max_tensor(DT_FLOAT, TensorShape({}));
898     test::FillValues<float>(&fake_quant_max_tensor, {18.0f});
899     Output fake_quant_max_op = Const(root.WithOpName("fake_quant_max_op"),
900                                      Input::Initializer(fake_quant_max_tensor));
901 
902     Output fake_quant_op =
903         FakeQuantWithMinMaxVars(root.WithOpName("fake_quant_op"), bias_add_op,
904                                 fake_quant_min_op, fake_quant_max_op);
905 
906     GraphDef float_graph_def;
907     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
908 
909     GraphDef converted_graph_def;
910     TestTransformedVersusFloatGraph(ConvertFakeQuantsToRequantize,
911                                     float_graph_def, {}, {}, {"fake_quant_op"},
912                                     {}, 1.0, &converted_graph_def);
913 
914     for (const NodeDef& node : converted_graph_def.node()) {
915       EXPECT_NE("FakeQuantWithMinMaxVars", node.op());
916     }
917   }
918 
TestMergeAdjacentRequantizes()919   void TestMergeAdjacentRequantizes() {
920     auto root = tensorflow::Scope::NewRootScope();
921     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
922 
923     Tensor input_tensor(DT_QUINT8, TensorShape({1, 1, 2, 6}));
924     test::FillValues<quint8>(&input_tensor,
925                              {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12});
926     Output input_op =
927         Const(root.WithOpName("input_op"), Input::Initializer(input_tensor));
928 
929     Tensor input_min_tensor(DT_FLOAT, TensorShape({}));
930     test::FillValues<float>(&input_min_tensor, {0.0f});
931     Output input_min_op = Const(root.WithOpName("input_min_op"),
932                                 Input::Initializer(input_min_tensor));
933 
934     Tensor input_max_tensor(DT_FLOAT, TensorShape({}));
935     test::FillValues<float>(&input_max_tensor, {255.0f});
936     Output input_max_op = Const(root.WithOpName("input_max_op"),
937                                 Input::Initializer(input_max_tensor));
938 
939     Tensor offset_tensor(DT_QUINT8, TensorShape({6}));
940     test::FillValues<quint8>(&offset_tensor, {1, 2, 3, 4, 5, 6});
941     Output offset_op =
942         Const(root.WithOpName("offset_op"), Input::Initializer(offset_tensor));
943 
944     Tensor offset_min_tensor(DT_FLOAT, TensorShape({}));
945     test::FillValues<float>(&offset_min_tensor, {0.0f});
946     Output offset_min_op = Const(root.WithOpName("offset_min_op"),
947                                  Input::Initializer(offset_min_tensor));
948 
949     Tensor offset_max_tensor(DT_FLOAT, TensorShape({}));
950     test::FillValues<float>(&offset_max_tensor, {255.0f});
951     Output offset_max_op = Const(root.WithOpName("offset_max_op"),
952                                  Input::Initializer(offset_max_tensor));
953 
954     QuantizedBiasAdd quantized_bias_add_op(
955         root.WithOpName("quantized_bias_add_op"), input_op, offset_op,
956         input_min_op, input_max_op, offset_min_op, offset_max_op, DT_QINT32);
957 
958     RequantizationRange requantization_range_op(
959         root.WithOpName("requantization_range_op"),
960         quantized_bias_add_op.output, quantized_bias_add_op.min_out,
961         quantized_bias_add_op.max_out);
962 
963     Requantize requantize_op(
964         root.WithOpName("requantize_op"), quantized_bias_add_op.output,
965         quantized_bias_add_op.min_out, quantized_bias_add_op.max_out,
966         requantization_range_op.output_min, requantization_range_op.output_max,
967         DT_QUINT8);
968 
969     Output dequantize_op =
970         Dequantize(root.WithOpName("dequantize_op"), requantize_op.output,
971                    requantize_op.output_min, requantize_op.output_max,
972                    Dequantize::Attrs().Mode("MIN_FIRST"));
973 
974     Tensor quantize_min_tensor(DT_FLOAT, TensorShape({}));
975     test::FillValues<float>(&quantize_min_tensor, {0.0f});
976     Output quantize_min_op = Const(root.WithOpName("quantize_min_op"),
977                                    Input::Initializer(quantize_min_tensor));
978 
979     Tensor quantize_max_tensor(DT_FLOAT, TensorShape({}));
980     test::FillValues<float>(&quantize_max_tensor, {255.0f});
981     Output quantize_max_op = Const(root.WithOpName("quantize_max_op"),
982                                    Input::Initializer(quantize_max_tensor));
983 
984     QuantizeV2 quantize_op(root.WithOpName("quantize_op"), dequantize_op,
985                            quantize_min_op, quantize_max_op, DT_QINT32,
986                            QuantizeV2::Attrs().Mode("MIN_FIRST"));
987 
988     Tensor fake_requantize_min_tensor(DT_FLOAT, TensorShape({}));
989     test::FillValues<float>(&fake_requantize_min_tensor, {0.0f});
990     Output fake_requantize_min_op =
991         Const(root.WithOpName("fake_requantize_min_op"),
992               Input::Initializer(fake_requantize_min_tensor));
993 
994     Tensor fake_requantize_max_tensor(DT_FLOAT, TensorShape({}));
995     test::FillValues<float>(&fake_requantize_max_tensor, {255.0f});
996     Output fake_requantize_max_op =
997         Const(root.WithOpName("fake_requantize_max_op"),
998               Input::Initializer(fake_requantize_max_tensor));
999 
1000     Requantize fake_requantize_op(
1001         root.WithOpName("fake_requantize_op"), quantize_op.output,
1002         quantize_op.output_min, quantize_op.output_max, fake_requantize_min_op,
1003         fake_requantize_max_op, DT_QUINT8);
1004 
1005     Output fake_dequantize_op = Dequantize(
1006         root.WithOpName("fake_dequantize_op"), fake_requantize_op.output,
1007         fake_requantize_op.output_min, fake_requantize_op.output_max,
1008         Dequantize::Attrs().Mode("MIN_FIRST"));
1009 
1010     GraphDef float_graph_def;
1011     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
1012 
1013     GraphDef converted_graph_def;
1014     TestTransformedVersusFloatGraph(MergeAdjacentRequantizes, float_graph_def,
1015                                     {}, {}, {"fake_dequantize_op"}, {}, 1.0,
1016                                     &converted_graph_def);
1017 
1018     int requantize_count = 0;
1019     for (const NodeDef& node : converted_graph_def.node()) {
1020       if (node.op() == "Requantize") {
1021         ++requantize_count;
1022       }
1023     }
1024     EXPECT_EQ(1, requantize_count);
1025   }
1026 
TestConvertFakeQuantsEndToEnd()1027   void TestConvertFakeQuantsEndToEnd() {
1028     auto root = tensorflow::Scope::NewRootScope();
1029     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
1030 
1031     Tensor input_tensor(DT_FLOAT, TensorShape({1, 1, 2, 6}));
1032     test::FillIota<float>(&input_tensor, 1);
1033     Output input_op =
1034         Const(root.WithOpName("input_op"), Input::Initializer(input_tensor));
1035 
1036     Tensor offset_tensor(DT_FLOAT, TensorShape({6}));
1037     test::FillIota<float>(&offset_tensor, 1);
1038     Output offset_op =
1039         Const(root.WithOpName("offset_op"), Input::Initializer(offset_tensor));
1040 
1041     Output bias_add_op =
1042         BiasAdd(root.WithOpName("bias_add_op"), input_op, offset_op);
1043 
1044     Tensor fake_quant_min_tensor(DT_FLOAT, TensorShape({}));
1045     test::FillValues<float>(&fake_quant_min_tensor, {0.0f});
1046     Output fake_quant_min_op = Const(root.WithOpName("fake_quant_min_op"),
1047                                      Input::Initializer(fake_quant_min_tensor));
1048 
1049     Tensor fake_quant_max_tensor(DT_FLOAT, TensorShape({}));
1050     test::FillValues<float>(&fake_quant_max_tensor, {18.0f});
1051     Output fake_quant_max_op = Const(root.WithOpName("fake_quant_max_op"),
1052                                      Input::Initializer(fake_quant_max_tensor));
1053 
1054     Output fake_quant_op =
1055         FakeQuantWithMinMaxVars(root.WithOpName("fake_quant_op"), bias_add_op,
1056                                 fake_quant_min_op, fake_quant_max_op);
1057 
1058     GraphDef float_graph_def;
1059     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
1060 
1061     GraphDef converted_graph_def;
1062     TestTransformedVersusFloatGraph(QuantizeNodes, float_graph_def, {}, {},
1063                                     {"fake_quant_op"}, {}, 1.0,
1064                                     &converted_graph_def);
1065 
1066     int requantize_count = 0;
1067     for (const NodeDef& node : converted_graph_def.node()) {
1068       EXPECT_NE("FakeQuantWithMinMaxVars", node.op());
1069       if (node.op() == "Requantize") {
1070         ++requantize_count;
1071       }
1072     }
1073     EXPECT_EQ(1, requantize_count);
1074   }
1075 
TestHoistFakeQuants()1076   void TestHoistFakeQuants() {
1077     auto root = tensorflow::Scope::NewRootScope();
1078     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
1079 
1080     Tensor input_tensor(DT_FLOAT, TensorShape({1, 1, 2, 6}));
1081     test::FillIota<float>(&input_tensor, 1);
1082     Output input_op =
1083         Const(root.WithOpName("input_op"), Input::Initializer(input_tensor));
1084 
1085     Tensor offset_tensor(DT_FLOAT, TensorShape({6}));
1086     test::FillIota<float>(&offset_tensor, 1);
1087     Output offset_op =
1088         Const(root.WithOpName("offset_op"), Input::Initializer(offset_tensor));
1089 
1090     Output bias_add_op =
1091         BiasAdd(root.WithOpName("bias_add_op"), input_op, offset_op);
1092 
1093     Output relu_op = Relu(root.WithOpName("relu_op"), bias_add_op);
1094 
1095     Output max_pool_op = MaxPool(root.WithOpName("max_pool_op"), relu_op,
1096                                  {1, 2, 2, 1}, {1, 1, 1, 1}, "SAME");
1097 
1098     Tensor fake_quant_min_tensor(DT_FLOAT, TensorShape({}));
1099     test::FillValues<float>(&fake_quant_min_tensor, {0.0f});
1100     Output fake_quant_min_op = Const(root.WithOpName("fake_quant_min_op"),
1101                                      Input::Initializer(fake_quant_min_tensor));
1102 
1103     Tensor fake_quant_max_tensor(DT_FLOAT, TensorShape({}));
1104     test::FillValues<float>(&fake_quant_max_tensor, {18.0f});
1105     Output fake_quant_max_op = Const(root.WithOpName("fake_quant_max_op"),
1106                                      Input::Initializer(fake_quant_max_tensor));
1107 
1108     Output fake_quant_op =
1109         FakeQuantWithMinMaxVars(root.WithOpName("fake_quant_op"), max_pool_op,
1110                                 fake_quant_min_op, fake_quant_max_op);
1111 
1112     GraphDef float_graph_def;
1113     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
1114 
1115     GraphDef converted_graph_def;
1116     TestTransformedVersusFloatGraph(HoistFakeQuants, float_graph_def, {}, {},
1117                                     {"fake_quant_op"}, {}, 1.0,
1118                                     &converted_graph_def);
1119 
1120     std::map<string, const NodeDef*> node_map;
1121     MapNamesToNodes(converted_graph_def, &node_map);
1122     EXPECT_EQ("MaxPool", node_map.at("fake_quant_op")->op());
1123     EXPECT_EQ("FakeQuantWithMinMaxVars",
1124               node_map.at(node_map.at("relu_op")->input(0))->op());
1125   }
1126 
TestMergeDuplicateQuantizes()1127   void TestMergeDuplicateQuantizes() {
1128     auto root = tensorflow::Scope::NewRootScope();
1129     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
1130 
1131     Tensor quantized_tensor(DT_QUINT8, TensorShape({}));
1132     test::FillValues<quint8>(&quantized_tensor, {0});
1133     Output quantized_op = Const(root.WithOpName("quantized_op"),
1134                                 Input::Initializer(quantized_tensor));
1135 
1136     Tensor quantized_min_tensor(DT_FLOAT, TensorShape({}));
1137     test::FillValues<float>(&quantized_min_tensor, {2.0f});
1138     Output quantized_min_op = Const(root.WithOpName("quantized_min_op"),
1139                                     Input::Initializer(quantized_min_tensor));
1140 
1141     Tensor quantized_max_tensor(DT_FLOAT, TensorShape({}));
1142     test::FillValues<float>(&quantized_max_tensor, {2.0f});
1143     Output quantized_max_op = Const(root.WithOpName("quantized_max_op"),
1144                                     Input::Initializer(quantized_min_tensor));
1145 
1146     Output dequantize_op =
1147         Dequantize(root.WithOpName("dequantize_op"), quantized_op,
1148                    quantized_min_op, quantized_max_op);
1149 
1150     Tensor quantize_reshape_dims1_tensor(DT_INT32, TensorShape({1}));
1151     test::FillValues<int32>(&quantize_reshape_dims1_tensor, {-1});
1152     Output quantize_reshape_dims1 =
1153         Const(root.WithOpName("dequantize_reshape_dims1"),
1154               Input::Initializer(quantize_reshape_dims1_tensor));
1155 
1156     Tensor quantize_reduction_dims1_tensor(DT_INT32, TensorShape({}));
1157     test::FillValues<int32>(&quantize_reduction_dims1_tensor, {0});
1158     Output quantize_reduction_dims1 =
1159         Const(root.WithOpName("quantize_reduction_dims1"),
1160               Input::Initializer(quantize_reduction_dims1_tensor));
1161 
1162     Output quantize_reshape1 = Reshape(root.WithOpName("quantize_reshape1"),
1163                                        dequantize_op, quantize_reshape_dims1);
1164 
1165     Output quantize_min1 =
1166         Min(root.WithOpName("quantize_min1"), quantize_reshape1,
1167             quantize_reduction_dims1, Min::Attrs().KeepDims(false));
1168 
1169     Output quantize_max1 =
1170         Max(root.WithOpName("quantize_max1"), quantize_reshape1,
1171             quantize_reduction_dims1, Max::Attrs().KeepDims(false));
1172 
1173     QuantizeV2 quantize_op1(root.WithOpName("quantize_op1"), dequantize_op,
1174                             quantize_min1, quantize_max1, DT_QUINT8,
1175                             QuantizeV2::Attrs().Mode("MIN_FIRST"));
1176 
1177     Tensor quantize_reshape_dims2_tensor(DT_INT32, TensorShape({1}));
1178     test::FillValues<int32>(&quantize_reshape_dims2_tensor, {-1});
1179     Output quantize_reshape_dims2 =
1180         Const(root.WithOpName("dequantize_reshape_dims2"),
1181               Input::Initializer(quantize_reshape_dims2_tensor));
1182 
1183     Tensor quantize_reduction_dims2_tensor(DT_INT32, TensorShape({}));
1184     test::FillValues<int32>(&quantize_reduction_dims2_tensor, {0});
1185     Output quantize_reduction_dims2 =
1186         Const(root.WithOpName("quantize_reduction_dims2"),
1187               Input::Initializer(quantize_reduction_dims2_tensor));
1188 
1189     Output quantize_reshape2 = Reshape(root.WithOpName("quantize_reshape2"),
1190                                        dequantize_op, quantize_reshape_dims2);
1191 
1192     Output quantize_min2 =
1193         Min(root.WithOpName("quantize_min2"), quantize_reshape2,
1194             quantize_reduction_dims2, Min::Attrs().KeepDims(false));
1195 
1196     Output quantize_max2 =
1197         Max(root.WithOpName("quantize_max2"), quantize_reshape2,
1198             quantize_reduction_dims2, Max::Attrs().KeepDims(false));
1199 
1200     QuantizeV2 quantize_op2(root.WithOpName("quantize_op2"), dequantize_op,
1201                             quantize_min1, quantize_max1, DT_QUINT8,
1202                             QuantizeV2::Attrs().Mode("MIN_FIRST"));
1203 
1204     Output final_dequantize1 =
1205         Dequantize(root.WithOpName("final_dequantize1"), quantize_op1.output,
1206                    quantize_op1.output_min, quantize_op1.output_max);
1207 
1208     Output final_dequantize2 =
1209         Dequantize(root.WithOpName("final_dequantize2"), quantize_op2.output,
1210                    quantize_op2.output_min, quantize_op2.output_max);
1211 
1212     Output add_op =
1213         Add(root.WithOpName("add_op"), final_dequantize1, final_dequantize2);
1214 
1215     GraphDef float_graph_def;
1216     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
1217 
1218     GraphDef merged_graph_def;
1219     TestTransformedVersusFloatGraph(MergeDuplicateNodes, float_graph_def, {},
1220                                     {}, {"add_op"}, {}, 1.0, &merged_graph_def);
1221 
1222     std::map<string, int> op_map;
1223     for (const NodeDef& node : merged_graph_def.node()) {
1224       ++op_map[node.op()];
1225     }
1226     EXPECT_EQ(1, op_map["QuantizeV2"]);
1227   }
1228 
TestMergeDuplicateConsts()1229   void TestMergeDuplicateConsts() {
1230     auto root = tensorflow::Scope::NewRootScope();
1231     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
1232 
1233     const int width = 10;
1234 
1235     Tensor a_tensor(DT_FLOAT, TensorShape({width}));
1236     test::FillIota<float>(&a_tensor, 1.0f);
1237     Output a_op = Const(root.WithOpName("a_op"), Input::Initializer(a_tensor));
1238 
1239     Tensor b_tensor(DT_FLOAT, TensorShape({width}));
1240     test::FillIota<float>(&b_tensor, 1.0f);
1241     Output b_op = Const(root.WithOpName("b_op"), Input::Initializer(b_tensor));
1242 
1243     Output add_op = Add(root.WithOpName("add_op"), a_op, b_op);
1244 
1245     Tensor c_tensor(DT_FLOAT, TensorShape({width}));
1246     test::FillIota<float>(&c_tensor, 2.0f);
1247     Output c_op = Const(root.WithOpName("c_op"), Input::Initializer(c_tensor));
1248 
1249     Output mul_op = Mul(root.WithOpName("mul_op"), add_op, c_op);
1250 
1251     GraphDef float_graph_def;
1252     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
1253 
1254     GraphDef merged_graph_def;
1255     TestTransformedVersusFloatGraph(MergeDuplicateNodes, float_graph_def, {},
1256                                     {}, {"mul_op"}, {}, 1.0, &merged_graph_def);
1257 
1258     std::map<string, const NodeDef*> node_map;
1259     MapNamesToNodes(merged_graph_def, &node_map);
1260     EXPECT_EQ(1, (node_map.count("a_op") + node_map.count("b_op")));
1261     string remaining_const;
1262     if (node_map.count("a_op")) {
1263       remaining_const = "a_op";
1264     } else {
1265       remaining_const = "b_op";
1266     }
1267     EXPECT_EQ(remaining_const, node_map["add_op"]->input(0));
1268     EXPECT_EQ(remaining_const, node_map["add_op"]->input(1));
1269     EXPECT_EQ(1, node_map.count("c_op"));
1270     EXPECT_EQ("add_op", node_map["mul_op"]->input(0));
1271     EXPECT_EQ("c_op", node_map["mul_op"]->input(1));
1272   }
1273 
TestMergeDuplicatesNested()1274   void TestMergeDuplicatesNested() {
1275     auto root = tensorflow::Scope::NewRootScope();
1276     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
1277 
1278     const int width = 10;
1279 
1280     Tensor a_tensor(DT_FLOAT, TensorShape({width}));
1281     test::FillIota<float>(&a_tensor, 1.0f);
1282     Output a_op = Const(root.WithOpName("a_op"), Input::Initializer(a_tensor));
1283 
1284     Output a_relu_op = Relu(root.WithOpName("a_relu_op"), a_op);
1285 
1286     Tensor b_tensor(DT_FLOAT, TensorShape({width}));
1287     test::FillIota<float>(&b_tensor, 1.0f);
1288     Output b_op = Const(root.WithOpName("b_op"), Input::Initializer(b_tensor));
1289 
1290     Output b_relu_op = Relu(root.WithOpName("b_relu_op"), b_op);
1291 
1292     Output add_op = Add(root.WithOpName("add_op"), a_relu_op, b_relu_op);
1293 
1294     Tensor c_tensor(DT_FLOAT, TensorShape({width}));
1295     test::FillIota<float>(&c_tensor, 2.0f);
1296     Output c_op = Const(root.WithOpName("c_op"), Input::Initializer(c_tensor));
1297 
1298     Output mul_op = Mul(root.WithOpName("mul_op"), add_op, c_op);
1299 
1300     GraphDef float_graph_def;
1301     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
1302 
1303     GraphDef merged_graph_def;
1304     TestTransformedVersusFloatGraph(MergeDuplicateNodes, float_graph_def, {},
1305                                     {}, {"mul_op"}, {}, 1.0, &merged_graph_def);
1306 
1307     std::map<string, const NodeDef*> node_map;
1308     MapNamesToNodes(merged_graph_def, &node_map);
1309     EXPECT_EQ(1, (node_map.count("a_op") + node_map.count("b_op")));
1310     EXPECT_EQ(1, (node_map.count("a_relu_op") + node_map.count("b_relu_op")));
1311     string remaining_relu;
1312     if (node_map.count("a_relu_op")) {
1313       remaining_relu = "a_relu_op";
1314     } else {
1315       remaining_relu = "b_relu_op";
1316     }
1317     EXPECT_EQ(remaining_relu, node_map["add_op"]->input(0));
1318     EXPECT_EQ(remaining_relu, node_map["add_op"]->input(1));
1319     EXPECT_EQ(1, node_map.count("c_op"));
1320     EXPECT_EQ("add_op", node_map["mul_op"]->input(0));
1321     EXPECT_EQ("c_op", node_map["mul_op"]->input(1));
1322   }
1323 
TestMergeDuplicatesInOut()1324   void TestMergeDuplicatesInOut() {
1325     auto root = tensorflow::Scope::NewRootScope();
1326     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
1327 
1328     const int width = 10;
1329 
1330     Tensor a_tensor(DT_FLOAT, TensorShape({width}));
1331     test::FillIota<float>(&a_tensor, 1.0f);
1332     Output a_op = Const(root.WithOpName("a_op"), Input::Initializer(a_tensor));
1333 
1334     Output a_relu_op = Relu(root.WithOpName("a_relu_op"), a_op);
1335 
1336     Tensor b_tensor(DT_FLOAT, TensorShape({width}));
1337     test::FillIota<float>(&b_tensor, 1.0f);
1338     Output b_op = Const(root.WithOpName("b_op"), Input::Initializer(b_tensor));
1339 
1340     Output b_relu_op = Relu(root.WithOpName("b_relu_op"), b_op);
1341 
1342     Output add_op = Add(root.WithOpName("add_op"), a_relu_op, b_relu_op);
1343 
1344     Tensor c_tensor(DT_FLOAT, TensorShape({width}));
1345     test::FillIota<float>(&c_tensor, 2.0f);
1346     Output c_op = Const(root.WithOpName("c_op"), Input::Initializer(c_tensor));
1347 
1348     Output mul_op1 = Mul(root.WithOpName("mul_op1"), add_op, c_op);
1349     Output mul_op2 = Mul(root.WithOpName("mul_op2"), add_op, c_op);
1350     Output mul_op3 = Mul(root.WithOpName("mul_op3"), add_op, c_op);
1351 
1352     Output final_mul_op =
1353         Mul(root.WithOpName("final_mul_op"), mul_op2, mul_op3);
1354 
1355     GraphDef float_graph_def;
1356     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
1357 
1358     GraphDef merged_graph_def;
1359     TestTransformedVersusFloatGraph(MergeDuplicateNodes, float_graph_def,
1360                                     {{"a_op", a_tensor}}, {{"a_op", a_tensor}},
1361                                     {"mul_op1", "final_mul_op"}, {}, 1.0,
1362                                     &merged_graph_def);
1363 
1364     std::map<string, const NodeDef*> node_map;
1365     MapNamesToNodes(merged_graph_def, &node_map);
1366     EXPECT_EQ(1, node_map.count("a_op"));
1367     EXPECT_EQ(1, node_map.count("b_op"));
1368     EXPECT_EQ(1, node_map.count("a_relu_op"));
1369     EXPECT_EQ(1, node_map.count("b_relu_op"));
1370     EXPECT_EQ(1, node_map.count("mul_op1"));
1371     EXPECT_EQ(1, node_map.count("final_mul_op"));
1372     EXPECT_EQ(1, (node_map.count("mul_op2") + node_map.count("mul_op3")));
1373     string remaining_mul;
1374     if (node_map.count("mul_op2")) {
1375       remaining_mul = "mul_op2";
1376     } else {
1377       remaining_mul = "mul_op3";
1378     }
1379     EXPECT_EQ(remaining_mul, node_map["final_mul_op"]->input(0));
1380     EXPECT_EQ(remaining_mul, node_map["final_mul_op"]->input(1));
1381     EXPECT_EQ(1, node_map.count("c_op"));
1382     EXPECT_EQ("add_op", node_map["mul_op1"]->input(0));
1383     EXPECT_EQ("c_op", node_map["mul_op1"]->input(1));
1384   }
1385 
TestExcludeNonFloat()1386   void TestExcludeNonFloat() {
1387     auto root = tensorflow::Scope::NewRootScope();
1388     using namespace ::tensorflow::ops;  // NOLINT(build/namespaces)
1389 
1390     Tensor int_constant_tensor(DT_INT32, TensorShape({4, 5}));
1391     test::FillIota<int32>(&int_constant_tensor, 1);
1392     Output int_constant = Const(root.WithOpName("int_constant"),
1393                                 Input::Initializer(int_constant_tensor));
1394 
1395     Tensor float_constant_tensor(DT_FLOAT, TensorShape({4, 5}));
1396     test::FillIota<float>(&float_constant_tensor, 2.0f);
1397     Output float_constant = Const(root.WithOpName("float_constant"),
1398                                   Input::Initializer(float_constant_tensor));
1399 
1400     Output excluded_reshape_op =
1401         Reshape(root.WithOpName("excluded_reshape_op"), int_constant, {10, 2});
1402 
1403     Output included_reshape_op = Reshape(root.WithOpName("included_reshape_op"),
1404                                          float_constant, {10, 2});
1405 
1406     Output excluded_relu_op =
1407         Relu(root.WithOpName("excluded_relu_op"), excluded_reshape_op);
1408 
1409     Output excluded_float_caster = Cast(
1410         root.WithOpName("excluded_float_caster"), excluded_relu_op, DT_FLOAT);
1411 
1412     Output included_relu_op =
1413         Relu(root.WithOpName("included_relu_op"), included_reshape_op);
1414 
1415     GraphDef float_graph_def;
1416     TF_ASSERT_OK(root.ToGraphDef(&float_graph_def));
1417 
1418     GraphDef quantized_graph_def;
1419     TestTransformedVersusFloatGraph(
1420         QuantizeNodes, float_graph_def, {}, {},
1421         {"excluded_float_caster", "included_relu_op"}, {}, 1.0,
1422         &quantized_graph_def);
1423 
1424     std::map<string, const NodeDef*> node_map;
1425     MapNamesToNodes(quantized_graph_def, &node_map);
1426     ASSERT_EQ(1, node_map.count("excluded_reshape_op"));
1427     EXPECT_EQ("Reshape", node_map.at("excluded_reshape_op")->op());
1428     ASSERT_EQ(1, node_map.count("included_reshape_op"));
1429     EXPECT_EQ("Dequantize", node_map.at("included_reshape_op")->op());
1430   }
1431 };
1432 
TEST_F(QuantizeNodesTest,TestIgnoreOps)1433 TEST_F(QuantizeNodesTest, TestIgnoreOps) {
1434   TestIgnoreOps({});
1435   TestIgnoreOps({"MatMul"});
1436   TestIgnoreOps({"MatMul", "Mul"});
1437 }
1438 
TEST_F(QuantizeNodesTest,TestQuantizeMatMulTiny)1439 TEST_F(QuantizeNodesTest, TestQuantizeMatMulTiny) { TestQuantizeMatMulTiny(); }
1440 
TEST_F(QuantizeNodesTest,TestQuantizeMatMulSmall)1441 TEST_F(QuantizeNodesTest, TestQuantizeMatMulSmall) {
1442   TestQuantizeMatMulSmall();
1443 }
1444 
TEST_F(QuantizeNodesTest,TestQuantizeMul)1445 TEST_F(QuantizeNodesTest, TestQuantizeMul) { TestQuantizeMul(); }
1446 
TEST_F(QuantizeNodesTest,TestQuantizeAdd)1447 TEST_F(QuantizeNodesTest, TestQuantizeAdd) { TestQuantizeAdd(); }
1448 
TEST_F(QuantizeNodesTest,TestOddPaddingProblem)1449 TEST_F(QuantizeNodesTest, TestOddPaddingProblem) {
1450   // Tests one error case we ran into in a real graph.
1451   TestQuantizeConv2D(1, 4, 4, 1, 3, 1, 2, "SAME",
1452                      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
1453                      {1, 2, 3, 4, 5, 6, 7, 8, 9});
1454 }
1455 
TEST_F(QuantizeNodesTest,TestQuantizeConv2D)1456 TEST_F(QuantizeNodesTest, TestQuantizeConv2D) {
1457   TestQuantizeConv2D(1, 4, 3, 1, 3, 1, 1, "SAME",
1458                      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12},
1459                      {1, 4, 7, 2, 5, 8, 3, 6, 9});
1460 }
1461 
TEST_F(QuantizeNodesTest,TestQuantizeBiasAdd)1462 TEST_F(QuantizeNodesTest, TestQuantizeBiasAdd) { TestQuantizeBiasAdd(); }
1463 
TEST_F(QuantizeNodesTest,TestQuantizeConcat)1464 TEST_F(QuantizeNodesTest, TestQuantizeConcat) { TestQuantizeConcat(); }
1465 
TEST_F(QuantizeNodesTest,TestQuantizeRelu)1466 TEST_F(QuantizeNodesTest, TestQuantizeRelu) { TestQuantizeRelu(); }
1467 
TEST_F(QuantizeNodesTest,TestQuantizeRelu6)1468 TEST_F(QuantizeNodesTest, TestQuantizeRelu6) { TestQuantizeRelu6(); }
1469 
TEST_F(QuantizeNodesTest,TestQuantizeMaxPool)1470 TEST_F(QuantizeNodesTest, TestQuantizeMaxPool) { TestQuantizeMaxPool(); }
1471 
TEST_F(QuantizeNodesTest,TestQuantizeAvgPool)1472 TEST_F(QuantizeNodesTest, TestQuantizeAvgPool) { TestQuantizeAvgPool(); }
1473 
TEST_F(QuantizeNodesTest,TestQuantizeReshape)1474 TEST_F(QuantizeNodesTest, TestQuantizeReshape) { TestQuantizeReshape(); }
1475 
TEST_F(QuantizeNodesTest,TestQuantizeResizeBilinear)1476 TEST_F(QuantizeNodesTest, TestQuantizeResizeBilinear) {
1477   TestQuantizeResizeBilinear();
1478 }
1479 
TEST_F(QuantizeNodesTest,TestRemoveRedundantQuantization)1480 TEST_F(QuantizeNodesTest, TestRemoveRedundantQuantization) {
1481   TestRemoveRedundantQuantization();
1482 }
1483 
TEST_F(QuantizeNodesTest,TestRemoveRedundantQuantizationWithBiasAdd)1484 TEST_F(QuantizeNodesTest, TestRemoveRedundantQuantizationWithBiasAdd) {
1485   TestRemoveRedundantQuantizationWithBiasAdd();
1486 }
1487 
TEST_F(QuantizeNodesTest,TestRemoveRedundantQuantizationWithMultipleOutputs)1488 TEST_F(QuantizeNodesTest, TestRemoveRedundantQuantizationWithMultipleOutputs) {
1489   TestRemoveRedundantQuantizationWithMultipleOutputs();
1490 }
1491 
TEST_F(QuantizeNodesTest,TestQuantizePlaceholders)1492 TEST_F(QuantizeNodesTest, TestQuantizePlaceholders) {
1493   TestQuantizePlaceholders();
1494 }
1495 
TEST_F(QuantizeNodesTest,TestInputRange)1496 TEST_F(QuantizeNodesTest, TestInputRange) { TestInputRange(); }
1497 
TEST_F(QuantizeNodesTest,TestFallbackRange)1498 TEST_F(QuantizeNodesTest, TestFallbackRange) { TestFallbackRange(); }
1499 
TEST_F(QuantizeNodesTest,TestConvertFakeQuantsToRequantize)1500 TEST_F(QuantizeNodesTest, TestConvertFakeQuantsToRequantize) {
1501   TestConvertFakeQuantsToRequantize();
1502 }
1503 
TEST_F(QuantizeNodesTest,TestMergeAdjacentRequantizes)1504 TEST_F(QuantizeNodesTest, TestMergeAdjacentRequantizes) {
1505   TestMergeAdjacentRequantizes();
1506 }
1507 
TEST_F(QuantizeNodesTest,TestConvertFakeQuantsEndToEnd)1508 TEST_F(QuantizeNodesTest, TestConvertFakeQuantsEndToEnd) {
1509   TestConvertFakeQuantsEndToEnd();
1510 }
1511 
TEST_F(QuantizeNodesTest,TestHoistFakeQuants)1512 TEST_F(QuantizeNodesTest, TestHoistFakeQuants) { TestHoistFakeQuants(); }
1513 
TEST_F(QuantizeNodesTest,TestMergeDuplicateQuantizes)1514 TEST_F(QuantizeNodesTest, TestMergeDuplicateQuantizes) {
1515   TestMergeDuplicateQuantizes();
1516 }
1517 
TEST_F(QuantizeNodesTest,TestMergeDuplicateConsts)1518 TEST_F(QuantizeNodesTest, TestMergeDuplicateConsts) {
1519   TestMergeDuplicateConsts();
1520 }
1521 
TEST_F(QuantizeNodesTest,TestMergeDuplicatesNested)1522 TEST_F(QuantizeNodesTest, TestMergeDuplicatesNested) {
1523   TestMergeDuplicatesNested();
1524 }
1525 
TEST_F(QuantizeNodesTest,TestMergeDuplicateInOut)1526 TEST_F(QuantizeNodesTest, TestMergeDuplicateInOut) {
1527   TestMergeDuplicatesInOut();
1528 }
1529 
TEST_F(QuantizeNodesTest,TestExcludeNonFloat)1530 TEST_F(QuantizeNodesTest, TestExcludeNonFloat) { TestExcludeNonFloat(); }
1531 
1532 }  // namespace graph_transforms
1533 }  // namespace tensorflow
1534