xref: /aosp_15_r20/external/XNNPACK/test/argmax-pooling-operator-tester.h (revision 4bdc94577ba0e567308109d787f7fec7b531ce36)
1 // Copyright 2019 Google LLC
2 //
3 // This source code is licensed under the BSD-style license found in the
4 // LICENSE file in the root directory of this source tree.
5 
6 #pragma once
7 
8 #include <gtest/gtest.h>
9 
10 #include <algorithm>
11 #include <cassert>
12 #include <cstddef>
13 #include <cstdlib>
14 #include <limits>
15 #include <random>
16 #include <vector>
17 
18 #include <xnnpack.h>
19 
20 
21 class ArgmaxPoolingOperatorTester {
22  public:
padding_tf_same(bool padding_same)23   inline ArgmaxPoolingOperatorTester& padding_tf_same(bool padding_same) {
24     if (padding_same) {
25       assert(padding_top() == 0);
26       assert(padding_left() == 0);
27       assert(padding_bottom() == 0);
28       assert(padding_right() == 0);
29     }
30     this->padding_tf_same_ = padding_same;
31     return *this;
32   }
33 
padding_tf_same()34   inline bool padding_tf_same() const {
35     return this->padding_tf_same_;
36   }
37 
padding(uint32_t padding)38   inline ArgmaxPoolingOperatorTester& padding(uint32_t padding) {
39     assert(!padding_tf_same());
40     this->padding_top_ = padding;
41     this->padding_right_ = padding;
42     this->padding_bottom_ = padding;
43     this->padding_left_ = padding;
44     return *this;
45   }
46 
padding(uint32_t padding_height,uint32_t padding_width)47   inline ArgmaxPoolingOperatorTester& padding(uint32_t padding_height, uint32_t padding_width) {
48     assert(!padding_tf_same());
49     this->padding_top_ = padding_height;
50     this->padding_right_ = padding_width;
51     this->padding_bottom_ = padding_height;
52     this->padding_left_ = padding_width;
53     return *this;
54   }
55 
padding_height(uint32_t padding_height)56   inline ArgmaxPoolingOperatorTester& padding_height(uint32_t padding_height) {
57     assert(!padding_tf_same());
58     this->padding_top_ = padding_height;
59     this->padding_bottom_ = padding_height;
60     return *this;
61   }
62 
padding_width(uint32_t padding_width)63   inline ArgmaxPoolingOperatorTester& padding_width(uint32_t padding_width) {
64     assert(!padding_tf_same());
65     this->padding_right_ = padding_width;
66     this->padding_left_ = padding_width;
67     return *this;
68   }
69 
padding_top(uint32_t padding_top)70   inline ArgmaxPoolingOperatorTester& padding_top(uint32_t padding_top) {
71     assert(!padding_tf_same());
72     this->padding_top_ = padding_top;
73     return *this;
74   }
75 
padding_top()76   inline uint32_t padding_top() const {
77     if (padding_tf_same()) {
78       const uint32_t total_padding_height = output_height() * pooling_height() - input_height();
79       return total_padding_height / 2;
80     } else {
81       return this->padding_top_;
82     }
83   }
84 
padding_left(uint32_t padding_left)85   inline ArgmaxPoolingOperatorTester& padding_left(uint32_t padding_left) {
86     assert(!padding_tf_same());
87     this->padding_left_ = padding_left;
88     return *this;
89   }
90 
padding_left()91   inline uint32_t padding_left() const {
92     if (padding_tf_same()) {
93       const uint32_t total_padding_width = output_width() * pooling_width() - input_width();
94       return total_padding_width / 2;
95     } else {
96       return this->padding_left_;
97     }
98   }
99 
padding_bottom(uint32_t padding_bottom)100   inline ArgmaxPoolingOperatorTester& padding_bottom(uint32_t padding_bottom) {
101     assert(!padding_tf_same());
102     this->padding_bottom_ = padding_bottom;
103     return *this;
104   }
105 
padding_bottom()106   inline uint32_t padding_bottom() const {
107     if (padding_tf_same()) {
108       const uint32_t total_padding_height = output_height() * pooling_height() - input_height();
109       return total_padding_height - total_padding_height / 2;
110     } else {
111       return this->padding_bottom_;
112     }
113   }
114 
padding_right(uint32_t padding_right)115   inline ArgmaxPoolingOperatorTester& padding_right(uint32_t padding_right) {
116     assert(!padding_tf_same());
117     this->padding_right_ = padding_right;
118     return *this;
119   }
120 
padding_right()121   inline uint32_t padding_right() const {
122     if (padding_tf_same()) {
123       const uint32_t total_padding_width = output_width() * pooling_width() - input_width();
124       return total_padding_width - total_padding_width / 2;
125     } else {
126       return this->padding_right_;
127     }
128   }
129 
input_size(size_t input_height,size_t input_width)130   inline ArgmaxPoolingOperatorTester& input_size(size_t input_height, size_t input_width) {
131     assert(input_height >= 1);
132     assert(input_width >= 1);
133     this->input_height_ = input_height;
134     this->input_width_ = input_width;
135     return *this;
136   }
137 
input_height(size_t input_height)138   inline ArgmaxPoolingOperatorTester& input_height(size_t input_height) {
139     assert(input_height >= 1);
140     this->input_height_ = input_height;
141     return *this;
142   }
143 
input_height()144   inline size_t input_height() const {
145     return this->input_height_;
146   }
147 
input_width(size_t input_width)148   inline ArgmaxPoolingOperatorTester& input_width(size_t input_width) {
149     assert(input_width >= 1);
150     this->input_width_ = input_width;
151     return *this;
152   }
153 
input_width()154   inline size_t input_width() const {
155     return this->input_width_;
156   }
157 
channels(size_t channels)158   inline ArgmaxPoolingOperatorTester& channels(size_t channels) {
159     assert(channels != 0);
160     this->channels_ = channels;
161     return *this;
162   }
163 
channels()164   inline size_t channels() const {
165     return this->channels_;
166   }
167 
batch_size(size_t batch_size)168   inline ArgmaxPoolingOperatorTester& batch_size(size_t batch_size) {
169     assert(batch_size != 0);
170     this->batch_size_ = batch_size;
171     return *this;
172   }
173 
batch_size()174   inline size_t batch_size() const {
175     return this->batch_size_;
176   }
177 
pooling_size(uint32_t pooling_size)178   inline ArgmaxPoolingOperatorTester& pooling_size(uint32_t pooling_size) {
179     assert(pooling_size >= 1);
180     this->pooling_height_ = pooling_size;
181     this->pooling_width_ = pooling_size;
182     return *this;
183   }
184 
pooling_size(uint32_t pooling_height,uint32_t pooling_width)185   inline ArgmaxPoolingOperatorTester& pooling_size(uint32_t pooling_height, uint32_t pooling_width) {
186     assert(pooling_height >= 1);
187     assert(pooling_width >= 1);
188     this->pooling_height_ = pooling_height;
189     this->pooling_width_ = pooling_width;
190     return *this;
191   }
192 
pooling_height(uint32_t pooling_height)193   inline ArgmaxPoolingOperatorTester& pooling_height(uint32_t pooling_height) {
194     assert(pooling_height >= 1);
195     this->pooling_height_ = pooling_height;
196     return *this;
197   }
198 
pooling_height()199   inline uint32_t pooling_height() const {
200     return this->pooling_height_;
201   }
202 
pooling_width(uint32_t pooling_width)203   inline ArgmaxPoolingOperatorTester& pooling_width(uint32_t pooling_width) {
204     assert(pooling_width >= 1);
205     this->pooling_width_ = pooling_width;
206     return *this;
207   }
208 
pooling_width()209   inline uint32_t pooling_width() const {
210     return this->pooling_width_;
211   }
212 
output_height()213   inline size_t output_height() const {
214     if (padding_tf_same()) {
215       return (input_height() + pooling_height() - 1) / pooling_height();
216     } else {
217       const size_t padded_input_height = padding_top() + input_height() + padding_bottom();
218       return padded_input_height / pooling_height();
219     }
220   }
221 
output_width()222   inline size_t output_width() const {
223     if (padding_tf_same()) {
224       return (input_width() + pooling_width() - 1) / pooling_width();
225     } else {
226       const size_t padded_input_width = padding_left() + input_width() + padding_right();
227       return padded_input_width / pooling_width();
228     }
229   }
230 
input_pixel_stride(size_t input_pixel_stride)231   inline ArgmaxPoolingOperatorTester& input_pixel_stride(size_t input_pixel_stride) {
232     assert(input_pixel_stride != 0);
233     this->input_pixel_stride_ = input_pixel_stride;
234     return *this;
235   }
236 
input_pixel_stride()237   inline size_t input_pixel_stride() const {
238     if (this->input_pixel_stride_ == 0) {
239       return channels();
240     } else {
241       assert(this->input_pixel_stride_ >= channels());
242       return this->input_pixel_stride_;
243     }
244   }
245 
output_pixel_stride(size_t output_pixel_stride)246   inline ArgmaxPoolingOperatorTester& output_pixel_stride(size_t output_pixel_stride) {
247     assert(output_pixel_stride != 0);
248     this->output_pixel_stride_ = output_pixel_stride;
249     return *this;
250   }
251 
output_pixel_stride()252   inline size_t output_pixel_stride() const {
253     if (this->output_pixel_stride_ == 0) {
254       return channels();
255     } else {
256       assert(this->output_pixel_stride_ >= channels());
257       return this->output_pixel_stride_;
258     }
259   }
260 
next_input_size(uint32_t next_input_height,uint32_t next_input_width)261   inline ArgmaxPoolingOperatorTester& next_input_size(uint32_t next_input_height, uint32_t next_input_width) {
262     assert(next_input_height >= 1);
263     assert(next_input_width >= 1);
264     this->next_input_height_ = next_input_height;
265     this->next_input_width_ = next_input_width;
266     return *this;
267   }
268 
next_input_height(uint32_t next_input_height)269   inline ArgmaxPoolingOperatorTester& next_input_height(uint32_t next_input_height) {
270     assert(next_input_height >= 1);
271     this->next_input_height_ = next_input_height;
272     return *this;
273   }
274 
next_input_height()275   inline uint32_t next_input_height() const {
276     if (this->next_input_height_ == 0) {
277       return input_height();
278     } else {
279       return this->next_input_height_;
280     }
281   }
282 
next_input_width(uint32_t next_input_width)283   inline ArgmaxPoolingOperatorTester& next_input_width(uint32_t next_input_width) {
284     assert(next_input_width >= 1);
285     this->next_input_width_ = next_input_width;
286     return *this;
287   }
288 
next_input_width()289   inline uint32_t next_input_width() const {
290     if (this->next_input_width_ == 0) {
291       return input_width();
292     } else {
293       return this->next_input_width_;
294     }
295   }
296 
next_output_height()297   inline size_t next_output_height() const {
298     const size_t padded_next_input_height = padding_top() + next_input_height() + padding_bottom();
299     return padded_next_input_height / pooling_height();
300   }
301 
next_output_width()302   inline size_t next_output_width() const {
303     const size_t padded_next_input_width = padding_left() + next_input_width() + padding_right();
304     return padded_next_input_width / pooling_width();
305   }
306 
next_batch_size(size_t next_batch_size)307   inline ArgmaxPoolingOperatorTester& next_batch_size(size_t next_batch_size) {
308     assert(next_batch_size >= 1);
309     this->next_batch_size_ = next_batch_size;
310     return *this;
311   }
312 
next_batch_size()313   inline size_t next_batch_size() const {
314     if (this->next_batch_size_ == 0) {
315       return batch_size();
316     } else {
317       return this->next_batch_size_;
318     }
319   }
320 
iterations(size_t iterations)321   inline ArgmaxPoolingOperatorTester& iterations(size_t iterations) {
322     this->iterations_ = iterations;
323     return *this;
324   }
325 
iterations()326   inline size_t iterations() const {
327     return this->iterations_;
328   }
329 
TestF32()330   void TestF32() const {
331     std::random_device random_device;
332     auto rng = std::mt19937(random_device());
333     std::uniform_real_distribution<float> f32dist;
334 
335     std::vector<float> input((batch_size() * input_height() * input_width() - 1) * input_pixel_stride() + channels() + XNN_EXTRA_BYTES / sizeof(float));
336     std::vector<float> output((batch_size() * output_height() * output_width() - 1) * output_pixel_stride() + channels());
337     std::vector<float> output_ref(batch_size() * output_height() * output_width() * channels());
338     std::vector<uint32_t> index(batch_size() * output_height() * output_width() * channels());
339     std::vector<uint32_t> index_ref(batch_size() * output_height() * output_width() * channels());
340     for (size_t iteration = 0; iteration < iterations(); iteration++) {
341       std::generate(input.begin(), input.end(), [&]() { return f32dist(rng); });
342       std::fill(output.begin(), output.end(), nanf(""));
343 
344       // Compute reference results, without clamping.
345       for (size_t i = 0; i < batch_size(); i++) {
346         for (size_t oy = 0; oy < output_height(); oy++) {
347           for (size_t ox = 0; ox < output_width(); ox++) {
348             for (size_t c = 0; c < channels(); c++) {
349               const size_t iy_top_left = std::max<size_t>(oy * pooling_height(), padding_top()) - padding_top();
350               const size_t ix_top_left = std::max<size_t>(ox * pooling_width(), padding_left()) - padding_left();
351               float max_value =
352                 input[((i * input_height() + iy_top_left) * input_width() + ix_top_left) * input_pixel_stride() + c];
353               uint32_t max_index = 0;
354               for (size_t py = 0; py < pooling_height(); py++) {
355                 const size_t iy = oy * pooling_height() + py - padding_top();
356                 for (size_t px = 0; px < pooling_width(); px++) {
357                   const size_t ix = ox * pooling_width() + px - padding_left();
358                   if (ix < input_width() && iy < input_height()) {
359                     const float value = input[((i * input_height() + iy) * input_width() + ix) * input_pixel_stride() + c];
360                     if (value > max_value) {
361                       max_value = value;
362                       max_index = uint32_t(px * pooling_height() + py);
363                     }
364                   }
365                 }
366               }
367               output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] = max_value;
368               index_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] = max_index;
369             }
370           }
371         }
372       }
373 
374       // Create, setup, run, and destroy Argmax Pooling operator.
375       ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
376       xnn_operator_t argmax_pooling_op = nullptr;
377 
378       ASSERT_EQ(xnn_status_success,
379         xnn_create_argmax_pooling2d_nhwc_f32(
380           padding_tf_same() ? 0 : padding_top(), padding_tf_same() ? 0 : padding_right(),
381           padding_tf_same() ? 0 : padding_bottom(), padding_tf_same() ? 0 : padding_left(),
382           pooling_height(), pooling_width(),
383           channels(), input_pixel_stride(), output_pixel_stride(),
384           padding_tf_same() ? XNN_FLAG_TENSORFLOW_SAME_PADDING : 0,
385           &argmax_pooling_op));
386       ASSERT_NE(nullptr, argmax_pooling_op);
387 
388       // Smart pointer to automatically delete argmax_pooling_op.
389       std::unique_ptr<xnn_operator, decltype(&xnn_delete_operator)> auto_argmax_pooling_op(argmax_pooling_op, xnn_delete_operator);
390 
391       ASSERT_EQ(xnn_status_success,
392         xnn_setup_argmax_pooling2d_nhwc_f32(
393           argmax_pooling_op,
394           batch_size(), input_height(), input_width(),
395           input.data(), output.data(), index.data(),
396           nullptr /* thread pool */));
397 
398       ASSERT_EQ(xnn_status_success,
399         xnn_run_operator(argmax_pooling_op, nullptr /* thread pool */));
400 
401       // Verify results.
402       for (size_t i = 0; i < batch_size(); i++) {
403         for (size_t y = 0; y < output_height(); y++) {
404           for (size_t x = 0; x < output_width(); x++) {
405             for (size_t c = 0; c < channels(); c++) {
406               ASSERT_EQ(output_ref[((i * output_height() + y) * output_width() + x) * channels() + c],
407                 output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c]) <<
408                 "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
409               ASSERT_EQ(index_ref[((i * output_height() + y) * output_width() + x) * channels() + c],
410                 index[((i * output_height() + y) * output_width() + x) * channels() + c]) <<
411                 "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
412             }
413           }
414         }
415       }
416     }
417   }
418 
TestSetupF32()419   void TestSetupF32() const {
420     std::random_device random_device;
421     auto rng = std::mt19937(random_device());
422     std::uniform_real_distribution<float> f32dist;
423 
424     std::vector<float> input(XNN_EXTRA_BYTES / sizeof(float) + std::max<size_t>(
425       (batch_size() * input_height() * input_width() - 1) * input_pixel_stride() + channels(),
426       (next_batch_size() * next_input_height() * next_input_width() - 1) * input_pixel_stride() + channels()));
427     std::vector<float> output(std::max<size_t>(
428       (batch_size() * output_height() * output_width() - 1) * output_pixel_stride() + channels(),
429       (next_batch_size() * next_output_height() * next_output_width() - 1) * output_pixel_stride() + channels()));
430     std::vector<uint32_t> index(std::max<size_t>(
431       batch_size() * output_height() * output_width() * channels(),
432       next_batch_size() * next_output_height() * next_output_width() * channels()));
433     std::vector<float> output_ref(batch_size() * output_height() * output_width() * channels());
434     std::vector<float> next_output_ref(next_batch_size() * next_output_height() * next_output_width() * channels());
435     std::vector<uint32_t> index_ref(batch_size() * output_height() * output_width() * channels());
436     std::vector<uint32_t> next_index_ref(next_batch_size() * next_output_height() * next_output_width() * channels());
437     for (size_t iteration = 0; iteration < iterations(); iteration++) {
438       std::generate(input.begin(), input.end(), [&]() { return f32dist(rng); });
439       std::fill(output.begin(), output.end(), nanf(""));
440 
441       // Compute reference results, without clamping.
442       for (size_t i = 0; i < batch_size(); i++) {
443         for (size_t oy = 0; oy < output_height(); oy++) {
444           for (size_t ox = 0; ox < output_width(); ox++) {
445             for (size_t c = 0; c < channels(); c++) {
446               const size_t iy_top_left = std::max<size_t>(oy * pooling_height(), padding_top()) - padding_top();
447               const size_t ix_top_left = std::max<size_t>(ox * pooling_width(), padding_left()) - padding_left();
448               float max_value =
449                 input[((i * input_height() + iy_top_left) * input_width() + ix_top_left) * input_pixel_stride() + c];
450               uint32_t max_index = 0;
451               for (size_t py = 0; py < pooling_height(); py++) {
452                 const size_t iy = oy * pooling_height() + py - padding_top();
453                 for (size_t px = 0; px < pooling_width(); px++) {
454                   const size_t ix = ox * pooling_width() + px - padding_left();
455                   if (ix < input_width() && iy < input_height()) {
456                     const float value = input[((i * input_height() + iy) * input_width() + ix) * input_pixel_stride() + c];
457                     if (value > max_value) {
458                       max_value = value;
459                       max_index = uint32_t(px * pooling_height() + py);
460                     }
461                   }
462                 }
463               }
464               output_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] = max_value;
465               index_ref[((i * output_height() + oy) * output_width() + ox) * channels() + c] = max_index;
466             }
467           }
468         }
469       }
470 
471       // Create, setup, and run Argmax Pooling operator once.
472       ASSERT_EQ(xnn_status_success, xnn_initialize(nullptr /* allocator */));
473       xnn_operator_t argmax_pooling_op = nullptr;
474 
475       ASSERT_EQ(xnn_status_success,
476         xnn_create_argmax_pooling2d_nhwc_f32(
477           padding_top(), padding_right(), padding_bottom(), padding_left(),
478           pooling_height(), pooling_width(),
479           channels(), input_pixel_stride(), output_pixel_stride(),
480           0, &argmax_pooling_op));
481       ASSERT_NE(nullptr, argmax_pooling_op);
482 
483       ASSERT_EQ(xnn_status_success,
484         xnn_setup_argmax_pooling2d_nhwc_f32(
485           argmax_pooling_op,
486           batch_size(), input_height(), input_width(),
487           input.data(), output.data(), index.data(),
488           nullptr /* thread pool */));
489 
490       ASSERT_EQ(xnn_status_success,
491         xnn_run_operator(argmax_pooling_op, nullptr /* thread pool */));
492 
493       // Verify results of the first run.
494       for (size_t i = 0; i < batch_size(); i++) {
495         for (size_t y = 0; y < output_height(); y++) {
496           for (size_t x = 0; x < output_width(); x++) {
497             for (size_t c = 0; c < channels(); c++) {
498               ASSERT_EQ(
499                   output_ref[((i * output_height() + y) * output_width() + x) * channels() + c],
500                   output[((i * output_height() + y) * output_width() + x) * output_pixel_stride() + c])
501                 << "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
502               ASSERT_EQ(
503                   index_ref[((i * output_height() + y) * output_width() + x) * channels() + c],
504                   index[((i * output_height() + y) * output_width() + x) * channels() + c])
505                 << "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
506             }
507           }
508         }
509       }
510 
511       // Re-generate data for the second run.
512       std::generate(input.begin(), input.end(), [&]() { return f32dist(rng); });
513       std::fill(output.begin(), output.end(), std::nanf(""));
514 
515       // Compute reference results for the second run, including clamping.
516       for (size_t i = 0; i < next_batch_size(); i++) {
517         for (size_t oy = 0; oy < next_output_height(); oy++) {
518           for (size_t ox = 0; ox < next_output_width(); ox++) {
519             for (size_t c = 0; c < channels(); c++) {
520               const size_t iy_top_left = std::max<size_t>(oy * pooling_height(), padding_top()) - padding_top();
521               const size_t ix_top_left = std::max<size_t>(ox * pooling_width(), padding_left()) - padding_left();
522               float max_value =
523                 input[((i * next_input_height() + iy_top_left) * next_input_width() + ix_top_left) * input_pixel_stride() + c];
524               uint32_t max_index = 0;
525               for (size_t py = 0; py < pooling_height(); py++) {
526                 const size_t iy = oy * pooling_height() + py - padding_top();
527                 for (size_t px = 0; px < pooling_width(); px++) {
528                   const size_t ix = ox * pooling_width() + px - padding_left();
529                   if (ix < next_input_width() && iy < next_input_height()) {
530                     const float value = input[((i * next_input_height() + iy) * next_input_width() + ix) * input_pixel_stride() + c];
531                     if (value > max_value) {
532                       max_value = value;
533                       max_index = uint32_t(px * pooling_height() + py);
534                     }
535                   }
536                 }
537               }
538               next_output_ref[((i * next_output_height() + oy) * next_output_width() + ox) * channels() + c] = max_value;
539               next_index_ref[((i * next_output_height() + oy) * next_output_width() + ox) * channels() + c] = max_index;
540             }
541           }
542         }
543       }
544 
545       // Setup and run Argmax Pooling operator the second time, and destroy the operator.
546       ASSERT_EQ(xnn_status_success,
547         xnn_setup_argmax_pooling2d_nhwc_f32(
548           argmax_pooling_op,
549           next_batch_size(), next_input_height(), next_input_width(),
550           input.data(), output.data(), index.data(),
551           nullptr /* thread pool */));
552 
553       ASSERT_EQ(xnn_status_success,
554         xnn_run_operator(argmax_pooling_op, nullptr /* thread pool */));
555 
556       ASSERT_EQ(xnn_status_success,
557         xnn_delete_operator(argmax_pooling_op));
558       argmax_pooling_op = nullptr;
559 
560       // Verify results of the second run.
561       for (size_t i = 0; i < next_batch_size(); i++) {
562         for (size_t y = 0; y < next_output_height(); y++) {
563           for (size_t x = 0; x < next_output_width(); x++) {
564             for (size_t c = 0; c < channels(); c++) {
565               ASSERT_EQ(
566                   next_output_ref[((i * next_output_height() + y) * next_output_width() + x) * channels() + c],
567                   output[((i * next_output_height() + y) * next_output_width() + x) * output_pixel_stride() + c])
568                 << "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
569               ASSERT_EQ(
570                   next_index_ref[((i * next_output_height() + y) * next_output_width() + x) * channels() + c],
571                   index[((i * next_output_height() + y) * next_output_width() + x) * output_pixel_stride() + c])
572                 << "in batch index " << i << ", pixel (" << y << ", " << x << "), channel " << c;
573             }
574           }
575         }
576       }
577     }
578   }
579 
580  private:
581   uint32_t padding_top_{0};
582   uint32_t padding_right_{0};
583   uint32_t padding_bottom_{0};
584   uint32_t padding_left_{0};
585   bool padding_tf_same_{false};
586   size_t input_height_{1};
587   size_t input_width_{1};
588   size_t channels_{1};
589   size_t batch_size_{1};
590   size_t input_pixel_stride_{0};
591   size_t output_pixel_stride_{0};
592   uint32_t pooling_height_{1};
593   uint32_t pooling_width_{1};
594   size_t next_input_height_{0};
595   size_t next_input_width_{0};
596   size_t next_batch_size_{0};
597   uint8_t qmin_{0};
598   uint8_t qmax_{255};
599   size_t iterations_{1};
600 };
601