| # Copyright 2018 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 low-level eager execution primitives.""" |
| |
| import sys |
| import traceback |
| |
| import numpy as np |
| |
| from tensorflow.python import pywrap_tfe |
| from tensorflow.python.eager import backprop |
| from tensorflow.python.eager import context |
| from tensorflow.python.eager import core |
| from tensorflow.python.eager import def_function |
| from tensorflow.python.eager import test |
| from tensorflow.python.framework import constant_op |
| from tensorflow.python.framework import dtypes |
| from tensorflow.python.framework import errors |
| from tensorflow.python.framework import ops |
| from tensorflow.python.framework import test_ops |
| from tensorflow.python.framework import test_util |
| from tensorflow.python.ops import array_ops |
| from tensorflow.python.ops import array_ops_stack |
| from tensorflow.python.ops import math_ops |
| from tensorflow.python.ops import random_ops |
| from tensorflow.python.ops import resource_variable_ops |
| |
| |
| @test_util.with_eager_op_as_function |
| class Tests(test.TestCase): |
| |
| @test_util.assert_no_new_tensors |
| @test_util.assert_no_garbage_created |
| def testFastpathExecute_MatMulCorrectResponse(self): |
| a_2_by_2 = random_ops.random_uniform((2, 2)) |
| b_2_by_2 = random_ops.random_uniform((2, 2)) |
| |
| a_100_by_784 = random_ops.random_uniform((100, 784)) |
| b_100_by_784 = random_ops.random_uniform((100, 784)) |
| |
| ctx = context.context() |
| ctx.ensure_initialized() |
| |
| self.assertAllClose( |
| math_ops.matmul(a_2_by_2, b_2_by_2), |
| pywrap_tfe.TFE_Py_FastPathExecute(ctx, "MatMul", None, |
| a_2_by_2, b_2_by_2, "transpose_a", |
| False, "transpose_b", False)) |
| self.assertAllClose( |
| math_ops.matmul(a_100_by_784, b_100_by_784, transpose_b=True), |
| pywrap_tfe.TFE_Py_FastPathExecute(ctx, "MatMul", None, |
| a_100_by_784, b_100_by_784, |
| "transpose_a", False, "transpose_b", |
| True)) |
| |
| @test_util.assert_no_new_tensors |
| @test_util.assert_no_garbage_created |
| def testFastpathExecute_ResourceVariableMatMulCorrectResponse(self): |
| ctx = context.context() |
| ctx.ensure_initialized() |
| |
| a_2_by_2 = constant_op.constant(1.0, shape=[2, 2]) |
| m = resource_variable_ops.ResourceVariable(a_2_by_2) |
| x = pywrap_tfe.TFE_Py_FastPathExecute(ctx, "MatMul", None, m, |
| m, "transpose_a", False, |
| "transpose_b", False) |
| y = pywrap_tfe.TFE_Py_FastPathExecute(ctx, "MatMul", None, |
| a_2_by_2, a_2_by_2, "transpose_a", |
| False, "transpose_b", False) |
| |
| self.assertAllEqual(x, y) |
| |
| @test_util.assert_no_new_tensors |
| @test_util.assert_no_garbage_created |
| def testFastpathExecute_TapeWrite(self): |
| ctx = context.context() |
| ctx.ensure_initialized() |
| |
| with backprop.GradientTape(persistent=True) as tape: |
| a_2_by_2 = constant_op.constant(1.0, shape=[2, 2]) |
| tape.watch(a_2_by_2) |
| z = pywrap_tfe.TFE_Py_FastPathExecute(ctx, "MatMul", None, |
| a_2_by_2, a_2_by_2, "transpose_a", |
| False, "transpose_b", False) |
| dz_dy = tape.gradient(z, [a_2_by_2])[0] |
| self.assertAllEqual(dz_dy.numpy(), |
| constant_op.constant(4.0, shape=[2, 2]).numpy()) |
| |
| @test_util.assert_no_new_tensors |
| @test_util.assert_no_garbage_created |
| def testFastpathExecute_ResourceVariableTapeWrite(self): |
| ctx = context.context() |
| ctx.ensure_initialized() |
| |
| with backprop.GradientTape(persistent=True) as tape: |
| a_2_by_2 = constant_op.constant(1.0, shape=[2, 2]) |
| m = resource_variable_ops.ResourceVariable(a_2_by_2) |
| tape.watch(m) |
| z = pywrap_tfe.TFE_Py_FastPathExecute(ctx, "MatMul", None, m, |
| m, "transpose_a", False, |
| "transpose_b", False) |
| dz_dy = tape.gradient(z, [m])[0] |
| self.assertAllEqual(dz_dy.numpy(), |
| constant_op.constant(4.0, shape=[2, 2]).numpy()) |
| |
| # Tests homogeneous list op |
| @test_util.assert_no_new_tensors |
| @test_util.assert_no_garbage_created |
| def testFastpathExecute_AddNCorrectResponse(self): |
| ctx = context.context() |
| ctx.ensure_initialized() |
| |
| a_2_by_2 = random_ops.random_uniform((2, 2)) |
| b_2_by_2 = random_ops.random_uniform((2, 2)) |
| |
| self.assertAllClose( |
| math_ops.add_n([a_2_by_2, b_2_by_2]), |
| pywrap_tfe.TFE_Py_FastPathExecute(ctx, "AddN", None, |
| [a_2_by_2, b_2_by_2])) |
| |
| # Tests homogeneous list op |
| @test_util.assert_no_new_tensors |
| @test_util.assert_no_garbage_created |
| def testFastpathExecute_AddNTapeWrite(self): |
| ctx = context.context() |
| ctx.ensure_initialized() |
| |
| a_2_by_2 = random_ops.random_uniform((2, 2)) |
| b_2_by_2 = random_ops.random_uniform((2, 2)) |
| |
| with backprop.GradientTape(persistent=True) as tape: |
| tape.watch(a_2_by_2) |
| tape.watch(b_2_by_2) |
| z1 = pywrap_tfe.TFE_Py_FastPathExecute(ctx, "AddN", None, |
| [a_2_by_2, b_2_by_2]) |
| z2 = math_ops.add_n([a_2_by_2, b_2_by_2]) |
| dz1_dy = tape.gradient(z1, [a_2_by_2])[0] |
| dz2_dy = tape.gradient(z2, [a_2_by_2])[0] |
| self.assertAllEqual(dz1_dy.numpy(), dz2_dy.numpy()) |
| |
| # Tests heterogeneous list op |
| @test_util.assert_no_new_tensors |
| @test_util.assert_no_garbage_created |
| def testFastpathExecute_IdentityNCorrectResponse(self): |
| ctx = context.context() |
| ctx.ensure_initialized() |
| |
| a_2_by_2 = random_ops.random_uniform((2, 2)) |
| b_2_by_2 = random_ops.random_uniform((2, 2)) |
| |
| self.assertAllClose( |
| array_ops.identity_n([a_2_by_2, b_2_by_2]), |
| pywrap_tfe.TFE_Py_FastPathExecute(ctx, "IdentityN", None, |
| [a_2_by_2, b_2_by_2])) |
| |
| # Tests heterogeneous list op |
| @test_util.assert_no_new_tensors |
| @test_util.assert_no_garbage_created |
| def testFastpathExecute_IdentityNTapeWrite(self): |
| ctx = context.context() |
| ctx.ensure_initialized() |
| |
| a_2_by_2 = random_ops.random_uniform((2, 2)) |
| b_2_by_2 = random_ops.random_uniform((2, 2)) |
| |
| with backprop.GradientTape(persistent=True) as tape: |
| tape.watch(a_2_by_2) |
| tape.watch(b_2_by_2) |
| z1 = pywrap_tfe.TFE_Py_FastPathExecute(ctx, "IdentityN", |
| None, [a_2_by_2, b_2_by_2]) |
| z2 = array_ops.identity_n([a_2_by_2, b_2_by_2]) |
| dz1_dy = tape.gradient(z1[0], [a_2_by_2])[0] |
| dz2_dy = tape.gradient(z2[0], [a_2_by_2])[0] |
| self.assertAllEqual(dz1_dy.numpy(), dz2_dy.numpy()) |
| |
| @test_util.assert_no_new_tensors |
| @test_util.assert_no_garbage_created |
| def testFastpathExecute_InvalidInputs(self): |
| a_2_by_2 = random_ops.random_uniform((2, 2)) |
| ctx = context.context() |
| ctx.ensure_initialized() |
| |
| assert ctx.executing_eagerly( |
| ), "The prototype doesn't contain C code for graph construction" |
| ctx_handle = ctx._handle # pylint: disable=protected-access |
| |
| # Not enough base params |
| with self.assertRaisesRegex(ValueError, |
| "at least 3 items in the input tuple"): |
| pywrap_tfe.TFE_Py_FastPathExecute(ctx, "Identity") |
| |
| # Not enough inputs |
| with self.assertRaisesRegex(ValueError, "Expected to be at least 4, was 3"): |
| pywrap_tfe.TFE_Py_FastPathExecute(ctx, "Identity", None) |
| |
| # Bad type |
| with self.assertRaisesRegex(TypeError, "expected a string for op_name"): |
| pywrap_tfe.TFE_Py_FastPathExecute(ctx, ctx_handle, None, |
| a_2_by_2) |
| |
| @test_util.assert_no_new_tensors |
| @test_util.assert_no_garbage_created |
| def testFastPathExecute_InvalidAttributes(self): |
| split_dim = constant_op.constant(0, dtype=dtypes.int32) |
| value = constant_op.constant([0, 1, 2, 3], dtype=dtypes.float32) |
| ctx = context.context() |
| ctx.ensure_initialized() |
| |
| with self.assertRaises(core._FallbackException): |
| pywrap_tfe.TFE_Py_FastPathExecute(ctx, "Split", None, |
| split_dim, value, "num_split", -1) |
| |
| @test_util.assert_no_new_tensors |
| @test_util.assert_no_garbage_created |
| def testFastPathExecute_VeryLargeOutputs(self): |
| split_dim = constant_op.constant(0, dtype=dtypes.int32) |
| value = constant_op.constant([0, 1, 2, 3], dtype=dtypes.float32) |
| ctx = context.context() |
| ctx.ensure_initialized() |
| |
| with self.assertRaisesRegex(ValueError, "Number of outputs is too big"): |
| pywrap_tfe.TFE_Py_FastPathExecute(ctx, "Split", None, split_dim, value, |
| "num_split", 1000000000000) |
| |
| @test_util.assert_no_new_tensors |
| @test_util.assert_no_garbage_created |
| def testSlowPathExecute_VeryLargeOutputs(self): |
| split_dim = constant_op.constant(0, dtype=dtypes.int32) |
| value = [0, 1, 2, 3] |
| ctx = context.context() |
| ctx.ensure_initialized() |
| |
| with self.assertRaises(core._FallbackException): |
| pywrap_tfe.TFE_Py_FastPathExecute(ctx, "Split", None, split_dim, value, |
| "num_split", 1000000000000) |
| |
| value = constant_op.constant(value) |
| attrs = ("num_split", 1000000000000, "T", value.dtype.as_datatype_enum) |
| with self.assertRaisesRegex(ValueError, "Number of outputs is too big"): |
| pywrap_tfe.TFE_Py_Execute(ctx._handle, None, "Split", [split_dim, value], |
| attrs, 1000000000000) |
| |
| @test_util.assert_no_new_tensors |
| @test_util.assert_no_garbage_created |
| def testInvalidNumOutputs(self): |
| with self.assertRaisesRegex( |
| Exception, r"Value for number_attr\(\) -1 < 0 \[Op:Split\]|" |
| r"Value for attr 'num_split' of -1 must be at least minimum 1"): |
| array_ops.split(value=[1, 2, 3], num_or_size_splits=-1) |
| |
| with self.assertRaisesRegex( |
| Exception, |
| r"Value for attr 'num_split' of 0 must be at least minimum 1"): |
| array_ops.split(value=[1, 2, 3], num_or_size_splits=0) |
| |
| def testIsFunction(self): |
| ctx = context.context() |
| self.assertFalse(ctx.has_function("not_a_function")) |
| |
| @def_function.function |
| def f(): |
| return 1. |
| |
| self.assertTrue(ctx.has_function(f.get_concrete_function().name)) |
| |
| def testEagerExecute_InvalidType(self): |
| # Test case for GitHub issue 26879. |
| with ops.Graph().as_default(): |
| a_2_by_2 = constant_op.constant(1.0, shape=[2, 2]) |
| m = resource_variable_ops.ResourceVariable(a_2_by_2) |
| with self.assertRaisesRegex(TypeError, |
| "Expected list for 'values' argument"): |
| _ = array_ops_stack.stack(m, axis=1) |
| |
| def testGraphResourceVariableRaisesFallback(self): |
| with ops.Graph().as_default(): |
| a_2_by_2 = constant_op.constant(1.0, shape=[2, 2]) |
| m = resource_variable_ops.ResourceVariable(a_2_by_2) |
| ctx = context.context() |
| ctx.ensure_initialized() |
| with self.assertRaises(core._FallbackException): |
| pywrap_tfe.TFE_Py_FastPathExecute(ctx, "MatMul", None, m, m, |
| "transpose_a", False, "transpose_b", |
| False) |
| |
| def testOpDefDefaultType(self): |
| im = np.random.randint( # pylint: disable=too-many-function-args |
| low=0, |
| high=65535, |
| size=100, |
| dtype=np.uint16).reshape(10, 10, 1) |
| |
| context.ensure_initialized() |
| |
| fastpath_dtype = test_ops.dtype_with_default_op(im).numpy() |
| slowpath_dtype = test_ops.dtype_with_default_op_eager_fallback( |
| im, None, context.context()).numpy() |
| # Ensure the fastpath and slowpath eager paths work. |
| self.assertEqual(fastpath_dtype, slowpath_dtype) |
| |
| with ops.Graph().as_default(), self.cached_session(): |
| graph_dtype_symbolic = test_ops.dtype_with_default_op(im) |
| |
| graph_dtype = self.evaluate(graph_dtype_symbolic) |
| # Ensure the eager path matches the graph path. |
| self.assertEqual(fastpath_dtype, graph_dtype) |
| |
| # Unfortunately, as of now, this doesn't work as expected on def_functions, |
| # since we convert the numpy arrays to tensors pre-tracing (which won't get |
| # overriddent by the default type). |
| @def_function.function |
| def func(im): |
| return test_ops.dtype_with_default_op(im) |
| |
| function_dtype = func(im).numpy() |
| self.assertNotEqual(fastpath_dtype, function_dtype) |
| |
| # Captures are OK, since they don't go through the conversion path. |
| @def_function.function |
| def func_captured(): |
| return test_ops.dtype_with_default_op(im) |
| |
| function_dtype = func_captured().numpy() |
| self.assertEqual(fastpath_dtype, function_dtype) |
| |
| def testConvertFromArrayInterface(self): |
| context.ensure_initialized() |
| ctx = context.context() |
| |
| class MyArrayClass(object): |
| |
| def __init__(self): |
| self.array = np.random.random(16) |
| |
| def __array__(self): |
| return self.array |
| |
| a = MyArrayClass() |
| t = ops.EagerTensor(a, device=ctx.device_name, dtype=None) |
| self.assertAllEqual(t, a) |
| |
| # TODO(b/147830189): Converting from EagerTensor should work. |
| # _ = ops.EagerTensor(t, device=ctx.device_name, dtype=None) |
| |
| # TODO(b/147828820): Converting with tensors should work. |
| # _ = ops.EagerTensor([[t]], device=ctx.device_name, dtype=None) |
| |
| def testFallbackErrorNotVisibleWhenFallbackMethodRaises(self): |
| ctx = context.context() |
| ctx.ensure_initialized() |
| |
| try: |
| math_ops.mat_mul([[1., 1.] * 2], [[1., 1.] * 3]) |
| except errors.InvalidArgumentError: |
| etype, value, tb = sys.exc_info() |
| full_exception_text = " ".join( |
| traceback.format_exception(etype, value, tb)) |
| |
| self.assertNotRegex(full_exception_text, "_FallbackException") |
| |
| def testIntAttrThatDoesNotFitIn32Bits(self): |
| # Tests bug where int attributes >= 2**31 raised an exception on platforms |
| # where sizeof(long) = 32 bits. |
| ctx = context.context() |
| ctx.ensure_initialized() |
| shape = constant_op.constant([10]) |
| minval = constant_op.constant(0) |
| maxval = constant_op.constant(10) |
| seed = 2**50 |
| pywrap_tfe.TFE_Py_FastPathExecute(ctx, "RandomUniformInt", None, |
| shape, minval, maxval, |
| "seed", seed) |
| |
| |
| if __name__ == "__main__": |
| test.main() |