From 3cd41cf72a998ac800f91e1dc507e4e8638330ab Mon Sep 17 00:00:00 2001 From: Jonathan Hseu Date: Tue, 8 Nov 2016 16:54:57 -0800 Subject: [PATCH] Rename tf.Tensor to tf.Output - Swap the alias direction and fix tests broken by the swap - Export Output in tensorflow.__all__ Change: 138583261 --- RELEASE.md | 2 + tensorflow/python/framework/framework_lib.py | 1 + tensorflow/python/framework/ops.py | 54 +++++++++---------- .../python/kernel_tests/constant_op_test.py | 12 ++--- .../python/kernel_tests/slice_op_test.py | 2 +- 5 files changed, 35 insertions(+), 36 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 861da161157a3b..729a2379216378 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -10,6 +10,8 @@ BUS_ANY was used. by adding .ok() to the call. * The C API type `TF_Session` has been renamed to `TF_DeprecatedSession`. Please use `TF_SessionWithGraph` instead. +* Renamed Tensor to Output in the Python API. Tensor will be an alias for Output + until TensorFlow 2.0 is released. # Release 0.11.0 diff --git a/tensorflow/python/framework/framework_lib.py b/tensorflow/python/framework/framework_lib.py index 4f44041df73179..1728c2c1c662c9 100644 --- a/tensorflow/python/framework/framework_lib.py +++ b/tensorflow/python/framework/framework_lib.py @@ -20,6 +20,7 @@ @@Graph @@Operation +@@Output @@Tensor ## Tensor types diff --git a/tensorflow/python/framework/ops.py b/tensorflow/python/framework/ops.py index 0c9a9707a987e9..73bae6e20f209f 100644 --- a/tensorflow/python/framework/ops.py +++ b/tensorflow/python/framework/ops.py @@ -193,31 +193,28 @@ class _TensorLike(object): pass -class Tensor(_TensorLike): +class Output(_TensorLike): """Represents one of the outputs of an `Operation`. - *Note:* the `Tensor` class will be replaced by `Output` in the future. - Currently these two are aliases for each other. - - A `Tensor` is a symbolic handle to one of the outputs of an + An `Output` is a symbolic handle to one of the outputs of an `Operation`. It does not hold the values of that operation's output, but instead provides a means of computing those values in a TensorFlow [`Session`](../../api_docs/python/client.md#Session). This class has two primary purposes: - 1. A `Tensor` can be passed as an input to another `Operation`. + 1. An `Output` can be passed as an input to another `Operation`. This builds a dataflow connection between operations, which enables TensorFlow to execute an entire `Graph` that represents a large, multi-step computation. 2. After the graph has been launched in a session, the value of the - `Tensor` can be computed by passing it to + `Output` can be computed by passing it to [`Session.run()`](../../api_docs/python/client.md#Session.run). `t.eval()` is a shortcut for calling `tf.get_default_session().run(t)`. - In the following example, `c`, `d`, and `e` are symbolic `Tensor` + In the following example, `c`, `d`, and `e` are symbolic `Output` objects, whereas `result` is a numpy array that stores a concrete value: @@ -285,7 +282,7 @@ class Tensor(_TensorLike): } def __init__(self, op, value_index, dtype): - """Creates a new `Tensor`. + """Creates a new `Output`. Args: op: An `Operation`. `Operation` that computes this tensor. @@ -302,7 +299,7 @@ def __init__(self, op, value_index, dtype): self._value_index = value_index self._dtype = dtypes.as_dtype(dtype) self._shape = tensor_shape.unknown_shape() - # List of operations that use this Tensor as input. We maintain this list + # List of operations that use this Output as input. We maintain this list # to easily navigate a computation graph. self._consumers = [] @@ -379,7 +376,7 @@ def get_shape(self): In some cases, the inferred shape may have unknown dimensions. If the caller has additional information about the values of these - dimensions, `Tensor.set_shape()` can be used to augment the + dimensions, `Output.set_shape()` can be used to augment the inferred shape. Returns: @@ -450,10 +447,10 @@ def _as_node_def_input(self): """Return a value to use for the NodeDef "input" attribute. The returned string can be used in a NodeDef "input" attribute - to indicate that the NodeDef uses this Tensor as input. + to indicate that the NodeDef uses this Output as input. Raises: - ValueError: if this Tensor's Operation does not have a name. + ValueError: if this Output's Operation does not have a name. Returns: a string. @@ -466,7 +463,7 @@ def _as_node_def_input(self): return "%s:%d" % (self._op.name, self._value_index) def __str__(self): - return "Tensor(\"%s\"%s%s%s)" % ( + return "Output(\"%s\"%s%s%s)" % ( self.name, (", shape=%s" % self.get_shape()) if self.get_shape().ndims is not None else "", @@ -474,7 +471,7 @@ def __str__(self): (", device=%s" % self.device) if self.device else "") def __repr__(self): - return "" % ( + return "" % ( self.name, self.get_shape(), self._dtype.name) def __hash__(self): @@ -485,37 +482,37 @@ def __eq__(self, other): # Necessary to support Python's collection membership operators return id(self) == id(other) - # NOTE(mrry): This enables the Tensor's overloaded "right" binary + # NOTE(mrry): This enables the Output's overloaded "right" binary # operators to run when the left operand is an ndarray, because it - # accords the Tensor class higher priority than an ndarray, or a + # accords the Output class higher priority than an ndarray, or a # numpy matrix. # TODO(mrry): Convert this to using numpy's __numpy_ufunc__ - # mechanism, which allows more control over how Tensors interact + # mechanism, which allows more control over how Outputs interact # with ndarrays. __array_priority__ = 100 @staticmethod def _override_operator(operator, func): - _override_helper(Tensor, operator, func) + _override_helper(Output, operator, func) def __iter__(self): """Dummy method to prevent iteration. Do not call. NOTE(mrry): If we register __getitem__ as an overloaded operator, - Python will valiantly attempt to iterate over the Tensor from 0 to + Python will valiantly attempt to iterate over the Output from 0 to infinity. Declaring this method prevents this unintended behavior. Raises: TypeError: when invoked. """ - raise TypeError("'Tensor' object is not iterable.") + raise TypeError("'Output' object is not iterable.") def __bool__(self): """Dummy method to prevent a tensor from being used as a Python `bool`. This overload raises a `TypeError` when the user inadvertently - treats a `Tensor` as a boolean (e.g. in an `if` statement). For + treats an `Output` as a boolean (e.g. in an `if` statement). For example: ```python @@ -527,12 +524,12 @@ def __bool__(self): ``` This disallows ambiguities between testing the Python value vs testing the - dynamic condition of the `Tensor`. + dynamic condition of the `Output`. Raises: `TypeError`. """ - raise TypeError("Using a `tf.Tensor` as a Python `bool` is not allowed. " + raise TypeError("Using a `tf.Output` as a Python `bool` is not allowed. " "Use `if t is not None:` instead of `if t:` to test if a " "tensor is defined, and use TensorFlow ops such as " "tf.cond to execute subgraphs conditioned on the value of " @@ -546,7 +543,7 @@ def __nonzero__(self): Raises: `TypeError`. """ - raise TypeError("Using a `tf.Tensor` as a Python `bool` is not allowed. " + raise TypeError("Using a `tf.Output` as a Python `bool` is not allowed. " "Use `if t is not None:` instead of `if t:` to test if a " "tensor is defined, and use TensorFlow ops such as " "tf.cond to execute subgraphs conditioned on the value of " @@ -559,12 +556,12 @@ def eval(self, feed_dict=None, session=None): produce the inputs needed for the operation that produces this tensor. - *N.B.* Before invoking `Tensor.eval()`, its graph must have been + *N.B.* Before invoking `Output.eval()`, its graph must have been launched in a session, and either a default session must be available, or `session` must be specified explicitly. Args: - feed_dict: A dictionary that maps `Tensor` objects to feed values. + feed_dict: A dictionary that maps `Output` objects to feed values. See [`Session.run()`](../../api_docs/python/client.md#Session.run) for a description of the valid feed values. session: (Optional.) The `Session` to be used to evaluate this tensor. If @@ -577,8 +574,7 @@ def eval(self, feed_dict=None, session=None): return _eval_using_default_session(self, feed_dict, self.graph, session) -# TODO(josh11b): Switch everyone from "Tensor" to "Output" to match C++ API. -Output = Tensor +Tensor = Output def _TensorTensorConversionFunction(t, dtype=None, name=None, as_ref=False): diff --git a/tensorflow/python/kernel_tests/constant_op_test.py b/tensorflow/python/kernel_tests/constant_op_test.py index 0ba17208e7708f..55e65a7d1d7796 100644 --- a/tensorflow/python/kernel_tests/constant_op_test.py +++ b/tensorflow/python/kernel_tests/constant_op_test.py @@ -596,16 +596,16 @@ def testBadShape(self): def testTensorStr(self): a = tf.placeholder(tf.float32, name="a") - self.assertEqual(" dtype=float32>", repr(a)) + self.assertEqual(" dtype=float32>", repr(a)) b = tf.placeholder(tf.int32, shape=(32, 40), name="b") self.assertEqual( - "", + "", repr(b)) c = tf.placeholder(tf.qint32, shape=(32, None, 2), name="c") self.assertEqual( - "", + "", repr(c)) @@ -695,13 +695,13 @@ def testBadShape(self): def testTensorStr(self): a = array_ops.placeholder_v2(tf.float32, shape=None, name="a") - self.assertEqual(" dtype=float32>", repr(a)) + self.assertEqual(" dtype=float32>", repr(a)) b = array_ops.placeholder_v2(tf.int32, shape=(32, 40), name="b") - self.assertEqual("", repr(b)) + self.assertEqual("", repr(b)) c = array_ops.placeholder_v2(tf.qint32, shape=(32, None, 2), name="c") - self.assertEqual("", repr(c)) + self.assertEqual("", repr(c)) class PlaceholderWithDefaultTest(tf.test.TestCase): diff --git a/tensorflow/python/kernel_tests/slice_op_test.py b/tensorflow/python/kernel_tests/slice_op_test.py index fd5c66c99d5454..48364994147f79 100644 --- a/tensorflow/python/kernel_tests/slice_op_test.py +++ b/tensorflow/python/kernel_tests/slice_op_test.py @@ -242,7 +242,7 @@ def testNotIterable(self): c = tf.constant(5.0) with self.assertRaisesWithPredicateMatch( TypeError, - lambda e: "'Tensor' object is not iterable" in str(e)): + lambda e: "'Output' object is not iterable" in str(e)): for _ in c: pass