1 /* Copyright 2015 The TensorFlow Authors. All Rights Reserved. 2 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 ==============================================================================*/ 15 16 #include "tensorflow/core/kernels/eigen_backward_spatial_convolutions.h" 17 #include "tensorflow/core/framework/types.h" 18 #include "tensorflow/core/kernels/eigen_backward_cuboid_convolutions.h" 19 #include "tensorflow/core/platform/test.h" 20 21 namespace Eigen { 22 23 namespace { 24 void EigenApprox(float a, float b) { 25 ASSERT_TRUE(std::abs(a - b) <= std::min(std::abs(a), std::abs(b)) * 1e-3); 26 } 27 static int ceil_div(int a, int b) { return (a + b - 1) / b; } 28 } // namespace 29 30 TEST(EigenBackwardSpatialConvolutionsTest, 31 test_simple_spatial_convolution_backward_input_valid) { 32 const int input_depth = 2; 33 const int input_rows = 3; 34 const int input_cols = 4; 35 const int output_depth = 5; 36 const int patch_rows = 2; 37 const int patch_cols = 2; 38 const int output_rows = input_rows - patch_rows + 1; 39 const int output_cols = input_cols - patch_cols + 1; 40 41 Tensor<float, 3> input_backward(input_depth, input_rows, input_cols); 42 Tensor<float, 4> kernel(output_depth, input_depth, patch_rows, patch_cols); 43 Tensor<float, 3> output_backward(output_depth, output_rows, output_cols); 44 45 output_backward = output_backward.constant(11.0f) + output_backward.random(); 46 kernel = kernel.constant(2.0f) + kernel.random(); 47 input_backward.setRandom(); 48 49 input_backward = SpatialConvolutionBackwardInput(kernel, output_backward, 50 input_rows, input_cols, 1); 51 52 EXPECT_EQ(input_backward.dimension(0), input_depth); 53 EXPECT_EQ(input_backward.dimension(1), input_rows); 54 EXPECT_EQ(input_backward.dimension(2), input_cols); 55 56 for (int id = 0; id < input_depth; ++id) { 57 for (int i = 0; i < input_rows; ++i) { 58 for (int j = 0; j < input_cols; ++j) { 59 float expected = 0.0f; 60 for (int c = 0; c < patch_cols; ++c) { 61 for (int r = 0; r < patch_rows; ++r) { 62 for (int od = 0; od < output_depth; ++od) { 63 int output_i = i - r; 64 int output_j = j - c; 65 if (output_i >= 0 && output_i < output_rows && output_j >= 0 && 66 output_j < output_cols) { 67 expected += output_backward(od, output_i, output_j) * 68 kernel(od, id, r, c); 69 } 70 } 71 } 72 } 73 EigenApprox(input_backward(id, i, j), expected); 74 } 75 } 76 } 77 } 78 79 TEST(EigenBackwardSpatialConvolutionsTest, 80 test_simple_spatial_convolution_backward_input_valid_row_major) { 81 const int input_depth = 2; 82 const int input_rows = 3; 83 const int input_cols = 4; 84 const int output_depth = 5; 85 const int patch_rows = 2; 86 const int patch_cols = 2; 87 const int output_rows = input_rows - patch_rows + 1; 88 const int output_cols = input_cols - patch_cols + 1; 89 90 Tensor<float, 3, RowMajor> input_backward(input_cols, input_rows, 91 input_depth); 92 Tensor<float, 4, RowMajor> kernel(patch_cols, patch_rows, input_depth, 93 output_depth); 94 Tensor<float, 3, RowMajor> output_backward(output_cols, output_rows, 95 output_depth); 96 97 output_backward = output_backward.constant(11.0f) + output_backward.random(); 98 kernel = kernel.constant(2.0f) + kernel.random(); 99 input_backward.setRandom(); 100 101 input_backward = SpatialConvolutionBackwardInput(kernel, output_backward, 102 input_rows, input_cols, 1); 103 104 EXPECT_EQ(input_backward.dimension(0), input_cols); 105 EXPECT_EQ(input_backward.dimension(1), input_rows); 106 EXPECT_EQ(input_backward.dimension(2), input_depth); 107 108 for (int id = 0; id < input_depth; ++id) { 109 for (int i = 0; i < input_rows; ++i) { 110 for (int j = 0; j < input_cols; ++j) { 111 float expected = 0.0f; 112 for (int c = 0; c < patch_cols; ++c) { 113 for (int r = 0; r < patch_rows; ++r) { 114 for (int od = 0; od < output_depth; ++od) { 115 int output_i = i - r; 116 int output_j = j - c; 117 if (output_i >= 0 && output_i < output_rows && output_j >= 0 && 118 output_j < output_cols) { 119 expected += output_backward(output_j, output_i, od) * 120 kernel(c, r, id, od); 121 } 122 } 123 } 124 } 125 EigenApprox(input_backward(j, i, id), expected); 126 } 127 } 128 } 129 } 130 131 TEST(EigenBackwardSpatialConvolutionsTest, 132 test_simple_cuboid_convolution_backward_input_valid) { 133 const int input_depth = 2; 134 const int input_planes = 5; 135 const int input_rows = 3; 136 const int input_cols = 4; 137 const int patch_rows = 2; 138 const int patch_cols = 2; 139 const int patch_planes = 2; 140 const int output_rows = input_rows - patch_rows + 1; 141 const int output_cols = input_cols - patch_cols + 1; 142 const int output_planes = input_planes - patch_planes + 1; 143 const int output_depth = 5; 144 145 Tensor<float, 4> input_backward(input_depth, input_planes, input_rows, 146 input_cols); 147 Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows, 148 patch_cols); 149 Tensor<float, 4> output_backward(output_depth, output_planes, output_rows, 150 output_cols); 151 152 output_backward = output_backward.constant(11.0f) + output_backward.random(); 153 kernel = kernel.constant(2.0f) + kernel.random(); 154 input_backward.setRandom(); 155 156 input_backward = CuboidConvolutionBackwardInput( 157 kernel, output_backward, input_planes, input_rows, input_cols); 158 159 EXPECT_EQ(input_backward.dimension(3), input_cols); 160 EXPECT_EQ(input_backward.dimension(2), input_rows); 161 EXPECT_EQ(input_backward.dimension(1), input_planes); 162 EXPECT_EQ(input_backward.dimension(0), input_depth); 163 164 for (int id = 0; id < input_depth; ++id) { 165 for (int i = 0; i < input_planes; ++i) { 166 for (int j = 0; j < input_rows; ++j) { 167 for (int k = 0; k < input_cols; ++k) { 168 float expected = 0.0f; 169 for (int c = 0; c < patch_cols; ++c) { 170 for (int r = 0; r < patch_rows; ++r) { 171 for (int p = 0; p < patch_planes; ++p) { 172 for (int od = 0; od < output_depth; ++od) { 173 int output_j = j - r; 174 int output_k = k - c; 175 int output_i = i - p; 176 if (output_i >= 0 && output_i < output_planes && 177 output_j >= 0 && output_j < output_rows && 178 output_k >= 0 && output_k < output_cols) { 179 expected += 180 output_backward(od, output_i, output_j, output_k) * 181 kernel(od, id, p, r, c); 182 } 183 } 184 } 185 } 186 } 187 EigenApprox(input_backward(id, i, j, k), expected); 188 } 189 } 190 } 191 } 192 } 193 194 TEST(EigenBackwardSpatialConvolutionsTest, 195 test_simple_cuboid_convolution_backward_input_valid_row_major) { 196 const int input_depth = 2; 197 const int input_planes = 5; 198 const int input_rows = 3; 199 const int input_cols = 4; 200 const int patch_rows = 2; 201 const int patch_cols = 2; 202 const int patch_planes = 2; 203 const int output_rows = input_rows - patch_rows + 1; 204 const int output_cols = input_cols - patch_cols + 1; 205 const int output_planes = input_planes - patch_planes + 1; 206 const int output_depth = 5; 207 208 Tensor<float, 4, RowMajor> input_backward(input_cols, input_rows, 209 input_planes, input_depth); 210 Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes, 211 input_depth, output_depth); 212 Tensor<float, 4, RowMajor> output_backward(output_cols, output_rows, 213 output_planes, output_depth); 214 215 output_backward = output_backward.constant(11.0f) + output_backward.random(); 216 kernel = kernel.constant(2.0f) + kernel.random(); 217 input_backward.setRandom(); 218 219 input_backward = CuboidConvolutionBackwardInput( 220 kernel, output_backward, input_planes, input_rows, input_cols); 221 222 EXPECT_EQ(input_backward.dimension(0), input_cols); 223 EXPECT_EQ(input_backward.dimension(1), input_rows); 224 EXPECT_EQ(input_backward.dimension(2), input_planes); 225 EXPECT_EQ(input_backward.dimension(3), input_depth); 226 227 for (int id = 0; id < input_depth; ++id) { 228 for (int i = 0; i < input_planes; ++i) { 229 for (int j = 0; j < input_rows; ++j) { 230 for (int k = 0; k < input_cols; ++k) { 231 float expected = 0.0f; 232 for (int c = 0; c < patch_cols; ++c) { 233 for (int r = 0; r < patch_rows; ++r) { 234 for (int p = 0; p < patch_planes; ++p) { 235 for (int od = 0; od < output_depth; ++od) { 236 int output_j = j - r; 237 int output_k = k - c; 238 int output_i = i - p; 239 if (output_i >= 0 && output_i < output_planes && 240 output_j >= 0 && output_j < output_rows && 241 output_k >= 0 && output_k < output_cols) { 242 expected += 243 output_backward(output_k, output_j, output_i, od) * 244 kernel(c, r, p, id, od); 245 } 246 } 247 } 248 } 249 } 250 EigenApprox(input_backward(k, j, i, id), expected); 251 } 252 } 253 } 254 } 255 } 256 257 TEST(EigenBackwardSpatialConvolutionsTest, 258 test_simple_spatial_convolution_backward_input_same) { 259 const int input_depth = 2; 260 const int input_rows = 7; 261 const int input_cols = 9; 262 const int output_depth = 3; 263 const int patch_rows = 4; 264 const int patch_cols = 4; 265 const int output_rows = input_rows; 266 const int output_cols = input_cols; 267 268 Tensor<float, 3> input_backward(input_depth, input_rows, input_cols); 269 Tensor<float, 4> kernel(output_depth, input_depth, patch_rows, patch_cols); 270 Tensor<float, 3> output_backward(output_depth, output_rows, output_cols); 271 272 output_backward = output_backward.constant(11.0f) + output_backward.random(); 273 kernel = kernel.constant(2.0f) + kernel.random(); 274 275 input_backward = SpatialConvolutionBackwardInput(kernel, output_backward, 276 input_rows, input_cols, 1); 277 278 EXPECT_EQ(input_backward.dimension(0), input_depth); 279 EXPECT_EQ(input_backward.dimension(1), input_rows); 280 EXPECT_EQ(input_backward.dimension(2), input_cols); 281 282 for (int id = 0; id < input_depth; ++id) { 283 for (int i = 0; i < input_rows; ++i) { 284 for (int j = 0; j < input_cols; ++j) { 285 float expected = 0.0f; 286 for (int c = 0; c < patch_cols; ++c) { 287 for (int r = 0; r < patch_rows; ++r) { 288 for (int od = 0; od < output_depth; ++od) { 289 int output_i = i - r + (patch_rows - 1) / 2; 290 int output_j = j - c + (patch_cols - 1) / 2; 291 if (output_i >= 0 && output_i < output_rows && output_j >= 0 && 292 output_j < output_cols) { 293 expected += output_backward(od, output_i, output_j) * 294 kernel(od, id, r, c); 295 } 296 } 297 } 298 } 299 EigenApprox(input_backward(id, i, j), expected); 300 } 301 } 302 } 303 } 304 305 TEST(EigenBackwardSpatialConvolutionsTest, 306 test_simple_spatial_convolution_backward_input_same_row_major) { 307 const int input_depth = 2; 308 const int input_rows = 7; 309 const int input_cols = 9; 310 const int output_depth = 3; 311 const int patch_rows = 4; 312 const int patch_cols = 4; 313 const int output_rows = input_rows; 314 const int output_cols = input_cols; 315 316 Tensor<float, 3, RowMajor> input_backward(input_cols, input_rows, 317 input_depth); 318 Tensor<float, 4, RowMajor> kernel(patch_cols, patch_rows, input_depth, 319 output_depth); 320 Tensor<float, 3, RowMajor> output_backward(output_cols, output_rows, 321 output_depth); 322 323 output_backward = output_backward.constant(11.0f) + output_backward.random(); 324 kernel = kernel.constant(2.0f) + kernel.random(); 325 326 input_backward = SpatialConvolutionBackwardInput(kernel, output_backward, 327 input_rows, input_cols, 1); 328 329 EXPECT_EQ(input_backward.dimension(0), input_cols); 330 EXPECT_EQ(input_backward.dimension(1), input_rows); 331 EXPECT_EQ(input_backward.dimension(2), input_depth); 332 333 for (int id = 0; id < input_depth; ++id) { 334 for (int i = 0; i < input_rows; ++i) { 335 for (int j = 0; j < input_cols; ++j) { 336 float expected = 0.0f; 337 for (int c = 0; c < patch_cols; ++c) { 338 for (int r = 0; r < patch_rows; ++r) { 339 for (int od = 0; od < output_depth; ++od) { 340 int output_i = i - r + (patch_rows - 1) / 2; 341 int output_j = j - c + (patch_cols - 1) / 2; 342 if (output_i >= 0 && output_i < output_rows && output_j >= 0 && 343 output_j < output_cols) { 344 expected += output_backward(output_j, output_i, od) * 345 kernel(c, r, id, od); 346 } 347 } 348 } 349 } 350 EigenApprox(input_backward(j, i, id), expected); 351 } 352 } 353 } 354 } 355 356 TEST(EigenBackwardSpatialConvolutionsTest, 357 test_simple_cuboid_convolution_backward_input_same) { 358 const int input_depth = 2; 359 const int input_planes = 5; 360 const int input_rows = 3; 361 const int input_cols = 4; 362 const int patch_rows = 3; 363 const int patch_cols = 2; 364 const int patch_planes = 4; 365 const int output_rows = input_rows; 366 const int output_cols = input_cols; 367 const int output_planes = input_planes; 368 const int output_depth = 5; 369 370 Tensor<float, 4> input_backward(input_depth, input_planes, input_rows, 371 input_cols); 372 Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows, 373 patch_cols); 374 Tensor<float, 4> output_backward(output_depth, output_planes, output_rows, 375 output_cols); 376 377 output_backward = output_backward.constant(11.0f) + output_backward.random(); 378 kernel = kernel.constant(2.0f) + kernel.random(); 379 input_backward.setRandom(); 380 381 input_backward = CuboidConvolutionBackwardInput( 382 kernel, output_backward, input_planes, input_rows, input_cols); 383 384 EXPECT_EQ(input_backward.dimension(3), input_cols); 385 EXPECT_EQ(input_backward.dimension(2), input_rows); 386 EXPECT_EQ(input_backward.dimension(1), input_planes); 387 EXPECT_EQ(input_backward.dimension(0), input_depth); 388 389 const int dz = patch_planes - 1; 390 const int dy = patch_rows - 1; 391 const int dx = patch_cols - 1; 392 393 const int forward_pad_x = dx / 2; 394 const int forward_pad_y = dy / 2; 395 const int forward_pad_z = dz / 2; 396 397 for (int id = 0; id < input_depth; ++id) { 398 for (int i = 0; i < input_planes; ++i) { 399 for (int j = 0; j < input_rows; ++j) { 400 for (int k = 0; k < input_cols; ++k) { 401 float expected = 0.0f; 402 for (int c = 0; c < patch_cols; ++c) { 403 for (int r = 0; r < patch_rows; ++r) { 404 for (int p = 0; p < patch_planes; ++p) { 405 for (int od = 0; od < output_depth; ++od) { 406 int output_i = i - p + forward_pad_z; 407 int output_j = j - r + forward_pad_y; 408 int output_k = k - c + forward_pad_x; 409 if (output_i >= 0 && output_i < output_planes && 410 output_j >= 0 && output_j < output_rows && 411 output_k >= 0 && output_k < output_cols) { 412 expected += 413 output_backward(od, output_i, output_j, output_k) * 414 kernel(od, id, p, r, c); 415 } 416 } 417 } 418 } 419 } 420 EigenApprox(input_backward(id, i, j, k), expected); 421 } 422 } 423 } 424 } 425 } 426 427 TEST(EigenBackwardSpatialConvolutionsTest, 428 test_simple_cuboid_convolution_backward_input_same_row_major) { 429 const int input_depth = 2; 430 const int input_planes = 5; 431 const int input_rows = 3; 432 const int input_cols = 4; 433 const int patch_rows = 2; 434 const int patch_cols = 3; 435 const int patch_planes = 4; 436 const int output_rows = input_rows; 437 const int output_cols = input_cols; 438 const int output_planes = input_planes; 439 const int output_depth = 5; 440 441 Tensor<float, 4, RowMajor> input_backward(input_cols, input_rows, 442 input_planes, input_depth); 443 Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes, 444 input_depth, output_depth); 445 Tensor<float, 4, RowMajor> output_backward(output_cols, output_rows, 446 output_planes, output_depth); 447 448 output_backward = output_backward.constant(11.0f) + output_backward.random(); 449 kernel = kernel.constant(2.0f) + kernel.random(); 450 input_backward.setRandom(); 451 452 input_backward = CuboidConvolutionBackwardInput( 453 kernel, output_backward, input_planes, input_rows, input_cols); 454 455 EXPECT_EQ(input_backward.dimension(0), input_cols); 456 EXPECT_EQ(input_backward.dimension(1), input_rows); 457 EXPECT_EQ(input_backward.dimension(2), input_planes); 458 EXPECT_EQ(input_backward.dimension(3), input_depth); 459 460 const int dz = patch_planes - 1; 461 const int dy = patch_rows - 1; 462 const int dx = patch_cols - 1; 463 464 const int forward_pad_x = dx / 2; 465 const int forward_pad_y = dy / 2; 466 const int forward_pad_z = dz / 2; 467 468 for (int id = 0; id < input_depth; ++id) { 469 for (int i = 0; i < input_planes; ++i) { 470 for (int j = 0; j < input_rows; ++j) { 471 for (int k = 0; k < input_cols; ++k) { 472 float expected = 0.0f; 473 for (int c = 0; c < patch_cols; ++c) { 474 for (int r = 0; r < patch_rows; ++r) { 475 for (int p = 0; p < patch_planes; ++p) { 476 for (int od = 0; od < output_depth; ++od) { 477 int output_i = i - p + forward_pad_z; 478 int output_j = j - r + forward_pad_y; 479 int output_k = k - c + forward_pad_x; 480 if (output_i >= 0 && output_i < output_planes && 481 output_j >= 0 && output_j < output_rows && 482 output_k >= 0 && output_k < output_cols) { 483 expected += 484 output_backward(output_k, output_j, output_i, od) * 485 kernel(c, r, p, id, od); 486 } 487 } 488 } 489 } 490 } 491 EigenApprox(input_backward(k, j, i, id), expected); 492 } 493 } 494 } 495 } 496 } 497 498 TEST(EigenBackwardSpatialConvolutionsTest, 499 test_batched_spatial_convolution_backward_input_valid) { 500 const int num_batches = 13; 501 const int input_depth = 2; 502 const int input_rows = 7; 503 const int input_cols = 9; 504 const int output_depth = 3; 505 const int patch_rows = 5; 506 const int patch_cols = 5; 507 const int output_rows = input_rows - patch_rows + 1; 508 const int output_cols = input_cols - patch_cols + 1; 509 510 Tensor<float, 4> input_backward(input_depth, input_rows, input_cols, 511 num_batches); 512 Tensor<float, 4> kernel(output_depth, input_depth, patch_rows, patch_cols); 513 Tensor<float, 4> output_backward(output_depth, output_rows, output_cols, 514 num_batches); 515 516 output_backward = output_backward.constant(11.0f) + output_backward.random(); 517 kernel = kernel.constant(2.0f) + kernel.random(); 518 input_backward.setRandom(); 519 520 input_backward = SpatialConvolutionBackwardInput(kernel, output_backward, 521 input_rows, input_cols, 1); 522 523 EXPECT_EQ(input_backward.dimension(0), input_depth); 524 EXPECT_EQ(input_backward.dimension(1), input_rows); 525 EXPECT_EQ(input_backward.dimension(2), input_cols); 526 EXPECT_EQ(input_backward.dimension(3), num_batches); 527 528 for (int b = 0; b < num_batches; ++b) { 529 for (int id = 0; id < input_depth; ++id) { 530 for (int i = 0; i < input_rows; ++i) { 531 for (int j = 0; j < input_cols; ++j) { 532 float expected = 0.0f; 533 for (int c = 0; c < patch_cols; ++c) { 534 for (int r = 0; r < patch_rows; ++r) { 535 for (int od = 0; od < output_depth; ++od) { 536 int output_i = i - r; 537 int output_j = j - c; 538 if (output_i >= 0 && output_i < output_rows && output_j >= 0 && 539 output_j < output_cols) { 540 expected += output_backward(od, output_i, output_j, b) * 541 kernel(od, id, r, c); 542 } 543 } 544 } 545 } 546 EigenApprox(input_backward(id, i, j, b), expected); 547 } 548 } 549 } 550 } 551 } 552 553 TEST(EigenBackwardSpatialConvolutionsTest, 554 test_batched_spatial_convolution_backward_input_valid_row_major) { 555 const int num_batches = 13; 556 const int input_depth = 2; 557 const int input_rows = 7; 558 const int input_cols = 9; 559 const int output_depth = 3; 560 const int patch_rows = 5; 561 const int patch_cols = 5; 562 const int output_rows = input_rows - patch_rows + 1; 563 const int output_cols = input_cols - patch_cols + 1; 564 565 Tensor<float, 4, RowMajor> input_backward(num_batches, input_cols, input_rows, 566 input_depth); 567 Tensor<float, 4, RowMajor> kernel(patch_cols, patch_rows, input_depth, 568 output_depth); 569 Tensor<float, 4, RowMajor> output_backward(num_batches, output_cols, 570 output_rows, output_depth); 571 572 output_backward = output_backward.constant(11.0f) + output_backward.random(); 573 kernel = kernel.constant(2.0f) + kernel.random(); 574 input_backward.setRandom(); 575 576 input_backward = SpatialConvolutionBackwardInput(kernel, output_backward, 577 input_rows, input_cols, 1); 578 579 EXPECT_EQ(input_backward.dimension(0), num_batches); 580 EXPECT_EQ(input_backward.dimension(1), input_cols); 581 EXPECT_EQ(input_backward.dimension(2), input_rows); 582 EXPECT_EQ(input_backward.dimension(3), input_depth); 583 584 for (int b = 0; b < num_batches; ++b) { 585 for (int id = 0; id < input_depth; ++id) { 586 for (int i = 0; i < input_rows; ++i) { 587 for (int j = 0; j < input_cols; ++j) { 588 float expected = 0.0f; 589 for (int c = 0; c < patch_cols; ++c) { 590 for (int r = 0; r < patch_rows; ++r) { 591 for (int od = 0; od < output_depth; ++od) { 592 int output_i = i - r; 593 int output_j = j - c; 594 if (output_i >= 0 && output_i < output_rows && output_j >= 0 && 595 output_j < output_cols) { 596 expected += output_backward(b, output_j, output_i, od) * 597 kernel(c, r, id, od); 598 } 599 } 600 } 601 } 602 EigenApprox(input_backward(b, j, i, id), expected); 603 } 604 } 605 } 606 } 607 } 608 609 TEST(EigenBackwardSpatialConvolutionsTest, 610 test_batched_cuboid_convolution_backward_input_valid) { 611 const int num_batches = 13; 612 const int input_depth = 2; 613 const int input_planes = 5; 614 const int input_rows = 3; 615 const int input_cols = 4; 616 const int patch_rows = 2; 617 const int patch_cols = 2; 618 const int patch_planes = 2; 619 const int output_rows = input_rows - patch_rows + 1; 620 const int output_cols = input_cols - patch_cols + 1; 621 const int output_planes = input_planes - patch_planes + 1; 622 const int output_depth = 5; 623 624 Tensor<float, 5> input_backward(input_depth, input_planes, input_rows, 625 input_cols, num_batches); 626 Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows, 627 patch_cols); 628 Tensor<float, 5> output_backward(output_depth, output_planes, output_rows, 629 output_cols, num_batches); 630 631 output_backward = output_backward.constant(11.0f) + output_backward.random(); 632 kernel = kernel.constant(2.0f) + kernel.random(); 633 input_backward.setRandom(); 634 635 input_backward = CuboidConvolutionBackwardInput( 636 kernel, output_backward, input_planes, input_rows, input_cols); 637 638 EXPECT_EQ(input_backward.dimension(4), num_batches); 639 EXPECT_EQ(input_backward.dimension(3), input_cols); 640 EXPECT_EQ(input_backward.dimension(2), input_rows); 641 EXPECT_EQ(input_backward.dimension(1), input_planes); 642 EXPECT_EQ(input_backward.dimension(0), input_depth); 643 644 for (int b = 0; b < num_batches; ++b) { 645 for (int id = 0; id < input_depth; ++id) { 646 for (int i = 0; i < input_planes; ++i) { 647 for (int j = 0; j < input_rows; ++j) { 648 for (int k = 0; k < input_cols; ++k) { 649 float expected = 0.0f; 650 for (int c = 0; c < patch_cols; ++c) { 651 for (int r = 0; r < patch_rows; ++r) { 652 for (int p = 0; p < patch_planes; ++p) { 653 for (int od = 0; od < output_depth; ++od) { 654 int output_i = i - p; 655 int output_j = j - r; 656 int output_k = k - c; 657 if (output_i >= 0 && output_i < output_planes && 658 output_j >= 0 && output_j < output_rows && 659 output_k >= 0 && output_k < output_cols) { 660 expected += 661 output_backward(od, output_i, output_j, output_k, b) * 662 kernel(od, id, p, r, c); 663 } 664 } 665 } 666 } 667 } 668 EigenApprox(input_backward(id, i, j, k, b), expected); 669 } 670 } 671 } 672 } 673 } 674 } 675 676 TEST(EigenBackwardSpatialConvolutionsTest, 677 test_batched_cuboid_convolution_backward_input_valid_row_major) { 678 const int num_batches = 13; 679 const int input_depth = 2; 680 const int input_planes = 5; 681 const int input_rows = 3; 682 const int input_cols = 4; 683 const int patch_rows = 2; 684 const int patch_cols = 2; 685 const int patch_planes = 2; 686 const int output_rows = input_rows - patch_rows + 1; 687 const int output_cols = input_cols - patch_cols + 1; 688 const int output_planes = input_planes - patch_planes + 1; 689 const int output_depth = 5; 690 691 Tensor<float, 5, RowMajor> input_backward(num_batches, input_cols, input_rows, 692 input_planes, input_depth); 693 Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes, 694 input_depth, output_depth); 695 Tensor<float, 5, RowMajor> output_backward( 696 num_batches, output_cols, output_rows, output_planes, output_depth); 697 698 output_backward = output_backward.constant(11.0f) + output_backward.random(); 699 kernel = kernel.constant(2.0f) + kernel.random(); 700 input_backward.setRandom(); 701 702 input_backward = CuboidConvolutionBackwardInput( 703 kernel, output_backward, input_planes, input_rows, input_cols); 704 705 EXPECT_EQ(input_backward.dimension(0), num_batches); 706 EXPECT_EQ(input_backward.dimension(1), input_cols); 707 EXPECT_EQ(input_backward.dimension(2), input_rows); 708 EXPECT_EQ(input_backward.dimension(3), input_planes); 709 EXPECT_EQ(input_backward.dimension(4), input_depth); 710 711 for (int b = 0; b < num_batches; ++b) { 712 for (int id = 0; id < input_depth; ++id) { 713 for (int i = 0; i < input_planes; ++i) { 714 for (int j = 0; j < input_rows; ++j) { 715 for (int k = 0; k < input_cols; ++k) { 716 float expected = 0.0f; 717 for (int c = 0; c < patch_cols; ++c) { 718 for (int r = 0; r < patch_rows; ++r) { 719 for (int p = 0; p < patch_planes; ++p) { 720 for (int od = 0; od < output_depth; ++od) { 721 int output_i = i - p; 722 int output_j = j - r; 723 int output_k = k - c; 724 if (output_i >= 0 && output_i < output_planes && 725 output_j >= 0 && output_j < output_rows && 726 output_k >= 0 && output_k < output_cols) { 727 expected += 728 output_backward(b, output_k, output_j, output_i, od) * 729 kernel(c, r, p, id, od); 730 } 731 } 732 } 733 } 734 } 735 EigenApprox(input_backward(b, k, j, i, id), expected); 736 } 737 } 738 } 739 } 740 } 741 } 742 743 static void test_batched_strided_spatial_convolution_backward_input_valid( 744 const int num_batches, const int input_depth, const int input_rows, 745 const int input_cols, const int output_depth) { 746 const int patch_rows = 3; 747 const int patch_cols = 3; 748 749 const int stride = 3; 750 751 const int output_rows = divup(input_rows - patch_rows + 1, stride); 752 const int output_cols = divup(input_cols - patch_cols + 1, stride); 753 754 Tensor<float, 4> input_backward(input_depth, input_rows, input_cols, 755 num_batches); 756 Tensor<float, 4> kernel(output_depth, input_depth, patch_rows, patch_cols); 757 Tensor<float, 4> output_backward(output_depth, output_rows, output_cols, 758 num_batches); 759 760 output_backward = output_backward.constant(11.0f) + output_backward.random(); 761 kernel = kernel.constant(2.0f) + kernel.random(); 762 input_backward.setRandom(); 763 764 input_backward = SpatialConvolutionBackwardInput( 765 kernel, output_backward, input_rows, input_cols, stride, stride); 766 767 EXPECT_EQ(input_backward.dimension(0), input_depth); 768 EXPECT_EQ(input_backward.dimension(1), input_rows); 769 EXPECT_EQ(input_backward.dimension(2), input_cols); 770 EXPECT_EQ(input_backward.dimension(3), num_batches); 771 772 for (int b = 0; b < num_batches; ++b) { 773 for (int id = 0; id < input_depth; ++id) { 774 for (int i = 0; i < input_rows; ++i) { 775 for (int j = 0; j < input_cols; ++j) { 776 float expected = 0.0f; 777 for (int c = 0; c < patch_cols; ++c) { 778 for (int r = 0; r < patch_rows; ++r) { 779 for (int od = 0; od < output_depth; ++od) { 780 int output_i = i - r; 781 int output_j = j - c; 782 if (output_i >= 0 && output_i / stride < output_rows && 783 output_j >= 0 && output_j / stride < output_cols && 784 output_i % stride == 0 && output_j % stride == 0) { 785 expected += output_backward(od, output_i / stride, 786 output_j / stride, b) * 787 kernel(od, id, r, c); 788 } 789 } 790 } 791 } 792 EigenApprox(input_backward(id, i, j, b), expected); 793 } 794 } 795 } 796 } 797 } 798 799 TEST(EigenBackwardSpatialConvolutionsTest, 800 test_batched_strided_spatial_convolution_backward_input_valid) { 801 int num_batches = 1; 802 int input_depth = 1; 803 int input_rows = 3; 804 int input_cols = 5; 805 int output_depth = 1; 806 test_batched_strided_spatial_convolution_backward_input_valid( 807 num_batches, input_depth, input_rows, input_cols, output_depth); 808 809 num_batches = 11; 810 input_depth = 2; 811 input_rows = 9; 812 input_cols = 13; 813 output_depth = 5; 814 test_batched_strided_spatial_convolution_backward_input_valid( 815 num_batches, input_depth, input_rows, input_cols, output_depth); 816 } 817 818 static void 819 test_batched_strided_spatial_convolution_backward_input_valid_row_major( 820 const int num_batches, const int input_depth, const int input_rows, 821 const int input_cols, const int output_depth) { 822 const int patch_rows = 3; 823 const int patch_cols = 3; 824 825 const int stride = 3; 826 827 const int output_rows = divup(input_rows - patch_rows + 1, stride); 828 const int output_cols = divup(input_cols - patch_cols + 1, stride); 829 830 Tensor<float, 4, RowMajor> input_backward(num_batches, input_cols, input_rows, 831 input_depth); 832 Tensor<float, 4, RowMajor> kernel(patch_cols, patch_rows, input_depth, 833 output_depth); 834 Tensor<float, 4, RowMajor> output_backward(num_batches, output_cols, 835 output_rows, output_depth); 836 837 output_backward = output_backward.constant(11.0f) + output_backward.random(); 838 kernel = kernel.constant(2.0f) + kernel.random(); 839 input_backward.setRandom(); 840 841 input_backward = SpatialConvolutionBackwardInput( 842 kernel, output_backward, input_rows, input_cols, stride, stride); 843 844 EXPECT_EQ(input_backward.dimension(0), num_batches); 845 EXPECT_EQ(input_backward.dimension(1), input_cols); 846 EXPECT_EQ(input_backward.dimension(2), input_rows); 847 EXPECT_EQ(input_backward.dimension(3), input_depth); 848 849 for (int b = 0; b < num_batches; ++b) { 850 for (int id = 0; id < input_depth; ++id) { 851 for (int i = 0; i < input_rows; ++i) { 852 for (int j = 0; j < input_cols; ++j) { 853 float expected = 0.0f; 854 for (int c = 0; c < patch_cols; ++c) { 855 for (int r = 0; r < patch_rows; ++r) { 856 for (int od = 0; od < output_depth; ++od) { 857 int output_i = i - r; 858 int output_j = j - c; 859 if (output_i >= 0 && output_i / stride < output_rows && 860 output_j >= 0 && output_j / stride < output_cols && 861 output_i % stride == 0 && output_j % stride == 0) { 862 expected += output_backward(b, output_j / stride, 863 output_i / stride, od) * 864 kernel(c, r, id, od); 865 } 866 } 867 } 868 } 869 EigenApprox(input_backward(b, j, i, id), expected); 870 } 871 } 872 } 873 } 874 } 875 876 TEST(EigenBackwardSpatialConvolutionsTest, 877 test_batched_strided_spatial_convolution_backward_input_valid_row_major) { 878 int num_batches = 1; 879 int input_depth = 1; 880 int input_rows = 3; 881 int input_cols = 5; 882 int output_depth = 1; 883 test_batched_strided_spatial_convolution_backward_input_valid_row_major( 884 num_batches, input_depth, input_rows, input_cols, output_depth); 885 886 num_batches = 11; 887 input_depth = 2; 888 input_rows = 9; 889 input_cols = 13; 890 output_depth = 5; 891 test_batched_strided_spatial_convolution_backward_input_valid_row_major( 892 num_batches, input_depth, input_rows, input_cols, output_depth); 893 } 894 895 TEST(EigenBackwardSpatialConvolutionsTest, 896 test_simple_spatial_convolution_backward_kernel_valid) { 897 const int input_depth = 2; 898 const int input_rows = 3; 899 const int input_cols = 4; 900 const int output_depth = 5; 901 const int patch_rows = 2; 902 const int patch_cols = 2; 903 const int output_rows = input_rows - patch_rows + 1; 904 const int output_cols = input_cols - patch_cols + 1; 905 906 Tensor<float, 3> input(input_depth, input_rows, input_cols); 907 Tensor<float, 4> kernel(output_depth, input_depth, patch_rows, patch_cols); 908 Tensor<float, 3> output_backward(output_depth, output_rows, output_cols); 909 910 output_backward = output_backward.constant(11.0f) + output_backward.random(); 911 input = input.constant(2.0f) + input.random(); 912 kernel.setRandom(); 913 914 kernel = SpatialConvolutionBackwardKernel(input, output_backward, patch_rows, 915 patch_cols, 1, 1); 916 917 EXPECT_EQ(kernel.dimension(0), output_depth); 918 EXPECT_EQ(kernel.dimension(1), input_depth); 919 EXPECT_EQ(kernel.dimension(2), patch_rows); 920 EXPECT_EQ(kernel.dimension(3), patch_cols); 921 922 for (int od = 0; od < output_depth; ++od) { 923 for (int id = 0; id < input_depth; ++id) { 924 for (int r = 0; r < patch_rows; ++r) { 925 for (int c = 0; c < patch_cols; ++c) { 926 float expected = 0.0f; 927 for (int i = 0; i < input_rows; ++i) { 928 for (int j = 0; j < input_cols; ++j) { 929 int output_i = i - r; 930 int output_j = j - c; 931 if (output_i >= 0 && output_i < output_rows && output_j >= 0 && 932 output_j < output_cols) { 933 expected += 934 input(id, i, j) * output_backward(od, output_i, output_j); 935 } 936 } 937 } 938 EigenApprox(kernel(od, id, r, c), expected); 939 } 940 } 941 } 942 } 943 } 944 945 TEST(EigenBackwardSpatialConvolutionsTest, 946 test_simple_spatial_convolution_backward_kernel_valid_row_major) { 947 const int input_depth = 2; 948 const int input_rows = 3; 949 const int input_cols = 4; 950 const int output_depth = 5; 951 const int patch_rows = 2; 952 const int patch_cols = 2; 953 const int output_rows = input_rows - patch_rows + 1; 954 const int output_cols = input_cols - patch_cols + 1; 955 956 Tensor<float, 3, RowMajor> input(input_cols, input_rows, input_depth); 957 Tensor<float, 4, RowMajor> kernel(patch_cols, patch_rows, input_depth, 958 output_depth); 959 Tensor<float, 3, RowMajor> output_backward(output_cols, output_rows, 960 output_depth); 961 962 output_backward = output_backward.constant(11.0f) + output_backward.random(); 963 input = input.constant(2.0f) + input.random(); 964 kernel.setRandom(); 965 966 kernel = SpatialConvolutionBackwardKernel(input, output_backward, patch_rows, 967 patch_cols, 1, 1); 968 969 EXPECT_EQ(kernel.dimension(0), patch_cols); 970 EXPECT_EQ(kernel.dimension(1), patch_rows); 971 EXPECT_EQ(kernel.dimension(2), input_depth); 972 EXPECT_EQ(kernel.dimension(3), output_depth); 973 974 for (int od = 0; od < output_depth; ++od) { 975 for (int id = 0; id < input_depth; ++id) { 976 for (int r = 0; r < patch_rows; ++r) { 977 for (int c = 0; c < patch_cols; ++c) { 978 float expected = 0.0f; 979 for (int i = 0; i < input_rows; ++i) { 980 for (int j = 0; j < input_cols; ++j) { 981 int output_i = i - r; 982 int output_j = j - c; 983 if (output_i >= 0 && output_i < output_rows && output_j >= 0 && 984 output_j < output_cols) { 985 expected += 986 input(j, i, id) * output_backward(output_j, output_i, od); 987 } 988 } 989 } 990 EigenApprox(kernel(c, r, id, od), expected); 991 } 992 } 993 } 994 } 995 } 996 997 TEST(EigenBackwardSpatialConvolutionsTest, 998 test_batched_atrous_spatial_convolution_backward_input_valid) { 999 const int num_batches = 11; 1000 const int patch_rows = 3; 1001 const int patch_cols = 3; 1002 1003 const int input_depth = 2; 1004 const int input_rows = 9; 1005 const int input_cols = 13; 1006 1007 const int in_stride = 3; 1008 const int patch_rows_eff = patch_rows + (patch_rows - 1) * (in_stride - 1); 1009 const int patch_cols_eff = patch_cols + (patch_cols - 1) * (in_stride - 1); 1010 1011 const int output_depth = 5; 1012 const int output_rows = input_rows - patch_rows_eff + 1; 1013 const int output_cols = input_cols - patch_cols_eff + 1; 1014 1015 Tensor<float, 4> output_backward(output_depth, output_rows, output_cols, 1016 num_batches); 1017 output_backward.setRandom(); 1018 Tensor<float, 4> kernel(output_depth, input_depth, patch_rows, patch_cols); 1019 kernel.setRandom(); 1020 1021 const array<DenseIndex, 4> kernel_strides({1, 1, in_stride, in_stride}); 1022 const Tensor<float, 4> kernel_eff = kernel.inflate(kernel_strides); 1023 1024 const Tensor<float, 4> input_backward = 1025 SpatialConvolutionBackwardInput(kernel, output_backward, input_rows, 1026 input_cols, 1, 1, in_stride, in_stride); 1027 const Tensor<float, 4> expected_input_backward = 1028 SpatialConvolutionBackwardInput(kernel_eff, output_backward, input_rows, 1029 input_cols); 1030 1031 EXPECT_EQ(input_backward.dimension(0), input_depth); 1032 EXPECT_EQ(input_backward.dimension(1), input_rows); 1033 EXPECT_EQ(input_backward.dimension(2), input_cols); 1034 EXPECT_EQ(input_backward.dimension(3), num_batches); 1035 1036 eigen_assert(dimensions_match(input_backward.dimensions(), 1037 expected_input_backward.dimensions())); 1038 for (ptrdiff_t i = 0; i < input_backward.dimensions().TotalSize(); ++i) { 1039 EigenApprox(input_backward.data()[i], expected_input_backward.data()[i]); 1040 } 1041 } 1042 1043 TEST( 1044 EigenBackwardSpatialConvolutionsTest, 1045 test_batched_atrous_spatial_convolution_backward_input_valid_unequal_strides) { 1046 const int num_batches = 11; 1047 const int patch_rows = 3; 1048 const int patch_cols = 3; 1049 1050 const int input_depth = 2; 1051 const int input_rows = 9; 1052 const int input_cols = 13; 1053 1054 const int row_in_stride = 3; 1055 const int col_in_stride = 1; 1056 const int patch_rows_eff = 1057 patch_rows + (patch_rows - 1) * (row_in_stride - 1); 1058 const int patch_cols_eff = 1059 patch_cols + (patch_cols - 1) * (col_in_stride - 1); 1060 1061 const int output_depth = 5; 1062 const int output_rows = input_rows - patch_rows_eff + 1; 1063 const int output_cols = input_cols - patch_cols_eff + 1; 1064 1065 Tensor<float, 4> output_backward(output_depth, output_rows, output_cols, 1066 num_batches); 1067 output_backward.setRandom(); 1068 Tensor<float, 4> kernel(output_depth, input_depth, patch_rows, patch_cols); 1069 kernel.setRandom(); 1070 1071 const array<DenseIndex, 4> kernel_strides( 1072 {1, 1, row_in_stride, col_in_stride}); 1073 const Tensor<float, 4> kernel_eff = kernel.inflate(kernel_strides); 1074 1075 const Tensor<float, 4> input_backward = SpatialConvolutionBackwardInput( 1076 kernel, output_backward, input_rows, input_cols, 1, 1, row_in_stride, 1077 col_in_stride); 1078 const Tensor<float, 4> expected_input_backward = 1079 SpatialConvolutionBackwardInput(kernel_eff, output_backward, input_rows, 1080 input_cols); 1081 1082 EXPECT_EQ(input_backward.dimension(0), input_depth); 1083 EXPECT_EQ(input_backward.dimension(1), input_rows); 1084 EXPECT_EQ(input_backward.dimension(2), input_cols); 1085 EXPECT_EQ(input_backward.dimension(3), num_batches); 1086 1087 eigen_assert(dimensions_match(input_backward.dimensions(), 1088 expected_input_backward.dimensions())); 1089 for (ptrdiff_t i = 0; i < input_backward.dimensions().TotalSize(); ++i) { 1090 EigenApprox(input_backward.data()[i], expected_input_backward.data()[i]); 1091 } 1092 } 1093 1094 TEST(EigenBackwardSpatialConvolutionsTest, 1095 test_batched_atrous_spatial_convolution_backward_input_valid_row_major) { 1096 const int num_batches = 11; 1097 const int patch_rows = 3; 1098 const int patch_cols = 3; 1099 1100 const int input_depth = 2; 1101 const int input_rows = 9; 1102 const int input_cols = 13; 1103 1104 const int in_stride = 3; 1105 const int patch_rows_eff = patch_rows + (patch_rows - 1) * (in_stride - 1); 1106 const int patch_cols_eff = patch_cols + (patch_cols - 1) * (in_stride - 1); 1107 1108 const int output_depth = 5; 1109 const int output_rows = input_rows - patch_rows_eff + 1; 1110 const int output_cols = input_cols - patch_cols_eff + 1; 1111 1112 Tensor<float, 4, RowMajor> output_backward(num_batches, output_cols, 1113 output_rows, output_depth); 1114 output_backward.setRandom(); 1115 1116 Tensor<float, 4, RowMajor> kernel(patch_cols, patch_rows, input_depth, 1117 output_depth); 1118 kernel.setRandom(); 1119 1120 const array<DenseIndex, 4> kernel_strides({in_stride, in_stride, 1, 1}); 1121 const Tensor<float, 4, RowMajor> kernel_eff = kernel.inflate(kernel_strides); 1122 1123 const Tensor<float, 4, RowMajor> input_backward = 1124 SpatialConvolutionBackwardInput(kernel, output_backward, input_rows, 1125 input_cols, 1, 1, in_stride, in_stride); 1126 const Tensor<float, 4, RowMajor> expected_input_backward = 1127 SpatialConvolutionBackwardInput(kernel_eff, output_backward, input_rows, 1128 input_cols); 1129 1130 EXPECT_EQ(input_backward.dimension(0), num_batches); 1131 EXPECT_EQ(input_backward.dimension(1), input_cols); 1132 EXPECT_EQ(input_backward.dimension(2), input_rows); 1133 EXPECT_EQ(input_backward.dimension(3), input_depth); 1134 1135 eigen_assert(dimensions_match(input_backward.dimensions(), 1136 expected_input_backward.dimensions())); 1137 for (ptrdiff_t i = 0; i < input_backward.dimensions().TotalSize(); ++i) { 1138 EigenApprox(input_backward.data()[i], expected_input_backward.data()[i]); 1139 } 1140 } 1141 1142 TEST(EigenBackwardSpatialConvolutionsTest, 1143 test_batched_atrous_spatial_convolution_backward_kernel_valid) { 1144 const int num_batches = 11; 1145 const int patch_rows = 3; 1146 const int patch_cols = 3; 1147 1148 const int input_depth = 2; 1149 const int input_rows = 9; 1150 const int input_cols = 13; 1151 1152 const int in_stride = 3; 1153 const int patch_rows_eff = patch_rows + (patch_rows - 1) * (in_stride - 1); 1154 const int patch_cols_eff = patch_cols + (patch_cols - 1) * (in_stride - 1); 1155 1156 const int output_depth = 5; 1157 const int output_rows = input_rows - patch_rows_eff + 1; 1158 const int output_cols = input_cols - patch_cols_eff + 1; 1159 1160 Tensor<float, 4> output_backward(output_depth, output_rows, output_cols, 1161 num_batches); 1162 output_backward.setRandom(); 1163 1164 Tensor<float, 4> input(input_depth, input_rows, input_cols, num_batches); 1165 input.setRandom(); 1166 1167 const array<DenseIndex, 4> kernel_strides({1, 1, in_stride, in_stride}); 1168 1169 const Tensor<float, 4> kernel_backward = 1170 SpatialConvolutionBackwardKernel(input, output_backward, patch_rows, 1171 patch_cols, 1, 1, in_stride, in_stride); 1172 const Tensor<float, 4> expected_kernel_backward = 1173 SpatialConvolutionBackwardKernel(input, output_backward, patch_rows_eff, 1174 patch_cols_eff) 1175 .stride(kernel_strides); 1176 1177 EXPECT_EQ(kernel_backward.dimension(0), output_depth); 1178 EXPECT_EQ(kernel_backward.dimension(1), input_depth); 1179 EXPECT_EQ(kernel_backward.dimension(2), patch_rows); 1180 EXPECT_EQ(kernel_backward.dimension(3), patch_cols); 1181 1182 eigen_assert(dimensions_match(kernel_backward.dimensions(), 1183 expected_kernel_backward.dimensions())); 1184 for (ptrdiff_t i = 0; i < kernel_backward.dimensions().TotalSize(); ++i) { 1185 EigenApprox(kernel_backward.data()[i], expected_kernel_backward.data()[i]); 1186 } 1187 } 1188 1189 TEST(EigenBackwardSpatialConvolutionsTest, 1190 test_batched_atrous_spatial_convolution_backward_kernel_valid_row_major) { 1191 const int num_batches = 11; 1192 const int patch_rows = 3; 1193 const int patch_cols = 3; 1194 1195 const int input_depth = 2; 1196 const int input_rows = 9; 1197 const int input_cols = 13; 1198 1199 const int in_stride = 3; 1200 const int patch_rows_eff = patch_rows + (patch_rows - 1) * (in_stride - 1); 1201 const int patch_cols_eff = patch_cols + (patch_cols - 1) * (in_stride - 1); 1202 1203 const int output_depth = 5; 1204 const int output_rows = input_rows - patch_rows_eff + 1; 1205 const int output_cols = input_cols - patch_cols_eff + 1; 1206 1207 Tensor<float, 4, RowMajor> output_backward(num_batches, output_cols, 1208 output_rows, output_depth); 1209 output_backward.setRandom(); 1210 1211 Tensor<float, 4, RowMajor> input(num_batches, input_cols, input_rows, 1212 input_depth); 1213 input.setRandom(); 1214 1215 const array<DenseIndex, 4> kernel_strides({in_stride, in_stride, 1, 1}); 1216 1217 const Tensor<float, 4, RowMajor> kernel_backward = 1218 SpatialConvolutionBackwardKernel(input, output_backward, patch_rows, 1219 patch_cols, 1, 1, in_stride, in_stride); 1220 const Tensor<float, 4, RowMajor> expected_kernel_backward = 1221 SpatialConvolutionBackwardKernel(input, output_backward, patch_rows_eff, 1222 patch_cols_eff) 1223 .stride(kernel_strides); 1224 1225 EXPECT_EQ(kernel_backward.dimension(0), patch_cols); 1226 EXPECT_EQ(kernel_backward.dimension(1), patch_rows); 1227 EXPECT_EQ(kernel_backward.dimension(2), input_depth); 1228 EXPECT_EQ(kernel_backward.dimension(3), output_depth); 1229 1230 eigen_assert(dimensions_match(kernel_backward.dimensions(), 1231 expected_kernel_backward.dimensions())); 1232 for (ptrdiff_t i = 0; i < kernel_backward.dimensions().TotalSize(); ++i) { 1233 EigenApprox(kernel_backward.data()[i], expected_kernel_backward.data()[i]); 1234 } 1235 } 1236 1237 TEST(EigenBackwardSpatialConvolutionsTest, 1238 test_simple_cuboid_convolution_backward_kernel_valid) { 1239 const int input_depth = 2; 1240 const int input_planes = 5; 1241 const int input_rows = 3; 1242 const int input_cols = 4; 1243 const int output_depth = 5; 1244 const int patch_rows = 2; 1245 const int patch_cols = 2; 1246 const int patch_planes = 3; 1247 const int output_rows = input_rows - patch_rows + 1; 1248 const int output_cols = input_cols - patch_cols + 1; 1249 const int output_planes = input_planes - patch_planes + 1; 1250 1251 Tensor<float, 4> input(input_depth, input_planes, input_rows, input_cols); 1252 Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows, 1253 patch_cols); 1254 Tensor<float, 4> output_backward(output_depth, output_planes, output_rows, 1255 output_cols); 1256 1257 output_backward = output_backward.constant(11.0f) + output_backward.random(); 1258 input = input.constant(2.0f) + input.random(); 1259 kernel.setRandom(); 1260 1261 kernel = CuboidConvolutionBackwardKernel(input, output_backward, patch_planes, 1262 patch_rows, patch_cols, 1, 1, 1); 1263 1264 EXPECT_EQ(kernel.dimension(0), output_depth); 1265 EXPECT_EQ(kernel.dimension(1), input_depth); 1266 EXPECT_EQ(kernel.dimension(2), patch_planes); 1267 EXPECT_EQ(kernel.dimension(3), patch_rows); 1268 EXPECT_EQ(kernel.dimension(4), patch_cols); 1269 1270 for (int od = 0; od < output_depth; ++od) { 1271 for (int id = 0; id < input_depth; ++id) { 1272 for (int p = 0; p < patch_planes; ++p) { 1273 for (int r = 0; r < patch_rows; ++r) { 1274 for (int c = 0; c < patch_cols; ++c) { 1275 float expected = 0.0f; 1276 for (int i = 0; i < input_planes; ++i) { 1277 for (int j = 0; j < input_rows; ++j) { 1278 for (int k = 0; k < input_cols; ++k) { 1279 int output_j = j - r; 1280 int output_k = k - c; 1281 int output_i = i - p; 1282 if (output_i >= 0 && output_i < output_planes && 1283 output_j >= 0 && output_j < output_rows && 1284 output_k >= 0 && output_k < output_cols) { 1285 expected += 1286 input(id, i, j, k) * 1287 output_backward(od, output_i, output_j, output_k); 1288 } 1289 } 1290 } 1291 } 1292 EigenApprox(kernel(od, id, p, r, c), expected); 1293 } 1294 } 1295 } 1296 } 1297 } 1298 } 1299 1300 TEST(EigenBackwardSpatialConvolutionsTest, 1301 test_simple_cuboid_convolution_backward_kernel_valid_row_major) { 1302 const int input_depth = 2; 1303 const int input_planes = 5; 1304 const int input_rows = 3; 1305 const int input_cols = 4; 1306 const int output_depth = 5; 1307 const int patch_rows = 2; 1308 const int patch_cols = 2; 1309 const int patch_planes = 3; 1310 const int output_rows = input_rows - patch_rows + 1; 1311 const int output_cols = input_cols - patch_cols + 1; 1312 const int output_planes = input_planes - patch_planes + 1; 1313 1314 Tensor<float, 4, RowMajor> input(input_cols, input_rows, input_planes, 1315 input_depth); 1316 Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes, 1317 input_depth, output_depth); 1318 Tensor<float, 4, RowMajor> output_backward(output_cols, output_rows, 1319 output_planes, output_depth); 1320 1321 output_backward = output_backward.constant(11.0f) + output_backward.random(); 1322 input = input.constant(2.0f) + input.random(); 1323 kernel.setRandom(); 1324 1325 kernel = CuboidConvolutionBackwardKernel(input, output_backward, patch_planes, 1326 patch_rows, patch_cols, 1, 1, 1); 1327 1328 EXPECT_EQ(kernel.dimension(4), output_depth); 1329 EXPECT_EQ(kernel.dimension(3), input_depth); 1330 EXPECT_EQ(kernel.dimension(2), patch_planes); 1331 EXPECT_EQ(kernel.dimension(1), patch_rows); 1332 EXPECT_EQ(kernel.dimension(0), patch_cols); 1333 1334 for (int od = 0; od < output_depth; ++od) { 1335 for (int id = 0; id < input_depth; ++id) { 1336 for (int p = 0; p < patch_planes; ++p) { 1337 for (int r = 0; r < patch_rows; ++r) { 1338 for (int c = 0; c < patch_cols; ++c) { 1339 float expected = 0.0f; 1340 for (int i = 0; i < input_planes; ++i) { 1341 for (int j = 0; j < input_rows; ++j) { 1342 for (int k = 0; k < input_cols; ++k) { 1343 int output_j = j - r; 1344 int output_k = k - c; 1345 int output_i = i - p; 1346 if (output_i >= 0 && output_i < output_planes && 1347 output_j >= 0 && output_j < output_rows && 1348 output_k >= 0 && output_k < output_cols) { 1349 expected += 1350 input(k, j, i, id) * 1351 output_backward(output_k, output_j, output_i, od); 1352 } 1353 } 1354 } 1355 } 1356 EigenApprox(kernel(c, r, p, id, od), expected); 1357 } 1358 } 1359 } 1360 } 1361 } 1362 } 1363 1364 TEST(EigenBackwardSpatialConvolutionsTest, 1365 test_batched_spatial_convolution_backward_kernel_valid) { 1366 const int num_batches = 13; 1367 const int input_depth = 2; 1368 const int input_rows = 7; 1369 const int input_cols = 9; 1370 const int output_depth = 3; 1371 const int patch_rows = 5; 1372 const int patch_cols = 5; 1373 const int output_rows = input_rows - patch_rows + 1; 1374 const int output_cols = input_cols - patch_cols + 1; 1375 1376 Tensor<float, 4> input(input_depth, input_rows, input_cols, num_batches); 1377 Tensor<float, 4> kernel_backward(output_depth, input_depth, patch_rows, 1378 patch_cols); 1379 Tensor<float, 4> output_backward(output_depth, output_rows, output_cols, 1380 num_batches); 1381 1382 output_backward = output_backward.constant(11.0f) + output_backward.random(); 1383 input = input.constant(2.0f) + input.random(); 1384 kernel_backward.setRandom(); 1385 1386 kernel_backward = SpatialConvolutionBackwardKernel( 1387 input, output_backward, patch_rows, patch_cols, 1, 1); 1388 1389 EXPECT_EQ(kernel_backward.dimension(0), output_depth); 1390 EXPECT_EQ(kernel_backward.dimension(1), input_depth); 1391 EXPECT_EQ(kernel_backward.dimension(2), patch_rows); 1392 EXPECT_EQ(kernel_backward.dimension(3), patch_cols); 1393 1394 for (int od = 0; od < output_depth; ++od) { 1395 for (int id = 0; id < input_depth; ++id) { 1396 for (int c = 0; c < patch_cols; ++c) { 1397 for (int r = 0; r < patch_rows; ++r) { 1398 float expected = 0.0f; 1399 for (int b = 0; b < num_batches; ++b) { 1400 for (int i = 0; i < input_rows; ++i) { 1401 for (int j = 0; j < input_cols; ++j) { 1402 int output_i = i - r; 1403 int output_j = j - c; 1404 if (output_i >= 0 && output_i < output_rows && output_j >= 0 && 1405 output_j < output_cols) { 1406 expected += input(id, i, j, b) * 1407 output_backward(od, output_i, output_j, b); 1408 } 1409 } 1410 } 1411 } 1412 EigenApprox(kernel_backward(od, id, r, c), expected); 1413 } 1414 } 1415 } 1416 } 1417 } 1418 1419 TEST(EigenBackwardSpatialConvolutionsTest, 1420 test_batched_spatial_convolution_backward_kernel_valid_row_major) { 1421 const int num_batches = 13; 1422 const int input_depth = 2; 1423 const int input_rows = 7; 1424 const int input_cols = 9; 1425 const int output_depth = 3; 1426 const int patch_rows = 4; 1427 const int patch_cols = 4; 1428 const int output_rows = input_rows - patch_rows + 1; 1429 const int output_cols = input_cols - patch_cols + 1; 1430 1431 Tensor<float, 4, RowMajor> input(num_batches, input_cols, input_rows, 1432 input_depth); 1433 Tensor<float, 4, RowMajor> kernel_backward(patch_cols, patch_rows, 1434 input_depth, output_depth); 1435 Tensor<float, 4, RowMajor> output_backward(num_batches, output_cols, 1436 output_rows, output_depth); 1437 1438 output_backward = output_backward.constant(11.0f) + output_backward.random(); 1439 input = input.constant(2.0f) + input.random(); 1440 kernel_backward.setRandom(); 1441 1442 kernel_backward = SpatialConvolutionBackwardKernel( 1443 input, output_backward, patch_rows, patch_cols, 1, 1); 1444 1445 EXPECT_EQ(kernel_backward.dimension(0), patch_cols); 1446 EXPECT_EQ(kernel_backward.dimension(1), patch_rows); 1447 EXPECT_EQ(kernel_backward.dimension(2), input_depth); 1448 EXPECT_EQ(kernel_backward.dimension(3), output_depth); 1449 1450 for (int od = 0; od < output_depth; ++od) { 1451 for (int id = 0; id < input_depth; ++id) { 1452 for (int c = 0; c < patch_cols; ++c) { 1453 for (int r = 0; r < patch_rows; ++r) { 1454 float expected = 0.0f; 1455 for (int b = 0; b < num_batches; ++b) { 1456 for (int i = 0; i < input_rows; ++i) { 1457 for (int j = 0; j < input_cols; ++j) { 1458 int output_i = i - r; 1459 int output_j = j - c; 1460 if (output_i >= 0 && output_i < output_rows && output_j >= 0 && 1461 output_j < output_cols) { 1462 expected += input(b, j, i, id) * 1463 output_backward(b, output_j, output_i, od); 1464 } 1465 } 1466 } 1467 } 1468 EigenApprox(kernel_backward(c, r, id, od), expected); 1469 } 1470 } 1471 } 1472 } 1473 } 1474 1475 TEST(EigenBackwardSpatialConvolutionsTest, 1476 test_batched_spatial_convolution_backward_kernel_valid_row_major_unequal) { 1477 const int num_batches = 13; 1478 const int input_depth = 2; 1479 const int input_rows = 7; 1480 const int input_cols = 9; 1481 const int output_depth = 3; 1482 const int patch_rows = 4; 1483 const int patch_cols = 4; 1484 const int r_stride = 2; 1485 const int c_stride = 1; 1486 const int output_rows = 1487 (input_rows - patch_rows + 1 + r_stride - 1) / r_stride; 1488 const int output_cols = 1489 (input_cols - patch_cols + 1 + c_stride - 1) / c_stride; 1490 1491 Tensor<float, 4, RowMajor> input(num_batches, input_cols, input_rows, 1492 input_depth); 1493 Tensor<float, 4, RowMajor> kernel_backward(patch_cols, patch_rows, 1494 input_depth, output_depth); 1495 Tensor<float, 4, RowMajor> output_backward(num_batches, output_cols, 1496 output_rows, output_depth); 1497 1498 output_backward = output_backward.constant(11.0f) + output_backward.random(); 1499 input = input.constant(2.0f) + input.random(); 1500 kernel_backward.setRandom(); 1501 1502 kernel_backward = SpatialConvolutionBackwardKernel( 1503 input, output_backward, patch_rows, patch_cols, r_stride, c_stride); 1504 1505 EXPECT_EQ(kernel_backward.dimension(0), patch_cols); 1506 EXPECT_EQ(kernel_backward.dimension(1), patch_rows); 1507 EXPECT_EQ(kernel_backward.dimension(2), input_depth); 1508 EXPECT_EQ(kernel_backward.dimension(3), output_depth); 1509 1510 for (int od = 0; od < output_depth; ++od) { 1511 for (int id = 0; id < input_depth; ++id) { 1512 for (int c = 0; c < patch_cols; ++c) { 1513 for (int r = 0; r < patch_rows; ++r) { 1514 float expected = 0.0f; 1515 for (int b = 0; b < num_batches; ++b) { 1516 for (int i = 0; i < input_rows; ++i) { 1517 for (int j = 0; j < input_cols; ++j) { 1518 int output_i = i - r; 1519 int output_j = j - c; 1520 if (output_i >= 0 && output_i / r_stride < output_rows && 1521 output_i % r_stride == 0 && output_j >= 0 && 1522 output_j / c_stride < output_cols && 1523 output_j % c_stride == 0) { 1524 expected += input(b, j, i, id) * 1525 output_backward(b, output_j / c_stride, 1526 output_i / r_stride, od); 1527 } 1528 } 1529 } 1530 } 1531 EigenApprox(kernel_backward(c, r, id, od), expected); 1532 } 1533 } 1534 } 1535 } 1536 } 1537 1538 TEST(EigenBackwardSpatialConvolutionsTest, 1539 test_batched_cuboid_convolution_backward_kernel_valid) { 1540 const int num_batches = 13; 1541 const int input_depth = 2; 1542 const int input_planes = 5; 1543 const int input_rows = 7; 1544 const int input_cols = 9; 1545 const int output_depth = 3; 1546 const int patch_rows = 5; 1547 const int patch_cols = 5; 1548 const int patch_planes = 3; 1549 const int output_rows = input_rows - patch_rows + 1; 1550 const int output_cols = input_cols - patch_cols + 1; 1551 const int output_planes = input_planes - patch_planes + 1; 1552 1553 Tensor<float, 5> input(input_depth, input_planes, input_rows, input_cols, 1554 num_batches); 1555 Tensor<float, 5> kernel_backward(output_depth, input_depth, patch_planes, 1556 patch_rows, patch_cols); 1557 Tensor<float, 5> output_backward(output_depth, output_planes, output_rows, 1558 output_cols, num_batches); 1559 1560 output_backward = output_backward.constant(11.0f) + output_backward.random(); 1561 input = input.constant(2.0f) + input.random(); 1562 kernel_backward.setRandom(); 1563 1564 kernel_backward = CuboidConvolutionBackwardKernel( 1565 input, output_backward, patch_planes, patch_rows, patch_cols, 1, 1, 1); 1566 1567 EXPECT_EQ(kernel_backward.dimension(0), output_depth); 1568 EXPECT_EQ(kernel_backward.dimension(1), input_depth); 1569 EXPECT_EQ(kernel_backward.dimension(2), patch_planes); 1570 EXPECT_EQ(kernel_backward.dimension(3), patch_rows); 1571 EXPECT_EQ(kernel_backward.dimension(4), patch_cols); 1572 1573 for (int od = 0; od < output_depth; ++od) { 1574 for (int id = 0; id < input_depth; ++id) { 1575 for (int p = 0; p < patch_planes; ++p) { 1576 for (int c = 0; c < patch_cols; ++c) { 1577 for (int r = 0; r < patch_rows; ++r) { 1578 float expected = 0.0f; 1579 for (int b = 0; b < num_batches; ++b) { 1580 for (int i = 0; i < input_planes; ++i) { 1581 for (int j = 0; j < input_rows; ++j) { 1582 for (int k = 0; k < input_cols; ++k) { 1583 int output_j = j - r; 1584 int output_k = k - c; 1585 int output_i = i - p; 1586 if (output_i >= 0 && output_i < output_planes && 1587 output_j >= 0 && output_j < output_rows && 1588 output_k >= 0 && output_k < output_cols) { 1589 expected += 1590 input(id, i, j, k, b) * 1591 output_backward(od, output_i, output_j, output_k, b); 1592 } 1593 } 1594 } 1595 } 1596 } 1597 EigenApprox(kernel_backward(od, id, p, r, c), expected); 1598 } 1599 } 1600 } 1601 } 1602 } 1603 } 1604 1605 TEST(EigenBackwardSpatialConvolutionsTest, 1606 test_batched_cuboid_convolution_backward_kernel_valid_row_major) { 1607 const int num_batches = 13; 1608 const int input_depth = 2; 1609 const int input_planes = 5; 1610 const int input_rows = 7; 1611 const int input_cols = 9; 1612 const int output_depth = 3; 1613 const int patch_rows = 5; 1614 const int patch_cols = 5; 1615 const int patch_planes = 3; 1616 const int output_rows = input_rows - patch_rows + 1; 1617 const int output_cols = input_cols - patch_cols + 1; 1618 const int output_planes = input_planes - patch_planes + 1; 1619 1620 Tensor<float, 5, RowMajor> input(num_batches, input_cols, input_rows, 1621 input_planes, input_depth); 1622 Tensor<float, 5, RowMajor> kernel_backward( 1623 patch_cols, patch_rows, patch_planes, input_depth, output_depth); 1624 Tensor<float, 5, RowMajor> output_backward( 1625 num_batches, output_cols, output_rows, output_planes, output_depth); 1626 1627 output_backward = output_backward.constant(11.0f) + output_backward.random(); 1628 input = input.constant(2.0f) + input.random(); 1629 kernel_backward.setRandom(); 1630 1631 kernel_backward = CuboidConvolutionBackwardKernel( 1632 input, output_backward, patch_planes, patch_rows, patch_cols, 1, 1, 1); 1633 1634 EXPECT_EQ(kernel_backward.dimension(4), output_depth); 1635 EXPECT_EQ(kernel_backward.dimension(3), input_depth); 1636 EXPECT_EQ(kernel_backward.dimension(2), patch_planes); 1637 EXPECT_EQ(kernel_backward.dimension(1), patch_rows); 1638 EXPECT_EQ(kernel_backward.dimension(0), patch_cols); 1639 1640 for (int od = 0; od < output_depth; ++od) { 1641 for (int id = 0; id < input_depth; ++id) { 1642 for (int p = 0; p < patch_planes; ++p) { 1643 for (int c = 0; c < patch_cols; ++c) { 1644 for (int r = 0; r < patch_rows; ++r) { 1645 float expected = 0.0f; 1646 for (int b = 0; b < num_batches; ++b) { 1647 for (int i = 0; i < input_planes; ++i) { 1648 for (int j = 0; j < input_rows; ++j) { 1649 for (int k = 0; k < input_cols; ++k) { 1650 int output_j = j - r; 1651 int output_k = k - c; 1652 int output_i = i - p; 1653 if (output_i >= 0 && output_i < output_planes && 1654 output_j >= 0 && output_j < output_rows && 1655 output_k >= 0 && output_k < output_cols) { 1656 expected += 1657 input(b, k, j, i, id) * 1658 output_backward(b, output_k, output_j, output_i, od); 1659 } 1660 } 1661 } 1662 } 1663 } 1664 EigenApprox(kernel_backward(c, r, p, id, od), expected); 1665 } 1666 } 1667 } 1668 } 1669 } 1670 } 1671 1672 TEST(EigenBackwardSpatialConvolutionsTest, 1673 test_batched_strided_spatial_convolution_backward_kernel_valid) { 1674 const int num_batches = 13; 1675 const int input_depth = 2; 1676 const int input_rows = 7; 1677 const int input_cols = 9; 1678 const int output_depth = 3; 1679 const int patch_rows = 5; 1680 const int patch_cols = 5; 1681 1682 const int stride = 2; 1683 1684 const int output_rows = (input_rows - patch_rows + 1 + stride - 1) / stride; 1685 const int output_cols = (input_cols - patch_cols + 1 + stride - 1) / stride; 1686 1687 Tensor<float, 4> input(input_depth, input_rows, input_cols, num_batches); 1688 Tensor<float, 4> kernel_backward(output_depth, input_depth, patch_rows, 1689 patch_cols); 1690 Tensor<float, 4> output_backward(output_depth, output_rows, output_cols, 1691 num_batches); 1692 1693 output_backward = output_backward.constant(11.0f) + output_backward.random(); 1694 input = input.constant(2.0f) + input.random(); 1695 kernel_backward.setRandom(); 1696 1697 kernel_backward = SpatialConvolutionBackwardKernel( 1698 input, output_backward, patch_rows, patch_cols, stride, stride); 1699 1700 EXPECT_EQ(kernel_backward.dimension(0), output_depth); 1701 EXPECT_EQ(kernel_backward.dimension(1), input_depth); 1702 EXPECT_EQ(kernel_backward.dimension(2), patch_rows); 1703 EXPECT_EQ(kernel_backward.dimension(3), patch_cols); 1704 1705 for (int od = 0; od < output_depth; ++od) { 1706 for (int id = 0; id < input_depth; ++id) { 1707 for (int c = 0; c < patch_cols; ++c) { 1708 for (int r = 0; r < patch_rows; ++r) { 1709 float expected = 0.0f; 1710 for (int b = 0; b < num_batches; ++b) { 1711 for (int i = 0; i < input_rows; ++i) { 1712 for (int j = 0; j < input_cols; ++j) { 1713 int output_i = i - r; 1714 int output_j = j - c; 1715 if (output_i >= 0 && output_i / stride < output_rows && 1716 output_j >= 0 && output_j / stride < output_cols && 1717 output_i % stride == 0 && output_j % stride == 0) { 1718 expected += input(id, i, j, b) * 1719 output_backward(od, output_i / stride, 1720 output_j / stride, b); 1721 } 1722 } 1723 } 1724 } 1725 EigenApprox(kernel_backward(od, id, r, c), expected); 1726 } 1727 } 1728 } 1729 } 1730 } 1731 1732 TEST(EigenBackwardSpatialConvolutionsTest, 1733 test_batched_strided_spatial_convolution_backward_kernel_valid_row_major) { 1734 const int num_batches = 13; 1735 const int input_depth = 2; 1736 const int input_rows = 7; 1737 const int input_cols = 9; 1738 const int output_depth = 3; 1739 const int patch_rows = 4; 1740 const int patch_cols = 4; 1741 1742 const int stride = 2; 1743 1744 const int output_rows = (input_rows - patch_rows + 1 + stride - 1) / stride; 1745 const int output_cols = (input_cols - patch_cols + 1 + stride - 1) / stride; 1746 1747 Tensor<float, 4, RowMajor> input(num_batches, input_cols, input_rows, 1748 input_depth); 1749 Tensor<float, 4, RowMajor> kernel_backward(patch_cols, patch_rows, 1750 input_depth, output_depth); 1751 Tensor<float, 4, RowMajor> output_backward(num_batches, output_cols, 1752 output_rows, output_depth); 1753 1754 output_backward = output_backward.constant(11.0f) + output_backward.random(); 1755 input = input.constant(2.0f) + input.random(); 1756 kernel_backward.setRandom(); 1757 1758 kernel_backward = SpatialConvolutionBackwardKernel( 1759 input, output_backward, patch_rows, patch_cols, stride, stride); 1760 1761 EXPECT_EQ(kernel_backward.dimension(0), patch_cols); 1762 EXPECT_EQ(kernel_backward.dimension(1), patch_rows); 1763 EXPECT_EQ(kernel_backward.dimension(2), input_depth); 1764 EXPECT_EQ(kernel_backward.dimension(3), output_depth); 1765 1766 for (int od = 0; od < output_depth; ++od) { 1767 for (int id = 0; id < input_depth; ++id) { 1768 for (int c = 0; c < patch_cols; ++c) { 1769 for (int r = 0; r < patch_rows; ++r) { 1770 float expected = 0.0f; 1771 for (int b = 0; b < num_batches; ++b) { 1772 for (int i = 0; i < input_rows; ++i) { 1773 for (int j = 0; j < input_cols; ++j) { 1774 int output_i = i - r; 1775 int output_j = j - c; 1776 if (output_i >= 0 && output_i / stride < output_rows && 1777 output_j >= 0 && output_j / stride < output_cols && 1778 output_i % stride == 0 && output_j % stride == 0) { 1779 expected += input(b, j, i, id) * 1780 output_backward(b, output_j / stride, 1781 output_i / stride, od); 1782 } 1783 } 1784 } 1785 } 1786 EigenApprox(kernel_backward(c, r, id, od), expected); 1787 } 1788 } 1789 } 1790 } 1791 } 1792 1793 TEST(EigenBackwardSpatialConvolutionsTest, 1794 test_batched_strided_cuboid_convolution_backward_kernel_valid) { 1795 const int num_batches = 13; 1796 const int input_depth = 2; 1797 const int input_planes = 8; 1798 const int input_rows = 7; 1799 const int input_cols = 9; 1800 const int output_depth = 3; 1801 const int patch_planes = 3; 1802 const int patch_rows = 3; 1803 const int patch_cols = 2; 1804 1805 const int stride_planes = 2; 1806 const int stride_cols = 3; 1807 const int stride_rows = 1; 1808 1809 const int output_rows = ceil_div(input_rows - patch_rows + 1, stride_rows); 1810 const int output_cols = ceil_div(input_cols - patch_cols + 1, stride_cols); 1811 const int output_planes = 1812 ceil_div(input_planes - patch_planes + 1, stride_planes); 1813 1814 Tensor<float, 5> input(input_depth, input_planes, input_rows, input_cols, 1815 num_batches); 1816 Tensor<float, 5> kernel_backward(output_depth, input_depth, patch_planes, 1817 patch_rows, patch_cols); 1818 Tensor<float, 5> output_backward(output_depth, output_planes, output_rows, 1819 output_cols, num_batches); 1820 1821 output_backward = output_backward.constant(11.0f) + output_backward.random(); 1822 input = input.constant(2.0f) + input.random(); 1823 kernel_backward.setRandom(); 1824 1825 kernel_backward = CuboidConvolutionBackwardKernel( 1826 input, output_backward, patch_planes, patch_rows, patch_cols, 1827 stride_planes, stride_rows, stride_cols); 1828 1829 EXPECT_EQ(kernel_backward.dimension(0), output_depth); 1830 EXPECT_EQ(kernel_backward.dimension(1), input_depth); 1831 EXPECT_EQ(kernel_backward.dimension(2), patch_planes); 1832 EXPECT_EQ(kernel_backward.dimension(3), patch_rows); 1833 EXPECT_EQ(kernel_backward.dimension(4), patch_cols); 1834 1835 for (int od = 0; od < output_depth; ++od) { 1836 for (int id = 0; id < input_depth; ++id) { 1837 for (int p = 0; p < patch_planes; ++p) { 1838 for (int c = 0; c < patch_cols; ++c) { 1839 for (int r = 0; r < patch_rows; ++r) { 1840 float expected = 0.0f; 1841 for (int b = 0; b < num_batches; ++b) { 1842 for (int i = 0; i < input_planes; ++i) { 1843 for (int j = 0; j < input_rows; ++j) { 1844 for (int k = 0; k < input_cols; ++k) { 1845 int output_j = j - r; 1846 int output_k = k - c; 1847 int output_i = i - p; 1848 if (output_i >= 0 && 1849 output_i / stride_planes < output_planes && 1850 output_j >= 0 && output_j / stride_rows < output_rows && 1851 output_k >= 0 && output_k / stride_cols < output_cols && 1852 output_i % stride_planes == 0 && 1853 output_j % stride_rows == 0 && 1854 output_k % stride_cols == 0) { 1855 expected += input(id, i, j, k, b) * 1856 output_backward(od, output_i / stride_planes, 1857 output_j / stride_rows, 1858 output_k / stride_cols, b); 1859 } 1860 } 1861 } 1862 } 1863 } 1864 EigenApprox(kernel_backward(od, id, p, r, c), expected); 1865 } 1866 } 1867 } 1868 } 1869 } 1870 } 1871 1872 TEST(EigenBackwardSpatialConvolutionsTest, 1873 test_batched_strided_cuboid_convolution_backward_kernel_valid_row_major) { 1874 const int num_batches = 13; 1875 const int input_depth = 2; 1876 const int input_planes = 8; 1877 const int input_rows = 7; 1878 const int input_cols = 9; 1879 const int output_depth = 3; 1880 const int patch_planes = 3; 1881 const int patch_rows = 3; 1882 const int patch_cols = 2; 1883 1884 const int stride_planes = 2; 1885 const int stride_cols = 3; 1886 const int stride_rows = 1; 1887 1888 const int output_rows = ceil_div(input_rows - patch_rows + 1, stride_rows); 1889 const int output_cols = ceil_div(input_cols - patch_cols + 1, stride_cols); 1890 const int output_planes = 1891 ceil_div(input_planes - patch_planes + 1, stride_planes); 1892 1893 Tensor<float, 5, RowMajor> input(num_batches, input_cols, input_rows, 1894 input_planes, input_depth); 1895 Tensor<float, 5, RowMajor> kernel_backward( 1896 patch_cols, patch_rows, patch_planes, input_depth, output_depth); 1897 Tensor<float, 5, RowMajor> output_backward( 1898 num_batches, output_cols, output_rows, output_planes, output_depth); 1899 1900 output_backward = output_backward.constant(11.0f) + output_backward.random(); 1901 input = input.constant(2.0f) + input.random(); 1902 kernel_backward.setRandom(); 1903 1904 kernel_backward = CuboidConvolutionBackwardKernel( 1905 input, output_backward, patch_planes, patch_rows, patch_cols, 1906 stride_planes, stride_rows, stride_cols); 1907 1908 EXPECT_EQ(kernel_backward.dimension(4), output_depth); 1909 EXPECT_EQ(kernel_backward.dimension(3), input_depth); 1910 EXPECT_EQ(kernel_backward.dimension(2), patch_planes); 1911 EXPECT_EQ(kernel_backward.dimension(1), patch_rows); 1912 EXPECT_EQ(kernel_backward.dimension(0), patch_cols); 1913 1914 for (int od = 0; od < output_depth; ++od) { 1915 for (int id = 0; id < input_depth; ++id) { 1916 for (int p = 0; p < patch_planes; ++p) { 1917 for (int c = 0; c < patch_cols; ++c) { 1918 for (int r = 0; r < patch_rows; ++r) { 1919 float expected = 0.0f; 1920 for (int b = 0; b < num_batches; ++b) { 1921 for (int i = 0; i < input_planes; ++i) { 1922 for (int j = 0; j < input_rows; ++j) { 1923 for (int k = 0; k < input_cols; ++k) { 1924 int output_j = j - r; 1925 int output_k = k - c; 1926 int output_i = i - p; 1927 if (output_i >= 0 && 1928 output_i / stride_planes < output_planes && 1929 output_j >= 0 && output_j / stride_rows < output_rows && 1930 output_k >= 0 && output_k / stride_cols < output_cols && 1931 output_i % stride_planes == 0 && 1932 output_j % stride_rows == 0 && 1933 output_k % stride_cols == 0) { 1934 expected += input(b, k, j, i, id) * 1935 output_backward(b, output_k / stride_cols, 1936 output_j / stride_rows, 1937 output_i / stride_planes, od); 1938 } 1939 } 1940 } 1941 } 1942 } 1943 EigenApprox(kernel_backward(c, r, p, id, od), expected); 1944 } 1945 } 1946 } 1947 } 1948 } 1949 } 1950 1951 TEST(EigenBackwardSpatialConvolutionsTest, 1952 test_batched_strided_cuboid_convolution_backward_input_valid) { 1953 const int num_batches = 13; 1954 const int input_depth = 2; 1955 const int input_planes = 14; 1956 const int input_rows = 13; 1957 const int input_cols = 15; 1958 const int patch_rows = 3; 1959 const int patch_cols = 2; 1960 const int patch_planes = 4; 1961 const int stride_rows = 3; 1962 const int stride_cols = 2; 1963 const int stride_planes = 3; 1964 const int output_rows = ceil_div(input_rows - patch_rows + 1, stride_rows); 1965 const int output_cols = ceil_div(input_cols - patch_cols + 1, stride_cols); 1966 const int output_planes = 1967 ceil_div(input_planes - patch_planes + 1, stride_planes); 1968 const int output_depth = 5; 1969 1970 Tensor<float, 5> input_backward(input_depth, input_planes, input_rows, 1971 input_cols, num_batches); 1972 Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows, 1973 patch_cols); 1974 Tensor<float, 5> output_backward(output_depth, output_planes, output_rows, 1975 output_cols, num_batches); 1976 1977 output_backward = output_backward.constant(11.0f) + output_backward.random(); 1978 kernel = kernel.constant(2.0f) + kernel.random(); 1979 input_backward.setRandom(); 1980 1981 input_backward = CuboidConvolutionBackwardInput( 1982 kernel, output_backward, input_planes, input_rows, input_cols, 1983 stride_planes, stride_rows, stride_cols); 1984 1985 EXPECT_EQ(input_backward.dimension(4), num_batches); 1986 EXPECT_EQ(input_backward.dimension(3), input_cols); 1987 EXPECT_EQ(input_backward.dimension(2), input_rows); 1988 EXPECT_EQ(input_backward.dimension(1), input_planes); 1989 EXPECT_EQ(input_backward.dimension(0), input_depth); 1990 1991 for (int b = 0; b < num_batches; ++b) { 1992 for (int id = 0; id < input_depth; ++id) { 1993 for (int i = 0; i < input_planes; ++i) { 1994 for (int j = 0; j < input_rows; ++j) { 1995 for (int k = 0; k < input_cols; ++k) { 1996 float expected = 0.0f; 1997 for (int c = 0; c < patch_cols; ++c) { 1998 for (int r = 0; r < patch_rows; ++r) { 1999 for (int p = 0; p < patch_planes; ++p) { 2000 for (int od = 0; od < output_depth; ++od) { 2001 int output_j = j - r; 2002 int output_k = k - c; 2003 int output_i = i - p; 2004 if (output_i >= 0 && 2005 output_i / stride_planes < output_planes && 2006 output_j >= 0 && output_j / stride_rows < output_rows && 2007 output_k >= 0 && output_k / stride_cols < output_cols && 2008 output_i % stride_planes == 0 && 2009 output_j % stride_rows == 0 && 2010 output_k % stride_cols == 0) { 2011 expected += output_backward(od, output_i / stride_planes, 2012 output_j / stride_rows, 2013 output_k / stride_cols, b) * 2014 kernel(od, id, p, r, c); 2015 } 2016 } 2017 } 2018 } 2019 } 2020 EigenApprox(input_backward(id, i, j, k, b), expected); 2021 } 2022 } 2023 } 2024 } 2025 } 2026 } 2027 2028 TEST(EigenBackwardSpatialConvolutionsTest, 2029 test_batched_strided_cuboid_convolution_backward_input_valid_row_major) { 2030 const int num_batches = 13; 2031 const int input_depth = 2; 2032 const int input_planes = 14; 2033 const int input_rows = 13; 2034 const int input_cols = 15; 2035 const int patch_rows = 3; 2036 const int patch_cols = 2; 2037 const int patch_planes = 4; 2038 const int stride_rows = 3; 2039 const int stride_cols = 2; 2040 const int stride_planes = 3; 2041 const int output_rows = ceil_div(input_rows - patch_rows + 1, stride_rows); 2042 const int output_cols = ceil_div(input_cols - patch_cols + 1, stride_cols); 2043 const int output_planes = 2044 ceil_div(input_planes - patch_planes + 1, stride_planes); 2045 const int output_depth = 5; 2046 2047 Tensor<float, 5, RowMajor> input_backward(num_batches, input_cols, input_rows, 2048 input_planes, input_depth); 2049 Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes, 2050 input_depth, output_depth); 2051 Tensor<float, 5, RowMajor> output_backward( 2052 num_batches, output_cols, output_rows, output_planes, output_depth); 2053 2054 output_backward = output_backward.constant(11.0f) + output_backward.random(); 2055 kernel = kernel.constant(2.0f) + kernel.random(); 2056 input_backward.setRandom(); 2057 2058 input_backward = CuboidConvolutionBackwardInput( 2059 kernel, output_backward, input_planes, input_rows, input_cols, 2060 stride_planes, stride_rows, stride_cols); 2061 2062 EXPECT_EQ(input_backward.dimension(0), num_batches); 2063 EXPECT_EQ(input_backward.dimension(1), input_cols); 2064 EXPECT_EQ(input_backward.dimension(2), input_rows); 2065 EXPECT_EQ(input_backward.dimension(3), input_planes); 2066 EXPECT_EQ(input_backward.dimension(4), input_depth); 2067 2068 for (int b = 0; b < num_batches; ++b) { 2069 for (int id = 0; id < input_depth; ++id) { 2070 for (int i = 0; i < input_planes; ++i) { 2071 for (int j = 0; j < input_rows; ++j) { 2072 for (int k = 0; k < input_cols; ++k) { 2073 float expected = 0.0f; 2074 for (int c = 0; c < patch_cols; ++c) { 2075 for (int r = 0; r < patch_rows; ++r) { 2076 for (int p = 0; p < patch_planes; ++p) { 2077 for (int od = 0; od < output_depth; ++od) { 2078 int output_j = j - r; 2079 int output_k = k - c; 2080 int output_i = i - p; 2081 if (output_i >= 0 && 2082 output_i / stride_planes < output_planes && 2083 output_j >= 0 && output_j / stride_rows < output_rows && 2084 output_k >= 0 && output_k / stride_cols < output_cols && 2085 output_i % stride_planes == 0 && 2086 output_j % stride_rows == 0 && 2087 output_k % stride_cols == 0) { 2088 expected += 2089 output_backward(b, output_k / stride_cols, 2090 output_j / stride_rows, 2091 output_i / stride_planes, od) * 2092 kernel(c, r, p, id, od); 2093 } 2094 } 2095 } 2096 } 2097 } 2098 EigenApprox(input_backward(b, k, j, i, id), expected); 2099 } 2100 } 2101 } 2102 } 2103 } 2104 } 2105 2106 } // namespace Eigen 2107