• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <random>
17 
18 #include "tensorflow/core/framework/fake_input.h"
19 #include "tensorflow/core/framework/node_def_builder.h"
20 #include "tensorflow/core/framework/tensor.h"
21 #include "tensorflow/core/kernels/ops_testutil.h"
22 #include "tensorflow/core/kernels/ops_util.h"
23 #include "tensorflow/core/lib/core/status_test_util.h"
24 #include "tensorflow/core/platform/test.h"
25 #include "tensorflow/core/platform/test_benchmark.h"
26 
27 namespace tensorflow {
28 
29 class QuantizedOpTest : public OpsTestBase {
30  protected:
31 };
32 
33 struct ParameterizedQuantizeOpTest : public OpsTestBase,
34                                      public ::testing::WithParamInterface<int> {
35 };
36 
TEST_F(QuantizedOpTest,QuantizeV2)37 TEST_F(QuantizedOpTest, QuantizeV2) {
38   TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
39                    .Input(FakeInput(DT_FLOAT))
40                    .Input(FakeInput(DT_FLOAT))
41                    .Input(FakeInput(DT_FLOAT))
42                    .Attr("T", DataTypeToEnum<quint8>::v())
43                    .Attr("mode", "MIN_FIRST")
44                    .Finalize(node_def()));
45   TF_ASSERT_OK(InitOp());
46   AddInputFromArray<float>(TensorShape({7}),
47                            {0.0, 1.0, 1.25, 1.75, 127.0, 255.0, 500.0});
48   // min_range = 0
49   AddInputFromArray<float>(TensorShape({1}), {0});
50   // max_range = 255
51   AddInputFromArray<float>(TensorShape({1}), {255.0f});
52   TF_ASSERT_OK(RunOpKernel());
53   Tensor expected(allocator(), DT_QUINT8, TensorShape({7}));
54   // Input element 0.0 should map to 0.
55   // Input element 500.0 is quantized to 255 because max_range = 255.
56   test::FillValues<quint8>(&expected, {0, 1, 1, 2, 127, 255, 255});
57   test::ExpectTensorEqual<quint8>(expected, *GetOutput(0));
58 }
59 
60 // Creates a tensor with the specified dims, using values chosen from data,
61 // multiplied by (1 + index) along the axis dimension.
62 template <typename T>
ScalePerSliceAlongAxis(std::vector<int64> dims,int axis,const std::vector<T> & data)63 std::vector<T> ScalePerSliceAlongAxis(std::vector<int64> dims, int axis,
64                                       const std::vector<T>& data) {
65   uint32 seed = 123;
66   std::minstd_rand rng(seed);
67   int64_t out_size = 1;
68   for (int dim : dims) {
69     out_size *= dim;
70   }
71   int minor_size = 1;
72   for (int i = axis + 1; i < dims.size(); ++i) {
73     minor_size *= dims[i];
74   }
75   std::vector<T> out(out_size);
76   int num_slices = (axis == -1) ? 1 : dims[axis];
77   for (int out_idx = 0; out_idx < out_size; ++out_idx) {
78     int in_idx = rng() % data.size();
79     T multiplier = ((out_idx / minor_size) % num_slices) + 1;
80     out[out_idx] = data[in_idx] * multiplier;
81   }
82   return out;
83 }
84 
TEST_P(ParameterizedQuantizeOpTest,QuantizeV2Quint8Scaled)85 TEST_P(ParameterizedQuantizeOpTest, QuantizeV2Quint8Scaled) {
86   const int axis = GetParam();
87   TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
88                    .Input(FakeInput(DT_FLOAT))
89                    .Input(FakeInput(DT_FLOAT))
90                    .Input(FakeInput(DT_FLOAT))
91                    .Attr("T", DataTypeToEnum<quint8>::v())
92                    .Attr("mode", "SCALED")
93                    .Attr("axis", axis)
94                    .Finalize(node_def()));
95   TF_ASSERT_OK(InitOp());
96   const std::vector<int64> dims = {2, 3, 4, 5};
97   int num_slices = (axis == -1) ? 1 : dims[axis];
98 
99   // Each channel contains the same 8 values multiplied by (channel + 1).
100   AddInputFromArray<float>(
101       TensorShape(dims),
102       ScalePerSliceAlongAxis<float>(
103           dims, axis, {-255.0, 0.0, 1.0, 1.25, 1.75, 64.0, 127.0, 500.0}));
104   std::vector<float> min_ranges(num_slices), max_ranges(num_slices);
105   for (int slice_idx = 0; slice_idx < num_slices; ++slice_idx) {
106     min_ranges[slice_idx] = (slice_idx + 1) * -255.0;
107     max_ranges[slice_idx] = (slice_idx + 1) * 127.0;
108   }
109   AddInputFromArray<float>(TensorShape({num_slices}), min_ranges);
110   AddInputFromArray<float>(TensorShape({num_slices}), max_ranges);
111   TF_ASSERT_OK(RunOpKernel());
112   // Input values < 0 should map to 0 even though min_range = -255, because
113   // we are performing quantization by scaling to quint8.
114   // Input value 0.0 should map to 0.
115   // The scale factor chosen should be 255 / 127 =  2.00787
116   // Output values are clipped to 255.
117 
118   Tensor expected(allocator(), DT_QUINT8, TensorShape(dims));
119   test::FillValues<quint8>(
120       &expected,
121       ScalePerSliceAlongAxis<quint8>(dims, -1, {0, 0, 2, 3, 4, 129, 255, 255}));
122 
123   auto output_min = *GetOutput(1);
124   auto output_max = *GetOutput(2);
125 
126   for (int slice_idx = 0; slice_idx < num_slices; ++slice_idx) {
127     EXPECT_EQ(output_min.flat<float>()(slice_idx), 0);
128     EXPECT_EQ(output_max.flat<float>()(slice_idx), 127.0 * (slice_idx + 1));
129   }
130 
131   auto output = *GetOutput(0);
132   test::ExpectTensorEqual<quint8>(expected, *GetOutput(0));
133 }
134 
TEST_F(QuantizedOpTest,QuantizeV2Quint8ScaledSmallInputRange)135 TEST_F(QuantizedOpTest, QuantizeV2Quint8ScaledSmallInputRange) {
136   TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
137                    .Input(FakeInput(DT_FLOAT))
138                    .Input(FakeInput(DT_FLOAT))
139                    .Input(FakeInput(DT_FLOAT))
140                    .Attr("T", DataTypeToEnum<quint8>::v())
141                    .Attr("mode", "SCALED")
142                    .Finalize(node_def()));
143   TF_ASSERT_OK(InitOp());
144   AddInputFromArray<float>(TensorShape({3}), {-1.0, 0.0, 2.0});
145   AddInputFromArray<float>(TensorShape({1}), {-1.0f});
146   AddInputFromArray<float>(TensorShape({1}), {2.0f});
147   TF_ASSERT_OK(RunOpKernel());
148   Tensor expected(allocator(), DT_QUINT8, TensorShape({3}));
149   // Input element -1.0 should map to 0 even though min_range = -1, because
150   // we are performing quantization by scaling to quint8.
151   // Input element 0.0 should map to 0.
152   // Input element 2.0 should map to max quint8 value 255.
153   test::FillValues<quint8>(&expected, {0, 0, 255});
154   test::ExpectTensorEqual<quint8>(expected, *GetOutput(0));
155 
156   Tensor expected_output_min(allocator(), DT_FLOAT, TensorShape({}));
157   test::FillValues<float>(&expected_output_min, {0.0});
158   test::ExpectTensorEqual<float>(expected_output_min, *GetOutput(1));
159 
160   Tensor expected_output_max(allocator(), DT_FLOAT, TensorShape({}));
161   test::FillValues<float>(&expected_output_max, {2.0});
162   test::ExpectTensorEqual<float>(expected_output_max, *GetOutput(2));
163 }
164 
TEST_P(ParameterizedQuantizeOpTest,QuantizeV2Qint8Scaled)165 TEST_P(ParameterizedQuantizeOpTest, QuantizeV2Qint8Scaled) {
166   const int axis = GetParam();
167   TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
168                    .Input(FakeInput(DT_FLOAT))
169                    .Input(FakeInput(DT_FLOAT))
170                    .Input(FakeInput(DT_FLOAT))
171                    .Attr("T", DataTypeToEnum<qint8>::v())
172                    .Attr("mode", "SCALED")
173                    .Attr("narrow_range", false)
174                    .Attr("axis", axis)
175                    .Finalize(node_def()));
176   TF_ASSERT_OK(InitOp());
177   const std::vector<int64> dims = {2, 3, 4, 5};
178   int num_slices = (axis == -1) ? 1 : dims[axis];
179 
180   // Each channel contains the same 7 values multiplied by (channel + 1).
181   AddInputFromArray<float>(
182       TensorShape(dims),
183       ScalePerSliceAlongAxis<float>(
184           dims, axis, {-128.0, 0.0, 1.0, 1.25, 1.75, 64.0, 127.0}));
185   std::vector<float> min_ranges(num_slices), max_ranges(num_slices);
186   for (int slice_idx = 0; slice_idx < num_slices; ++slice_idx) {
187     min_ranges[slice_idx] = (slice_idx + 1) * -128.0;
188     max_ranges[slice_idx] = (slice_idx + 1) * 100.0;
189   }
190   AddInputFromArray<float>(TensorShape({num_slices}), min_ranges);
191   AddInputFromArray<float>(TensorShape({num_slices}), max_ranges);
192   TF_ASSERT_OK(RunOpKernel());
193 
194   // Input element 0.0 should map to 0.
195   // Input element 127.0 maps to 127 instead of 100.
196   // (i.e. the max_ranges[] values should be ignored because their magnitude is
197   // less than the min_ranges[] values).
198   Tensor expected(allocator(), DT_QINT8, TensorShape(dims));
199   test::FillValues<qint8>(
200       &expected,
201       ScalePerSliceAlongAxis<qint8>(dims, -1, {-128, 0, 1, 1, 2, 64, 127}));
202 
203   auto output_min = *GetOutput(1);
204   auto output_max = *GetOutput(2);
205 
206   for (int slice_idx = 0; slice_idx < num_slices; ++slice_idx) {
207     EXPECT_EQ(output_min.flat<float>()(slice_idx), -128.0 * (slice_idx + 1));
208     EXPECT_EQ(output_max.flat<float>()(slice_idx), 127.0 * (slice_idx + 1));
209   }
210 
211   auto output = *GetOutput(0);
212   test::ExpectTensorEqual<qint8>(expected, *GetOutput(0));
213 }
214 
TEST_P(ParameterizedQuantizeOpTest,QuantizeV2Qint8ScaledNarrowRange)215 TEST_P(ParameterizedQuantizeOpTest, QuantizeV2Qint8ScaledNarrowRange) {
216   const int axis = GetParam();
217   TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
218                    .Input(FakeInput(DT_FLOAT))
219                    .Input(FakeInput(DT_FLOAT))
220                    .Input(FakeInput(DT_FLOAT))
221                    .Attr("T", DataTypeToEnum<qint8>::v())
222                    .Attr("mode", "SCALED")
223                    .Attr("narrow_range", true)
224                    .Attr("axis", axis)
225                    .Finalize(node_def()));
226   TF_ASSERT_OK(InitOp());
227   const std::vector<int64> dims = {2, 3, 4, 5};
228   int num_slices = (axis == -1) ? 1 : dims[axis];
229 
230   // Each channel contains the same 7 values multiplied by (channel + 1).
231   AddInputFromArray<float>(
232       TensorShape(dims),
233       ScalePerSliceAlongAxis<float>(
234           dims, axis, {-128.0, 0.0, 1.0, 1.25, 1.75, 64.0, 127.0}));
235   std::vector<float> min_ranges(num_slices), max_ranges(num_slices);
236   for (int slice_idx = 0; slice_idx < num_slices; ++slice_idx) {
237     min_ranges[slice_idx] = (slice_idx + 1) * -128.0;
238     max_ranges[slice_idx] = (slice_idx + 1) * 100.0;
239   }
240   AddInputFromArray<float>(TensorShape({num_slices}), min_ranges);
241   AddInputFromArray<float>(TensorShape({num_slices}), max_ranges);
242   TF_ASSERT_OK(RunOpKernel());
243 
244   // Input element 0.0 should map to 0.
245   // Input element 127.0 maps to 127 instead of 100.
246   // (i.e. the max_ranges[] values should be ignored because their magnitude is
247   // less than the min_ranges[] values).
248   Tensor expected(allocator(), DT_QINT8, TensorShape(dims));
249   test::FillValues<qint8>(
250       &expected,
251       ScalePerSliceAlongAxis<qint8>(dims, -1, {-127, 0, 1, 1, 2, 64, 126}));
252 
253   auto output_min = *GetOutput(1);
254   auto output_max = *GetOutput(2);
255 
256   for (int slice_idx = 0; slice_idx < num_slices; ++slice_idx) {
257     EXPECT_EQ(output_min.flat<float>()(slice_idx), -128.0 * (slice_idx + 1));
258     EXPECT_EQ(output_max.flat<float>()(slice_idx), 128.0 * (slice_idx + 1));
259   }
260 
261   auto output = *GetOutput(0);
262   test::ExpectTensorEqual<qint8>(expected, *GetOutput(0));
263 }
264 
265 // Instantiate parameterized tests for axis = -1, 1, 3.
266 INSTANTIATE_TEST_SUITE_P(All, ParameterizedQuantizeOpTest,
267                          ::testing::Values(-1, 1, 3));
268 
TEST_F(QuantizedOpTest,QuantizeV2Qint8ScaledSmallInputRange)269 TEST_F(QuantizedOpTest, QuantizeV2Qint8ScaledSmallInputRange) {
270   TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
271                    .Input(FakeInput(DT_FLOAT))
272                    .Input(FakeInput(DT_FLOAT))
273                    .Input(FakeInput(DT_FLOAT))
274                    .Attr("T", DataTypeToEnum<qint8>::v())
275                    .Attr("mode", "SCALED")
276                    .Finalize(node_def()));
277   TF_ASSERT_OK(InitOp());
278   AddInputFromArray<float>(TensorShape({3}), {-0.064, 0.0, 0.127});
279   AddInputFromArray<float>(TensorShape({1}), {-0.064f});
280   AddInputFromArray<float>(TensorShape({1}), {0.127f});
281   TF_ASSERT_OK(RunOpKernel());
282   Tensor expected(allocator(), DT_QINT8, TensorShape({3}));
283   // Input element 0.0 should map to 0.
284   // Input element 2.0 should map to 127, max value of qint8.
285   test::FillValues<qint8>(&expected, {-64, 0, 127});
286   test::ExpectTensorEqual<qint8>(expected, *GetOutput(0));
287 
288   Tensor expected_output_min(allocator(), DT_FLOAT, TensorShape({}));
289   test::FillValues<float>(&expected_output_min, {-0.128});
290   test::ExpectTensorEqual<float>(expected_output_min, *GetOutput(1));
291 
292   Tensor expected_output_max(allocator(), DT_FLOAT, TensorShape({}));
293   test::FillValues<float>(&expected_output_max, {0.127});
294   test::ExpectTensorEqual<float>(expected_output_max, *GetOutput(2));
295 }
296 
TEST_F(QuantizedOpTest,QuantizeV2Qint8ScaledRoundToEven)297 TEST_F(QuantizedOpTest, QuantizeV2Qint8ScaledRoundToEven) {
298   TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
299                    .Input(FakeInput(DT_FLOAT))
300                    .Input(FakeInput(DT_FLOAT))
301                    .Input(FakeInput(DT_FLOAT))
302                    .Attr("T", DataTypeToEnum<qint8>::v())
303                    .Attr("mode", "SCALED")
304                    .Attr("round_mode", "HALF_TO_EVEN")
305                    .Finalize(node_def()));
306   TF_ASSERT_OK(InitOp());
307   AddInputFromArray<float>(TensorShape({7}),
308                            {-126.5, 0.0, 1.0, 2.5, 3.5, 64.0, 127.0});
309   AddInputFromArray<float>(TensorShape({1}), {-128.0f});
310   AddInputFromArray<float>(TensorShape({1}), {-128.0f});
311   TF_ASSERT_OK(RunOpKernel());
312   Tensor expected(allocator(), DT_QINT8, TensorShape({7}));
313   // Input element 0.0 should map to 0.
314   // Input element 127.0 maps to 127.
315   test::FillValues<qint8>(&expected, {-126, 0, 1, 2, 4, 64, 127});
316   test::ExpectTensorEqual<qint8>(expected, *GetOutput(0));
317 
318   Tensor expected_output_min(allocator(), DT_FLOAT, TensorShape({}));
319   test::FillValues<float>(&expected_output_min, {-128.0});
320   test::ExpectTensorEqual<float>(expected_output_min, *GetOutput(1));
321 
322   Tensor expected_output_max(allocator(), DT_FLOAT, TensorShape({}));
323   test::FillValues<float>(&expected_output_max, {127.0});
324   test::ExpectTensorEqual<float>(expected_output_max, *GetOutput(2));
325 }
326 
TEST_F(QuantizedOpTest,QuantizeV2Qint8ScaledRoundAwayFromZero)327 TEST_F(QuantizedOpTest, QuantizeV2Qint8ScaledRoundAwayFromZero) {
328   TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
329                    .Input(FakeInput(DT_FLOAT))
330                    .Input(FakeInput(DT_FLOAT))
331                    .Input(FakeInput(DT_FLOAT))
332                    .Attr("T", DataTypeToEnum<qint8>::v())
333                    .Attr("mode", "SCALED")
334                    .Attr("round_mode", "HALF_AWAY_FROM_ZERO")
335                    .Finalize(node_def()));
336   TF_ASSERT_OK(InitOp());
337   AddInputFromArray<float>(TensorShape({7}),
338                            {-126.5, 0.0, 1.0, 2.5, 3.5, 64.0, 127.0});
339   AddInputFromArray<float>(TensorShape({1}), {-128.0f});
340   AddInputFromArray<float>(TensorShape({1}), {-128.0f});
341   TF_ASSERT_OK(RunOpKernel());
342   Tensor expected(allocator(), DT_QINT8, TensorShape({7}));
343   // Input element 0.0 should map to 0.
344   // Input element 127.0 maps to 127.
345   test::FillValues<qint8>(&expected, {-127, 0, 1, 3, 4, 64, 127});
346   test::ExpectTensorEqual<qint8>(expected, *GetOutput(0));
347 
348   Tensor expected_output_min(allocator(), DT_FLOAT, TensorShape({}));
349   test::FillValues<float>(&expected_output_min, {-128.0});
350   test::ExpectTensorEqual<float>(expected_output_min, *GetOutput(1));
351 
352   Tensor expected_output_max(allocator(), DT_FLOAT, TensorShape({}));
353   test::FillValues<float>(&expected_output_max, {127.0});
354   test::ExpectTensorEqual<float>(expected_output_max, *GetOutput(2));
355 }
356 
TEST_F(QuantizedOpTest,QuantizeV2_32Bit)357 TEST_F(QuantizedOpTest, QuantizeV2_32Bit) {
358   TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
359                    .Input(FakeInput(DT_FLOAT))
360                    .Input(FakeInput(DT_FLOAT))
361                    .Input(FakeInput(DT_FLOAT))
362                    .Attr("T", DataTypeToEnum<qint32>::v())
363                    .Attr("mode", "MIN_FIRST")
364                    .Finalize(node_def()));
365   TF_ASSERT_OK(InitOp());
366   const int element_count = 8;
367   AddInputFromArray<float>(
368       TensorShape({element_count}),
369       {-500.0f, 0.0f, 1.0f, 1.25f, 1.75f, 127.0f, 255.0f, 500.0f});
370   AddInputFromArray<float>(TensorShape({1}), {-256.0f});
371   AddInputFromArray<float>(TensorShape({1}), {256.0f});
372   TF_ASSERT_OK(RunOpKernel());
373   Tensor expected(allocator(), DT_QINT32, TensorShape({element_count}));
374   test::FillValues<qint32>(&expected,
375                            {
376                                std::numeric_limits<int32>::min(),
377                                0,
378                                static_cast<int32>(1.0f * (1 << 23)),
379                                static_cast<int32>(1.25f * (1 << 23)),
380                                static_cast<int32>(1.75f * (1 << 23)),
381                                static_cast<int32>(127.0f * (1 << 23)),
382                                static_cast<int32>(255.0f * (1 << 23)),
383                                std::numeric_limits<int32>::max(),
384                            });
385   // We expect there will be some fuzziness in the lower bits, since this is
386   // converting from float.
387   const int64_t epsilon = 1 << 8;
388   const qint32* output_data = GetOutput(0)->flat<qint32>().data();
389   const qint32* expected_data = expected.flat<qint32>().data();
390   for (int i = 0; i < element_count; ++i) {
391     const int64_t delta = output_data[i] - expected_data[i];
392     EXPECT_GT(epsilon, std::abs(delta))
393         << "output_data[" << i << "]=" << output_data[i] << ", expected_data["
394         << i << "]=" << expected_data[i] << ", delta=" << delta;
395   }
396 }
397 
TEST_F(QuantizedOpTest,QuantizeV2Ports)398 TEST_F(QuantizedOpTest, QuantizeV2Ports) {
399   TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
400                    .Input(FakeInput(DT_FLOAT))
401                    .Input(FakeInput(DT_FLOAT))
402                    .Input(FakeInput(DT_FLOAT))
403                    .Attr("T", DataTypeToEnum<quint8>::v())
404                    .Attr("mode", "MIN_FIRST")
405                    .Finalize(node_def()));
406   TF_ASSERT_OK(InitOp());
407   AddInputFromArray<float>(TensorShape({6}),
408                            {1.0, 1.25, 1.75, 127.0, 255.0, 500.0});
409   AddInputFromArray<float>(TensorShape({1}), {0});
410   AddInputFromArray<float>(TensorShape({1}), {255.0f});
411   TF_ASSERT_OK(RunOpKernel());
412   Tensor expected(allocator(), DT_QUINT8, TensorShape({6}));
413   test::FillValues<quint8>(&expected, {1, 1, 2, 127, 255, 255});
414   test::ExpectTensorEqual<quint8>(expected, *GetOutput(0));
415   const float output_min = GetOutput(1)->flat<float>()(0);
416   const float output_max = GetOutput(2)->flat<float>()(0);
417   EXPECT_NEAR(0.0f, output_min, 1e-5f);
418   EXPECT_NEAR(255.0f, output_max, 1e-5f);
419 }
420 
TEST_F(QuantizedOpTest,QuantizeV2EqualRange)421 TEST_F(QuantizedOpTest, QuantizeV2EqualRange) {
422   TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
423                    .Input(FakeInput(DT_FLOAT))
424                    .Input(FakeInput(DT_FLOAT))
425                    .Input(FakeInput(DT_FLOAT))
426                    .Attr("T", DataTypeToEnum<quint8>::v())
427                    .Attr("mode", "MIN_FIRST")
428                    .Finalize(node_def()));
429   TF_ASSERT_OK(InitOp());
430   AddInputFromArray<float>(TensorShape({6}), {0.0, 0.0, 0.0, 0.0, 0.0, 0.0});
431   AddInputFromArray<float>(TensorShape({1}), {0.0f});
432   AddInputFromArray<float>(TensorShape({1}), {0.0f});
433   TF_ASSERT_OK(RunOpKernel());
434   Tensor expected(allocator(), DT_QUINT8, TensorShape({6}));
435   test::FillValues<quint8>(&expected, {0, 0, 0, 0, 0, 0});
436   test::ExpectTensorEqual<quint8>(expected, *GetOutput(0));
437   const float output_min = GetOutput(1)->flat<float>()(0);
438   const float output_max = GetOutput(2)->flat<float>()(0);
439   EXPECT_NEAR(0.0f, output_min, 1e-5f);
440   EXPECT_LT(0.0f, output_max);
441 }
442 
TEST_F(QuantizedOpTest,QuantizeV2MovesMinToIncludeZero)443 TEST_F(QuantizedOpTest, QuantizeV2MovesMinToIncludeZero) {
444   TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
445                    .Input(FakeInput(DT_FLOAT))
446                    .Input(FakeInput(DT_FLOAT))
447                    .Input(FakeInput(DT_FLOAT))
448                    .Attr("T", DataTypeToEnum<quint8>::v())
449                    .Attr("mode", "MIN_FIRST")
450                    .Finalize(node_def()));
451   TF_ASSERT_OK(InitOp());
452   AddInputFromArray<float>(TensorShape({3}), {0.1, 0.2, 0.3});
453   AddInputFromArray<float>(TensorShape({1}), {0.1});
454   AddInputFromArray<float>(TensorShape({1}), {0.3});
455   TF_ASSERT_OK(RunOpKernel());
456   Tensor expected(allocator(), DT_QUINT8, TensorShape({3}));
457   test::FillValues<quint8>(&expected, {85, 170, 255});
458   test::ExpectTensorEqual<quint8>(expected, *GetOutput(0));
459   const float output_min = GetOutput(1)->flat<float>()(0);
460   const float output_max = GetOutput(2)->flat<float>()(0);
461   EXPECT_NEAR(0.0f, output_min, 1e-5f);
462   EXPECT_NEAR(0.3f, output_max, 1e-5f);
463 }
464 
TEST_F(QuantizedOpTest,QuantizeV2MovesMaxToIncludeZero)465 TEST_F(QuantizedOpTest, QuantizeV2MovesMaxToIncludeZero) {
466   TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
467                    .Input(FakeInput(DT_FLOAT))
468                    .Input(FakeInput(DT_FLOAT))
469                    .Input(FakeInput(DT_FLOAT))
470                    .Attr("T", DataTypeToEnum<quint8>::v())
471                    .Attr("mode", "MIN_FIRST")
472                    .Finalize(node_def()));
473   TF_ASSERT_OK(InitOp());
474   AddInputFromArray<float>(TensorShape({3}), {-0.1, -0.2, -0.3});
475   AddInputFromArray<float>(TensorShape({1}), {-0.3});
476   AddInputFromArray<float>(TensorShape({1}), {-0.1});
477   TF_ASSERT_OK(RunOpKernel());
478   Tensor expected(allocator(), DT_QUINT8, TensorShape({3}));
479   test::FillValues<quint8>(&expected, {170, 85, 0});
480   test::ExpectTensorEqual<quint8>(expected, *GetOutput(0));
481   const float output_min = GetOutput(1)->flat<float>()(0);
482   const float output_max = GetOutput(2)->flat<float>()(0);
483   EXPECT_NEAR(-0.3f, output_min, 1e-5f);
484   EXPECT_NEAR(0.0f, output_max, 1e-5f);
485 }
486 
TEST_F(QuantizedOpTest,Dequantize)487 TEST_F(QuantizedOpTest, Dequantize) {
488   TF_ASSERT_OK(NodeDefBuilder("dequantize_op", "Dequantize")
489                    .Input(FakeInput(DT_QUINT8))
490                    .Input(FakeInput(DT_FLOAT))
491                    .Input(FakeInput(DT_FLOAT))
492                    .Attr("T", DataTypeToEnum<quint8>::v())
493                    .Attr("mode", "MIN_FIRST")
494                    .Finalize(node_def()));
495   TF_ASSERT_OK(InitOp());
496   AddInputFromArray<quint8>(TensorShape({6}), {1, 2, 4, 8, 16, 255});
497   AddInputFromArray<float>(TensorShape({1}), {0});
498   AddInputFromArray<float>(TensorShape({1}), {255.0f});
499   TF_ASSERT_OK(RunOpKernel());
500   Tensor expected(allocator(), DT_FLOAT, TensorShape({6}));
501   test::FillValues<float>(&expected, {1.0, 2.0, 4.0, 8.0, 16.0, 255.0});
502   test::ExpectTensorNear<float>(expected, *GetOutput(0), 0.5);
503 }
504 
TEST_F(QuantizedOpTest,QuantizeV2DisableEnsureMinimumRange)505 TEST_F(QuantizedOpTest, QuantizeV2DisableEnsureMinimumRange) {
506   TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
507                    .Input(FakeInput(DT_FLOAT))
508                    .Input(FakeInput(DT_FLOAT))
509                    .Input(FakeInput(DT_FLOAT))
510                    .Attr("T", DataTypeToEnum<qint8>::v())
511                    .Attr("mode", "MIN_FIRST")
512                    .Attr("ensure_minimum_range", 0.0f)
513                    .Finalize(node_def()));
514   TF_ASSERT_OK(InitOp());
515   AddInputFromArray<float>(TensorShape({3}), {-0.000001, 0.0, 0.000042});
516   AddInputFromArray<float>(TensorShape({1}), {-0.000128});
517   AddInputFromArray<float>(TensorShape({1}), {0.000127});
518   TF_ASSERT_OK(RunOpKernel());
519   Tensor expected(allocator(), DT_QINT8, TensorShape({3}));
520   test::FillValues<qint8>(&expected, {-1, 0, 42});
521   for (int i = 0; i < 3; ++i) {
522     LOG(INFO) << GetOutput(0)->flat<qint8>()(i);
523   }
524   test::ExpectTensorEqual<qint8>(expected, *GetOutput(0));
525   const float output_min = GetOutput(1)->flat<float>()(0);
526   const float output_max = GetOutput(2)->flat<float>()(0);
527   LOG(INFO) << "output_min = " << output_min;
528   LOG(INFO) << "output_max = " << output_max;
529   EXPECT_NEAR(-0.000128f, output_min, 1e-7f);
530   EXPECT_NEAR(0.000127, output_max, 1e-7f);
531 }
532 
533 }  // end namespace tensorflow
534