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 """Functional tests for Stack and ParallelStack 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 array_ops 28 from tensorflow.python.ops import gradient_checker 29 from tensorflow.python.ops import variables 30 from tensorflow.python.platform import test 31 32 33 def np_split_squeeze(array, axis): 34 axis_len = array.shape[axis] 35 return [ 36 np.squeeze( 37 arr, axis=(axis,)) for arr in np.split( 38 array, axis_len, axis=axis) 39 ] 40 41 42 class StackOpTest(test.TestCase): 43 44 def testSimple(self): 45 np.random.seed(7) 46 with self.test_session(use_gpu=True): 47 for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2): 48 for dtype in [np.bool, np.float32, np.int32, np.int64]: 49 data = np.random.randn(*shape).astype(dtype) 50 # Convert [data[0], data[1], ...] separately to tensorflow 51 # TODO(irving): Remove list() once we handle maps correctly 52 xs = list(map(constant_op.constant, data)) 53 # Stack back into a single tensorflow tensor 54 c = array_ops.stack(xs) 55 self.assertAllEqual(c.eval(), data) 56 57 def testSimpleParallelCPU(self): 58 np.random.seed(7) 59 with self.test_session(use_gpu=False): 60 for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2): 61 data = np.random.randn(*shape).astype(np.float32) 62 xs = list(map(constant_op.constant, data)) 63 c = array_ops.parallel_stack(xs) 64 self.assertAllEqual(c.eval(), data) 65 66 def testSimpleParallelGPU(self): 67 np.random.seed(7) 68 with self.test_session(use_gpu=True): 69 for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2): 70 data = np.random.randn(*shape).astype(np.float32) 71 xs = list(map(constant_op.constant, data)) 72 c = array_ops.parallel_stack(xs) 73 self.assertAllEqual(c.eval(), data) 74 75 def testConst(self): 76 np.random.seed(7) 77 with self.test_session(use_gpu=True): 78 for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2): 79 for dtype in [np.bool, np.float32, np.int32, np.int64]: 80 data = np.random.randn(*shape).astype(dtype) 81 # Stack back into a single tensorflow tensor directly using np array 82 c = array_ops.stack(data) 83 # This is implemented via a Const: 84 self.assertEqual(c.op.type, "Const") 85 self.assertAllEqual(c.eval(), data) 86 87 # Python lists also work for 1-D case: 88 if len(shape) == 1: 89 data_list = list(data) 90 cl = array_ops.stack(data_list) 91 self.assertEqual(cl.op.type, "Const") 92 self.assertAllEqual(cl.eval(), data) 93 94 # Verify that shape induction works with shapes produced via const stack 95 a = constant_op.constant([1, 2, 3, 4, 5, 6]) 96 b = array_ops.reshape(a, array_ops.stack([2, 3])) 97 self.assertAllEqual(b.get_shape(), [2, 3]) 98 99 def testConstParallelCPU(self): 100 np.random.seed(7) 101 with self.test_session(use_gpu=False): 102 for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2): 103 data = np.random.randn(*shape).astype(np.float32) 104 if len(shape) == 1: 105 data_list = list(data) 106 cl = array_ops.parallel_stack(data_list) 107 self.assertAllEqual(cl.eval(), data) 108 109 data = np.random.randn(*shape).astype(np.float32) 110 c = array_ops.parallel_stack(data) 111 self.assertAllEqual(c.eval(), data) 112 113 def testConstParallelGPU(self): 114 np.random.seed(7) 115 with self.test_session(use_gpu=True): 116 for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2): 117 data = np.random.randn(*shape).astype(np.float32) 118 if len(shape) == 1: 119 data_list = list(data) 120 cl = array_ops.parallel_stack(data_list) 121 self.assertAllEqual(cl.eval(), data) 122 123 data = np.random.randn(*shape).astype(np.float32) 124 c = array_ops.parallel_stack(data) 125 self.assertAllEqual(c.eval(), data) 126 127 def testGradientsAxis0(self): 128 np.random.seed(7) 129 for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2): 130 data = np.random.randn(*shape) 131 shapes = [shape[1:]] * shape[0] 132 with self.test_session(use_gpu=True): 133 # TODO(irving): Remove list() once we handle maps correctly 134 xs = list(map(constant_op.constant, data)) 135 c = array_ops.stack(xs) 136 err = gradient_checker.compute_gradient_error(xs, shapes, c, shape) 137 self.assertLess(err, 1e-6) 138 139 def testGradientsAxis1(self): 140 np.random.seed(7) 141 for shape in (2, 3), (3, 2), (4, 3, 2): 142 data = np.random.randn(*shape) 143 shapes = [shape[1:]] * shape[0] 144 out_shape = list(shape[1:]) 145 out_shape.insert(1, shape[0]) 146 with self.test_session(use_gpu=True): 147 # TODO(irving): Remove list() once we handle maps correctly 148 xs = list(map(constant_op.constant, data)) 149 c = array_ops.stack(xs, axis=1) 150 err = gradient_checker.compute_gradient_error(xs, shapes, c, out_shape) 151 self.assertLess(err, 1e-6) 152 153 def testZeroSizeCPU(self): 154 # Verify that stack doesn't crash for zero size inputs 155 with self.test_session(use_gpu=False): 156 for shape in (0,), (3, 0), (0, 3): 157 x = np.zeros((2,) + shape).astype(np.int32) 158 p = array_ops.stack(list(x)).eval() 159 self.assertAllEqual(p, x) 160 161 p = array_ops.parallel_stack(list(x)).eval() 162 self.assertAllEqual(p, x) 163 164 def testZeroSizeGPU(self): 165 # Verify that stack doesn't crash for zero size inputs 166 with self.test_session(use_gpu=True): 167 for shape in (0,), (3, 0), (0, 3): 168 x = np.zeros((2,) + shape).astype(np.int32) 169 p = array_ops.stack(list(x)).eval() 170 self.assertAllEqual(p, x) 171 172 p = array_ops.parallel_stack(list(x)).eval() 173 self.assertAllEqual(p, x) 174 175 def testAxis0DefaultCPU(self): 176 with self.test_session(use_gpu=False): 177 t = [constant_op.constant([1, 2, 3]), constant_op.constant([4, 5, 6])] 178 stacked = array_ops.stack(t).eval() 179 parallel_stacked = array_ops.parallel_stack(t).eval() 180 181 expected = np.array([[1, 2, 3], [4, 5, 6]]) 182 self.assertAllEqual(stacked, expected) 183 self.assertAllEqual(parallel_stacked, expected) 184 185 def testAxis0DefaultGPU(self): 186 with self.test_session(use_gpu=True): 187 t = [constant_op.constant([1, 2, 3]), constant_op.constant([4, 5, 6])] 188 stacked = array_ops.stack(t).eval() 189 parallel_stacked = array_ops.parallel_stack(t).eval() 190 191 expected = np.array([[1, 2, 3], [4, 5, 6]]) 192 self.assertAllEqual(stacked, expected) 193 self.assertAllEqual(parallel_stacked, expected) 194 195 def testAgainstNumpy(self): 196 # For 1 to 5 dimensions. 197 for i in range(1, 6): 198 expected = np.random.random(np.random.permutation(i) + 1) 199 200 # For all the possible axis to split it, including negative indices. 201 for j in range(-i, i): 202 test_arrays = np_split_squeeze(expected, j) 203 204 with self.test_session(use_gpu=True): 205 actual_pack = array_ops.stack(test_arrays, axis=j) 206 self.assertEqual(expected.shape, actual_pack.get_shape()) 207 actual_pack = actual_pack.eval() 208 209 actual_stack = array_ops.stack(test_arrays, axis=j) 210 self.assertEqual(expected.shape, actual_stack.get_shape()) 211 actual_stack = actual_stack.eval() 212 213 self.assertNDArrayNear(expected, actual_stack, 1e-6) 214 215 def testDimOutOfRange(self): 216 t = [constant_op.constant([1, 2, 3]), constant_op.constant([4, 5, 6])] 217 with self.assertRaisesRegexp(ValueError, r"axis = 2 not in \[-2, 2\)"): 218 array_ops.stack(t, axis=2) 219 220 def testDimOutOfNegativeRange(self): 221 t = [constant_op.constant([1, 2, 3]), constant_op.constant([4, 5, 6])] 222 with self.assertRaisesRegexp(ValueError, r"axis = -3 not in \[-2, 2\)"): 223 array_ops.stack(t, axis=-3) 224 225 226 class AutomaticStackingTest(test.TestCase): 227 228 def testSimple(self): 229 with self.test_session(use_gpu=True): 230 self.assertAllEqual( 231 [1, 0, 2], 232 ops.convert_to_tensor([1, constant_op.constant(0), 2]).eval()) 233 self.assertAllEqual([[0, 0, 0], [0, 1, 0], [0, 0, 0]], 234 ops.convert_to_tensor( 235 [[0, 0, 0], [0, constant_op.constant(1), 0], 236 [0, 0, 0]]).eval()) 237 self.assertAllEqual([[0, 0, 0], [0, 1, 0], [0, 0, 0]], 238 ops.convert_to_tensor( 239 [[0, 0, 0], constant_op.constant([0, 1, 0]), 240 [0, 0, 0]]).eval()) 241 self.assertAllEqual([[0, 0, 0], [0, 1, 0], [0, 0, 0]], 242 ops.convert_to_tensor([ 243 constant_op.constant([0, 0, 0]), 244 constant_op.constant([0, 1, 0]), 245 constant_op.constant([0, 0, 0]) 246 ]).eval()) 247 248 def testWithNDArray(self): 249 with self.test_session(use_gpu=True): 250 result = ops.convert_to_tensor([[[0., 0.], 251 constant_op.constant([1., 1.])], 252 np.array( 253 [[2., 2.], [3., 3.]], 254 dtype=np.float32)]) 255 self.assertAllEqual([[[0., 0.], [1., 1.]], [[2., 2.], [3., 3.]]], 256 result.eval()) 257 258 def testVariable(self): 259 with self.test_session(use_gpu=True): 260 v = variables.Variable(17) 261 result = ops.convert_to_tensor([[0, 0, 0], [0, v, 0], [0, 0, 0]]) 262 v.initializer.run() 263 self.assertAllEqual([[0, 0, 0], [0, 17, 0], [0, 0, 0]], result.eval()) 264 265 v.assign(38).op.run() 266 self.assertAllEqual([[0, 0, 0], [0, 38, 0], [0, 0, 0]], result.eval()) 267 268 def testDtype(self): 269 t_0 = ops.convert_to_tensor([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]]) 270 self.assertEqual(dtypes.float32, t_0.dtype) 271 272 t_1 = ops.convert_to_tensor([[0., 0., 0.], constant_op.constant( 273 [0., 0., 0.], dtype=dtypes.float64), [0., 0., 0.]]) 274 self.assertEqual(dtypes.float64, t_1.dtype) 275 276 t_2 = ops.convert_to_tensor( 277 [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], dtype=dtypes.float64) 278 self.assertEqual(dtypes.float64, t_2.dtype) 279 280 with self.assertRaises(TypeError): 281 ops.convert_to_tensor([ 282 constant_op.constant( 283 [0., 0., 0.], dtype=dtypes.float32), constant_op.constant( 284 [0., 0., 0.], dtype=dtypes.float64), [0., 0., 0.] 285 ]) 286 287 with self.assertRaises(TypeError): 288 ops.convert_to_tensor( 289 [[0., 0., 0.], constant_op.constant( 290 [0., 0., 0.], dtype=dtypes.float64), [0., 0., 0.]], 291 dtype=dtypes.float32) 292 293 with self.assertRaises(TypeError): 294 ops.convert_to_tensor( 295 [constant_op.constant( 296 [0., 0., 0.], dtype=dtypes.float64)], 297 dtype=dtypes.float32) 298 299 def testPlaceholder(self): 300 with self.test_session(use_gpu=True): 301 # Test using placeholder with a defined shape. 302 ph_0 = array_ops.placeholder(dtypes.int32, shape=[]) 303 result_0 = ops.convert_to_tensor([[0, 0, 0], [0, ph_0, 0], [0, 0, 0]]) 304 self.assertAllEqual( 305 [[0, 0, 0], [0, 1, 0], [0, 0, 0]], result_0.eval(feed_dict={ph_0: 1})) 306 self.assertAllEqual( 307 [[0, 0, 0], [0, 2, 0], [0, 0, 0]], result_0.eval(feed_dict={ph_0: 2})) 308 309 # Test using placeholder with an undefined shape. 310 ph_1 = array_ops.placeholder(dtypes.int32) 311 result_1 = ops.convert_to_tensor([[0, 0, 0], [0, ph_1, 0], [0, 0, 0]]) 312 self.assertAllEqual( 313 [[0, 0, 0], [0, 1, 0], [0, 0, 0]], result_1.eval(feed_dict={ph_1: 1})) 314 self.assertAllEqual( 315 [[0, 0, 0], [0, 2, 0], [0, 0, 0]], result_1.eval(feed_dict={ph_1: 2})) 316 317 def testShapeErrors(self): 318 # Static shape error. 319 ph_0 = array_ops.placeholder(dtypes.int32, shape=[1]) 320 with self.assertRaises(ValueError): 321 ops.convert_to_tensor([[0, 0, 0], [0, ph_0, 0], [0, 0, 0]]) 322 323 # Dynamic shape error. 324 ph_1 = array_ops.placeholder(dtypes.int32) 325 result_1 = ops.convert_to_tensor([[0, 0, 0], [0, ph_1, 0], [0, 0, 0]]) 326 with self.test_session(use_gpu=True): 327 with self.assertRaises(errors_impl.InvalidArgumentError): 328 result_1.eval(feed_dict={ph_1: [1]}) 329 330 331 if __name__ == "__main__": 332 test.main() 333