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