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