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