• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_cuboid_convolutions.h"
17 
18 #include "tensorflow/core/platform/test.h"
19 
20 namespace Eigen {
21 
22 namespace {
EigenApprox(float a,float b)23 void EigenApprox(float a, float b) {
24   ASSERT_TRUE(std::abs(a - b) <= std::min(std::abs(a), std::abs(b)) * 1e-3);
25 }
ceil_div(int a,int b)26 static int ceil_div(int a, int b) { return (a + b - 1) / b; }
27 }  // namespace
28 
TEST(EigenBackwardSpatialConvolutionsTest,test_simple_cuboid_convolution_backward_input_valid)29 TEST(EigenBackwardSpatialConvolutionsTest,
30      test_simple_cuboid_convolution_backward_input_valid) {
31   const int input_depth = 2;
32   const int input_planes = 5;
33   const int input_rows = 3;
34   const int input_cols = 4;
35   const int patch_rows = 2;
36   const int patch_cols = 2;
37   const int patch_planes = 2;
38   const int output_rows = input_rows - patch_rows + 1;
39   const int output_cols = input_cols - patch_cols + 1;
40   const int output_planes = input_planes - patch_planes + 1;
41   const int output_depth = 5;
42 
43   Tensor<float, 4> input_backward(input_depth, input_planes, input_rows,
44                                   input_cols);
45   Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows,
46                           patch_cols);
47   Tensor<float, 4> output_backward(output_depth, output_planes, output_rows,
48                                    output_cols);
49 
50   output_backward = output_backward.constant(11.0f) + output_backward.random();
51   kernel = kernel.constant(2.0f) + kernel.random();
52   input_backward.setRandom();
53 
54   input_backward = CuboidConvolutionBackwardInput(
55       kernel, output_backward, input_planes, input_rows, input_cols);
56 
57   EXPECT_EQ(input_backward.dimension(3), input_cols);
58   EXPECT_EQ(input_backward.dimension(2), input_rows);
59   EXPECT_EQ(input_backward.dimension(1), input_planes);
60   EXPECT_EQ(input_backward.dimension(0), input_depth);
61 
62   for (int id = 0; id < input_depth; ++id) {
63     for (int i = 0; i < input_planes; ++i) {
64       for (int j = 0; j < input_rows; ++j) {
65         for (int k = 0; k < input_cols; ++k) {
66           float expected = 0.0f;
67           for (int c = 0; c < patch_cols; ++c) {
68             for (int r = 0; r < patch_rows; ++r) {
69               for (int p = 0; p < patch_planes; ++p) {
70                 for (int od = 0; od < output_depth; ++od) {
71                   int output_j = j - r;
72                   int output_k = k - c;
73                   int output_i = i - p;
74                   if (output_i >= 0 && output_i < output_planes &&
75                       output_j >= 0 && output_j < output_rows &&
76                       output_k >= 0 && output_k < output_cols) {
77                     expected +=
78                         output_backward(od, output_i, output_j, output_k) *
79                         kernel(od, id, p, r, c);
80                   }
81                 }
82               }
83             }
84           }
85           EigenApprox(input_backward(id, i, j, k), expected);
86         }
87       }
88     }
89   }
90 }
91 
TEST(EigenBackwardSpatialConvolutionsTest,test_simple_cuboid_convolution_backward_input_valid_row_major)92 TEST(EigenBackwardSpatialConvolutionsTest,
93      test_simple_cuboid_convolution_backward_input_valid_row_major) {
94   const int input_depth = 2;
95   const int input_planes = 5;
96   const int input_rows = 3;
97   const int input_cols = 4;
98   const int patch_rows = 2;
99   const int patch_cols = 2;
100   const int patch_planes = 2;
101   const int output_rows = input_rows - patch_rows + 1;
102   const int output_cols = input_cols - patch_cols + 1;
103   const int output_planes = input_planes - patch_planes + 1;
104   const int output_depth = 5;
105 
106   Tensor<float, 4, RowMajor> input_backward(input_cols, input_rows,
107                                             input_planes, input_depth);
108   Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes,
109                                     input_depth, output_depth);
110   Tensor<float, 4, RowMajor> output_backward(output_cols, output_rows,
111                                              output_planes, output_depth);
112 
113   output_backward = output_backward.constant(11.0f) + output_backward.random();
114   kernel = kernel.constant(2.0f) + kernel.random();
115   input_backward.setRandom();
116 
117   input_backward = CuboidConvolutionBackwardInput(
118       kernel, output_backward, input_planes, input_rows, input_cols);
119 
120   EXPECT_EQ(input_backward.dimension(0), input_cols);
121   EXPECT_EQ(input_backward.dimension(1), input_rows);
122   EXPECT_EQ(input_backward.dimension(2), input_planes);
123   EXPECT_EQ(input_backward.dimension(3), input_depth);
124 
125   for (int id = 0; id < input_depth; ++id) {
126     for (int i = 0; i < input_planes; ++i) {
127       for (int j = 0; j < input_rows; ++j) {
128         for (int k = 0; k < input_cols; ++k) {
129           float expected = 0.0f;
130           for (int c = 0; c < patch_cols; ++c) {
131             for (int r = 0; r < patch_rows; ++r) {
132               for (int p = 0; p < patch_planes; ++p) {
133                 for (int od = 0; od < output_depth; ++od) {
134                   int output_j = j - r;
135                   int output_k = k - c;
136                   int output_i = i - p;
137                   if (output_i >= 0 && output_i < output_planes &&
138                       output_j >= 0 && output_j < output_rows &&
139                       output_k >= 0 && output_k < output_cols) {
140                     expected +=
141                         output_backward(output_k, output_j, output_i, od) *
142                         kernel(c, r, p, id, od);
143                   }
144                 }
145               }
146             }
147           }
148           EigenApprox(input_backward(k, j, i, id), expected);
149         }
150       }
151     }
152   }
153 }
154 
TEST(EigenBackwardSpatialConvolutionsTest,test_simple_cuboid_convolution_backward_input_same)155 TEST(EigenBackwardSpatialConvolutionsTest,
156      test_simple_cuboid_convolution_backward_input_same) {
157   const int input_depth = 2;
158   const int input_planes = 5;
159   const int input_rows = 3;
160   const int input_cols = 4;
161   const int patch_rows = 3;
162   const int patch_cols = 2;
163   const int patch_planes = 4;
164   const int output_rows = input_rows;
165   const int output_cols = input_cols;
166   const int output_planes = input_planes;
167   const int output_depth = 5;
168 
169   Tensor<float, 4> input_backward(input_depth, input_planes, input_rows,
170                                   input_cols);
171   Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows,
172                           patch_cols);
173   Tensor<float, 4> output_backward(output_depth, output_planes, output_rows,
174                                    output_cols);
175 
176   output_backward = output_backward.constant(11.0f) + output_backward.random();
177   kernel = kernel.constant(2.0f) + kernel.random();
178   input_backward.setRandom();
179 
180   input_backward = CuboidConvolutionBackwardInput(
181       kernel, output_backward, input_planes, input_rows, input_cols);
182 
183   EXPECT_EQ(input_backward.dimension(3), input_cols);
184   EXPECT_EQ(input_backward.dimension(2), input_rows);
185   EXPECT_EQ(input_backward.dimension(1), input_planes);
186   EXPECT_EQ(input_backward.dimension(0), input_depth);
187 
188   const int dz = patch_planes - 1;
189   const int dy = patch_rows - 1;
190   const int dx = patch_cols - 1;
191 
192   const int forward_pad_x = dx / 2;
193   const int forward_pad_y = dy / 2;
194   const int forward_pad_z = dz / 2;
195 
196   for (int id = 0; id < input_depth; ++id) {
197     for (int i = 0; i < input_planes; ++i) {
198       for (int j = 0; j < input_rows; ++j) {
199         for (int k = 0; k < input_cols; ++k) {
200           float expected = 0.0f;
201           for (int c = 0; c < patch_cols; ++c) {
202             for (int r = 0; r < patch_rows; ++r) {
203               for (int p = 0; p < patch_planes; ++p) {
204                 for (int od = 0; od < output_depth; ++od) {
205                   int output_i = i - p + forward_pad_z;
206                   int output_j = j - r + forward_pad_y;
207                   int output_k = k - c + forward_pad_x;
208                   if (output_i >= 0 && output_i < output_planes &&
209                       output_j >= 0 && output_j < output_rows &&
210                       output_k >= 0 && output_k < output_cols) {
211                     expected +=
212                         output_backward(od, output_i, output_j, output_k) *
213                         kernel(od, id, p, r, c);
214                   }
215                 }
216               }
217             }
218           }
219           EigenApprox(input_backward(id, i, j, k), expected);
220         }
221       }
222     }
223   }
224 }
225 
TEST(EigenBackwardSpatialConvolutionsTest,test_simple_cuboid_convolution_backward_input_same_row_major)226 TEST(EigenBackwardSpatialConvolutionsTest,
227      test_simple_cuboid_convolution_backward_input_same_row_major) {
228   const int input_depth = 2;
229   const int input_planes = 5;
230   const int input_rows = 3;
231   const int input_cols = 4;
232   const int patch_rows = 2;
233   const int patch_cols = 3;
234   const int patch_planes = 4;
235   const int output_rows = input_rows;
236   const int output_cols = input_cols;
237   const int output_planes = input_planes;
238   const int output_depth = 5;
239 
240   Tensor<float, 4, RowMajor> input_backward(input_cols, input_rows,
241                                             input_planes, input_depth);
242   Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes,
243                                     input_depth, output_depth);
244   Tensor<float, 4, RowMajor> output_backward(output_cols, output_rows,
245                                              output_planes, output_depth);
246 
247   output_backward = output_backward.constant(11.0f) + output_backward.random();
248   kernel = kernel.constant(2.0f) + kernel.random();
249   input_backward.setRandom();
250 
251   input_backward = CuboidConvolutionBackwardInput(
252       kernel, output_backward, input_planes, input_rows, input_cols);
253 
254   EXPECT_EQ(input_backward.dimension(0), input_cols);
255   EXPECT_EQ(input_backward.dimension(1), input_rows);
256   EXPECT_EQ(input_backward.dimension(2), input_planes);
257   EXPECT_EQ(input_backward.dimension(3), input_depth);
258 
259   const int dz = patch_planes - 1;
260   const int dy = patch_rows - 1;
261   const int dx = patch_cols - 1;
262 
263   const int forward_pad_x = dx / 2;
264   const int forward_pad_y = dy / 2;
265   const int forward_pad_z = dz / 2;
266 
267   for (int id = 0; id < input_depth; ++id) {
268     for (int i = 0; i < input_planes; ++i) {
269       for (int j = 0; j < input_rows; ++j) {
270         for (int k = 0; k < input_cols; ++k) {
271           float expected = 0.0f;
272           for (int c = 0; c < patch_cols; ++c) {
273             for (int r = 0; r < patch_rows; ++r) {
274               for (int p = 0; p < patch_planes; ++p) {
275                 for (int od = 0; od < output_depth; ++od) {
276                   int output_i = i - p + forward_pad_z;
277                   int output_j = j - r + forward_pad_y;
278                   int output_k = k - c + forward_pad_x;
279                   if (output_i >= 0 && output_i < output_planes &&
280                       output_j >= 0 && output_j < output_rows &&
281                       output_k >= 0 && output_k < output_cols) {
282                     expected +=
283                         output_backward(output_k, output_j, output_i, od) *
284                         kernel(c, r, p, id, od);
285                   }
286                 }
287               }
288             }
289           }
290           EigenApprox(input_backward(k, j, i, id), expected);
291         }
292       }
293     }
294   }
295 }
296 
TEST(EigenBackwardSpatialConvolutionsTest,test_batched_cuboid_convolution_backward_input_valid)297 TEST(EigenBackwardSpatialConvolutionsTest,
298      test_batched_cuboid_convolution_backward_input_valid) {
299   const int num_batches = 13;
300   const int input_depth = 2;
301   const int input_planes = 5;
302   const int input_rows = 3;
303   const int input_cols = 4;
304   const int patch_rows = 2;
305   const int patch_cols = 2;
306   const int patch_planes = 2;
307   const int output_rows = input_rows - patch_rows + 1;
308   const int output_cols = input_cols - patch_cols + 1;
309   const int output_planes = input_planes - patch_planes + 1;
310   const int output_depth = 5;
311 
312   Tensor<float, 5> input_backward(input_depth, input_planes, input_rows,
313                                   input_cols, num_batches);
314   Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows,
315                           patch_cols);
316   Tensor<float, 5> output_backward(output_depth, output_planes, output_rows,
317                                    output_cols, num_batches);
318 
319   output_backward = output_backward.constant(11.0f) + output_backward.random();
320   kernel = kernel.constant(2.0f) + kernel.random();
321   input_backward.setRandom();
322 
323   input_backward = CuboidConvolutionBackwardInput(
324       kernel, output_backward, input_planes, input_rows, input_cols);
325 
326   EXPECT_EQ(input_backward.dimension(4), num_batches);
327   EXPECT_EQ(input_backward.dimension(3), input_cols);
328   EXPECT_EQ(input_backward.dimension(2), input_rows);
329   EXPECT_EQ(input_backward.dimension(1), input_planes);
330   EXPECT_EQ(input_backward.dimension(0), input_depth);
331 
332   for (int b = 0; b < num_batches; ++b) {
333     for (int id = 0; id < input_depth; ++id) {
334       for (int i = 0; i < input_planes; ++i) {
335         for (int j = 0; j < input_rows; ++j) {
336           for (int k = 0; k < input_cols; ++k) {
337             float expected = 0.0f;
338             for (int c = 0; c < patch_cols; ++c) {
339               for (int r = 0; r < patch_rows; ++r) {
340                 for (int p = 0; p < patch_planes; ++p) {
341                   for (int od = 0; od < output_depth; ++od) {
342                     int output_i = i - p;
343                     int output_j = j - r;
344                     int output_k = k - c;
345                     if (output_i >= 0 && output_i < output_planes &&
346                         output_j >= 0 && output_j < output_rows &&
347                         output_k >= 0 && output_k < output_cols) {
348                       expected +=
349                           output_backward(od, output_i, output_j, output_k, b) *
350                           kernel(od, id, p, r, c);
351                     }
352                   }
353                 }
354               }
355             }
356             EigenApprox(input_backward(id, i, j, k, b), expected);
357           }
358         }
359       }
360     }
361   }
362 }
363 
TEST(EigenBackwardSpatialConvolutionsTest,test_batched_cuboid_convolution_backward_input_valid_row_major)364 TEST(EigenBackwardSpatialConvolutionsTest,
365      test_batched_cuboid_convolution_backward_input_valid_row_major) {
366   const int num_batches = 13;
367   const int input_depth = 2;
368   const int input_planes = 5;
369   const int input_rows = 3;
370   const int input_cols = 4;
371   const int patch_rows = 2;
372   const int patch_cols = 2;
373   const int patch_planes = 2;
374   const int output_rows = input_rows - patch_rows + 1;
375   const int output_cols = input_cols - patch_cols + 1;
376   const int output_planes = input_planes - patch_planes + 1;
377   const int output_depth = 5;
378 
379   Tensor<float, 5, RowMajor> input_backward(num_batches, input_cols, input_rows,
380                                             input_planes, input_depth);
381   Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes,
382                                     input_depth, output_depth);
383   Tensor<float, 5, RowMajor> output_backward(
384       num_batches, output_cols, output_rows, output_planes, output_depth);
385 
386   output_backward = output_backward.constant(11.0f) + output_backward.random();
387   kernel = kernel.constant(2.0f) + kernel.random();
388   input_backward.setRandom();
389 
390   input_backward = CuboidConvolutionBackwardInput(
391       kernel, output_backward, input_planes, input_rows, input_cols);
392 
393   EXPECT_EQ(input_backward.dimension(0), num_batches);
394   EXPECT_EQ(input_backward.dimension(1), input_cols);
395   EXPECT_EQ(input_backward.dimension(2), input_rows);
396   EXPECT_EQ(input_backward.dimension(3), input_planes);
397   EXPECT_EQ(input_backward.dimension(4), input_depth);
398 
399   for (int b = 0; b < num_batches; ++b) {
400     for (int id = 0; id < input_depth; ++id) {
401       for (int i = 0; i < input_planes; ++i) {
402         for (int j = 0; j < input_rows; ++j) {
403           for (int k = 0; k < input_cols; ++k) {
404             float expected = 0.0f;
405             for (int c = 0; c < patch_cols; ++c) {
406               for (int r = 0; r < patch_rows; ++r) {
407                 for (int p = 0; p < patch_planes; ++p) {
408                   for (int od = 0; od < output_depth; ++od) {
409                     int output_i = i - p;
410                     int output_j = j - r;
411                     int output_k = k - c;
412                     if (output_i >= 0 && output_i < output_planes &&
413                         output_j >= 0 && output_j < output_rows &&
414                         output_k >= 0 && output_k < output_cols) {
415                       expected +=
416                           output_backward(b, output_k, output_j, output_i, od) *
417                           kernel(c, r, p, id, od);
418                     }
419                   }
420                 }
421               }
422             }
423             EigenApprox(input_backward(b, k, j, i, id), expected);
424           }
425         }
426       }
427     }
428   }
429 }
430 
TEST(EigenBackwardSpatialConvolutionsTest,test_simple_cuboid_convolution_backward_kernel_valid)431 TEST(EigenBackwardSpatialConvolutionsTest,
432      test_simple_cuboid_convolution_backward_kernel_valid) {
433   const int input_depth = 2;
434   const int input_planes = 5;
435   const int input_rows = 3;
436   const int input_cols = 4;
437   const int output_depth = 5;
438   const int patch_rows = 2;
439   const int patch_cols = 2;
440   const int patch_planes = 3;
441   const int output_rows = input_rows - patch_rows + 1;
442   const int output_cols = input_cols - patch_cols + 1;
443   const int output_planes = input_planes - patch_planes + 1;
444 
445   // TODO(ezhulenev): Support backward kernel convolution without batch
446   // dimension.
447   Tensor<float, 5> input(input_depth, input_planes, input_rows, input_cols,
448                          /*num_batches*/ 1);
449   Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows,
450                           patch_cols);
451   Tensor<float, 5> output_backward(output_depth, output_planes, output_rows,
452                                    output_cols, /*num_batches*/ 1);
453 
454   output_backward = output_backward.constant(11.0f) + output_backward.random();
455   input = input.constant(2.0f) + input.random();
456   kernel.setRandom();
457 
458   kernel = CuboidConvolutionBackwardKernel(input, output_backward, patch_planes,
459                                            patch_rows, patch_cols, 1, 1, 1);
460 
461   EXPECT_EQ(kernel.dimension(0), output_depth);
462   EXPECT_EQ(kernel.dimension(1), input_depth);
463   EXPECT_EQ(kernel.dimension(2), patch_planes);
464   EXPECT_EQ(kernel.dimension(3), patch_rows);
465   EXPECT_EQ(kernel.dimension(4), patch_cols);
466 
467   for (int od = 0; od < output_depth; ++od) {
468     for (int id = 0; id < input_depth; ++id) {
469       for (int p = 0; p < patch_planes; ++p) {
470         for (int r = 0; r < patch_rows; ++r) {
471           for (int c = 0; c < patch_cols; ++c) {
472             float expected = 0.0f;
473             for (int i = 0; i < input_planes; ++i) {
474               for (int j = 0; j < input_rows; ++j) {
475                 for (int k = 0; k < input_cols; ++k) {
476                   int output_j = j - r;
477                   int output_k = k - c;
478                   int output_i = i - p;
479                   if (output_i >= 0 && output_i < output_planes &&
480                       output_j >= 0 && output_j < output_rows &&
481                       output_k >= 0 && output_k < output_cols) {
482                     expected += input(id, i, j, k, /*batch*/ 0) *
483                                 output_backward(od, output_i, output_j,
484                                                 output_k, /*batch*/ 0);
485                   }
486                 }
487               }
488             }
489             EigenApprox(kernel(od, id, p, r, c), expected);
490           }
491         }
492       }
493     }
494   }
495 }
496 
TEST(EigenBackwardSpatialConvolutionsTest,test_simple_cuboid_convolution_backward_kernel_valid_row_major)497 TEST(EigenBackwardSpatialConvolutionsTest,
498      test_simple_cuboid_convolution_backward_kernel_valid_row_major) {
499   const int input_depth = 2;
500   const int input_planes = 5;
501   const int input_rows = 3;
502   const int input_cols = 4;
503   const int output_depth = 5;
504   const int patch_rows = 2;
505   const int patch_cols = 2;
506   const int patch_planes = 3;
507   const int output_rows = input_rows - patch_rows + 1;
508   const int output_cols = input_cols - patch_cols + 1;
509   const int output_planes = input_planes - patch_planes + 1;
510 
511   // TODO(ezhulenev): Support backward kernel convolution without batch
512   // dimension.
513   Tensor<float, 5, RowMajor> input(/*num_batches*/ 1, input_cols, input_rows,
514                                    input_planes, input_depth);
515   Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes,
516                                     input_depth, output_depth);
517   Tensor<float, 5, RowMajor> output_backward(
518       /*num_batches*/ 1, output_cols, output_rows, output_planes, output_depth);
519 
520   output_backward = output_backward.constant(11.0f) + output_backward.random();
521   input = input.constant(2.0f) + input.random();
522   kernel.setRandom();
523 
524   kernel = CuboidConvolutionBackwardKernel(input, output_backward, patch_planes,
525                                            patch_rows, patch_cols, 1, 1, 1);
526 
527   EXPECT_EQ(kernel.dimension(4), output_depth);
528   EXPECT_EQ(kernel.dimension(3), input_depth);
529   EXPECT_EQ(kernel.dimension(2), patch_planes);
530   EXPECT_EQ(kernel.dimension(1), patch_rows);
531   EXPECT_EQ(kernel.dimension(0), patch_cols);
532 
533   for (int od = 0; od < output_depth; ++od) {
534     for (int id = 0; id < input_depth; ++id) {
535       for (int p = 0; p < patch_planes; ++p) {
536         for (int r = 0; r < patch_rows; ++r) {
537           for (int c = 0; c < patch_cols; ++c) {
538             float expected = 0.0f;
539             for (int i = 0; i < input_planes; ++i) {
540               for (int j = 0; j < input_rows; ++j) {
541                 for (int k = 0; k < input_cols; ++k) {
542                   int output_j = j - r;
543                   int output_k = k - c;
544                   int output_i = i - p;
545                   if (output_i >= 0 && output_i < output_planes &&
546                       output_j >= 0 && output_j < output_rows &&
547                       output_k >= 0 && output_k < output_cols) {
548                     expected += input(/*batch*/ 0, k, j, i, id) *
549                                 output_backward(/*batch*/ 0, output_k, output_j,
550                                                 output_i, od);
551                   }
552                 }
553               }
554             }
555             EigenApprox(kernel(c, r, p, id, od), expected);
556           }
557         }
558       }
559     }
560   }
561 }
562 
TEST(EigenBackwardSpatialConvolutionsTest,test_batched_cuboid_convolution_backward_kernel_valid)563 TEST(EigenBackwardSpatialConvolutionsTest,
564      test_batched_cuboid_convolution_backward_kernel_valid) {
565   const int num_batches = 13;
566   const int input_depth = 2;
567   const int input_planes = 5;
568   const int input_rows = 7;
569   const int input_cols = 9;
570   const int output_depth = 3;
571   const int patch_rows = 5;
572   const int patch_cols = 5;
573   const int patch_planes = 3;
574   const int output_rows = input_rows - patch_rows + 1;
575   const int output_cols = input_cols - patch_cols + 1;
576   const int output_planes = input_planes - patch_planes + 1;
577 
578   Tensor<float, 5> input(input_depth, input_planes, input_rows, input_cols,
579                          num_batches);
580   Tensor<float, 5> kernel_backward(output_depth, input_depth, patch_planes,
581                                    patch_rows, patch_cols);
582   Tensor<float, 5> output_backward(output_depth, output_planes, output_rows,
583                                    output_cols, num_batches);
584 
585   output_backward = output_backward.constant(11.0f) + output_backward.random();
586   input = input.constant(2.0f) + input.random();
587   kernel_backward.setRandom();
588 
589   kernel_backward = CuboidConvolutionBackwardKernel(
590       input, output_backward, patch_planes, patch_rows, patch_cols, 1, 1, 1);
591 
592   EXPECT_EQ(kernel_backward.dimension(0), output_depth);
593   EXPECT_EQ(kernel_backward.dimension(1), input_depth);
594   EXPECT_EQ(kernel_backward.dimension(2), patch_planes);
595   EXPECT_EQ(kernel_backward.dimension(3), patch_rows);
596   EXPECT_EQ(kernel_backward.dimension(4), patch_cols);
597 
598   for (int od = 0; od < output_depth; ++od) {
599     for (int id = 0; id < input_depth; ++id) {
600       for (int p = 0; p < patch_planes; ++p) {
601         for (int c = 0; c < patch_cols; ++c) {
602           for (int r = 0; r < patch_rows; ++r) {
603             float expected = 0.0f;
604             for (int b = 0; b < num_batches; ++b) {
605               for (int i = 0; i < input_planes; ++i) {
606                 for (int j = 0; j < input_rows; ++j) {
607                   for (int k = 0; k < input_cols; ++k) {
608                     int output_j = j - r;
609                     int output_k = k - c;
610                     int output_i = i - p;
611                     if (output_i >= 0 && output_i < output_planes &&
612                         output_j >= 0 && output_j < output_rows &&
613                         output_k >= 0 && output_k < output_cols) {
614                       expected +=
615                           input(id, i, j, k, b) *
616                           output_backward(od, output_i, output_j, output_k, b);
617                     }
618                   }
619                 }
620               }
621             }
622             EigenApprox(kernel_backward(od, id, p, r, c), expected);
623           }
624         }
625       }
626     }
627   }
628 }
629 
TEST(EigenBackwardSpatialConvolutionsTest,test_batched_cuboid_convolution_backward_kernel_valid_row_major)630 TEST(EigenBackwardSpatialConvolutionsTest,
631      test_batched_cuboid_convolution_backward_kernel_valid_row_major) {
632   const int num_batches = 13;
633   const int input_depth = 2;
634   const int input_planes = 5;
635   const int input_rows = 7;
636   const int input_cols = 9;
637   const int output_depth = 3;
638   const int patch_rows = 5;
639   const int patch_cols = 5;
640   const int patch_planes = 3;
641   const int output_rows = input_rows - patch_rows + 1;
642   const int output_cols = input_cols - patch_cols + 1;
643   const int output_planes = input_planes - patch_planes + 1;
644 
645   Tensor<float, 5, RowMajor> input(num_batches, input_cols, input_rows,
646                                    input_planes, input_depth);
647   Tensor<float, 5, RowMajor> kernel_backward(
648       patch_cols, patch_rows, patch_planes, input_depth, output_depth);
649   Tensor<float, 5, RowMajor> output_backward(
650       num_batches, output_cols, output_rows, output_planes, output_depth);
651 
652   output_backward = output_backward.constant(11.0f) + output_backward.random();
653   input = input.constant(2.0f) + input.random();
654   kernel_backward.setRandom();
655 
656   kernel_backward = CuboidConvolutionBackwardKernel(
657       input, output_backward, patch_planes, patch_rows, patch_cols, 1, 1, 1);
658 
659   EXPECT_EQ(kernel_backward.dimension(4), output_depth);
660   EXPECT_EQ(kernel_backward.dimension(3), input_depth);
661   EXPECT_EQ(kernel_backward.dimension(2), patch_planes);
662   EXPECT_EQ(kernel_backward.dimension(1), patch_rows);
663   EXPECT_EQ(kernel_backward.dimension(0), patch_cols);
664 
665   for (int od = 0; od < output_depth; ++od) {
666     for (int id = 0; id < input_depth; ++id) {
667       for (int p = 0; p < patch_planes; ++p) {
668         for (int c = 0; c < patch_cols; ++c) {
669           for (int r = 0; r < patch_rows; ++r) {
670             float expected = 0.0f;
671             for (int b = 0; b < num_batches; ++b) {
672               for (int i = 0; i < input_planes; ++i) {
673                 for (int j = 0; j < input_rows; ++j) {
674                   for (int k = 0; k < input_cols; ++k) {
675                     int output_j = j - r;
676                     int output_k = k - c;
677                     int output_i = i - p;
678                     if (output_i >= 0 && output_i < output_planes &&
679                         output_j >= 0 && output_j < output_rows &&
680                         output_k >= 0 && output_k < output_cols) {
681                       expected +=
682                           input(b, k, j, i, id) *
683                           output_backward(b, output_k, output_j, output_i, od);
684                     }
685                   }
686                 }
687               }
688             }
689             EigenApprox(kernel_backward(c, r, p, id, od), expected);
690           }
691         }
692       }
693     }
694   }
695 }
696 
TEST(EigenBackwardSpatialConvolutionsTest,test_batched_strided_cuboid_convolution_backward_kernel_valid)697 TEST(EigenBackwardSpatialConvolutionsTest,
698      test_batched_strided_cuboid_convolution_backward_kernel_valid) {
699   const int num_batches = 13;
700   const int input_depth = 2;
701   const int input_planes = 8;
702   const int input_rows = 7;
703   const int input_cols = 9;
704   const int output_depth = 3;
705   const int patch_planes = 3;
706   const int patch_rows = 3;
707   const int patch_cols = 2;
708 
709   const int stride_planes = 2;
710   const int stride_cols = 3;
711   const int stride_rows = 1;
712 
713   const int output_rows = ceil_div(input_rows - patch_rows + 1, stride_rows);
714   const int output_cols = ceil_div(input_cols - patch_cols + 1, stride_cols);
715   const int output_planes =
716       ceil_div(input_planes - patch_planes + 1, stride_planes);
717 
718   Tensor<float, 5> input(input_depth, input_planes, input_rows, input_cols,
719                          num_batches);
720   Tensor<float, 5> kernel_backward(output_depth, input_depth, patch_planes,
721                                    patch_rows, patch_cols);
722   Tensor<float, 5> output_backward(output_depth, output_planes, output_rows,
723                                    output_cols, num_batches);
724 
725   output_backward = output_backward.constant(11.0f) + output_backward.random();
726   input = input.constant(2.0f) + input.random();
727   kernel_backward.setRandom();
728 
729   kernel_backward = CuboidConvolutionBackwardKernel(
730       input, output_backward, patch_planes, patch_rows, patch_cols,
731       stride_planes, stride_rows, stride_cols);
732 
733   EXPECT_EQ(kernel_backward.dimension(0), output_depth);
734   EXPECT_EQ(kernel_backward.dimension(1), input_depth);
735   EXPECT_EQ(kernel_backward.dimension(2), patch_planes);
736   EXPECT_EQ(kernel_backward.dimension(3), patch_rows);
737   EXPECT_EQ(kernel_backward.dimension(4), patch_cols);
738 
739   for (int od = 0; od < output_depth; ++od) {
740     for (int id = 0; id < input_depth; ++id) {
741       for (int p = 0; p < patch_planes; ++p) {
742         for (int c = 0; c < patch_cols; ++c) {
743           for (int r = 0; r < patch_rows; ++r) {
744             float expected = 0.0f;
745             for (int b = 0; b < num_batches; ++b) {
746               for (int i = 0; i < input_planes; ++i) {
747                 for (int j = 0; j < input_rows; ++j) {
748                   for (int k = 0; k < input_cols; ++k) {
749                     int output_j = j - r;
750                     int output_k = k - c;
751                     int output_i = i - p;
752                     if (output_i >= 0 &&
753                         output_i / stride_planes < output_planes &&
754                         output_j >= 0 && output_j / stride_rows < output_rows &&
755                         output_k >= 0 && output_k / stride_cols < output_cols &&
756                         output_i % stride_planes == 0 &&
757                         output_j % stride_rows == 0 &&
758                         output_k % stride_cols == 0) {
759                       expected += input(id, i, j, k, b) *
760                                   output_backward(od, output_i / stride_planes,
761                                                   output_j / stride_rows,
762                                                   output_k / stride_cols, b);
763                     }
764                   }
765                 }
766               }
767             }
768             EigenApprox(kernel_backward(od, id, p, r, c), expected);
769           }
770         }
771       }
772     }
773   }
774 }
775 
TEST(EigenBackwardSpatialConvolutionsTest,test_batched_strided_cuboid_convolution_backward_kernel_valid_row_major)776 TEST(EigenBackwardSpatialConvolutionsTest,
777      test_batched_strided_cuboid_convolution_backward_kernel_valid_row_major) {
778   const int num_batches = 13;
779   const int input_depth = 2;
780   const int input_planes = 8;
781   const int input_rows = 7;
782   const int input_cols = 9;
783   const int output_depth = 3;
784   const int patch_planes = 3;
785   const int patch_rows = 3;
786   const int patch_cols = 2;
787 
788   const int stride_planes = 2;
789   const int stride_cols = 3;
790   const int stride_rows = 1;
791 
792   const int output_rows = ceil_div(input_rows - patch_rows + 1, stride_rows);
793   const int output_cols = ceil_div(input_cols - patch_cols + 1, stride_cols);
794   const int output_planes =
795       ceil_div(input_planes - patch_planes + 1, stride_planes);
796 
797   Tensor<float, 5, RowMajor> input(num_batches, input_cols, input_rows,
798                                    input_planes, input_depth);
799   Tensor<float, 5, RowMajor> kernel_backward(
800       patch_cols, patch_rows, patch_planes, input_depth, output_depth);
801   Tensor<float, 5, RowMajor> output_backward(
802       num_batches, output_cols, output_rows, output_planes, output_depth);
803 
804   output_backward = output_backward.constant(11.0f) + output_backward.random();
805   input = input.constant(2.0f) + input.random();
806   kernel_backward.setRandom();
807 
808   kernel_backward = CuboidConvolutionBackwardKernel(
809       input, output_backward, patch_planes, patch_rows, patch_cols,
810       stride_planes, stride_rows, stride_cols);
811 
812   EXPECT_EQ(kernel_backward.dimension(4), output_depth);
813   EXPECT_EQ(kernel_backward.dimension(3), input_depth);
814   EXPECT_EQ(kernel_backward.dimension(2), patch_planes);
815   EXPECT_EQ(kernel_backward.dimension(1), patch_rows);
816   EXPECT_EQ(kernel_backward.dimension(0), patch_cols);
817 
818   for (int od = 0; od < output_depth; ++od) {
819     for (int id = 0; id < input_depth; ++id) {
820       for (int p = 0; p < patch_planes; ++p) {
821         for (int c = 0; c < patch_cols; ++c) {
822           for (int r = 0; r < patch_rows; ++r) {
823             float expected = 0.0f;
824             for (int b = 0; b < num_batches; ++b) {
825               for (int i = 0; i < input_planes; ++i) {
826                 for (int j = 0; j < input_rows; ++j) {
827                   for (int k = 0; k < input_cols; ++k) {
828                     int output_j = j - r;
829                     int output_k = k - c;
830                     int output_i = i - p;
831                     if (output_i >= 0 &&
832                         output_i / stride_planes < output_planes &&
833                         output_j >= 0 && output_j / stride_rows < output_rows &&
834                         output_k >= 0 && output_k / stride_cols < output_cols &&
835                         output_i % stride_planes == 0 &&
836                         output_j % stride_rows == 0 &&
837                         output_k % stride_cols == 0) {
838                       expected += input(b, k, j, i, id) *
839                                   output_backward(b, output_k / stride_cols,
840                                                   output_j / stride_rows,
841                                                   output_i / stride_planes, od);
842                     }
843                   }
844                 }
845               }
846             }
847             EigenApprox(kernel_backward(c, r, p, id, od), expected);
848           }
849         }
850       }
851     }
852   }
853 }
854 
TEST(EigenBackwardSpatialConvolutionsTest,test_batched_strided_cuboid_convolution_backward_input_valid)855 TEST(EigenBackwardSpatialConvolutionsTest,
856      test_batched_strided_cuboid_convolution_backward_input_valid) {
857   const int num_batches = 13;
858   const int input_depth = 2;
859   const int input_planes = 14;
860   const int input_rows = 13;
861   const int input_cols = 15;
862   const int patch_rows = 3;
863   const int patch_cols = 2;
864   const int patch_planes = 4;
865   const int stride_rows = 3;
866   const int stride_cols = 2;
867   const int stride_planes = 3;
868   const int output_rows = ceil_div(input_rows - patch_rows + 1, stride_rows);
869   const int output_cols = ceil_div(input_cols - patch_cols + 1, stride_cols);
870   const int output_planes =
871       ceil_div(input_planes - patch_planes + 1, stride_planes);
872   const int output_depth = 5;
873 
874   Tensor<float, 5> input_backward(input_depth, input_planes, input_rows,
875                                   input_cols, num_batches);
876   Tensor<float, 5> kernel(output_depth, input_depth, patch_planes, patch_rows,
877                           patch_cols);
878   Tensor<float, 5> output_backward(output_depth, output_planes, output_rows,
879                                    output_cols, num_batches);
880 
881   output_backward = output_backward.constant(11.0f) + output_backward.random();
882   kernel = kernel.constant(2.0f) + kernel.random();
883   input_backward.setRandom();
884 
885   input_backward = CuboidConvolutionBackwardInput(
886       kernel, output_backward, input_planes, input_rows, input_cols,
887       stride_planes, stride_rows, stride_cols);
888 
889   EXPECT_EQ(input_backward.dimension(4), num_batches);
890   EXPECT_EQ(input_backward.dimension(3), input_cols);
891   EXPECT_EQ(input_backward.dimension(2), input_rows);
892   EXPECT_EQ(input_backward.dimension(1), input_planes);
893   EXPECT_EQ(input_backward.dimension(0), input_depth);
894 
895   for (int b = 0; b < num_batches; ++b) {
896     for (int id = 0; id < input_depth; ++id) {
897       for (int i = 0; i < input_planes; ++i) {
898         for (int j = 0; j < input_rows; ++j) {
899           for (int k = 0; k < input_cols; ++k) {
900             float expected = 0.0f;
901             for (int c = 0; c < patch_cols; ++c) {
902               for (int r = 0; r < patch_rows; ++r) {
903                 for (int p = 0; p < patch_planes; ++p) {
904                   for (int od = 0; od < output_depth; ++od) {
905                     int output_j = j - r;
906                     int output_k = k - c;
907                     int output_i = i - p;
908                     if (output_i >= 0 &&
909                         output_i / stride_planes < output_planes &&
910                         output_j >= 0 && output_j / stride_rows < output_rows &&
911                         output_k >= 0 && output_k / stride_cols < output_cols &&
912                         output_i % stride_planes == 0 &&
913                         output_j % stride_rows == 0 &&
914                         output_k % stride_cols == 0) {
915                       expected += output_backward(od, output_i / stride_planes,
916                                                   output_j / stride_rows,
917                                                   output_k / stride_cols, b) *
918                                   kernel(od, id, p, r, c);
919                     }
920                   }
921                 }
922               }
923             }
924             EigenApprox(input_backward(id, i, j, k, b), expected);
925           }
926         }
927       }
928     }
929   }
930 }
931 
TEST(EigenBackwardSpatialConvolutionsTest,test_batched_strided_cuboid_convolution_backward_input_valid_row_major)932 TEST(EigenBackwardSpatialConvolutionsTest,
933      test_batched_strided_cuboid_convolution_backward_input_valid_row_major) {
934   const int num_batches = 13;
935   const int input_depth = 2;
936   const int input_planes = 14;
937   const int input_rows = 13;
938   const int input_cols = 15;
939   const int patch_rows = 3;
940   const int patch_cols = 2;
941   const int patch_planes = 4;
942   const int stride_rows = 3;
943   const int stride_cols = 2;
944   const int stride_planes = 3;
945   const int output_rows = ceil_div(input_rows - patch_rows + 1, stride_rows);
946   const int output_cols = ceil_div(input_cols - patch_cols + 1, stride_cols);
947   const int output_planes =
948       ceil_div(input_planes - patch_planes + 1, stride_planes);
949   const int output_depth = 5;
950 
951   Tensor<float, 5, RowMajor> input_backward(num_batches, input_cols, input_rows,
952                                             input_planes, input_depth);
953   Tensor<float, 5, RowMajor> kernel(patch_cols, patch_rows, patch_planes,
954                                     input_depth, output_depth);
955   Tensor<float, 5, RowMajor> output_backward(
956       num_batches, output_cols, output_rows, output_planes, output_depth);
957 
958   output_backward = output_backward.constant(11.0f) + output_backward.random();
959   kernel = kernel.constant(2.0f) + kernel.random();
960   input_backward.setRandom();
961 
962   input_backward = CuboidConvolutionBackwardInput(
963       kernel, output_backward, input_planes, input_rows, input_cols,
964       stride_planes, stride_rows, stride_cols);
965 
966   EXPECT_EQ(input_backward.dimension(0), num_batches);
967   EXPECT_EQ(input_backward.dimension(1), input_cols);
968   EXPECT_EQ(input_backward.dimension(2), input_rows);
969   EXPECT_EQ(input_backward.dimension(3), input_planes);
970   EXPECT_EQ(input_backward.dimension(4), input_depth);
971 
972   for (int b = 0; b < num_batches; ++b) {
973     for (int id = 0; id < input_depth; ++id) {
974       for (int i = 0; i < input_planes; ++i) {
975         for (int j = 0; j < input_rows; ++j) {
976           for (int k = 0; k < input_cols; ++k) {
977             float expected = 0.0f;
978             for (int c = 0; c < patch_cols; ++c) {
979               for (int r = 0; r < patch_rows; ++r) {
980                 for (int p = 0; p < patch_planes; ++p) {
981                   for (int od = 0; od < output_depth; ++od) {
982                     int output_j = j - r;
983                     int output_k = k - c;
984                     int output_i = i - p;
985                     if (output_i >= 0 &&
986                         output_i / stride_planes < output_planes &&
987                         output_j >= 0 && output_j / stride_rows < output_rows &&
988                         output_k >= 0 && output_k / stride_cols < output_cols &&
989                         output_i % stride_planes == 0 &&
990                         output_j % stride_rows == 0 &&
991                         output_k % stride_cols == 0) {
992                       expected +=
993                           output_backward(b, output_k / stride_cols,
994                                           output_j / stride_rows,
995                                           output_i / stride_planes, od) *
996                           kernel(c, r, p, id, od);
997                     }
998                   }
999                 }
1000               }
1001             }
1002             EigenApprox(input_backward(b, k, j, i, id), expected);
1003           }
1004         }
1005       }
1006     }
1007   }
1008 }
1009 
1010 }  // namespace Eigen
1011