diff options
author | 2016-03-17 17:46:03 -0800 | |
---|---|---|
committer | 2016-03-18 08:47:32 -0700 | |
commit | 50edc4e878c076c44fecc847af110a19b171eb63 (patch) | |
tree | 8ea27610ddefee566c2a3f750c2e034085af5b83 /tensorflow/core/kernels/eigen_backward_spatial_convolutions_test.cc | |
parent | 1a39c2c1979706084338352d1264951b3ec9c6bc (diff) |
TensorFlow: move eigen some NN code from our third_party/eigen3 copy
to being part of TF, add tests.
Change: 117509710
Diffstat (limited to 'tensorflow/core/kernels/eigen_backward_spatial_convolutions_test.cc')
-rw-r--r-- | tensorflow/core/kernels/eigen_backward_spatial_convolutions_test.cc | 1959 |
1 files changed, 1959 insertions, 0 deletions
diff --git a/tensorflow/core/kernels/eigen_backward_spatial_convolutions_test.cc b/tensorflow/core/kernels/eigen_backward_spatial_convolutions_test.cc new file mode 100644 index 0000000000..9e77a71cb5 --- /dev/null +++ b/tensorflow/core/kernels/eigen_backward_spatial_convolutions_test.cc @@ -0,0 +1,1959 @@ +/* Copyright 2015 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/core/kernels/eigen_backward_spatial_convolutions.h" +#include "tensorflow/core/framework/types.h" +#include "tensorflow/core/kernels/eigen_backward_cuboid_convolutions.h" +#include "tensorflow/core/platform/test.h" + +namespace Eigen { + +namespace { +void EigenApprox(float a, float b) { + ASSERT_TRUE(std::abs(a - b) <= std::min(std::abs(a), std::abs(b)) * 1e-3); +} +static int ceil_div(int a, int b) { return (a + b - 1) / b; } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_simple_spatial_convolution_backward_input_valid) { + const int input_depth = 2; + const int input_rows = 3; + const int input_cols = 4; + const int output_depth = 5; + const int patch_rows = 2; + const int patch_cols = 2; + const int output_rows = input_rows - patch_rows + 1; + const int output_cols = input_cols - patch_cols + 1; + + Tensor<float, 3> input_backward(input_depth, input_rows, input_cols); + Tensor<float, 4> kernel(output_depth, input_depth, patch_rows, patch_cols); + Tensor<float, 3> output_backward(output_depth, output_rows, output_cols); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + kernel = kernel.constant(2.0f) + kernel.random(); + input_backward.setRandom(); + + input_backward = SpatialConvolutionBackwardInput(kernel, output_backward, + input_rows, input_cols, 1); + + EXPECT_EQ(input_backward.dimension(0), input_depth); + EXPECT_EQ(input_backward.dimension(1), input_rows); + EXPECT_EQ(input_backward.dimension(2), input_cols); + + for (int id = 0; id < input_depth; ++id) { + for (int i = 0; i < input_rows; ++i) { + for (int j = 0; j < input_cols; ++j) { + float expected = 0.0f; + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + for (int od = 0; od < output_depth; ++od) { + int output_i = i - r; + int output_j = j - c; + if (output_i >= 0 && output_i < output_rows && output_j >= 0 && + output_j < output_cols) { + expected += output_backward(od, output_i, output_j) * + kernel(od, id, r, c); + } + } + } + } + EigenApprox(input_backward(id, i, j), expected); + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_simple_spatial_convolution_backward_input_valid_row_major) { + const int input_depth = 2; + const int input_rows = 3; + const int input_cols = 4; + const int output_depth = 5; + const int patch_rows = 2; + const int patch_cols = 2; + const int output_rows = input_rows - patch_rows + 1; + const int output_cols = input_cols - patch_cols + 1; + + Tensor<float, 3, RowMajor> input_backward(input_cols, input_rows, + input_depth); + Tensor<float, 4, RowMajor> kernel(patch_cols, patch_rows, input_depth, + output_depth); + Tensor<float, 3, RowMajor> output_backward(output_cols, output_rows, + output_depth); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + kernel = kernel.constant(2.0f) + kernel.random(); + input_backward.setRandom(); + + input_backward = SpatialConvolutionBackwardInput(kernel, output_backward, + input_rows, input_cols, 1); + + EXPECT_EQ(input_backward.dimension(0), input_cols); + EXPECT_EQ(input_backward.dimension(1), input_rows); + EXPECT_EQ(input_backward.dimension(2), input_depth); + + for (int id = 0; id < input_depth; ++id) { + for (int i = 0; i < input_rows; ++i) { + for (int j = 0; j < input_cols; ++j) { + float expected = 0.0f; + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + for (int od = 0; od < output_depth; ++od) { + int output_i = i - r; + int output_j = j - c; + if (output_i >= 0 && output_i < output_rows && output_j >= 0 && + output_j < output_cols) { + expected += output_backward(output_j, output_i, od) * + kernel(c, r, id, od); + } + } + } + } + EigenApprox(input_backward(j, i, id), expected); + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_simple_cuboid_convolution_backward_input_valid) { + const int input_depth = 2; + const int input_planes = 5; + const int input_rows = 3; + const int input_cols = 4; + const int patch_rows = 2; + const int patch_cols = 2; + const int patch_planes = 2; + const int output_rows = input_rows - patch_rows + 1; + const int output_cols = input_cols - patch_cols + 1; + const int output_planes = input_planes - patch_planes + 1; + const int output_depth = 5; + + Tensor<float, 4> input_backward(input_depth, input_planes, input_rows, + input_cols); + Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows, + patch_cols); + Tensor<float, 4> output_backward(output_depth, output_planes, output_rows, + output_cols); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + kernel = kernel.constant(2.0f) + kernel.random(); + input_backward.setRandom(); + + input_backward = CuboidConvolutionBackwardInput( + kernel, output_backward, input_planes, input_rows, input_cols); + + EXPECT_EQ(input_backward.dimension(3), input_cols); + EXPECT_EQ(input_backward.dimension(2), input_rows); + EXPECT_EQ(input_backward.dimension(1), input_planes); + EXPECT_EQ(input_backward.dimension(0), input_depth); + + for (int id = 0; id < input_depth; ++id) { + for (int i = 0; i < input_planes; ++i) { + for (int j = 0; j < input_rows; ++j) { + for (int k = 0; k < input_cols; ++k) { + float expected = 0.0f; + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + for (int p = 0; p < patch_planes; ++p) { + for (int od = 0; od < output_depth; ++od) { + int output_j = j - r; + int output_k = k - c; + int output_i = i - p; + if (output_i >= 0 && output_i < output_planes && + output_j >= 0 && output_j < output_rows && + output_k >= 0 && output_k < output_cols) { + expected += + output_backward(od, output_i, output_j, output_k) * + kernel(od, id, p, r, c); + } + } + } + } + } + EigenApprox(input_backward(id, i, j, k), expected); + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_simple_cuboid_convolution_backward_input_valid_row_major) { + const int input_depth = 2; + const int input_planes = 5; + const int input_rows = 3; + const int input_cols = 4; + const int patch_rows = 2; + const int patch_cols = 2; + const int patch_planes = 2; + const int output_rows = input_rows - patch_rows + 1; + const int output_cols = input_cols - patch_cols + 1; + const int output_planes = input_planes - patch_planes + 1; + const int output_depth = 5; + + Tensor<float, 4, RowMajor> input_backward(input_cols, input_rows, + input_planes, input_depth); + Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes, + input_depth, output_depth); + Tensor<float, 4, RowMajor> output_backward(output_cols, output_rows, + output_planes, output_depth); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + kernel = kernel.constant(2.0f) + kernel.random(); + input_backward.setRandom(); + + input_backward = CuboidConvolutionBackwardInput( + kernel, output_backward, input_planes, input_rows, input_cols); + + EXPECT_EQ(input_backward.dimension(0), input_cols); + EXPECT_EQ(input_backward.dimension(1), input_rows); + EXPECT_EQ(input_backward.dimension(2), input_planes); + EXPECT_EQ(input_backward.dimension(3), input_depth); + + for (int id = 0; id < input_depth; ++id) { + for (int i = 0; i < input_planes; ++i) { + for (int j = 0; j < input_rows; ++j) { + for (int k = 0; k < input_cols; ++k) { + float expected = 0.0f; + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + for (int p = 0; p < patch_planes; ++p) { + for (int od = 0; od < output_depth; ++od) { + int output_j = j - r; + int output_k = k - c; + int output_i = i - p; + if (output_i >= 0 && output_i < output_planes && + output_j >= 0 && output_j < output_rows && + output_k >= 0 && output_k < output_cols) { + expected += + output_backward(output_k, output_j, output_i, od) * + kernel(c, r, p, id, od); + } + } + } + } + } + EigenApprox(input_backward(k, j, i, id), expected); + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_simple_spatial_convolution_backward_input_same) { + const int input_depth = 2; + const int input_rows = 7; + const int input_cols = 9; + const int output_depth = 3; + const int patch_rows = 4; + const int patch_cols = 4; + const int output_rows = input_rows; + const int output_cols = input_cols; + + Tensor<float, 3> input_backward(input_depth, input_rows, input_cols); + Tensor<float, 4> kernel(output_depth, input_depth, patch_rows, patch_cols); + Tensor<float, 3> output_backward(output_depth, output_rows, output_cols); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + kernel = kernel.constant(2.0f) + kernel.random(); + + input_backward = SpatialConvolutionBackwardInput(kernel, output_backward, + input_rows, input_cols, 1); + + EXPECT_EQ(input_backward.dimension(0), input_depth); + EXPECT_EQ(input_backward.dimension(1), input_rows); + EXPECT_EQ(input_backward.dimension(2), input_cols); + + for (int id = 0; id < input_depth; ++id) { + for (int i = 0; i < input_rows; ++i) { + for (int j = 0; j < input_cols; ++j) { + float expected = 0.0f; + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + for (int od = 0; od < output_depth; ++od) { + int output_i = i - r + (patch_rows - 1) / 2; + int output_j = j - c + (patch_cols - 1) / 2; + if (output_i >= 0 && output_i < output_rows && output_j >= 0 && + output_j < output_cols) { + expected += output_backward(od, output_i, output_j) * + kernel(od, id, r, c); + } + } + } + } + EigenApprox(input_backward(id, i, j), expected); + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_simple_spatial_convolution_backward_input_same_row_major) { + const int input_depth = 2; + const int input_rows = 7; + const int input_cols = 9; + const int output_depth = 3; + const int patch_rows = 4; + const int patch_cols = 4; + const int output_rows = input_rows; + const int output_cols = input_cols; + + Tensor<float, 3, RowMajor> input_backward(input_cols, input_rows, + input_depth); + Tensor<float, 4, RowMajor> kernel(patch_cols, patch_rows, input_depth, + output_depth); + Tensor<float, 3, RowMajor> output_backward(output_cols, output_rows, + output_depth); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + kernel = kernel.constant(2.0f) + kernel.random(); + + input_backward = SpatialConvolutionBackwardInput(kernel, output_backward, + input_rows, input_cols, 1); + + EXPECT_EQ(input_backward.dimension(0), input_cols); + EXPECT_EQ(input_backward.dimension(1), input_rows); + EXPECT_EQ(input_backward.dimension(2), input_depth); + + for (int id = 0; id < input_depth; ++id) { + for (int i = 0; i < input_rows; ++i) { + for (int j = 0; j < input_cols; ++j) { + float expected = 0.0f; + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + for (int od = 0; od < output_depth; ++od) { + int output_i = i - r + (patch_rows - 1) / 2; + int output_j = j - c + (patch_cols - 1) / 2; + if (output_i >= 0 && output_i < output_rows && output_j >= 0 && + output_j < output_cols) { + expected += output_backward(output_j, output_i, od) * + kernel(c, r, id, od); + } + } + } + } + EigenApprox(input_backward(j, i, id), expected); + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_simple_cuboid_convolution_backward_input_same) { + const int input_depth = 2; + const int input_planes = 5; + const int input_rows = 3; + const int input_cols = 4; + const int patch_rows = 3; + const int patch_cols = 2; + const int patch_planes = 4; + const int output_rows = input_rows; + const int output_cols = input_cols; + const int output_planes = input_planes; + const int output_depth = 5; + + Tensor<float, 4> input_backward(input_depth, input_planes, input_rows, + input_cols); + Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows, + patch_cols); + Tensor<float, 4> output_backward(output_depth, output_planes, output_rows, + output_cols); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + kernel = kernel.constant(2.0f) + kernel.random(); + input_backward.setRandom(); + + input_backward = CuboidConvolutionBackwardInput( + kernel, output_backward, input_planes, input_rows, input_cols); + + EXPECT_EQ(input_backward.dimension(3), input_cols); + EXPECT_EQ(input_backward.dimension(2), input_rows); + EXPECT_EQ(input_backward.dimension(1), input_planes); + EXPECT_EQ(input_backward.dimension(0), input_depth); + + const int dz = patch_planes - 1; + const int dy = patch_rows - 1; + const int dx = patch_cols - 1; + + const int forward_pad_x = dx - dx / 2; + const int forward_pad_y = dy - dy / 2; + const int forward_pad_z = dz - dz / 2; + + for (int id = 0; id < input_depth; ++id) { + for (int i = 0; i < input_planes; ++i) { + for (int j = 0; j < input_rows; ++j) { + for (int k = 0; k < input_cols; ++k) { + float expected = 0.0f; + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + for (int p = 0; p < patch_planes; ++p) { + for (int od = 0; od < output_depth; ++od) { + int output_i = i - p + forward_pad_z; + int output_j = j - r + forward_pad_y; + int output_k = k - c + forward_pad_x; + if (output_i >= 0 && output_i < output_planes && + output_j >= 0 && output_j < output_rows && + output_k >= 0 && output_k < output_cols) { + expected += + output_backward(od, output_i, output_j, output_k) * + kernel(od, id, p, r, c); + } + } + } + } + } + EigenApprox(input_backward(id, i, j, k), expected); + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_simple_cuboid_convolution_backward_input_same_row_major) { + const int input_depth = 2; + const int input_planes = 5; + const int input_rows = 3; + const int input_cols = 4; + const int patch_rows = 2; + const int patch_cols = 3; + const int patch_planes = 4; + const int output_rows = input_rows; + const int output_cols = input_cols; + const int output_planes = input_planes; + const int output_depth = 5; + + Tensor<float, 4, RowMajor> input_backward(input_cols, input_rows, + input_planes, input_depth); + Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes, + input_depth, output_depth); + Tensor<float, 4, RowMajor> output_backward(output_cols, output_rows, + output_planes, output_depth); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + kernel = kernel.constant(2.0f) + kernel.random(); + input_backward.setRandom(); + + input_backward = CuboidConvolutionBackwardInput( + kernel, output_backward, input_planes, input_rows, input_cols); + + EXPECT_EQ(input_backward.dimension(0), input_cols); + EXPECT_EQ(input_backward.dimension(1), input_rows); + EXPECT_EQ(input_backward.dimension(2), input_planes); + EXPECT_EQ(input_backward.dimension(3), input_depth); + + const int dz = patch_planes - 1; + const int dy = patch_rows - 1; + const int dx = patch_cols - 1; + + const int forward_pad_x = dx - dx / 2; + const int forward_pad_y = dy - dy / 2; + const int forward_pad_z = dz - dz / 2; + + for (int id = 0; id < input_depth; ++id) { + for (int i = 0; i < input_planes; ++i) { + for (int j = 0; j < input_rows; ++j) { + for (int k = 0; k < input_cols; ++k) { + float expected = 0.0f; + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + for (int p = 0; p < patch_planes; ++p) { + for (int od = 0; od < output_depth; ++od) { + int output_i = i - p + forward_pad_z; + int output_j = j - r + forward_pad_y; + int output_k = k - c + forward_pad_x; + if (output_i >= 0 && output_i < output_planes && + output_j >= 0 && output_j < output_rows && + output_k >= 0 && output_k < output_cols) { + expected += + output_backward(output_k, output_j, output_i, od) * + kernel(c, r, p, id, od); + } + } + } + } + } + EigenApprox(input_backward(k, j, i, id), expected); + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_spatial_convolution_backward_input_valid) { + const int num_batches = 13; + const int input_depth = 2; + const int input_rows = 7; + const int input_cols = 9; + const int output_depth = 3; + const int patch_rows = 5; + const int patch_cols = 5; + const int output_rows = input_rows - patch_rows + 1; + const int output_cols = input_cols - patch_cols + 1; + + Tensor<float, 4> input_backward(input_depth, input_rows, input_cols, + num_batches); + Tensor<float, 4> kernel(output_depth, input_depth, patch_rows, patch_cols); + Tensor<float, 4> output_backward(output_depth, output_rows, output_cols, + num_batches); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + kernel = kernel.constant(2.0f) + kernel.random(); + input_backward.setRandom(); + + input_backward = SpatialConvolutionBackwardInput(kernel, output_backward, + input_rows, input_cols, 1); + + EXPECT_EQ(input_backward.dimension(0), input_depth); + EXPECT_EQ(input_backward.dimension(1), input_rows); + EXPECT_EQ(input_backward.dimension(2), input_cols); + EXPECT_EQ(input_backward.dimension(3), num_batches); + + for (int b = 0; b < num_batches; ++b) { + for (int id = 0; id < input_depth; ++id) { + for (int i = 0; i < input_rows; ++i) { + for (int j = 0; j < input_cols; ++j) { + float expected = 0.0f; + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + for (int od = 0; od < output_depth; ++od) { + int output_i = i - r; + int output_j = j - c; + if (output_i >= 0 && output_i < output_rows && output_j >= 0 && + output_j < output_cols) { + expected += output_backward(od, output_i, output_j, b) * + kernel(od, id, r, c); + } + } + } + } + EigenApprox(input_backward(id, i, j, b), expected); + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_spatial_convolution_backward_input_valid_row_major) { + const int num_batches = 13; + const int input_depth = 2; + const int input_rows = 7; + const int input_cols = 9; + const int output_depth = 3; + const int patch_rows = 5; + const int patch_cols = 5; + const int output_rows = input_rows - patch_rows + 1; + const int output_cols = input_cols - patch_cols + 1; + + Tensor<float, 4, RowMajor> input_backward(num_batches, input_cols, input_rows, + input_depth); + Tensor<float, 4, RowMajor> kernel(patch_cols, patch_rows, input_depth, + output_depth); + Tensor<float, 4, RowMajor> output_backward(num_batches, output_cols, + output_rows, output_depth); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + kernel = kernel.constant(2.0f) + kernel.random(); + input_backward.setRandom(); + + input_backward = SpatialConvolutionBackwardInput(kernel, output_backward, + input_rows, input_cols, 1); + + EXPECT_EQ(input_backward.dimension(0), num_batches); + EXPECT_EQ(input_backward.dimension(1), input_cols); + EXPECT_EQ(input_backward.dimension(2), input_rows); + EXPECT_EQ(input_backward.dimension(3), input_depth); + + for (int b = 0; b < num_batches; ++b) { + for (int id = 0; id < input_depth; ++id) { + for (int i = 0; i < input_rows; ++i) { + for (int j = 0; j < input_cols; ++j) { + float expected = 0.0f; + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + for (int od = 0; od < output_depth; ++od) { + int output_i = i - r; + int output_j = j - c; + if (output_i >= 0 && output_i < output_rows && output_j >= 0 && + output_j < output_cols) { + expected += output_backward(b, output_j, output_i, od) * + kernel(c, r, id, od); + } + } + } + } + EigenApprox(input_backward(b, j, i, id), expected); + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_cuboid_convolution_backward_input_valid) { + const int num_batches = 13; + const int input_depth = 2; + const int input_planes = 5; + const int input_rows = 3; + const int input_cols = 4; + const int patch_rows = 2; + const int patch_cols = 2; + const int patch_planes = 2; + const int output_rows = input_rows - patch_rows + 1; + const int output_cols = input_cols - patch_cols + 1; + const int output_planes = input_planes - patch_planes + 1; + const int output_depth = 5; + + Tensor<float, 5> input_backward(input_depth, input_planes, input_rows, + input_cols, num_batches); + Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows, + patch_cols); + Tensor<float, 5> output_backward(output_depth, output_planes, output_rows, + output_cols, num_batches); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + kernel = kernel.constant(2.0f) + kernel.random(); + input_backward.setRandom(); + + input_backward = CuboidConvolutionBackwardInput( + kernel, output_backward, input_planes, input_rows, input_cols); + + EXPECT_EQ(input_backward.dimension(4), num_batches); + EXPECT_EQ(input_backward.dimension(3), input_cols); + EXPECT_EQ(input_backward.dimension(2), input_rows); + EXPECT_EQ(input_backward.dimension(1), input_planes); + EXPECT_EQ(input_backward.dimension(0), input_depth); + + for (int b = 0; b < num_batches; ++b) { + for (int id = 0; id < input_depth; ++id) { + for (int i = 0; i < input_planes; ++i) { + for (int j = 0; j < input_rows; ++j) { + for (int k = 0; k < input_cols; ++k) { + float expected = 0.0f; + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + for (int p = 0; p < patch_planes; ++p) { + for (int od = 0; od < output_depth; ++od) { + int output_i = i - p; + int output_j = j - r; + int output_k = k - c; + if (output_i >= 0 && output_i < output_planes && + output_j >= 0 && output_j < output_rows && + output_k >= 0 && output_k < output_cols) { + expected += + output_backward(od, output_i, output_j, output_k, b) * + kernel(od, id, p, r, c); + } + } + } + } + } + EigenApprox(input_backward(id, i, j, k, b), expected); + } + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_cuboid_convolution_backward_input_valid_row_major) { + const int num_batches = 13; + const int input_depth = 2; + const int input_planes = 5; + const int input_rows = 3; + const int input_cols = 4; + const int patch_rows = 2; + const int patch_cols = 2; + const int patch_planes = 2; + const int output_rows = input_rows - patch_rows + 1; + const int output_cols = input_cols - patch_cols + 1; + const int output_planes = input_planes - patch_planes + 1; + const int output_depth = 5; + + Tensor<float, 5, RowMajor> input_backward(num_batches, input_cols, input_rows, + input_planes, input_depth); + Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes, + input_depth, output_depth); + Tensor<float, 5, RowMajor> output_backward( + num_batches, output_cols, output_rows, output_planes, output_depth); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + kernel = kernel.constant(2.0f) + kernel.random(); + input_backward.setRandom(); + + input_backward = CuboidConvolutionBackwardInput( + kernel, output_backward, input_planes, input_rows, input_cols); + + EXPECT_EQ(input_backward.dimension(0), num_batches); + EXPECT_EQ(input_backward.dimension(1), input_cols); + EXPECT_EQ(input_backward.dimension(2), input_rows); + EXPECT_EQ(input_backward.dimension(3), input_planes); + EXPECT_EQ(input_backward.dimension(4), input_depth); + + for (int b = 0; b < num_batches; ++b) { + for (int id = 0; id < input_depth; ++id) { + for (int i = 0; i < input_planes; ++i) { + for (int j = 0; j < input_rows; ++j) { + for (int k = 0; k < input_cols; ++k) { + float expected = 0.0f; + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + for (int p = 0; p < patch_planes; ++p) { + for (int od = 0; od < output_depth; ++od) { + int output_i = i - p; + int output_j = j - r; + int output_k = k - c; + if (output_i >= 0 && output_i < output_planes && + output_j >= 0 && output_j < output_rows && + output_k >= 0 && output_k < output_cols) { + expected += + output_backward(b, output_k, output_j, output_i, od) * + kernel(c, r, p, id, od); + } + } + } + } + } + EigenApprox(input_backward(b, k, j, i, id), expected); + } + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_strided_spatial_convolution_backward_input_valid) { + const int num_batches = 11; + const int input_depth = 2; + const int input_rows = 9; + const int input_cols = 13; + const int output_depth = 5; + const int patch_rows = 3; + const int patch_cols = 3; + + const int stride = 3; + + const int output_rows = (input_rows - patch_rows + 1 + stride - 1) / stride; + const int output_cols = (input_cols - patch_cols + 1 + stride - 1) / stride; + + Tensor<float, 4> input_backward(input_depth, input_rows, input_cols, + num_batches); + Tensor<float, 4> kernel(output_depth, input_depth, patch_rows, patch_cols); + Tensor<float, 4> output_backward(output_depth, output_rows, output_cols, + num_batches); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + kernel = kernel.constant(2.0f) + kernel.random(); + input_backward.setRandom(); + + input_backward = SpatialConvolutionBackwardInput( + kernel, output_backward, input_rows, input_cols, stride); + + EXPECT_EQ(input_backward.dimension(0), input_depth); + EXPECT_EQ(input_backward.dimension(1), input_rows); + EXPECT_EQ(input_backward.dimension(2), input_cols); + EXPECT_EQ(input_backward.dimension(3), num_batches); + + for (int b = 0; b < num_batches; ++b) { + for (int id = 0; id < input_depth; ++id) { + for (int i = 0; i < input_rows; ++i) { + for (int j = 0; j < input_cols; ++j) { + float expected = 0.0f; + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + for (int od = 0; od < output_depth; ++od) { + int output_i = i - r; + int output_j = j - c; + if (output_i >= 0 && output_i / stride < output_rows && + output_j >= 0 && output_j / stride < output_cols && + output_i % stride == 0 && output_j % stride == 0) { + expected += output_backward(od, output_i / stride, + output_j / stride, b) * + kernel(od, id, r, c); + } + } + } + } + EigenApprox(input_backward(id, i, j, b), expected); + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_strided_spatial_convolution_backward_input_valid_row_major) { + const int num_batches = 11; + const int input_depth = 3; + const int input_rows = 5; + const int input_cols = 9; + const int output_depth = 1; + const int patch_rows = 3; + const int patch_cols = 3; + + const int stride = 2; + + const int output_rows = (input_rows - patch_rows + 2) / stride; + const int output_cols = (input_cols - patch_cols + 2) / stride; + + Tensor<float, 4, RowMajor> input_backward(num_batches, input_cols, input_rows, + input_depth); + Tensor<float, 4, RowMajor> kernel(patch_cols, patch_rows, input_depth, + output_depth); + Tensor<float, 4, RowMajor> output_backward(num_batches, output_cols, + output_rows, output_depth); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + kernel = kernel.constant(2.0f) + kernel.random(); + input_backward.setRandom(); + + input_backward = SpatialConvolutionBackwardInput( + kernel, output_backward, input_rows, input_cols, stride); + + EXPECT_EQ(input_backward.dimension(0), num_batches); + EXPECT_EQ(input_backward.dimension(1), input_cols); + EXPECT_EQ(input_backward.dimension(2), input_rows); + EXPECT_EQ(input_backward.dimension(3), input_depth); + + for (int b = 0; b < num_batches; ++b) { + for (int id = 0; id < input_depth; ++id) { + for (int i = 0; i < input_rows; ++i) { + for (int j = 0; j < input_cols; ++j) { + float expected = 0.0f; + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + for (int od = 0; od < output_depth; ++od) { + int output_i = i - r; + int output_j = j - c; + if (output_i >= 0 && output_i / stride < output_rows && + output_j >= 0 && output_j / stride < output_cols && + output_i % stride == 0 && output_j % stride == 0) { + expected += output_backward(b, output_j / stride, + output_i / stride, od) * + kernel(c, r, id, od); + } + } + } + } + EigenApprox(input_backward(b, j, i, id), expected); + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_simple_spatial_convolution_backward_kernel_valid) { + const int input_depth = 2; + const int input_rows = 3; + const int input_cols = 4; + const int output_depth = 5; + const int patch_rows = 2; + const int patch_cols = 2; + const int output_rows = input_rows - patch_rows + 1; + const int output_cols = input_cols - patch_cols + 1; + + Tensor<float, 3> input(input_depth, input_rows, input_cols); + Tensor<float, 4> kernel(output_depth, input_depth, patch_rows, patch_cols); + Tensor<float, 3> output_backward(output_depth, output_rows, output_cols); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + input = input.constant(2.0f) + input.random(); + kernel.setRandom(); + + kernel = SpatialConvolutionBackwardKernel(input, output_backward, patch_rows, + patch_cols, 1); + + EXPECT_EQ(kernel.dimension(0), output_depth); + EXPECT_EQ(kernel.dimension(1), input_depth); + EXPECT_EQ(kernel.dimension(2), patch_rows); + EXPECT_EQ(kernel.dimension(3), patch_cols); + + for (int od = 0; od < output_depth; ++od) { + for (int id = 0; id < input_depth; ++id) { + for (int r = 0; r < patch_rows; ++r) { + for (int c = 0; c < patch_cols; ++c) { + float expected = 0.0f; + for (int i = 0; i < input_rows; ++i) { + for (int j = 0; j < input_cols; ++j) { + int output_i = i - r; + int output_j = j - c; + if (output_i >= 0 && output_i < output_rows && output_j >= 0 && + output_j < output_cols) { + expected += + input(id, i, j) * output_backward(od, output_i, output_j); + } + } + } + EigenApprox(kernel(od, id, r, c), expected); + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_simple_spatial_convolution_backward_kernel_valid_row_major) { + const int input_depth = 2; + const int input_rows = 3; + const int input_cols = 4; + const int output_depth = 5; + const int patch_rows = 2; + const int patch_cols = 2; + const int output_rows = input_rows - patch_rows + 1; + const int output_cols = input_cols - patch_cols + 1; + + Tensor<float, 3, RowMajor> input(input_cols, input_rows, input_depth); + Tensor<float, 4, RowMajor> kernel(patch_cols, patch_rows, input_depth, + output_depth); + Tensor<float, 3, RowMajor> output_backward(output_cols, output_rows, + output_depth); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + input = input.constant(2.0f) + input.random(); + kernel.setRandom(); + + kernel = SpatialConvolutionBackwardKernel(input, output_backward, patch_rows, + patch_cols, 1); + + EXPECT_EQ(kernel.dimension(0), patch_cols); + EXPECT_EQ(kernel.dimension(1), patch_rows); + EXPECT_EQ(kernel.dimension(2), input_depth); + EXPECT_EQ(kernel.dimension(3), output_depth); + + for (int od = 0; od < output_depth; ++od) { + for (int id = 0; id < input_depth; ++id) { + for (int r = 0; r < patch_rows; ++r) { + for (int c = 0; c < patch_cols; ++c) { + float expected = 0.0f; + for (int i = 0; i < input_rows; ++i) { + for (int j = 0; j < input_cols; ++j) { + int output_i = i - r; + int output_j = j - c; + if (output_i >= 0 && output_i < output_rows && output_j >= 0 && + output_j < output_cols) { + expected += + input(j, i, id) * output_backward(output_j, output_i, od); + } + } + } + EigenApprox(kernel(c, r, id, od), expected); + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_atrous_spatial_convolution_backward_input_valid) { + const int num_batches = 11; + const int patch_rows = 3; + const int patch_cols = 3; + + const int input_depth = 2; + const int input_rows = 9; + const int input_cols = 13; + + const int in_stride = 3; + const int patch_rows_eff = patch_rows + (patch_rows - 1) * (in_stride - 1); + const int patch_cols_eff = patch_cols + (patch_cols - 1) * (in_stride - 1); + + const int output_depth = 5; + const int output_rows = input_rows - patch_rows_eff + 1; + const int output_cols = input_cols - patch_cols_eff + 1; + + Tensor<float, 4> output_backward(output_depth, output_rows, output_cols, + num_batches); + output_backward.setRandom(); + Tensor<float, 4> kernel(output_depth, input_depth, patch_rows, patch_cols); + kernel.setRandom(); + + const array<DenseIndex, 4> kernel_strides({1, 1, in_stride, in_stride}); + const Tensor<float, 4> kernel_eff = kernel.inflate(kernel_strides); + + const Tensor<float, 4> input_backward = SpatialConvolutionBackwardInput( + kernel, output_backward, input_rows, input_cols, 1, in_stride); + const Tensor<float, 4> expected_input_backward = + SpatialConvolutionBackwardInput(kernel_eff, output_backward, input_rows, + input_cols); + + EXPECT_EQ(input_backward.dimension(0), input_depth); + EXPECT_EQ(input_backward.dimension(1), input_rows); + EXPECT_EQ(input_backward.dimension(2), input_cols); + EXPECT_EQ(input_backward.dimension(3), num_batches); + + eigen_assert(dimensions_match(input_backward.dimensions(), + expected_input_backward.dimensions())); + for (size_t i = 0; i < input_backward.dimensions().TotalSize(); ++i) { + EigenApprox(input_backward.data()[i], expected_input_backward.data()[i]); + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_atrous_spatial_convolution_backward_input_valid_row_major) { + const int num_batches = 11; + const int patch_rows = 3; + const int patch_cols = 3; + + const int input_depth = 2; + const int input_rows = 9; + const int input_cols = 13; + + const int in_stride = 3; + const int patch_rows_eff = patch_rows + (patch_rows - 1) * (in_stride - 1); + const int patch_cols_eff = patch_cols + (patch_cols - 1) * (in_stride - 1); + + const int output_depth = 5; + const int output_rows = input_rows - patch_rows_eff + 1; + const int output_cols = input_cols - patch_cols_eff + 1; + + Tensor<float, 4, RowMajor> output_backward(num_batches, output_cols, + output_rows, output_depth); + output_backward.setRandom(); + + Tensor<float, 4, RowMajor> kernel(patch_cols, patch_rows, input_depth, + output_depth); + kernel.setRandom(); + + const array<DenseIndex, 4> kernel_strides({in_stride, in_stride, 1, 1}); + const Tensor<float, 4, RowMajor> kernel_eff = kernel.inflate(kernel_strides); + + const Tensor<float, 4, RowMajor> input_backward = + SpatialConvolutionBackwardInput(kernel, output_backward, input_rows, + input_cols, 1, in_stride); + const Tensor<float, 4, RowMajor> expected_input_backward = + SpatialConvolutionBackwardInput(kernel_eff, output_backward, input_rows, + input_cols); + + EXPECT_EQ(input_backward.dimension(0), num_batches); + EXPECT_EQ(input_backward.dimension(1), input_cols); + EXPECT_EQ(input_backward.dimension(2), input_rows); + EXPECT_EQ(input_backward.dimension(3), input_depth); + + eigen_assert(dimensions_match(input_backward.dimensions(), + expected_input_backward.dimensions())); + for (size_t i = 0; i < input_backward.dimensions().TotalSize(); ++i) { + EigenApprox(input_backward.data()[i], expected_input_backward.data()[i]); + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_atrous_spatial_convolution_backward_kernel_valid) { + const int num_batches = 11; + const int patch_rows = 3; + const int patch_cols = 3; + + const int input_depth = 2; + const int input_rows = 9; + const int input_cols = 13; + + const int in_stride = 3; + const int patch_rows_eff = patch_rows + (patch_rows - 1) * (in_stride - 1); + const int patch_cols_eff = patch_cols + (patch_cols - 1) * (in_stride - 1); + + const int output_depth = 5; + const int output_rows = input_rows - patch_rows_eff + 1; + const int output_cols = input_cols - patch_cols_eff + 1; + + Tensor<float, 4> output_backward(output_depth, output_rows, output_cols, + num_batches); + output_backward.setRandom(); + + Tensor<float, 4> input(input_depth, input_rows, input_cols, num_batches); + input.setRandom(); + + const array<DenseIndex, 4> kernel_strides({1, 1, in_stride, in_stride}); + + const Tensor<float, 4> kernel_backward = SpatialConvolutionBackwardKernel( + input, output_backward, patch_rows, patch_cols, 1, in_stride); + const Tensor<float, 4> expected_kernel_backward = + SpatialConvolutionBackwardKernel(input, output_backward, patch_rows_eff, + patch_cols_eff) + .stride(kernel_strides); + + EXPECT_EQ(kernel_backward.dimension(0), output_depth); + EXPECT_EQ(kernel_backward.dimension(1), input_depth); + EXPECT_EQ(kernel_backward.dimension(2), patch_rows); + EXPECT_EQ(kernel_backward.dimension(3), patch_cols); + + eigen_assert(dimensions_match(kernel_backward.dimensions(), + expected_kernel_backward.dimensions())); + for (size_t i = 0; i < kernel_backward.dimensions().TotalSize(); ++i) { + EigenApprox(kernel_backward.data()[i], expected_kernel_backward.data()[i]); + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_atrous_spatial_convolution_backward_kernel_valid_row_major) { + const int num_batches = 11; + const int patch_rows = 3; + const int patch_cols = 3; + + const int input_depth = 2; + const int input_rows = 9; + const int input_cols = 13; + + const int in_stride = 3; + const int patch_rows_eff = patch_rows + (patch_rows - 1) * (in_stride - 1); + const int patch_cols_eff = patch_cols + (patch_cols - 1) * (in_stride - 1); + + const int output_depth = 5; + const int output_rows = input_rows - patch_rows_eff + 1; + const int output_cols = input_cols - patch_cols_eff + 1; + + Tensor<float, 4, RowMajor> output_backward(num_batches, output_cols, + output_rows, output_depth); + output_backward.setRandom(); + + Tensor<float, 4, RowMajor> input(num_batches, input_cols, input_rows, + input_depth); + input.setRandom(); + + const array<DenseIndex, 4> kernel_strides({in_stride, in_stride, 1, 1}); + + const Tensor<float, 4, RowMajor> kernel_backward = + SpatialConvolutionBackwardKernel(input, output_backward, patch_rows, + patch_cols, 1, in_stride); + const Tensor<float, 4, RowMajor> expected_kernel_backward = + SpatialConvolutionBackwardKernel(input, output_backward, patch_rows_eff, + patch_cols_eff) + .stride(kernel_strides); + + EXPECT_EQ(kernel_backward.dimension(0), patch_cols); + EXPECT_EQ(kernel_backward.dimension(1), patch_rows); + EXPECT_EQ(kernel_backward.dimension(2), input_depth); + EXPECT_EQ(kernel_backward.dimension(3), output_depth); + + eigen_assert(dimensions_match(kernel_backward.dimensions(), + expected_kernel_backward.dimensions())); + for (size_t i = 0; i < kernel_backward.dimensions().TotalSize(); ++i) { + EigenApprox(kernel_backward.data()[i], expected_kernel_backward.data()[i]); + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_simple_cuboid_convolution_backward_kernel_valid) { + const int input_depth = 2; + const int input_planes = 5; + const int input_rows = 3; + const int input_cols = 4; + const int output_depth = 5; + const int patch_rows = 2; + const int patch_cols = 2; + const int patch_planes = 3; + const int output_rows = input_rows - patch_rows + 1; + const int output_cols = input_cols - patch_cols + 1; + const int output_planes = input_planes - patch_planes + 1; + + Tensor<float, 4> input(input_depth, input_planes, input_rows, input_cols); + Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows, + patch_cols); + Tensor<float, 4> output_backward(output_depth, output_planes, output_rows, + output_cols); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + input = input.constant(2.0f) + input.random(); + kernel.setRandom(); + + kernel = CuboidConvolutionBackwardKernel(input, output_backward, patch_planes, + patch_rows, patch_cols, 1, 1, 1); + + EXPECT_EQ(kernel.dimension(0), output_depth); + EXPECT_EQ(kernel.dimension(1), input_depth); + EXPECT_EQ(kernel.dimension(2), patch_planes); + EXPECT_EQ(kernel.dimension(3), patch_rows); + EXPECT_EQ(kernel.dimension(4), patch_cols); + + for (int od = 0; od < output_depth; ++od) { + for (int id = 0; id < input_depth; ++id) { + for (int p = 0; p < patch_planes; ++p) { + for (int r = 0; r < patch_rows; ++r) { + for (int c = 0; c < patch_cols; ++c) { + float expected = 0.0f; + for (int i = 0; i < input_planes; ++i) { + for (int j = 0; j < input_rows; ++j) { + for (int k = 0; k < input_cols; ++k) { + int output_j = j - r; + int output_k = k - c; + int output_i = i - p; + if (output_i >= 0 && output_i < output_planes && + output_j >= 0 && output_j < output_rows && + output_k >= 0 && output_k < output_cols) { + expected += + input(id, i, j, k) * + output_backward(od, output_i, output_j, output_k); + } + } + } + } + EigenApprox(kernel(od, id, p, r, c), expected); + } + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_simple_cuboid_convolution_backward_kernel_valid_row_major) { + const int input_depth = 2; + const int input_planes = 5; + const int input_rows = 3; + const int input_cols = 4; + const int output_depth = 5; + const int patch_rows = 2; + const int patch_cols = 2; + const int patch_planes = 3; + const int output_rows = input_rows - patch_rows + 1; + const int output_cols = input_cols - patch_cols + 1; + const int output_planes = input_planes - patch_planes + 1; + + Tensor<float, 4, RowMajor> input(input_cols, input_rows, input_planes, + input_depth); + Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes, + input_depth, output_depth); + Tensor<float, 4, RowMajor> output_backward(output_cols, output_rows, + output_planes, output_depth); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + input = input.constant(2.0f) + input.random(); + kernel.setRandom(); + + kernel = CuboidConvolutionBackwardKernel(input, output_backward, patch_planes, + patch_rows, patch_cols, 1, 1, 1); + + EXPECT_EQ(kernel.dimension(4), output_depth); + EXPECT_EQ(kernel.dimension(3), input_depth); + EXPECT_EQ(kernel.dimension(2), patch_planes); + EXPECT_EQ(kernel.dimension(1), patch_rows); + EXPECT_EQ(kernel.dimension(0), patch_cols); + + for (int od = 0; od < output_depth; ++od) { + for (int id = 0; id < input_depth; ++id) { + for (int p = 0; p < patch_planes; ++p) { + for (int r = 0; r < patch_rows; ++r) { + for (int c = 0; c < patch_cols; ++c) { + float expected = 0.0f; + for (int i = 0; i < input_planes; ++i) { + for (int j = 0; j < input_rows; ++j) { + for (int k = 0; k < input_cols; ++k) { + int output_j = j - r; + int output_k = k - c; + int output_i = i - p; + if (output_i >= 0 && output_i < output_planes && + output_j >= 0 && output_j < output_rows && + output_k >= 0 && output_k < output_cols) { + expected += + input(k, j, i, id) * + output_backward(output_k, output_j, output_i, od); + } + } + } + } + EigenApprox(kernel(c, r, p, id, od), expected); + } + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_spatial_convolution_backward_kernel_valid) { + const int num_batches = 13; + const int input_depth = 2; + const int input_rows = 7; + const int input_cols = 9; + const int output_depth = 3; + const int patch_rows = 5; + const int patch_cols = 5; + const int output_rows = input_rows - patch_rows + 1; + const int output_cols = input_cols - patch_cols + 1; + + Tensor<float, 4> input(input_depth, input_rows, input_cols, num_batches); + Tensor<float, 4> kernel_backward(output_depth, input_depth, patch_rows, + patch_cols); + Tensor<float, 4> output_backward(output_depth, output_rows, output_cols, + num_batches); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + input = input.constant(2.0f) + input.random(); + kernel_backward.setRandom(); + + kernel_backward = SpatialConvolutionBackwardKernel(input, output_backward, + patch_rows, patch_cols, 1); + + EXPECT_EQ(kernel_backward.dimension(0), output_depth); + EXPECT_EQ(kernel_backward.dimension(1), input_depth); + EXPECT_EQ(kernel_backward.dimension(2), patch_rows); + EXPECT_EQ(kernel_backward.dimension(3), patch_cols); + + for (int od = 0; od < output_depth; ++od) { + for (int id = 0; id < input_depth; ++id) { + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + float expected = 0.0f; + for (int b = 0; b < num_batches; ++b) { + for (int i = 0; i < input_rows; ++i) { + for (int j = 0; j < input_cols; ++j) { + int output_i = i - r; + int output_j = j - c; + if (output_i >= 0 && output_i < output_rows && output_j >= 0 && + output_j < output_cols) { + expected += input(id, i, j, b) * + output_backward(od, output_i, output_j, b); + } + } + } + } + EigenApprox(kernel_backward(od, id, r, c), expected); + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_spatial_convolution_backward_kernel_valid_row_major) { + const int num_batches = 13; + const int input_depth = 2; + const int input_rows = 7; + const int input_cols = 9; + const int output_depth = 3; + const int patch_rows = 4; + const int patch_cols = 4; + const int output_rows = input_rows - patch_rows + 1; + const int output_cols = input_cols - patch_cols + 1; + + Tensor<float, 4, RowMajor> input(num_batches, input_cols, input_rows, + input_depth); + Tensor<float, 4, RowMajor> kernel_backward(patch_cols, patch_rows, + input_depth, output_depth); + Tensor<float, 4, RowMajor> output_backward(num_batches, output_cols, + output_rows, output_depth); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + input = input.constant(2.0f) + input.random(); + kernel_backward.setRandom(); + + kernel_backward = SpatialConvolutionBackwardKernel(input, output_backward, + patch_rows, patch_cols, 1); + + EXPECT_EQ(kernel_backward.dimension(0), patch_cols); + EXPECT_EQ(kernel_backward.dimension(1), patch_rows); + EXPECT_EQ(kernel_backward.dimension(2), input_depth); + EXPECT_EQ(kernel_backward.dimension(3), output_depth); + + for (int od = 0; od < output_depth; ++od) { + for (int id = 0; id < input_depth; ++id) { + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + float expected = 0.0f; + for (int b = 0; b < num_batches; ++b) { + for (int i = 0; i < input_rows; ++i) { + for (int j = 0; j < input_cols; ++j) { + int output_i = i - r; + int output_j = j - c; + if (output_i >= 0 && output_i < output_rows && output_j >= 0 && + output_j < output_cols) { + expected += input(b, j, i, id) * + output_backward(b, output_j, output_i, od); + } + } + } + } + EigenApprox(kernel_backward(c, r, id, od), expected); + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_cuboid_convolution_backward_kernel_valid) { + const int num_batches = 13; + const int input_depth = 2; + const int input_planes = 5; + const int input_rows = 7; + const int input_cols = 9; + const int output_depth = 3; + const int patch_rows = 5; + const int patch_cols = 5; + const int patch_planes = 3; + const int output_rows = input_rows - patch_rows + 1; + const int output_cols = input_cols - patch_cols + 1; + const int output_planes = input_planes - patch_planes + 1; + + Tensor<float, 5> input(input_depth, input_planes, input_rows, input_cols, + num_batches); + Tensor<float, 5> kernel_backward(output_depth, input_depth, patch_planes, + patch_rows, patch_cols); + Tensor<float, 5> output_backward(output_depth, output_planes, output_rows, + output_cols, num_batches); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + input = input.constant(2.0f) + input.random(); + kernel_backward.setRandom(); + + kernel_backward = CuboidConvolutionBackwardKernel( + input, output_backward, patch_planes, patch_rows, patch_cols, 1, 1, 1); + + EXPECT_EQ(kernel_backward.dimension(0), output_depth); + EXPECT_EQ(kernel_backward.dimension(1), input_depth); + EXPECT_EQ(kernel_backward.dimension(2), patch_planes); + EXPECT_EQ(kernel_backward.dimension(3), patch_rows); + EXPECT_EQ(kernel_backward.dimension(4), patch_cols); + + for (int od = 0; od < output_depth; ++od) { + for (int id = 0; id < input_depth; ++id) { + for (int p = 0; p < patch_planes; ++p) { + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + float expected = 0.0f; + for (int b = 0; b < num_batches; ++b) { + for (int i = 0; i < input_planes; ++i) { + for (int j = 0; j < input_rows; ++j) { + for (int k = 0; k < input_cols; ++k) { + int output_j = j - r; + int output_k = k - c; + int output_i = i - p; + if (output_i >= 0 && output_i < output_planes && + output_j >= 0 && output_j < output_rows && + output_k >= 0 && output_k < output_cols) { + expected += + input(id, i, j, k, b) * + output_backward(od, output_i, output_j, output_k, b); + } + } + } + } + } + EigenApprox(kernel_backward(od, id, p, r, c), expected); + } + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_cuboid_convolution_backward_kernel_valid_row_major) { + const int num_batches = 13; + const int input_depth = 2; + const int input_planes = 5; + const int input_rows = 7; + const int input_cols = 9; + const int output_depth = 3; + const int patch_rows = 5; + const int patch_cols = 5; + const int patch_planes = 3; + const int output_rows = input_rows - patch_rows + 1; + const int output_cols = input_cols - patch_cols + 1; + const int output_planes = input_planes - patch_planes + 1; + + Tensor<float, 5, RowMajor> input(num_batches, input_cols, input_rows, + input_planes, input_depth); + Tensor<float, 5, RowMajor> kernel_backward( + patch_cols, patch_rows, patch_planes, input_depth, output_depth); + Tensor<float, 5, RowMajor> output_backward( + num_batches, output_cols, output_rows, output_planes, output_depth); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + input = input.constant(2.0f) + input.random(); + kernel_backward.setRandom(); + + kernel_backward = CuboidConvolutionBackwardKernel( + input, output_backward, patch_planes, patch_rows, patch_cols, 1, 1, 1); + + EXPECT_EQ(kernel_backward.dimension(4), output_depth); + EXPECT_EQ(kernel_backward.dimension(3), input_depth); + EXPECT_EQ(kernel_backward.dimension(2), patch_planes); + EXPECT_EQ(kernel_backward.dimension(1), patch_rows); + EXPECT_EQ(kernel_backward.dimension(0), patch_cols); + + for (int od = 0; od < output_depth; ++od) { + for (int id = 0; id < input_depth; ++id) { + for (int p = 0; p < patch_planes; ++p) { + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + float expected = 0.0f; + for (int b = 0; b < num_batches; ++b) { + for (int i = 0; i < input_planes; ++i) { + for (int j = 0; j < input_rows; ++j) { + for (int k = 0; k < input_cols; ++k) { + int output_j = j - r; + int output_k = k - c; + int output_i = i - p; + if (output_i >= 0 && output_i < output_planes && + output_j >= 0 && output_j < output_rows && + output_k >= 0 && output_k < output_cols) { + expected += + input(b, k, j, i, id) * + output_backward(b, output_k, output_j, output_i, od); + } + } + } + } + } + EigenApprox(kernel_backward(c, r, p, id, od), expected); + } + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_strided_spatial_convolution_backward_kernel_valid) { + const int num_batches = 13; + const int input_depth = 2; + const int input_rows = 7; + const int input_cols = 9; + const int output_depth = 3; + const int patch_rows = 5; + const int patch_cols = 5; + + const int stride = 2; + + const int output_rows = (input_rows - patch_rows + 1 + stride - 1) / stride; + const int output_cols = (input_cols - patch_cols + 1 + stride - 1) / stride; + + Tensor<float, 4> input(input_depth, input_rows, input_cols, num_batches); + Tensor<float, 4> kernel_backward(output_depth, input_depth, patch_rows, + patch_cols); + Tensor<float, 4> output_backward(output_depth, output_rows, output_cols, + num_batches); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + input = input.constant(2.0f) + input.random(); + kernel_backward.setRandom(); + + kernel_backward = SpatialConvolutionBackwardKernel( + input, output_backward, patch_rows, patch_cols, stride); + + EXPECT_EQ(kernel_backward.dimension(0), output_depth); + EXPECT_EQ(kernel_backward.dimension(1), input_depth); + EXPECT_EQ(kernel_backward.dimension(2), patch_rows); + EXPECT_EQ(kernel_backward.dimension(3), patch_cols); + + for (int od = 0; od < output_depth; ++od) { + for (int id = 0; id < input_depth; ++id) { + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + float expected = 0.0f; + for (int b = 0; b < num_batches; ++b) { + for (int i = 0; i < input_rows; ++i) { + for (int j = 0; j < input_cols; ++j) { + int output_i = i - r; + int output_j = j - c; + if (output_i >= 0 && output_i / stride < output_rows && + output_j >= 0 && output_j / stride < output_cols && + output_i % stride == 0 && output_j % stride == 0) { + expected += input(id, i, j, b) * + output_backward(od, output_i / stride, + output_j / stride, b); + } + } + } + } + EigenApprox(kernel_backward(od, id, r, c), expected); + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_strided_spatial_convolution_backward_kernel_valid_row_major) { + const int num_batches = 13; + const int input_depth = 2; + const int input_rows = 7; + const int input_cols = 9; + const int output_depth = 3; + const int patch_rows = 4; + const int patch_cols = 4; + + const int stride = 2; + + const int output_rows = (input_rows - patch_rows + 1 + stride - 1) / stride; + const int output_cols = (input_cols - patch_cols + 1 + stride - 1) / stride; + + Tensor<float, 4, RowMajor> input(num_batches, input_cols, input_rows, + input_depth); + Tensor<float, 4, RowMajor> kernel_backward(patch_cols, patch_rows, + input_depth, output_depth); + Tensor<float, 4, RowMajor> output_backward(num_batches, output_cols, + output_rows, output_depth); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + input = input.constant(2.0f) + input.random(); + kernel_backward.setRandom(); + + kernel_backward = SpatialConvolutionBackwardKernel( + input, output_backward, patch_rows, patch_cols, stride); + + EXPECT_EQ(kernel_backward.dimension(0), patch_cols); + EXPECT_EQ(kernel_backward.dimension(1), patch_rows); + EXPECT_EQ(kernel_backward.dimension(2), input_depth); + EXPECT_EQ(kernel_backward.dimension(3), output_depth); + + for (int od = 0; od < output_depth; ++od) { + for (int id = 0; id < input_depth; ++id) { + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + float expected = 0.0f; + for (int b = 0; b < num_batches; ++b) { + for (int i = 0; i < input_rows; ++i) { + for (int j = 0; j < input_cols; ++j) { + int output_i = i - r; + int output_j = j - c; + if (output_i >= 0 && output_i / stride < output_rows && + output_j >= 0 && output_j / stride < output_cols && + output_i % stride == 0 && output_j % stride == 0) { + expected += input(b, j, i, id) * + output_backward(b, output_j / stride, + output_i / stride, od); + } + } + } + } + EigenApprox(kernel_backward(c, r, id, od), expected); + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_strided_cuboid_convolution_backward_kernel_valid) { + const int num_batches = 13; + const int input_depth = 2; + const int input_planes = 8; + const int input_rows = 7; + const int input_cols = 9; + const int output_depth = 3; + const int patch_planes = 3; + const int patch_rows = 3; + const int patch_cols = 2; + + const int stride_planes = 2; + const int stride_cols = 3; + const int stride_rows = 1; + + const int output_rows = ceil_div(input_rows - patch_rows + 1, stride_rows); + const int output_cols = ceil_div(input_cols - patch_cols + 1, stride_cols); + const int output_planes = + ceil_div(input_planes - patch_planes + 1, stride_planes); + + Tensor<float, 5> input(input_depth, input_planes, input_rows, input_cols, + num_batches); + Tensor<float, 5> kernel_backward(output_depth, input_depth, patch_planes, + patch_rows, patch_cols); + Tensor<float, 5> output_backward(output_depth, output_planes, output_rows, + output_cols, num_batches); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + input = input.constant(2.0f) + input.random(); + kernel_backward.setRandom(); + + kernel_backward = CuboidConvolutionBackwardKernel( + input, output_backward, patch_planes, patch_rows, patch_cols, + stride_planes, stride_rows, stride_cols); + + EXPECT_EQ(kernel_backward.dimension(0), output_depth); + EXPECT_EQ(kernel_backward.dimension(1), input_depth); + EXPECT_EQ(kernel_backward.dimension(2), patch_planes); + EXPECT_EQ(kernel_backward.dimension(3), patch_rows); + EXPECT_EQ(kernel_backward.dimension(4), patch_cols); + + for (int od = 0; od < output_depth; ++od) { + for (int id = 0; id < input_depth; ++id) { + for (int p = 0; p < patch_planes; ++p) { + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + float expected = 0.0f; + for (int b = 0; b < num_batches; ++b) { + for (int i = 0; i < input_planes; ++i) { + for (int j = 0; j < input_rows; ++j) { + for (int k = 0; k < input_cols; ++k) { + int output_j = j - r; + int output_k = k - c; + int output_i = i - p; + if (output_i >= 0 && + output_i / stride_planes < output_planes && + output_j >= 0 && output_j / stride_rows < output_rows && + output_k >= 0 && output_k / stride_cols < output_cols && + output_i % stride_planes == 0 && + output_j % stride_rows == 0 && + output_k % stride_cols == 0) { + expected += input(id, i, j, k, b) * + output_backward(od, output_i / stride_planes, + output_j / stride_rows, + output_k / stride_cols, b); + } + } + } + } + } + EigenApprox(kernel_backward(od, id, p, r, c), expected); + } + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_strided_cuboid_convolution_backward_kernel_valid_row_major) { + const int num_batches = 13; + const int input_depth = 2; + const int input_planes = 8; + const int input_rows = 7; + const int input_cols = 9; + const int output_depth = 3; + const int patch_planes = 3; + const int patch_rows = 3; + const int patch_cols = 2; + + const int stride_planes = 2; + const int stride_cols = 3; + const int stride_rows = 1; + + const int output_rows = ceil_div(input_rows - patch_rows + 1, stride_rows); + const int output_cols = ceil_div(input_cols - patch_cols + 1, stride_cols); + const int output_planes = + ceil_div(input_planes - patch_planes + 1, stride_planes); + + Tensor<float, 5, RowMajor> input(num_batches, input_cols, input_rows, + input_planes, input_depth); + Tensor<float, 5, RowMajor> kernel_backward( + patch_cols, patch_rows, patch_planes, input_depth, output_depth); + Tensor<float, 5, RowMajor> output_backward( + num_batches, output_cols, output_rows, output_planes, output_depth); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + input = input.constant(2.0f) + input.random(); + kernel_backward.setRandom(); + + kernel_backward = CuboidConvolutionBackwardKernel( + input, output_backward, patch_planes, patch_rows, patch_cols, + stride_planes, stride_rows, stride_cols); + + EXPECT_EQ(kernel_backward.dimension(4), output_depth); + EXPECT_EQ(kernel_backward.dimension(3), input_depth); + EXPECT_EQ(kernel_backward.dimension(2), patch_planes); + EXPECT_EQ(kernel_backward.dimension(1), patch_rows); + EXPECT_EQ(kernel_backward.dimension(0), patch_cols); + + for (int od = 0; od < output_depth; ++od) { + for (int id = 0; id < input_depth; ++id) { + for (int p = 0; p < patch_planes; ++p) { + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + float expected = 0.0f; + for (int b = 0; b < num_batches; ++b) { + for (int i = 0; i < input_planes; ++i) { + for (int j = 0; j < input_rows; ++j) { + for (int k = 0; k < input_cols; ++k) { + int output_j = j - r; + int output_k = k - c; + int output_i = i - p; + if (output_i >= 0 && + output_i / stride_planes < output_planes && + output_j >= 0 && output_j / stride_rows < output_rows && + output_k >= 0 && output_k / stride_cols < output_cols && + output_i % stride_planes == 0 && + output_j % stride_rows == 0 && + output_k % stride_cols == 0) { + expected += input(b, k, j, i, id) * + output_backward(b, output_k / stride_cols, + output_j / stride_rows, + output_i / stride_planes, od); + } + } + } + } + } + EigenApprox(kernel_backward(c, r, p, id, od), expected); + } + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_strided_cuboid_convolution_backward_input_valid) { + const int num_batches = 13; + const int input_depth = 2; + const int input_planes = 14; + const int input_rows = 13; + const int input_cols = 15; + const int patch_rows = 3; + const int patch_cols = 2; + const int patch_planes = 4; + const int stride_rows = 3; + const int stride_cols = 2; + const int stride_planes = 3; + const int output_rows = ceil_div(input_rows - patch_rows + 1, stride_rows); + const int output_cols = ceil_div(input_cols - patch_cols + 1, stride_cols); + const int output_planes = + ceil_div(input_planes - patch_planes + 1, stride_planes); + const int output_depth = 5; + + Tensor<float, 5> input_backward(input_depth, input_planes, input_rows, + input_cols, num_batches); + Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows, + patch_cols); + Tensor<float, 5> output_backward(output_depth, output_planes, output_rows, + output_cols, num_batches); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + kernel = kernel.constant(2.0f) + kernel.random(); + input_backward.setRandom(); + + input_backward = CuboidConvolutionBackwardInput( + kernel, output_backward, input_planes, input_rows, input_cols, + stride_planes, stride_rows, stride_cols); + + EXPECT_EQ(input_backward.dimension(4), num_batches); + EXPECT_EQ(input_backward.dimension(3), input_cols); + EXPECT_EQ(input_backward.dimension(2), input_rows); + EXPECT_EQ(input_backward.dimension(1), input_planes); + EXPECT_EQ(input_backward.dimension(0), input_depth); + + for (int b = 0; b < num_batches; ++b) { + for (int id = 0; id < input_depth; ++id) { + for (int i = 0; i < input_planes; ++i) { + for (int j = 0; j < input_rows; ++j) { + for (int k = 0; k < input_cols; ++k) { + float expected = 0.0f; + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + for (int p = 0; p < patch_planes; ++p) { + for (int od = 0; od < output_depth; ++od) { + int output_j = j - r; + int output_k = k - c; + int output_i = i - p; + if (output_i >= 0 && + output_i / stride_planes < output_planes && + output_j >= 0 && output_j / stride_rows < output_rows && + output_k >= 0 && output_k / stride_cols < output_cols && + output_i % stride_planes == 0 && + output_j % stride_rows == 0 && + output_k % stride_cols == 0) { + expected += output_backward(od, output_i / stride_planes, + output_j / stride_rows, + output_k / stride_cols, b) * + kernel(od, id, p, r, c); + } + } + } + } + } + EigenApprox(input_backward(id, i, j, k, b), expected); + } + } + } + } + } +} + +TEST(EigenBackwardSpatialConvolutionsTest, + test_batched_strided_cuboid_convolution_backward_input_valid_row_major) { + const int num_batches = 13; + const int input_depth = 2; + const int input_planes = 14; + const int input_rows = 13; + const int input_cols = 15; + const int patch_rows = 3; + const int patch_cols = 2; + const int patch_planes = 4; + const int stride_rows = 3; + const int stride_cols = 2; + const int stride_planes = 3; + const int output_rows = ceil_div(input_rows - patch_rows + 1, stride_rows); + const int output_cols = ceil_div(input_cols - patch_cols + 1, stride_cols); + const int output_planes = + ceil_div(input_planes - patch_planes + 1, stride_planes); + const int output_depth = 5; + + Tensor<float, 5, RowMajor> input_backward(num_batches, input_cols, input_rows, + input_planes, input_depth); + Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes, + input_depth, output_depth); + Tensor<float, 5, RowMajor> output_backward( + num_batches, output_cols, output_rows, output_planes, output_depth); + + output_backward = output_backward.constant(11.0f) + output_backward.random(); + kernel = kernel.constant(2.0f) + kernel.random(); + input_backward.setRandom(); + + input_backward = CuboidConvolutionBackwardInput( + kernel, output_backward, input_planes, input_rows, input_cols, + stride_planes, stride_rows, stride_cols); + + EXPECT_EQ(input_backward.dimension(0), num_batches); + EXPECT_EQ(input_backward.dimension(1), input_cols); + EXPECT_EQ(input_backward.dimension(2), input_rows); + EXPECT_EQ(input_backward.dimension(3), input_planes); + EXPECT_EQ(input_backward.dimension(4), input_depth); + + for (int b = 0; b < num_batches; ++b) { + for (int id = 0; id < input_depth; ++id) { + for (int i = 0; i < input_planes; ++i) { + for (int j = 0; j < input_rows; ++j) { + for (int k = 0; k < input_cols; ++k) { + float expected = 0.0f; + for (int c = 0; c < patch_cols; ++c) { + for (int r = 0; r < patch_rows; ++r) { + for (int p = 0; p < patch_planes; ++p) { + for (int od = 0; od < output_depth; ++od) { + int output_j = j - r; + int output_k = k - c; + int output_i = i - p; + if (output_i >= 0 && + output_i / stride_planes < output_planes && + output_j >= 0 && output_j / stride_rows < output_rows && + output_k >= 0 && output_k / stride_cols < output_cols && + output_i % stride_planes == 0 && + output_j % stride_rows == 0 && + output_k % stride_cols == 0) { + expected += + output_backward(b, output_k / stride_cols, + output_j / stride_rows, + output_i / stride_planes, od) * + kernel(c, r, p, id, od); + } + } + } + } + } + EigenApprox(input_backward(b, k, j, i, id), expected); + } + } + } + } + } +} + +} // namespace Eigen |