| # Copyright 2015 The TensorFlow Authors. All Rights Reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # ============================================================================== |
| """Tests for tensorflow.ops.linalg_grad.""" |
| |
| from __future__ import absolute_import |
| from __future__ import division |
| from __future__ import print_function |
| |
| import numpy as np |
| |
| from tensorflow.python.framework import constant_op |
| from tensorflow.python.ops import array_ops |
| from tensorflow.python.ops import gradient_checker |
| from tensorflow.python.ops import gradients_impl |
| from tensorflow.python.ops import linalg_ops |
| from tensorflow.python.ops import math_ops |
| from tensorflow.python.ops.linalg import linalg_impl |
| from tensorflow.python.platform import test as test_lib |
| |
| |
| def _AddTest(test, op_name, testcase_name, fn): |
| test_name = '_'.join(['test', op_name, testcase_name]) |
| if hasattr(test, test_name): |
| raise RuntimeError('Test %s defined more than once' % test_name) |
| setattr(test, test_name, fn) |
| |
| |
| class ShapeTest(test_lib.TestCase): |
| |
| def testBatchGradientUnknownSize(self): |
| with self.cached_session(): |
| batch_size = constant_op.constant(3) |
| matrix_size = constant_op.constant(4) |
| batch_identity = array_ops.tile( |
| array_ops.expand_dims( |
| array_ops.diag(array_ops.ones([matrix_size])), 0), |
| [batch_size, 1, 1]) |
| determinants = linalg_ops.matrix_determinant(batch_identity) |
| reduced = math_ops.reduce_sum(determinants) |
| sum_grad = gradients_impl.gradients(reduced, batch_identity)[0] |
| self.assertAllClose(batch_identity.eval(), sum_grad.eval()) |
| |
| |
| class MatrixUnaryFunctorGradientTest(test_lib.TestCase): |
| pass # Filled in below |
| |
| |
| def _GetMatrixUnaryFunctorGradientTest(functor_, dtype_, shape_, **kwargs_): |
| |
| def Test(self): |
| with self.test_session(use_gpu=True): |
| np.random.seed(1) |
| a_np = np.random.uniform( |
| low=-1.0, high=1.0, |
| size=np.prod(shape_)).reshape(shape_).astype(dtype_) |
| a = constant_op.constant(a_np) |
| b = functor_(a, **kwargs_) |
| |
| # Optimal stepsize for central difference is O(epsilon^{1/3}). |
| epsilon = np.finfo(dtype_).eps |
| delta = epsilon**(1.0 / 3.0) |
| # tolerance obtained by looking at actual differences using |
| # np.linalg.norm(theoretical-numerical, np.inf) on -mavx build |
| tol = 1e-6 if dtype_ == np.float64 else 0.05 |
| |
| theoretical, numerical = gradient_checker.compute_gradient( |
| a, |
| a.get_shape().as_list(), |
| b, |
| b.get_shape().as_list(), |
| x_init_value=a_np, |
| delta=delta) |
| self.assertAllClose(theoretical, numerical, atol=tol, rtol=tol) |
| |
| return Test |
| |
| |
| class MatrixBinaryFunctorGradientTest(test_lib.TestCase): |
| pass # Filled in below |
| |
| |
| def _GetMatrixBinaryFunctorGradientTest(functor_, |
| dtype_, |
| shape_, |
| float32_tol_fudge=1.0, |
| **kwargs_): |
| |
| def Test(self): |
| # TODO(rmlarsen): Debug illegal address bug on CUDA and re-enable |
| # GPU test for matrix_solve. |
| use_gpu = False if functor_ == linalg_ops.matrix_solve else True |
| |
| with self.test_session(use_gpu=use_gpu): |
| np.random.seed(1) |
| a_np = np.random.uniform( |
| low=-1.0, high=1.0, |
| size=np.prod(shape_)).reshape(shape_).astype(dtype_) |
| a = constant_op.constant(a_np) |
| |
| b_np = np.random.uniform( |
| low=-1.0, high=1.0, |
| size=np.prod(shape_)).reshape(shape_).astype(dtype_) |
| b = constant_op.constant(b_np) |
| c = functor_(a, b, **kwargs_) |
| |
| # Optimal stepsize for central difference is O(epsilon^{1/3}). |
| epsilon = np.finfo(dtype_).eps |
| delta = epsilon**(1.0 / 3.0) |
| # tolerance obtained by looking at actual differences using |
| # np.linalg.norm(theoretical-numerical, np.inf) on -mavx build |
| tol = 1e-6 if dtype_ == np.float64 else float32_tol_fudge * 0.05 |
| # The gradients for a and b may be of very different magnitudes, |
| # so to not get spurious failures we test them separately. |
| for factor, factor_init in [a, a_np], [b, b_np]: |
| theoretical, numerical = gradient_checker.compute_gradient( |
| factor, |
| factor.get_shape().as_list(), |
| c, |
| c.get_shape().as_list(), |
| x_init_value=factor_init, |
| delta=delta) |
| self.assertAllClose(theoretical, numerical, atol=tol, rtol=tol) |
| |
| return Test |
| |
| |
| if __name__ == '__main__': |
| # Tests for gradients of binary matrix operations. |
| for dtype in np.float32, np.float64: |
| for size in 2, 5, 10: |
| # We skip the rank 4, size 10 case: it is slow and conceptually covered |
| # by the other cases. |
| for extra in [(), (2,), (3,)] + [(3, 2)] * (size < 10): |
| for adjoint in False, True: |
| shape = extra + (size, size) |
| name = '%s_%s_adj_%s' % (dtype.__name__, '_'.join(map(str, shape)), |
| str(adjoint)) |
| _AddTest(MatrixBinaryFunctorGradientTest, 'MatrixSolveGradient', name, |
| _GetMatrixBinaryFunctorGradientTest( |
| linalg_ops.matrix_solve, dtype, shape, adjoint=adjoint)) |
| |
| for lower in True, False: |
| name = '%s_low_%s' % (name, lower) |
| _AddTest(MatrixBinaryFunctorGradientTest, |
| 'MatrixTriangularSolveGradient', name, |
| _GetMatrixBinaryFunctorGradientTest( |
| linalg_ops.matrix_triangular_solve, |
| dtype, |
| shape, |
| float32_tol_fudge=4.0, |
| adjoint=adjoint, |
| lower=lower)) |
| |
| # Tests for gradients of unary matrix operations. |
| for dtype in np.float32, np.float64: |
| for size in 2, 5, 10: |
| # We skip the rank 4, size 10 case: it is slow and conceptually covered |
| # by the other cases. |
| for extra in [(), (2,), (3,)] + [(3, 2)] * (size < 10): |
| shape = extra + (size, size) |
| name = '%s_%s' % (dtype.__name__, '_'.join(map(str, shape))) |
| _AddTest(MatrixUnaryFunctorGradientTest, 'MatrixInverseGradient', name, |
| _GetMatrixUnaryFunctorGradientTest(linalg_ops.matrix_inverse, |
| dtype, shape)) |
| _AddTest(MatrixUnaryFunctorGradientTest, 'MatrixExponentialGradient', |
| name, |
| _GetMatrixUnaryFunctorGradientTest( |
| linalg_impl.matrix_exponential, dtype, shape)) |
| _AddTest( |
| MatrixUnaryFunctorGradientTest, 'MatrixDeterminantGradient', name, |
| _GetMatrixUnaryFunctorGradientTest(linalg_ops.matrix_determinant, |
| dtype, shape)) |
| _AddTest( |
| MatrixUnaryFunctorGradientTest, 'LogMatrixDeterminantGradient', |
| name, |
| _GetMatrixUnaryFunctorGradientTest( |
| lambda x: linalg_ops.log_matrix_determinant(x)[1], |
| dtype, shape)) |
| |
| # Tests for gradients of matrix_solve_ls |
| for dtype in np.float32, np.float64: |
| for rows in 2, 5, 10: |
| for cols in 2, 5, 10: |
| for l2_regularization in 1e-6, 0.001, 1.0: |
| shape = (rows, cols) |
| name = '%s_%s_%s' % (dtype.__name__, '_'.join(map(str, shape)), |
| l2_regularization) |
| _AddTest( |
| MatrixBinaryFunctorGradientTest, |
| 'MatrixSolveLsGradient', |
| name, |
| # pylint: disable=long-lambda,g-long-lambda |
| _GetMatrixBinaryFunctorGradientTest( |
| (lambda a, b, l=l2_regularization: |
| linalg_ops.matrix_solve_ls(a, b, l)), |
| dtype, |
| shape, |
| float32_tol_fudge=4.0)) |
| |
| test_lib.main() |