1 /* Copyright 2016 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15
16 #include "tensorflow/core/framework/fake_input.h"
17 #include "tensorflow/core/framework/node_def_builder.h"
18 #include "tensorflow/core/framework/tensor.h"
19 #include "tensorflow/core/framework/tensor_testutil.h"
20 #include "tensorflow/core/kernels/ops_testutil.h"
21 #include "tensorflow/core/lib/core/status_test_util.h"
22
23 namespace tensorflow {
24
25 using tensorflow::AllocatorAttributes;
26 using tensorflow::DT_FLOAT;
27 using tensorflow::NodeDefBuilder;
28 using tensorflow::OpsTestBase;
29 using tensorflow::Tensor;
30 using tensorflow::TensorShape;
31 using tensorflow::test::ExpectClose;
32 using tensorflow::test::FillValues;
33
34 class QuantOpsTest : public OpsTestBase {
35 protected:
AddRandomInput(const TensorShape & shape)36 void AddRandomInput(const TensorShape& shape) {
37 CHECK_GT(input_types_.size(), inputs_.size())
38 << "Adding more inputs than types; perhaps you need to call MakeOp";
39 Tensor* input = new Tensor(device_->GetAllocator(AllocatorAttributes()),
40 DT_FLOAT, shape);
41 input->flat<float>().setRandom();
42 tensors_.push_back(input);
43 bool is_ref = IsRefType(input_types_[inputs_.size()]);
44 if (is_ref) {
45 CHECK_EQ(RemoveRefType(input_types_[inputs_.size()]), DT_FLOAT);
46 inputs_.push_back({&lock_for_refs_, input});
47 } else {
48 CHECK_EQ(input_types_[inputs_.size()], DT_FLOAT);
49 inputs_.push_back({nullptr, input});
50 }
51 }
52
RunTestFakeQuantWithMinMaxArgs(const int num_bits,const bool narrow_range,const float min,const float max,const TensorShape & shape,const gtl::ArraySlice<float> data,gtl::ArraySlice<float> expected_data)53 void RunTestFakeQuantWithMinMaxArgs(const int num_bits,
54 const bool narrow_range, const float min,
55 const float max, const TensorShape& shape,
56 const gtl::ArraySlice<float> data,
57 gtl::ArraySlice<float> expected_data) {
58 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxArgs")
59 .Input(FakeInput(DT_FLOAT)) // inputs
60 .Attr("min", min)
61 .Attr("max", max)
62 .Attr("num_bits", num_bits)
63 .Attr("narrow_range", narrow_range)
64 .Finalize(node_def()));
65 TF_EXPECT_OK(InitOp());
66 // Downstream inputs.
67 AddInputFromArray<float>(shape, data);
68
69 // Tested code.
70 TF_ASSERT_OK(RunOpKernel());
71
72 Tensor* output = GetOutput(0);
73 Tensor expected(allocator(), DT_FLOAT, shape);
74 FillValues<float>(&expected, expected_data);
75 ExpectClose(expected, *output);
76 }
77
RunTestFakeQuantWithMinMaxVars(const int num_bits,const bool narrow_range,const float min,const float max,const TensorShape & shape,const gtl::ArraySlice<float> data,gtl::ArraySlice<float> expected_data)78 void RunTestFakeQuantWithMinMaxVars(const int num_bits,
79 const bool narrow_range, const float min,
80 const float max, const TensorShape& shape,
81 const gtl::ArraySlice<float> data,
82 gtl::ArraySlice<float> expected_data) {
83 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVars")
84 .Input(FakeInput(DT_FLOAT)) // inputs
85 .Input(FakeInput(DT_FLOAT)) // min
86 .Input(FakeInput(DT_FLOAT)) // max
87 .Attr("num_bits", num_bits)
88 .Attr("narrow_range", narrow_range)
89 .Finalize(node_def()));
90 TF_EXPECT_OK(InitOp());
91 // Downstream inputs.
92 AddInputFromArray<float>(shape, data);
93 // Min.
94 AddInputFromArray<float>(TensorShape({}), {min});
95 // Max.
96 AddInputFromArray<float>(TensorShape({}), {max});
97
98 // Tested code.
99 TF_ASSERT_OK(RunOpKernel());
100
101 Tensor* output = GetOutput(0);
102 Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 3}));
103 FillValues<float>(&expected, expected_data);
104 ExpectClose(expected, *output);
105 }
106
RunTestFakeQuantWithMinMaxVarsPerChannel(const int num_bits,const bool narrow_range,const TensorShape & minmax_shape,const gtl::ArraySlice<float> min,const gtl::ArraySlice<float> max,const TensorShape & shape,const gtl::ArraySlice<float> data,gtl::ArraySlice<float> expected_data)107 void RunTestFakeQuantWithMinMaxVarsPerChannel(
108 const int num_bits, const bool narrow_range,
109 const TensorShape& minmax_shape, const gtl::ArraySlice<float> min,
110 const gtl::ArraySlice<float> max, const TensorShape& shape,
111 const gtl::ArraySlice<float> data, gtl::ArraySlice<float> expected_data) {
112 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannel")
113 .Input(FakeInput(DT_FLOAT)) // inputs
114 .Input(FakeInput(DT_FLOAT)) // min
115 .Input(FakeInput(DT_FLOAT)) // max
116 .Attr("num_bits", num_bits)
117 .Attr("narrow_range", narrow_range)
118 .Finalize(node_def()));
119 TF_EXPECT_OK(InitOp());
120 // Downstream inputs.
121 AddInputFromArray<float>(shape, data);
122 // Min.
123 AddInputFromArray<float>(minmax_shape, min);
124 // Max.
125 AddInputFromArray<float>(minmax_shape, max);
126
127 // Tested code.
128 TF_ASSERT_OK(RunOpKernel());
129
130 Tensor* output = GetOutput(0);
131 Tensor expected(allocator(), DT_FLOAT, shape);
132 FillValues<float>(&expected, expected_data);
133 ExpectClose(expected, *output);
134 }
135 };
136
TEST_F(QuantOpsTest,WithArgsNoNudging_RegularRange)137 TEST_F(QuantOpsTest, WithArgsNoNudging_RegularRange) {
138 // Original quantization range: [-10 + 0 / 4, -10 + 255 / 4], scale: 1/4.
139 // Original zero point: 40, no nudging necessary.
140 // Expected quantized values: -10.0, -9.75, ..., 53.75.
141 RunTestFakeQuantWithMinMaxArgs(
142 8, false, -10.0f, 53.75f, TensorShape({2, 3}),
143 {-10.1f, -10.0f, -9.9f, -9.75f, 53.75f, 53.8f},
144 {-10.0f, -10.0f, -10.0f, -9.75f, 53.75f, 53.75f});
145 }
146
TEST_F(QuantOpsTest,WithArgsNoNudging_NarrowRange)147 TEST_F(QuantOpsTest, WithArgsNoNudging_NarrowRange) {
148 // Original quantization range: [-10 + 0 / 4, -10 + 254 / 4], scale: 1/4.
149 // Original zero point: 41, no nudging necessary.
150 // Expected quantized values: -10.0, -9.75, ..., 53.5.
151 RunTestFakeQuantWithMinMaxArgs(
152 8, true, -10.0f, 53.5f, TensorShape({2, 3}),
153 {-10.1f, -10.0f, -9.9f, -9.75f, 53.5f, 53.6f},
154 {-10.0f, -10.0f, -10.0f, -9.75f, 53.5f, 53.5f});
155 }
156
TEST_F(QuantOpsTest,WithArgsNudgedDown_RegularRange)157 TEST_F(QuantOpsTest, WithArgsNudgedDown_RegularRange) {
158 // Original quantization range: [-0.4 / 4 + 0 / 4, -0.4 / 4 + 255 / 4].
159 // Scale: 1/4, original zero point: 0.4, nudged to 0.
160 // Nudged range: [0.0; 63.75].
161 // Expected quantized values: 0.0, 0.25, 0.5, ..., 63.75.
162 RunTestFakeQuantWithMinMaxArgs(8, false, -0.1f, 63.65f, TensorShape({2, 3}),
163 {-0.1f, 0.0f, 0.1f, 0.25f, 63.75f, 63.8f},
164 {0.0f, 0.0f, 0.0f, 0.25f, 63.75f, 63.75f});
165 }
166
TEST_F(QuantOpsTest,WithArgsNudgedDown_NarrowRange)167 TEST_F(QuantOpsTest, WithArgsNudgedDown_NarrowRange) {
168 // Original quantization range: [-0.4 / 4 + 0 / 4, -0.4 / 4 + 254 / 4].
169 // Scale: 1/4, original zero point: 1.4, nudged to 1.
170 // Nudged range: [0.0; 63.5].
171 // Expected quantized values: 0.0, 0.25, 0.5, ..., 63.5.
172 RunTestFakeQuantWithMinMaxArgs(8, true, -0.1f, 63.4f, TensorShape({2, 3}),
173 {-0.1f, 0.0f, 0.1f, 0.25f, 63.5f, 63.6f},
174 {0.0f, 0.0f, 0.0f, 0.25f, 63.5f, 63.5f});
175 }
176
TEST_F(QuantOpsTest,WithArgsNudgedUp_RegularRange)177 TEST_F(QuantOpsTest, WithArgsNudgedUp_RegularRange) {
178 // Original quantization range: [-0.51 / 4 + 0 / 4, -0.51 / 4 + 255 / 4].
179 // Scale: 1/4, original zero point: 0.51, nudged to 1.
180 // Nudged range: [-0.25; 63.5].
181 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.5.
182 RunTestFakeQuantWithMinMaxArgs(8, false, -0.1275f, 63.6225f,
183 TensorShape({2, 3}),
184 {-0.26f, -0.25f, -0.24f, 0.0f, 63.5f, 63.6f},
185 {-0.25f, -0.25f, -0.25f, 0.0f, 63.5f, 63.5f});
186 }
187
TEST_F(QuantOpsTest,WithArgsNudgedUp_NarrowRange)188 TEST_F(QuantOpsTest, WithArgsNudgedUp_NarrowRange) {
189 // Original quantization range: [-0.51 / 4 + 0 / 4, -0.51 / 4 + 254 / 4].
190 // Scale: 1/4, original zero point: 1.51, nudged to 2.
191 // Nudged range: [-0.25; 63.25].
192 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.25.
193 RunTestFakeQuantWithMinMaxArgs(
194 8, true, -0.1275f, 63.3725f, TensorShape({2, 3}),
195 {-0.26f, -0.25f, -0.24f, 0.0f, 63.25f, 63.3f},
196 {-0.25f, -0.25f, -0.25f, 0.0f, 63.25f, 63.25f});
197 }
198
TEST_F(QuantOpsTest,WithArgsNudgedZeroIs255_RegularRange)199 TEST_F(QuantOpsTest, WithArgsNudgedZeroIs255_RegularRange) {
200 // Original quantization range: [0.4 / 4 - 255 / 4, 0.4 / 4 + 0 / 4].
201 // Scale: 1/4, original zero point: 254.6, nudged to 255.
202 // Nudged range: [-63.75; 0.0].
203 // Expected quantized values: -63.75, -63.5, -63.25, ..., 0.0.
204 RunTestFakeQuantWithMinMaxArgs(
205 8, false, -63.65f, 0.1f, TensorShape({2, 3}),
206 {-63.8f, -63.75f, -63.7f, -63.5f, 0.0f, 0.1f},
207 {-63.75f, -63.75f, -63.75f, -63.5f, 0.0f, 0.0f});
208 }
209
TEST_F(QuantOpsTest,WithArgsNudgedZeroIs255_NarrowRange)210 TEST_F(QuantOpsTest, WithArgsNudgedZeroIs255_NarrowRange) {
211 // Original quantization range: [0.4 / 4 - 254 / 4, 0.4 / 4 + 0 / 4].
212 // Scale: 1/4, original zero point: 254.6, nudged to 255.
213 // Nudged range: [-63.5; 0.0].
214 // Expected quantized values: -63.5, -63.25, -63.0, ..., 0.0.
215 RunTestFakeQuantWithMinMaxArgs(8, true, -63.4f, 0.1f, TensorShape({2, 3}),
216 {-63.6f, -63.5f, -63.4f, -63.25f, 0.0f, 0.1f},
217 {-63.5f, -63.5f, -63.5f, -63.25f, 0.0f, 0.0f});
218 }
219
TEST_F(QuantOpsTest,WithArgsNoNudging_4Bits_RegularRange)220 TEST_F(QuantOpsTest, WithArgsNoNudging_4Bits_RegularRange) {
221 // Original quantization range: [-6 + 0 / 2, -6 + 15 / 2], scale: 1/2.
222 // Original zero point: 12, no nudging necessary.
223 // Expected quantized values: -6, -5.5, ..., 1.5.
224 RunTestFakeQuantWithMinMaxArgs(4, false, -6.0f, 1.5f, TensorShape({2, 3}),
225 {-6.1f, -6.0f, -5.9f, -5.5f, 1.5f, 1.6f},
226 {-6.0f, -6.0f, -6.0f, -5.5f, 1.5f, 1.5f});
227 }
228
TEST_F(QuantOpsTest,WithArgsNoNudging_4Bits_NarrowRange)229 TEST_F(QuantOpsTest, WithArgsNoNudging_4Bits_NarrowRange) {
230 // Original quantization range: [-6 + 0 / 2, -6 + 14 / 2], scale: 1/2.
231 // Original zero point: 13, no nudging necessary.
232 // Expected quantized values: -6, -5.5, ..., 1.0.
233 RunTestFakeQuantWithMinMaxArgs(4, true, -6.0f, 1.0f, TensorShape({2, 3}),
234 {-6.1f, -6.0f, -5.9f, -5.5f, 1.0f, 1.1f},
235 {-6.0f, -6.0f, -6.0f, -5.5f, 1.0f, 1.0f});
236 }
237
TEST_F(QuantOpsTest,WithArgsNudgedDown_4Bits_RegularRange)238 TEST_F(QuantOpsTest, WithArgsNudgedDown_4Bits_RegularRange) {
239 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 15 / 2].
240 // Scale: 1/2, original zero point: 0.2, nudged to 0.
241 // Nudged range: [0.0; 7.5].
242 // Expected quantized values: 0.0, 0.5, ..., 7.5.
243 RunTestFakeQuantWithMinMaxArgs(4, false, -0.1f, 7.4f, TensorShape({2, 3}),
244 {-0.1f, 0.0f, 0.1f, 0.5f, 7.5f, 7.6f},
245 {0.0f, 0.0f, 0.0f, 0.5f, 7.5f, 7.5f});
246 }
247
TEST_F(QuantOpsTest,WithArgsNudgedDown_4Bits_NarrowRange)248 TEST_F(QuantOpsTest, WithArgsNudgedDown_4Bits_NarrowRange) {
249 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 14 / 2].
250 // Scale: 1/2, original zero point: 1.2, nudged to 1.
251 // Nudged range: [0.0; 7.0].
252 // Expected quantized values: 0.0, 0.5, ..., 7.0.
253 RunTestFakeQuantWithMinMaxArgs(4, true, -0.1f, 6.9f, TensorShape({2, 3}),
254 {-0.1f, 0.0f, 0.1f, 0.5f, 7.0f, 7.1f},
255 {0.0f, 0.0f, 0.0f, 0.5f, 7.0f, 7.0f});
256 }
257
TEST_F(QuantOpsTest,WithArgsNudgedUp_4Bits_RegularRange)258 TEST_F(QuantOpsTest, WithArgsNudgedUp_4Bits_RegularRange) {
259 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 15 / 2].
260 // Scale: 1/2, original zero point: 0.8, nudged to 1.
261 // Nudged range: [-0.5; 7.0].
262 // Expected quantized values: -0.5, 0.0, 0.5, ..., 7.0.
263 RunTestFakeQuantWithMinMaxArgs(4, false, -0.4f, 7.1f, TensorShape({2, 3}),
264 {-0.6f, -0.5f, -0.24f, 0.0f, 7.0f, 7.1f},
265 {-0.5f, -0.5f, -0.00f, 0.0f, 7.0f, 7.0f});
266 }
267
TEST_F(QuantOpsTest,WithArgsNudgedUp_4Bits_NarrowRange)268 TEST_F(QuantOpsTest, WithArgsNudgedUp_4Bits_NarrowRange) {
269 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 14 / 2].
270 // Scale: 1/2, original zero point: 1.8, nudged to 2.
271 // Nudged range: [-0.5; 6.5].
272 // Expected quantized values: -0.5, 0.0, 0.5, ..., 6.5.
273 RunTestFakeQuantWithMinMaxArgs(4, true, -0.4f, 6.6f, TensorShape({2, 3}),
274 {-0.6f, -0.5f, -0.24f, 0.0f, 6.5f, 6.6f},
275 {-0.5f, -0.5f, 0.0f, 0.0f, 6.5f, 6.5f});
276 }
277
TEST_F(QuantOpsTest,WithArgsNudgedZeroIs15_4Bits_RegularRange)278 TEST_F(QuantOpsTest, WithArgsNudgedZeroIs15_4Bits_RegularRange) {
279 // Original quantization range: [0.4 / 2 - 15 / 2, 0.4 / 2 + 0 / 2].
280 // Scale: 1/2, original zero point: 14.6, nudged to 15.
281 // Nudged range: [-7.5; 0.0].
282 // Expected quantized values: -7.5, -7.0, ..., 0.0.
283 RunTestFakeQuantWithMinMaxArgs(4, false, -7.3f, 0.2f, TensorShape({2, 3}),
284 {-7.6f, -7.5f, -7.4f, -7.2f, 0.0f, 0.1f},
285 {-7.5f, -7.5f, -7.5f, -7.0f, 0.0f, 0.0f});
286 }
287
TEST_F(QuantOpsTest,WithArgsNudgedZeroIs15_4Bits_NarrowRange)288 TEST_F(QuantOpsTest, WithArgsNudgedZeroIs15_4Bits_NarrowRange) {
289 // Original quantization range: [0.4 / 2 - 14 / 2, 0.4 / 2 + 0 / 2].
290 // Scale: 1/2, original zero point: 14.6, nudged to 15.
291 // Nudged range: [-7.0; 0.0].
292 // Expected quantized values: -7.0, -6.5, ..., 0.0.
293 RunTestFakeQuantWithMinMaxArgs(4, true, -6.8f, 0.2f, TensorShape({2, 3}),
294 {-7.1f, -7.0f, -6.9f, -6.7f, 0.0f, 0.1f},
295 {-7.0f, -7.0f, -7.0f, -6.5f, 0.0f, 0.0f});
296 }
297
TEST_F(QuantOpsTest,WithArgsNoNudging_2Bits_RegularRange)298 TEST_F(QuantOpsTest, WithArgsNoNudging_2Bits_RegularRange) {
299 // Original quantization range: [-1 + 0 / 2, -1 + 3 / 2], scale: 1/2.
300 // Original zero point: 2, no nudging necessary.
301 // Expected quantized values: -1.0, -0.5, 0.0, 0.5.
302 RunTestFakeQuantWithMinMaxArgs(2, false, -1.0f, 0.5f, TensorShape({2, 3}),
303 {-1.1f, -1.0f, -0.9f, -0.3f, 0.1f, 0.6f},
304 {-1.0f, -1.0f, -1.0f, -0.5f, 0.0f, 0.5f});
305 }
306
TEST_F(QuantOpsTest,WithArgsNoNudging_2Bits_NarrowRange)307 TEST_F(QuantOpsTest, WithArgsNoNudging_2Bits_NarrowRange) {
308 // Original quantization range: [-1 + 0 / 2, -1 + 2 / 2], scale: 1/2.
309 // Original zero point: 3, no nudging necessary.
310 // Expected quantized values: -1.0, -0.5, 0.0.
311 RunTestFakeQuantWithMinMaxArgs(2, true, -1.0f, 0.0f, TensorShape({2, 3}),
312 {-1.1f, -1.0f, -0.9f, -0.3f, 0.0f, 0.1f},
313 {-1.0f, -1.0f, -1.0f, -0.5f, 0.0f, 0.0f});
314 }
315
TEST_F(QuantOpsTest,WithArgsNudgedDown_2Bits_RegularRange)316 TEST_F(QuantOpsTest, WithArgsNudgedDown_2Bits_RegularRange) {
317 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 3 / 2].
318 // Scale: 1/2, original zero point: 0.2, nudged to 0.
319 // Nudged range: [0.0; 1.5].
320 // Expected quantized values: 0.0, 0.5, 1.0, 1.5.
321 RunTestFakeQuantWithMinMaxArgs(2, false, -0.1f, 1.4f, TensorShape({2, 3}),
322 {-0.2f, 0.1f, 0.7f, 1.0f, 1.3f, 1.6f},
323 {0.0f, 0.0f, 0.5f, 1.0f, 1.5f, 1.5f});
324 }
325
TEST_F(QuantOpsTest,WithArgsNudgedDown_2Bits_NarrowRange)326 TEST_F(QuantOpsTest, WithArgsNudgedDown_2Bits_NarrowRange) {
327 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 2 / 2].
328 // Scale: 1/2, original zero point: 1.2, nudged to 1.
329 // Nudged range: [0.0; 1.0].
330 // Expected quantized values: 0.0, 0.5, 1.0.
331 RunTestFakeQuantWithMinMaxArgs(2, true, -0.1f, 0.9f, TensorShape({2, 3}),
332 {-0.1f, 0.1f, 0.7f, 0.9f, 1.0f, 1.1f},
333 {-0.0f, 0.0f, 0.5f, 1.0f, 1.0f, 1.0f});
334 }
335
TEST_F(QuantOpsTest,WithArgsNudgedUp_2Bits_RegularRange)336 TEST_F(QuantOpsTest, WithArgsNudgedUp_2Bits_RegularRange) {
337 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 3 / 2].
338 // Scale: 1/2, original zero point: 0.8, nudged to 1.
339 // Nudged range: [-0.5; 1.0].
340 // Expected quantized values: -0.5, 0.0, 0.5, 1.0.
341 RunTestFakeQuantWithMinMaxArgs(2, false, -0.4f, 1.1f, TensorShape({2, 3}),
342 {-0.6f, -0.5f, -0.24f, 0.0f, 1.0f, 1.1f},
343 {-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f});
344 }
345
TEST_F(QuantOpsTest,WithArgsNudgedUp_2Bits_NarrowRange)346 TEST_F(QuantOpsTest, WithArgsNudgedUp_2Bits_NarrowRange) {
347 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 2 / 2].
348 // Scale: 1/2, original zero point: 1.8, nudged to 2.
349 // Nudged range: [-0.5; 0.5].
350 // Expected quantized values: -0.5, 0.0, 0.5.
351 RunTestFakeQuantWithMinMaxArgs(2, true, -0.4f, 0.6f, TensorShape({2, 3}),
352 {-0.6f, -0.5f, -0.24f, 0.0f, 0.5f, 0.6f},
353 {-0.5f, -0.5f, -0.00f, 0.0f, 0.5f, 0.5f});
354 }
355
TEST_F(QuantOpsTest,WithArgsGradient_RegularRange)356 TEST_F(QuantOpsTest, WithArgsGradient_RegularRange) {
357 // Original quantization range: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 255 / 4].
358 // Scale: 1/4, original zero point: 0.5, nudged to 1.
359 // Nudged range: [-0.25; 63.5].
360 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.5.
361 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxArgsGradient")
362 .Input(FakeInput(DT_FLOAT)) // gradient
363 .Input(FakeInput(DT_FLOAT)) // inputs
364 .Attr("min", -0.125f)
365 .Attr("max", 63.625f)
366 .Attr("narrow_range", false)
367 .Finalize(node_def()));
368 TF_EXPECT_OK(InitOp());
369 // Upstream gradients.
370 AddRandomInput(TensorShape({2, 3}));
371 // Downstream inputs.
372 AddInputFromArray<float>(TensorShape({2, 3}),
373 {-0.26f, -0.25f, -0.24f, 0.0f, 63.5f, 63.6f});
374
375 // Tested code.
376 TF_ASSERT_OK(RunOpKernel());
377
378 Tensor* output = GetOutput(0);
379 auto input_flat = GetInput(0).flat<float>();
380 Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 3}));
381 FillValues<float>(&expected, {0.0f, input_flat(1), input_flat(2),
382 input_flat(3), input_flat(4), 0.0f});
383 ExpectClose(expected, *output);
384 }
385
TEST_F(QuantOpsTest,WithArgsGradient_NarrowRange)386 TEST_F(QuantOpsTest, WithArgsGradient_NarrowRange) {
387 // Original quantization range: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 254 / 4].
388 // Scale: 1/4, original zero point: 1.5, nudged to 2.
389 // Nudged range: [-0.25; 63.25].
390 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.5.
391 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxArgsGradient")
392 .Input(FakeInput(DT_FLOAT)) // gradient
393 .Input(FakeInput(DT_FLOAT)) // inputs
394 .Attr("min", -0.125f)
395 .Attr("max", 63.375f)
396 .Attr("narrow_range", true)
397 .Finalize(node_def()));
398 TF_EXPECT_OK(InitOp());
399 // Upstream gradients.
400 AddRandomInput(TensorShape({2, 3}));
401 // Downstream inputs.
402 AddInputFromArray<float>(TensorShape({2, 3}),
403 {-0.26f, -0.25f, -0.24f, 0.0f, 63.25f, 63.3f});
404
405 // Tested code.
406 TF_ASSERT_OK(RunOpKernel());
407
408 Tensor* output = GetOutput(0);
409 auto input_flat = GetInput(0).flat<float>();
410 Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 3}));
411 FillValues<float>(&expected, {0.0f, input_flat(1), input_flat(2),
412 input_flat(3), input_flat(4), 0.0f});
413 ExpectClose(expected, *output);
414 }
415
TEST_F(QuantOpsTest,WithArgsGradient_4Bits_RegularRange)416 TEST_F(QuantOpsTest, WithArgsGradient_4Bits_RegularRange) {
417 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 15 / 2].
418 // Scale: 1/2, original zero point: 0.8, nudged to 1.
419 // Nudged range: [-0.5; 7.0].
420 // Expected quantized values: -0.5, 0.0, 0.5, ..., 7.0.
421 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxArgsGradient")
422 .Input(FakeInput(DT_FLOAT)) // gradient
423 .Input(FakeInput(DT_FLOAT)) // inputs
424 .Attr("min", -0.4f)
425 .Attr("max", 7.1f)
426 .Attr("num_bits", 4)
427 .Attr("narrow_range", false)
428 .Finalize(node_def()));
429 TF_EXPECT_OK(InitOp());
430 // Upstream gradients.
431 AddRandomInput(TensorShape({2, 3}));
432 // Downstream inputs.
433 AddInputFromArray<float>(TensorShape({2, 3}),
434 {-0.6f, -0.5f, -0.4f, 0.0f, 7.0f, 7.1f});
435
436 // Tested code.
437 TF_ASSERT_OK(RunOpKernel());
438
439 Tensor* output = GetOutput(0);
440 auto input_flat = GetInput(0).flat<float>();
441 Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 3}));
442 FillValues<float>(&expected, {0.0f, input_flat(1), input_flat(2),
443 input_flat(3), input_flat(4), 0.0f});
444 ExpectClose(expected, *output);
445 }
446
TEST_F(QuantOpsTest,WithArgsGradient_4Bits_NarrowRange)447 TEST_F(QuantOpsTest, WithArgsGradient_4Bits_NarrowRange) {
448 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 14 / 2].
449 // Scale: 1/2, original zero point: 1.8, nudged to 2.
450 // Nudged range: [-0.5; 6.5].
451 // Expected quantized values: -0.5, 0.0, 0.5, ..., 6.5.
452 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxArgsGradient")
453 .Input(FakeInput(DT_FLOAT)) // gradient
454 .Input(FakeInput(DT_FLOAT)) // inputs
455 .Attr("min", -0.4f)
456 .Attr("max", 6.6f)
457 .Attr("num_bits", 4)
458 .Attr("narrow_range", true)
459 .Finalize(node_def()));
460 TF_EXPECT_OK(InitOp());
461 // Upstream gradients.
462 AddRandomInput(TensorShape({2, 3}));
463 // Downstream inputs.
464 AddInputFromArray<float>(TensorShape({2, 3}),
465 {-0.6f, -0.5f, -0.4f, 0.0f, 6.5f, 6.6f});
466
467 // Tested code.
468 TF_ASSERT_OK(RunOpKernel());
469
470 Tensor* output = GetOutput(0);
471 auto input_flat = GetInput(0).flat<float>();
472 Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 3}));
473 FillValues<float>(&expected, {0.0f, input_flat(1), input_flat(2),
474 input_flat(3), input_flat(4), 0.0f});
475 ExpectClose(expected, *output);
476 }
477
TEST_F(QuantOpsTest,WithVars_ZeroMinAndMax)478 TEST_F(QuantOpsTest, WithVars_ZeroMinAndMax) {
479 RunTestFakeQuantWithMinMaxVars(8, false, 0.0f, 0.0f, TensorShape({2, 3}),
480 {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
481 {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
482 }
483
TEST_F(QuantOpsTest,WithVarsNoNudging_RegularRange)484 TEST_F(QuantOpsTest, WithVarsNoNudging_RegularRange) {
485 // Original quantization range: [-10 + 0 / 4, -10 + 255 / 4], scale: 1/4.
486 // Original zero point: 40, no nudging necessary.
487 // Expected quantized values: -10.0, -10.25, ..., 53.75.
488 RunTestFakeQuantWithMinMaxVars(
489 8, false, -10.0f, 53.75f, TensorShape({2, 3}),
490 {-10.1f, -10.0f, -9.9f, -9.75f, 53.75f, 53.8f},
491 {-10.0f, -10.0f, -10.0f, -9.75f, 53.75f, 53.75f});
492 }
493
TEST_F(QuantOpsTest,WithVarsNoNudging_NarrowRange)494 TEST_F(QuantOpsTest, WithVarsNoNudging_NarrowRange) {
495 // Original quantization range: [-10 + 0 / 4, -10 + 254 / 4], scale: 1/4.
496 // Original zero point: 41, no nudging necessary.
497 // Expected quantized values: -10.0, -10.25, ..., 53.5.
498 RunTestFakeQuantWithMinMaxVars(
499 8, true, -10.0f, 53.5f, TensorShape({2, 3}),
500 {-10.1f, -10.0f, -9.90f, -9.75f, 53.5f, 53.6f},
501 {-10.0f, -10.0f, -10.0f, -9.75f, 53.5f, 53.5f});
502 }
503
TEST_F(QuantOpsTest,WithVarsNudgedDown_RegularRange)504 TEST_F(QuantOpsTest, WithVarsNudgedDown_RegularRange) {
505 // Original quantization range: [-0.4 / 4 + 0 / 4, -0.4 / 4 + 255 / 4].
506 // Scale: 1/4, original zero point: 0.4, nudged to 0.
507 // Nudged range: [0.0; 63.75].
508 // Expected quantized values: 0.0, 0.25, 0.5, ..., 63.75.
509 RunTestFakeQuantWithMinMaxVars(8, false, -0.1f, 63.65f, TensorShape({2, 3}),
510 {-0.1f, 0.0f, 0.1f, 0.25f, 63.75f, 63.8f},
511 {-0.0f, 0.0f, 0.0f, 0.25f, 63.75f, 63.75f});
512 }
513
TEST_F(QuantOpsTest,WithVarsNudgedDown_NarrowRange)514 TEST_F(QuantOpsTest, WithVarsNudgedDown_NarrowRange) {
515 // Original quantization range: [-0.4 / 4 + 0 / 4, -0.4 / 4 + 254 / 4].
516 // Scale: 1/4, original zero point: 1.4, nudged to 1.
517 // Nudged range: [0.0; 63.5].
518 // Expected quantized values: 0.0, 0.25, 0.5, ..., 63.5.
519 RunTestFakeQuantWithMinMaxVars(8, true, -0.1f, 63.4f, TensorShape({2, 3}),
520 {-0.1f, 0.0f, 0.1f, 0.25f, 63.5f, 63.6f},
521 {-0.0f, 0.0f, 0.0f, 0.25f, 63.5f, 63.5f});
522 }
523
TEST_F(QuantOpsTest,WithVarsNudgedUp_RegularRange)524 TEST_F(QuantOpsTest, WithVarsNudgedUp_RegularRange) {
525 // Original quantization range: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 255 / 4].
526 // Scale: 1/4, original zero point: 0.5, nudged to 1.
527 // Nudged range: [-0.25; 63.5].
528 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.5.
529 RunTestFakeQuantWithMinMaxVars(8, false, -0.125f, 63.625f,
530 TensorShape({2, 3}),
531 {-0.26f, -0.25f, -0.24f, 0.0f, 63.5f, 63.6f},
532 {-0.25f, -0.25f, -0.25f, 0.0f, 63.5f, 63.5f});
533 }
534
TEST_F(QuantOpsTest,WithVarsNudgedUp_NarrowRange)535 TEST_F(QuantOpsTest, WithVarsNudgedUp_NarrowRange) {
536 // Original quantization range: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 254 / 4].
537 // Scale: 1/4, original zero point: 1.5, nudged to 2.
538 // Nudged range: [-0.25; 63.25].
539 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.25.
540 RunTestFakeQuantWithMinMaxVars(
541 8, true, -0.125f, 63.375f, TensorShape({2, 3}),
542 {-0.26f, -0.25f, -0.24f, 0.0f, 63.25f, 63.3f},
543 {-0.25f, -0.25f, -0.25f, 0.0f, 63.25f, 63.25f});
544 }
545
TEST_F(QuantOpsTest,WithVarsNudgedZeroIs255_RegularRange)546 TEST_F(QuantOpsTest, WithVarsNudgedZeroIs255_RegularRange) {
547 // Original quantization range: [0.4 / 4 - 255 / 4, 0.4 / 4 + 0 / 4].
548 // Scale: 1/4, original zero point: 254.6, nudged to 255.
549 // Nudged range: [-63.75; 0.0].
550 // Expected quantized values: -63.75, -63.5, -63.25, ..., 0.0.
551 RunTestFakeQuantWithMinMaxVars(
552 8, false, -63.65f, 0.1f, TensorShape({2, 3}),
553 {-63.80f, -63.75f, -63.70f, -63.5f, 0.0f, 0.1f},
554 {-63.75f, -63.75f, -63.75f, -63.5f, 0.0f, 0.0f});
555 }
556
TEST_F(QuantOpsTest,WithVarsNudgedZeroIs255_NarrowRange)557 TEST_F(QuantOpsTest, WithVarsNudgedZeroIs255_NarrowRange) {
558 // Original quantization range: [0.4 / 4 - 254 / 4, 0.4 / 4 + 0 / 4].
559 // Scale: 1/4, original zero point: 254.6, nudged to 255.
560 // Nudged range: [-63.5; 0.0].
561 // Expected quantized values: -63.5, -63.25, -63.0, ..., 0.0.
562 RunTestFakeQuantWithMinMaxVars(8, true, -63.4f, 0.1f, TensorShape({2, 3}),
563 {-63.6f, -63.5f, -63.4f, -63.25f, 0.0f, 0.1f},
564 {-63.5f, -63.5f, -63.5f, -63.25f, 0.0f, 0.0f});
565 }
566
TEST_F(QuantOpsTest,WithVarsNoNudging_4Bits_RegularRange)567 TEST_F(QuantOpsTest, WithVarsNoNudging_4Bits_RegularRange) {
568 // Original quantization range: [-6 + 0 / 2, -6 + 15 / 2], scale: 1/2.
569 // Original zero point: 12, no nudging necessary.
570 // Expected quantized values: -6, -5.5, ..., 1.5.
571 RunTestFakeQuantWithMinMaxVars(4, false, -6.0f, 1.5f, TensorShape({2, 3}),
572 {-6.1f, -6.0f, -5.9f, -5.5f, 1.5f, 1.6f},
573 {-6.0f, -6.0f, -6.0f, -5.5f, 1.5f, 1.5f});
574 }
575
TEST_F(QuantOpsTest,WithVarsNoNudging_4Bits_NarrowRange)576 TEST_F(QuantOpsTest, WithVarsNoNudging_4Bits_NarrowRange) {
577 // Original quantization range: [-6 + 0 / 2, -6 + 14 / 2], scale: 1/2.
578 // Original zero point: 13, no nudging necessary.
579 // Expected quantized values: -6, -5.5, ..., 1.0.
580 RunTestFakeQuantWithMinMaxVars(4, true, -6.0f, 1.0f, TensorShape({2, 3}),
581 {-6.1f, -6.0f, -5.9f, -5.5f, 1.0f, 1.1f},
582 {-6.0f, -6.0f, -6.0f, -5.5f, 1.0f, 1.0f});
583 }
584
TEST_F(QuantOpsTest,WithVarsNudgedDown_4Bits_RegularRange)585 TEST_F(QuantOpsTest, WithVarsNudgedDown_4Bits_RegularRange) {
586 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 15 / 2].
587 // Scale: 1/2, original zero point: 0.2, nudged to 0.
588 // Nudged range: [0.0; 7.5].
589 // Expected quantized values: 0.0, 0.5, ..., 7.5.
590 RunTestFakeQuantWithMinMaxVars(4, false, -0.1f, 7.4f, TensorShape({2, 3}),
591 {-0.1f, 0.0f, 0.1f, 0.5f, 7.5f, 7.6f},
592 {-0.0f, 0.0f, 0.0f, 0.5f, 7.5f, 7.5f});
593 }
594
TEST_F(QuantOpsTest,WithVarsNudgedDown_4Bits_NarrowRange)595 TEST_F(QuantOpsTest, WithVarsNudgedDown_4Bits_NarrowRange) {
596 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 14 / 2].
597 // Scale: 1/2, original zero point: 1.2, nudged to 1.
598 // Nudged range: [0.0; 7.0].
599 // Expected quantized values: 0.0, 0.5, ..., 7.0.
600 RunTestFakeQuantWithMinMaxVars(4, true, -0.1f, 6.9f, TensorShape({2, 3}),
601 {-0.1f, 0.0f, 0.1f, 0.5f, 7.0f, 7.1f},
602 {-0.0f, 0.0f, 0.0f, 0.5f, 7.0f, 7.0f});
603 }
604
TEST_F(QuantOpsTest,WithVarsNudgedUp_4Bits_RegularRange)605 TEST_F(QuantOpsTest, WithVarsNudgedUp_4Bits_RegularRange) {
606 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 15 / 2].
607 // Scale: 1/2, original zero point: 0.8, nudged to 1.
608 // Nudged range: [-0.5; 7.0].
609 // Expected quantized values: -0.5, 0.0, 0.5, ..., 7.0.
610 RunTestFakeQuantWithMinMaxVars(4, false, -0.4f, 7.1f, TensorShape({2, 3}),
611 {-0.6f, -0.5f, -0.24f, 0.0f, 7.0f, 7.1f},
612 {-0.5f, -0.5f, -0.00f, 0.0f, 7.0f, 7.0f});
613 }
614
TEST_F(QuantOpsTest,WithVarsNudgedUp_4Bits_NarrowRange)615 TEST_F(QuantOpsTest, WithVarsNudgedUp_4Bits_NarrowRange) {
616 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 14 / 2].
617 // Scale: 1/2, original zero point: 1.8, nudged to 2.
618 // Nudged range: [-0.5; 6.5].
619 // Expected quantized values: -0.5, 0.0, 0.5, ..., 6.5.
620 RunTestFakeQuantWithMinMaxVars(4, true, -0.4f, 6.6f, TensorShape({2, 3}),
621 {-0.6f, -0.5f, -0.24f, 0.0f, 6.5f, 6.6f},
622 {-0.5f, -0.5f, -0.00f, 0.0f, 6.5f, 6.5f});
623 }
624
TEST_F(QuantOpsTest,WithVarsNudgedZero15_4Bits_RegularRange)625 TEST_F(QuantOpsTest, WithVarsNudgedZero15_4Bits_RegularRange) {
626 // Original quantization range: [0.4 / 2 - 15 / 2, 0.4 / 2 + 0 / 2].
627 // Scale: 1/2, original zero point: 14.6, nudged to 15.
628 // Nudged range: [-7.5; 0.0].
629 // Expected quantized values: -7.5, -7.0, ..., 0.0.
630 RunTestFakeQuantWithMinMaxVars(4, false, -7.3f, 0.2f, TensorShape({2, 3}),
631 {-7.6f, -7.5f, -7.4f, -7.2f, 0.0f, 0.1f},
632 {-7.5f, -7.5f, -7.5f, -7.0f, 0.0f, 0.0f});
633 }
634
TEST_F(QuantOpsTest,WithVarsNudgedZero15_4Bits_NarrowRange)635 TEST_F(QuantOpsTest, WithVarsNudgedZero15_4Bits_NarrowRange) {
636 // Original quantization range: [0.4 / 2 - 14 / 2, 0.4 / 2 + 0 / 2].
637 // Scale: 1/2, original zero point: 14.6, nudged to 15.
638 // Nudged range: [-7.0; 0.0].
639 // Expected quantized values: -7.0, -6.5, ..., 0.0.
640 RunTestFakeQuantWithMinMaxVars(4, true, -6.8f, 0.2f, TensorShape({2, 3}),
641 {-7.1f, -7.0f, -6.9f, -6.5f, 0.0f, 0.1f},
642 {-7.0f, -7.0f, -7.0f, -6.5f, 0.0f, 0.0f});
643 }
644
TEST_F(QuantOpsTest,WithVarsGradient_ZeroMinAndMax)645 TEST_F(QuantOpsTest, WithVarsGradient_ZeroMinAndMax) {
646 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsGradient")
647 .Attr("narrow_range", false)
648 .Input(FakeInput(DT_FLOAT)) // gradients
649 .Input(FakeInput(DT_FLOAT)) // inputs
650 .Input(FakeInput(DT_FLOAT)) // min
651 .Input(FakeInput(DT_FLOAT)) // max
652 .Finalize(node_def()));
653 TF_EXPECT_OK(InitOp());
654 // Upstream gradients.
655 AddRandomInput(TensorShape({2, 3}));
656 // Downstream inputs.
657 AddInputFromArray<float>(TensorShape({2, 3}),
658 {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
659 // Min.
660 AddInputFromArray<float>(TensorShape({}), {0.0f});
661 // Max.
662 AddInputFromArray<float>(TensorShape({}), {0.0f});
663
664 // Tested code.
665 TF_ASSERT_OK(RunOpKernel());
666
667 Tensor* output_bprop_wrt_input = GetOutput(0);
668 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({2, 3}));
669 auto in_flat = GetInput(0).flat<float>();
670 FillValues<float>(
671 &expected_bprop_wrt_input,
672 {in_flat(0), in_flat(1), in_flat(2), in_flat(3), in_flat(4), in_flat(5)});
673 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
674
675 Tensor* output_bprop_wrt_min = GetOutput(1);
676 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({}));
677 expected_bprop_wrt_min.flat<float>()(0) = 0.0f;
678 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
679
680 Tensor* output_bprop_wrt_max = GetOutput(2);
681 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({}));
682 expected_bprop_wrt_max.flat<float>()(0) = 0.0f;
683 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
684 }
685
TEST_F(QuantOpsTest,WithVarsGradient_RegularRange)686 TEST_F(QuantOpsTest, WithVarsGradient_RegularRange) {
687 // Original quantization range: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 255 / 4].
688 // Scale: 1/4, original zero point: 0.5, nudged to 1.
689 // Nudged range: [-0.25; 63.5].
690 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.5.
691 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsGradient")
692 .Attr("narrow_range", false)
693 .Input(FakeInput(DT_FLOAT)) // gradients
694 .Input(FakeInput(DT_FLOAT)) // inputs
695 .Input(FakeInput(DT_FLOAT)) // min
696 .Input(FakeInput(DT_FLOAT)) // max
697 .Finalize(node_def()));
698 TF_EXPECT_OK(InitOp());
699 // Upstream gradients.
700 AddRandomInput(TensorShape({2, 3}));
701 // Downstream inputs.
702 AddInputFromArray<float>(TensorShape({2, 3}),
703 {-0.26f, -0.25f, -0.24f, 0.0f, 63.5f, 63.6f});
704 // Min.
705 AddInputFromArray<float>(TensorShape({}), {-0.125f});
706 // Max.
707 AddInputFromArray<float>(TensorShape({}), {63.625f});
708
709 // Tested code.
710 TF_ASSERT_OK(RunOpKernel());
711
712 Tensor* output_bprop_wrt_input = GetOutput(0);
713 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({2, 3}));
714 auto in_flat = GetInput(0).flat<float>();
715 FillValues<float>(&expected_bprop_wrt_input, {0.0f, in_flat(1), in_flat(2),
716 in_flat(3), in_flat(4), 0.0f});
717 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
718
719 Tensor* output_bprop_wrt_min = GetOutput(1);
720 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({}));
721 expected_bprop_wrt_min.flat<float>()(0) = in_flat(0);
722 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
723
724 Tensor* output_bprop_wrt_max = GetOutput(2);
725 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({}));
726 expected_bprop_wrt_max.flat<float>()(0) = in_flat(5);
727 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
728 }
729
TEST_F(QuantOpsTest,WithVarsGradient_NarrowRange)730 TEST_F(QuantOpsTest, WithVarsGradient_NarrowRange) {
731 // Original quantization range: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 254 / 4].
732 // Scale: 1/4, original zero point: 1.5, nudged to 2.
733 // Nudged range: [-0.25; 63.25].
734 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.25.
735 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsGradient")
736 .Attr("narrow_range", true)
737 .Input(FakeInput(DT_FLOAT)) // gradients
738 .Input(FakeInput(DT_FLOAT)) // inputs
739 .Input(FakeInput(DT_FLOAT)) // min
740 .Input(FakeInput(DT_FLOAT)) // max
741 .Finalize(node_def()));
742 TF_EXPECT_OK(InitOp());
743 // Upstream gradients.
744 AddRandomInput(TensorShape({2, 3}));
745 // Downstream inputs.
746 AddInputFromArray<float>(TensorShape({2, 3}),
747 {-0.26f, -0.25f, -0.24f, 0.0f, 63.25f, 63.3f});
748 // Min.
749 AddInputFromArray<float>(TensorShape({}), {-0.125f});
750 // Max.
751 AddInputFromArray<float>(TensorShape({}), {63.375f});
752
753 // Tested code.
754 TF_ASSERT_OK(RunOpKernel());
755
756 Tensor* output_bprop_wrt_input = GetOutput(0);
757 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({2, 3}));
758 auto in_flat = GetInput(0).flat<float>();
759 FillValues<float>(&expected_bprop_wrt_input, {0.0f, in_flat(1), in_flat(2),
760 in_flat(3), in_flat(4), 0.0f});
761 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
762
763 Tensor* output_bprop_wrt_min = GetOutput(1);
764 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({}));
765 expected_bprop_wrt_min.flat<float>()(0) = in_flat(0);
766 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
767
768 Tensor* output_bprop_wrt_max = GetOutput(2);
769 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({}));
770 expected_bprop_wrt_max.flat<float>()(0) = in_flat(5);
771 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
772 }
773
TEST_F(QuantOpsTest,WithVarsGradient_4Bits_RegularRange)774 TEST_F(QuantOpsTest, WithVarsGradient_4Bits_RegularRange) {
775 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 15 / 2].
776 // Scale: 1/2, original zero point: 0.8, nudged to 1.
777 // Nudged range: [-0.5; 7.0].
778 // Expected quantized values: -0.5, 0.0, 0.5, ..., 7.0.
779 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsGradient")
780 .Attr("num_bits", 4)
781 .Attr("narrow_range", false)
782 .Input(FakeInput(DT_FLOAT)) // gradients
783 .Input(FakeInput(DT_FLOAT)) // inputs
784 .Input(FakeInput(DT_FLOAT)) // min
785 .Input(FakeInput(DT_FLOAT)) // max
786 .Finalize(node_def()));
787 TF_EXPECT_OK(InitOp());
788 // Upstream gradients.
789 AddRandomInput(TensorShape({2, 3}));
790 // Downstream inputs.
791 AddInputFromArray<float>(TensorShape({2, 3}),
792 {-0.6f, -0.5f, -0.4f, 0.0f, 7.0f, 7.1f});
793 // Min.
794 AddInputFromArray<float>(TensorShape({}), {-0.4f});
795 // Max.
796 AddInputFromArray<float>(TensorShape({}), {7.1f});
797
798 // Tested code.
799 TF_ASSERT_OK(RunOpKernel());
800
801 Tensor* output_bprop_wrt_input = GetOutput(0);
802 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({2, 3}));
803 auto in_flat = GetInput(0).flat<float>();
804 FillValues<float>(&expected_bprop_wrt_input, {0.0f, in_flat(1), in_flat(2),
805 in_flat(3), in_flat(4), 0.0f});
806 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
807
808 Tensor* output_bprop_wrt_min = GetOutput(1);
809 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({}));
810 expected_bprop_wrt_min.flat<float>()(0) = in_flat(0);
811 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
812
813 Tensor* output_bprop_wrt_max = GetOutput(2);
814 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({}));
815 expected_bprop_wrt_max.flat<float>()(0) = in_flat(5);
816 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
817 }
818
TEST_F(QuantOpsTest,WithVarsGradient_4Bits_NarrowRange)819 TEST_F(QuantOpsTest, WithVarsGradient_4Bits_NarrowRange) {
820 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 14 / 2].
821 // Scale: 1/2, original zero point: 1.8, nudged to 2.
822 // Nudged range: [-0.5; 6.5].
823 // Expected quantized values: -0.5, 0.0, 0.5, ..., 6.5.
824 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsGradient")
825 .Attr("num_bits", 4)
826 .Attr("narrow_range", true)
827 .Input(FakeInput(DT_FLOAT)) // gradients
828 .Input(FakeInput(DT_FLOAT)) // inputs
829 .Input(FakeInput(DT_FLOAT)) // min
830 .Input(FakeInput(DT_FLOAT)) // max
831 .Finalize(node_def()));
832 TF_EXPECT_OK(InitOp());
833 // Upstream gradients.
834 AddRandomInput(TensorShape({2, 3}));
835 // Downstream inputs.
836 AddInputFromArray<float>(TensorShape({2, 3}),
837 {-0.6f, -0.5f, -0.4f, 0.0f, 6.5f, 6.6f});
838 // Min.
839 AddInputFromArray<float>(TensorShape({}), {-0.4f});
840 // Max.
841 AddInputFromArray<float>(TensorShape({}), {6.6f});
842
843 // Tested code.
844 TF_ASSERT_OK(RunOpKernel());
845
846 Tensor* output_bprop_wrt_input = GetOutput(0);
847 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({2, 3}));
848 auto in_flat = GetInput(0).flat<float>();
849 FillValues<float>(&expected_bprop_wrt_input, {0.0f, in_flat(1), in_flat(2),
850 in_flat(3), in_flat(4), 0.0f});
851 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
852
853 Tensor* output_bprop_wrt_min = GetOutput(1);
854 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({}));
855 expected_bprop_wrt_min.flat<float>()(0) = in_flat(0);
856 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
857
858 Tensor* output_bprop_wrt_max = GetOutput(2);
859 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({}));
860 expected_bprop_wrt_max.flat<float>()(0) = in_flat(5);
861 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
862 }
863
TEST_F(QuantOpsTest,WithVarsPerChannel_ZeroMinAndMax)864 TEST_F(QuantOpsTest, WithVarsPerChannel_ZeroMinAndMax) {
865 RunTestFakeQuantWithMinMaxVarsPerChannel(
866 8, false, TensorShape({4}), {0.0f, 0.0f, 0.0f, 0.0f},
867 {0.0f, 0.0f, 0.0f, 0.0f}, TensorShape({4}), {0.0f, 0.0f, 0.0f, 0.0f},
868 {0.0f, 0.0f, 0.0f, 0.0f});
869 }
870
TEST_F(QuantOpsTest,WithVarsPerChannelDim1NudgedDown_RegularRange)871 TEST_F(QuantOpsTest, WithVarsPerChannelDim1NudgedDown_RegularRange) {
872 // Original quantization ranges: [-0.4 / 4 + 0 / 4, -0.4 / 4 + 255 / 4].
873 // Scale: 1/4, original zero point: 0.4, nudged to 0.
874 // Nudged ranges: [0.0; 63.75].
875 // Expected quantized values: 0.0, 0.25, 0.5, ..., 63.75.
876 RunTestFakeQuantWithMinMaxVarsPerChannel(
877 8, false, TensorShape({4}), {-0.1f, -0.1f, -0.1f, -0.1f},
878 {63.65f, 63.65f, 63.65f, 63.65f}, TensorShape({4}),
879 {-0.1f, 0.0f, 63.75f, 63.8f}, {0.0f, 0.0f, 63.75f, 63.75f});
880 }
881
TEST_F(QuantOpsTest,WithVarsPerChannelDim1NudgedDown_NarrowRange)882 TEST_F(QuantOpsTest, WithVarsPerChannelDim1NudgedDown_NarrowRange) {
883 // Original quantization ranges: [-0.4 / 4 + 0 / 4, -0.4 / 4 + 254 / 4].
884 // Scale: 1/4, original zero point: 1.4, nudged to 1.
885 // Nudged ranges: [0.0; 63.5].
886 // Expected quantized values: 0.0, 0.25, 0.5, ..., 63.5.
887 RunTestFakeQuantWithMinMaxVarsPerChannel(
888 8, true, TensorShape({4}), {-0.1f, -0.1f, -0.1f, -0.1f},
889 {63.4f, 63.4f, 63.4f, 63.4f}, TensorShape({4}),
890 {-0.1f, 0.0f, 63.5f, 63.6f}, {0.0f, 0.0f, 63.5f, 63.5f});
891 }
892
TEST_F(QuantOpsTest,WithVarsPerChannelDim1NudgedUp_RegularRange)893 TEST_F(QuantOpsTest, WithVarsPerChannelDim1NudgedUp_RegularRange) {
894 // Original quantization ranges: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 255 / 4].
895 // Scale: 1/4, original zero point: 0.5, nudged to 1.
896 // Nudged ranges: [-0.25; 63.5].
897 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.5.
898 RunTestFakeQuantWithMinMaxVarsPerChannel(
899 8, false, TensorShape({4}), {-0.125f, -0.125f, -0.125f, -0.125f},
900 {63.625f, 63.625f, 63.625f, 63.625f}, TensorShape({4}),
901 {-0.26f, -0.25f, -0.24f, 63.6f}, {-0.25f, -0.25f, -0.25f, 63.5f});
902 }
903
TEST_F(QuantOpsTest,WithVarsPerChannelDim1NudgedUp_NarrowRange)904 TEST_F(QuantOpsTest, WithVarsPerChannelDim1NudgedUp_NarrowRange) {
905 // Original quantization ranges: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 254 / 4].
906 // Scale: 1/4, original zero point: 1.5, nudged to 2.
907 // Nudged ranges: [-0.25; 63.25].
908 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.25.
909 RunTestFakeQuantWithMinMaxVarsPerChannel(
910 8, true, TensorShape({4}), {-0.125f, -0.125f, -0.125f, -0.125f},
911 {63.375f, 63.375f, 63.375f, 63.375f}, TensorShape({4}),
912 {-0.26f, -0.25f, -0.24f, 63.3f}, {-0.25f, -0.25f, -0.25f, 63.25f});
913 }
914
TEST_F(QuantOpsTest,WithVarsPerChannelDim2NudgedDown_RegularRange)915 TEST_F(QuantOpsTest, WithVarsPerChannelDim2NudgedDown_RegularRange) {
916 // Original quantization ranges: [-0.4 / 4 + 0 / 4, -0.4 / 4 + 255 / 4].
917 // Scale: 1/4, original zero point: 0.4, nudged to 0.
918 // Nudged ranges: [0.0; 63.75].
919 // Expected quantized values: 0.0, 0.25, 0.5, ..., 63.75.
920 RunTestFakeQuantWithMinMaxVarsPerChannel(
921 8, false, TensorShape({3}), {-0.1f, -0.1f, -0.1f},
922 {63.65f, 63.65f, 63.65f}, TensorShape({2, 3}),
923 {-0.1f, 0.0f, 0.1f, 0.25f, 63.75f, 63.80f},
924 {-0.0f, 0.0f, 0.0f, 0.25f, 63.75f, 63.75f});
925 }
926
TEST_F(QuantOpsTest,WithVarsPerChannelDim2NudgedDown_NarrowRange)927 TEST_F(QuantOpsTest, WithVarsPerChannelDim2NudgedDown_NarrowRange) {
928 // Original quantization ranges: [-0.4 / 4 + 0 / 4, -0.4 / 4 + 254 / 4].
929 // Scale: 1/4, original zero point: 1.4, nudged to 1.
930 // Nudged ranges: [0.0; 63.5].
931 // Expected quantized values: 0.0, 0.25, 0.5, ..., 63.5.
932 RunTestFakeQuantWithMinMaxVarsPerChannel(
933 8, true, TensorShape({3}), {-0.1f, -0.1f, -0.1f}, {63.4f, 63.4f, 63.4f},
934 TensorShape({2, 3}), {-0.1f, 0.0f, 0.1f, 0.25f, 63.5f, 63.6f},
935 {0.0f, 0.0f, 0.0f, 0.25f, 63.5f, 63.5f});
936 }
937
TEST_F(QuantOpsTest,WithVarsPerChannelDim2NudgedUp_RegularRange)938 TEST_F(QuantOpsTest, WithVarsPerChannelDim2NudgedUp_RegularRange) {
939 // Original quantization ranges: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 255 / 4].
940 // Scale: 1/4, original zero point: 0.5, nudged to 1.
941 // Nudged ranges: [-0.25; 63.5].
942 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.5.
943 RunTestFakeQuantWithMinMaxVarsPerChannel(
944 8, false, TensorShape({3}), {-0.125f, -0.125f, -0.125f},
945 {63.625f, 63.625f, 63.625f}, TensorShape({2, 3}),
946 {-0.26f, -0.25f, -0.24f, 0.0f, 63.5f, 63.6f},
947 {-0.25f, -0.25f, -0.25f, 0.0f, 63.5f, 63.5f});
948 }
949
TEST_F(QuantOpsTest,WithVarsPerChannelDim2NudgedUp_NarrowRange)950 TEST_F(QuantOpsTest, WithVarsPerChannelDim2NudgedUp_NarrowRange) {
951 // Original quantization ranges: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 254 / 4].
952 // Scale: 1/4, original zero point: 1.5, nudged to 2.
953 // Nudged ranges: [-0.25; 63.25].
954 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.25.
955 RunTestFakeQuantWithMinMaxVarsPerChannel(
956 8, true, TensorShape({3}), {-0.125f, -0.125f, -0.125f},
957 {63.375f, 63.375f, 63.375f}, TensorShape({2, 3}),
958 {-0.26f, -0.25f, -0.24f, 0.0f, 63.25f, 63.3f},
959 {-0.25f, -0.25f, -0.25f, 0.0f, 63.25f, 63.25f});
960 }
961
TEST_F(QuantOpsTest,WithVarsPerChannelDim4NudgedDown_RegularRange)962 TEST_F(QuantOpsTest, WithVarsPerChannelDim4NudgedDown_RegularRange) {
963 // Original quantization ranges: [-0.4 / 4 + 0 / 4, -0.4 / 4 + 255 / 4].
964 // Scale: 1/4, original zero point: 0.4, nudged to 0.
965 // Nudged ranges: [0.0; 63.75].
966 // Expected quantized values: 0.0, 0.25, 0.5, ..., 63.75.
967 // clang-format off
968 RunTestFakeQuantWithMinMaxVarsPerChannel(
969 8, false,
970 TensorShape({4}), {-0.1f, -0.1f, -0.1f, -0.1f},
971 {63.65f, 63.65f, 63.65f, 63.65f},
972 TensorShape({1, 2, 3, 4}),
973 {-0.1f, 0.0f, 0.1f, 0.25f, 0.5f, 0.75f,
974 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.25f,
975 63.0f, 63.25f, 63.5f, 63.7f, 63.75f, 63.8f,
976 63.9f, 100.0f, 100.0f, 100.0f, 100.0f, 1000.0f},
977 { 0.0f, 0.0f, 0.0f, 0.25f, 0.5f, 0.75f,
978 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.25f,
979 63.0f, 63.25f, 63.5f, 63.75f, 63.75f, 63.75f,
980 63.75f, 63.75f, 63.75f, 63.75f, 63.75f, 63.75f});
981 // clang-format on
982 }
983
TEST_F(QuantOpsTest,WithVarsPerChannelDim4NudgedDown_NarrowRange)984 TEST_F(QuantOpsTest, WithVarsPerChannelDim4NudgedDown_NarrowRange) {
985 // Original quantization ranges: [-0.4 / 4 + 0 / 4, -0.4 / 4 + 254 / 4].
986 // Scale: 1/4, original zero point: 1.4, nudged to 1.
987 // Nudged ranges: [0.0; 63.5].
988 // Expected quantized values: 0.0, 0.25, 0.5, ..., 63.5.
989 // clang-format off
990 RunTestFakeQuantWithMinMaxVarsPerChannel(
991 8, true,
992 TensorShape({4}), {-0.1f, -0.1f, -0.1f, -0.1f},
993 {63.4f, 63.4f, 63.4f, 63.4f},
994 TensorShape({1, 2, 3, 4}),
995 {-0.1f, 0.0f, 0.1f, 0.25f, 0.5f, 0.75f,
996 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.25f,
997 63.0f, 63.25f, 63.3f, 63.4f, 63.5f, 63.6f,
998 63.7f, 100.0f, 100.0f, 100.0f, 100.0f, 1000.0f},
999 { 0.0f, 0.0f, 0.0f, 0.25f, 0.5f, 0.75f,
1000 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.25f,
1001 63.0f, 63.25f, 63.25f, 63.5f, 63.5f, 63.5f,
1002 63.5f, 63.5f, 63.5f, 63.5f, 63.5f, 63.5f});
1003 // clang-format on
1004 }
1005
TEST_F(QuantOpsTest,WithVarsPerChannelDim4NudgedUp_RegularRange)1006 TEST_F(QuantOpsTest, WithVarsPerChannelDim4NudgedUp_RegularRange) {
1007 // Original quantization ranges: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 255 / 4].
1008 // Scale: 1/4, original zero point: 0.5, nudged to 1.
1009 // Nudged ranges: [-0.25; 63.5].
1010 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.5.
1011 // clang-format off
1012 RunTestFakeQuantWithMinMaxVarsPerChannel(
1013 8, false,
1014 TensorShape({4}), {-0.125f, -0.125f, -0.125f, -0.125f},
1015 {63.625f, 63.625f, 63.625f, 63.625f},
1016 TensorShape({1, 2, 3, 4}),
1017 { -0.3f, -0.25f, -0.2f, 0.0f, 0.25f, 0.5f,
1018 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f,
1019 63.0f, 63.25f, 63.4f, 63.5f, 63.6f, 63.7f,
1020 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 1000.0f},
1021 {-0.25f, -0.25f, -0.25f, 0.0f, 0.25f, 0.5f,
1022 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f,
1023 63.0f, 63.25f, 63.5f, 63.5f, 63.5f, 63.5f,
1024 63.5f, 63.5f, 63.5f, 63.5f, 63.5f, 63.5f});
1025 // clang-format on
1026 }
1027
TEST_F(QuantOpsTest,WithVarsPerChannelDim4NudgedUp_NarrowRange)1028 TEST_F(QuantOpsTest, WithVarsPerChannelDim4NudgedUp_NarrowRange) {
1029 // Original quantization ranges: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 254 / 4].
1030 // Scale: 1/4, original zero point: 1.5, nudged to 2.
1031 // Nudged ranges: [-0.25; 63.25].
1032 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.25.
1033 // clang-format off
1034 RunTestFakeQuantWithMinMaxVarsPerChannel(
1035 8, true,
1036 TensorShape({4}), {-0.125f, -0.125f, -0.125f, -0.125f},
1037 {63.375f, 63.375f, 63.375f, 63.375f},
1038 TensorShape({1, 2, 3, 4}),
1039 { -0.3f, -0.25f, -0.2f, 0.0f, 0.25f, 0.5f,
1040 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f,
1041 63.0f, 63.2f, 63.25f, 63.3f, 63.4f, 63.5f,
1042 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 1000.0f},
1043 { -0.25f, -0.25f, -0.25f, 0.0f, 0.25f, 0.5f,
1044 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f,
1045 63.0f, 63.25f, 63.25f, 63.25f, 63.25f, 63.25f,
1046 63.25f, 63.25f, 63.25f, 63.25f, 63.25f, 63.25f});
1047 // clang-format on
1048 }
1049
TEST_F(QuantOpsTest,WithVarsPerChannelDim1NudgedDown_4Bits_RegularRange)1050 TEST_F(QuantOpsTest, WithVarsPerChannelDim1NudgedDown_4Bits_RegularRange) {
1051 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 15 / 2].
1052 // Scale: 1/2, original zero point: 0.2, nudged to 0.
1053 // Nudged range: [0.0; 7.5].
1054 // Expected quantized values: 0.0, 0.5, ..., 7.5.
1055 RunTestFakeQuantWithMinMaxVarsPerChannel(
1056 4, false, TensorShape({4}), {-0.1f, -0.1f, -0.1f, -0.1f},
1057 {7.4f, 7.4f, 7.4f, 7.4f}, TensorShape({4}), {-0.1f, 0.0f, 7.5f, 7.6f},
1058 {0.0f, 0.0f, 7.5f, 7.5f});
1059 }
1060
TEST_F(QuantOpsTest,WithVarsPerChannelDim1NudgedDown_4Bits_NarrowRange)1061 TEST_F(QuantOpsTest, WithVarsPerChannelDim1NudgedDown_4Bits_NarrowRange) {
1062 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 14 / 2].
1063 // Scale: 1/2, original zero point: 1.2, nudged to 1.
1064 // Nudged range: [0.0; 7.0].
1065 // Expected quantized values: 0.0, 0.5, ..., 7.0.
1066 RunTestFakeQuantWithMinMaxVarsPerChannel(
1067 4, true, TensorShape({4}), {-0.1f, -0.1f, -0.1f, -0.1f},
1068 {6.9f, 6.9f, 6.9f, 6.9f}, TensorShape({4}), {-0.1f, 0.0f, 7.0f, 7.1f},
1069 {0.0f, 0.0f, 7.0f, 7.0f});
1070 }
1071
TEST_F(QuantOpsTest,WithVarsPerChannelDim1NudgedUp_4Bits_RegularRange)1072 TEST_F(QuantOpsTest, WithVarsPerChannelDim1NudgedUp_4Bits_RegularRange) {
1073 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 15 / 2].
1074 // Scale: 1/2, original zero point: 0.8, nudged to 1.
1075 // Nudged range: [-0.5; 7.0].
1076 // Expected quantized values: -0.5, 0.0, 0.5, ..., 7.0.
1077 RunTestFakeQuantWithMinMaxVarsPerChannel(
1078 4, false, TensorShape({4}), {-0.4f, -0.4f, -0.4f, -0.4f},
1079 {7.1f, 7.1f, 7.1f, 7.1f}, TensorShape({4}), {-0.6f, -0.5f, 7.0f, 7.1f},
1080 {-0.5f, -0.5f, 7.0f, 7.0f});
1081 }
1082
TEST_F(QuantOpsTest,WithVarsPerChannelDim1NudgedUp_4Bits_NarrowRange)1083 TEST_F(QuantOpsTest, WithVarsPerChannelDim1NudgedUp_4Bits_NarrowRange) {
1084 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 14 / 2].
1085 // Scale: 1/2, original zero point: 1.8, nudged to 2.
1086 // Nudged range: [-0.5; 6.5].
1087 // Expected quantized values: -0.5, 0.0, 0.5, ..., 6.5.
1088 RunTestFakeQuantWithMinMaxVarsPerChannel(
1089 4, true, TensorShape({4}), {-0.4f, -0.4f, -0.4f, -0.4f},
1090 {6.6f, 6.6f, 6.6f, 6.6f}, TensorShape({4}), {-0.6f, -0.5f, 6.5f, 6.6f},
1091 {-0.5f, -0.5f, 6.5f, 6.5f});
1092 }
1093
TEST_F(QuantOpsTest,WithVarsPerChannelDim2NudgedDown_4Bits_RegularRange)1094 TEST_F(QuantOpsTest, WithVarsPerChannelDim2NudgedDown_4Bits_RegularRange) {
1095 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 15 / 2].
1096 // Scale: 1/2, original zero point: 0.2, nudged to 0.
1097 // Nudged range: [0.0; 7.5].
1098 // Expected quantized values: 0.0, 0.5, ..., 7.5.
1099 RunTestFakeQuantWithMinMaxVarsPerChannel(
1100 4, false, TensorShape({3}), {-0.1f, -0.1f, -0.1f}, {7.4f, 7.4f, 7.4f},
1101 TensorShape({2, 3}), {-0.1f, 0.0f, 0.1f, 0.5f, 7.5f, 7.6f},
1102 {0.0f, 0.0f, 0.0f, 0.5f, 7.5f, 7.5f});
1103 }
1104
TEST_F(QuantOpsTest,WithVarsPerChannelDim2NudgedDown_4Bits_NarrowRange)1105 TEST_F(QuantOpsTest, WithVarsPerChannelDim2NudgedDown_4Bits_NarrowRange) {
1106 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 14 / 2].
1107 // Scale: 1/2, original zero point: 1.2, nudged to 1.
1108 // Nudged range: [0.0; 7.0].
1109 // Expected quantized values: 0.0, 0.5, ..., 7.0.
1110 RunTestFakeQuantWithMinMaxVarsPerChannel(
1111 4, true, TensorShape({3}), {-0.1f, -0.1f, -0.1f}, {6.9f, 6.9f, 6.9f},
1112 TensorShape({2, 3}), {-0.1f, 0.0f, 0.1f, 0.5f, 7.0f, 7.1f},
1113 {0.0f, 0.0f, 0.0f, 0.5f, 7.0f, 7.0f});
1114 }
1115
TEST_F(QuantOpsTest,WithVarsPerChannelDim2NudgedUp_4Bits_RegularRange)1116 TEST_F(QuantOpsTest, WithVarsPerChannelDim2NudgedUp_4Bits_RegularRange) {
1117 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 15 / 2].
1118 // Scale: 1/2, original zero point: 0.8, nudged to 1.
1119 // Nudged range: [-0.5; 7.0].
1120 // Expected quantized values: -0.5, 0.0, 0.5, ..., 7.0.
1121 RunTestFakeQuantWithMinMaxVarsPerChannel(
1122 4, false, TensorShape({3}), {-0.4f, -0.4f, -0.4f}, {7.1f, 7.1f, 7.1f},
1123 TensorShape({2, 3}), {-0.51f, -0.5f, -0.24f, 0.0f, 7.0f, 7.1f},
1124 {-0.5f, -0.5f, 0.0f, 0.0f, 7.0f, 7.0f});
1125 }
1126
TEST_F(QuantOpsTest,WithVarsPerChannelDim2NudgedUp_4Bits_NarrowRange)1127 TEST_F(QuantOpsTest, WithVarsPerChannelDim2NudgedUp_4Bits_NarrowRange) {
1128 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 14 / 2].
1129 // Scale: 1/2, original zero point: 1.8, nudged to 2.
1130 // Nudged range: [-0.5; 6.5].
1131 // Expected quantized values: -0.5, 0.0, 0.5, ..., 7.0.
1132 RunTestFakeQuantWithMinMaxVarsPerChannel(
1133 4, true, TensorShape({3}), {-0.4f, -0.4f, -0.4f}, {6.6f, 6.6f, 6.6f},
1134 TensorShape({2, 3}), {-0.6f, -0.5f, -0.24f, 0.0f, 6.5f, 6.6f},
1135 {-0.5f, -0.5f, 0.0f, 0.0f, 6.5f, 6.5f});
1136 }
1137
TEST_F(QuantOpsTest,WithVarsPerChannelDim4NudgedDown_4Bits_RegularRange)1138 TEST_F(QuantOpsTest, WithVarsPerChannelDim4NudgedDown_4Bits_RegularRange) {
1139 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 15 / 2].
1140 // Scale: 1/2, original zero point: 0.2, nudged to 0.
1141 // Nudged range: [0.0; 7.5].
1142 // Expected quantized values: 0.0, 0.5, ..., 7.5.
1143 // clang-format off
1144 RunTestFakeQuantWithMinMaxVarsPerChannel(
1145 4, false,
1146 TensorShape({4}), {-0.1f, -0.1f, -0.1f, -0.1f}, {7.4f, 7.4f, 7.4f, 7.4f},
1147 TensorShape({1, 2, 3, 4}),
1148 {-0.1f, 0.0f, 0.1f, 0.5f, 1.0f, 1.5f,
1149 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f,
1150 6.0f, 6.5f, 7.0f, 7.4f, 7.5f, 7.7f,
1151 7.8f, 100.0f, 100.0f, 100.0f, 100.0f, 1000.0f},
1152 { 0.0f, 0.0f, 0.0f, 0.5f, 1.0f, 1.5f,
1153 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f,
1154 6.0f, 6.5f, 7.0f, 7.5f, 7.5f, 7.5f,
1155 7.5f, 7.5f, 7.5f, 7.5f, 7.5f, 7.5f});
1156 // clang-format on
1157 }
1158
TEST_F(QuantOpsTest,WithVarsPerChannelDim4NudgedDown_4Bits_NarrowRange)1159 TEST_F(QuantOpsTest, WithVarsPerChannelDim4NudgedDown_4Bits_NarrowRange) {
1160 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 14 / 2].
1161 // Scale: 1/2, original zero point: 1.2, nudged to 1.
1162 // Nudged range: [0.0; 7.0].
1163 // Expected quantized values: 0.0, 0.5, ..., 7.0.
1164 // clang-format off
1165 RunTestFakeQuantWithMinMaxVarsPerChannel(
1166 4, true,
1167 TensorShape({4}), {-0.1f, -0.1f, -0.1f, -0.1f}, {6.9f, 6.9f, 6.9f, 6.9f},
1168 TensorShape({1, 2, 3, 4}),
1169 {-0.1f, 0.0f, 0.1f, 0.5f, 1.0f, 1.5f,
1170 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f,
1171 6.0f, 6.5f, 6.8f, 6.9f, 7.0f, 7.1f,
1172 7.2f, 100.0f, 100.0f, 100.0f, 100.0f, 1000.0f},
1173 { 0.0f, 0.0f, 0.0f, 0.5f, 1.0f, 1.5f,
1174 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f,
1175 6.0f, 6.5f, 7.0f, 7.0f, 7.0f, 7.0f,
1176 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f});
1177 // clang-format on
1178 }
1179
TEST_F(QuantOpsTest,WithVarsPerChannelDim4NudgedUp_4Bits_RegularRange)1180 TEST_F(QuantOpsTest, WithVarsPerChannelDim4NudgedUp_4Bits_RegularRange) {
1181 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 15 / 2].
1182 // Scale: 1/2, original zero point: 0.8, nudged to 1.
1183 // Nudged range: [-0.5; 7.0].
1184 // Expected quantized values: -0.5, 0.0, 0.5, ..., 7.0.
1185 // clang-format off
1186 RunTestFakeQuantWithMinMaxVarsPerChannel(
1187 4, false,
1188 TensorShape({4}), {-0.4f, -0.4f, -0.4f, -0.4f}, {7.1f, 7.1f, 7.1f, 7.1f},
1189 TensorShape({1, 2, 3, 4}),
1190 { -0.6f, -0.5f, -0.4f, 0.0f, 0.5f, 1.0f,
1191 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f,
1192 6.0f, 6.5f, 6.9f, 7.0f, 7.1f, 7.7f,
1193 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 1000.0f},
1194 { -0.5f, -0.5f, -0.5f, 0.0f, 0.5f, 1.0f,
1195 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f,
1196 6.0f, 6.5f, 7.0f, 7.0f, 7.0f, 7.0f,
1197 7.0f, 7.0f, 7.0f, 7.0f, 7.0f, 7.0f});
1198 // clang-format on
1199 }
1200
TEST_F(QuantOpsTest,WithVarsPerChannelDim4NudgedUp_4Bits_NarrowRange)1201 TEST_F(QuantOpsTest, WithVarsPerChannelDim4NudgedUp_4Bits_NarrowRange) {
1202 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 14 / 2].
1203 // Scale: 1/2, original zero point: 1.8, nudged to 2.
1204 // Nudged range: [-0.5; 6.5].
1205 // Expected quantized values: -0.5, 0.0, 0.5, ..., 7.0.
1206 // clang-format off
1207 RunTestFakeQuantWithMinMaxVarsPerChannel(
1208 4, true,
1209 TensorShape({4}), {-0.4f, -0.4f, -0.4f, -0.4f}, {6.6f, 6.6f, 6.6f, 6.6f},
1210 TensorShape({1, 2, 3, 4}),
1211 { -0.6f, -0.5f, -0.4f, 0.0f, 0.5f, 1.0f,
1212 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f,
1213 5.5f, 6.0f, 6.4f, 6.5f, 6.6f, 6.7f,
1214 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 1000.0f},
1215 { -0.5f , -0.5f, -0.5f, 0.0f, 0.5f, 1.0f,
1216 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f,
1217 5.5f, 6.0f, 6.5f, 6.5f, 6.5f, 6.5f,
1218 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f});
1219 // clang-format on
1220 }
1221
TEST_F(QuantOpsTest,WithVarsPerChannelDim1GradientNudgedDown_ZeroMinAndMax)1222 TEST_F(QuantOpsTest, WithVarsPerChannelDim1GradientNudgedDown_ZeroMinAndMax) {
1223 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
1224 .Attr("narrow_range", false)
1225 .Input(FakeInput(DT_FLOAT)) // gradients
1226 .Input(FakeInput(DT_FLOAT)) // inputs
1227 .Input(FakeInput(DT_FLOAT)) // min
1228 .Input(FakeInput(DT_FLOAT)) // max
1229 .Finalize(node_def()));
1230 TF_EXPECT_OK(InitOp());
1231 // Upstream gradients.
1232 AddRandomInput(TensorShape({4}));
1233 // Downstream inputs.
1234 AddInputFromArray<float>(TensorShape({4}), {0.0, 0.0, 0.0, 0.0f});
1235 // Min.
1236 AddInputFromArray<float>(TensorShape({4}), {0.0, 0.0, 0.0, 0.0f});
1237 // Max.
1238 AddInputFromArray<float>(TensorShape({4}), {0.0, 0.0, 0.0, 0.0f});
1239
1240 // Tested code.
1241 TF_ASSERT_OK(RunOpKernel());
1242
1243 Tensor* output_bprop_wrt_input = GetOutput(0);
1244 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({4}));
1245 auto grad_flat = GetInput(0).flat<float>();
1246 FillValues<float>(&expected_bprop_wrt_input,
1247 {grad_flat(0), grad_flat(1), grad_flat(2), grad_flat(3)});
1248 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
1249
1250 Tensor* output_bprop_wrt_min = GetOutput(1);
1251 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
1252 FillValues<float>(&expected_bprop_wrt_min, {0.0f, 0.0f, 0.0f, 0.0f});
1253 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
1254
1255 Tensor* output_bprop_wrt_max = GetOutput(2);
1256 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
1257 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, 0.0f, 0.0f});
1258 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
1259 }
1260
TEST_F(QuantOpsTest,WithVarsPerChannelDim1GradientNudgedDown_RegularRange)1261 TEST_F(QuantOpsTest, WithVarsPerChannelDim1GradientNudgedDown_RegularRange) {
1262 // Original quantization ranges: [-0.4 / 4 + 0 / 4, -0.4 / 4 + 255 / 4].
1263 // Scale: 1/4, original zero point: 0.4, nudged to 0.
1264 // Nudged ranges: [0.0; 63.75].
1265 // Expected quantized values: 0.0, 0.25, 0.5, ..., 63.75.
1266 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
1267 .Attr("narrow_range", false)
1268 .Input(FakeInput(DT_FLOAT)) // gradients
1269 .Input(FakeInput(DT_FLOAT)) // inputs
1270 .Input(FakeInput(DT_FLOAT)) // min
1271 .Input(FakeInput(DT_FLOAT)) // max
1272 .Finalize(node_def()));
1273 TF_EXPECT_OK(InitOp());
1274 // Upstream gradients.
1275 AddRandomInput(TensorShape({4}));
1276 // Downstream inputs.
1277 AddInputFromArray<float>(TensorShape({4}), {-0.1f, 0.0f, 63.75f, 63.8f});
1278 // Min.
1279 AddInputFromArray<float>(TensorShape({4}), {-0.1f, -0.1f, -0.1f, -0.1f});
1280 // Max.
1281 AddInputFromArray<float>(TensorShape({4}), {63.65f, 63.65f, 63.65f, 63.65f});
1282
1283 // Tested code.
1284 TF_ASSERT_OK(RunOpKernel());
1285
1286 Tensor* output_bprop_wrt_input = GetOutput(0);
1287 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({4}));
1288 auto grad_flat = GetInput(0).flat<float>();
1289 FillValues<float>(&expected_bprop_wrt_input,
1290 {0.0f, grad_flat(1), grad_flat(2), 0.0f});
1291 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
1292
1293 Tensor* output_bprop_wrt_min = GetOutput(1);
1294 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
1295 FillValues<float>(&expected_bprop_wrt_min, {grad_flat(0), 0.0f, 0.0f, 0.0f});
1296 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
1297
1298 Tensor* output_bprop_wrt_max = GetOutput(2);
1299 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
1300 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, 0.0f, grad_flat(3)});
1301 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
1302 }
1303
TEST_F(QuantOpsTest,WithVarsPerChannelDim1GradientNudgedDown_NarrowRange)1304 TEST_F(QuantOpsTest, WithVarsPerChannelDim1GradientNudgedDown_NarrowRange) {
1305 // Original quantization ranges: [-0.4 / 4 + 0 / 4, -0.4 / 4 + 254 / 4].
1306 // Scale: 1/4, original zero point: 1.4, nudged to 1.
1307 // Nudged ranges: [0.0; 63.5].
1308 // Expected quantized values: 0.0, 0.25, 0.5, ..., 63.5.
1309 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
1310 .Attr("narrow_range", true)
1311 .Input(FakeInput(DT_FLOAT)) // gradients
1312 .Input(FakeInput(DT_FLOAT)) // inputs
1313 .Input(FakeInput(DT_FLOAT)) // min
1314 .Input(FakeInput(DT_FLOAT)) // max
1315 .Finalize(node_def()));
1316 TF_EXPECT_OK(InitOp());
1317 // Upstream gradients.
1318 AddRandomInput(TensorShape({4}));
1319 // Downstream inputs.
1320 AddInputFromArray<float>(TensorShape({4}), {-0.1f, 0.0f, 63.5f, 63.6f});
1321 // Min.
1322 AddInputFromArray<float>(TensorShape({4}), {-0.1f, -0.1f, -0.1f, -0.1f});
1323 // Max.
1324 AddInputFromArray<float>(TensorShape({4}), {63.4f, 63.4f, 63.4f, 63.4f});
1325
1326 // Tested code.
1327 TF_ASSERT_OK(RunOpKernel());
1328
1329 Tensor* output_bprop_wrt_input = GetOutput(0);
1330 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({4}));
1331 auto grad_flat = GetInput(0).flat<float>();
1332 FillValues<float>(&expected_bprop_wrt_input,
1333 {0.0f, grad_flat(1), grad_flat(2), 0.0f});
1334 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
1335
1336 Tensor* output_bprop_wrt_min = GetOutput(1);
1337 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
1338 FillValues<float>(&expected_bprop_wrt_min, {grad_flat(0), 0.0f, 0.0f, 0.0f});
1339 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
1340
1341 Tensor* output_bprop_wrt_max = GetOutput(2);
1342 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
1343 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, 0.0f, grad_flat(3)});
1344 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
1345 }
1346
TEST_F(QuantOpsTest,WithVarsPerChannelDim1GradientNudgedUp_RegularRange)1347 TEST_F(QuantOpsTest, WithVarsPerChannelDim1GradientNudgedUp_RegularRange) {
1348 // Original quantization ranges: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 255 / 4].
1349 // Scale: 1/4, original zero point: 0.5, nudged to 1.
1350 // Nudged ranges: [-0.25; 63.5].
1351 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.5.
1352 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
1353 .Attr("narrow_range", false)
1354 .Input(FakeInput(DT_FLOAT)) // gradients
1355 .Input(FakeInput(DT_FLOAT)) // inputs
1356 .Input(FakeInput(DT_FLOAT)) // min
1357 .Input(FakeInput(DT_FLOAT)) // max
1358 .Finalize(node_def()));
1359 TF_EXPECT_OK(InitOp());
1360 // Upstream gradients.
1361 AddRandomInput(TensorShape({4}));
1362 // Downstream inputs.
1363 AddInputFromArray<float>(TensorShape({4}), {-0.3f, -0.25f, 63.5f, 63.6f});
1364 // Min.
1365 AddInputFromArray<float>(TensorShape({4}),
1366 {-0.125f, -0.125f, -0.125f, -0.125f});
1367 // Max.
1368 AddInputFromArray<float>(TensorShape({4}),
1369 {63.625f, 63.625f, 63.625f, 63.625f});
1370
1371 // Tested code.
1372 TF_ASSERT_OK(RunOpKernel());
1373
1374 Tensor* output_bprop_wrt_input = GetOutput(0);
1375 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({4}));
1376 auto grad_flat = GetInput(0).flat<float>();
1377 FillValues<float>(&expected_bprop_wrt_input,
1378 {0.0f, grad_flat(1), grad_flat(2), 0.0f});
1379 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
1380
1381 Tensor* output_bprop_wrt_min = GetOutput(1);
1382 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
1383 FillValues<float>(&expected_bprop_wrt_min, {grad_flat(0), 0.0f, 0.0f, 0.0f});
1384 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
1385
1386 Tensor* output_bprop_wrt_max = GetOutput(2);
1387 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
1388 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, 0.0f, grad_flat(3)});
1389 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
1390 }
1391
TEST_F(QuantOpsTest,WithVarsPerChannelDim1GradientNudgedUp_NarrowRange)1392 TEST_F(QuantOpsTest, WithVarsPerChannelDim1GradientNudgedUp_NarrowRange) {
1393 // Original quantization ranges: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 254 / 4].
1394 // Scale: 1/4, original zero point: 1.5, nudged to 2.
1395 // Nudged ranges: [-0.25; 63.25].
1396 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.25.
1397 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
1398 .Attr("narrow_range", true)
1399 .Input(FakeInput(DT_FLOAT)) // gradients
1400 .Input(FakeInput(DT_FLOAT)) // inputs
1401 .Input(FakeInput(DT_FLOAT)) // min
1402 .Input(FakeInput(DT_FLOAT)) // max
1403 .Finalize(node_def()));
1404 TF_EXPECT_OK(InitOp());
1405 // Upstream gradients.
1406 AddRandomInput(TensorShape({4}));
1407 // Downstream inputs.
1408 AddInputFromArray<float>(TensorShape({4}), {-0.3f, -0.25f, 63.25f, 63.3f});
1409 // Min.
1410 AddInputFromArray<float>(TensorShape({4}),
1411 {-0.125f, -0.125f, -0.125f, -0.125f});
1412 // Max.
1413 AddInputFromArray<float>(TensorShape({4}),
1414 {63.375f, 63.375f, 63.375f, 63.375f});
1415
1416 // Tested code.
1417 TF_ASSERT_OK(RunOpKernel());
1418
1419 Tensor* output_bprop_wrt_input = GetOutput(0);
1420 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({4}));
1421 auto grad_flat = GetInput(0).flat<float>();
1422 FillValues<float>(&expected_bprop_wrt_input,
1423 {0.0f, grad_flat(1), grad_flat(2), 0.0f});
1424 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
1425
1426 Tensor* output_bprop_wrt_min = GetOutput(1);
1427 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
1428 FillValues<float>(&expected_bprop_wrt_min, {grad_flat(0), 0.0f, 0.0f, 0.0f});
1429 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
1430
1431 Tensor* output_bprop_wrt_max = GetOutput(2);
1432 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
1433 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, 0.0f, grad_flat(3)});
1434 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
1435 }
1436
TEST_F(QuantOpsTest,WithVarsPerChannelDim2GradientNudgedDown_RegularRange)1437 TEST_F(QuantOpsTest, WithVarsPerChannelDim2GradientNudgedDown_RegularRange) {
1438 // Original quantization ranges: [-0.4 / 4 + 0 / 4, -0.4 / 4 + 255 / 4].
1439 // Scale: 1/4, original zero point: 0.4, nudged to 0.
1440 // Nudged ranges: [0.0; 63.75].
1441 // Expected quantized values: 0.0, 0.25, 0.5, ..., 63.75.
1442 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
1443 .Attr("narrow_range", false)
1444 .Input(FakeInput(DT_FLOAT)) // gradients
1445 .Input(FakeInput(DT_FLOAT)) // inputs
1446 .Input(FakeInput(DT_FLOAT)) // min
1447 .Input(FakeInput(DT_FLOAT)) // max
1448 .Finalize(node_def()));
1449 TF_EXPECT_OK(InitOp());
1450 // Upstream gradients.
1451 AddRandomInput(TensorShape({2, 3}));
1452 // Downstream inputs.
1453 AddInputFromArray<float>(TensorShape({2, 3}),
1454 {-0.1f, 0.0f, 0.1f, 0.25f, 63.75f, 63.8f});
1455 // Min.
1456 AddInputFromArray<float>(TensorShape({3}), {-0.1f, -0.1f, -0.1f});
1457 // Max.
1458 AddInputFromArray<float>(TensorShape({3}), {63.65f, 63.65f, 63.65f});
1459
1460 // Tested code.
1461 TF_ASSERT_OK(RunOpKernel());
1462
1463 Tensor* output_bprop_wrt_input = GetOutput(0);
1464 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({2, 3}));
1465 auto grad_flat = GetInput(0).flat<float>();
1466 FillValues<float>(
1467 &expected_bprop_wrt_input,
1468 {0.0f, grad_flat(1), grad_flat(2), grad_flat(3), grad_flat(4), 0.0f});
1469 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
1470
1471 Tensor* output_bprop_wrt_min = GetOutput(1);
1472 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({3}));
1473 FillValues<float>(&expected_bprop_wrt_min, {grad_flat(0), 0.0f, 0.0f});
1474 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
1475
1476 Tensor* output_bprop_wrt_max = GetOutput(2);
1477 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({3}));
1478 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, grad_flat(5)});
1479 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
1480 }
1481
TEST_F(QuantOpsTest,WithVarsPerChannelDim2GradientNudgedDown_NarrowRange)1482 TEST_F(QuantOpsTest, WithVarsPerChannelDim2GradientNudgedDown_NarrowRange) {
1483 // Original quantization ranges: [-0.4 / 4 + 0 / 4, -0.4 / 4 + 254 / 4].
1484 // Scale: 1/4, original zero point: 1.4, nudged to 1.
1485 // Nudged ranges: [0.0; 63.5].
1486 // Expected quantized values: 0.0, 0.25, 0.5, ..., 63.5.
1487 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
1488 .Attr("narrow_range", true)
1489 .Input(FakeInput(DT_FLOAT)) // gradients
1490 .Input(FakeInput(DT_FLOAT)) // inputs
1491 .Input(FakeInput(DT_FLOAT)) // min
1492 .Input(FakeInput(DT_FLOAT)) // max
1493 .Finalize(node_def()));
1494 TF_EXPECT_OK(InitOp());
1495 // Upstream gradients.
1496 AddRandomInput(TensorShape({2, 3}));
1497 // Downstream inputs.
1498 AddInputFromArray<float>(TensorShape({2, 3}),
1499 {-0.1f, 0.0f, 0.1f, 0.25f, 63.5f, 63.6f});
1500 // Min.
1501 AddInputFromArray<float>(TensorShape({3}), {-0.1f, -0.1f, -0.1f});
1502 // Max.
1503 AddInputFromArray<float>(TensorShape({3}), {63.4f, 63.4f, 63.4f});
1504
1505 // Tested code.
1506 TF_ASSERT_OK(RunOpKernel());
1507
1508 Tensor* output_bprop_wrt_input = GetOutput(0);
1509 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({2, 3}));
1510 auto grad_flat = GetInput(0).flat<float>();
1511 FillValues<float>(
1512 &expected_bprop_wrt_input,
1513 {0.0f, grad_flat(1), grad_flat(2), grad_flat(3), grad_flat(4), 0.0f});
1514 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
1515
1516 Tensor* output_bprop_wrt_min = GetOutput(1);
1517 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({3}));
1518 FillValues<float>(&expected_bprop_wrt_min, {grad_flat(0), 0.0f, 0.0f});
1519 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
1520
1521 Tensor* output_bprop_wrt_max = GetOutput(2);
1522 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({3}));
1523 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, grad_flat(5)});
1524 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
1525 }
1526
TEST_F(QuantOpsTest,WithVarsPerChannelDim2GradientNudgedUp_RegularRange)1527 TEST_F(QuantOpsTest, WithVarsPerChannelDim2GradientNudgedUp_RegularRange) {
1528 // Original quantization ranges: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 255 / 4].
1529 // Scale: 1/4, original zero point: 0.5, nudged to 1.
1530 // Nudged ranges: [-0.25; 63.5].
1531 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.5.
1532 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
1533 .Attr("narrow_range", false)
1534 .Input(FakeInput(DT_FLOAT)) // gradients
1535 .Input(FakeInput(DT_FLOAT)) // inputs
1536 .Input(FakeInput(DT_FLOAT)) // min
1537 .Input(FakeInput(DT_FLOAT)) // max
1538 .Finalize(node_def()));
1539 TF_EXPECT_OK(InitOp());
1540 // Upstream gradients.
1541 AddRandomInput(TensorShape({2, 3}));
1542 // Downstream inputs.
1543 AddInputFromArray<float>(TensorShape({2, 3}),
1544 {-0.3f, -0.25f, -0.2f, 0.0f, 63.5f, 63.6f});
1545 // Min.
1546 AddInputFromArray<float>(TensorShape({3}), {-0.125f, -0.125f, -0.125f});
1547 // Max.
1548 AddInputFromArray<float>(TensorShape({3}), {63.625f, 63.625f, 63.625f});
1549
1550 // Tested code.
1551 TF_ASSERT_OK(RunOpKernel());
1552
1553 Tensor* output_bprop_wrt_input = GetOutput(0);
1554 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({2, 3}));
1555 auto grad_flat = GetInput(0).flat<float>();
1556 FillValues<float>(
1557 &expected_bprop_wrt_input,
1558 {0.0f, grad_flat(1), grad_flat(2), grad_flat(3), grad_flat(4), 0.0f});
1559 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
1560
1561 Tensor* output_bprop_wrt_min = GetOutput(1);
1562 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({3}));
1563 FillValues<float>(&expected_bprop_wrt_min, {grad_flat(0), 0.0f, 0.0f});
1564 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
1565
1566 Tensor* output_bprop_wrt_max = GetOutput(2);
1567 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({3}));
1568 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, grad_flat(5)});
1569 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
1570 }
1571
TEST_F(QuantOpsTest,WithVarsPerChannelDim2GradientNudgedUp_NarrowRange)1572 TEST_F(QuantOpsTest, WithVarsPerChannelDim2GradientNudgedUp_NarrowRange) {
1573 // Original quantization ranges: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 254 / 4].
1574 // Scale: 1/4, original zero point: 1.5, nudged to 2.
1575 // Nudged ranges: [-0.25; 63.25].
1576 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.25.
1577 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
1578 .Attr("narrow_range", true)
1579 .Input(FakeInput(DT_FLOAT)) // gradients
1580 .Input(FakeInput(DT_FLOAT)) // inputs
1581 .Input(FakeInput(DT_FLOAT)) // min
1582 .Input(FakeInput(DT_FLOAT)) // max
1583 .Finalize(node_def()));
1584 TF_EXPECT_OK(InitOp());
1585 // Upstream gradients.
1586 AddRandomInput(TensorShape({2, 3}));
1587 // Downstream inputs.
1588 AddInputFromArray<float>(TensorShape({2, 3}),
1589 {-0.3f, -0.25f, -0.2f, 0.0f, 63.25f, 63.3f});
1590 // Min.
1591 AddInputFromArray<float>(TensorShape({3}), {-0.125f, -0.125f, -0.125f});
1592 // Max.
1593 AddInputFromArray<float>(TensorShape({3}), {63.375f, 63.375f, 63.375f});
1594
1595 // Tested code.
1596 TF_ASSERT_OK(RunOpKernel());
1597
1598 Tensor* output_bprop_wrt_input = GetOutput(0);
1599 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({2, 3}));
1600 auto grad_flat = GetInput(0).flat<float>();
1601 FillValues<float>(
1602 &expected_bprop_wrt_input,
1603 {0.0f, grad_flat(1), grad_flat(2), grad_flat(3), grad_flat(4), 0.0f});
1604 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
1605
1606 Tensor* output_bprop_wrt_min = GetOutput(1);
1607 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({3}));
1608 FillValues<float>(&expected_bprop_wrt_min, {grad_flat(0), 0.0f, 0.0f});
1609 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
1610
1611 Tensor* output_bprop_wrt_max = GetOutput(2);
1612 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({3}));
1613 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, grad_flat(5)});
1614 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
1615 }
1616
TEST_F(QuantOpsTest,WithVarsPerChannelDim4GradientNudgedDown_RegularRange)1617 TEST_F(QuantOpsTest, WithVarsPerChannelDim4GradientNudgedDown_RegularRange) {
1618 // Original quantization ranges: [-0.4 / 4 + 0 / 4, -0.4 / 4 + 255 / 4].
1619 // Scale: 1/4, original zero point: 0.4, nudged to 0.
1620 // Nudged ranges: [0.0; 63.75].
1621 // Expected quantized values: 0.0, 0.25, 0.5, ..., 63.75.
1622 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
1623 .Attr("narrow_range", false)
1624 .Input(FakeInput(DT_FLOAT)) // gradients
1625 .Input(FakeInput(DT_FLOAT)) // inputs
1626 .Input(FakeInput(DT_FLOAT)) // min
1627 .Input(FakeInput(DT_FLOAT)) // max
1628 .Finalize(node_def()));
1629 TF_EXPECT_OK(InitOp());
1630 // Upstream gradients.
1631 AddRandomInput(TensorShape({1, 2, 3, 4}));
1632 // Downstream inputs.
1633 // clang-format off
1634 AddInputFromArray<float>(TensorShape({1, 2, 3, 4}),
1635 {-0.1f, 0.0f, 63.75f, 63.8f, -0.1f, 0.0f,
1636 63.75f, 63.8f, -0.1f, 0.0f, 63.75f, 63.8f,
1637 -0.1f, 0.0f, 63.75f, 63.8f, -0.1f, 0.0f,
1638 63.75f, 63.8f, -0.1f, 0.0f, 63.75f, 63.8f});
1639 // clang-format on
1640 // Min.
1641 AddInputFromArray<float>(TensorShape({4}), {-0.1f, -0.1f, -0.1f, -0.1f});
1642 // Max.
1643 AddInputFromArray<float>(TensorShape({4}), {63.65f, 63.65f, 63.65f, 63.65f});
1644
1645 // Tested code.
1646 TF_ASSERT_OK(RunOpKernel());
1647
1648 Tensor* output_bprop_wrt_input = GetOutput(0);
1649 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT,
1650 TensorShape({1, 2, 3, 4}));
1651 auto grad_flat = GetInput(0).flat<float>();
1652 FillValues<float>(&expected_bprop_wrt_input,
1653 {0.0f, grad_flat(1), grad_flat(2), 0.0f,
1654 0.0f, grad_flat(5), grad_flat(6), 0.0f,
1655 0.0f, grad_flat(9), grad_flat(10), 0.0f,
1656 0.0f, grad_flat(13), grad_flat(14), 0.0f,
1657 0.0f, grad_flat(17), grad_flat(18), 0.0f,
1658 0.0f, grad_flat(21), grad_flat(22), 0.0f});
1659 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
1660
1661 Tensor* output_bprop_wrt_min = GetOutput(1);
1662 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
1663 FillValues<float>(&expected_bprop_wrt_min,
1664 {grad_flat(0) + grad_flat(4) + grad_flat(8) +
1665 grad_flat(12) + grad_flat(16) + grad_flat(20),
1666 0.0f, 0.0f, 0.0f});
1667 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
1668
1669 Tensor* output_bprop_wrt_max = GetOutput(2);
1670 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
1671 FillValues<float>(&expected_bprop_wrt_max,
1672 {0.0f, 0.0f, 0.0f,
1673 grad_flat(3) + grad_flat(7) + grad_flat(11) +
1674 grad_flat(15) + grad_flat(19) + grad_flat(23)});
1675 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
1676 }
1677
TEST_F(QuantOpsTest,WithVarsPerChannelDim4GradientNudgedDown_NarrowRange)1678 TEST_F(QuantOpsTest, WithVarsPerChannelDim4GradientNudgedDown_NarrowRange) {
1679 // Original quantization ranges: [-0.4 / 4 + 0 / 4, -0.4 / 4 + 254 / 4].
1680 // Scale: 1/4, original zero point: 1.4, nudged to 1.
1681 // Nudged ranges: [0.0; 63.5].
1682 // Expected quantized values: 0.0, 0.25, 0.5, ..., 63.5.
1683 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
1684 .Attr("narrow_range", true)
1685 .Input(FakeInput(DT_FLOAT)) // gradients
1686 .Input(FakeInput(DT_FLOAT)) // inputs
1687 .Input(FakeInput(DT_FLOAT)) // min
1688 .Input(FakeInput(DT_FLOAT)) // max
1689 .Finalize(node_def()));
1690 TF_EXPECT_OK(InitOp());
1691 // Upstream gradients.
1692 AddRandomInput(TensorShape({1, 2, 3, 4}));
1693 // Downstream inputs.
1694 // clang-format off
1695 AddInputFromArray<float>(TensorShape({1, 2, 3, 4}),
1696 {-0.1f, 0.0f, 63.5f, 63.6f, -0.1f, 0.0f,
1697 63.5f, 63.6f, -0.1f, 0.0f, 63.5f, 63.6f,
1698 -0.1f, 0.0f, 63.5f, 63.6f, -0.1f, 0.0f,
1699 63.5f, 63.6f, -0.1f, 0.0f, 63.5f, 63.6f});
1700 // clang-format on
1701 // Min.
1702 AddInputFromArray<float>(TensorShape({4}), {-0.1f, -0.1f, -0.1f, -0.1f});
1703 // Max.
1704 AddInputFromArray<float>(TensorShape({4}), {63.4f, 63.4f, 63.4f, 63.4f});
1705
1706 // Tested code.
1707 TF_ASSERT_OK(RunOpKernel());
1708
1709 Tensor* output_bprop_wrt_input = GetOutput(0);
1710 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT,
1711 TensorShape({1, 2, 3, 4}));
1712 auto grad_flat = GetInput(0).flat<float>();
1713 FillValues<float>(&expected_bprop_wrt_input,
1714 {0.0f, grad_flat(1), grad_flat(2), 0.0f,
1715 0.0f, grad_flat(5), grad_flat(6), 0.0f,
1716 0.0f, grad_flat(9), grad_flat(10), 0.0f,
1717 0.0f, grad_flat(13), grad_flat(14), 0.0f,
1718 0.0f, grad_flat(17), grad_flat(18), 0.0f,
1719 0.0f, grad_flat(21), grad_flat(22), 0.0f});
1720 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
1721
1722 Tensor* output_bprop_wrt_min = GetOutput(1);
1723 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
1724 FillValues<float>(&expected_bprop_wrt_min,
1725 {grad_flat(0) + grad_flat(4) + grad_flat(8) +
1726 grad_flat(12) + grad_flat(16) + grad_flat(20),
1727 0.0f, 0.0f, 0.0f});
1728 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
1729
1730 Tensor* output_bprop_wrt_max = GetOutput(2);
1731 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
1732 FillValues<float>(&expected_bprop_wrt_max,
1733 {0.0f, 0.0f, 0.0f,
1734 grad_flat(3) + grad_flat(7) + grad_flat(11) +
1735 grad_flat(15) + grad_flat(19) + grad_flat(23)});
1736 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
1737 }
1738
TEST_F(QuantOpsTest,WithVarsPerChannelDim4GradientNudgedUp_RegularRange)1739 TEST_F(QuantOpsTest, WithVarsPerChannelDim4GradientNudgedUp_RegularRange) {
1740 // Original quantization ranges: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 255 / 4].
1741 // Scale: 1/4, original zero point: 0.5, nudged to 1.
1742 // Nudged ranges: [-0.25; 63.5].
1743 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.5.
1744 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
1745 .Attr("narrow_range", false)
1746 .Input(FakeInput(DT_FLOAT)) // gradients
1747 .Input(FakeInput(DT_FLOAT)) // inputs
1748 .Input(FakeInput(DT_FLOAT)) // min
1749 .Input(FakeInput(DT_FLOAT)) // max
1750 .Finalize(node_def()));
1751 TF_EXPECT_OK(InitOp());
1752 // Upstream gradients.
1753 AddRandomInput(TensorShape({1, 2, 3, 4}));
1754 // Downstream inputs.
1755 // clang-format off
1756 AddInputFromArray<float>(TensorShape({1, 2, 3, 4}),
1757 {-0.3f, -0.25f, 63.5f, 63.6f, -0.3f, -0.25f,
1758 63.5f, 63.6f, -0.3f, -0.25f, 63.5f, 63.6f,
1759 -0.3f, -0.25f, 63.5f, 63.6f, -0.3f, -0.25f,
1760 63.5f, 63.6f, -0.3f, -0.25f, 63.5f, 63.6f});
1761 // clang-format on
1762 // Min.
1763 AddInputFromArray<float>(TensorShape({4}),
1764 {-0.125f, -0.125f, -0.125f, -0.125f});
1765 // Max.
1766 AddInputFromArray<float>(TensorShape({4}),
1767 {63.625f, 63.625f, 63.625f, 63.625f});
1768
1769 // Tested code.
1770 TF_ASSERT_OK(RunOpKernel());
1771
1772 Tensor* output_bprop_wrt_input = GetOutput(0);
1773 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT,
1774 TensorShape({1, 2, 3, 4}));
1775 auto grad_flat = GetInput(0).flat<float>();
1776 FillValues<float>(&expected_bprop_wrt_input,
1777 {0.0f, grad_flat(1), grad_flat(2), 0.0f,
1778 0.0f, grad_flat(5), grad_flat(6), 0.0f,
1779 0.0f, grad_flat(9), grad_flat(10), 0.0f,
1780 0.0f, grad_flat(13), grad_flat(14), 0.0f,
1781 0.0f, grad_flat(17), grad_flat(18), 0.0f,
1782 0.0f, grad_flat(21), grad_flat(22), 0.0f});
1783 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
1784
1785 Tensor* output_bprop_wrt_min = GetOutput(1);
1786 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
1787 FillValues<float>(&expected_bprop_wrt_min,
1788 {grad_flat(0) + grad_flat(4) + grad_flat(8) +
1789 grad_flat(12) + grad_flat(16) + grad_flat(20),
1790 0.0f, 0.0f, 0.0f});
1791 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
1792
1793 Tensor* output_bprop_wrt_max = GetOutput(2);
1794 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
1795 FillValues<float>(&expected_bprop_wrt_max,
1796 {0.0f, 0.0f, 0.0f,
1797 grad_flat(3) + grad_flat(7) + grad_flat(11) +
1798 grad_flat(15) + grad_flat(19) + grad_flat(23)});
1799 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
1800 }
1801
TEST_F(QuantOpsTest,WithVarsPerChannelDim4GradientNudgedUp_NarrowRange)1802 TEST_F(QuantOpsTest, WithVarsPerChannelDim4GradientNudgedUp_NarrowRange) {
1803 // Original quantization ranges: [-0.5 / 4 + 0 / 4, -0.5 / 4 + 254 / 4].
1804 // Scale: 1/4, original zero point: 1.5, nudged to 2.
1805 // Nudged ranges: [-0.25; 63.25].
1806 // Expected quantized values: -0.25, 0.0, 0.25, ..., 63.25.
1807 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
1808 .Attr("narrow_range", true)
1809 .Input(FakeInput(DT_FLOAT)) // gradients
1810 .Input(FakeInput(DT_FLOAT)) // inputs
1811 .Input(FakeInput(DT_FLOAT)) // min
1812 .Input(FakeInput(DT_FLOAT)) // max
1813 .Finalize(node_def()));
1814 TF_EXPECT_OK(InitOp());
1815 // Upstream gradients.
1816 AddRandomInput(TensorShape({1, 2, 3, 4}));
1817 // Downstream inputs.
1818 // clang-format off
1819 AddInputFromArray<float>(TensorShape({1, 2, 3, 4}),
1820 { -0.3f, -0.25f, 63.25f, 63.3f, -0.3f, -0.25f,
1821 63.25f, 63.3f, -0.3f, -0.25f, 63.25f, 63.3f,
1822 -0.3f, -0.25f, 63.25f, 63.3f, -0.3f, -0.25f,
1823 63.25f, 63.3f, -0.3f, -0.25f, 63.25f, 63.3f});
1824 // clang-format on
1825 // Min.
1826 AddInputFromArray<float>(TensorShape({4}),
1827 {-0.125f, -0.125f, -0.125f, -0.125f});
1828 // Max.
1829 AddInputFromArray<float>(TensorShape({4}),
1830 {63.375f, 63.375f, 63.375f, 63.375f});
1831
1832 // Tested code.
1833 TF_ASSERT_OK(RunOpKernel());
1834
1835 Tensor* output_bprop_wrt_input = GetOutput(0);
1836 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT,
1837 TensorShape({1, 2, 3, 4}));
1838 auto grad_flat = GetInput(0).flat<float>();
1839 FillValues<float>(&expected_bprop_wrt_input,
1840 {0.0f, grad_flat(1), grad_flat(2), 0.0f,
1841 0.0f, grad_flat(5), grad_flat(6), 0.0f,
1842 0.0f, grad_flat(9), grad_flat(10), 0.0f,
1843 0.0f, grad_flat(13), grad_flat(14), 0.0f,
1844 0.0f, grad_flat(17), grad_flat(18), 0.0f,
1845 0.0f, grad_flat(21), grad_flat(22), 0.0f});
1846 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
1847
1848 Tensor* output_bprop_wrt_min = GetOutput(1);
1849 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
1850 FillValues<float>(&expected_bprop_wrt_min,
1851 {grad_flat(0) + grad_flat(4) + grad_flat(8) +
1852 grad_flat(12) + grad_flat(16) + grad_flat(20),
1853 0.0f, 0.0f, 0.0f});
1854 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
1855
1856 Tensor* output_bprop_wrt_max = GetOutput(2);
1857 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
1858 FillValues<float>(&expected_bprop_wrt_max,
1859 {0.0f, 0.0f, 0.0f,
1860 grad_flat(3) + grad_flat(7) + grad_flat(11) +
1861 grad_flat(15) + grad_flat(19) + grad_flat(23)});
1862 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
1863 }
1864
TEST_F(QuantOpsTest,WithVarsPerChannelDim1GradientNudgedDown_4Bits_RegularRange)1865 TEST_F(QuantOpsTest,
1866 WithVarsPerChannelDim1GradientNudgedDown_4Bits_RegularRange) {
1867 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 15 / 2].
1868 // Scale: 1/2, original zero point: 0.2, nudged to 0.
1869 // Nudged range: [0.0; 7.5].
1870 // Expected quantized values: 0.0, 0.5, ..., 7.5.
1871 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
1872 .Attr("num_bits", 4)
1873 .Attr("narrow_range", false)
1874 .Input(FakeInput(DT_FLOAT)) // gradients
1875 .Input(FakeInput(DT_FLOAT)) // inputs
1876 .Input(FakeInput(DT_FLOAT)) // min
1877 .Input(FakeInput(DT_FLOAT)) // max
1878 .Finalize(node_def()));
1879 TF_EXPECT_OK(InitOp());
1880 // Upstream gradients.
1881 AddRandomInput(TensorShape({4}));
1882 // Downstream inputs.
1883 AddInputFromArray<float>(TensorShape({4}), {-0.1f, 0.0f, 7.5f, 7.6f});
1884 // Min.
1885 AddInputFromArray<float>(TensorShape({4}), {-0.1f, -0.1f, -0.1f, -0.1f});
1886 // Max.
1887 AddInputFromArray<float>(TensorShape({4}), {7.4f, 7.4f, 7.4f, 7.4f});
1888
1889 // Tested code.
1890 TF_ASSERT_OK(RunOpKernel());
1891
1892 Tensor* output_bprop_wrt_input = GetOutput(0);
1893 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({4}));
1894 auto grad_flat = GetInput(0).flat<float>();
1895 FillValues<float>(&expected_bprop_wrt_input,
1896 {0.0f, grad_flat(1), grad_flat(2), 0.0f});
1897 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
1898
1899 Tensor* output_bprop_wrt_min = GetOutput(1);
1900 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
1901 FillValues<float>(&expected_bprop_wrt_min, {grad_flat(0), 0.0f, 0.0f, 0.0f});
1902 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
1903
1904 Tensor* output_bprop_wrt_max = GetOutput(2);
1905 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
1906 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, 0.0f, grad_flat(3)});
1907 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
1908 }
1909
TEST_F(QuantOpsTest,WithVarsPerChannelDim1GradientNudgedDown_4Bits_NarrowRange)1910 TEST_F(QuantOpsTest,
1911 WithVarsPerChannelDim1GradientNudgedDown_4Bits_NarrowRange) {
1912 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 14 / 2].
1913 // Scale: 1/2, original zero point: 1.2, nudged to 1.
1914 // Nudged range: [0.0; 7.0].
1915 // Expected quantized values: 0.0, 0.5, ..., 7.0.
1916 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
1917 .Attr("num_bits", 4)
1918 .Attr("narrow_range", true)
1919 .Input(FakeInput(DT_FLOAT)) // gradients
1920 .Input(FakeInput(DT_FLOAT)) // inputs
1921 .Input(FakeInput(DT_FLOAT)) // min
1922 .Input(FakeInput(DT_FLOAT)) // max
1923 .Finalize(node_def()));
1924 TF_EXPECT_OK(InitOp());
1925 // Upstream gradients.
1926 AddRandomInput(TensorShape({4}));
1927 // Downstream inputs.
1928 AddInputFromArray<float>(TensorShape({4}), {-0.1f, 0.0f, 7.0f, 7.1f});
1929 // Min.
1930 AddInputFromArray<float>(TensorShape({4}), {-0.1f, -0.1f, -0.1f, -0.1f});
1931 // Max.
1932 AddInputFromArray<float>(TensorShape({4}), {6.9f, 6.9f, 6.9f, 6.9f});
1933
1934 // Tested code.
1935 TF_ASSERT_OK(RunOpKernel());
1936
1937 Tensor* output_bprop_wrt_input = GetOutput(0);
1938 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({4}));
1939 auto grad_flat = GetInput(0).flat<float>();
1940 FillValues<float>(&expected_bprop_wrt_input,
1941 {0.0f, grad_flat(1), grad_flat(2), 0.0f});
1942 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
1943
1944 Tensor* output_bprop_wrt_min = GetOutput(1);
1945 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
1946 FillValues<float>(&expected_bprop_wrt_min, {grad_flat(0), 0.0f, 0.0f, 0.0f});
1947 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
1948
1949 Tensor* output_bprop_wrt_max = GetOutput(2);
1950 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
1951 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, 0.0f, grad_flat(3)});
1952 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
1953 }
1954
TEST_F(QuantOpsTest,WithVarsPerChannelDim1GradientNudgedUp_4Bits_RegularRange)1955 TEST_F(QuantOpsTest,
1956 WithVarsPerChannelDim1GradientNudgedUp_4Bits_RegularRange) {
1957 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 15 / 2].
1958 // Scale: 1/2, original zero point: 0.8, nudged to 1.
1959 // Nudged range: [-0.5; 7.0].
1960 // Expected quantized values: -0.5, 0.0, 0.5, ..., 7.0.
1961 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
1962 .Attr("num_bits", 4)
1963 .Attr("narrow_range", false)
1964 .Input(FakeInput(DT_FLOAT)) // gradients
1965 .Input(FakeInput(DT_FLOAT)) // inputs
1966 .Input(FakeInput(DT_FLOAT)) // min
1967 .Input(FakeInput(DT_FLOAT)) // max
1968 .Finalize(node_def()));
1969 TF_EXPECT_OK(InitOp());
1970 // Upstream gradients.
1971 AddRandomInput(TensorShape({4}));
1972 // Downstream inputs.
1973 AddInputFromArray<float>(TensorShape({4}), {-0.6f, -0.5f, 7.0f, 7.1f});
1974 // Min.
1975 AddInputFromArray<float>(TensorShape({4}), {-0.4f, -0.4f, -0.4f, -0.4f});
1976 // Max.
1977 AddInputFromArray<float>(TensorShape({4}), {7.1f, 7.1f, 7.1f, 7.1f});
1978
1979 // Tested code.
1980 TF_ASSERT_OK(RunOpKernel());
1981
1982 Tensor* output_bprop_wrt_input = GetOutput(0);
1983 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({4}));
1984 auto grad_flat = GetInput(0).flat<float>();
1985 FillValues<float>(&expected_bprop_wrt_input,
1986 {0.0f, grad_flat(1), grad_flat(2), 0.0f});
1987 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
1988
1989 Tensor* output_bprop_wrt_min = GetOutput(1);
1990 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
1991 FillValues<float>(&expected_bprop_wrt_min, {grad_flat(0), 0.0f, 0.0f, 0.0f});
1992 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
1993
1994 Tensor* output_bprop_wrt_max = GetOutput(2);
1995 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
1996 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, 0.0f, grad_flat(3)});
1997 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
1998 }
1999
TEST_F(QuantOpsTest,WithVarsPerChannelDim1GradientNudgedUp_4Bits_NarrowRange)2000 TEST_F(QuantOpsTest, WithVarsPerChannelDim1GradientNudgedUp_4Bits_NarrowRange) {
2001 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 14 / 2].
2002 // Scale: 1/2, original zero point: 1.8, nudged to 2.
2003 // Nudged range: [-0.5; 6.5].
2004 // Expected quantized values: -0.5, 0.0, 0.5, ..., 6.5.
2005 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
2006 .Attr("num_bits", 4)
2007 .Attr("narrow_range", true)
2008 .Input(FakeInput(DT_FLOAT)) // gradients
2009 .Input(FakeInput(DT_FLOAT)) // inputs
2010 .Input(FakeInput(DT_FLOAT)) // min
2011 .Input(FakeInput(DT_FLOAT)) // max
2012 .Finalize(node_def()));
2013 TF_EXPECT_OK(InitOp());
2014 // Upstream gradients.
2015 AddRandomInput(TensorShape({4}));
2016 // Downstream inputs.
2017 AddInputFromArray<float>(TensorShape({4}), {-0.6f, -0.5f, 6.5f, 6.6f});
2018 // Min.
2019 AddInputFromArray<float>(TensorShape({4}), {-0.4f, -0.4f, -0.4f, -0.4f});
2020 // Max.
2021 AddInputFromArray<float>(TensorShape({4}), {6.6f, 6.6f, 6.6f, 6.6f});
2022
2023 // Tested code.
2024 TF_ASSERT_OK(RunOpKernel());
2025
2026 Tensor* output_bprop_wrt_input = GetOutput(0);
2027 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({4}));
2028 auto grad_flat = GetInput(0).flat<float>();
2029 FillValues<float>(&expected_bprop_wrt_input,
2030 {0.0f, grad_flat(1), grad_flat(2), 0.0f});
2031 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
2032
2033 Tensor* output_bprop_wrt_min = GetOutput(1);
2034 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
2035 FillValues<float>(&expected_bprop_wrt_min, {grad_flat(0), 0.0f, 0.0f, 0.0f});
2036 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
2037
2038 Tensor* output_bprop_wrt_max = GetOutput(2);
2039 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
2040 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, 0.0f, grad_flat(3)});
2041 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
2042 }
2043
TEST_F(QuantOpsTest,WithVarsPerChannelDim2GradientNudgedDown_4Bits_RegularRange)2044 TEST_F(QuantOpsTest,
2045 WithVarsPerChannelDim2GradientNudgedDown_4Bits_RegularRange) {
2046 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 15 / 2].
2047 // Scale: 1/2, original zero point: 0.2, nudged to 0.
2048 // Nudged range: [0.0; 7.5].
2049 // Expected quantized values: 0.0, 0.5, ..., 7.5.
2050 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
2051 .Attr("num_bits", 4)
2052 .Attr("narrow_range", false)
2053 .Input(FakeInput(DT_FLOAT)) // gradients
2054 .Input(FakeInput(DT_FLOAT)) // inputs
2055 .Input(FakeInput(DT_FLOAT)) // min
2056 .Input(FakeInput(DT_FLOAT)) // max
2057 .Finalize(node_def()));
2058 TF_EXPECT_OK(InitOp());
2059 // Upstream gradients.
2060 AddRandomInput(TensorShape({2, 3}));
2061 // Downstream inputs.
2062 AddInputFromArray<float>(TensorShape({2, 3}),
2063 {-0.1f, 0.0f, 0.1f, 0.5f, 7.5f, 7.6f});
2064 // Min.
2065 AddInputFromArray<float>(TensorShape({3}), {-0.1f, -0.1f, -0.1f});
2066 // Max.
2067 AddInputFromArray<float>(TensorShape({3}), {7.4f, 7.4f, 7.4f});
2068
2069 // Tested code.
2070 TF_ASSERT_OK(RunOpKernel());
2071
2072 Tensor* output_bprop_wrt_input = GetOutput(0);
2073 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({2, 3}));
2074 auto grad_flat = GetInput(0).flat<float>();
2075 FillValues<float>(
2076 &expected_bprop_wrt_input,
2077 {0.0f, grad_flat(1), grad_flat(2), grad_flat(3), grad_flat(4), 0.0f});
2078 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
2079
2080 Tensor* output_bprop_wrt_min = GetOutput(1);
2081 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({3}));
2082 FillValues<float>(&expected_bprop_wrt_min, {grad_flat(0), 0.0f, 0.0f});
2083 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
2084
2085 Tensor* output_bprop_wrt_max = GetOutput(2);
2086 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({3}));
2087 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, grad_flat(5)});
2088 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
2089 }
2090
TEST_F(QuantOpsTest,WithVarsPerChannelDim2GradientNudgedDown_4Bits_NarrowRange)2091 TEST_F(QuantOpsTest,
2092 WithVarsPerChannelDim2GradientNudgedDown_4Bits_NarrowRange) {
2093 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 14 / 2].
2094 // Scale: 1/2, original zero point: 1.2, nudged to 1.
2095 // Nudged range: [0.0; 7.0].
2096 // Expected quantized values: 0.0, 0.5, ..., 7.0.
2097 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
2098 .Attr("num_bits", 4)
2099 .Attr("narrow_range", true)
2100 .Input(FakeInput(DT_FLOAT)) // gradients
2101 .Input(FakeInput(DT_FLOAT)) // inputs
2102 .Input(FakeInput(DT_FLOAT)) // min
2103 .Input(FakeInput(DT_FLOAT)) // max
2104 .Finalize(node_def()));
2105 TF_EXPECT_OK(InitOp());
2106 // Upstream gradients.
2107 AddRandomInput(TensorShape({2, 3}));
2108 // Downstream inputs.
2109 AddInputFromArray<float>(TensorShape({2, 3}),
2110 {-0.1f, 0.0f, 0.1f, 0.5f, 7.0f, 7.1f});
2111 // Min.
2112 AddInputFromArray<float>(TensorShape({3}), {-0.1f, -0.1f, -0.1f});
2113 // Max.
2114 AddInputFromArray<float>(TensorShape({3}), {6.9f, 6.9f, 6.9f});
2115
2116 // Tested code.
2117 TF_ASSERT_OK(RunOpKernel());
2118
2119 Tensor* output_bprop_wrt_input = GetOutput(0);
2120 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({2, 3}));
2121 auto grad_flat = GetInput(0).flat<float>();
2122 FillValues<float>(
2123 &expected_bprop_wrt_input,
2124 {0.0f, grad_flat(1), grad_flat(2), grad_flat(3), grad_flat(4), 0.0f});
2125 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
2126
2127 Tensor* output_bprop_wrt_min = GetOutput(1);
2128 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({3}));
2129 FillValues<float>(&expected_bprop_wrt_min, {grad_flat(0), 0.0f, 0.0f});
2130 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
2131
2132 Tensor* output_bprop_wrt_max = GetOutput(2);
2133 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({3}));
2134 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, grad_flat(5)});
2135 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
2136 }
2137
TEST_F(QuantOpsTest,WithVarsPerChannelDim2GradientNudgedUp_4Bits_RegularRange)2138 TEST_F(QuantOpsTest,
2139 WithVarsPerChannelDim2GradientNudgedUp_4Bits_RegularRange) {
2140 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 15 / 2].
2141 // Scale: 1/2, original zero point: 0.8, nudged to 1.
2142 // Nudged range: [-0.5; 7.0].
2143 // Expected quantized values: -0.5, 0.0, 0.5, ..., 7.0.
2144 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
2145 .Attr("num_bits", 4)
2146 .Attr("narrow_range", false)
2147 .Input(FakeInput(DT_FLOAT)) // gradients
2148 .Input(FakeInput(DT_FLOAT)) // inputs
2149 .Input(FakeInput(DT_FLOAT)) // min
2150 .Input(FakeInput(DT_FLOAT)) // max
2151 .Finalize(node_def()));
2152 TF_EXPECT_OK(InitOp());
2153 // Upstream gradients.
2154 AddRandomInput(TensorShape({2, 3}));
2155 // Downstream inputs.
2156 AddInputFromArray<float>(TensorShape({2, 3}),
2157 {-0.6f, -0.5f, -0.4f, 0.0f, 7.0f, 7.1f});
2158 // Min.
2159 AddInputFromArray<float>(TensorShape({3}), {-0.4f, -0.4f, -0.4f});
2160 // Max.
2161 AddInputFromArray<float>(TensorShape({3}), {7.1f, 7.1f, 7.1f});
2162
2163 // Tested code.
2164 TF_ASSERT_OK(RunOpKernel());
2165
2166 Tensor* output_bprop_wrt_input = GetOutput(0);
2167 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({2, 3}));
2168 auto grad_flat = GetInput(0).flat<float>();
2169 FillValues<float>(
2170 &expected_bprop_wrt_input,
2171 {0.0f, grad_flat(1), grad_flat(2), grad_flat(3), grad_flat(4), 0.0f});
2172 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
2173
2174 Tensor* output_bprop_wrt_min = GetOutput(1);
2175 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({3}));
2176 FillValues<float>(&expected_bprop_wrt_min, {grad_flat(0), 0.0f, 0.0f});
2177 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
2178
2179 Tensor* output_bprop_wrt_max = GetOutput(2);
2180 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({3}));
2181 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, grad_flat(5)});
2182 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
2183 }
2184
TEST_F(QuantOpsTest,WithVarsPerChannelDim2GradientNudgedUp_4Bits_NarrowRange)2185 TEST_F(QuantOpsTest, WithVarsPerChannelDim2GradientNudgedUp_4Bits_NarrowRange) {
2186 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 14 / 2].
2187 // Scale: 1/2, original zero point: 1.8, nudged to 2.
2188 // Nudged range: [-0.5; 6.5].
2189 // Expected quantized values: -0.5, 0.0, 0.5, ..., 6.5.
2190 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
2191 .Attr("num_bits", 4)
2192 .Attr("narrow_range", true)
2193 .Input(FakeInput(DT_FLOAT)) // gradients
2194 .Input(FakeInput(DT_FLOAT)) // inputs
2195 .Input(FakeInput(DT_FLOAT)) // min
2196 .Input(FakeInput(DT_FLOAT)) // max
2197 .Finalize(node_def()));
2198 TF_EXPECT_OK(InitOp());
2199 // Upstream gradients.
2200 AddRandomInput(TensorShape({2, 3}));
2201 // Downstream inputs.
2202 AddInputFromArray<float>(TensorShape({2, 3}),
2203 {-0.6f, -0.5f, -0.4f, 0.0f, 6.5f, 6.6f});
2204 // Min.
2205 AddInputFromArray<float>(TensorShape({3}), {-0.4f, -0.4f, -0.4f});
2206 // Max.
2207 AddInputFromArray<float>(TensorShape({3}), {6.6f, 6.6f, 6.6f});
2208
2209 // Tested code.
2210 TF_ASSERT_OK(RunOpKernel());
2211
2212 Tensor* output_bprop_wrt_input = GetOutput(0);
2213 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT, TensorShape({2, 3}));
2214 auto grad_flat = GetInput(0).flat<float>();
2215 FillValues<float>(
2216 &expected_bprop_wrt_input,
2217 {0.0f, grad_flat(1), grad_flat(2), grad_flat(3), grad_flat(4), 0.0f});
2218 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
2219
2220 Tensor* output_bprop_wrt_min = GetOutput(1);
2221 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({3}));
2222 FillValues<float>(&expected_bprop_wrt_min, {grad_flat(0), 0.0f, 0.0f});
2223 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
2224
2225 Tensor* output_bprop_wrt_max = GetOutput(2);
2226 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({3}));
2227 FillValues<float>(&expected_bprop_wrt_max, {0.0f, 0.0f, grad_flat(5)});
2228 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
2229 }
2230
TEST_F(QuantOpsTest,WithVarsPerChannelDim4GradientNudgedDown_4Bits_RegularRange)2231 TEST_F(QuantOpsTest,
2232 WithVarsPerChannelDim4GradientNudgedDown_4Bits_RegularRange) {
2233 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 15 / 2].
2234 // Scale: 1/2, original zero point: 0.2, nudged to 0.
2235 // Nudged range: [0.0; 7.5].
2236 // Expected quantized values: 0.0, 0.5, ..., 7.5.
2237 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
2238 .Attr("num_bits", 4)
2239 .Attr("narrow_range", false)
2240 .Input(FakeInput(DT_FLOAT)) // gradients
2241 .Input(FakeInput(DT_FLOAT)) // inputs
2242 .Input(FakeInput(DT_FLOAT)) // min
2243 .Input(FakeInput(DT_FLOAT)) // max
2244 .Finalize(node_def()));
2245 TF_EXPECT_OK(InitOp());
2246 // Upstream gradients.
2247 AddRandomInput(TensorShape({1, 2, 3, 4}));
2248 // Downstream inputs.
2249 // clang-format off
2250 AddInputFromArray<float>(TensorShape({1, 2, 3, 4}),
2251 {-0.1f, 0.0f, 7.5f, 7.6f, -0.1f, 0.0f,
2252 7.5f, 7.6f, -0.1f, 0.0f, 7.5f, 7.6f,
2253 -0.1f, 0.0f, 7.5f, 7.6f, -0.1f, 0.0f,
2254 7.5f, 7.6f, -0.1f, 0.0f, 7.5f, 7.6f});
2255 // clang-format on
2256 // Min.
2257 AddInputFromArray<float>(TensorShape({4}), {-0.1f, -0.1f, -0.1f, -0.1f});
2258 // Max.
2259 AddInputFromArray<float>(TensorShape({4}), {7.4f, 7.4f, 7.4f, 7.4f});
2260
2261 // Tested code.
2262 TF_ASSERT_OK(RunOpKernel());
2263
2264 Tensor* output_bprop_wrt_input = GetOutput(0);
2265 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT,
2266 TensorShape({1, 2, 3, 4}));
2267 auto grad_flat = GetInput(0).flat<float>();
2268 FillValues<float>(&expected_bprop_wrt_input,
2269 {0.0f, grad_flat(1), grad_flat(2), 0.0f,
2270 0.0f, grad_flat(5), grad_flat(6), 0.0f,
2271 0.0f, grad_flat(9), grad_flat(10), 0.0f,
2272 0.0f, grad_flat(13), grad_flat(14), 0.0f,
2273 0.0f, grad_flat(17), grad_flat(18), 0.0f,
2274 0.0f, grad_flat(21), grad_flat(22), 0.0f});
2275 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
2276
2277 Tensor* output_bprop_wrt_min = GetOutput(1);
2278 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
2279 FillValues<float>(&expected_bprop_wrt_min,
2280 {grad_flat(0) + grad_flat(4) + grad_flat(8) +
2281 grad_flat(12) + grad_flat(16) + grad_flat(20),
2282 0.0f, 0.0f, 0.0f});
2283 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
2284
2285 Tensor* output_bprop_wrt_max = GetOutput(2);
2286 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
2287 FillValues<float>(&expected_bprop_wrt_max,
2288 {0.0f, 0.0f, 0.0f,
2289 grad_flat(3) + grad_flat(7) + grad_flat(11) +
2290 grad_flat(15) + grad_flat(19) + grad_flat(23)});
2291 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
2292 }
2293
TEST_F(QuantOpsTest,WithVarsPerChannelDim4GradientNudgedDown_4Bits_NarrowRange)2294 TEST_F(QuantOpsTest,
2295 WithVarsPerChannelDim4GradientNudgedDown_4Bits_NarrowRange) {
2296 // Original quantization range: [-0.2 / 2 + 0 / 2, -0.2 / 2 + 14 / 2].
2297 // Scale: 1/2, original zero point: 1.2, nudged to 1.
2298 // Nudged range: [0.0; 7.0].
2299 // Expected quantized values: 0.0, 0.5, ..., 7.0.
2300 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
2301 .Attr("num_bits", 4)
2302 .Attr("narrow_range", true)
2303 .Input(FakeInput(DT_FLOAT)) // gradients
2304 .Input(FakeInput(DT_FLOAT)) // inputs
2305 .Input(FakeInput(DT_FLOAT)) // min
2306 .Input(FakeInput(DT_FLOAT)) // max
2307 .Finalize(node_def()));
2308 TF_EXPECT_OK(InitOp());
2309 // Upstream gradients.
2310 AddRandomInput(TensorShape({1, 2, 3, 4}));
2311 // Downstream inputs.
2312 // clang-format off
2313 AddInputFromArray<float>(TensorShape({1, 2, 3, 4}),
2314 {-0.1f, 0.0f, 7.0f, 7.1f, -0.1f, 0.0f,
2315 7.0f, 7.1f, -0.1f, 0.0f, 7.0f, 7.1f,
2316 -0.1f, 0.0f, 7.0f, 7.1f, -0.1f, 0.0f,
2317 7.0f, 7.1f, -0.1f, 0.0f, 7.0f, 7.1f});
2318 // clang-format on
2319 // Min.
2320 AddInputFromArray<float>(TensorShape({4}), {-0.1f, -0.1f, -0.1f, -0.1f});
2321 // Max.
2322 AddInputFromArray<float>(TensorShape({4}), {6.9f, 6.9f, 6.9f, 6.9f});
2323
2324 // Tested code.
2325 TF_ASSERT_OK(RunOpKernel());
2326
2327 Tensor* output_bprop_wrt_input = GetOutput(0);
2328 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT,
2329 TensorShape({1, 2, 3, 4}));
2330 auto grad_flat = GetInput(0).flat<float>();
2331 FillValues<float>(&expected_bprop_wrt_input,
2332 {0.0f, grad_flat(1), grad_flat(2), 0.0f,
2333 0.0f, grad_flat(5), grad_flat(6), 0.0f,
2334 0.0f, grad_flat(9), grad_flat(10), 0.0f,
2335 0.0f, grad_flat(13), grad_flat(14), 0.0f,
2336 0.0f, grad_flat(17), grad_flat(18), 0.0f,
2337 0.0f, grad_flat(21), grad_flat(22), 0.0f});
2338 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
2339
2340 Tensor* output_bprop_wrt_min = GetOutput(1);
2341 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
2342 FillValues<float>(&expected_bprop_wrt_min,
2343 {grad_flat(0) + grad_flat(4) + grad_flat(8) +
2344 grad_flat(12) + grad_flat(16) + grad_flat(20),
2345 0.0f, 0.0f, 0.0f});
2346 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
2347
2348 Tensor* output_bprop_wrt_max = GetOutput(2);
2349 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
2350 FillValues<float>(&expected_bprop_wrt_max,
2351 {0.0f, 0.0f, 0.0f,
2352 grad_flat(3) + grad_flat(7) + grad_flat(11) +
2353 grad_flat(15) + grad_flat(19) + grad_flat(23)});
2354 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
2355 }
2356
TEST_F(QuantOpsTest,WithVarsPerChannelDim4GradientNudgedUp_4Bits_RegularRange)2357 TEST_F(QuantOpsTest,
2358 WithVarsPerChannelDim4GradientNudgedUp_4Bits_RegularRange) {
2359 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 15 / 2].
2360 // Scale: 1/2, original zero point: 0.8, nudged to 1.
2361 // Nudged range: [-0.5; 7.0].
2362 // Expected quantized values: -0.5, 0.0, 0.5, ..., 7.0.
2363 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
2364 .Attr("num_bits", 4)
2365 .Attr("narrow_range", false)
2366 .Input(FakeInput(DT_FLOAT)) // gradients
2367 .Input(FakeInput(DT_FLOAT)) // inputs
2368 .Input(FakeInput(DT_FLOAT)) // min
2369 .Input(FakeInput(DT_FLOAT)) // max
2370 .Finalize(node_def()));
2371 TF_EXPECT_OK(InitOp());
2372 // Upstream gradients.
2373 AddRandomInput(TensorShape({1, 2, 3, 4}));
2374 // Downstream inputs.
2375 // clang-format off
2376 AddInputFromArray<float>(TensorShape({1, 2, 3, 4}),
2377 {-0.6f, -0.5f, 7.0f, 7.1f, -0.6f, -0.5f,
2378 7.0f, 7.1f, -0.6f, -0.5f, 7.0f, 7.1f,
2379 -0.6f, -0.5f, 7.0f, 7.1f, -0.6f, -0.5f,
2380 7.0f, 7.1f, -0.6f, -0.5f, 7.0f, 7.1f});
2381 // clang-format on
2382 // Min.
2383 AddInputFromArray<float>(TensorShape({4}), {-0.4f, -0.4f, -0.4f, -0.4f});
2384 // Max.
2385 AddInputFromArray<float>(TensorShape({4}), {7.1f, 7.1f, 7.1f, 7.1f});
2386
2387 // Tested code.
2388 TF_ASSERT_OK(RunOpKernel());
2389
2390 Tensor* output_bprop_wrt_input = GetOutput(0);
2391 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT,
2392 TensorShape({1, 2, 3, 4}));
2393 auto grad_flat = GetInput(0).flat<float>();
2394 FillValues<float>(&expected_bprop_wrt_input,
2395 {0.0f, grad_flat(1), grad_flat(2), 0.0f,
2396 0.0f, grad_flat(5), grad_flat(6), 0.0f,
2397 0.0f, grad_flat(9), grad_flat(10), 0.0f,
2398 0.0f, grad_flat(13), grad_flat(14), 0.0f,
2399 0.0f, grad_flat(17), grad_flat(18), 0.0f,
2400 0.0f, grad_flat(21), grad_flat(22), 0.0f});
2401 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
2402
2403 Tensor* output_bprop_wrt_min = GetOutput(1);
2404 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
2405 FillValues<float>(&expected_bprop_wrt_min,
2406 {grad_flat(0) + grad_flat(4) + grad_flat(8) +
2407 grad_flat(12) + grad_flat(16) + grad_flat(20),
2408 0.0f, 0.0f, 0.0f});
2409 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
2410
2411 Tensor* output_bprop_wrt_max = GetOutput(2);
2412 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
2413 FillValues<float>(&expected_bprop_wrt_max,
2414 {0.0f, 0.0f, 0.0f,
2415 grad_flat(3) + grad_flat(7) + grad_flat(11) +
2416 grad_flat(15) + grad_flat(19) + grad_flat(23)});
2417 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
2418 }
2419
TEST_F(QuantOpsTest,WithVarsPerChannelDim4GradientNudgedUp_4Bits_NarrowRange)2420 TEST_F(QuantOpsTest, WithVarsPerChannelDim4GradientNudgedUp_4Bits_NarrowRange) {
2421 // Original quantization range: [-0.8 / 2 + 0 / 2, -0.8 / 2 + 14 / 2].
2422 // Scale: 1/2, original zero point: 1.8, nudged to 2.
2423 // Nudged range: [-0.5; 6.5].
2424 // Expected quantized values: -0.5, 0.0, 0.5, ..., 7.0.
2425 TF_EXPECT_OK(NodeDefBuilder("op", "FakeQuantWithMinMaxVarsPerChannelGradient")
2426 .Attr("num_bits", 4)
2427 .Attr("narrow_range", true)
2428 .Input(FakeInput(DT_FLOAT)) // gradients
2429 .Input(FakeInput(DT_FLOAT)) // inputs
2430 .Input(FakeInput(DT_FLOAT)) // min
2431 .Input(FakeInput(DT_FLOAT)) // max
2432 .Finalize(node_def()));
2433 TF_EXPECT_OK(InitOp());
2434 // Upstream gradients.
2435 AddRandomInput(TensorShape({1, 2, 3, 4}));
2436 // Downstream inputs.
2437 // clang-format off
2438 AddInputFromArray<float>(TensorShape({1, 2, 3, 4}),
2439 {-0.6f, -0.5f, 6.5f, 6.6f, -0.6f, -0.5f,
2440 6.5f, 6.6f, -0.6f, -0.5f, 6.5f, 6.6f,
2441 -0.6f, -0.5f, 6.5f, 6.6f, -0.6f, -0.5f,
2442 6.5f, 6.6f, -0.6f, -0.5f, 6.5f, 6.6f});
2443 // clang-format on
2444 // Min.
2445 AddInputFromArray<float>(TensorShape({4}), {-0.4f, -0.4f, -0.4f, -0.4f});
2446 // Max.
2447 AddInputFromArray<float>(TensorShape({4}), {6.6f, 6.6f, 6.6f, 6.6f});
2448
2449 // Tested code.
2450 TF_ASSERT_OK(RunOpKernel());
2451
2452 Tensor* output_bprop_wrt_input = GetOutput(0);
2453 Tensor expected_bprop_wrt_input(allocator(), DT_FLOAT,
2454 TensorShape({1, 2, 3, 4}));
2455 auto grad_flat = GetInput(0).flat<float>();
2456 FillValues<float>(&expected_bprop_wrt_input,
2457 {0.0f, grad_flat(1), grad_flat(2), 0.0f,
2458 0.0f, grad_flat(5), grad_flat(6), 0.0f,
2459 0.0f, grad_flat(9), grad_flat(10), 0.0f,
2460 0.0f, grad_flat(13), grad_flat(14), 0.0f,
2461 0.0f, grad_flat(17), grad_flat(18), 0.0f,
2462 0.0f, grad_flat(21), grad_flat(22), 0.0f});
2463 ExpectClose(expected_bprop_wrt_input, *output_bprop_wrt_input);
2464
2465 Tensor* output_bprop_wrt_min = GetOutput(1);
2466 Tensor expected_bprop_wrt_min(allocator(), DT_FLOAT, TensorShape({4}));
2467 FillValues<float>(&expected_bprop_wrt_min,
2468 {grad_flat(0) + grad_flat(4) + grad_flat(8) +
2469 grad_flat(12) + grad_flat(16) + grad_flat(20),
2470 0.0f, 0.0f, 0.0f});
2471 ExpectClose(expected_bprop_wrt_min, *output_bprop_wrt_min);
2472
2473 Tensor* output_bprop_wrt_max = GetOutput(2);
2474 Tensor expected_bprop_wrt_max(allocator(), DT_FLOAT, TensorShape({4}));
2475 FillValues<float>(&expected_bprop_wrt_max,
2476 {0.0f, 0.0f, 0.0f,
2477 grad_flat(3) + grad_flat(7) + grad_flat(11) +
2478 grad_flat(15) + grad_flat(19) + grad_flat(23)});
2479 ExpectClose(expected_bprop_wrt_max, *output_bprop_wrt_max);
2480 }
2481
2482 } // namespace tensorflow
2483