aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/python/ops/math_ops.py
diff options
context:
space:
mode:
Diffstat (limited to 'tensorflow/python/ops/math_ops.py')
-rw-r--r--tensorflow/python/ops/math_ops.py1201
1 files changed, 1201 insertions, 0 deletions
diff --git a/tensorflow/python/ops/math_ops.py b/tensorflow/python/ops/math_ops.py
new file mode 100644
index 0000000000..d96320e96e
--- /dev/null
+++ b/tensorflow/python/ops/math_ops.py
@@ -0,0 +1,1201 @@
+"""## Arithmetic Operators
+
+TensorFlow provides several operations that you can use to add basic arithmetic
+operators to your graph.
+
+@@add
+@@sub
+@@mul
+@@div
+@@mod
+
+## Basic Math Functions
+
+TensorFlow provides several operations that you can use to add basic
+mathematical functions to your graph.
+
+@@add_n
+@@abs
+@@neg
+@@sign
+@@inv
+@@square
+@@round
+@@sqrt
+@@rsqrt
+@@pow
+@@exp
+@@log
+@@ceil
+@@floor
+@@maximum
+@@minimum
+@@cos
+@@sin
+
+## Matrix Math Functions
+
+TensorFlow provides several operations that you can use to add basic
+mathematical functions for matrices to your graph.
+
+@@diag
+@@transpose
+
+@@matmul
+@@batch_matmul
+
+@@matrix_determinant
+@@batch_matrix_determinant
+
+@@matrix_inverse
+@@batch_matrix_inverse
+
+@@cholesky
+@@batch_cholesky
+
+## Complex Number Functions
+
+TensorFlow provides several operations that you can use to add complex number
+functions to your graph.
+
+@@complex
+@@complex_abs
+@@conj
+@@imag
+@@real
+
+## Reduction
+
+TensorFlow provides several operations that you can use to perform
+common math computations that reduce various dimensions of a tensor.
+
+@@reduce_sum
+@@reduce_prod
+@@reduce_min
+@@reduce_max
+@@reduce_mean
+@@reduce_all
+@@reduce_any
+
+@@accumulate_n
+
+## Segmentation
+
+TensorFlow provides several operations that you can use to perform common
+math computations on tensor segments.
+Here a segmentation is a partitioning of a tensor along
+the first dimension, i.e. it defines a mapping from the first dimension onto
+`segment_ids`. The `segment_ids` tensor should be the size of
+the first dimension, `d0`, with consecutive IDs in the range `0` to `k`,
+where `k<d0`.
+In particular, a segmentation of a matrix tensor is a mapping of rows to
+segments.
+
+For example:
+
+```python
+c = tf.constant([[1,2,3,4], [-1,-2,-3,-4], [5,6,7,8]])
+tf.segment_sum(c, tf.constant([0, 0, 1]))
+ ==> [[0 0 0 0]
+ [5 6 7 8]]
+```
+
+@@segment_sum
+@@segment_prod
+@@segment_min
+@@segment_max
+@@segment_mean
+
+@@unsorted_segment_sum
+
+@@sparse_segment_sum
+@@sparse_segment_mean
+
+
+## Sequence Comparison and Indexing
+
+TensorFlow provides several operations that you can use to add sequence
+comparison and index extraction to your graph. You can use these operations to
+determine sequence differences and determine the indexes of specific values in
+a tensor.
+
+@@argmin
+@@argmax
+
+@@listdiff
+@@where
+@@unique
+
+@@edit_distance
+
+@@invert_permutation
+"""
+import itertools
+
+import tensorflow.python.platform
+
+import numpy as np
+
+from tensorflow.python.framework import ops
+from tensorflow.python.framework import tensor_shape
+from tensorflow.python.framework import tensor_util
+from tensorflow.python.framework import types
+from tensorflow.python.ops import array_ops
+from tensorflow.python.ops import common_shapes
+from tensorflow.python.ops import gen_math_ops
+from tensorflow.python.ops import state_ops
+from tensorflow.python.ops import gen_state_ops
+# pylint: disable=wildcard-import,undefined-variable
+from tensorflow.python.ops.gen_math_ops import *
+
+
+# Aliases for some automatically-generated names.
+argmax = gen_math_ops.arg_max
+argmin = gen_math_ops.arg_min
+linspace = gen_math_ops.lin_space
+
+
+# pylint: disable=anomalous-backslash-in-string,protected-access
+def abs(x, name=None):
+ """Computes the absolute value of a tensor.
+
+ Given a tensor of real numbers `x`, this operation returns a tensor
+ containing the absolute value of each element in `x`. For example, if x is
+ an input element and y is an output element, this operation computes
+ \\\\(y = |x|\\\\).
+
+ See [`tf.complex_abs()`](#tf_complex_abs) to compute the absolute value of a complex
+ number.
+
+ Args:
+ x: A `Tensor` of type `float`, `double`, `int32`, or `int64`.
+ name: A name for the operation (optional).
+
+ Returns:
+ A `Tensor` the same size and type as `x` with absolute values.
+ """
+ with ops.op_scope([x], name, "Abs") as name:
+ x = ops.convert_to_tensor(x, name="x")
+ if x.dtype == types.complex64:
+ return gen_math_ops.complex_abs(x, name=name)
+ return gen_math_ops._abs(x, name=name)
+
+
+
+def pow(x, y, name=None):
+ """Computes the power of one value to another.
+
+ Given a tensor `x` and a tensor `y`, this operation computes \\\\(x^y\\\\) for
+ corresponding elements in `x` and `y`. For example:
+
+ ```
+ # tensor 'x' is [[2, 2]], [3, 3]]
+ # tensor 'y' is [[8, 16], [2, 3]]
+ tf.pow(x, y) ==> [[256, 65536], [9, 27]]
+ ```
+
+ Args:
+ x: A `Tensor` of type `float`, `double`, `int32`, `complex64`, or `int64`.
+ y: A `Tensor` of type `float`, `double`, `int32`, `complex64`, or `int64`.
+ name: A name for the operation (optional).
+
+ Returns:
+ A `Tensor`.
+ """
+ with ops.op_scope([x], name, "Pow") as name:
+ return gen_math_ops._pow(x, y, name=name)
+
+
+def complex(real, imag, name=None):
+ """Converts two real numbers to a complex number.
+
+ Given a tensor `real` representing the real part of a complex number, and a
+ tensor `imag` representing the imaginary part of a complex number, this
+ operation computes complex numbers elementwise of the form \\\\(a + bj\\\\),
+ where *a* represents the `real` part and *b* represents the `imag` part.
+
+ The input tensors `real` and `imag` must be the same shape.
+
+ For example:
+
+ ```
+ # tensor 'real' is [2.25, 3.25]
+ # tensor `imag` is [4.75, 5.75]
+ tf.complex(real, imag) ==> [[2.25 + 4.74j], [3.25 + 5.75j]]
+ ```
+
+ Args:
+ real: A `Tensor` of type `float`.
+ imag: A `Tensor` of type `float`.
+ name: A name for the operation (optional).
+
+ Returns:
+ A `Tensor` of type `complex64`.
+ """
+ with ops.op_scope([real, imag], name, "Complex") as name:
+ return gen_math_ops._complex(real, imag, name=name)
+
+
+def round(x, name=None):
+ """Rounds the values of a tensor to the nearest integer, element-wise.
+
+ For example:
+
+ ```python
+ # 'a' is [0.9, 2.5, 2.3, -4.4]
+ tf.round(a) ==> [ 1.0, 3.0, 2.0, -4.0 ]
+ ```
+
+ Args:
+ x: A `Tensor` of type `float` or `double`.
+ name: A name for the operation (optional).
+
+ Returns:
+ A `Tensor` of same shape and type as `x`.
+ """
+ x = ops.convert_to_tensor(x, name="x")
+ if x.dtype.is_integer:
+ return x
+ else:
+ return floor(x + 0.5, name=name)
+
+
+def cast(x, dtype, name=None):
+ """Casts a tensor to a new type.
+
+ The operation casts `x` (in case of `Tensor`) or `x.values`
+ (in case of `SparseTensor`) to `dtype`.
+
+ For example:
+
+ ```python
+ # tensor `a` is [1.8, 2.2], dtype=tf.float
+ tf.cast(a, tf.int32) ==> [1, 2] # dtype=tf.int32
+ ```
+
+ Args:
+ x: A `Tensor` or `SparseTensor`.
+ dtype: The destination type.
+ name: A name for the operation (optional).
+
+ Returns:
+ A `Tensor` or `SparseTensor` with same shape as `x`.
+
+ Raises:
+ TypeError: If `x` cannot be cast to the `dtype`.
+ """
+ with ops.op_scope([x], name, "Cast") as name:
+ if isinstance(x, ops.SparseTensor):
+ values_cast = cast(x.values, dtype, name=name)
+ return ops.SparseTensor(x.indices, values_cast, x.shape)
+ else:
+ # TODO(mdevin): Handle what Josh said.
+ #
+ # Could return ops.convert_to_tensor(x, dtype=dtype, ...) here, but that
+ # allows some conversions that cast() can't do, e.g. casting numbers to
+ # strings.
+ x = ops.convert_to_tensor(x, name="x")
+ if x.dtype.base_dtype == dtype:
+ return x
+ return gen_math_ops.cast(x, dtype, name=name)
+
+
+def to_float(x, name="ToFloat"):
+ """Casts a tensor to type `float32`.
+
+ Args:
+ x: A `Tensor` or `SparseTensor`.
+ name: A name for the operation (optional).
+
+ Returns:
+ A `Tensor` or `SparseTensor` with same shape as `x` with type `float32`.
+
+ Raises:
+ TypeError: If `x` cannot be cast to the `float32`.
+ """
+ return cast(x, types.float32, name=name)
+
+
+def to_double(x, name="ToDouble"):
+ """Casts a tensor to type `float64`.
+
+ Args:
+ x: A `Tensor` or `SparseTensor`.
+ name: A name for the operation (optional).
+
+ Returns:
+ A `Tensor` or `SparseTensor` with same shape as `x` with type `float64`.
+
+ Raises:
+ TypeError: If `x` cannot be cast to the `float64`.
+ """
+ return cast(x, types.float64, name=name)
+
+
+def to_int32(x, name="ToInt32"):
+ """Casts a tensor to type `int32`.
+
+ Args:
+ x: A `Tensor` or `SparseTensor`.
+ name: A name for the operation (optional).
+
+ Returns:
+ A `Tensor` or `SparseTensor` with same shape as `x` with type `int32`.
+
+ Raises:
+ TypeError: If `x` cannot be cast to the `int32`.
+ """
+ return cast(x, types.int32, name=name)
+
+
+def to_int64(x, name="ToInt64"):
+ """Casts a tensor to type `int64`.
+
+ Args:
+ x: A `Tensor` or `SparseTensor`.
+ name: A name for the operation (optional).
+
+ Returns:
+ A `Tensor` or `SparseTensor` with same shape as `x` with type `int64`.
+
+ Raises:
+ TypeError: If `x` cannot be cast to the `int64`.
+ """
+ return cast(x, types.int64, name=name)
+
+
+def to_bfloat16(x, name="ToBFloat16"):
+ """Casts a tensor to type `bfloat16`.
+
+ Args:
+ x: A `Tensor` or `SparseTensor`.
+ name: A name for the operation (optional).
+
+ Returns:
+ A `Tensor` or `SparseTensor` with same shape as `x` with type `bfloat16`.
+
+ Raises:
+ TypeError: If `x` cannot be cast to the `bfloat16`.
+ """
+ return cast(x, types.bfloat16, name=name)
+
+
+ops.Tensor._override_operator("__neg__", neg)
+ops.Tensor._override_operator("__abs__", abs)
+# __invert__ corresponds to the ~ operator. Here we follow the numpy convention
+# ~ marks an elementwise bit-wise inverse. This is only implemented for boolean
+# tensors and will throw a TypeError if used on nonboolean arrays
+ops.Tensor._override_operator("__invert__", logical_not)
+
+
+def _OverrideBinaryOperatorHelper(func, op_name):
+ """Register operators with different tensor and scalar versions.
+
+ Args:
+ func: the operator
+ op_name: name of the operator being overridden
+ """
+
+ def binary_op_wrapper(x, y):
+ with ops.op_scope([x, y], None, op_name) as name:
+ assert isinstance(x, ops.Tensor)
+ y = ops.convert_to_tensor(y, dtype=x.dtype.base_dtype, name="y")
+ return func(x, y, name=name)
+
+ ops.Tensor._override_operator("__%s__" % op_name, binary_op_wrapper)
+ del binary_op_wrapper
+
+ def r_binary_op_wrapper(y, x):
+ with ops.op_scope([x, y], None, op_name) as name:
+ assert isinstance(y, ops.Tensor)
+ x = ops.convert_to_tensor(x, dtype=y.dtype.base_dtype, name="x")
+ return func(x, y, name=name)
+
+ ops.Tensor._override_operator("__r%s__" % op_name, r_binary_op_wrapper)
+ del r_binary_op_wrapper
+
+
+_OverrideBinaryOperatorHelper(add, "add")
+_OverrideBinaryOperatorHelper(sub, "sub")
+_OverrideBinaryOperatorHelper(mul, "mul")
+_OverrideBinaryOperatorHelper(div, "div")
+_OverrideBinaryOperatorHelper(mod, "mod")
+
+
+def logical_xor(x, y, name="LogicalXor"):
+ """x ^ y = (x | y) & ~(x & y)."""
+ # TODO(alemi) Make this a cwise op if people end up relying on it.
+ return logical_and(logical_or(x, y), logical_not(logical_and(x, y)),
+ name=name)
+
+_OverrideBinaryOperatorHelper(logical_and, "and")
+_OverrideBinaryOperatorHelper(logical_or, "or")
+_OverrideBinaryOperatorHelper(logical_xor, "xor")
+
+ops.Tensor._override_operator("__lt__", less)
+ops.Tensor._override_operator("__le__", less_equal)
+ops.Tensor._override_operator("__gt__", greater)
+ops.Tensor._override_operator("__ge__", greater_equal)
+
+
+def range(start, limit, delta=1, name="range"):
+ """Creates a sequence of integers.
+
+ This operation creates a sequence of integers that begins at `start` and
+ extends by increments of `delta` up to but not including `limit`.
+
+ For example:
+
+ ```
+ # 'start' is 3
+ # 'limit' is 18
+ # 'delta' is 3
+ tf.range(start, limit, delta) ==> [3, 6, 9, 12, 15]
+ ```
+
+ Args:
+ start: A 0-D (scalar) of type `int32`. First entry in sequence.
+ limit: A 0-D (scalar) of type `int32`. Upper limit of sequence,
+ exclusive.
+ delta: A 0-D `Tensor` (scalar) of type `int32`. Optional. Default is 1.
+ Number that increments `start`.
+ name: A name for the operation (optional).
+
+ Returns:
+ An 1-D `int32` `Tensor`.
+ """
+ return gen_math_ops._range(start, limit, delta, name=name)
+
+
+@ops.RegisterShape("Range")
+def _RangeShape(op):
+ start_value = tensor_util.ConstantValue(op.inputs[0])
+ limit_value = tensor_util.ConstantValue(op.inputs[1])
+ delta_value = tensor_util.ConstantValue(op.inputs[2])
+ if start_value is None or limit_value is None or delta_value is None:
+ return [tensor_shape.vector(None)]
+ else:
+ return [tensor_shape.vector(
+ (limit_value - start_value + delta_value - 1) / delta_value)]
+
+
+# Reduction operations
+def _ReductionDims(x, reduction_indices):
+ """Returns range(0, rank(x)) if reduction_indices is None."""
+ if reduction_indices is not None:
+ return reduction_indices
+ else:
+ return range(0, array_ops.rank(x))
+
+
+def reduce_sum(input_tensor, reduction_indices=None, keep_dims=False,
+ name=None):
+ """Computes the sum of elements across dimensions of a tensor.
+
+ Reduces `input_tensor` along the dimensions given in `reduction_indices`.
+ Unless `keep_dims` is true, the rank of the tensor is reduced by 1 for each
+ entry in `reduction_indices`. If `keep_dims` is true, the reduced dimensions
+ are retained with length 1.
+
+ If `reduction_indices` has no entries, all dimensions are reduced, and a
+ tensor with a single element is returned.
+
+ For example:
+
+ ```python
+ # 'x' is [[1, 1, 1]]
+ # [1, 1, 1]]
+ tf.reduce_sum(x) ==> 6
+ tf.reduce_sum(x, 0) ==> [2, 2, 2]
+ tf.reduce_sum(x, 1) ==> [3, 3]
+ tf.reduce_sum(x, 1, keep_dims=True) ==> [[3], [3]]
+ tf.reduce_sum(x, [0, 1]) ==> 6
+ ```
+
+ Args:
+ input_tensor: The tensor to reduce. Should have numeric type.
+ reduction_indices: The dimensions to reduce. If `None` (the defaut),
+ reduces all dimensions.
+ keep_dims: If true, retains reduced dimensions with length 1.
+ name: A name for the operation (optional).
+
+ Returns:
+ The reduced tensor.
+ """
+ return gen_math_ops._sum(input_tensor, _ReductionDims(input_tensor,
+ reduction_indices),
+ keep_dims, name=name)
+
+
+def reduce_mean(input_tensor, reduction_indices=None, keep_dims=False,
+ name=None):
+ """Computes the mean of elements across dimensions of a tensor.
+
+ Reduces `input_tensor` along the dimensions given in `reduction_indices`.
+ Unless `keep_dims` is true, the rank of the tensor is reduced by 1 for each
+ entry in `reduction_indices`. If `keep_dims` is true, the reduced dimensions
+ are retained with length 1.
+
+ If `reduction_indices` has no entries, all dimensions are reduced, and a
+ tensor with a single element is returned.
+
+ For example:
+
+ ```python
+ # 'x' is [[1., 1. ]]
+ # [2., 2.]]
+ tf.reduce_mean(x) ==> 1.5
+ tf.reduce_mean(x, 0) ==> [1.5, 1.5]
+ tf.reduce_mean(x, 1) ==> [1., 2.]
+ ```
+
+ Args:
+ input_tensor: The tensor to reduce. Should have numeric type.
+ reduction_indices: The dimensions to reduce. If `None` (the defaut),
+ reduces all dimensions.
+ keep_dims: If true, retains reduced dimensions with length 1.
+ name: A name for the operation (optional).
+
+ Returns:
+ The reduced tensor.
+ """
+ return gen_math_ops._mean(input_tensor, _ReductionDims(input_tensor,
+ reduction_indices),
+ keep_dims, name=name)
+
+
+def reduce_prod(input_tensor, reduction_indices=None, keep_dims=False,
+ name=None):
+ """Computes the product of elements across dimensions of a tensor.
+
+ Reduces `input_tensor` along the dimensions given in `reduction_indices`.
+ Unless `keep_dims` is true, the rank of the tensor is reduced by 1 for each
+ entry in `reduction_indices`. If `keep_dims` is true, the reduced dimensions
+ are retained with length 1.
+
+ If `reduction_indices` has no entries, all dimensions are reduced, and a
+ tensor with a single element is returned.
+
+ Args:
+ input_tensor: The tensor to reduce. Should have numeric type.
+ reduction_indices: The dimensions to reduce. If `None` (the defaut),
+ reduces all dimensions.
+ keep_dims: If true, retains reduced dimensions with length 1.
+ name: A name for the operation (optional).
+
+ Returns:
+ The reduced tensor.
+ """
+ return gen_math_ops._prod(input_tensor, _ReductionDims(input_tensor,
+ reduction_indices),
+ keep_dims, name=name)
+
+
+def reduce_min(input_tensor, reduction_indices=None, keep_dims=False,
+ name=None):
+ """Computes the minimum of elements across dimensions of a tensor.
+
+ Reduces `input_tensor` along the dimensions given in `reduction_indices`.
+ Unless `keep_dims` is true, the rank of the tensor is reduced by 1 for each
+ entry in `reduction_indices`. If `keep_dims` is true, the reduced dimensions
+ are retained with length 1.
+
+ If `reduction_indices` has no entries, all dimensions are reduced, and a
+ tensor with a single element is returned.
+
+ Args:
+ input_tensor: The tensor to reduce. Should have numeric type.
+ reduction_indices: The dimensions to reduce. If `None` (the defaut),
+ reduces all dimensions.
+ keep_dims: If true, retains reduced dimensions with length 1.
+ name: A name for the operation (optional).
+
+ Returns:
+ The reduced tensor.
+ """
+ return gen_math_ops._min(input_tensor, _ReductionDims(input_tensor,
+ reduction_indices),
+ keep_dims, name=name)
+
+
+def reduce_max(input_tensor, reduction_indices=None, keep_dims=False,
+ name=None):
+ """Computes the maximum of elements across dimensions of a tensor.
+
+ Reduces `input_tensor` along the dimensions given in `reduction_indices`.
+ Unless `keep_dims` is true, the rank of the tensor is reduced by 1 for each
+ entry in `reduction_indices`. If `keep_dims` is true, the reduced dimensions
+ are retained with length 1.
+
+ If `reduction_indices` has no entries, all dimensions are reduced, and a
+ tensor with a single element is returned.
+
+ Args:
+ input_tensor: The tensor to reduce. Should have numeric type.
+ reduction_indices: The dimensions to reduce. If `None` (the defaut),
+ reduces all dimensions.
+ keep_dims: If true, retains reduced dimensions with length 1.
+ name: A name for the operation (optional).
+
+ Returns:
+ The reduced tensor.
+ """
+ return gen_math_ops._max(input_tensor, _ReductionDims(input_tensor,
+ reduction_indices),
+ keep_dims, name=name)
+
+
+def reduce_all(input_tensor, reduction_indices=None, keep_dims=False,
+ name=None):
+ """Computes the "logical and" of elements across dimensions of a tensor.
+
+ Reduces `input_tensor` along the dimensions given in `reduction_indices`.
+ Unless `keep_dims` is true, the rank of the tensor is reduced by 1 for each
+ entry in `reduction_indices`. If `keep_dims` is true, the reduced dimensions
+ are retained with length 1.
+
+ If `reduction_indices` has no entries, all dimensions are reduced, and a
+ tensor with a single element is returned.
+
+ For example:
+
+ ```python
+ # 'x' is [[True, True]]
+ # [False, False]]
+ tf.reduce_all(x) ==> False
+ tf.reduce_all(x, 0) ==> [False, False]
+ tf.reduce_all(x, 1) ==> [True, False]
+ ```
+
+ Args:
+ input_tensor: The boolean tensor to reduce.
+ reduction_indices: The dimensions to reduce. If `None` (the defaut),
+ reduces all dimensions.
+ keep_dims: If true, retains reduced dimensions with length 1.
+ name: A name for the operation (optional).
+
+ Returns:
+ The reduced tensor.
+ """
+ return gen_math_ops._all(input_tensor, _ReductionDims(input_tensor,
+ reduction_indices),
+ keep_dims, name=name)
+
+
+def reduce_any(input_tensor, reduction_indices=None, keep_dims=False,
+ name=None):
+ """Computes the "logical or" of elements across dimensions of a tensor.
+
+ Reduces `input_tensor` along the dimensions given in `reduction_indices`.
+ Unless `keep_dims` is true, the rank of the tensor is reduced by 1 for each
+ entry in `reduction_indices`. If `keep_dims` is true, the reduced dimensions
+ are retained with length 1.
+
+ If `reduction_indices` has no entries, all dimensions are reduced, and a
+ tensor with a single element is returned.
+
+ For example:
+
+ ```python
+ # 'x' is [[True, True]]
+ # [False, False]]
+ tf.reduce_any(x) ==> True
+ tf.reduce_any(x, 0) ==> [True, True]
+ tf.reduce_any(x, 1) ==> [True, False]
+ ```
+
+ Args:
+ input_tensor: The boolean tensor to reduce.
+ reduction_indices: The dimensions to reduce. If `None` (the defaut),
+ reduces all dimensions.
+ keep_dims: If true, retains reduced dimensions with length 1.
+ name: A name for the operation (optional).
+
+ Returns:
+ The reduced tensor.
+ """
+ return gen_math_ops._any(input_tensor, _ReductionDims(input_tensor,
+ reduction_indices),
+ keep_dims, name=name)
+
+
+def matmul(a, b,
+ transpose_a=False, transpose_b=False,
+ a_is_sparse=False, b_is_sparse=False,
+ name=None):
+ """Multiplies matrix `a` by matrix `b`, producing `a` * `b`.
+
+ The inputs must be two-dimensional matrices, with matching inner dimensions,
+ possibly after transposition.
+
+ Both matrices must be of the same type. The supported types are:
+ `float`, `double`, `int32`, `complex64`.
+
+ Either matrix can be transposed on the fly by setting the corresponding flag
+ to `True`. This is `False` by default.
+
+ If one or both of the matrices contain a lot of zeros, a more efficient
+ multiplication algorithm can be used by setting the corresponding
+ `a_is_sparse` or `b_is_sparse` flag to `True`. These are `False` by default.
+
+ For example:
+
+ ```python
+ # 2-D tensor `a`
+ a = tf.constant([1, 2, 3, 4, 5, 6], shape=[2, 3]) => [[1. 2. 3.]
+ [4. 5. 6.]]
+ # 2-D tensor `b`
+ b = tf.constant([7, 8, 9, 10, 11, 12], shape=[3, 2]) => [[7. 8.]
+ [9. 10.]
+ [11. 12.]]
+ c = tf.matmul(a, b) => [[58 64]
+ [139 154]]
+ ```
+
+ Args:
+ a: `Tensor` of type `float`, `double`, `int32` or `complex64`.
+ b: `Tensor` with same type as `a`.
+ transpose_a: If `True`, `a` is transposed before multiplication.
+ transpose_b: If `True`, `b` is transposed before multiplication.
+ a_is_sparse: If `True`, `a` is treated as a sparse matrix.
+ b_is_sparse: If `True`, `b` is treated as a sparse matrix.
+ name: Name for the operation (optional).
+
+ Returns:
+ A `Tensor` of the same type as `a`.
+ """
+ with ops.op_scope([a, b], name, "MatMul") as name:
+ a = ops.convert_to_tensor(a, name="a")
+ b = ops.convert_to_tensor(b, name="b")
+ if a.dtype == types.float32 and (a_is_sparse or b_is_sparse):
+ return sparse_matmul(a, b,
+ transpose_a=transpose_a,
+ transpose_b=transpose_b,
+ a_is_sparse=a_is_sparse,
+ b_is_sparse=b_is_sparse,
+ name=name)
+ else:
+ return gen_math_ops._mat_mul(a, b,
+ transpose_a=transpose_a,
+ transpose_b=transpose_b,
+ name=name)
+
+sparse_matmul = gen_math_ops._sparse_mat_mul
+batch_matmul = gen_math_ops._batch_mat_mul
+
+ops.RegisterShape("MatMul")(common_shapes.matmul_shape)
+ops.RegisterShape("SparseMatMul")(common_shapes.matmul_shape)
+
+
+def _as_indexed_slices(x):
+ """Convert 'x' to IndexedSlices.
+
+ Convert a dense Tensor to a block-sparse IndexedSlices.
+
+ Args:
+ x: Either a Tensor object, or an IndexedSlices object.
+
+ Returns:
+ An IndexedSlices object.
+
+ Raises:
+ TypeError: If 'x' is not a Tensor or an IndexedSlices object.
+ """
+ # TODO(mdevin): op_scope
+ if not isinstance(x, (ops.Tensor, ops.IndexedSlices)):
+ raise TypeError("Not a Tensor or IndexedSlices: %s" % type(x))
+ if isinstance(x, ops.IndexedSlices):
+ return x
+ x_shape = array_ops.shape(x)
+ return ops.IndexedSlices(x, range(0, x_shape[0]), x_shape)
+
+
+def _as_indexed_slices_list(inputs):
+ """Convert all elements of 'inputs' to IndexedSlices.
+
+ Additionally, homogenize the types of all the indices to
+ either int32 or int64.
+
+ Args:
+ inputs: List containing either Tensor or IndexedSlices objects.
+
+ Returns:
+ A list of IndexedSlices objects.
+
+ Raises:
+ TypeError: If 'inputs' is not a list or a tuple.
+ """
+ if not isinstance(inputs, (list, tuple)):
+ raise TypeError("Expected a list or tuple, not a %s" % type(inputs))
+ outputs = [_as_indexed_slices(i) for i in inputs]
+ with_int32_index = [o.indices for o in outputs
+ if o.indices.dtype == types.int32]
+ if not with_int32_index or len(with_int32_index) == len(outputs):
+ return outputs
+ casted_outputs = []
+ for o in outputs:
+ if o.indices.dtype == types.int32:
+ casted_outputs.append(
+ ops.IndexedSlices(o.values, cast(o.indices, types.int64),
+ o.dense_shape))
+ else:
+ casted_outputs.append(o)
+ return casted_outputs
+
+
+def accumulate_n(inputs, shape=None, tensor_dtype=None, name=None):
+ """Returns the element-wise sum of a list of tensors.
+
+ Optionally, pass `shape` and `tensor_dtype` for shape and type checking,
+ otherwise, these are inferred.
+
+ For example:
+
+ ```python
+ # tensor 'a' is [[1, 2], [3, 4]
+ # tensor `b` is [[5, 0], [0, 6]]
+ tf.accumulate_n([a, b, a]) ==> [[7, 4], [6, 14]]
+
+ # Explicitly pass shape and type
+ tf.accumulate_n([a, b, a], shape=[2, 2], tensor_dtype=tf.int32)
+ ==> [[7, 4], [6, 14]]
+ ```
+
+ Args:
+ inputs: A list of `Tensor` objects, each with same shape and type.
+ shape: Shape of elements of `inputs`.
+ tensor_dtype: The type of `inputs`.
+ name: A name for the operation (optional).
+
+ Returns:
+ A `Tensor` of same shape and type as the elements of `inputs`.
+
+ Raises:
+ ValueError: If `inputs` don't all have same shape and dtype or the shape
+ cannot be inferred.
+ """
+ if tensor_dtype is None:
+ if not inputs or not isinstance(inputs, (list, tuple)):
+ raise ValueError("inputs must be a list of at least one Tensor with the "
+ "same dtype and shape")
+ inputs = ops.convert_n_to_tensor_or_indexed_slices(inputs)
+ if not all(isinstance(x, ops.Tensor) for x in inputs):
+ raise ValueError("inputs must be a list of at least one Tensor with the "
+ "same dtype and shape")
+ if not all(x.dtype == inputs[0].dtype for x in inputs):
+ raise ValueError("inputs must be a list of at least one Tensor with the "
+ "same dtype and shape")
+ tensor_dtype = inputs[0].dtype
+ if shape is not None:
+ shape = tensor_shape.as_shape(shape)
+ else:
+ shape = tensor_shape.unknown_shape()
+ for input_tensor in inputs:
+ if isinstance(input_tensor, ops.Tensor):
+ shape = shape.merge_with(input_tensor.get_shape())
+ if not shape.is_fully_defined():
+ # TODO(pbar): Make a version of assign_add that accepts an uninitialized
+ # lvalue, and takes its shape from that? This would allow accumulate_n to
+ # work in all situations that add_n currently works.
+ raise ValueError("Cannot infer the shape of the accumulator for "
+ "accumulate_n. Pass the shape argument, or set the shape "
+ "of at least one of the inputs.")
+ with ops.op_scope(inputs, name, "AccumulateN") as name:
+ var = gen_state_ops._temporary_variable(shape=shape, dtype=tensor_dtype)
+ var_name = var.op.name
+ var = state_ops.assign(var, array_ops.zeros_like(inputs[0]))
+ update_ops = []
+ for input_tensor in inputs:
+ op = state_ops.assign_add(var, input_tensor, use_locking=True)
+ update_ops.append(op)
+ with ops.control_dependencies(update_ops):
+ return gen_state_ops._destroy_temporary_variable(var,
+ var_name=var_name,
+ name=name)
+
+
+@ops.RegisterShape("BatchMatMul")
+def _BatchMatMulShape(op):
+ """Shape function for BatchMatMul op."""
+ a_shape = op.inputs[0].get_shape()
+ adj_a = op.get_attr("adj_x")
+ b_shape = op.inputs[1].get_shape()
+ adj_b = op.get_attr("adj_y")
+ if not a_shape.is_fully_defined() or not b_shape.is_fully_defined():
+ return [tensor_shape.unknown_shape()]
+ batch_dims = a_shape[:-2].merge_with(b_shape[:-2])
+ output_rows = a_shape[-1] if adj_a else a_shape[-2]
+ output_cols = b_shape[-2] if adj_b else b_shape[-1]
+ inner_a = a_shape[-2] if adj_a else a_shape[-1]
+ inner_b = b_shape[-1] if adj_b else b_shape[-2]
+ inner_a.assert_is_compatible_with(inner_b)
+ return [batch_dims.concatenate([output_rows, output_cols])]
+
+
+def sigmoid(x, name=None):
+ """Computes sigmoid of `x` element-wise.
+
+ Specifically, `y = 1 / (1 + exp(-x))`.
+
+ Args:
+ x: A Tensor with type `float`, `double`, `int32`, `complex64`, `int64`,
+ or `qint32`.
+ name: A name for the operation (optional).
+
+ Returns:
+ A Tensor with the same type as `x` if `x.dtype != qint32`
+ otherwise the return type is `quint8`.
+ """
+ with ops.op_scope([x], name, "Sigmoid") as name:
+ x = ops.convert_to_tensor(x, name="x")
+ return gen_math_ops._sigmoid(x, name=name)
+
+
+def tanh(x, name=None):
+ """Computes hyperbolic tangent of `x` element-wise.
+
+ Args:
+ x: A Tensor with type `float`, `double`, `int32`, `complex64`, `int64`,
+ or `qint32`.
+ name: A name for the operation (optional).
+
+ Returns:
+ A Tensor with the same type as `x` if `x.dtype != qint32` otherwise
+ the return type is `quint8`.
+ """
+ with ops.op_scope([x], name, "Tanh") as name:
+ x = ops.convert_to_tensor(x, name="x")
+ return gen_math_ops._tanh(x, name=name)
+
+
+ops.RegisterShape("Abs")(common_shapes.unchanged_shape)
+ops.RegisterShape("Ceil")(common_shapes.unchanged_shape)
+ops.RegisterShape("Conj")(common_shapes.unchanged_shape)
+ops.RegisterShape("Cos")(common_shapes.unchanged_shape)
+ops.RegisterShape("Exp")(common_shapes.unchanged_shape)
+ops.RegisterShape("Floor")(common_shapes.unchanged_shape)
+ops.RegisterShape("Imag")(common_shapes.unchanged_shape)
+ops.RegisterShape("Inv")(common_shapes.unchanged_shape)
+ops.RegisterShape("IsFinite")(common_shapes.unchanged_shape)
+ops.RegisterShape("IsInf")(common_shapes.unchanged_shape)
+ops.RegisterShape("IsNan")(common_shapes.unchanged_shape)
+ops.RegisterShape("Log")(common_shapes.unchanged_shape)
+ops.RegisterShape("LogicalNot")(common_shapes.unchanged_shape)
+ops.RegisterShape("Neg")(common_shapes.unchanged_shape)
+ops.RegisterShape("Real")(common_shapes.unchanged_shape)
+ops.RegisterShape("Rsqrt")(common_shapes.unchanged_shape)
+ops.RegisterShape("Sign")(common_shapes.unchanged_shape)
+ops.RegisterShape("Sin")(common_shapes.unchanged_shape)
+ops.RegisterShape("Sqrt")(common_shapes.unchanged_shape)
+ops.RegisterShape("Square")(common_shapes.unchanged_shape)
+ops.RegisterShape("Sigmoid")(common_shapes.unchanged_shape)
+ops.RegisterShape("Tanh")(common_shapes.unchanged_shape)
+ops.RegisterShape("Cast")(common_shapes.unchanged_shape)
+ops.RegisterShape("ComplexAbs")(common_shapes.unchanged_shape)
+
+
+@ops.RegisterShape("Add")
+@ops.RegisterShape("Complex")
+@ops.RegisterShape("Div")
+@ops.RegisterShape("Equal")
+@ops.RegisterShape("Greater")
+@ops.RegisterShape("GreaterEqual")
+@ops.RegisterShape("Less")
+@ops.RegisterShape("LessEqual")
+@ops.RegisterShape("LogicalAnd")
+@ops.RegisterShape("LogicalOr")
+@ops.RegisterShape("Maximum")
+@ops.RegisterShape("Minimum")
+@ops.RegisterShape("Mod")
+@ops.RegisterShape("Mul")
+@ops.RegisterShape("NotEqual")
+@ops.RegisterShape("Pow")
+@ops.RegisterShape("Sub")
+def _BroadcastShape(op):
+ """Common shape function for binary operators that broadcast their inputs."""
+ shape_x = op.inputs[0].get_shape()
+ shape_y = op.inputs[1].get_shape()
+ if shape_x.ndims is None or shape_y.ndims is None:
+ return [tensor_shape.unknown_shape()]
+
+ # To compute the broadcasted dimensions, we zip together shape_x and shape_y,
+ # and pad with 1 to make them the same length.
+ broadcasted_dims = reversed(list(itertools.izip_longest(
+ reversed(shape_x.dims), reversed(shape_y.dims),
+ fillvalue=tensor_shape.Dimension(1))))
+ # Next we combine the dimensions according to the numpy broadcasting rules.
+ # http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html
+ return_dims = []
+ for (dim_x, dim_y) in broadcasted_dims:
+ if dim_x.value is None or dim_y.value is None:
+ # One or both dimensions is unknown. If either dimension is greater than
+ # 1, we assume that the program is correct, and the other dimension will
+ # be broadcast to match it.
+ # TODO(mrry): If we eliminate the shape checks in C++, we must still
+ # assert that the unknown dim is either 1 or the same as the known dim.
+ if dim_x.value is not None and dim_x.value > 1:
+ return_dims.append(dim_x)
+ elif dim_y.value is not None and dim_y.value > 1:
+ return_dims.append(dim_y)
+ else:
+ return_dims.append(None)
+ elif dim_x.value == 1:
+ # We will broadcast dim_x to dim_y.
+ return_dims.append(dim_y)
+ elif dim_y.value == 1:
+ # We will broadcast dim_y to dim_x.
+ return_dims.append(dim_x)
+ elif dim_x.value == dim_y.value:
+ # The dimensions are compatible, so output is the same size in that
+ # dimension.
+ return_dims.append(dim_x.merge_with(dim_y))
+ else:
+ raise ValueError("Incompatible shapes for broadcasting: %s and %s"
+ % (shape_x, shape_y))
+ return [tensor_shape.TensorShape(return_dims)]
+
+
+@ops.RegisterShape("AddN")
+def _AddNShape(op):
+ merged_shape = tensor_shape.unknown_shape()
+ for input_ in op.inputs:
+ merged_shape = merged_shape.merge_with(input_.get_shape())
+ return [merged_shape]
+
+
+@ops.RegisterShape("Select")
+def _SelectShape(op):
+ # All three inputs must have the same shape.
+ return [op.inputs[0].get_shape()
+ .merge_with(op.inputs[1].get_shape())
+ .merge_with(op.inputs[2].get_shape())]
+
+
+@ops.RegisterShape("ArgMax")
+@ops.RegisterShape("ArgMin")
+def _ArgOpShape(op):
+ """Common shape function for arg-reduction ops."""
+ dimension_shape = op.inputs[1].get_shape()
+ dimension_shape.assert_is_compatible_with(tensor_shape.scalar())
+ input_shape = op.inputs[0].get_shape()
+ if input_shape.ndims is None:
+ return [tensor_shape.unknown_shape()]
+ elif input_shape.ndims <= 1:
+ return [tensor_shape.scalar()]
+
+ dimension = tensor_util.ConstantValue(op.inputs[1])
+ if dimension is None:
+ return [tensor_shape.unknown_shape(ndims=input_shape.ndims - 1)]
+ elif 0 <= dimension and dimension < input_shape.ndims:
+ returned_shape = []
+ for i, dim in enumerate(input_shape.dims):
+ if i != dimension:
+ returned_shape.append(dim)
+ return [tensor_shape.TensorShape(returned_shape)]
+ else:
+ raise ValueError(
+ "dimension (%d) must be in the range [0, %d), where %d is the number "
+ "of dimensions in the input"
+ % (dimension, input_shape.ndims, input_shape.ndims))
+
+
+@ops.RegisterShape("All")
+@ops.RegisterShape("Any")
+@ops.RegisterShape("Max")
+@ops.RegisterShape("Mean")
+@ops.RegisterShape("Min")
+@ops.RegisterShape("Prod")
+@ops.RegisterShape("Sum")
+def _ReductionShape(op):
+ """Common shape function for reduction ops."""
+ input_shape = op.inputs[0].get_shape()
+ reduction_indices = tensor_util.ConstantValue(op.inputs[1])
+ keep_dims = op.get_attr("keep_dims")
+ if reduction_indices is None or input_shape.ndims is None:
+ if keep_dims:
+ return [tensor_shape.unknown_shape(ndims=input_shape.ndims)]
+ else:
+ return [tensor_shape.unknown_shape()]
+
+ # Turn reduction_indices from scalar to vector if necessary
+ reduction_indices = np.ravel(reduction_indices)
+
+ for reduction_index in reduction_indices:
+ if reduction_index < 0 or reduction_index >= input_shape.ndims:
+ raise ValueError("Invalid reduction dimension %d for input with %d "
+ "dimensions" % (reduction_index, input_shape.ndims))
+
+ returned_dims = []
+ if keep_dims:
+ for i, dim in enumerate(input_shape.dims):
+ if i in reduction_indices:
+ returned_dims.append(1)
+ else:
+ returned_dims.append(dim)
+ else:
+ for i, dim in enumerate(input_shape.dims):
+ if i not in reduction_indices:
+ returned_dims.append(dim)
+ return [tensor_shape.TensorShape(returned_dims)]
+
+
+@ops.RegisterShape("SegmentMax")
+@ops.RegisterShape("SegmentMean")
+@ops.RegisterShape("SegmentMin")
+@ops.RegisterShape("SegmentProd")
+@ops.RegisterShape("SegmentSum")
+def _SegmentReductionShape(op):
+ """Common shape function for segment reduction ops."""
+ data_shape = op.inputs[0].get_shape()
+ segment_ids_shape = op.inputs[1].get_shape()
+ segment_ids_shape.assert_has_rank(1)
+ return [tensor_shape.TensorShape([None]).concatenate(data_shape[1:])]
+
+
+@ops.RegisterShape("SparseSegmentMean")
+@ops.RegisterShape("SparseSegmentSum")
+def _SparseSegmentReductionShape(op):
+ """Common shape function for sparse segment reduction ops."""
+ data_shape = op.inputs[0].get_shape()
+ indices_shape = op.inputs[1].get_shape()
+ indices_shape.assert_has_rank(1)
+ segment_ids_shape = op.inputs[2].get_shape()
+ segment_ids_shape.assert_has_rank(1)
+ indices_shape.assert_is_compatible_with(segment_ids_shape)
+ return [tensor_shape.TensorShape([None]).concatenate(data_shape[1:])]
+
+
+@ops.RegisterShape("SparseSegmentMeanGrad")
+def _SparseSegmentMeanGradShape(op):
+ """Shape function for the SparseSegmentMeanGrad op."""
+ input_shape = op.inputs[0].get_shape()
+ indices_shape = op.inputs[1].get_shape().with_rank(1)
+ unused_segment_ids_shape = op.inputs[2].get_shape().merge_with(indices_shape)
+ unused_output_dim0_shape = op.inputs[3].get_shape().merge_with(
+ tensor_shape.scalar())
+ output_dim0 = tensor_util.ConstantValue(op.inputs[3])
+ if output_dim0 is not None:
+ dim0 = output_dim0[0]
+ else:
+ dim0 = None
+ return [tensor_shape.TensorShape([dim0]).concatenate(input_shape[1:])]
+
+
+@ops.RegisterShape("UnsortedSegmentSum")
+def _UnsortedSegmentSumShape(op):
+ """Shape function for UnsortedSegmentSum."""
+ data_shape = op.inputs[0].get_shape()
+ segment_ids_shape = op.inputs[1].get_shape()
+ mid = segment_ids_shape.ndims
+ if mid is None:
+ return [tensor_shape.unknown_shape()]
+ else:
+ num_segments = tensor_util.ConstantValue(op.inputs[2])
+ return [tensor_shape.TensorShape([num_segments]).concatenate(
+ data_shape[mid:])]
+
+
+@ops.RegisterShape("LinSpace")
+def _LinspaceShape(op):
+ num = tensor_util.ConstantValue(op.inputs[2])
+ return [tensor_shape.vector(num)]