Home | History | Annotate | Download | only in kernel_tests
      1 # Copyright 2015 The TensorFlow Authors. All Rights Reserved.
      2 #
      3 # Licensed under the Apache License, Version 2.0 (the "License");
      4 # you may not use this file except in compliance with the License.
      5 # You may obtain a copy of the License at
      6 #
      7 #     http://www.apache.org/licenses/LICENSE-2.0
      8 #
      9 # Unless required by applicable law or agreed to in writing, software
     10 # distributed under the License is distributed on an "AS IS" BASIS,
     11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 # See the License for the specific language governing permissions and
     13 # limitations under the License.
     14 # ==============================================================================
     15 """Tests for tensorflow.ops.linalg_grad."""
     16 
     17 from __future__ import absolute_import
     18 from __future__ import division
     19 from __future__ import print_function
     20 
     21 import numpy as np
     22 
     23 from tensorflow.python.framework import constant_op
     24 from tensorflow.python.ops import array_ops
     25 from tensorflow.python.ops import gradient_checker
     26 from tensorflow.python.ops import gradients_impl
     27 from tensorflow.python.ops import linalg_ops
     28 from tensorflow.python.ops import math_ops
     29 from tensorflow.python.platform import test as test_lib
     30 
     31 
     32 def _AddTest(test, op_name, testcase_name, fn):
     33   test_name = '_'.join(['test', op_name, testcase_name])
     34   if hasattr(test, test_name):
     35     raise RuntimeError('Test %s defined more than once' % test_name)
     36   setattr(test, test_name, fn)
     37 
     38 
     39 class ShapeTest(test_lib.TestCase):
     40 
     41   def testBatchGradientUnknownSize(self):
     42     with self.test_session():
     43       batch_size = constant_op.constant(3)
     44       matrix_size = constant_op.constant(4)
     45       batch_identity = array_ops.tile(
     46           array_ops.expand_dims(
     47               array_ops.diag(array_ops.ones([matrix_size])), 0),
     48           [batch_size, 1, 1])
     49       determinants = linalg_ops.matrix_determinant(batch_identity)
     50       reduced = math_ops.reduce_sum(determinants)
     51       sum_grad = gradients_impl.gradients(reduced, batch_identity)[0]
     52       self.assertAllClose(batch_identity.eval(), sum_grad.eval())
     53 
     54 
     55 class MatrixUnaryFunctorGradientTest(test_lib.TestCase):
     56   pass  # Filled in below
     57 
     58 
     59 def _GetMatrixUnaryFunctorGradientTest(functor_, dtype_, shape_, **kwargs_):
     60 
     61   def Test(self):
     62     with self.test_session(use_gpu=True):
     63       np.random.seed(1)
     64       a_np = np.random.uniform(
     65           low=-1.0, high=1.0,
     66           size=np.prod(shape_)).reshape(shape_).astype(dtype_)
     67       a = constant_op.constant(a_np)
     68       b = functor_(a, **kwargs_)
     69 
     70       # Optimal stepsize for central difference is O(epsilon^{1/3}).
     71       epsilon = np.finfo(dtype_).eps
     72       delta = epsilon**(1.0 / 3.0)
     73       # tolerance obtained by looking at actual differences using
     74       # np.linalg.norm(theoretical-numerical, np.inf) on -mavx build
     75       tol = 1e-6 if dtype_ == np.float64 else 0.05
     76 
     77       theoretical, numerical = gradient_checker.compute_gradient(
     78           a,
     79           a.get_shape().as_list(),
     80           b,
     81           b.get_shape().as_list(),
     82           x_init_value=a_np,
     83           delta=delta)
     84       self.assertAllClose(theoretical, numerical, atol=tol, rtol=tol)
     85 
     86   return Test
     87 
     88 
     89 class MatrixBinaryFunctorGradientTest(test_lib.TestCase):
     90   pass  # Filled in below
     91 
     92 
     93 def _GetMatrixBinaryFunctorGradientTest(functor_,
     94                                         dtype_,
     95                                         shape_,
     96                                         float32_tol_fudge=1.0,
     97                                         **kwargs_):
     98 
     99   def Test(self):
    100     # TODO(rmlarsen): Debug illegal address bug on CUDA and re-enable
    101     # GPU test for matrix_solve.
    102     use_gpu = False if functor_ == linalg_ops.matrix_solve else True
    103 
    104     with self.test_session(use_gpu=use_gpu):
    105       np.random.seed(1)
    106       a_np = np.random.uniform(
    107           low=-1.0, high=1.0,
    108           size=np.prod(shape_)).reshape(shape_).astype(dtype_)
    109       a = constant_op.constant(a_np)
    110 
    111       b_np = np.random.uniform(
    112           low=-1.0, high=1.0,
    113           size=np.prod(shape_)).reshape(shape_).astype(dtype_)
    114       b = constant_op.constant(b_np)
    115       c = functor_(a, b, **kwargs_)
    116 
    117       # Optimal stepsize for central difference is O(epsilon^{1/3}).
    118       epsilon = np.finfo(dtype_).eps
    119       delta = epsilon**(1.0 / 3.0)
    120       # tolerance obtained by looking at actual differences using
    121       # np.linalg.norm(theoretical-numerical, np.inf) on -mavx build
    122       tol = 1e-6 if dtype_ == np.float64 else float32_tol_fudge * 0.04
    123       # The gradients for a and b may be of very different magnitudes,
    124       # so to not get spurious failures we test them separately.
    125       for factor, factor_init in [a, a_np], [b, b_np]:
    126         theoretical, numerical = gradient_checker.compute_gradient(
    127             factor,
    128             factor.get_shape().as_list(),
    129             c,
    130             c.get_shape().as_list(),
    131             x_init_value=factor_init,
    132             delta=delta)
    133         self.assertAllClose(theoretical, numerical, atol=tol, rtol=tol)
    134 
    135   return Test
    136 
    137 
    138 if __name__ == '__main__':
    139   # Tests for gradients of binary matrix operations.
    140   for dtype in np.float32, np.float64:
    141     for size in 2, 5, 10:
    142       # We skip the rank 4, size 10 case: it is slow and conceptually covered
    143       # by the other cases.
    144       for extra in [(), (2,), (3,)] + [(3, 2)] * (size < 10):
    145         for adjoint in False, True:
    146           shape = extra + (size, size)
    147           name = '%s_%s_adj_%s' % (dtype.__name__, '_'.join(map(str, shape)),
    148                                    str(adjoint))
    149           _AddTest(MatrixBinaryFunctorGradientTest, 'MatrixSolveGradient', name,
    150                    _GetMatrixBinaryFunctorGradientTest(
    151                        linalg_ops.matrix_solve, dtype, shape, adjoint=adjoint))
    152 
    153           for lower in True, False:
    154             name = '%s_low_%s' % (name, lower)
    155             _AddTest(MatrixBinaryFunctorGradientTest,
    156                      'MatrixTriangularSolveGradient', name,
    157                      _GetMatrixBinaryFunctorGradientTest(
    158                          linalg_ops.matrix_triangular_solve,
    159                          dtype,
    160                          shape,
    161                          float32_tol_fudge=4.0,
    162                          adjoint=adjoint,
    163                          lower=lower))
    164 
    165   # Tests for gradients of unary matrix operations.
    166   for dtype in np.float32, np.float64:
    167     for size in 2, 5, 10:
    168       # We skip the rank 4, size 10 case: it is slow and conceptually covered
    169       # by the other cases.
    170       for extra in [(), (2,), (3,)] + [(3, 2)] * (size < 10):
    171         shape = extra + (size, size)
    172         name = '%s_%s' % (dtype.__name__, '_'.join(map(str, shape)))
    173         _AddTest(MatrixUnaryFunctorGradientTest, 'MatrixInverseGradient', name,
    174                  _GetMatrixUnaryFunctorGradientTest(linalg_ops.matrix_inverse,
    175                                                     dtype, shape))
    176         _AddTest(
    177             MatrixUnaryFunctorGradientTest, 'MatrixDeterminantGradient', name,
    178             _GetMatrixUnaryFunctorGradientTest(linalg_ops.matrix_determinant,
    179                                                dtype, shape))
    180 
    181   # Tests for gradients of matrix_solve_ls
    182   for dtype in np.float32, np.float64:
    183     for rows in 2, 5, 10:
    184       for cols in 2, 5, 10:
    185         for l2_regularization in 1e-6, 0.001, 1.0:
    186           shape = (rows, cols)
    187           name = '%s_%s_%s' % (dtype.__name__, '_'.join(map(str, shape)),
    188                                l2_regularization)
    189           _AddTest(
    190               MatrixBinaryFunctorGradientTest,
    191               'MatrixSolveLsGradient',
    192               name,
    193               # pylint: disable=long-lambda,g-long-lambda
    194               _GetMatrixBinaryFunctorGradientTest(
    195                   (lambda a, b, l=l2_regularization:
    196                    linalg_ops.matrix_solve_ls(a, b, l)),
    197                   dtype,
    198                   shape,
    199                   float32_tol_fudge=4.0))
    200 
    201   test_lib.main()
    202