• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
17from __future__ import absolute_import
18from __future__ import division
19from __future__ import print_function
20
21import numpy as np
22
23from tensorflow.python.framework import constant_op
24from tensorflow.python.framework import dtypes
25from tensorflow.python.framework import errors_impl
26from tensorflow.python.framework import ops
27from tensorflow.python.framework import test_util
28from tensorflow.python.ops import array_ops
29from tensorflow.python.ops import gradient_checker
30from tensorflow.python.ops import variables
31from tensorflow.python.platform import test
32
33
34def np_split_squeeze(array, axis):
35  axis_len = array.shape[axis]
36  return [
37      np.squeeze(
38          arr, axis=(axis,)) for arr in np.split(
39              array, axis_len, axis=axis)
40  ]
41
42
43class StackOpTest(test.TestCase):
44
45  def randn(self, shape, dtype):
46    data = np.random.randn(*shape)
47    if dtype == np.bool:
48      return data < 0  # Naive casting yields True with P(1)!
49    else:
50      return data.astype(dtype)
51
52  @test_util.run_deprecated_v1
53  def testSimple(self):
54    np.random.seed(7)
55    with self.session():
56      for shape in (2,), (3,), (2, 3), (3, 2), (8, 2, 10):
57        rank = len(shape)
58        for axis in range(-rank, rank):
59          for dtype in [np.bool, np.float32, np.int32, np.int64]:
60            data = self.randn(shape, dtype)
61            xs = np_split_squeeze(data, axis)
62            # Stack back into a single tensorflow tensor
63            with self.subTest(shape=shape, axis=axis, dtype=dtype):
64              c = array_ops.stack(xs, axis=axis)
65              self.assertAllEqual(c, data)
66
67  def testSimpleParallelCPU(self):
68    # tf.parallel_stack is only supported in graph mode.
69    with ops.Graph().as_default():
70      np.random.seed(7)
71      with test_util.device(use_gpu=False):
72        for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2), (100, 24, 24, 3):
73          with self.subTest(shape=shape):
74            data = self.randn(shape, np.float32)
75            xs = list(map(constant_op.constant, data))
76            c = array_ops.parallel_stack(xs)
77            self.assertAllEqual(c, data)
78
79  def testSimpleParallelGPU(self):
80    # tf.parallel_stack is only supported in graph mode.
81    with ops.Graph().as_default():
82      with test_util.device(use_gpu=True):
83        for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2), (100, 24, 24, 3):
84          with self.subTest(shape=shape):
85            data = self.randn(shape, np.float32)
86            xs = list(map(constant_op.constant, data))
87            c = array_ops.parallel_stack(xs)
88            self.assertAllEqual(c, data)
89
90  @test_util.run_deprecated_v1
91  def testConst(self):
92    np.random.seed(7)
93    with self.session():
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      # Check on a variety of shapes and types
100      for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2), (8, 2, 10):
101        for dtype in [np.bool, np.float32, np.int16, np.int32, np.int64]:
102          with self.subTest(shape=shape, dtype=dtype):
103            data = self.randn(shape, dtype)
104            # Stack back into a single tensorflow tensor directly using np array
105            c = array_ops.stack(data)
106            # This is implemented via a Const:
107            self.assertEqual(c.op.type, "Const")
108            self.assertAllEqual(c, data)
109
110            # Python lists also work for 1-D case:
111            if len(shape) == 1:
112              data_list = list(data)
113              cl = array_ops.stack(data_list)
114              self.assertEqual(cl.op.type, "Const")
115              self.assertAllEqual(cl, data)
116
117  def testConstParallelCPU(self):
118    # tf.parallel_stack is only supported in graph mode.
119    with ops.Graph().as_default():
120      np.random.seed(7)
121      with test_util.device(use_gpu=False):
122        for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2), (8, 2, 10):
123          with self.subTest(shape=shape):
124            data = self.randn(shape, np.float32)
125            if len(shape) == 1:
126              data_list = list(data)
127              cl = array_ops.parallel_stack(data_list)
128              self.assertAllEqual(cl, data)
129
130            data = self.randn(shape, np.float32)
131            c = array_ops.parallel_stack(data)
132            self.assertAllEqual(c, data)
133
134  def testConstParallelGPU(self):
135    # tf.parallel_stack is only supported in graph mode.
136    with ops.Graph().as_default():
137      np.random.seed(7)
138      with test_util.device(use_gpu=True):
139        for shape in (2,), (3,), (2, 3), (3, 2), (4, 3, 2):
140          with self.subTest(shape=shape):
141            data = self.randn(shape, np.float32)
142            if len(shape) == 1:
143              data_list = list(data)
144              cl = array_ops.parallel_stack(data_list)
145              self.assertAllEqual(cl, data)
146
147            data = self.randn(shape, np.float32)
148            c = array_ops.parallel_stack(data)
149            self.assertAllEqual(c, data)
150
151  @test_util.run_deprecated_v1
152  def testGradientsAxis0(self):
153    np.random.seed(7)
154    for shape in (2,), (3,), (2, 3), (3, 2), (8, 2, 10):
155      data = np.random.randn(*shape)
156      shapes = [shape[1:]] * shape[0]
157      with self.subTest(shape=shape):
158        with self.cached_session():
159          # TODO(irving): Remove list() once we handle maps correctly
160          xs = list(map(constant_op.constant, data))
161          c = array_ops.stack(xs)
162          err = gradient_checker.compute_gradient_error(xs, shapes, c, shape)
163          self.assertLess(err, 1e-6)
164
165  @test_util.run_deprecated_v1
166  def testGradientsAxis1(self):
167    np.random.seed(7)
168    for shape in (2, 3), (3, 2), (8, 2, 10):
169      data = np.random.randn(*shape)
170      shapes = [shape[1:]] * shape[0]
171      out_shape = list(shape[1:])
172      out_shape.insert(1, shape[0])
173      with self.subTest(shape=shape):
174        with self.cached_session():
175          # TODO(irving): Remove list() once we handle maps correctly
176          xs = list(map(constant_op.constant, data))
177          c = array_ops.stack(xs, axis=1)
178          err = gradient_checker.compute_gradient_error(xs, shapes, c,
179                                                        out_shape)
180          self.assertLess(err, 1e-6)
181
182  def testZeroSizeCPU(self):
183    # tf.parallel_stack is only supported in graph mode.
184    with ops.Graph().as_default():
185      # Verify that stack doesn't crash for zero size inputs
186      with test_util.device(use_gpu=False):
187        for shape in (0,), (3, 0), (0, 3):
188          with self.subTest(shape=shape):
189            x = np.zeros((2,) + shape).astype(np.int32)
190            p = self.evaluate(array_ops.stack(list(x)))
191            self.assertAllEqual(p, x)
192
193            p = self.evaluate(array_ops.parallel_stack(list(x)))
194            self.assertAllEqual(p, x)
195
196  def testZeroSizeGPU(self):
197    # tf.parallel_stack is only supported in graph mode.
198    with ops.Graph().as_default():
199      # Verify that stack doesn't crash for zero size inputs
200      with test_util.device(use_gpu=True):
201        for shape in (0,), (3, 0), (0, 3):
202          with self.subTest(shape=shape):
203            x = np.zeros((2,) + shape).astype(np.int32)
204            p = self.evaluate(array_ops.stack(list(x)))
205            self.assertAllEqual(p, x)
206
207            p = self.evaluate(array_ops.parallel_stack(list(x)))
208            self.assertAllEqual(p, x)
209
210  def testAxis0DefaultCPU(self):
211    # tf.parallel_stack is only supported in graph mode.
212    with ops.Graph().as_default():
213      with test_util.device(use_gpu=False):
214        t = [constant_op.constant([1, 2, 3]), constant_op.constant([4, 5, 6])]
215        stacked = self.evaluate(array_ops.stack(t))
216        parallel_stacked = self.evaluate(array_ops.parallel_stack(t))
217
218      expected = np.array([[1, 2, 3], [4, 5, 6]])
219      self.assertAllEqual(stacked, expected)
220      self.assertAllEqual(parallel_stacked, expected)
221
222  def testAxis0DefaultGPU(self):
223    # tf.parallel_stack is only supported in graph mode.
224    with ops.Graph().as_default():
225      with test_util.device(use_gpu=True):
226        t = [constant_op.constant([1, 2, 3]), constant_op.constant([4, 5, 6])]
227        stacked = self.evaluate(array_ops.stack(t))
228        parallel_stacked = self.evaluate(array_ops.parallel_stack(t))
229
230      expected = np.array([[1, 2, 3], [4, 5, 6]])
231      self.assertAllEqual(stacked, expected)
232      self.assertAllEqual(parallel_stacked, expected)
233
234  def testAgainstNumpy(self):
235    # For 1 to 5 dimensions.
236    for shape in (3,), (2, 2, 3), (4, 1, 2, 2), (8, 2, 10):
237      rank = len(shape)
238      expected = self.randn(shape, np.float32)
239      for dtype in [np.bool, np.float32, np.int32, np.int64]:
240        # For all the possible axis to split it, including negative indices.
241        for axis in range(-rank, rank):
242          test_arrays = np_split_squeeze(expected, axis)
243
244          with self.cached_session():
245            with self.subTest(shape=shape, dtype=dtype, axis=axis):
246              actual_pack = array_ops.stack(test_arrays, axis=axis)
247              self.assertEqual(expected.shape, actual_pack.get_shape())
248              actual_pack = self.evaluate(actual_pack)
249
250              actual_stack = array_ops.stack(test_arrays, axis=axis)
251              self.assertEqual(expected.shape, actual_stack.get_shape())
252              actual_stack = self.evaluate(actual_stack)
253
254              self.assertNDArrayNear(expected, actual_stack, 1e-6)
255
256  def testDimOutOfRange(self):
257    t = [constant_op.constant([1, 2, 3]), constant_op.constant([4, 5, 6])]
258    with self.assertRaisesRegex(ValueError, r"axis = 2 not in \[-2, 2\)"):
259      array_ops.stack(t, axis=2)
260
261  def testDimOutOfNegativeRange(self):
262    t = [constant_op.constant([1, 2, 3]), constant_op.constant([4, 5, 6])]
263    with self.assertRaisesRegex(ValueError, r"axis = -3 not in \[-2, 2\)"):
264      array_ops.stack(t, axis=-3)
265
266  def testComplex(self):
267    np.random.seed(7)
268    with self.session():
269      for shape in (2,), (3,), (2, 3), (3, 2), (8, 2, 10):
270        for dtype in [np.complex64, np.complex128]:
271          with self.subTest(shape=shape, dtype=dtype):
272            data = self.randn(shape, dtype)
273            xs = list(map(constant_op.constant, data))
274            c = array_ops.stack(xs)
275            self.assertAllEqual(self.evaluate(c), data)
276
277
278class AutomaticStackingTest(test.TestCase):
279
280  @test_util.run_deprecated_v1
281  def testSimple(self):
282    with self.session():
283      self.assertAllEqual(
284          [1, 0, 2],
285          ops.convert_to_tensor([1, constant_op.constant(0), 2]).eval())
286      self.assertAllEqual([[0, 0, 0], [0, 1, 0], [0, 0, 0]],
287                          ops.convert_to_tensor(
288                              [[0, 0, 0], [0, constant_op.constant(1), 0],
289                               [0, 0, 0]]).eval())
290      self.assertAllEqual([[0, 0, 0], [0, 1, 0], [0, 0, 0]],
291                          ops.convert_to_tensor(
292                              [[0, 0, 0], constant_op.constant([0, 1, 0]),
293                               [0, 0, 0]]).eval())
294      self.assertAllEqual([[0, 0, 0], [0, 1, 0], [0, 0, 0]],
295                          ops.convert_to_tensor([
296                              constant_op.constant([0, 0, 0]),
297                              constant_op.constant([0, 1, 0]),
298                              constant_op.constant([0, 0, 0])
299                          ]).eval())
300
301  def testWithNDArray(self):
302    with self.session():
303      result = ops.convert_to_tensor([[[0., 0.],
304                                       constant_op.constant([1., 1.])],
305                                      np.array(
306                                          [[2., 2.], [3., 3.]],
307                                          dtype=np.float32)])
308      self.assertAllEqual([[[0., 0.], [1., 1.]], [[2., 2.], [3., 3.]]],
309                          self.evaluate(result))
310
311  @test_util.run_deprecated_v1
312  def testVariable(self):
313    with self.session():
314      v = variables.Variable(17)
315      result = ops.convert_to_tensor([[0, 0, 0], [0, v, 0], [0, 0, 0]])
316      self.evaluate(v.initializer)
317      self.assertAllEqual([[0, 0, 0], [0, 17, 0], [0, 0, 0]],
318                          self.evaluate(result))
319
320      v.assign(38).op.run()
321      self.assertAllEqual([[0, 0, 0], [0, 38, 0], [0, 0, 0]],
322                          self.evaluate(result))
323
324  def testDtype(self):
325    t_0 = ops.convert_to_tensor([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]])
326    self.assertEqual(dtypes.float32, t_0.dtype)
327
328    t_1 = ops.convert_to_tensor([[0., 0., 0.], constant_op.constant(
329        [0., 0., 0.], dtype=dtypes.float64), [0., 0., 0.]])
330    self.assertEqual(dtypes.float64, t_1.dtype)
331
332    t_2 = ops.convert_to_tensor(
333        [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], dtype=dtypes.float64)
334    self.assertEqual(dtypes.float64, t_2.dtype)
335
336    t_3 = ops.convert_to_tensor(
337        [[0., 0., 0.],
338         constant_op.constant([0., 0., 0.], dtype=dtypes.float64), [0., 0., 0.]
339        ],
340        dtype=dtypes.float32)
341    self.assertEqual(dtypes.float32, t_3.dtype)
342
343    t_4 = ops.convert_to_tensor(
344        [constant_op.constant([0., 0., 0.], dtype=dtypes.float64)],
345        dtype=dtypes.float32)
346    self.assertEqual(dtypes.float32, t_4.dtype)
347
348    with self.assertRaises(TypeError):
349      ops.convert_to_tensor([
350          constant_op.constant(
351              [0., 0., 0.], dtype=dtypes.float32), constant_op.constant(
352                  [0., 0., 0.], dtype=dtypes.float64), [0., 0., 0.]
353      ])
354
355  def testDtypeConversionWhenTensorDtypeMismatch(self):
356    t_0 = ops.convert_to_tensor([0., 0., 0.])
357    self.assertEqual(dtypes.float32, t_0.dtype)
358
359    t_1 = ops.convert_to_tensor([0, 0, 0])
360    self.assertEqual(dtypes.int32, t_1.dtype)
361
362    t_2 = ops.convert_to_tensor([t_0, t_0, t_1], dtype=dtypes.float64)
363    self.assertEqual(dtypes.float64, t_2.dtype)
364
365  @test_util.run_deprecated_v1
366  def testPlaceholder(self):
367    with self.session():
368      # Test using placeholder with a defined shape.
369      ph_0 = array_ops.placeholder(dtypes.int32, shape=[])
370      result_0 = ops.convert_to_tensor([[0, 0, 0], [0, ph_0, 0], [0, 0, 0]])
371      self.assertAllEqual(
372          [[0, 0, 0], [0, 1, 0], [0, 0, 0]], result_0.eval(feed_dict={ph_0: 1}))
373      self.assertAllEqual(
374          [[0, 0, 0], [0, 2, 0], [0, 0, 0]], result_0.eval(feed_dict={ph_0: 2}))
375
376      # Test using placeholder with an undefined shape.
377      ph_1 = array_ops.placeholder(dtypes.int32)
378      result_1 = ops.convert_to_tensor([[0, 0, 0], [0, ph_1, 0], [0, 0, 0]])
379      self.assertAllEqual(
380          [[0, 0, 0], [0, 1, 0], [0, 0, 0]], result_1.eval(feed_dict={ph_1: 1}))
381      self.assertAllEqual(
382          [[0, 0, 0], [0, 2, 0], [0, 0, 0]], result_1.eval(feed_dict={ph_1: 2}))
383
384  @test_util.run_deprecated_v1
385  def testShapeErrors(self):
386    # Static shape error.
387    ph_0 = array_ops.placeholder(dtypes.int32, shape=[1])
388    with self.assertRaises(ValueError):
389      ops.convert_to_tensor([[0, 0, 0], [0, ph_0, 0], [0, 0, 0]])
390
391    # Dynamic shape error.
392    ph_1 = array_ops.placeholder(dtypes.int32)
393    result_1 = ops.convert_to_tensor([[0, 0, 0], [0, ph_1, 0], [0, 0, 0]])
394    with self.session():
395      with self.assertRaises(errors_impl.InvalidArgumentError):
396        result_1.eval(feed_dict={ph_1: [1]})
397
398
399if __name__ == "__main__":
400  test.main()
401