1/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7    http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations under the License.
14==============================================================================*/
15
16#include <memory>
17#include <vector>
18
19#include "tensorflow/core/framework/function_testlib.h"
20#include "tensorflow/core/framework/tensor_testutil.h"
21#include "tensorflow/core/platform/test.h"
22#include "tensorflow/core/public/session.h"
23
24namespace tensorflow {
25namespace {
26
27namespace f = test::function;
28using FDH = FunctionDefHelper;
29
30std::unique_ptr<Session> NewSession() {
31  SessionOptions opts;
32  (*opts.config.mutable_device_count())["CPU"] = 1;
33  return std::unique_ptr<Session>(NewSession(opts));
34}
35
36std::vector<Tensor> PackGrad(const Tensor& x0, const Tensor& x1,
37                             const Tensor& dy, int axis) {
38  auto T = DT_FLOAT;
39  auto gdef = test::function::GDef(
40      {f::NDef("x0", "Placeholder", {}, {{"dtype", T}}),
41       f::NDef("x1", "Placeholder", {}, {{"dtype", T}}),
42       f::NDef("axis", "Placeholder", {}, {{"dtype", DT_INT32}}),
43       f::NDef("dy", "Placeholder", {}, {{"dtype", T}}),
44       f::NDef("dx", "SymbolicGradient", {"x0", "x1", "dy"},
45               {{"f", FDH::FunctionRef("Pack",
46                                       {{"N", 2}, {"T", T}, {"axis", axis}})},
47                {"Tin", DataTypeSlice{T, T, T}},
48                {"Tout", DataTypeSlice{T, T}}})});
49  VLOG(1) << DebugStringWhole(gdef);
50  auto sess = NewSession();
51  TF_CHECK_OK(sess->Create(gdef));
52  std::vector<Tensor> out;
53  TF_CHECK_OK(sess->Run({{"x0:0", x0},
54                         {"x1:0", x1},
55                         {"axis:0", test::AsScalar(axis)},
56                         {"dy:0", dy}},
57                        {"dx:0", "dx:1"}, {}, &out));
58  CHECK_EQ(out.size(), 2);
59  TF_CHECK_OK(sess->Close());
60  return out;
61}
62
63TEST(ArrayGradTest, PackGrad) {
64  Tensor x0(DT_FLOAT, {2, 3});
65  x0.flat<float>().setZero();
66  Tensor x1(DT_FLOAT, {2, 3});
67  x1.flat<float>().setZero();
68  Tensor dy(DT_FLOAT, {2, 2, 3});
69  test::FillIota<float>(&dy, 0);
70  auto dx = PackGrad(x0, x1, dy, 0);
71  test::ExpectClose(dx[0],
72                    test::AsTensor<float>({0., 1., 2., 3., 4., 5.}, {2, 3}));
73  test::ExpectClose(dx[1],
74                    test::AsTensor<float>({6., 7., 8., 9., 10., 11.}, {2, 3}));
75}
76
77std::vector<Tensor> UnpackGrad(const Tensor& x, const Tensor& dy0,
78                               const Tensor& dy1, int axis) {
79  auto T = DT_FLOAT;
80  auto gdef = test::function::GDef(
81      {f::NDef("x", "Placeholder", {}, {{"dtype", T}}),
82       f::NDef("axis", "Placeholder", {}, {{"dtype", DT_INT32}}),
83       f::NDef("dy0", "Placeholder", {}, {{"dtype", T}}),
84       f::NDef("dy1", "Placeholder", {}, {{"dtype", T}}),
85       f::NDef("dx", "SymbolicGradient", {"x", "dy0", "dy1"},
86               {{"f", FDH::FunctionRef("Unpack",
87                                       {{"num", 2}, {"T", T}, {"axis", axis}})},
88                {"Tin", DataTypeSlice{T, T, T}},
89                {"Tout", DataTypeSlice{T}}})});
90  VLOG(1) << DebugStringWhole(gdef);
91  auto sess = NewSession();
92  TF_CHECK_OK(sess->Create(gdef));
93  std::vector<Tensor> out;
94  TF_CHECK_OK(sess->Run({{"x:0", x},
95                         {"axis:0", test::AsScalar(axis)},
96                         {"dy0:0", dy0},
97                         {"dy1:0", dy1}},
98                        {"dx:0"}, {}, &out));
99  CHECK_EQ(out.size(), 1);
100  TF_CHECK_OK(sess->Close());
101  return out;
102}
103
104TEST(ArrayGradTest, UnpackGrad) {
105  Tensor x(DT_FLOAT, {2, 2, 3});
106  x.flat<float>().setZero();
107  Tensor dy0(DT_FLOAT, {2, 3});
108  Tensor dy1(DT_FLOAT, {2, 3});
109  test::FillIota<float>(&dy0, 0);
110  test::FillIota<float>(&dy1, 100);
111  auto dx = UnpackGrad(x, dy0, dy1, 0);
112  test::ExpectClose(dx[0], test::AsTensor<float>({0., 1., 2., 3., 4., 5., 100.,
113                                                  101., 102., 103., 104., 105.},
114                                                 {2, 2, 3}));
115}
116
117std::vector<Tensor> ConcatGrad(int dim, const Tensor& x0, const Tensor& x1,
118                               const Tensor& dy) {
119  auto T = DT_FLOAT;
120  auto gdef = test::function::GDef(
121      {f::NDef("dim", "Placeholder", {}, {{"dtype", DT_INT32}}),
122       f::NDef("x0", "Placeholder", {}, {{"dtype", T}}),
123       f::NDef("x1", "Placeholder", {}, {{"dtype", T}}),
124       f::NDef("dy", "Placeholder", {}, {{"dtype", T}}),
125       f::NDef("dx", "SymbolicGradient", {"dim", "x0", "x1", "dy"},
126               {{"f", FDH::FunctionRef("Concat", {{"N", 2}, {"T", T}})},
127                {"Tin", DataTypeSlice{DT_INT32, T, T, T}},
128                {"Tout", DataTypeSlice{DT_INT32, T, T}}})});
129  VLOG(1) << DebugStringWhole(gdef);
130  auto sess = NewSession();
131  TF_CHECK_OK(sess->Create(gdef));
132  std::vector<Tensor> out;
133  TF_CHECK_OK(sess->Run(
134      {{"dim", test::AsScalar(dim)}, {"x0:0", x0}, {"x1:0", x1}, {"dy:0", dy}},
135      {"dx:0", "dx:1", "dx:2"}, {}, &out));
136  CHECK_EQ(out.size(), 3);
137  TF_CHECK_OK(sess->Close());
138  return out;
139}
140
141std::vector<Tensor> ConcatGradV2(int dim, const Tensor& x0, const Tensor& x1,
142                                 const Tensor& dy) {
143  auto T = DT_FLOAT;
144  auto gdef = test::function::GDef(
145      {f::NDef("x0", "Placeholder", {}, {{"dtype", T}}),
146       f::NDef("x1", "Placeholder", {}, {{"dtype", T}}),
147       f::NDef("dim", "Placeholder", {}, {{"dtype", DT_INT32}}),
148       f::NDef("dy", "Placeholder", {}, {{"dtype", T}}),
149       f::NDef("dx", "SymbolicGradient", {"x0", "x1", "dim", "dy"},
150               {{"f", FDH::FunctionRef("ConcatV2", {{"N", 2}, {"T", T}})},
151                {"Tin", DataTypeSlice{T, T, DT_INT32, T}},
152                {"Tout", DataTypeSlice{T, T, DT_INT32}}})});
153  VLOG(1) << DebugStringWhole(gdef);
154  auto sess = NewSession();
155  TF_CHECK_OK(sess->Create(gdef));
156  std::vector<Tensor> out;
157  TF_CHECK_OK(sess->Run(
158      {{"x0:0", x0}, {"x1:0", x1}, {"dim", test::AsScalar(dim)}, {"dy:0", dy}},
159      {"dx:0", "dx:1", "dx:2"}, {}, &out));
160  CHECK_EQ(out.size(), 3);
161  TF_CHECK_OK(sess->Close());
162  return out;
163}
164
165TEST(ArrayGradTest, ConcatGrad) {
166  Tensor x0(DT_FLOAT, {2, 3, 5});
167  x0.flat<float>().setZero();
168  Tensor x1(DT_FLOAT, {2, 1, 5});
169  x1.flat<float>().setZero();
170  Tensor dy(DT_FLOAT, {2, 4, 5});
171  test::FillIota<float>(&dy, 0);
172
173  // Test Concat.
174  auto dx = ConcatGrad(1, x0, x1, dy);
175  test::ExpectTensorEqual<int32>(dx[0], test::AsScalar(0));
176  test::ExpectClose(
177      dx[1],
178      test::AsTensor<float>({0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.,
179                             10., 11., 12., 13., 14., 20., 21., 22., 23., 24.,
180                             25., 26., 27., 28., 29., 30., 31., 32., 33., 34.},
181                            {2, 3, 5}));
182  test::ExpectClose(dx[2], test::AsTensor<float>({15., 16., 17., 18., 19., 35.,
183                                                  36., 37., 38., 39.},
184                                                 {2, 1, 5}));
185
186  // Test ConcatV2 with positive concat axis.
187  dx = ConcatGradV2(1, x0, x1, dy);
188  test::ExpectTensorEqual<int32>(dx[dx.size() - 1], test::AsScalar(0));
189  test::ExpectClose(
190      dx[0],
191      test::AsTensor<float>({0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.,
192                             10., 11., 12., 13., 14., 20., 21., 22., 23., 24.,
193                             25., 26., 27., 28., 29., 30., 31., 32., 33., 34.},
194                            {2, 3, 5}));
195  test::ExpectClose(dx[1], test::AsTensor<float>({15., 16., 17., 18., 19., 35.,
196                                                  36., 37., 38., 39.},
197                                                 {2, 1, 5}));
198
199  // Test ConcatV2 with negative concat axis.
200  dx = ConcatGradV2(-2, x0, x1, dy);
201  test::ExpectTensorEqual<int32>(dx[dx.size() - 1], test::AsScalar(0));
202  test::ExpectClose(
203      dx[0],
204      test::AsTensor<float>({0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.,
205                             10., 11., 12., 13., 14., 20., 21., 22., 23., 24.,
206                             25., 26., 27., 28., 29., 30., 31., 32., 33., 34.},
207                            {2, 3, 5}));
208  test::ExpectClose(dx[1], test::AsTensor<float>({15., 16., 17., 18., 19., 35.,
209                                                  36., 37., 38., 39.},
210                                                 {2, 1, 5}));
211}
212
213std::vector<Tensor> SplitGrad(int dim, const Tensor& x, const Tensor& dy0,
214                              const Tensor& dy1) {
215  auto T = DT_FLOAT;
216  auto gdef = test::function::GDef(
217      {f::NDef("dim", "Placeholder", {}, {{"dtype", DT_INT32}}),
218       f::NDef("x", "Placeholder", {}, {{"dtype", T}}),
219       f::NDef("dy0", "Placeholder", {}, {{"dtype", T}}),
220       f::NDef("dy1", "Placeholder", {}, {{"dtype", T}}),
221       f::NDef("dx", "SymbolicGradient", {"dim", "x", "dy0", "dy1"},
222               {{"f", FDH::FunctionRef(
223                          "Split",
224                          {{"split_dim", dim}, {"num_split", 2}, {"T", T}})},
225                {"Tin", DataTypeSlice{DT_INT32, T, T, T}},
226                {"Tout", DataTypeSlice{DT_INT32, T}}})});
227  VLOG(1) << DebugStringWhole(gdef);
228  auto sess = NewSession();
229  TF_CHECK_OK(sess->Create(gdef));
230  std::vector<Tensor> out;
231  TF_CHECK_OK(sess->Run({{"dim", test::AsScalar(dim)},
232                         {"x:0", x},
233                         {"dy0:0", dy0},
234                         {"dy1:0", dy1}},
235                        {"dx:0", "dx:1"}, {}, &out));
236  CHECK_EQ(out.size(), 2);
237  TF_CHECK_OK(sess->Close());
238  return out;
239}
240
241TEST(ArrayGradTest, SplitGrad) {
242  Tensor x(DT_FLOAT, {2, 4, 5});
243  x.flat<float>().setZero();
244  Tensor dy0(DT_FLOAT, {2, 2, 5});
245  Tensor dy1(DT_FLOAT, {2, 2, 5});
246  test::FillIota<float>(&dy0, 0);
247  test::FillIota<float>(&dy1, 100);
248  auto dx = SplitGrad(1, x, dy0, dy1);
249  test::ExpectTensorEqual<int32>(dx[0], test::AsScalar(0));
250  test::ExpectClose(
251      dx[1], test::AsTensor<float>(
252                 {0.,   1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,
253                  100., 101., 102., 103., 104., 105., 106., 107., 108., 109.,
254                  10.,  11.,  12.,  13.,  14.,  15.,  16.,  17.,  18.,  19.,
255                  110., 111., 112., 113., 114., 115., 116., 117., 118., 119.},
256                 {2, 4, 5}));
257}
258
259std::vector<Tensor> ReshapeGrad(const Tensor& x, const Tensor& s,
260                                const Tensor& dy) {
261  auto T = DT_FLOAT;
262  auto gdef = test::function::GDef(
263      {f::NDef("x", "Placeholder", {}, {{"dtype", T}}),
264       f::NDef("s", "Placeholder", {}, {{"dtype", DT_INT32}}),
265       f::NDef("dy", "Placeholder", {}, {{"dtype", T}}),
266       f::NDef("dx", "SymbolicGradient", {"x", "s", "dy"},
267               {{"f", FDH::FunctionRef("Reshape", {{"T", T}})},
268                {"Tin", DataTypeSlice{T, DT_INT32, T}},
269                {"Tout", DataTypeSlice{T, DT_INT32}}})});
270  VLOG(1) << DebugStringWhole(gdef);
271  auto sess = NewSession();
272  TF_CHECK_OK(sess->Create(gdef));
273  std::vector<Tensor> out;
274  TF_CHECK_OK(sess->Run({{"x:0", x}, {"s:0", s}, {"dy:0", dy}},
275                        {"dx:0", "dx:1"}, {}, &out));
276  CHECK_EQ(out.size(), 2);
277  TF_CHECK_OK(sess->Close());
278  return out;
279}
280
281TEST(ArrayGradTest, ReshapeGrad) {
282  Tensor x(DT_FLOAT, {2, 4, 5});
283  x.flat<float>().setZero();
284  auto s = test::AsTensor<int32>({8, 5});
285  Tensor dy(DT_FLOAT, {8, 5});
286  test::FillIota<float>(&dy, 73);
287  auto dx = ReshapeGrad(x, s, dy);
288  test::ExpectClose(
289      dx[0], test::AsTensor<float>(
290                 {73.,  74.,  75.,  76.,  77.,  78.,  79.,  80.,  81.,  82.,
291                  83.,  84.,  85.,  86.,  87.,  88.,  89.,  90.,  91.,  92.,
292                  93.,  94.,  95.,  96.,  97.,  98.,  99.,  100., 101., 102.,
293                  103., 104., 105., 106., 107., 108., 109., 110., 111., 112.},
294                 {2, 4, 5}));
295  test::ExpectTensorEqual<int32>(dx[1], test::AsTensor<int32>({0, 0}));
296}
297
298std::vector<Tensor> ExpandDimsGrad(const Tensor& x, const Tensor& s,
299                                   const Tensor& dy) {
300  auto T = DT_FLOAT;
301  auto gdef = test::function::GDef(
302      {f::NDef("x", "Placeholder", {}, {{"dtype", T}}),
303       f::NDef("s", "Placeholder", {}, {{"dtype", DT_INT32}}),
304       f::NDef("dy", "Placeholder", {}, {{"dtype", T}}),
305       f::NDef("dx", "SymbolicGradient", {"x", "s", "dy"},
306               {{"f", FDH::FunctionRef("ExpandDims", {{"T", T}})},
307                {"Tin", DataTypeSlice{T, DT_INT32, T}},
308                {"Tout", DataTypeSlice{T, DT_INT32}}})});
309  VLOG(1) << DebugStringWhole(gdef);
310  auto sess = NewSession();
311  TF_CHECK_OK(sess->Create(gdef));
312  std::vector<Tensor> out;
313  TF_CHECK_OK(sess->Run({{"x:0", x}, {"s:0", s}, {"dy:0", dy}},
314                        {"dx:0", "dx:1"}, {}, &out));
315  CHECK_EQ(out.size(), 2);
316  TF_CHECK_OK(sess->Close());
317  return out;
318}
319
320TEST(ArrayGradTest, ExpandDimsGrad) {
321  Tensor x(DT_FLOAT, {2, 4, 5});
322  x.flat<float>().setZero();
323  auto s = test::AsTensor<int32>({1});
324  Tensor dy(DT_FLOAT, {2, 1, 4, 5});
325  test::FillIota<float>(&dy, 73);
326  auto dx = ExpandDimsGrad(x, s, dy);
327  test::ExpectClose(
328      dx[0], test::AsTensor<float>(
329                 {73.,  74.,  75.,  76.,  77.,  78.,  79.,  80.,  81.,  82.,
330                  83.,  84.,  85.,  86.,  87.,  88.,  89.,  90.,  91.,  92.,
331                  93.,  94.,  95.,  96.,  97.,  98.,  99.,  100., 101., 102.,
332                  103., 104., 105., 106., 107., 108., 109., 110., 111., 112.},
333                 {2, 4, 5}));
334  test::ExpectTensorEqual<int32>(dx[1], test::AsTensor<int32>({0}));
335}
336
337std::vector<Tensor> SqueezeGrad(const Tensor& x, const Tensor& dy) {
338  auto T = DT_FLOAT;
339  auto gdef = test::function::GDef(
340      {f::NDef("x", "Placeholder", {}, {{"dtype", T}}),
341       f::NDef("dy", "Placeholder", {}, {{"dtype", T}}),
342       f::NDef("dx", "SymbolicGradient", {"x", "dy"},
343               {{"f", FDH::FunctionRef("Squeeze", {{"T", T}})},
344                {"Tin", DataTypeSlice{T, T}},
345                {"Tout", DataTypeSlice{T}}})});
346  VLOG(1) << DebugStringWhole(gdef);
347  auto sess = NewSession();
348  TF_CHECK_OK(sess->Create(gdef));
349  std::vector<Tensor> out;
350  TF_CHECK_OK(sess->Run({{"x:0", x}, {"dy:0", dy}}, {"dx:0"}, {}, &out));
351  CHECK_EQ(out.size(), 1);
352  TF_CHECK_OK(sess->Close());
353  return out;
354}
355
356TEST(ArrayGradTest, SqueezeGrad) {
357  Tensor x(DT_FLOAT, {2, 1, 3});
358  x.flat<float>().setZero();
359  Tensor dy(DT_FLOAT, {2, 3});
360  test::FillIota<float>(&dy, 1);
361  auto dx = SqueezeGrad(x, dy);
362  test::ExpectClose(dx[0],
363                    test::AsTensor<float>({1., 2., 3., 4., 5., 6.}, {2, 1, 3}));
364}
365
366std::vector<Tensor> TransposeGrad(const Tensor& x, const Tensor& p,
367                                  const Tensor& dy) {
368  auto T = DT_FLOAT;
369  auto gdef = test::function::GDef(
370      {f::NDef("x", "Placeholder", {}, {{"dtype", T}}),
371       f::NDef("p", "Placeholder", {}, {{"dtype", DT_INT32}}),
372       f::NDef("dy", "Placeholder", {}, {{"dtype", T}}),
373       f::NDef("dx", "SymbolicGradient", {"x", "p", "dy"},
374               {{"f", FDH::FunctionRef("Transpose", {{"T", T}})},
375                {"Tin", DataTypeSlice{T, DT_INT32, T}},
376                {"Tout", DataTypeSlice{T, DT_INT32}}})});
377  VLOG(1) << DebugStringWhole(gdef);
378  auto sess = NewSession();
379  TF_CHECK_OK(sess->Create(gdef));
380  std::vector<Tensor> out;
381  TF_CHECK_OK(sess->Run({{"x:0", x}, {"p:0", p}, {"dy:0", dy}},
382                        {"dx:0", "dx:1"}, {}, &out));
383  CHECK_EQ(out.size(), 2);
384  TF_CHECK_OK(sess->Close());
385  return out;
386}
387
388TEST(ArrayGradTest, TransposeGrad) {
389  Tensor x(DT_FLOAT, {2, 4, 5});
390  x.flat<float>().setZero();
391  auto p = test::AsTensor<int32>({2, 0, 1});
392  Tensor dy(DT_FLOAT, {5, 2, 4});
393  test::FillIota<float>(&dy, 0);
394  auto dx = TransposeGrad(x, p, dy);
395  test::ExpectClose(dx[0], test::AsTensor<float>(
396                               {0., 8.,  16., 24., 32., 1., 9.,  17., 25., 33.,
397                                2., 10., 18., 26., 34., 3., 11., 19., 27., 35.,
398                                4., 12., 20., 28., 36., 5., 13., 21., 29., 37.,
399                                6., 14., 22., 30., 38., 7., 15., 23., 31., 39.},
400                               {2, 4, 5}));
401  test::ExpectTensorEqual<int32>(dx[1], test::AsTensor<int32>({0, 0, 0}));
402}
403
404std::vector<Tensor> ReverseGrad(const Tensor& x, const Tensor& dims,
405                                const Tensor& dy) {
406  auto T = DT_FLOAT;
407  auto gdef = test::function::GDef(
408      {f::NDef("x", "Placeholder", {}, {{"dtype", T}}),
409       f::NDef("dims", "Placeholder", {}, {{"dtype", DT_BOOL}}),
410       f::NDef("dy", "Placeholder", {}, {{"dtype", T}}),
411       f::NDef("dx", "SymbolicGradient", {"x", "dims", "dy"},
412               {{"f", FDH::FunctionRef("Reverse", {{"T", T}})},
413                {"Tin", DataTypeSlice{T, DT_BOOL, T}},
414                {"Tout", DataTypeSlice{T, DT_BOOL}}})});
415  VLOG(1) << DebugStringWhole(gdef);
416  auto sess = NewSession();
417  TF_CHECK_OK(sess->Create(gdef));
418  std::vector<Tensor> out;
419  TF_CHECK_OK(sess->Run({{"x:0", x}, {"dims:0", dims}, {"dy:0", dy}},
420                        {"dx:0", "dx:1"}, {}, &out));
421  CHECK_EQ(out.size(), 2);
422  TF_CHECK_OK(sess->Close());
423  return out;
424}
425
426TEST(ArrayGradTest, ReverseGrad) {
427  Tensor x(DT_FLOAT, {2, 3});
428  x.flat<float>().setZero();
429  auto dims = test::AsTensor<bool>({false, true});
430  Tensor dy(DT_FLOAT, {2, 3});
431  test::FillIota<float>(&dy, 1);
432  auto dx = ReverseGrad(x, dims, dy);
433  test::ExpectClose(dx[0],
434                    test::AsTensor<float>({3., 2., 1., 6., 5., 4.}, {2, 3}));
435  test::ExpectTensorEqual<bool>(dx[1], test::AsTensor<bool>({false, false}));
436}
437
438std::vector<Tensor> ReverseV2Grad(const Tensor& x, const Tensor& axis,
439                                  const Tensor& dy) {
440  auto T = DT_FLOAT;
441  auto Tidx = DT_INT32;
442  auto gdef = test::function::GDef(
443      {f::NDef("x", "Placeholder", {}, {{"dtype", T}}),
444       f::NDef("axis", "Placeholder", {}, {{"dtype", DT_INT32}}),
445       f::NDef("dy", "Placeholder", {}, {{"dtype", T}}),
446       f::NDef(
447           "dx", "SymbolicGradient", {"x", "axis", "dy"},
448           {{"f", FDH::FunctionRef("ReverseV2", {{"T", T}, {"Tidx", Tidx}})},
449            {"Tin", DataTypeSlice{T, DT_INT32, T}},
450            {"Tout", DataTypeSlice{T, DT_INT32}}})});
451  VLOG(1) << DebugStringWhole(gdef);
452  auto sess = NewSession();
453  TF_CHECK_OK(sess->Create(gdef));
454  std::vector<Tensor> out;
455  TF_CHECK_OK(sess->Run({{"x:0", x}, {"axis:0", axis}, {"dy:0", dy}},
456                        {"dx:0", "dx:1"}, {}, &out));
457  CHECK_EQ(out.size(), 2);
458  TF_CHECK_OK(sess->Close());
459  return out;
460}
461
462TEST(ArrayGradTest, ReverseV2Grad) {
463  Tensor x(DT_FLOAT, {2, 3});
464  x.flat<float>().setZero();
465  auto axis = test::AsTensor<int32>({1});
466  Tensor dy(DT_FLOAT, {2, 3});
467  test::FillIota<float>(&dy, 1);
468  auto dx = ReverseV2Grad(x, axis, dy);
469  test::ExpectTensorEqual<float>(
470      dx[0], test::AsTensor<float>({3., 2., 1., 6., 5., 4.}, {2, 3}));
471  test::ExpectTensorEqual<int32>(dx[1], test::AsTensor<int32>({0}));
472}
473
474std::vector<Tensor> SliceGrad(const Tensor& x, const Tensor& b, const Tensor& s,
475                              const Tensor& dy) {
476  auto T = DT_FLOAT;
477  auto gdef = test::function::GDef(
478      {f::NDef("x", "Placeholder", {}, {{"dtype", T}}),
479       f::NDef("b", "Placeholder", {}, {{"dtype", DT_INT32}}),
480       f::NDef("s", "Placeholder", {}, {{"dtype", DT_INT32}}),
481       f::NDef("dy", "Placeholder", {}, {{"dtype", T}}),
482       f::NDef(
483           "dx", "SymbolicGradient", {"x", "b", "s", "dy"},
484           {{"f", FDH::FunctionRef("Slice", {{"T", T}, {"Index", DT_INT32}})},
485            {"Tin", DataTypeSlice{T, DT_INT32, DT_INT32, T}},
486            {"Tout", DataTypeSlice{T, DT_INT32, DT_INT32}}})});
487  VLOG(1) << DebugStringWhole(gdef);
488  auto sess = NewSession();
489  TF_CHECK_OK(sess->Create(gdef));
490  std::vector<Tensor> out;
491  TF_CHECK_OK(sess->Run({{"x:0", x}, {"b:0", b}, {"s:0", s}, {"dy:0", dy}},
492                        {"dx:0", "dx:1", "dx:2"}, {}, &out));
493  CHECK_EQ(out.size(), 3);
494  TF_CHECK_OK(sess->Close());
495  return out;
496}
497
498TEST(ArrayGradTest, SliceGrad) {
499  Tensor x(DT_FLOAT, {2, 3, 4});
500  x.flat<float>().setZero();
501  auto begin = test::AsTensor<int32>({1, 1, 1});
502  auto size = test::AsTensor<int32>({1, 2, 2});
503  Tensor dy(DT_FLOAT, {1, 2, 2});
504  test::FillIota<float>(&dy, 1);
505  auto dx = SliceGrad(x, begin, size, dy);
506  test::ExpectClose(dx[0],
507                    test::AsTensor<float>(
508                        {
509                            0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
510                            0., 0., 0., 0., 0., 1., 2., 0., 0., 3., 4., 0.,
511                        },
512                        {2, 3, 4}));
513  test::ExpectTensorEqual<int32>(dx[1], test::AsTensor<int32>({0, 0, 0}));
514  test::ExpectTensorEqual<int32>(dx[2], test::AsTensor<int32>({0, 0, 0}));
515}
516
517std::vector<Tensor> StridedSliceGrad(const Tensor& x, const Tensor& begin,
518                                     const Tensor& end, const Tensor& strides,
519                                     const Tensor& dy, int32 begin_mask,
520                                     int32 end_mask, int32 ellipsis_mask,
521                                     int32 new_axis_mask,
522                                     int32 shrink_axis_mask) {
523  auto T = DT_FLOAT;
524  auto gdef = test::function::GDef(
525      {f::NDef("x", "Placeholder", {}, {{"dtype", T}}),
526       f::NDef("begin", "Placeholder", {}, {{"dtype", DT_INT32}}),
527       f::NDef("end", "Placeholder", {}, {{"dtype", DT_INT32}}),
528       f::NDef("strides", "Placeholder", {}, {{"dtype", DT_INT32}}),
529       f::NDef("dy", "Placeholder", {}, {{"dtype", T}}),
530       f::NDef(
531           "dx", "SymbolicGradient", {"x", "begin", "end", "strides", "dy"},
532           {{"f", FDH::FunctionRef("StridedSlice",
533                                   {
534                                       {"T", T},
535                                       {"Index", DT_INT32},
536                                       {"begin_mask", begin_mask},
537                                       {"end_mask", end_mask},
538                                       {"new_axis_mask", new_axis_mask},
539                                       {"shrink_axis_mask", shrink_axis_mask},
540                                       {"ellipsis_mask", ellipsis_mask},
541                                   })},
542            {"Tin", DataTypeSlice{T, DT_INT32, DT_INT32, DT_INT32, T}},
543            {"Tout", DataTypeSlice{T, DT_INT32, DT_INT32, DT_INT32}}})});
544  VLOG(1) << DebugStringWhole(gdef);
545  auto sess = NewSession();
546  TF_CHECK_OK(sess->Create(gdef));
547  std::vector<Tensor> out;
548  TF_CHECK_OK(sess->Run({{"x:0", x},
549                         {"begin:0", begin},
550                         {"end:0", end},
551                         {"strides:0", strides},
552                         {"dy:0", dy}},
553                        {"dx:0", "dx:1", "dx:2", "dx:3"}, {}, &out));
554  CHECK_EQ(out.size(), 4);
555  TF_CHECK_OK(sess->Close());
556  return out;
557}
558
559std::vector<Tensor> StridedSliceGradGrad(
560    const Tensor& shape, const Tensor& begin, const Tensor& end,
561    const Tensor& strides, const Tensor& dy, const Tensor& grad,
562    int32 begin_mask, int32 end_mask, int32 ellipsis_mask, int32 new_axis_mask,
563    int32 shrink_axis_mask) {
564  auto T = DT_FLOAT;
565  auto gdef = test::function::GDef(
566      {f::NDef("shape", "Placeholder", {}, {{"dtype", DT_INT32}}),
567       f::NDef("begin", "Placeholder", {}, {{"dtype", DT_INT32}}),
568       f::NDef("end", "Placeholder", {}, {{"dtype", DT_INT32}}),
569       f::NDef("strides", "Placeholder", {}, {{"dtype", DT_INT32}}),
570       f::NDef("dy", "Placeholder", {}, {{"dtype", T}}),
571       f::NDef("grad", "Placeholder", {}, {{"dtype", T}}),
572       f::NDef(
573           "dx", "SymbolicGradient",
574           {"shape", "begin", "end", "strides", "dy", "grad"},
575           {{"f", FDH::FunctionRef("StridedSliceGrad",
576                                   {
577                                       {"T", T},
578                                       {"Index", DT_INT32},
579                                       {"begin_mask", begin_mask},
580                                       {"end_mask", end_mask},
581                                       {"new_axis_mask", new_axis_mask},
582                                       {"shrink_axis_mask", shrink_axis_mask},
583                                       {"ellipsis_mask", ellipsis_mask},
584                                   })},
585            {"Tin",
586             DataTypeSlice{DT_INT32, DT_INT32, DT_INT32, DT_INT32, T, T}},
587            {"Tout",
588             DataTypeSlice{DT_INT32, DT_INT32, DT_INT32, DT_INT32, T}}})});
589  VLOG(1) << DebugStringWhole(gdef);
590  auto sess = NewSession();
591  TF_CHECK_OK(sess->Create(gdef));
592  std::vector<Tensor> out;
593  TF_CHECK_OK(sess->Run({{"shape:0", shape},
594                         {"begin:0", begin},
595                         {"end:0", end},
596                         {"strides:0", strides},
597                         {"dy:0", dy},
598                         {"grad:0", grad}},
599                        {"dx:0", "dx:1", "dx:2", "dx:3", "dx:4"}, {}, &out));
600  CHECK_EQ(out.size(), 5);
601  TF_CHECK_OK(sess->Close());
602  return out;
603}
604
605TEST(ArrayGradTest, StridedSliceGrad) {
606  Tensor x(DT_FLOAT, {2, 3, 4});
607  x.flat<float>().setZero();
608  Tensor x_shape = test::AsTensor<int32>({2, 3, 4}, {3});
609
610  {
611    auto start = test::AsTensor<int32>({1, 1, 1});
612    auto stop = test::AsTensor<int32>({2, 3, 3});
613    auto strides = test::AsTensor<int32>({1, 1, 1});
614    Tensor dy(DT_FLOAT, {1, 2, 2});
615    test::FillIota<float>(&dy, 1);
616    int begin_mask = 0, end_mask = 0, new_axis_mask = 0, shrink_axis_mask = 0,
617        ellipsis_mask = 0;
618    auto dx =
619        StridedSliceGrad(x, start, stop, strides, dy, begin_mask, end_mask,
620                         ellipsis_mask, new_axis_mask, shrink_axis_mask);
621    test::ExpectClose(dx[0],
622                      test::AsTensor<float>(
623                          {
624                              0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
625                              0., 0., 0., 0., 0., 1., 2., 0., 0., 3., 4., 0.,
626                          },
627                          {2, 3, 4}));
628    test::ExpectTensorEqual<int32>(dx[1], test::AsTensor<int32>({0, 0, 0}));
629    test::ExpectTensorEqual<int32>(dx[2], test::AsTensor<int32>({0, 0, 0}));
630    auto ddx = StridedSliceGradGrad(x_shape, start, stop, strides, dy, dx[0],
631                                    begin_mask, end_mask, ellipsis_mask,
632                                    new_axis_mask, shrink_axis_mask);
633    test::ExpectClose(ddx[4], dy);
634  }
635
636  // test equivalent of python tf.gradients(foo[1:2, 1:3, 1:3])
637  {
638    auto start = test::AsTensor<int32>({1, 1, 1});
639    auto stop = test::AsTensor<int32>({2, 3, 3});
640    auto strides = test::AsTensor<int32>({1, 1, 1});
641    Tensor dy(DT_FLOAT, {1, 2, 2});
642    test::FillIota<float>(&dy, 1);
643    int begin_mask = 0, end_mask = 0, new_axis_mask = 0, shrink_axis_mask = 0,
644        ellipsis_mask = 0;
645    auto dx =
646        StridedSliceGrad(x, start, stop, strides, dy, begin_mask, end_mask,
647                         ellipsis_mask, new_axis_mask, shrink_axis_mask);
648    test::ExpectClose(dx[0],
649                      test::AsTensor<float>(
650                          {
651                              0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
652                              0., 0., 0., 0., 0., 1., 2., 0., 0., 3., 4., 0.,
653                          },
654                          {2, 3, 4}));
655    test::ExpectTensorEqual<int32>(dx[1], test::AsTensor<int32>({0, 0, 0}));
656    test::ExpectTensorEqual<int32>(dx[2], test::AsTensor<int32>({0, 0, 0}));
657    auto ddx = StridedSliceGradGrad(x_shape, start, stop, strides, dy, dx[0],
658                                    begin_mask, end_mask, ellipsis_mask,
659                                    new_axis_mask, shrink_axis_mask);
660    test::ExpectClose(ddx[4], dy);
661  }
662
663  // test equivalent of python tf.gradients(foo[1, 1:, :-2, None])
664  {
665    int dontcare = 66;
666    auto start = test::AsTensor<int32>({1, 1, dontcare, dontcare});
667    auto stop = test::AsTensor<int32>({2, dontcare, -2, dontcare});
668    auto strides = test::AsTensor<int32>({1, 1, 1, dontcare});
669    Tensor dy(DT_FLOAT, {2, 2, 1});
670    test::FillIota<float>(&dy, 1);
671    int begin_mask = 4, end_mask = 2, new_axis_mask = 8, shrink_axis_mask = 1,
672        ellipsis_mask = 0;
673    auto dx =
674        StridedSliceGrad(x, start, stop, strides, dy, begin_mask, end_mask,
675                         ellipsis_mask, new_axis_mask, shrink_axis_mask);
676    test::ExpectClose(dx[0],
677                      test::AsTensor<float>(
678                          {
679                              0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
680                              0., 0., 0., 0., 1., 2., 0., 0., 3., 4., 0., 0.,
681                          },
682                          {2, 3, 4}));
683    test::ExpectTensorEqual<int32>(dx[1], test::AsTensor<int32>({0, 0, 0, 0}));
684    test::ExpectTensorEqual<int32>(dx[2], test::AsTensor<int32>({0, 0, 0, 0}));
685    auto ddx = StridedSliceGradGrad(x_shape, start, stop, strides, dy, dx[0],
686                                    begin_mask, end_mask, ellipsis_mask,
687                                    new_axis_mask, shrink_axis_mask);
688    test::ExpectClose(ddx[4], dy);
689  }
690
691  // test equivalent of tf.gradients(foo[1, ...]) i.e. foo[1, 0:3, 0:4]
692  {
693    int dontcare = 66;
694    auto start = test::AsTensor<int32>({1, dontcare});
695    auto stop = test::AsTensor<int32>({2, dontcare});
696    auto strides = test::AsTensor<int32>({1, 1});
697    Tensor dy(DT_FLOAT, {3, 4});
698    test::FillIota<float>(&dy, 1);
699    int begin_mask = 0, end_mask = 0, new_axis_mask = 0, shrink_axis_mask = 1,
700        ellipsis_mask = 2;
701    auto dx =
702        StridedSliceGrad(x, start, stop, strides, dy, begin_mask, end_mask,
703                         ellipsis_mask, new_axis_mask, shrink_axis_mask);
704    test::ExpectClose(dx[0],
705                      test::AsTensor<float>(
706                          {
707                              0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,  0.,  0.,
708                              1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.,
709                          },
710                          {2, 3, 4}));
711    test::ExpectTensorEqual<int32>(dx[1], test::AsTensor<int32>({0, 0}));
712    test::ExpectTensorEqual<int32>(dx[2], test::AsTensor<int32>({0, 0}));
713    auto ddx = StridedSliceGradGrad(x_shape, start, stop, strides, dy, dx[0],
714                                    begin_mask, end_mask, ellipsis_mask,
715                                    new_axis_mask, shrink_axis_mask);
716    test::ExpectClose(ddx[4], dy);
717  }
718}
719
720}  // namespace
721}  // namespace tensorflow
722