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 """Functional tests for scan ops.""" 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.framework import dtypes 25 from tensorflow.python.framework import errors_impl 26 from tensorflow.python.framework import ops 27 from tensorflow.python.ops import gradient_checker 28 from tensorflow.python.ops import math_ops 29 from tensorflow.python.platform import test 30 31 32 def numpy_reverse(x, axis): 33 length = len(x.shape) 34 if axis < 0: 35 axis = length + axis 36 37 ix = [ 38 slice(None, None, -1) if i == axis else slice(None) for i in range(length) 39 ] 40 return x[ix] 41 42 43 def handle_options(func, x, axis, exclusive, reverse): 44 """Adds tf options to numpy scan ops.""" 45 length = len(x.shape) 46 if axis < 0: 47 axis = length + axis 48 49 if reverse: 50 x = numpy_reverse(x, axis) 51 52 if exclusive: 53 ix_head = [slice(0, 1) if i == axis else slice(None) for i in range(length)] 54 ix_init = [ 55 slice(0, -1) if i == axis else slice(None) for i in range(length) 56 ] 57 if func == np.cumsum: 58 init = np.zeros_like(x[ix_head]) 59 elif func == np.cumprod: 60 init = np.ones_like(x[ix_head]) 61 else: 62 raise ValueError("Unknown scan function.") 63 x = np.concatenate([init, func(x[ix_init], axis)], axis=axis) 64 else: 65 x = func(x, axis=axis) 66 67 if reverse: 68 x = numpy_reverse(x, axis) 69 return x 70 71 72 class CumsumTest(test.TestCase): 73 74 valid_dtypes = [ 75 np.int32, np.int64, np.float16, np.float32, np.float64, np.complex64, 76 np.complex128 77 ] 78 79 def _compare(self, x, axis, exclusive, reverse): 80 np_out = handle_options(np.cumsum, x, axis, exclusive, reverse) 81 with self.test_session(use_gpu=True): 82 tf_out = math_ops.cumsum(x, axis, exclusive, reverse).eval() 83 84 self.assertAllClose(np_out, tf_out) 85 86 def _compareAll(self, x, axis): 87 for exclusive in [True, False]: 88 for reverse in [True, False]: 89 self._compare(x, axis, exclusive, reverse) 90 91 def testEmpty(self): 92 for dtype in self.valid_dtypes: 93 x = np.zeros([0]).astype(dtype) 94 for axis in (-1, 0): 95 self._compareAll(x, axis) 96 97 def testAxisType(self): 98 for dtype in self.valid_dtypes: 99 x = np.arange(1, 6).reshape([5]).astype(dtype) 100 for axis_dtype in [dtypes.int64, dtypes.int32]: 101 with self.test_session(use_gpu=True): 102 axis = constant_op.constant(0, axis_dtype) 103 tf_out = math_ops.cumsum(x, axis).eval() 104 105 def test1D(self): 106 for dtype in self.valid_dtypes: 107 x = np.arange(1, 6).reshape([5]).astype(dtype) 108 for axis in (-1, 0): 109 self._compareAll(x, axis) 110 111 def test2D(self): 112 for dtype in self.valid_dtypes: 113 x = np.arange(0, 10).reshape([2, 5]).astype(dtype) 114 for axis in (-2, -1, 0, 1): 115 self._compareAll(x, axis) 116 117 def test3D(self): 118 for dtype in self.valid_dtypes: 119 x = np.arange(0, 20).reshape([2, 2, 5]).astype(dtype) 120 for axis in (-3, -2, -1, 0, 1, 2): 121 self._compareAll(x, axis) 122 123 def test6D(self): 124 for dtype in self.valid_dtypes: 125 x = np.arange(1, 145).reshape([2, 2, 3, 3, 2, 2]).astype(dtype) 126 for axis in range(-6, 6, 3): 127 self._compareAll(x, axis) 128 129 def testInvalidAxis(self): 130 x = np.arange(0, 10).reshape([2, 5]).astype(np.float32) 131 input_tensor = ops.convert_to_tensor(x) 132 with self.test_session(use_gpu=True): 133 with self.assertRaisesWithPredicateMatch( 134 errors_impl.InvalidArgumentError, 135 lambda e: "Expected scan axis in the range [-2, 2)" in str(e)): 136 math_ops.cumsum(input_tensor, -3).eval() 137 with self.assertRaisesWithPredicateMatch( 138 errors_impl.InvalidArgumentError, 139 lambda e: "Expected scan axis in the range [-2, 2)" in str(e)): 140 math_ops.cumsum(input_tensor, 2).eval() 141 with self.assertRaisesWithPredicateMatch( 142 errors_impl.InvalidArgumentError, 143 lambda e: "axis must be a scalar" in str(e)): 144 math_ops.cumsum(input_tensor, [0]).eval() 145 146 def _compareGradient(self, shape, axis, exclusive, reverse): 147 x = np.arange(0, 50).reshape(shape).astype(np.float64) 148 with self.test_session(use_gpu=True): 149 t = ops.convert_to_tensor(x) 150 result = math_ops.cumsum(t, axis, exclusive, reverse) 151 jacob_t, jacob_n = gradient_checker.compute_gradient( 152 t, shape, result, shape, x_init_value=x, delta=1) 153 self.assertAllClose(jacob_t, jacob_n, rtol=1e-8, atol=1e-8) 154 155 def testGradient(self): 156 for axis in (-1, 0): 157 self._compareGradient([50], axis, False, False) 158 159 def testGradientReverse(self): 160 for axis in (-1, 0): 161 self._compareGradient([50], axis, False, True) 162 163 def testGradientExclusive(self): 164 for axis in (-1, 0): 165 self._compareGradient([50], axis, True, False) 166 167 def testGradientExclusiveReverse(self): 168 for axis in (-1, 0): 169 self._compareGradient([50], axis, True, True) 170 171 def testGradient2D(self): 172 for axis in (-1, 0, 1): 173 for exclusive in [True, False]: 174 for reverse in [True, False]: 175 self._compareGradient([5, 10], axis, exclusive, reverse) 176 177 178 class CumprodTest(test.TestCase): 179 180 valid_dtypes = [ 181 np.int32, np.int64, np.float16, np.float32, np.float64, np.complex64, 182 np.complex128 183 ] 184 185 def _compare(self, x, axis, exclusive, reverse): 186 np_out = handle_options(np.cumprod, x, axis, exclusive, reverse) 187 with self.test_session(use_gpu=True): 188 tf_out = math_ops.cumprod(x, axis, exclusive, reverse).eval() 189 190 self.assertAllClose(np_out, tf_out) 191 192 def _compareAll(self, x, axis): 193 for exclusive in [True, False]: 194 for reverse in [True, False]: 195 self._compare(x, axis, exclusive, reverse) 196 197 def testEmpty(self): 198 for dtype in self.valid_dtypes: 199 x = np.zeros([0]).astype(dtype) 200 for axis in (-1, 0): 201 self._compareAll(x, axis) 202 203 def testAxisType(self): 204 for dtype in self.valid_dtypes: 205 x = np.arange(1, 6).reshape([5]).astype(dtype) 206 for axis_dtype in [dtypes.int64, dtypes.int32]: 207 with self.test_session(use_gpu=True): 208 axis = constant_op.constant(0, axis_dtype) 209 tf_out = math_ops.cumprod(x, axis).eval() 210 211 def test1D(self): 212 for dtype in self.valid_dtypes: 213 x = np.arange(1, 6).reshape([5]).astype(dtype) 214 for axis in (-1, 0): 215 self._compareAll(x, axis) 216 217 def test2D(self): 218 for dtype in self.valid_dtypes: 219 x = np.arange(1, 11).reshape([2, 5]).astype(dtype) 220 for axis in (-2, -1, 0, 1): 221 self._compareAll(x, axis) 222 223 def test3D(self): 224 for dtype in self.valid_dtypes: 225 x = np.arange(1, 21).reshape([2, 2, 5]).astype(dtype) 226 for axis in (-3, -2, -1, 0, 1, 2): 227 self._compareAll(x, axis) 228 229 def test6D(self): 230 for dtype in self.valid_dtypes: 231 x = np.arange(1, 145).reshape([2, 2, 3, 3, 2, 2]).astype(dtype) 232 for axis in range(-6, 6, 3): 233 self._compareAll(x, axis) 234 235 def testInvalidAxis(self): 236 x = np.arange(0, 10).reshape([2, 5]).astype(np.float32) 237 input_tensor = ops.convert_to_tensor(x) 238 with self.test_session(use_gpu=True): 239 with self.assertRaisesWithPredicateMatch( 240 errors_impl.InvalidArgumentError, 241 lambda e: "Expected scan axis in the range [-2, 2)" in str(e)): 242 math_ops.cumprod(input_tensor, -3).eval() 243 with self.assertRaisesWithPredicateMatch( 244 errors_impl.InvalidArgumentError, 245 lambda e: "Expected scan axis in the range [-2, 2)" in str(e)): 246 math_ops.cumprod(input_tensor, 2).eval() 247 with self.assertRaisesWithPredicateMatch( 248 errors_impl.InvalidArgumentError, 249 lambda e: "axis must be a scalar" in str(e)): 250 math_ops.cumprod(input_tensor, [0]).eval() 251 252 def _compareGradient(self, shape, axis, exclusive, reverse): 253 x = np.arange(1, 9).reshape(shape).astype(np.float64) 254 with self.test_session(use_gpu=True): 255 t = ops.convert_to_tensor(x) 256 result = math_ops.cumprod(t, axis, exclusive, reverse) 257 jacob_t, jacob_n = gradient_checker.compute_gradient( 258 t, shape, result, shape, x_init_value=x, delta=1) 259 self.assertAllClose(jacob_t, jacob_n, rtol=1e-8, atol=1e-8) 260 261 def testGradient(self): 262 for axis in (-1, 0): 263 self._compareGradient([8], axis, False, False) 264 265 def testGradientReverse(self): 266 for axis in (-1, 0): 267 self._compareGradient([8], axis, False, True) 268 269 def testGradientExclusive(self): 270 for axis in (-1, 0): 271 self._compareGradient([8], axis, True, False) 272 273 def testGradientExclusiveReverse(self): 274 for axis in (-1, 0): 275 self._compareGradient([8], axis, True, True) 276 277 def testGradient2D(self): 278 for axis in (-2, -1, 0, 1): 279 for exclusive in [True, False]: 280 for reverse in [True, False]: 281 self._compareGradient([2, 4], axis, exclusive, reverse) 282 283 284 if __name__ == "__main__": 285 test.main() 286