Skip to content

Commit

Permalink
Rename tf.Tensor to tf.Output
Browse files Browse the repository at this point in the history
- Swap the alias direction and fix tests broken by the swap
- Export Output in tensorflow.__all__
Change: 138583261
  • Loading branch information
Jonathan Hseu authored and tensorflower-gardener committed Nov 9, 2016
1 parent bb829c6 commit 3cd41cf
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 36 deletions.
2 changes: 2 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions tensorflow/python/framework/framework_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
@@Graph
@@Operation
@@Output
@@Tensor
## Tensor types
Expand Down
54 changes: 25 additions & 29 deletions tensorflow/python/framework/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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.
Expand All @@ -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 = []

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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.
Expand All @@ -466,15 +463,15 @@ 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 "",
(", dtype=%s" % self._dtype.name) if self._dtype else "",
(", device=%s" % self.device) if self.device else "")

def __repr__(self):
return "<tf.Tensor '%s' shape=%s dtype=%s>" % (
return "<tf.Output '%s' shape=%s dtype=%s>" % (
self.name, self.get_shape(), self._dtype.name)

def __hash__(self):
Expand All @@ -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
Expand All @@ -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 "
Expand All @@ -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 "
Expand All @@ -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
Expand All @@ -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):
Expand Down
12 changes: 6 additions & 6 deletions tensorflow/python/kernel_tests/constant_op_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,16 +596,16 @@ def testBadShape(self):

def testTensorStr(self):
a = tf.placeholder(tf.float32, name="a")
self.assertEqual("<tf.Tensor 'a:0' shape=<unknown> dtype=float32>", repr(a))
self.assertEqual("<tf.Output 'a:0' shape=<unknown> dtype=float32>", repr(a))

b = tf.placeholder(tf.int32, shape=(32, 40), name="b")
self.assertEqual(
"<tf.Tensor 'b:0' shape=(32, 40) dtype=int32>",
"<tf.Output 'b:0' shape=(32, 40) dtype=int32>",
repr(b))

c = tf.placeholder(tf.qint32, shape=(32, None, 2), name="c")
self.assertEqual(
"<tf.Tensor 'c:0' shape=(32, ?, 2) dtype=qint32>",
"<tf.Output 'c:0' shape=(32, ?, 2) dtype=qint32>",
repr(c))


Expand Down Expand Up @@ -695,13 +695,13 @@ def testBadShape(self):

def testTensorStr(self):
a = array_ops.placeholder_v2(tf.float32, shape=None, name="a")
self.assertEqual("<tf.Tensor 'a:0' shape=<unknown> dtype=float32>", repr(a))
self.assertEqual("<tf.Output 'a:0' shape=<unknown> dtype=float32>", repr(a))

b = array_ops.placeholder_v2(tf.int32, shape=(32, 40), name="b")
self.assertEqual("<tf.Tensor 'b:0' shape=(32, 40) dtype=int32>", repr(b))
self.assertEqual("<tf.Output 'b:0' shape=(32, 40) dtype=int32>", repr(b))

c = array_ops.placeholder_v2(tf.qint32, shape=(32, None, 2), name="c")
self.assertEqual("<tf.Tensor 'c:0' shape=(32, ?, 2) dtype=qint32>", repr(c))
self.assertEqual("<tf.Output 'c:0' shape=(32, ?, 2) dtype=qint32>", repr(c))


class PlaceholderWithDefaultTest(tf.test.TestCase):
Expand Down
2 changes: 1 addition & 1 deletion tensorflow/python/kernel_tests/slice_op_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 3cd41cf

Please sign in to comment.