Home | History | Annotate | Download | only in ops
      1 /* Copyright 2016 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 <memory>
     17 #include <vector>
     18 
     19 #include "tensorflow/core/framework/function_testlib.h"
     20 #include "tensorflow/core/framework/op_kernel.h"
     21 #include "tensorflow/core/framework/tensor_testutil.h"
     22 #include "tensorflow/core/lib/strings/str_util.h"
     23 #include "tensorflow/core/platform/test.h"
     24 #include "tensorflow/core/public/session.h"
     25 
     26 namespace tensorflow {
     27 namespace {
     28 
     29 namespace f = test::function;
     30 using FDH = FunctionDefHelper;
     31 
     32 std::unique_ptr<Session> NewSession() {
     33   SessionOptions opts;
     34   (*opts.config.mutable_device_count())["CPU"] = 1;
     35   return std::unique_ptr<Session>(NewSession(opts));
     36 }
     37 
     38 class MathGradTest : public ::testing::Test {
     39  protected:
     40   // Unary
     41   // dst is the output dtype of op_node.
     42   Status Unary(const FDH::Node& op_node, const Tensor& x, const DataType dst,
     43                Tensor* y) {
     44     const DataType src = x.dtype();
     45     auto adef = [](const string& name,
     46                    const DataType type) {  // E.g., x:float, dy:double
     47       return strings::StrCat(name, ":", DataTypeString(type));
     48     };
     49     // Sum(op(x)), sum all output of op(x).
     50     auto test = FDH::Define("Test", {adef("x", src)}, {adef("l", dst)}, {},
     51                             {
     52                                 op_node,
     53                                 FDH::Const("zero", 0),
     54                                 FDH::Const("one", 1),
     55                                 {{"r"}, "Rank", {"x"}, {{"T", src}}},
     56                                 {{"indices"}, "Range", {"zero", "r", "one"}},
     57                                 {{"l"}, "Sum", {"y", "indices"}, {{"T", dst}}},
     58                             });
     59 
     60     // TestGrad = Test'(x)
     61     auto grad = FDH::Define(
     62         "TestGrad", {adef("x", src)}, {adef("dx", src)}, {},
     63         {
     64             FDH::Const("one", 1),
     65             {{"dy"}, "Cast", {"one"}, {{"DstT", dst}, {"SrcT", DT_INT32}}},
     66             {{"grad"},
     67              "SymbolicGradient",
     68              {"x", "dy"},
     69              {
     70                  {"f", FDH::FunctionRef("Test")},
     71                  {"Tin", DataTypeSlice{src, dst}},
     72                  {"Tout", DataTypeSlice{src}},
     73              }},
     74             {{"dx"}, "Identity", {"grad"}, {{"T", src}}},
     75         });
     76     // Each test case will feed in "x:0" and expects to get "dx:0".
     77     auto gdef = test::function::GDef(
     78         {
     79             f::NDef("x", "Placeholder", {}, {{"dtype", src}}),
     80             f::NDef("dx", "TestGrad", {"x"}, {}),
     81         },
     82         {test, grad});
     83 
     84     auto sess = NewSession();
     85     TF_CHECK_OK(sess->Create(gdef));
     86     std::vector<Tensor> outputs;
     87     auto s = sess->Run({{"x:0", x}}, {"dx:0"}, {}, &outputs);
     88     if (s.ok()) {
     89       CHECK_EQ(outputs.size(), 1);
     90       *y = outputs[0];
     91     }
     92     TF_CHECK_OK(sess->Close());
     93     return s;
     94   }
     95 
     96   Status Unary(const string& op, const Tensor& x, Tensor* y) {
     97     const FDH::Node op_node = {{"y"}, op, {"x"}, {{"T", x.dtype()}}};
     98     return Unary(op_node, x, x.dtype(), y);
     99   }
    100 
    101   // Unary op expecting OK.
    102   Tensor SymGrad(const string& op, const Tensor& x) {
    103     Tensor ret;
    104     TF_CHECK_OK(Unary(op, x, &ret));
    105     return ret;
    106   }
    107 
    108   Tensor SymCastGrad(const Tensor& x, const DataType dst) {
    109     Tensor ret;
    110     const FDH::Node op_node = {
    111         {"y"}, "Cast", {"x"}, {{"SrcT", x.dtype()}, {"DstT", dst}}};
    112     TF_CHECK_OK(Unary(op_node, x, dst, &ret));
    113     return ret;
    114   }
    115 
    116   // Binary
    117   void SymGrad(const string& op, const Tensor& x, const Tensor& y, Tensor* dx,
    118                Tensor* dy) {
    119     const DataType T = x.dtype();
    120     auto adef = [T](const string& name) {  // E.g., x:float, dy:double
    121       return strings::StrCat(name, ":", DataTypeString(T));
    122     };
    123     // Sum(op(x)), sum all output of op(x).
    124     auto test = FDH::Define("Test", {adef("x"), adef("y")}, {adef("l")}, {},
    125                             {
    126                                 {{"z"}, op, {"x", "y"}, {{"T", T}}},
    127                                 FDH::Const("zero", 0),
    128                                 FDH::Const("one", 1),
    129                                 {{"r"}, "Rank", {"z"}, {{"T", T}}},
    130                                 {{"indices"}, "Range", {"zero", "r", "one"}},
    131                                 {{"l"}, "Sum", {"z", "indices"}, {{"T", T}}},
    132                             });
    133 
    134     // TestGrad = Test'(x, y)
    135     auto grad = FDH::Define(
    136         "TestGrad", {adef("x"), adef("y")}, {adef("dx"), adef("dy")}, {},
    137         {
    138             FDH::Const("one", 1),
    139             {{"dz"}, "Cast", {"one"}, {{"DstT", T}, {"SrcT", DT_INT32}}},
    140             {{"grad0", "grad1"},
    141              "SymbolicGradient",
    142              {"x", "y", "dz"},
    143              {
    144                  {"f", FDH::FunctionRef("Test")},
    145                  {"Tin", DataTypeSlice{T, T, T}},
    146                  {"Tout", DataTypeSlice{T, T}},
    147              }},
    148             {{"dx"}, "Identity", {"grad0"}, {{"T", T}}},
    149             {{"dy"}, "Identity", {"grad1"}, {{"T", T}}},
    150         });
    151     // Each test case will feed in "x:0" and "y:0" and expects to get "d0" and
    152     // "d:0".
    153     auto gdef = test::function::GDef(
    154         {
    155             f::NDef("x", "Placeholder", {}, {{"dtype", T}}),
    156             f::NDef("y", "Placeholder", {}, {{"dtype", T}}),
    157             f::NDef("d", "TestGrad", {"x", "y"}, {}),
    158         },
    159         {test, grad});
    160 
    161     auto sess = NewSession();
    162     TF_CHECK_OK(sess->Create(gdef));
    163     std::vector<Tensor> outputs;
    164     TF_CHECK_OK(
    165         sess->Run({{"x:0", x}, {"y:0", y}}, {"d:0", "d:1"}, {}, &outputs));
    166     CHECK_EQ(outputs.size(), 2);
    167     TF_CHECK_OK(sess->Close());
    168     *dx = outputs[0];
    169     *dy = outputs[1];
    170   }
    171 
    172   // Reduction grad
    173   void ReductionGrad(const string& op, const Tensor& x, const Tensor& idx,
    174                      Tensor* dx, Tensor* di) {
    175     const DataType T = x.dtype();
    176     auto adef = [T](const string& name) {  // E.g., x:float, dy:double
    177       return strings::StrCat(name, ":", DataTypeString(T));
    178     };
    179     // Sum(op(x, idx)), sum all output of op(x, idx).
    180     auto test = FDH::Define("Test", {adef("x"), "i:int32"}, {adef("l")}, {},
    181                             {
    182                                 {{"y"}, op, {"x", "i"}, {{"T", T}}},
    183                                 FDH::Const("zero", 0),
    184                                 FDH::Const("one", 1),
    185                                 {{"r"}, "Rank", {"y"}, {{"T", T}}},
    186                                 {{"indices"}, "Range", {"zero", "r", "one"}},
    187                                 {{"l"}, "Sum", {"y", "indices"}, {{"T", T}}},
    188                             });
    189 
    190     // TestGrad = Test'(x)
    191     auto grad = FDH::Define(
    192         "TestGrad", {adef("x"), "i:int32"}, {adef("dx"), "di:int32"}, {},
    193         {
    194             FDH::Const("one", 1),
    195             {{"dy"}, "Cast", {"one"}, {{"DstT", T}, {"SrcT", DT_INT32}}},
    196             {{"grad0", "grad1"},
    197              "SymbolicGradient",
    198              {"x", "i", "dy"},
    199              {
    200                  {"f", FDH::FunctionRef("Test")},
    201                  {"Tin", DataTypeSlice{T, DT_INT32, T}},
    202                  {"Tout", DataTypeSlice{T, DT_INT32}},
    203              }},
    204             {{"dx"}, "Identity", {"grad0"}, {{"T", T}}},
    205             {{"di"}, "Identity", {"grad1"}, {{"T", DT_INT32}}},
    206         });
    207     // Each test case will feed in "x:0" and expects to get "dx:0".
    208     auto gdef = test::function::GDef(
    209         {
    210             f::NDef("x", "Placeholder", {}, {{"dtype", T}}),
    211             f::NDef("i", "Placeholder", {}, {{"dtype", DT_INT32}}),
    212             f::NDef("d", "TestGrad", {"x", "i"}, {}),
    213         },
    214         {test, grad});
    215 
    216     auto sess = NewSession();
    217     TF_CHECK_OK(sess->Create(gdef));
    218     std::vector<Tensor> outputs;
    219     TF_CHECK_OK(
    220         sess->Run({{"x:0", x}, {"i:0", idx}}, {"d:0", "d:1"}, {}, &outputs));
    221     CHECK_EQ(outputs.size(), 2);
    222     TF_CHECK_OK(sess->Close());
    223     *dx = outputs[0];
    224     *di = outputs[1];
    225   }
    226 
    227   Tensor MatMulCommon(const string& opname, const string& attr_adj_x,
    228                       const string& attr_adj_y, const Tensor& x, bool ax,
    229                       const Tensor& y, bool ay) {
    230     auto T = x.dtype();
    231     auto gdef = test::function::GDef(
    232         {
    233             f::NDef("x", "Placeholder", {}, {{"dtype", T}}),
    234             f::NDef("y", "Placeholder", {}, {{"dtype", T}}),
    235             f::NDef("z", opname, {"x", "y"},
    236                     {{"T", T}, {attr_adj_x, ax}, {attr_adj_y, ay}}),
    237         },
    238         {});
    239     auto sess = NewSession();
    240     TF_CHECK_OK(sess->Create(gdef));
    241     std::vector<Tensor> outputs;
    242     TF_CHECK_OK(sess->Run({{"x:0", x}, {"y:0", y}}, {"z:0"}, {}, &outputs));
    243     CHECK_EQ(outputs.size(), 1);
    244     TF_CHECK_OK(sess->Close());
    245     return outputs[0];
    246   }
    247 
    248   Tensor MatMul(const Tensor& x, bool ax, const Tensor& y, bool ay) {
    249     return MatMulCommon("MatMul", "transpose_a", "transpose_b", x, ax, y, ay);
    250   }
    251 
    252   Tensor BatchMatMul(const Tensor& x, bool ax, const Tensor& y, bool ay) {
    253     return MatMulCommon("BatchMatMul", "adj_x", "adj_y", x, ax, y, ay);
    254   }
    255 
    256   void MatMulGradCommon(const string& opname, const string& attr_adj_x,
    257                         const string& attr_adj_y, const Tensor& x, bool ax,
    258                         const Tensor& y, bool ay, Tensor* dx, Tensor* dy) {
    259     const DataType T = x.dtype();
    260     auto adef = [T](const string& name) {  // E.g., x:float, dy:double
    261       return strings::StrCat(name, ":", DataTypeString(T));
    262     };
    263     // Sum(op(x)), sum all output of op(x).
    264     auto test =
    265         FDH::Define("Test", {adef("x"), adef("y")}, {adef("l")}, {},
    266                     {
    267                         {{"z"},
    268                          opname,
    269                          {"x", "y"},
    270                          {{"T", T}, {attr_adj_x, ax}, {attr_adj_y, ay}}},
    271                         FDH::Const("zero", 0),
    272                         FDH::Const("one", 1),
    273                         {{"r"}, "Rank", {"z"}, {{"T", T}}},
    274                         {{"indices"}, "Range", {"zero", "r", "one"}},
    275                         {{"l"}, "Sum", {"z", "indices"}, {{"T", T}}},
    276                     });
    277 
    278     // TestGrad = Test'(x, y)
    279     auto grad = FDH::Define(
    280         "TestGrad", {adef("x"), adef("y")}, {adef("dx"), adef("dy")}, {},
    281         {
    282             FDH::Const("one", 1),
    283             {{"dz"}, "Cast", {"one"}, {{"DstT", T}, {"SrcT", DT_INT32}}},
    284             {{"grad0", "grad1"},
    285              "SymbolicGradient",
    286              {"x", "y", "dz"},
    287              {
    288                  {"f", FDH::FunctionRef("Test")},
    289                  {"Tin", DataTypeSlice{T, T, T}},
    290                  {"Tout", DataTypeSlice{T, T}},
    291              }},
    292             {{"dx"}, "Identity", {"grad0"}, {{"T", T}}},
    293             {{"dy"}, "Identity", {"grad1"}, {{"T", T}}},
    294         });
    295     // Each test case will feed in "x:0" and "y:0" and expects to get "d0" and
    296     // "d:0".
    297     auto gdef = test::function::GDef(
    298         {
    299             f::NDef("x", "Placeholder", {}, {{"dtype", T}}),
    300             f::NDef("y", "Placeholder", {}, {{"dtype", T}}),
    301             f::NDef("d", "TestGrad", {"x", "y"}, {}),
    302         },
    303         {test, grad});
    304 
    305     auto sess = NewSession();
    306     TF_CHECK_OK(sess->Create(gdef));
    307     std::vector<Tensor> outputs;
    308     TF_CHECK_OK(
    309         sess->Run({{"x:0", x}, {"y:0", y}}, {"d:0", "d:1"}, {}, &outputs));
    310     CHECK_EQ(outputs.size(), 2);
    311     TF_CHECK_OK(sess->Close());
    312     *dx = outputs[0];
    313     *dy = outputs[1];
    314   }
    315 
    316   void MatMulGrad(const Tensor& x, bool ax, const Tensor& y, bool ay,
    317                   Tensor* dx, Tensor* dy) {
    318     return MatMulGradCommon("MatMul", "transpose_a", "transpose_b", x, ax, y,
    319                             ay, dx, dy);
    320   }
    321 
    322   void BatchMatMulGrad(const Tensor& x, bool ax, const Tensor& y, bool ay,
    323                        Tensor* dx, Tensor* dy) {
    324     return MatMulGradCommon("BatchMatMul", "adj_x", "adj_y", x, ax, y, ay, dx,
    325                             dy);
    326   }
    327 
    328   void SelectGrad(const Tensor& c, const Tensor& x, const Tensor& y, Tensor* dc,
    329                   Tensor* dx, Tensor* dy) {
    330     auto T = DT_FLOAT;
    331     // Sum(Select(c, x, y))
    332     auto test =
    333         FDH::Define("Test", {"c:bool", "x:float", "y:float"}, {"l:float"}, {},
    334                     {
    335                         {{"z"}, "Select", {"c", "x", "y"}, {{"T", T}}},
    336                         FDH::Const("zero", 0),
    337                         FDH::Const("one", 1),
    338                         {{"r"}, "Rank", {"z"}, {{"T", T}}},
    339                         {{"indices"}, "Range", {"zero", "r", "one"}},
    340                         {{"l"}, "Sum", {"z", "indices"}, {{"T", T}}},
    341                     });
    342 
    343     // TestGrad(x, y) = Test'(c, x, y)
    344     auto grad = FDH::Define("TestGrad", {"c:bool", "x:float", "y:float"},
    345                             {"dc:bool", "dx:float", "dy:float"}, {},
    346                             {FDH::Const("dz", 1.f),
    347                              {{"grad0", "grad1", "grad2"},
    348                               "SymbolicGradient",
    349                               {"c", "x", "y", "dz"},
    350                               {
    351                                   {"f", FDH::FunctionRef("Test")},
    352                                   {"Tin", DataTypeSlice{DT_BOOL, T, T, T}},
    353                                   {"Tout", DataTypeSlice{DT_BOOL, T, T}},
    354                               }},
    355                              {{"dc"}, "Identity", {"grad0"}, {{"T", DT_BOOL}}},
    356                              {{"dx"}, "Identity", {"grad1"}, {{"T", T}}},
    357                              {{"dy"}, "Identity", {"grad2"}, {{"T", T}}}});
    358     // Each test case will feed in "x:0" and expects to get "dx:0".
    359     auto gdef = test::function::GDef(
    360         {
    361             f::NDef("c", "Placeholder", {}, {{"dtype", DT_BOOL}}),
    362             f::NDef("x", "Placeholder", {}, {{"dtype", T}}),
    363             f::NDef("y", "Placeholder", {}, {{"dtype", T}}),
    364             f::NDef("d", "TestGrad", {"c", "x", "y"}, {}),
    365         },
    366         {test, grad});
    367 
    368     auto sess = NewSession();
    369     TF_CHECK_OK(sess->Create(gdef));
    370     std::vector<Tensor> outputs;
    371     TF_CHECK_OK(sess->Run({{"c:0", c}, {"x:0", x}, {"y:0", y}},
    372                           {"d:0", "d:1", "d:2"}, {}, &outputs));
    373     CHECK_EQ(outputs.size(), 3);
    374     TF_CHECK_OK(sess->Close());
    375     *dc = outputs[0];
    376     *dx = outputs[1];
    377     *dy = outputs[2];
    378   }
    379 };
    380 
    381 void HasError(const Status& s, const string& substr) {
    382   EXPECT_TRUE(str_util::StrContains(s.ToString(), substr))
    383       << s << ", expected substring " << substr;
    384 }
    385 
    386 REGISTER_OP("TestOpWithNoGrad")
    387     .Input("x: T")
    388     .Output("y: T")
    389     .Attr("T: {float, double}")
    390     .Doc(R"doc(
    391 Test op with no grad registered.
    392 
    393 x: input
    394 y: output
    395 )doc");
    396 
    397 class TestOp : public OpKernel {
    398  public:
    399   explicit TestOp(OpKernelConstruction* ctx) : OpKernel(ctx) {}
    400   void Compute(OpKernelContext* ctx) override { ctx->set_output(0, Tensor()); }
    401 };
    402 REGISTER_KERNEL_BUILDER(Name("TestOpWithNoGrad").Device(DEVICE_CPU), TestOp);
    403 #ifdef TENSORFLOW_USE_SYCL
    404 REGISTER_KERNEL_BUILDER(Name("TestOpWithNoGrad").Device(DEVICE_SYCL), TestOp);
    405 #endif  // TENSORFLOW_USE_SYCL
    406 
    407 TEST_F(MathGradTest, Error_Reporting) {
    408   auto x = test::AsTensor<float>({-3.f});
    409   auto dx = test::AsTensor<float>({3.f});
    410   Tensor donotcare;
    411   HasError(Unary("TestOpWithNoGrad", x, &donotcare),
    412            "No gradient defined for op: TestOpWithNoGrad");
    413 }
    414 
    415 TEST_F(MathGradTest, Abs) {
    416   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    417                                  TensorShape({2, 3}));
    418   auto g = [](float x) { return x < 0 ? -1.f : 1.f; };
    419   auto dx = test::AsTensor<float>(
    420       {g(-3.f), g(-2.f), g(-1.f), g(1.f), g(2.f), g(3.f)}, TensorShape({2, 3}));
    421   auto ans = SymGrad("Abs", x);
    422   test::ExpectClose(ans, dx);
    423 }
    424 
    425 TEST_F(MathGradTest, Neg) {
    426   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    427                                  TensorShape({2, 3}));
    428   auto g = [](float x) { return -1.f; };
    429   auto dx = test::AsTensor<float>(
    430       {g(-3.f), g(-2.f), g(-1.f), g(1.f), g(2.f), g(3.f)}, TensorShape({2, 3}));
    431   auto ans = SymGrad("Neg", x);
    432   test::ExpectClose(ans, dx);
    433 }
    434 
    435 TEST_F(MathGradTest, Reciprocal) {
    436   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    437                                  TensorShape({2, 3}));
    438   auto g = [](float x) { return -1.f / (x * x); };
    439   auto dx = test::AsTensor<float>(
    440       {g(-3.f), g(-2.f), g(-1.f), g(1.f), g(2.f), g(3.f)}, TensorShape({2, 3}));
    441   auto ans = SymGrad("Reciprocal", x);
    442   test::ExpectClose(ans, dx);
    443 }
    444 
    445 TEST_F(MathGradTest, Square) {
    446   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    447                                  TensorShape({2, 3}));
    448   auto g = [](float x) { return 2 * x; };
    449   auto dx = test::AsTensor<float>(
    450       {g(-3.f), g(-2.f), g(-1.f), g(1.f), g(2.f), g(3.f)}, TensorShape({2, 3}));
    451   auto ans = SymGrad("Square", x);
    452   test::ExpectClose(ans, dx);
    453 }
    454 
    455 TEST_F(MathGradTest, Sqrt) {
    456   auto x = test::AsTensor<float>({1.f, 2.f, 3.f, 4.f, 5.f, 6.f},
    457                                  TensorShape({2, 3}));
    458   auto g = [](float x) { return 0.5f / std::sqrt(x); };
    459   auto dx = test::AsTensor<float>(
    460       {g(1.f), g(2.f), g(3.f), g(4.f), g(5.f), g(6.f)}, TensorShape({2, 3}));
    461   auto ans = SymGrad("Sqrt", x);
    462   test::ExpectClose(ans, dx);
    463 }
    464 
    465 TEST_F(MathGradTest, Rsqrt) {
    466   auto x = test::AsTensor<float>({1.f, 2.f, 3.f, 4.f, 5.f, 6.f},
    467                                  TensorShape({2, 3}));
    468   auto g = [](float x) { return -0.5f / (x * std::sqrt(x)); };
    469   auto dx = test::AsTensor<float>(
    470       {g(1.f), g(2.f), g(3.f), g(4.f), g(5.f), g(6.f)}, TensorShape({2, 3}));
    471   auto ans = SymGrad("Rsqrt", x);
    472   test::ExpectClose(ans, dx);
    473 }
    474 
    475 TEST_F(MathGradTest, Exp) {
    476   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    477                                  TensorShape({2, 3}));
    478   auto g = [](float x) { return std::exp(x); };
    479   auto dx = test::AsTensor<float>(
    480       {g(-3.f), g(-2.f), g(-1.f), g(1.f), g(2.f), g(3.f)}, TensorShape({2, 3}));
    481   auto ans = SymGrad("Exp", x);
    482   test::ExpectClose(ans, dx);
    483 }
    484 
    485 TEST_F(MathGradTest, Expm1) {
    486   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    487                                  TensorShape({2, 3}));
    488   auto g = [](float x) { return std::exp(x); };
    489   auto dx = test::AsTensor<float>(
    490       {g(-3.f), g(-2.f), g(-1.f), g(1.f), g(2.f), g(3.f)}, TensorShape({2, 3}));
    491   auto ans = SymGrad("Expm1", x);
    492   test::ExpectClose(ans, dx);
    493 }
    494 
    495 TEST_F(MathGradTest, Log) {
    496   auto x = test::AsTensor<float>({0.1f, 1.f, 2.f, 3.f, 4.f, 10.f},
    497                                  TensorShape({2, 3}));
    498   auto g = [](float x) { return 1 / x; };
    499   auto dx = test::AsTensor<float>(
    500       {g(.1f), g(1.f), g(2.f), g(3.f), g(4.f), g(10.f)}, TensorShape({2, 3}));
    501   auto ans = SymGrad("Log", x);
    502   test::ExpectClose(ans, dx);
    503 }
    504 
    505 TEST_F(MathGradTest, Log1p) {
    506   auto x = test::AsTensor<float>({0.1f, 1.f, 2.f, 3.f, 4.f, 10.f},
    507                                  TensorShape({2, 3}));
    508   auto g = [](float x) { return 1 / (1 + x); };
    509   auto dx = test::AsTensor<float>(
    510       {g(.1f), g(1.f), g(2.f), g(3.f), g(4.f), g(10.f)}, TensorShape({2, 3}));
    511   auto ans = SymGrad("Log1p", x);
    512   test::ExpectClose(ans, dx);
    513 }
    514 
    515 TEST_F(MathGradTest, Sinh) {
    516   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    517                                  TensorShape({2, 3}));
    518   auto g = [](float x) { return std::cosh(x); };
    519   auto dx = test::AsTensor<float>(
    520       {g(-3.f), g(-2.f), g(-1.f), g(1.f), g(2.f), g(3.f)}, TensorShape({2, 3}));
    521   auto ans = SymGrad("Sinh", x);
    522   test::ExpectClose(ans, dx);
    523 }
    524 
    525 TEST_F(MathGradTest, Cosh) {
    526   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    527                                  TensorShape({2, 3}));
    528   auto g = [](float x) { return std::sinh(x); };
    529   auto dx = test::AsTensor<float>(
    530       {g(-3.f), g(-2.f), g(-1.f), g(1.f), g(2.f), g(3.f)}, TensorShape({2, 3}));
    531   auto ans = SymGrad("Cosh", x);
    532   test::ExpectClose(ans, dx);
    533 }
    534 
    535 TEST_F(MathGradTest, Tanh) {
    536   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    537                                  TensorShape({2, 3}));
    538   auto g = [](float x) {
    539     auto y = std::tanh(x);
    540     return 1 - y * y;
    541   };
    542   auto dx = test::AsTensor<float>(
    543       {g(-3.f), g(-2.f), g(-1.f), g(1.f), g(2.f), g(3.f)}, TensorShape({2, 3}));
    544   auto ans = SymGrad("Tanh", x);
    545   test::ExpectClose(ans, dx);
    546 }
    547 
    548 TEST_F(MathGradTest, Asinh) {
    549   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    550                                  TensorShape({2, 3}));
    551   auto g = [](float x) {
    552     auto y = std::asinh(x);
    553     return std::cosh(y);
    554   };
    555   auto dx = test::AsTensor<float>(
    556       {g(-3.f), g(-2.f), g(-1.f), g(1.f), g(2.f), g(3.f)}, TensorShape({2, 3}));
    557   auto ans = SymGrad("Asinh", x);
    558   test::ExpectClose(ans, dx);
    559 }
    560 
    561 TEST_F(MathGradTest, Acosh) {
    562   auto x = test::AsTensor<float>({6.f, 5.f, 4.f, 1.f, 2.f, 3.f},
    563                                  TensorShape({2, 3}));
    564   auto g = [](float x) {
    565     auto y = std::acosh(x);
    566     return std::sinh(y);
    567   };
    568   auto dx = test::AsTensor<float>(
    569       {g(6.f), g(5.f), g(4.f), g(1.f), g(2.f), g(3.f)}, TensorShape({2, 3}));
    570   auto ans = SymGrad("Acosh", x);
    571   test::ExpectClose(ans, dx);
    572 }
    573 
    574 TEST_F(MathGradTest, Atanh) {
    575   auto x = test::AsTensor<float>({-0.3f, -0.2f, -0.1f, 0.1f, 0.2f, 0.3f},
    576                                  TensorShape({2, 3}));
    577   auto g = [](float x) { return 1.f / (1.f - x * x); };
    578   auto dx = test::AsTensor<float>(
    579       {g(-0.3f), g(-0.2f), g(-0.1f), g(0.1f), g(0.2f), g(0.3f)},
    580       TensorShape({2, 3}));
    581   auto ans = SymGrad("Atanh", x);
    582   test::ExpectClose(ans, dx);
    583 }
    584 
    585 TEST_F(MathGradTest, Sigmoid) {
    586   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    587                                  TensorShape({2, 3}));
    588   auto g = [](float x) {
    589     auto y = 1.f / (1.f + std::exp(-x));
    590     return y * (1 - y);
    591   };
    592   auto dx = test::AsTensor<float>(
    593       {g(-3.f), g(-2.f), g(-1.f), g(1.f), g(2.f), g(3.f)}, TensorShape({2, 3}));
    594   auto ans = SymGrad("Sigmoid", x);
    595   test::ExpectClose(ans, dx);
    596 }
    597 
    598 TEST_F(MathGradTest, Sign) {
    599   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    600                                  TensorShape({2, 3}));
    601   auto g = [](float x) { return 0.f; };
    602   auto dx = test::AsTensor<float>(
    603       {g(-3.f), g(-2.f), g(-1.f), g(1.f), g(2.f), g(3.f)}, TensorShape({2, 3}));
    604   auto ans = SymGrad("Sign", x);
    605   test::ExpectClose(ans, dx);
    606 }
    607 
    608 TEST_F(MathGradTest, Sin) {
    609   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    610                                  TensorShape({2, 3}));
    611   auto g = [](float x) { return std::cos(x); };
    612   auto dx = test::AsTensor<float>(
    613       {g(-3.f), g(-2.f), g(-1.f), g(1.f), g(2.f), g(3.f)}, TensorShape({2, 3}));
    614   auto ans = SymGrad("Sin", x);
    615   test::ExpectClose(ans, dx);
    616 }
    617 
    618 TEST_F(MathGradTest, Cos) {
    619   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    620                                  TensorShape({2, 3}));
    621   auto g = [](float x) { return -std::sin(x); };
    622   auto dx = test::AsTensor<float>(
    623       {g(-3.f), g(-2.f), g(-1.f), g(1.f), g(2.f), g(3.f)}, TensorShape({2, 3}));
    624   auto ans = SymGrad("Cos", x);
    625   test::ExpectClose(ans, dx);
    626 }
    627 
    628 TEST_F(MathGradTest, Cast) {
    629   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    630                                  TensorShape({2, 3}));
    631   auto g = [](float x) { return 1.f; };
    632   auto dx = test::AsTensor<float>(
    633       {g(-3.f), g(-2.f), g(-1.f), g(1.f), g(2.f), g(3.f)}, TensorShape({2, 3}));
    634   Tensor ans = SymCastGrad(x, DT_INT32);
    635   test::ExpectClose(ans, dx);
    636 }
    637 
    638 // TODO(zhifengc)
    639 // TEST_F(MathGradSComplexTest, Real) {}
    640 // TEST_F(MathGradSComplexTest, Imag) {}
    641 // TEST_F(MathGradSComplexTest, Angle) {}
    642 // TEST_F(MathGradSComplexTest, Conj) {}
    643 // TEST_F(MathGradTernary, Select) {}
    644 
    645 TEST_F(MathGradTest, Add) {
    646   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    647                                  TensorShape({2, 3}));
    648   auto y = test::AsTensor<float>({-10.f, 10.f}, TensorShape({2, 1}));
    649   auto ans_dx = test::AsTensor<float>({1.f, 1.f, 1.f, 1.f, 1.f, 1.f},
    650                                       TensorShape({2, 3}));
    651   auto ans_dy = test::AsTensor<float>({3.f, 3.f}, TensorShape({2, 1}));
    652   Tensor dx;
    653   Tensor dy;
    654   {
    655     SymGrad("Add", x, y, &dx, &dy);
    656     test::ExpectClose(ans_dx, dx);
    657     test::ExpectClose(ans_dy, dy);
    658   }
    659   {  // Swap x and y
    660     SymGrad("Add", y, x, &dy, &dx);
    661     test::ExpectClose(ans_dx, dx);
    662     test::ExpectClose(ans_dy, dy);
    663   }
    664 }
    665 
    666 TEST_F(MathGradTest, Sub) {
    667   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    668                                  TensorShape({2, 3}));
    669   auto y = test::AsTensor<float>({-10.f, 10.f}, TensorShape({2, 1}));
    670   Tensor dx;
    671   Tensor dy;
    672   {
    673     SymGrad("Sub", x, y, &dx, &dy);
    674     auto ans_dx = test::AsTensor<float>({1.f, 1.f, 1.f, 1.f, 1.f, 1.f},
    675                                         TensorShape({2, 3}));
    676     auto ans_dy = test::AsTensor<float>({-3.f, -3.f}, TensorShape({2, 1}));
    677     test::ExpectClose(ans_dx, dx);
    678     test::ExpectClose(ans_dy, dy);
    679   }
    680   {  // Swap x and y
    681     SymGrad("Sub", y, x, &dy, &dx);
    682     auto ans_dx = test::AsTensor<float>({-1.f, -1.f, -1.f, -1.f, -1.f, -1.f},
    683                                         TensorShape({2, 3}));
    684     auto ans_dy = test::AsTensor<float>({3.f, 3.f}, TensorShape({2, 1}));
    685     test::ExpectClose(ans_dx, dx);
    686     test::ExpectClose(ans_dy, dy);
    687   }
    688 }
    689 
    690 TEST_F(MathGradTest, Mul) {
    691   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    692                                  TensorShape({2, 3}));
    693   auto y = test::AsTensor<float>({-10.f, 10.f}, TensorShape({2, 1}));
    694   auto ans_dx = test::AsTensor<float>({-10.f, -10.f, -10.f, 10.f, 10.f, 10.f},
    695                                       TensorShape({2, 3}));
    696   auto ans_dy = test::AsTensor<float>({-3.f + (-2.f) + (-1.f), 1.f + 2.f + 3.f},
    697                                       TensorShape({2, 1}));
    698   Tensor dx;
    699   Tensor dy;
    700   {
    701     SymGrad("Mul", x, y, &dx, &dy);
    702     test::ExpectClose(ans_dx, dx);
    703     test::ExpectClose(ans_dy, dy);
    704   }
    705   {  // Swap x and y
    706     SymGrad("Mul", y, x, &dy, &dx);
    707     test::ExpectClose(ans_dx, dx);
    708     test::ExpectClose(ans_dy, dy);
    709   }
    710 }
    711 
    712 TEST_F(MathGradTest, Div) {
    713   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    714                                  TensorShape({2, 3}));
    715   auto y = test::AsTensor<float>({-10.f, 10.f}, TensorShape({2, 1}));
    716   Tensor dx;
    717   Tensor dy;
    718   {
    719     SymGrad("Div", x, y, &dx, &dy);
    720     {
    721       auto g = [](float x, float y) { return 1.f / y; };
    722       test::ExpectClose(dx, test::AsTensor<float>(
    723                                 {g(-3.f, -10.f), g(-2.f, -10.f), g(-1.f, -10.f),
    724                                  g(1.f, 10.f), g(2.f, 10.f), g(3.f, 10.f)},
    725                                 TensorShape({2, 3})));
    726     }
    727     {
    728       auto g = [](float x, float y) { return -x / (y * y); };
    729       test::ExpectClose(dy,
    730                         test::AsTensor<float>(
    731                             {g(-3.f, -10.f) + g(-2.f, -10.f) + g(-1.f, -10.f),
    732                              g(1.f, 10.f) + g(2.f, 10.f) + g(3.f, 10.f)},
    733                             TensorShape({2, 1})));
    734     }
    735   }
    736   {  // Swap x and y
    737     SymGrad("Div", y, x, &dy, &dx);
    738     {
    739       auto g = [](float x, float y) { return 1.f / y; };
    740       test::ExpectClose(dy,
    741                         test::AsTensor<float>(
    742                             {g(-10.f, -3.f) + g(-10.f, -2.f) + g(-10.f, -1.f),
    743                              g(10.f, 1.f) + g(10.f, 2.f) + g(10.f, 3.f)},
    744                             TensorShape({2, 1})));
    745     }
    746     {
    747       auto g = [](float x, float y) { return -x / (y * y); };
    748       test::ExpectClose(dx, test::AsTensor<float>(
    749                                 {g(-10.f, -3.f), g(-10.f, -2.f), g(-10.f, -1.f),
    750                                  g(10.f, 1.f), g(10.f, 2.f), g(10.f, 3.f)},
    751                                 TensorShape({2, 3})));
    752     }
    753   }
    754 }
    755 
    756 TEST_F(MathGradTest, DivNoNan) {
    757   auto x = test::AsTensor<float>(
    758       {0.f, -3.f, -2.f, -1.f, 0.f, 1.f, 2.f, 3.f, 0.f}, TensorShape({3, 3}));
    759   auto y = test::AsTensor<float>({-10.f, 0.f, 10.f}, TensorShape({3, 1}));
    760   Tensor dx;
    761   Tensor dy;
    762   {
    763     SymGrad("DivNoNan", x, y, &dx, &dy);
    764     {
    765       auto g = [](float x, float y) {
    766         if (y == 0.f) {
    767           return 0.f;
    768         } else {
    769           return 1.f / y;
    770         }
    771       };
    772       test::ExpectClose(dx, test::AsTensor<float>(
    773                                 {g(0.f, -10.f), g(-3.f, -10.f), g(-2.f, -10.f),
    774                                  g(-1.f, 0.f), g(0.f, 0.f), g(1.f, 0.f),
    775                                  g(2.f, 10.f), g(3.f, 10.f), g(0.f, 10.f)},
    776                                 TensorShape({3, 3})));
    777     }
    778     {
    779       auto g = [](float x, float y) {
    780         if (y == 0.f) {
    781           return 0.f;
    782         } else {
    783           return -x / (y * y);
    784         }
    785       };
    786       test::ExpectClose(dy,
    787                         test::AsTensor<float>(
    788                             {g(0.f, -10.f) + g(-3.f, -10.f) + g(-2.f, -10.f),
    789                              g(-1.f, 0.f) + g(0.f, 0.f) + g(1.f, 0.f),
    790                              g(2.f, 10.f) + g(3.f, 10.f) + g(0.f, 10.f)},
    791                             TensorShape({3, 1})));
    792     }
    793   }
    794   {  // Swap x and y.
    795     SymGrad("DivNoNan", y, x, &dy, &dx);
    796     {
    797       auto g = [](float x, float y) {
    798         if (y == 0.f) {
    799           return 0.f;
    800         } else {
    801           return 1.f / y;
    802         }
    803       };
    804       test::ExpectClose(dy,
    805                         test::AsTensor<float>(
    806                             {g(-10.f, 0.f) + g(-10.f, -3.f) + g(-10.f, -2.f),
    807                              g(0.f, -1.f) + g(0.f, 0.f) + g(0.f, 1.f),
    808                              g(10.f, 2.f) + g(10.f, 3.f) + g(10.f, 0.f)},
    809                             TensorShape({3, 1})));
    810     }
    811     {
    812       auto g = [](float x, float y) {
    813         if (y == 0.f) {
    814           return 0.f;
    815         } else {
    816           return -x / (y * y);
    817         }
    818       };
    819       test::ExpectClose(dx, test::AsTensor<float>(
    820                                 {g(-10.f, 0.f), g(-10.f, -3.f), g(-10.f, -2.f),
    821                                  g(0.f, -1.f), g(0.f, 0.f), g(0.f, 1.f),
    822                                  g(10.f, 2.f), g(10.f, 3.f), g(10.f, 0.f)},
    823                                 TensorShape({3, 3})));
    824     }
    825   }
    826 }
    827 
    828 TEST_F(MathGradTest, Pow) {
    829   auto x = test::AsTensor<float>({0.f, 1.f, 2.f, 3.f, 4.f, 5.f},
    830                                  TensorShape({2, 3}));
    831   auto y = test::AsTensor<float>({.5f, 2.f}, TensorShape({2, 1}));
    832   Tensor dx;
    833   Tensor dy;
    834   auto g = [](float x, float y) { return y * std::pow(x, y - 1); };
    835   auto h = [](float x, float y) {
    836     return std::pow(x, y) * (x ? std::log(x) : 0);
    837   };
    838   {
    839     SymGrad("Pow", x, y, &dx, &dy);
    840     test::ExpectClose(
    841         dx, test::AsTensor<float>({g(0.f, .5f), g(1.f, .5f), g(2.f, .5f),
    842                                    g(3.f, 2.f), g(4.f, 2.f), g(5.f, 2.f)},
    843                                   TensorShape({2, 3})));
    844     test::ExpectClose(
    845         dy, test::AsTensor<float>({h(0.f, .5f) + h(1.f, .5f) + h(2.f, .5f),
    846                                    h(3.f, 2.f) + h(4.f, 2.f) + h(5.f, 2.f)},
    847                                   TensorShape({2, 1})));
    848   }
    849   {  // Swap x and y
    850     SymGrad("Pow", y, x, &dy, &dx);
    851     test::ExpectClose(
    852         dy, test::AsTensor<float>({g(.5f, 0.f) + g(.5f, 1.f) + g(.5f, 2.f),
    853                                    g(2.f, 3.f) + g(2.f, 4.f) + g(2.f, 5.f)},
    854                                   TensorShape({2, 1})));
    855     test::ExpectClose(
    856         dx, test::AsTensor<float>({h(.5f, 0.f), h(.5f, 1.f), h(.5f, 2.f),
    857                                    h(2.f, 3.f), h(2.f, 4.f), h(2.f, 5.f)},
    858                                   TensorShape({2, 3})));
    859   }
    860 }
    861 
    862 // TODO{lukeiwanski}: Implement Complex Pow for SYCL
    863 #ifndef TENSORFLOW_USE_SYCL
    864 TEST_F(MathGradTest, ComplexPow) {
    865   auto x = test::AsTensor<complex64>({0.f, 2.f, -2.f}, TensorShape({3}));
    866   auto y = test::AsTensor<complex64>({2.f, 2.f, 2.f}, TensorShape({3}));
    867   Tensor dx;
    868   Tensor dy;
    869   auto g = [](complex64 x, complex64 y) { return y * std::pow(x, y - 1.f); };
    870   auto h = [](complex64 x, complex64 y) {
    871     return std::pow(x, y) * (x != complex64(0) ? std::log(x) : 0);
    872   };
    873   SymGrad("Pow", x, y, &dx, &dy);
    874 
    875   // This case failed on Kokoro MacOS:
    876   // dx[2] = (-4,6.0398321011234657e-07),
    877   // test::AsTensor[2] = (-4,-3.4969110629390343e-07).
    878   // dx[2] on linux is close to test::AsTensor[2].
    879   // This error hasn't shown up before because
    880   // ExpectClose used to check just the magnitude of a complex number, i.e.,
    881   // std::abs(complex) = sqrt(real^2 + imag^2).
    882   // Now ExpectClose checks the value of each component separately.
    883   // Workaround: I set a big tolerance to make the case pass for now.
    884   // TODO(penporn): Fix this or file a bug. This is not a precision issue.
    885   // Even the most significant digit (or the sign) doesn't match.
    886   test::ExpectClose(
    887       dx,
    888       test::AsTensor<complex64>({g(0.f, 2.f), g(2.f, 2.f), g(-2.f, 2.f)},
    889                                 TensorShape({3})),
    890       1e-6f);
    891 
    892   // This case failed on Kokoro MacOS:
    893   // dx[2] = (2.7725925445556641,12.56636905670166),
    894   // test::AsTensor[2] = (2.7725865840911865,12.566371917724609)
    895   // dx[2] on linux is close to test::AsTensor[2].
    896   // Default atol = rtol = 5.96046e-07.
    897   // Real: diff = 5.96046e-06 > threshold = 2.248633e-06 <- failed
    898   // Complex: diff = 2.86102e-06 <= threshold = 8.08618e-06 <- passed
    899   // Again, this error hasn't shown up before because ExpectClose used to
    900   // check just the magnitude of the complex number. Now it checks each
    901   // component separately.
    902   // Workaround: Set a larger tolerance for now.
    903   // TODO(penporn): See if this is a precision issue or a bug.
    904   test::ExpectClose(
    905       dy,
    906       test::AsTensor<complex64>({h(0.f, 2.f), h(2.f, 2.f), h(-2.f, 2.f)},
    907                                 TensorShape({3})),
    908       4.5e-6f);
    909 }
    910 #endif  // TENSORFLOW_USE_SYCL
    911 
    912 TEST_F(MathGradTest, Xlogy) {
    913   auto x = test::AsTensor<float>({0.f, 0.f, 2.f, 3.f, 4.f, 5.f},
    914                                  TensorShape({2, 3}));
    915   auto y = test::AsTensor<float>({.5f, 2.f}, TensorShape({2, 1}));
    916   Tensor dx;
    917   Tensor dy;
    918   auto g = [](float x, float y) -> float { return x == 0. ? 0. : std::log(y); };
    919   auto h = [](float x, float y) -> float { return x == 0. ? 0. : x / y; };
    920   SymGrad("Xlogy", x, y, &dx, &dy);
    921   test::ExpectClose(
    922       dx, test::AsTensor<float>({g(0.f, .5f), g(0.f, 0.f), g(2.f, .5f),
    923                                  g(3.f, 2.f), g(4.f, 2.f), g(5.f, 2.f)},
    924                                 TensorShape({2, 3})));
    925   test::ExpectClose(
    926       dy, test::AsTensor<float>({h(0.f, .5f) + h(0.f, 0.f) + h(2.f, .5f),
    927                                  h(3.f, 2.f) + h(4.f, 2.f) + h(5.f, 2.f)},
    928                                 TensorShape({2, 1})));
    929 }
    930 
    931 TEST_F(MathGradTest, Xdivy) {
    932   auto x = test::AsTensor<float>({0.f, 0.f, 2.f, 3.f, 4.f, 5.f},
    933                                  TensorShape({2, 3}));
    934   auto y = test::AsTensor<float>({.5f, 2.f}, TensorShape({2, 1}));
    935   Tensor dx;
    936   Tensor dy;
    937   auto g = [](float x, float y) -> float { return x == 0. ? 0. : 1 / y; };
    938   auto h = [](float x, float y) -> float {
    939     return x == 0. ? 0. : -x / (y * y);
    940   };
    941   SymGrad("Xdivy", x, y, &dx, &dy);
    942   test::ExpectClose(
    943       dx, test::AsTensor<float>({g(0.f, .5f), g(0.f, 0.f), g(2.f, .5f),
    944                                  g(3.f, 2.f), g(4.f, 2.f), g(5.f, 2.f)},
    945                                 TensorShape({2, 3})));
    946   test::ExpectClose(
    947       dy, test::AsTensor<float>({h(0.f, .5f) + h(0.f, 0.f) + h(2.f, .5f),
    948                                  h(3.f, 2.f) + h(4.f, 2.f) + h(5.f, 2.f)},
    949                                 TensorShape({2, 1})));
    950 }
    951 
    952 TEST_F(MathGradTest, SquaredDifference) {
    953   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    954                                  TensorShape({2, 3}));
    955   auto y = test::AsTensor<float>({.5f, 2.f}, TensorShape({2, 1}));
    956   Tensor dx;
    957   Tensor dy;
    958   auto g = [](float x, float y) -> float { return 2. * (x - y); };
    959   auto h = [](float x, float y) -> float { return 2. * (y - x); };
    960   SymGrad("SquaredDifference", x, y, &dx, &dy);
    961   test::ExpectClose(
    962       dx, test::AsTensor<float>({g(-3.f, .5f), g(-2.f, .5f), g(-1.f, .5f),
    963                                  g(1.f, 2.f), g(2.f, 2.f), g(3.f, 2.f)},
    964                                 TensorShape({2, 3})));
    965   test::ExpectClose(
    966       dy, test::AsTensor<float>({h(-3.f, .5f) + h(-2.f, .5f) + h(-1.f, .5f),
    967                                  h(1.f, 2.f) + h(2.f, 2.f) + h(3.f, 2.f)},
    968                                 TensorShape({2, 1})));
    969 }
    970 
    971 TEST_F(MathGradTest, Maximum) {
    972   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
    973                                  TensorShape({2, 3}));
    974   auto y = test::AsTensor<float>({-1.5f, 1.5f}, TensorShape({2, 1}));
    975   Tensor dx;
    976   Tensor dy;
    977   {
    978     SymGrad("Maximum", x, y, &dx, &dy);
    979     {
    980       auto g = [](float x, float y) { return x >= y ? 1.f : 0.f; };
    981       test::ExpectClose(dx, test::AsTensor<float>(
    982                                 {g(-3.f, -1.5f), g(-2.f, -1.5f), g(-1.f, -1.5f),
    983                                  g(1.f, 1.5f), g(2.f, 1.5f), g(3.f, 1.5f)},
    984                                 TensorShape({2, 3})));
    985     }
    986     {
    987       auto g = [](float x, float y) { return x < y ? 1.f : 0.f; };
    988       test::ExpectClose(dy,
    989                         test::AsTensor<float>(
    990                             {g(-3.f, -1.5f) + g(-2.f, -1.5f) + g(-1.f, -1.5f),
    991                              g(1.f, 1.5f) + g(2.f, 1.5f) + g(3.f, 1.5f)},
    992                             TensorShape({2, 1})));
    993     }
    994   }
    995   {  // Swap x and y
    996     SymGrad("Maximum", y, x, &dy, &dx);
    997     {
    998       auto g = [](float x, float y) { return x >= y ? 1.f : 0.f; };
    999       test::ExpectClose(dy,
   1000                         test::AsTensor<float>(
   1001                             {g(-1.5f, -3.f) + g(-1.5f, -2.f) + g(-1.5f, -1.f),
   1002                              g(1.5f, 1.f) + g(1.5f, 2.f) + g(1.5f, 3.f)},
   1003                             TensorShape({2, 1})));
   1004     }
   1005     {
   1006       auto g = [](float x, float y) { return x < y ? 1.f : 0.f; };
   1007       test::ExpectClose(dx, test::AsTensor<float>(
   1008                                 {g(-1.5f, -3.f), g(-1.5f, -2.f), g(-1.5f, -1.f),
   1009                                  g(1.5f, 1.f), g(1.5f, 2.f), g(1.5f, 3.f)},
   1010                                 TensorShape({2, 3})));
   1011     }
   1012   }
   1013 }
   1014 
   1015 TEST_F(MathGradTest, Minimum) {
   1016   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
   1017                                  TensorShape({2, 3}));
   1018   auto y = test::AsTensor<float>({-1.5f, 1.5f}, TensorShape({2, 1}));
   1019   Tensor dx;
   1020   Tensor dy;
   1021   {
   1022     SymGrad("Minimum", x, y, &dx, &dy);
   1023     {
   1024       auto g = [](float x, float y) { return x <= y ? 1.f : 0.f; };
   1025       test::ExpectClose(dx, test::AsTensor<float>(
   1026                                 {g(-3.f, -1.5f), g(-2.f, -1.5f), g(-1.f, -1.5f),
   1027                                  g(1.f, 1.5f), g(2.f, 1.5f), g(3.f, 1.5f)},
   1028                                 TensorShape({2, 3})));
   1029     }
   1030     {
   1031       auto g = [](float x, float y) { return x > y ? 1.f : 0.f; };
   1032       test::ExpectClose(dy,
   1033                         test::AsTensor<float>(
   1034                             {g(-3.f, -1.5f) + g(-2.f, -1.5f) + g(-1.f, -1.5f),
   1035                              g(1.f, 1.5f) + g(2.f, 1.5f) + g(3.f, 1.5f)},
   1036                             TensorShape({2, 1})));
   1037     }
   1038   }
   1039   {  // Swap x and y
   1040     SymGrad("Minimum", y, x, &dy, &dx);
   1041     {
   1042       auto g = [](float x, float y) { return x <= y ? 1.f : 0.f; };
   1043       test::ExpectClose(dy,
   1044                         test::AsTensor<float>(
   1045                             {g(-1.5f, -3.f) + g(-1.5f, -2.f) + g(-1.5f, -1.f),
   1046                              g(1.5f, 1.f) + g(1.5f, 2.f) + g(1.5f, 3.f)},
   1047                             TensorShape({2, 1})));
   1048     }
   1049     {
   1050       auto g = [](float x, float y) { return x > y ? 1.f : 0.f; };
   1051       test::ExpectClose(dx, test::AsTensor<float>(
   1052                                 {g(-1.5f, -3.f), g(-1.5f, -2.f), g(-1.5f, -1.f),
   1053                                  g(1.5f, 1.f), g(1.5f, 2.f), g(1.5f, 3.f)},
   1054                                 TensorShape({2, 3})));
   1055     }
   1056   }
   1057 }
   1058 
   1059 TEST_F(MathGradTest, Select) {
   1060   auto c = test::AsTensor<bool>({true, false, false, true, true, false},
   1061                                 TensorShape({2, 3}));
   1062   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
   1063                                  TensorShape({2, 3}));
   1064   auto y = test::AsTensor<float>({3.f, 2.f, 1.f, 1.f, 2.f, 3.f},
   1065                                  TensorShape({2, 3}));
   1066   Tensor dc;
   1067   Tensor dx;
   1068   Tensor dy;
   1069   {
   1070     SelectGrad(c, x, y, &dc, &dx, &dy);
   1071     test::ExpectTensorEqual<bool>(
   1072         dc, test::AsTensor<bool>({false, false, false, false, false, false},
   1073                                  TensorShape({2, 3})));
   1074     test::ExpectTensorEqual<float>(
   1075         dx, test::AsTensor<float>({1.f, 0.f, 0.f, 1.f, 1.f, 0.f},
   1076                                   TensorShape({2, 3})));
   1077     test::ExpectTensorEqual<float>(
   1078         dy, test::AsTensor<float>({0.f, 1.f, 1.f, 0.f, 0.f, 1.f},
   1079                                   TensorShape({2, 3})));
   1080   }
   1081 }
   1082 
   1083 TEST_F(MathGradTest, MatMul_00) {
   1084   auto x = test::AsTensor<float>({1.f, 2.f, 3.f, 4.f, 5.f, 6.f},
   1085                                  TensorShape({2, 3}));
   1086   auto y = test::AsTensor<float>({-1.f, .5f, 2.f}, TensorShape({3, 1}));
   1087   Tensor dx;
   1088   Tensor dy;
   1089   MatMulGrad(x, false, y, false, &dx, &dy);
   1090   auto dz = test::AsTensor<float>({1.f, 1.f}, TensorShape({2, 1}));
   1091   test::ExpectClose(dx, MatMul(dz, false, y, true));
   1092   test::ExpectClose(dy, MatMul(x, true, dz, false));
   1093 }
   1094 
   1095 TEST_F(MathGradTest, MatMul_01) {
   1096   auto x = test::AsTensor<float>({1.f, 2.f, 3.f, 4.f, 5.f, 6.f},
   1097                                  TensorShape({2, 3}));
   1098   auto y = test::AsTensor<float>({-1.f, .5f, 2.f}, TensorShape({1, 3}));
   1099   Tensor dx;
   1100   Tensor dy;
   1101   MatMulGrad(x, false, y, true, &dx, &dy);
   1102   auto dz = test::AsTensor<float>({1.f, 1.f}, TensorShape({2, 1}));
   1103   test::ExpectClose(dx, MatMul(dz, false, y, false));
   1104   test::ExpectClose(dy, MatMul(dz, true, x, false));
   1105 }
   1106 
   1107 TEST_F(MathGradTest, MatMul_10) {
   1108   auto x = test::AsTensor<float>({1.f, 2.f, 3.f, 4.f, 5.f, 6.f},
   1109                                  TensorShape({3, 2}));
   1110   auto y = test::AsTensor<float>({-1.f, .5f, 2.f}, TensorShape({3, 1}));
   1111   Tensor dx;
   1112   Tensor dy;
   1113   MatMulGrad(x, true, y, false, &dx, &dy);
   1114   auto dz = test::AsTensor<float>({1.f, 1.f}, TensorShape({2, 1}));
   1115   test::ExpectClose(dx, MatMul(y, false, dz, true));
   1116   test::ExpectClose(dy, MatMul(x, false, dz, false));
   1117 }
   1118 
   1119 TEST_F(MathGradTest, MatMul_11) {
   1120   auto x = test::AsTensor<float>({1.f, 2.f, 3.f, 4.f, 5.f, 6.f},
   1121                                  TensorShape({3, 2}));
   1122   auto y = test::AsTensor<float>({-1.f, .5f, 2.f}, TensorShape({1, 3}));
   1123   Tensor dx;
   1124   Tensor dy;
   1125   MatMulGrad(x, true, y, true, &dx, &dy);
   1126   auto dz = test::AsTensor<float>({1.f, 1.f}, TensorShape({2, 1}));
   1127   test::ExpectClose(dx, MatMul(y, true, dz, true));
   1128   test::ExpectClose(dy, MatMul(dz, true, x, true));
   1129 }
   1130 
   1131 // TODO{lukeiwanski}: Implement BatchMatMul for SYCL
   1132 #ifndef TENSORFLOW_USE_SYCL
   1133 TEST_F(MathGradTest, BatchMatMul_00) {
   1134   auto x = test::AsTensor<float>({1.f, 2.f, 3.f, 4.f, 5.f, 6.f},
   1135                                  TensorShape({1, 2, 3}));
   1136   auto y = test::AsTensor<float>({-1.f, .5f, 2.f}, TensorShape({1, 3, 1}));
   1137   Tensor dx;
   1138   Tensor dy;
   1139   BatchMatMulGrad(x, false, y, false, &dx, &dy);
   1140   auto dz = test::AsTensor<float>({1.f, 1.f}, TensorShape({1, 2, 1}));
   1141   test::ExpectClose(dx, BatchMatMul(dz, false, y, true));
   1142   test::ExpectClose(dy, BatchMatMul(x, true, dz, false));
   1143 }
   1144 
   1145 TEST_F(MathGradTest, BatchMatMul_01) {
   1146   auto x = test::AsTensor<float>({1.f, 2.f, 3.f, 4.f, 5.f, 6.f},
   1147                                  TensorShape({1, 2, 3}));
   1148   auto y = test::AsTensor<float>({-1.f, .5f, 2.f}, TensorShape({1, 1, 3}));
   1149   Tensor dx;
   1150   Tensor dy;
   1151   BatchMatMulGrad(x, false, y, true, &dx, &dy);
   1152   auto dz = test::AsTensor<float>({1.f, 1.f}, TensorShape({1, 2, 1}));
   1153   test::ExpectClose(dx, BatchMatMul(dz, false, y, false));
   1154   test::ExpectClose(dy, BatchMatMul(dz, true, x, false));
   1155 }
   1156 
   1157 TEST_F(MathGradTest, BatchMatMul_10) {
   1158   auto x = test::AsTensor<float>({1.f, 2.f, 3.f, 4.f, 5.f, 6.f},
   1159                                  TensorShape({1, 3, 2}));
   1160   auto y = test::AsTensor<float>({-1.f, .5f, 2.f}, TensorShape({1, 3, 1}));
   1161   Tensor dx;
   1162   Tensor dy;
   1163   BatchMatMulGrad(x, true, y, false, &dx, &dy);
   1164   auto dz = test::AsTensor<float>({1.f, 1.f}, TensorShape({1, 2, 1}));
   1165   test::ExpectClose(dx, BatchMatMul(y, false, dz, true));
   1166   test::ExpectClose(dy, BatchMatMul(x, false, dz, false));
   1167 }
   1168 
   1169 TEST_F(MathGradTest, BatchMatMul_11) {
   1170   auto x = test::AsTensor<float>({1.f, 2.f, 3.f, 4.f, 5.f, 6.f},
   1171                                  TensorShape({1, 3, 2}));
   1172   auto y = test::AsTensor<float>({-1.f, .5f, 2.f}, TensorShape({1, 1, 3}));
   1173   Tensor dx;
   1174   Tensor dy;
   1175   BatchMatMulGrad(x, true, y, true, &dx, &dy);
   1176   auto dz = test::AsTensor<float>({1.f, 1.f}, TensorShape({1, 2, 1}));
   1177   test::ExpectClose(dx, BatchMatMul(y, true, dz, true));
   1178   test::ExpectClose(dy, BatchMatMul(dz, true, x, true));
   1179 }
   1180 #endif  // TENSORFLOW_USE_SYCL
   1181 
   1182 TEST_F(MathGradTest, Sum_dim0) {
   1183   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
   1184                                  TensorShape({2, 3}));
   1185   auto i = test::AsTensor<int32>({0}, TensorShape({}));
   1186   Tensor dx;
   1187   Tensor di;
   1188   ReductionGrad("Sum", x, i, &dx, &di);
   1189   test::ExpectTensorEqual<float>(
   1190       dx, test::AsTensor<float>({1.f, 1.f, 1.f, 1.f, 1.f, 1.f},
   1191                                 TensorShape({2, 3})));
   1192   test::ExpectTensorEqual<int32>(di,
   1193                                  test::AsTensor<int32>({0}, TensorShape({})));
   1194 }
   1195 
   1196 TEST_F(MathGradTest, Sum_dim1) {
   1197   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
   1198                                  TensorShape({2, 3}));
   1199   auto i = test::AsTensor<int32>({1}, TensorShape({}));
   1200   Tensor dx;
   1201   Tensor di;
   1202   ReductionGrad("Sum", x, i, &dx, &di);
   1203   test::ExpectTensorEqual<float>(
   1204       dx, test::AsTensor<float>({1.f, 1.f, 1.f, 1.f, 1.f, 1.f},
   1205                                 TensorShape({2, 3})));
   1206   test::ExpectTensorEqual<int32>(di,
   1207                                  test::AsTensor<int32>({0}, TensorShape({})));
   1208 }
   1209 
   1210 TEST_F(MathGradTest, Mean_dim0) {
   1211   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
   1212                                  TensorShape({2, 3}));
   1213   auto i = test::AsTensor<int32>({0}, TensorShape({}));
   1214   Tensor dx;
   1215   Tensor di;
   1216   ReductionGrad("Mean", x, i, &dx, &di);
   1217   test::ExpectTensorEqual<float>(
   1218       dx, test::AsTensor<float>(
   1219               {1.f / 2, 1.f / 2, 1.f / 2, 1.f / 2, 1.f / 2, 1.f / 2},
   1220               TensorShape({2, 3})));
   1221   test::ExpectTensorEqual<int32>(di,
   1222                                  test::AsTensor<int32>({0}, TensorShape({})));
   1223 }
   1224 
   1225 TEST_F(MathGradTest, Mean_dim1) {
   1226   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
   1227                                  TensorShape({2, 3}));
   1228   auto i = test::AsTensor<int32>({1}, TensorShape({}));
   1229   Tensor dx;
   1230   Tensor di;
   1231   ReductionGrad("Mean", x, i, &dx, &di);
   1232   test::ExpectTensorEqual<float>(
   1233       dx, test::AsTensor<float>(
   1234               {1.f / 3, 1.f / 3, 1.f / 3, 1.f / 3, 1.f / 3, 1.f / 3},
   1235               TensorShape({2, 3})));
   1236   test::ExpectTensorEqual<int32>(di,
   1237                                  test::AsTensor<int32>({0}, TensorShape({})));
   1238 }
   1239 
   1240 TEST_F(MathGradTest, Mean_dim0_dim1) {
   1241   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
   1242                                  TensorShape({2, 3}));
   1243   auto i = test::AsTensor<int32>({0, 1}, TensorShape({2}));
   1244   Tensor dx;
   1245   Tensor di;
   1246   ReductionGrad("Mean", x, i, &dx, &di);
   1247   test::ExpectTensorEqual<float>(
   1248       dx, test::AsTensor<float>(
   1249               {1.f / 6, 1.f / 6, 1.f / 6, 1.f / 6, 1.f / 6, 1.f / 6},
   1250               TensorShape({2, 3})));
   1251   test::ExpectTensorEqual<int32>(
   1252       di, test::AsTensor<int32>({0, 0}, TensorShape({2})));
   1253 }
   1254 
   1255 TEST_F(MathGradTest, Min_dim0) {
   1256   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
   1257                                  TensorShape({2, 3}));
   1258   auto i = test::AsTensor<int32>({0}, TensorShape({}));
   1259   Tensor dx;
   1260   Tensor di;
   1261   ReductionGrad("Min", x, i, &dx, &di);
   1262   test::ExpectTensorEqual<float>(
   1263       dx, test::AsTensor<float>({1.f, 1.f, 1.f, 0.f, 0.f, 0.f},
   1264                                 TensorShape({2, 3})));
   1265   test::ExpectTensorEqual<int32>(di,
   1266                                  test::AsTensor<int32>({0}, TensorShape({})));
   1267 }
   1268 
   1269 TEST_F(MathGradTest, Min_dim1) {
   1270   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
   1271                                  TensorShape({2, 3}));
   1272   auto i = test::AsTensor<int32>({1}, TensorShape({}));
   1273   Tensor dx;
   1274   Tensor di;
   1275   ReductionGrad("Min", x, i, &dx, &di);
   1276   test::ExpectTensorEqual<float>(
   1277       dx, test::AsTensor<float>({1.f, 0.f, 0.f, 1.f, 0.f, 0.f},
   1278                                 TensorShape({2, 3})));
   1279   test::ExpectTensorEqual<int32>(di,
   1280                                  test::AsTensor<int32>({0}, TensorShape({})));
   1281 }
   1282 
   1283 TEST_F(MathGradTest, Min_dim0_dim1) {
   1284   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
   1285                                  TensorShape({2, 3}));
   1286   auto i = test::AsTensor<int32>({0, 1}, TensorShape({2}));
   1287   Tensor dx;
   1288   Tensor di;
   1289   ReductionGrad("Min", x, i, &dx, &di);
   1290   test::ExpectTensorEqual<float>(
   1291       dx, test::AsTensor<float>({1.f, 0.f, 0.f, 0.f, 0.f, 0.f},
   1292                                 TensorShape({2, 3})));
   1293   test::ExpectTensorEqual<int32>(
   1294       di, test::AsTensor<int32>({0, 0}, TensorShape({2})));
   1295 }
   1296 
   1297 TEST_F(MathGradTest, Min_dim0_dim1_Dups) {
   1298   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, -3.f},
   1299                                  TensorShape({2, 3}));
   1300   auto i = test::AsTensor<int32>({0, 1}, TensorShape({2}));
   1301   Tensor dx;
   1302   Tensor di;
   1303   ReductionGrad("Min", x, i, &dx, &di);
   1304   test::ExpectTensorEqual<float>(
   1305       dx, test::AsTensor<float>({.5f, 0.f, 0.f, 0.f, 0.f, .5f},
   1306                                 TensorShape({2, 3})));
   1307   test::ExpectTensorEqual<int32>(
   1308       di, test::AsTensor<int32>({0, 0}, TensorShape({2})));
   1309 }
   1310 
   1311 TEST_F(MathGradTest, Max_dim0) {
   1312   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
   1313                                  TensorShape({2, 3}));
   1314   auto i = test::AsTensor<int32>({0}, TensorShape({}));
   1315   Tensor dx;
   1316   Tensor di;
   1317   ReductionGrad("Max", x, i, &dx, &di);
   1318   LOG(INFO) << dx.SummarizeValue(6);
   1319   test::ExpectTensorEqual<float>(
   1320       dx, test::AsTensor<float>({0.f, 0.f, 0.f, 1.f, 1.f, 1.f},
   1321                                 TensorShape({2, 3})));
   1322   test::ExpectTensorEqual<int32>(di,
   1323                                  test::AsTensor<int32>({0}, TensorShape({})));
   1324 }
   1325 
   1326 TEST_F(MathGradTest, Max_dim1) {
   1327   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
   1328                                  TensorShape({2, 3}));
   1329   auto i = test::AsTensor<int32>({1}, TensorShape({}));
   1330   Tensor dx;
   1331   Tensor di;
   1332   ReductionGrad("Max", x, i, &dx, &di);
   1333   test::ExpectTensorEqual<float>(
   1334       dx, test::AsTensor<float>({0.f, 0.f, 1.f, 0.f, 0.f, 1.f},
   1335                                 TensorShape({2, 3})));
   1336   test::ExpectTensorEqual<int32>(di,
   1337                                  test::AsTensor<int32>({0}, TensorShape({})));
   1338 }
   1339 
   1340 TEST_F(MathGradTest, Max_dim0_dim1) {
   1341   auto x = test::AsTensor<float>({-3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
   1342                                  TensorShape({2, 3}));
   1343   auto i = test::AsTensor<int32>({0, 1}, TensorShape({2}));
   1344   Tensor dx;
   1345   Tensor di;
   1346   ReductionGrad("Max", x, i, &dx, &di);
   1347   test::ExpectTensorEqual<float>(
   1348       dx, test::AsTensor<float>({0.f, 0.f, 0.f, 0.f, 0.f, 1.f},
   1349                                 TensorShape({2, 3})));
   1350   test::ExpectTensorEqual<int32>(
   1351       di, test::AsTensor<int32>({0, 0}, TensorShape({2})));
   1352 }
   1353 
   1354 TEST_F(MathGradTest, Max_dim0_dim1_Dups) {
   1355   auto x = test::AsTensor<float>({3.f, -2.f, -1.f, 1.f, 2.f, 3.f},
   1356                                  TensorShape({2, 3}));
   1357   auto i = test::AsTensor<int32>({0, 1}, TensorShape({2}));
   1358   Tensor dx;
   1359   Tensor di;
   1360   ReductionGrad("Max", x, i, &dx, &di);
   1361   test::ExpectTensorEqual<float>(
   1362       dx, test::AsTensor<float>({.5f, 0.f, 0.f, 0.f, 0.f, .5f},
   1363                                 TensorShape({2, 3})));
   1364   test::ExpectTensorEqual<int32>(
   1365       di, test::AsTensor<int32>({0, 0}, TensorShape({2})));
   1366 }
   1367 
   1368 }  // namespace
   1369 }  // namespace tensorflow
   1370