Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorflow/python/ops/sparse_ops.py: 30%
584 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
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# ==============================================================================
16# pylint: disable=g-short-docstring-punctuation
17"""Sparse Tensor Representation.
19See also `tf.sparse.SparseTensor`.
20"""
22import numbers
24import numpy as np
26from tensorflow.python.framework import constant_op
27from tensorflow.python.framework import dtypes
28from tensorflow.python.framework import ops
29from tensorflow.python.framework import sparse_tensor
30from tensorflow.python.framework import tensor_shape
31from tensorflow.python.framework import tensor_util
32from tensorflow.python.ops import array_ops
33from tensorflow.python.ops import array_ops_stack
34from tensorflow.python.ops import check_ops
35from tensorflow.python.ops import control_flow_ops
36from tensorflow.python.ops import gen_sparse_ops
37from tensorflow.python.ops import math_ops
38from tensorflow.python.ops import special_math_ops
39# go/tf-wildcard-import
40# pylint: disable=wildcard-import
41from tensorflow.python.ops.gen_sparse_ops import *
42# pylint: enable=wildcard-import
43from tensorflow.python.util import compat
44from tensorflow.python.util import deprecation
45from tensorflow.python.util import dispatch
46from tensorflow.python.util import nest
47from tensorflow.python.util import tf_inspect
48from tensorflow.python.util.compat import collections_abc
49from tensorflow.python.util.tf_export import get_canonical_name_for_symbol
50from tensorflow.python.util.tf_export import tf_export
53def _convert_to_sparse_tensor(sp_input):
54 """Convert `sp_input` to `SparseTensor` and return it.
56 Args:
57 sp_input: `SparseTensor` or `SparseTensorValue`.
59 Returns:
60 `sp_input` converted to `SparseTensor`.
62 Raises:
63 ValueError: if `sp_input` is neither `SparseTensor` nor `SparseTensorValue`.
64 """
65 if isinstance(sp_input, sparse_tensor.SparseTensorValue):
66 return sparse_tensor.SparseTensor.from_value(sp_input)
67 if not isinstance(sp_input, sparse_tensor.SparseTensor):
68 raise TypeError("Input must be a SparseTensor.")
69 return sp_input
72def _convert_to_sparse_tensors(sp_inputs):
73 """Convert `sp_inputs` to `SparseTensor` objects and return them.
75 Args:
76 sp_inputs: `list` or `tuple` of `SparseTensor` or `SparseTensorValue`
77 objects.
79 Returns:
80 `sp_inputs` converted to `SparseTensor` objects.
82 Raises:
83 ValueError: if any item in `sp_inputs` is neither `SparseTensor` nor
84 `SparseTensorValue`.
85 """
86 if isinstance(sp_inputs, list):
87 return [_convert_to_sparse_tensor(sp_input) for sp_input in sp_inputs]
88 if isinstance(sp_inputs, tuple):
89 return (_convert_to_sparse_tensor(sp_input) for sp_input in sp_inputs)
90 raise TypeError("Inputs must be a list or tuple.")
93def _make_int64_tensor(value, name):
94 if isinstance(value, compat.integral_types):
95 return ops.convert_to_tensor(value, name=name, dtype=dtypes.int64)
96 if not isinstance(value, ops.Tensor):
97 raise TypeError("{} must be an integer value".format(name))
98 if value.dtype == dtypes.int64:
99 return value
100 return math_ops.cast(value, dtypes.int64)
103@tf_export("sparse.from_dense")
104def from_dense(tensor, name=None):
105 """Converts a dense tensor into a sparse tensor.
107 Only elements not equal to zero will be present in the result. The resulting
108 `SparseTensor` has the same dtype and shape as the input.
110 >>> sp = tf.sparse.from_dense([0, 0, 3, 0, 1])
111 >>> sp.shape.as_list()
112 [5]
113 >>> sp.values.numpy()
114 array([3, 1], dtype=int32)
115 >>> sp.indices.numpy()
116 array([[2],
117 [4]])
119 Args:
120 tensor: A dense `Tensor` to be converted to a `SparseTensor`.
121 name: Optional name for the op.
123 Returns:
124 The `SparseTensor`.
125 """
126 with ops.name_scope(name, "dense_to_sparse"):
127 tensor = ops.convert_to_tensor(tensor)
128 indices = array_ops.where_v2(
129 math_ops.not_equal(tensor, array_ops.zeros_like(tensor)))
130 values = array_ops.gather_nd(tensor, indices)
131 shape = array_ops.shape(tensor, out_type=dtypes.int64)
132 return sparse_tensor.SparseTensor(indices, values, shape)
135@tf_export("sparse.expand_dims")
136def sparse_expand_dims(sp_input, axis=None, name=None):
137 """Returns a tensor with an length 1 axis inserted at index `axis`.
139 Given a tensor `input`, this operation inserts a dimension of length 1 at the
140 dimension index `axis` of `input`'s shape. The dimension index follows python
141 indexing rules: It's zero-based, a negative index it is counted backward
142 from the end.
144 This operation is useful to:
146 * Add an outer "batch" dimension to a single element.
147 * Align axes for broadcasting.
148 * To add an inner vector length axis to a tensor of scalars.
150 For example:
152 If you have a sparse tensor with shape `[height, width, depth]`:
154 >>> sp = tf.sparse.SparseTensor(indices=[[3,4,1]], values=[7,],
155 ... dense_shape=[10,10,3])
157 You can add an outer `batch` axis by passing `axis=0`:
159 >>> tf.sparse.expand_dims(sp, axis=0).shape.as_list()
160 [1, 10, 10, 3]
162 The new axis location matches Python `list.insert(axis, 1)`:
164 >>> tf.sparse.expand_dims(sp, axis=1).shape.as_list()
165 [10, 1, 10, 3]
167 Following standard python indexing rules, a negative `axis` counts from the
168 end so `axis=-1` adds an inner most dimension:
170 >>> tf.sparse.expand_dims(sp, axis=-1).shape.as_list()
171 [10, 10, 3, 1]
173 Note: Unlike `tf.expand_dims` this function includes a default value for the
174 `axis`: `-1`. So if `axis is not specified, an inner dimension is added.
176 >>> sp.shape.as_list()
177 [10, 10, 3]
178 >>> tf.sparse.expand_dims(sp).shape.as_list()
179 [10, 10, 3, 1]
181 This operation requires that `axis` is a valid index for `input.shape`,
182 following python indexing rules:
184 ```
185 -1-tf.rank(input) <= axis <= tf.rank(input)
186 ```
188 This operation is related to:
190 * `tf.expand_dims`, which provides this functionality for dense tensors.
191 * `tf.squeeze`, which removes dimensions of size 1, from dense tensors.
192 * `tf.sparse.reshape`, which provides more flexible reshaping capability.
194 Args:
195 sp_input: A `SparseTensor`.
196 axis: 0-D (scalar). Specifies the dimension index at which to expand the
197 shape of `input`. Must be in the range `[-rank(sp_input) - 1,
198 rank(sp_input)]`. Defaults to `-1`.
199 name: The name of the output `SparseTensor`.
201 Returns:
202 A `SparseTensor` with the same data as `sp_input`, but its shape has an
203 additional dimension of size 1 added.
204 """
205 rank = sp_input.dense_shape.get_shape()[0]
206 if rank is None:
207 rank = array_ops.shape(sp_input.dense_shape)[0]
208 axis = -1 if axis is None else axis
210 with ops.name_scope(name, default_name="expand_dims", values=[sp_input]):
211 if isinstance(axis, compat.integral_types):
212 axis = ops.convert_to_tensor(axis, name="axis", dtype=dtypes.int32)
213 elif not isinstance(axis, ops.Tensor):
214 raise TypeError("axis must be an integer value in range [-rank(sp_input)"
215 " - 1, rank(sp_input)]")
217 # Convert axis to a positive value if it is negative.
218 axis = array_ops.where_v2(axis >= 0, axis, axis + rank + 1)
220 # Create the new column of indices for the sparse tensor by slicing
221 # the indices and inserting a new column of indices for the new dimension.
222 column_size = array_ops.shape(sp_input.indices)[0]
223 new_index = array_ops.zeros([column_size, 1], dtype=dtypes.int64)
224 indices_before = array_ops.slice(sp_input.indices, [0, 0], [-1, axis])
225 indices_after = array_ops.slice(sp_input.indices, [0, axis], [-1, -1])
226 indices = array_ops.concat(
227 [indices_before, new_index, indices_after], axis=1)
229 # Create the new dense shape by splicing the tensor [1] in the correct
230 # dimension of the existing shape.
231 shape_before = array_ops.slice(sp_input.dense_shape, [0], [axis])
232 shape_after = array_ops.slice(sp_input.dense_shape, [axis], [-1])
233 new_shape = ops.convert_to_tensor([1], name="new_shape", dtype=dtypes.int64)
234 shape = array_ops.concat([shape_before, new_shape, shape_after], axis=0)
236 # Create the output sparse tensor.
237 return sparse_tensor.SparseTensor(
238 indices=indices, values=sp_input.values, dense_shape=shape)
241@tf_export("sparse.eye")
242def sparse_eye(num_rows,
243 num_columns=None,
244 dtype=dtypes.float32,
245 name=None):
246 """Creates a two-dimensional sparse tensor with ones along the diagonal.
248 Args:
249 num_rows: Non-negative integer or `int32` scalar `tensor` giving the number
250 of rows in the resulting matrix.
251 num_columns: Optional non-negative integer or `int32` scalar `tensor` giving
252 the number of columns in the resulting matrix. Defaults to `num_rows`.
253 dtype: The type of element in the resulting `Tensor`.
254 name: A name for this `Op`. Defaults to "eye".
256 Returns:
257 A `SparseTensor` of shape [num_rows, num_columns] with ones along the
258 diagonal.
259 """
260 with ops.name_scope(name, default_name="eye", values=[num_rows, num_columns]):
261 num_rows = _make_int64_tensor(num_rows, "num_rows")
262 num_columns = num_rows if num_columns is None else _make_int64_tensor(
263 num_columns, "num_columns")
265 # Create the sparse tensor.
266 diag_size = math_ops.minimum(num_rows, num_columns)
267 diag_range = math_ops.range(diag_size, dtype=dtypes.int64)
269 return sparse_tensor.SparseTensor(
270 indices=array_ops_stack.stack([diag_range, diag_range], axis=1),
271 values=array_ops.ones(diag_size, dtype=dtype),
272 dense_shape=[num_rows, num_columns])
275# pylint: disable=protected-access
276@tf_export(v1=["sparse.concat", "sparse_concat"])
277@deprecation.deprecated_endpoints("sparse_concat")
278@deprecation.deprecated_args(
279 None, "concat_dim is deprecated, use axis instead", "concat_dim")
280def sparse_concat(axis,
281 sp_inputs,
282 name=None,
283 expand_nonconcat_dim=False,
284 concat_dim=None,
285 expand_nonconcat_dims=None):
286 """Concatenates a list of `SparseTensor` along the specified dimension.
288 Concatenation is with respect to the dense versions of each sparse input.
289 It is assumed that each inputs is a `SparseTensor` whose elements are ordered
290 along increasing dimension number.
292 If expand_nonconcat_dim is False, all inputs' shapes must match, except for
293 the concat dimension. If expand_nonconcat_dim is True, then inputs' shapes are
294 allowed to vary among all inputs.
296 The `indices`, `values`, and `shapes` lists must have the same length.
298 If expand_nonconcat_dim is False, then the output shape is identical to the
299 inputs', except along the concat dimension, where it is the sum of the inputs'
300 sizes along that dimension.
302 If expand_nonconcat_dim is True, then the output shape along the non-concat
303 dimensions will be expand to be the largest among all inputs, and it is the
304 sum of the inputs sizes along the concat dimension.
306 The output elements will be resorted to preserve the sort order along
307 increasing dimension number.
309 This op runs in `O(M log M)` time, where `M` is the total number of non-empty
310 values across all inputs. This is due to the need for an internal sort in
311 order to concatenate efficiently across an arbitrary dimension.
313 For example, if `axis = 1` and the inputs are
315 sp_inputs[0]: shape = [2, 3]
316 [0, 2]: "a"
317 [1, 0]: "b"
318 [1, 1]: "c"
320 sp_inputs[1]: shape = [2, 4]
321 [0, 1]: "d"
322 [0, 2]: "e"
324 then the output will be
326 shape = [2, 7]
327 [0, 2]: "a"
328 [0, 4]: "d"
329 [0, 5]: "e"
330 [1, 0]: "b"
331 [1, 1]: "c"
333 Graphically this is equivalent to doing
335 [ a] concat [ d e ] = [ a d e ]
336 [b c ] [ ] [b c ]
338 Another example, if 'axis = 1' and the inputs are
340 sp_inputs[0]: shape = [3, 3]
341 [0, 2]: "a"
342 [1, 0]: "b"
343 [2, 1]: "c"
345 sp_inputs[1]: shape = [2, 4]
346 [0, 1]: "d"
347 [0, 2]: "e"
349 if expand_nonconcat_dim = False, this will result in an error. But if
350 expand_nonconcat_dim = True, this will result in:
352 shape = [3, 7]
353 [0, 2]: "a"
354 [0, 4]: "d"
355 [0, 5]: "e"
356 [1, 0]: "b"
357 [2, 1]: "c"
359 Graphically this is equivalent to doing
361 [ a] concat [ d e ] = [ a d e ]
362 [b ] [ ] [b ]
363 [ c ] [ c ]
366 Args:
367 axis: Dimension to concatenate along. Must be in range [-rank, rank),
368 where rank is the number of dimensions in each input `SparseTensor`.
369 sp_inputs: List of `SparseTensor` to concatenate.
370 name: A name prefix for the returned tensors (optional).
371 expand_nonconcat_dim: Whether to allow the expansion in the non-concat
372 dimensions. Defaulted to False.
373 concat_dim: The old (deprecated) name for axis.
374 expand_nonconcat_dims: alias for expand_nonconcat_dim
376 Returns:
377 A `SparseTensor` with the concatenated output.
379 Raises:
380 TypeError: If `sp_inputs` is not a list of `SparseTensor`.
381 """
382 expand_nonconcat_dim = deprecation.deprecated_argument_lookup(
383 "expand_nonconcat_dims", expand_nonconcat_dims,
384 "expand_nonconcat_dim", expand_nonconcat_dim)
385 if expand_nonconcat_dims is not None:
386 expand_nonconcat_dim = expand_nonconcat_dims
387 axis = deprecation.deprecated_argument_lookup("axis", axis, "concat_dim",
388 concat_dim)
389 return sparse_concat_v2(axis, sp_inputs, expand_nonconcat_dim, name)
392@tf_export("sparse.concat", v1=[])
393def sparse_concat_v2(axis, sp_inputs, expand_nonconcat_dims=False, name=None): # pylint: disable=missing-docstring
394 sp_inputs = _convert_to_sparse_tensors(sp_inputs)
396 if len(sp_inputs) == 1: # Degenerate case of one tensor.
397 return sp_inputs[0]
399 inds = [sp_input.indices for sp_input in sp_inputs]
400 vals = [sp_input.values for sp_input in sp_inputs]
401 shapes = [sp_input.dense_shape for sp_input in sp_inputs]
403 if expand_nonconcat_dims:
404 max_shape = math_ops.reduce_max(
405 array_ops.concat(
406 [array_ops.reshape(shape, [1, -1]) for shape in shapes], 0), 0)
407 shapes = [
408 array_ops.concat([
409 max_shape[:axis], shape[-1:]
410 if axis == -1 else shape[axis:axis + 1], []
411 if axis == -1 else max_shape[axis + 1:]
412 ], 0) for shape in shapes
413 ]
415 output_ind, output_val, output_shape = (
416 gen_sparse_ops.sparse_concat(inds, vals, shapes, axis, name=name))
418 input_shapes = [inp.shape for inp in sp_inputs]
419 if all(shape.rank is not None for shape in input_shapes):
420 if expand_nonconcat_dims:
421 static_output_shape = []
422 for dim in range(input_shapes[0].rank):
423 static_output_shape.append(
424 max(tensor_shape.dimension_at_index(shape, dim)
425 for shape in input_shapes))
426 else:
427 static_output_shape = input_shapes[0].as_list()
428 static_output_shape[axis] = sum(
429 tensor_shape.dimension_at_index(shape, axis)
430 for shape in input_shapes)
431 else:
432 static_output_shape = tensor_shape.unknown_shape()
433 if all(shape.is_fully_defined() for shape in input_shapes):
434 output_shape = ops.convert_to_tensor(static_output_shape,
435 dtype=dtypes.int64)
436 return sparse_tensor.SparseTensor(output_ind, output_val, output_shape)
437 else:
438 # In case there are partially defined shape, we couldn't update the
439 # output_shape tensor value. We update the output._dense_shape_default,
440 # which populate output.shape as the best effort.
441 output = sparse_tensor.SparseTensor(output_ind, output_val, output_shape)
442 output.set_shape(tensor_shape.TensorShape(static_output_shape))
443 return output
446sparse_concat_v2.__doc__ = sparse_concat.__doc__.replace(
447 " concat_dim: The old (deprecated) name for axis.\n",
448 "").replace(" expand_nonconcat_dims: alias for expand_nonconcat_dim\n",
449 "")
452@tf_export(v1=["sparse.add", "sparse_add"])
453@deprecation.deprecated_endpoints("sparse_add")
454@deprecation.deprecated_args(
455 None, "thresh is deprecated, use threshold instead", "thresh")
456def sparse_add(a, b, threshold=None, thresh=None):
457 """Adds two tensors, at least one of each is a `SparseTensor`.
459 If one `SparseTensor` and one `Tensor` are passed in, returns a `Tensor`. If
460 both arguments are `SparseTensor`s, this returns a `SparseTensor`. The order
461 of arguments does not matter. Use vanilla `tf.add()` for adding two dense
462 `Tensor`s.
464 The shapes of the two operands must match: broadcasting is not supported.
466 The indices of any input `SparseTensor` are assumed ordered in standard
467 lexicographic order. If this is not the case, before this step run
468 `SparseReorder` to restore index ordering.
470 If both arguments are sparse, we perform "clipping" as follows. By default,
471 if two values sum to zero at some index, the output `SparseTensor` would still
472 include that particular location in its index, storing a zero in the
473 corresponding value slot. To override this, callers can specify `thresh`,
474 indicating that if the sum has a magnitude strictly smaller than `thresh`, its
475 corresponding value and index would then not be included. In particular,
476 `thresh == 0.0` (default) means everything is kept and actual thresholding
477 happens only for a positive value.
479 For example, suppose the logical sum of two sparse operands is (densified):
481 [ 2]
482 [.1 0]
483 [ 6 -.2]
485 Then,
487 * `thresh == 0` (the default): all 5 index/value pairs will be returned.
488 * `thresh == 0.11`: only .1 and 0 will vanish, and the remaining three
489 index/value pairs will be returned.
490 * `thresh == 0.21`: .1, 0, and -.2 will vanish.
492 Args:
493 a: The first operand; `SparseTensor` or `Tensor`.
494 b: The second operand; `SparseTensor` or `Tensor`. At least one operand
495 must be sparse.
496 threshold: An optional 0-D `Tensor` (defaults to `0`). The magnitude
497 threshold that determines if an output value/index pair takes space. Its
498 dtype should match that of the values if they are real; if the latter are
499 complex64/complex128, then the dtype should be float32/float64,
500 correspondingly.
501 thresh: Deprecated alias for `threshold`.
503 Returns:
504 A `SparseTensor` or a `Tensor`, representing the sum.
506 Raises:
507 TypeError: If both `a` and `b` are `Tensor`s. Use `tf.add()` instead.
508 """
509 threshold = deprecation.deprecated_argument_lookup("threshold", threshold,
510 "thresh", thresh)
511 if threshold is None:
512 threshold = 0
513 return sparse_add_v2(a, b, threshold)
516@tf_export("sparse.add", v1=[])
517def sparse_add_v2(a, b, threshold=0):
518 """Adds two tensors, at least one of each is a `SparseTensor`.
520 If one `SparseTensor` and one `Tensor` are passed in, returns a `Tensor`. If
521 both arguments are `SparseTensor`s, this returns a `SparseTensor`. The order
522 of arguments does not matter. Use vanilla `tf.add()` for adding two dense
523 `Tensor`s.
525 The shapes of the two operands must match: broadcasting is not supported.
527 The indices of any input `SparseTensor` are assumed ordered in standard
528 lexicographic order. If this is not the case, before this step run
529 `SparseReorder` to restore index ordering.
531 If both arguments are sparse, we perform "clipping" as follows. By default,
532 if two values sum to zero at some index, the output `SparseTensor` would still
533 include that particular location in its index, storing a zero in the
534 corresponding value slot. To override this, callers can specify `threshold`,
535 indicating that if the sum has a magnitude strictly smaller than `threshold`,
536 its corresponding value and index would then not be included. In particular,
537 `threshold == 0.0` (default) means everything is kept and actual thresholding
538 happens only for a positive value.
540 For example, suppose the logical sum of two sparse operands is (densified):
542 [ 2]
543 [.1 0]
544 [ 6 -.2]
546 Then,
548 * `threshold == 0` (the default): all 5 index/value pairs will be
549 returned.
550 * `threshold == 0.11`: only .1 and 0 will vanish, and the remaining three
551 index/value pairs will be returned.
552 * `threshold == 0.21`: .1, 0, and -.2 will vanish.
554 Args:
555 a: The first operand; `SparseTensor` or `Tensor`.
556 b: The second operand; `SparseTensor` or `Tensor`. At least one operand
557 must be sparse.
558 threshold: A 0-D `Tensor`. The magnitude threshold that determines if an
559 output value/index pair takes space. Its dtype should match that of the
560 values if they are real; if the latter are complex64/complex128, then the
561 dtype should be float32/float64, correspondingly.
563 Returns:
564 A `SparseTensor` or a `Tensor`, representing the sum.
566 Raises:
567 TypeError: If both `a` and `b` are `Tensor`s. Use `tf.add()` instead.
568 """
569 sparse_classes = (sparse_tensor.SparseTensor, sparse_tensor.SparseTensorValue)
570 if not any(isinstance(inp, sparse_classes) for inp in [a, b]):
571 raise TypeError("At least one input should be SparseTensor; do you mean to"
572 " use tf.add()?")
574 if all(isinstance(inp, sparse_classes) for inp in [a, b]):
575 a = _convert_to_sparse_tensor(a)
576 b = _convert_to_sparse_tensor(b)
577 threshold = ops.convert_to_tensor(
578 threshold, dtype=a.values.dtype.real_dtype.base_dtype, name="threshold")
579 output_ind, output_val, output_shape = (
580 gen_sparse_ops.sparse_add(a.indices, a.values, a.dense_shape,
581 b.indices, b.values, b.dense_shape,
582 threshold))
584 # Attempt to get output_shape statically.
585 a.get_shape().assert_is_compatible_with(b.get_shape())
586 static_shape = array_ops.broadcast_static_shape(a.get_shape(),
587 b.get_shape())
588 if static_shape.is_fully_defined():
589 output_shape = static_shape.as_list()
591 return sparse_tensor.SparseTensor(output_ind, output_val, output_shape)
592 else:
593 # swap to make `a` the SparseTensor.
594 if isinstance(b, sparse_classes):
595 a, b = b, a
596 return gen_sparse_ops.sparse_tensor_dense_add(a.indices, a.values,
597 a.dense_shape, b)
600@tf_export("sparse.cross")
601def sparse_cross(inputs, name=None, separator=None):
602 """Generates sparse cross from a list of sparse and dense tensors.
604 For example, if the inputs are
606 * inputs[0]: SparseTensor with shape = [2, 2]
607 [0, 0]: "a"
608 [1, 0]: "b"
609 [1, 1]: "c"
610 * inputs[1]: SparseTensor with shape = [2, 1]
611 [0, 0]: "d"
612 [1, 0]: "e"
613 * inputs[2]: Tensor [["f"], ["g"]]
615 then the output will be:
617 shape = [2, 2]
618 [0, 0]: "a_X_d_X_f"
619 [1, 0]: "b_X_e_X_g"
620 [1, 1]: "c_X_e_X_g"
622 Customized separator "_Y_":
624 >>> inp_0 = tf.constant([['a'], ['b']])
625 >>> inp_1 = tf.constant([['c'], ['d']])
626 >>> output = tf.sparse.cross([inp_0, inp_1], separator='_Y_')
627 >>> output.values
628 <tf.Tensor: shape=(2,), dtype=string, numpy=array([b'a_Y_c', b'b_Y_d'],
629 dtype=object)>
632 Args:
633 inputs: An iterable of `Tensor` or `SparseTensor`.
634 name: Optional name for the op.
635 separator: A string added between each string being joined. Defaults to
636 '_X_'.
638 Returns:
639 A `SparseTensor` of type `string`.
640 """
641 if separator is None:
642 separator = "_X_"
643 separator = ops.convert_to_tensor(separator, dtypes.string)
644 indices, values, shapes, dense_inputs = _sparse_cross_internal_v2(inputs)
645 indices_out, values_out, shape_out = gen_sparse_ops.sparse_cross_v2(
646 indices=indices,
647 values=values,
648 shapes=shapes,
649 dense_inputs=dense_inputs,
650 sep=separator,
651 name=name)
652 return sparse_tensor.SparseTensor(indices_out, values_out, shape_out)
655_sparse_cross = sparse_cross
658@tf_export("sparse.cross_hashed")
659def sparse_cross_hashed(inputs, num_buckets=0, hash_key=None, name=None):
660 """Generates hashed sparse cross from a list of sparse and dense tensors.
662 For example, if the inputs are
664 * inputs[0]: SparseTensor with shape = [2, 2]
665 [0, 0]: "a"
666 [1, 0]: "b"
667 [1, 1]: "c"
668 * inputs[1]: SparseTensor with shape = [2, 1]
669 [0, 0]: "d"
670 [1, 0]: "e"
671 * inputs[2]: Tensor [["f"], ["g"]]
673 then the output will be:
675 shape = [2, 2]
676 [0, 0]: FingerprintCat64(
677 Fingerprint64("f"), FingerprintCat64(
678 Fingerprint64("d"), Fingerprint64("a")))
679 [1, 0]: FingerprintCat64(
680 Fingerprint64("g"), FingerprintCat64(
681 Fingerprint64("e"), Fingerprint64("b")))
682 [1, 1]: FingerprintCat64(
683 Fingerprint64("g"), FingerprintCat64(
684 Fingerprint64("e"), Fingerprint64("c")))
686 Args:
687 inputs: An iterable of `Tensor` or `SparseTensor`.
688 num_buckets: An `int` that is `>= 0`.
689 output = hashed_value%num_buckets if num_buckets > 0 else hashed_value.
690 hash_key: Integer hash_key that will be used by the `FingerprintCat64`
691 function. If not given, will use a default key.
692 name: Optional name for the op.
694 Returns:
695 A `SparseTensor` of type `int64`.
696 """
697 return _sparse_cross_internal(
698 inputs=inputs,
699 hashed_output=True,
700 num_buckets=num_buckets,
701 hash_key=hash_key,
702 name=name)
705_sparse_cross_hashed = sparse_cross_hashed
707_DEFAULT_HASH_KEY = 0xDECAFCAFFE
710def _sparse_cross_internal_v2(inputs):
711 """See gen_sparse_ops.sparse_cross_v2."""
712 if not isinstance(inputs, (tuple, list)):
713 raise TypeError("Inputs must be a list")
714 if not all(
715 isinstance(i, sparse_tensor.SparseTensor) or isinstance(i, ops.Tensor)
716 for i in inputs):
717 raise TypeError("All inputs must be Tensor or SparseTensor.")
718 sparse_inputs = [
719 i for i in inputs if isinstance(i, sparse_tensor.SparseTensor)
720 ]
721 dense_inputs = [
722 i for i in inputs if not isinstance(i, sparse_tensor.SparseTensor)
723 ]
724 indices = [sp_input.indices for sp_input in sparse_inputs]
725 values = [sp_input.values for sp_input in sparse_inputs]
726 shapes = [sp_input.dense_shape for sp_input in sparse_inputs]
727 for i in range(len(values)):
728 if values[i].dtype != dtypes.string:
729 values[i] = math_ops.cast(values[i], dtypes.int64)
730 for i in range(len(dense_inputs)):
731 if dense_inputs[i].dtype != dtypes.string:
732 dense_inputs[i] = math_ops.cast(dense_inputs[i], dtypes.int64)
733 return indices, values, shapes, dense_inputs
736def _sparse_cross_internal(inputs,
737 hashed_output=False,
738 num_buckets=0,
739 hash_key=None,
740 name=None):
741 """See gen_sparse_ops.sparse_cross."""
742 if not isinstance(inputs, (tuple, list)):
743 raise TypeError("Inputs must be a list")
744 if not all(
745 isinstance(i, sparse_tensor.SparseTensor) or isinstance(i, ops.Tensor)
746 for i in inputs):
747 raise TypeError("All inputs must be SparseTensors")
749 sparse_inputs = [
750 i for i in inputs if isinstance(i, sparse_tensor.SparseTensor)
751 ]
752 dense_inputs = [
753 i for i in inputs if not isinstance(i, sparse_tensor.SparseTensor)
754 ]
756 indices = [sp_input.indices for sp_input in sparse_inputs]
757 values = [sp_input.values for sp_input in sparse_inputs]
758 shapes = [sp_input.dense_shape for sp_input in sparse_inputs]
759 out_type = dtypes.int64 if hashed_output else dtypes.string
761 internal_type = dtypes.string
762 for i in range(len(values)):
763 if values[i].dtype != dtypes.string:
764 values[i] = math_ops.cast(values[i], dtypes.int64)
765 internal_type = dtypes.int64
766 for i in range(len(dense_inputs)):
767 if dense_inputs[i].dtype != dtypes.string:
768 dense_inputs[i] = math_ops.cast(dense_inputs[i], dtypes.int64)
769 internal_type = dtypes.int64
771 indices_out, values_out, shape_out = gen_sparse_ops.sparse_cross(
772 indices=indices,
773 values=values,
774 shapes=shapes,
775 dense_inputs=dense_inputs,
776 hashed_output=hashed_output,
777 num_buckets=num_buckets,
778 hash_key=hash_key or _DEFAULT_HASH_KEY,
779 out_type=out_type,
780 internal_type=internal_type,
781 name=name)
783 return sparse_tensor.SparseTensor(indices_out, values_out, shape_out)
786def sparse_dense_cwise_add(sp_t, dense_t):
787 """Adds up a SparseTensor and a dense Tensor, using these special rules:
789 (1) Broadcasts the dense side to have the same shape as the sparse side, if
790 eligible;
791 (2) Then, only the dense values pointed to by the indices of the SparseTensor
792 participate in the cwise addition.
794 By the rules, the result is a logical SparseTensor with exactly the same
795 indices and shape, but possibly with different non-zero values. The output of
796 this Op is the resultant non-zero values.
798 Args:
799 sp_t: the SparseTensor operand.
800 dense_t: the dense Tensor operand; must have the same dtype and a
801 broadcast-compatible shape as `sp_t`.
803 Returns:
804 output: the SparseTensor output.
805 """
806 result = gen_sparse_ops.sparse_dense_cwise_add(sp_t.indices, sp_t.values,
807 sp_t.dense_shape, dense_t)
808 return sparse_tensor.SparseTensor(sp_t.indices, result, sp_t.dense_shape)
811@tf_export("sparse.reorder", v1=["sparse.reorder", "sparse_reorder"])
812@deprecation.deprecated_endpoints("sparse_reorder")
813def sparse_reorder(sp_input, name=None):
814 """Reorders a `SparseTensor` into the canonical, row-major ordering.
816 Note that by convention, all sparse ops preserve the canonical ordering
817 along increasing dimension number. The only time ordering can be violated
818 is during manual manipulation of the indices and values to add entries.
820 Reordering does not affect the shape of the `SparseTensor`.
822 For example, if `sp_input` has shape `[4, 5]` and `indices` / `values`:
824 [0, 3]: b
825 [0, 1]: a
826 [3, 1]: d
827 [2, 0]: c
829 then the output will be a `SparseTensor` of shape `[4, 5]` and
830 `indices` / `values`:
832 [0, 1]: a
833 [0, 3]: b
834 [2, 0]: c
835 [3, 1]: d
837 Args:
838 sp_input: The input `SparseTensor`.
839 name: A name prefix for the returned tensors (optional)
841 Returns:
842 A `SparseTensor` with the same shape and non-empty values, but in
843 canonical ordering.
845 Raises:
846 TypeError: If `sp_input` is not a `SparseTensor`.
847 """
848 sp_input = _convert_to_sparse_tensor(sp_input)
850 reordered_ind, reordered_val = (
851 gen_sparse_ops.sparse_reorder(
852 sp_input.indices, sp_input.values, sp_input.dense_shape, name=name))
854 if sp_input.get_shape().is_fully_defined():
855 dense_shape = sp_input.get_shape().as_list()
856 return sparse_tensor.SparseTensor(reordered_ind, reordered_val, dense_shape)
857 else:
858 dense_shape = array_ops.identity(sp_input.dense_shape)
859 sp_output = sparse_tensor.SparseTensor(reordered_ind, reordered_val,
860 dense_shape)
861 # propagate the static shape
862 sp_output.set_shape(sp_input.shape)
863 return sp_output
866@tf_export("sparse.reshape", v1=["sparse.reshape", "sparse_reshape"])
867@deprecation.deprecated_endpoints("sparse_reshape")
868@dispatch.add_dispatch_support
869def sparse_reshape(sp_input, shape, name=None):
870 """Reshapes a `SparseTensor` to represent values in a new dense shape.
872 This operation has the same semantics as `reshape` on the represented dense
873 tensor. The indices of non-empty values in `sp_input` are recomputed based
874 on the new dense shape, and a new `SparseTensor` is returned containing the
875 new indices and new shape. The order of non-empty values in `sp_input` is
876 unchanged.
878 If one component of `shape` is the special value -1, the size of that
879 dimension is computed so that the total dense size remains constant. At
880 most one component of `shape` can be -1. The number of dense elements
881 implied by `shape` must be the same as the number of dense elements
882 originally represented by `sp_input`.
884 For example, if `sp_input` has shape `[2, 3, 6]` and `indices` / `values`:
886 [0, 0, 0]: a
887 [0, 0, 1]: b
888 [0, 1, 0]: c
889 [1, 0, 0]: d
890 [1, 2, 3]: e
892 and `shape` is `[9, -1]`, then the output will be a `SparseTensor` of
893 shape `[9, 4]` and `indices` / `values`:
895 [0, 0]: a
896 [0, 1]: b
897 [1, 2]: c
898 [4, 2]: d
899 [8, 1]: e
901 Args:
902 sp_input: The input `SparseTensor`.
903 shape: A 1-D (vector) int64 `Tensor` specifying the new dense shape of the
904 represented `SparseTensor`.
905 name: A name prefix for the returned tensors (optional)
907 Returns:
908 A `SparseTensor` with the same non-empty values but with indices calculated
909 by the new dense shape.
911 Raises:
912 TypeError: If `sp_input` is not a `SparseTensor`.
913 ValueError: If argument `shape` requests a `SparseTensor` with a different
914 number of elements than `sp_input`.
915 ValueError: If `shape` has more than one inferred (== -1) dimension.
916 """
917 sp_input = _convert_to_sparse_tensor(sp_input)
918 shape = math_ops.cast(shape, dtype=dtypes.int64)
920 with ops.name_scope(name, "SparseReshape", [sp_input]) as name:
921 reshaped_ind, reshaped_shape = gen_sparse_ops.sparse_reshape(
922 sp_input.indices, sp_input.dense_shape, shape, name=name)
924 reshaped_shape_const = tensor_util.constant_value_as_shape(shape)
925 reshaped_shape_const = (
926 reshaped_shape_const.as_list() if reshaped_shape_const.ndims is not None
927 else None)
929 if (reshaped_shape_const is not None
930 and sp_input.shape.is_fully_defined()):
931 # constant_value_as_shape tends to get more information about the partial
932 # shape values, but here we specifically need to know if the *user* passed
933 # a shape with 2+ unknown dimensions; and for that constant_value
934 # provides either the user's direct value or None if only partial elements
935 # are known via the python shape inference code.
936 shape_const_by_user = tensor_util.constant_value(shape)
937 if shape_const_by_user is not None:
938 num_implied_by_user = sum(d == -1 for d in shape_const_by_user)
939 if num_implied_by_user > 1:
940 raise ValueError(
941 "At most one dimension can be inferred (-1). Found: %s"
942 % shape_const_by_user)
943 original_reshaped_shape = list(reshaped_shape_const) # A copy
944 in_shape_size = np.prod(sp_input.shape.as_list())
945 num_implied = sum(dim is None for dim in reshaped_shape_const)
947 # If there is a 0 dim in the user-provided shape, we cannot infer the
948 # unknown dim reliably. This is why we skip the `if` branch below when
949 # a 0 is present in `reshaped_shape_const`. Same below.
950 if num_implied == 1 and 0 not in reshaped_shape_const:
951 implied_idx = original_reshaped_shape.index(None)
952 non_implied_idx = (
953 original_reshaped_shape[:implied_idx] +
954 original_reshaped_shape[implied_idx + 1:])
955 reshaped_shape_const[implied_idx] = int(
956 in_shape_size // np.prod(non_implied_idx))
957 if num_implied == 0 or (num_implied == 1 and
958 0 not in reshaped_shape_const):
959 reshaped_size = np.prod(reshaped_shape_const)
960 if reshaped_size != in_shape_size:
961 raise ValueError(
962 "Cannot reshape a tensor with %d elements to shape %s "
963 "(%d elements)." %
964 (in_shape_size, original_reshaped_shape, reshaped_size))
965 reshaped_shape = constant_op.constant(
966 reshaped_shape_const, dtype=dtypes.int64)
968 return sparse_tensor.SparseTensor(reshaped_ind,
969 array_ops.identity(sp_input.values),
970 reshaped_shape)
973# TODO(aselle): Remove keyword required once for 1.0 final
974class KeywordRequired:
976 def __repr__(self):
977 # This is needed to make documentation without fully qualified module paths
978 return "KeywordRequired()"
981@tf_export(v1=["sparse.split", "sparse_split"])
982@deprecation.deprecated_endpoints("sparse_split")
983@deprecation.deprecated_args(
984 None, "split_dim is deprecated, use axis instead", "split_dim")
985def sparse_split(keyword_required=KeywordRequired(),
986 sp_input=None,
987 num_split=None,
988 axis=None,
989 name=None,
990 split_dim=None):
991 """Split a `SparseTensor` into `num_split` tensors along `axis`.
993 If the `sp_input.dense_shape[axis]` is not an integer multiple of `num_split`
994 each slice starting from 0:`shape[axis] % num_split` gets extra one
995 dimension. For example, if `axis = 1` and `num_split = 2` and the
996 input is:
998 input_tensor = shape = [2, 7]
999 [ a d e ]
1000 [b c ]
1002 Graphically the output tensors are:
1004 output_tensor[0] =
1005 [ a ]
1006 [b c ]
1008 output_tensor[1] =
1009 [ d e ]
1010 [ ]
1012 Args:
1013 keyword_required: Python 2 standin for * (temporary for argument reorder)
1014 sp_input: The `SparseTensor` to split.
1015 num_split: A Python integer. The number of ways to split.
1016 axis: A 0-D `int32` `Tensor`. The dimension along which to split. Must be in
1017 range [-rank, rank), where rank is the number of dimensions in the input
1018 `SparseTensor`.
1019 name: A name for the operation (optional).
1020 split_dim: Deprecated old name for axis.
1022 Returns:
1023 `num_split` `SparseTensor` objects resulting from splitting `value`.
1025 Raises:
1026 TypeError: If `sp_input` is not a `SparseTensor`.
1027 ValueError: If the deprecated `split_dim` and `axis` are both non None.
1028 """
1029 if not isinstance(keyword_required, KeywordRequired):
1030 raise ValueError("Keyword arguments are required for this function.")
1031 if sp_input is None:
1032 raise ValueError("sp_input is required")
1033 if num_split is None:
1034 raise ValueError("num_split is required")
1035 if axis is None:
1036 raise ValueError("axis is required")
1037 axis = deprecation.deprecated_argument_lookup("axis", axis, "split_dim",
1038 split_dim)
1039 sp_input = _convert_to_sparse_tensor(sp_input)
1041 output_inds, output_vals, output_shapes = (
1042 gen_sparse_ops.sparse_split(
1043 axis,
1044 sp_input.indices,
1045 sp_input.values,
1046 sp_input.dense_shape,
1047 num_split,
1048 name=name))
1049 sparse_tensors = []
1050 for i in range(0, num_split):
1051 sparse_tensors.append(
1052 sparse_tensor.SparseTensor(output_inds[i], output_vals[i],
1053 output_shapes[i]))
1054 return sparse_tensors
1057@tf_export("sparse.split", v1=[])
1058def sparse_split_v2(sp_input=None,
1059 num_split=None,
1060 axis=None,
1061 name=None):
1062 """Split a `SparseTensor` into `num_split` tensors along `axis`.
1064 If the `sp_input.dense_shape[axis]` is not an integer multiple of `num_split`
1065 each slice starting from 0:`shape[axis] % num_split` gets extra one
1066 dimension. For example:
1068 >>> indices = [[0, 2], [0, 4], [0, 5], [1, 0], [1, 1]]
1069 >>> values = [1, 2, 3, 4, 5]
1070 >>> t = tf.sparse.SparseTensor(indices=indices, values=values,
1071 ... dense_shape=[2, 7])
1072 >>> tf.sparse.to_dense(t)
1073 <tf.Tensor: shape=(2, 7), dtype=int32, numpy=
1074 array([[0, 0, 1, 0, 2, 3, 0],
1075 [4, 5, 0, 0, 0, 0, 0]], dtype=int32)>
1077 >>> output = tf.sparse.split(sp_input=t, num_split=2, axis=1)
1078 >>> tf.sparse.to_dense(output[0])
1079 <tf.Tensor: shape=(2, 4), dtype=int32, numpy=
1080 array([[0, 0, 1, 0],
1081 [4, 5, 0, 0]], dtype=int32)>
1082 >>> tf.sparse.to_dense(output[1])
1083 <tf.Tensor: shape=(2, 3), dtype=int32, numpy=
1084 array([[2, 3, 0],
1085 [0, 0, 0]], dtype=int32)>
1087 >>> output = tf.sparse.split(sp_input=t, num_split=2, axis=0)
1088 >>> tf.sparse.to_dense(output[0])
1089 <tf.Tensor: shape=(1, 7), dtype=int32, numpy=array([[0, 0, 1, 0, 2, 3, 0]],
1090 dtype=int32)>
1091 >>> tf.sparse.to_dense(output[1])
1092 <tf.Tensor: shape=(1, 7), dtype=int32, numpy=array([[4, 5, 0, 0, 0, 0, 0]],
1093 dtype=int32)>
1095 >>> output = tf.sparse.split(sp_input=t, num_split=2, axis=-1)
1096 >>> tf.sparse.to_dense(output[0])
1097 <tf.Tensor: shape=(2, 4), dtype=int32, numpy=
1098 array([[0, 0, 1, 0],
1099 [4, 5, 0, 0]], dtype=int32)>
1100 >>> tf.sparse.to_dense(output[1])
1101 <tf.Tensor: shape=(2, 3), dtype=int32, numpy=
1102 array([[2, 3, 0],
1103 [0, 0, 0]], dtype=int32)>
1105 Args:
1106 sp_input: The `SparseTensor` to split.
1107 num_split: A Python integer. The number of ways to split.
1108 axis: A 0-D `int32` `Tensor`. The dimension along which to split. Must be in
1109 range [-rank, rank), where rank is the number of dimensions in the input
1110 `SparseTensor`.
1111 name: A name for the operation (optional).
1113 Returns:
1114 `num_split` `SparseTensor` objects resulting from splitting `value`.
1116 Raises:
1117 TypeError: If `sp_input` is not a `SparseTensor`.
1118 """
1119 return sparse_split(sp_input=sp_input,
1120 num_split=num_split,
1121 axis=axis,
1122 name=name,
1123 split_dim=None)
1126@tf_export("sparse.slice", v1=["sparse.slice", "sparse_slice"])
1127@deprecation.deprecated_endpoints("sparse_slice")
1128def sparse_slice(sp_input, start, size, name=None):
1129 """Slice a `SparseTensor` based on the `start` and `size`.
1131 For example, if the input is
1133 input_tensor = shape = [2, 7]
1134 [ a d e ]
1135 [b c ]
1137 Graphically the output tensors are:
1139 sparse.slice([0, 0], [2, 4]) = shape = [2, 4]
1140 [ a ]
1141 [b c ]
1143 sparse.slice([0, 4], [2, 3]) = shape = [2, 3]
1144 [ d e ]
1145 [ ]
1147 Args:
1148 sp_input: The `SparseTensor` to split.
1149 start: 1-D. tensor represents the start of the slice.
1150 size: 1-D. tensor represents the size of the slice.
1151 name: A name for the operation (optional).
1153 Returns:
1154 A `SparseTensor` objects resulting from splicing.
1156 Raises:
1157 TypeError: If `sp_input` is not a `SparseTensor`.
1158 """
1159 sp_input = _convert_to_sparse_tensor(sp_input)
1160 start = ops.convert_to_tensor(start, dtypes.int64)
1161 size = ops.convert_to_tensor(size, dtypes.int64)
1163 with ops.name_scope(name, "SparseSlice", [sp_input]) as name:
1164 output_indices, output_values, output_shape = gen_sparse_ops.sparse_slice(
1165 sp_input.indices,
1166 sp_input.values,
1167 sp_input.dense_shape,
1168 start,
1169 size,
1170 name=name)
1172 return sparse_tensor.SparseTensor(output_indices, output_values,
1173 output_shape)
1176@tf_export(v1=["sparse_to_dense"])
1177@dispatch.add_dispatch_support
1178@deprecation.deprecated(
1179 None,
1180 "Create a `tf.sparse.SparseTensor` and use `tf.sparse.to_dense` instead.")
1181def sparse_to_dense(sparse_indices,
1182 output_shape,
1183 sparse_values,
1184 default_value=0,
1185 validate_indices=True,
1186 name=None):
1187 """Converts a sparse representation into a dense tensor.
1189 Builds an array `dense` with shape `output_shape` such that
1191 ```python
1192 # If sparse_indices is scalar
1193 dense[i] = (i == sparse_indices ? sparse_values : default_value)
1195 # If sparse_indices is a vector, then for each i
1196 dense[sparse_indices[i]] = sparse_values[i]
1198 # If sparse_indices is an n by d matrix, then for each i in [0, n)
1199 dense[sparse_indices[i][0], ..., sparse_indices[i][d-1]] = sparse_values[i]
1200 ```
1202 All other values in `dense` are set to `default_value`. If `sparse_values`
1203 is a scalar, all sparse indices are set to this single value.
1205 Indices should be sorted in lexicographic order, and indices must not
1206 contain any repeats. If `validate_indices` is True, these properties
1207 are checked during execution.
1209 Args:
1210 sparse_indices: A 0-D, 1-D, or 2-D `Tensor` of type `int32` or `int64`.
1211 `sparse_indices[i]` contains the complete index where `sparse_values[i]`
1212 will be placed.
1213 output_shape: A 1-D `Tensor` of the same type as `sparse_indices`. Shape
1214 of the dense output tensor.
1215 sparse_values: A 0-D or 1-D `Tensor`. Values corresponding to each row of
1216 `sparse_indices`, or a scalar value to be used for all sparse indices.
1217 default_value: A 0-D `Tensor` of the same type as `sparse_values`. Value
1218 to set for indices not specified in `sparse_indices`. Defaults to zero.
1219 validate_indices: A boolean value. If True, indices are checked to make
1220 sure they are sorted in lexicographic order and that there are no repeats.
1221 name: A name for the operation (optional).
1223 Returns:
1224 Dense `Tensor` of shape `output_shape`. Has the same type as
1225 `sparse_values`.
1226 """
1227 return gen_sparse_ops.sparse_to_dense(
1228 sparse_indices,
1229 output_shape,
1230 sparse_values,
1231 default_value=default_value,
1232 validate_indices=validate_indices,
1233 name=name)
1236@tf_export("sparse.reduce_max", v1=[])
1237def sparse_reduce_max_v2(
1238 sp_input, axis=None, keepdims=None, output_is_sparse=False, name=None):
1239 """Computes `tf.sparse.maximum` of elements across dimensions of a SparseTensor.
1241 This is the reduction operation for the elementwise `tf.sparse.maximum` op.
1243 This Op takes a SparseTensor and is the sparse counterpart to
1244 `tf.reduce_max()`. In particular, this Op also returns a dense `Tensor`
1245 if `output_is_sparse` is `False`, or a `SparseTensor` if `output_is_sparse`
1246 is `True`.
1248 Note: A gradient is not defined for this function, so it can't be used
1249 in training models that need gradient descent.
1251 Reduces `sp_input` along the dimensions given in `axis`. Unless
1252 `keepdims` is true, the rank of the tensor is reduced by 1 for each entry in
1253 `axis`. If `keepdims` is true, the reduced dimensions are retained
1254 with length 1.
1256 If `axis` has no entries, all dimensions are reduced, and a tensor
1257 with a single element is returned. Additionally, the axes can be negative,
1258 similar to the indexing rules in Python.
1260 The values not defined in `sp_input` don't participate in the reduce max,
1261 as opposed to be implicitly assumed 0 -- hence it can return negative values
1262 for sparse `axis`. But, in case there are no values in
1263 `axis`, it will reduce to 0. See second example below.
1265 For example:
1267 # 'x' represents [[1, ?, 2]
1268 # [?, 3, ?]]
1269 # where ? is implicitly-zero.
1271 >>> x = tf.sparse.SparseTensor([[0, 0], [0, 2], [1, 1]], [1, 2, 3], [2, 3])
1272 >>> tf.sparse.reduce_max(x)
1273 <tf.Tensor: shape=(), dtype=int32, numpy=3>
1274 >>> tf.sparse.reduce_max(x, 0)
1275 <tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 3, 2], dtype=int32)>
1276 >>> tf.sparse.reduce_max(x, 1)
1277 <tf.Tensor: shape=(2,), dtype=int32, numpy=array([2, 3], dtype=int32)>
1278 >>> tf.sparse.reduce_max(x, 1, keepdims=True)
1279 <tf.Tensor: shape=(2, 1), dtype=int32, numpy=
1280 array([[2],
1281 [3]], dtype=int32)>
1282 >>> tf.sparse.reduce_max(x, [0, 1])
1283 <tf.Tensor: shape=(), dtype=int32, numpy=3>
1285 # 'y' represents [[-7, ?]
1286 # [ 4, 3]
1287 # [ ?, ?]
1289 >>> y = tf.sparse.SparseTensor([[0, 0,], [1, 0], [1, 1]], [-7, 4, 3],
1290 ... [3, 2])
1291 >>> tf.sparse.reduce_max(y, 1)
1292 <tf.Tensor: shape=(3,), dtype=int32, numpy=array([-7, 4, 0], dtype=int32)>
1294 Args:
1295 sp_input: The SparseTensor to reduce. Should have numeric type.
1296 axis: The dimensions to reduce; list or scalar. If `None` (the
1297 default), reduces all dimensions.
1298 keepdims: If true, retain reduced dimensions with length 1.
1299 output_is_sparse: If true, returns a `SparseTensor` instead of a dense
1300 `Tensor` (the default).
1301 name: A name for the operation (optional).
1303 Returns:
1304 The reduced Tensor or the reduced SparseTensor if `output_is_sparse` is
1305 True.
1306 """
1307 if keepdims is None:
1308 keepdims = False
1310 if output_is_sparse:
1311 output_ind, output_val, output_shape = (
1312 gen_sparse_ops.sparse_reduce_max_sparse(
1313 sp_input.indices,
1314 sp_input.values,
1315 sp_input.dense_shape,
1316 math_ops._ReductionDims(sp_input, axis),
1317 keepdims,
1318 name=name))
1320 return sparse_tensor.SparseTensor(output_ind, output_val, output_shape)
1322 return gen_sparse_ops.sparse_reduce_max(
1323 sp_input.indices,
1324 sp_input.values,
1325 sp_input.dense_shape,
1326 math_ops._ReductionDims(sp_input, axis),
1327 keepdims,
1328 name=name)
1331@tf_export(v1=["sparse.reduce_max", "sparse_reduce_max"])
1332@deprecation.deprecated_endpoints("sparse_reduce_max")
1333@deprecation.deprecated_args(
1334 None, "keep_dims is deprecated, use keepdims instead", "keep_dims")
1335@deprecation.deprecated_args(
1336 None, "reduction_axes is deprecated, use axis instead",
1337 "reduction_axes")
1338def sparse_reduce_max(sp_input, axis=None, keepdims=None,
1339 reduction_axes=None, keep_dims=None):
1340 """Computes `tf.sparse.maximum` of elements across dimensions of a SparseTensor.
1342 This is the reduction operation for the elementwise `tf.sparse.maximum` op.
1344 This Op takes a SparseTensor and is the sparse counterpart to
1345 `tf.reduce_max()`. In particular, this Op also returns a dense `Tensor`
1346 instead of a sparse one.
1348 Note: A gradient is not defined for this function, so it can't be used
1349 in training models that need gradient descent.
1351 Reduces `sp_input` along the dimensions given in `reduction_axes`. Unless
1352 `keepdims` is true, the rank of the tensor is reduced by 1 for each entry in
1353 `reduction_axes`. If `keepdims` is true, the reduced dimensions are retained
1354 with length 1.
1356 If `reduction_axes` has no entries, all dimensions are reduced, and a tensor
1357 with a single element is returned. Additionally, the axes can be negative,
1358 similar to the indexing rules in Python.
1360 The values not defined in `sp_input` don't participate in the reduce max,
1361 as opposed to be implicitly assumed 0 -- hence it can return negative values
1362 for sparse `reduction_axes`. But, in case there are no values in
1363 `reduction_axes`, it will reduce to 0. See second example below.
1365 For example:
1367 # 'x' represents [[1, ?, 2]
1368 # [?, 3, ?]]
1369 # where ? is implicitly-zero.
1371 >>> x = tf.sparse.SparseTensor([[0, 0], [0, 2], [1, 1]], [1, 2, 3], [2, 3])
1372 >>> tf.sparse.reduce_max(x)
1373 <tf.Tensor: shape=(), dtype=int32, numpy=3>
1374 >>> tf.sparse.reduce_max(x, 0)
1375 <tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 3, 2], dtype=int32)>
1376 >>> tf.sparse.reduce_max(x, 1)
1377 <tf.Tensor: shape=(2,), dtype=int32, numpy=array([2, 3], dtype=int32)>
1378 >>> tf.sparse.reduce_max(x, 1, keepdims=True)
1379 <tf.Tensor: shape=(2, 1), dtype=int32, numpy=
1380 array([[2],
1381 [3]], dtype=int32)>
1382 >>> tf.sparse.reduce_max(x, [0, 1])
1383 <tf.Tensor: shape=(), dtype=int32, numpy=3>
1385 # 'y' represents [[-7, ?]
1386 # [ 4, 3]
1387 # [ ?, ?]
1389 >>> y = tf.sparse.SparseTensor([[0, 0,], [1, 0], [1, 1]], [-7, 4, 3],
1390 ... [3, 2])
1391 >>> tf.sparse.reduce_max(y, 1)
1392 <tf.Tensor: shape=(3,), dtype=int32, numpy=array([-7, 4, 0], dtype=int32)>
1394 Args:
1395 sp_input: The SparseTensor to reduce. Should have numeric type.
1396 axis: The dimensions to reduce; list or scalar. If `None` (the
1397 default), reduces all dimensions.
1398 keepdims: If true, retain reduced dimensions with length 1.
1399 reduction_axes: Deprecated name of `axis`.
1400 keep_dims: Deprecated alias for `keepdims`.
1402 Returns:
1403 The reduced Tensor.
1404 """
1405 keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims,
1406 "keep_dims", keep_dims)
1407 axis = deprecation.deprecated_argument_lookup("axis", axis, "reduction_axes",
1408 reduction_axes)
1409 if keepdims is None:
1410 keepdims = False
1412 return gen_sparse_ops.sparse_reduce_max(
1413 sp_input.indices, sp_input.values, sp_input.dense_shape,
1414 math_ops._ReductionDims(sp_input, axis), keepdims)
1417@tf_export(v1=["sparse.reduce_max_sparse", "sparse_reduce_max_sparse"])
1418@deprecation.deprecated_endpoints("sparse_reduce_max_sparse")
1419@deprecation.deprecated_args(
1420 None, "keep_dims is deprecated, use keepdims instead", "keep_dims")
1421def sparse_reduce_max_sparse(sp_input,
1422 axis=None,
1423 keepdims=None,
1424 reduction_axes=None,
1425 keep_dims=None):
1426 """Computes the max of elements across dimensions of a SparseTensor.
1428 This Op takes a SparseTensor and is the sparse counterpart to
1429 `tf.reduce_max()`. In contrast to SparseReduceSum, this Op returns a
1430 SparseTensor.
1432 Note: A gradient is not defined for this function, so it can't be used
1433 in training models that need gradient descent.
1435 Reduces `sp_input` along the dimensions given in `reduction_axes`. Unless
1436 `keepdims` is true, the rank of the tensor is reduced by 1 for each entry in
1437 `reduction_axes`. If `keepdims` is true, the reduced dimensions are retained
1438 with length 1.
1440 If `reduction_axes` has no entries, all dimensions are reduced, and a tensor
1441 with a single element is returned. Additionally, the axes can be negative,
1442 which are interpreted according to the indexing rules in Python.
1444 Args:
1445 sp_input: The SparseTensor to reduce. Should have numeric type.
1446 axis: The dimensions to reduce; list or scalar. If `None` (the
1447 default), reduces all dimensions.
1448 keepdims: If true, retain reduced dimensions with length 1.
1449 reduction_axes: Deprecated name of axis.
1450 keep_dims: Deprecated alias for `keepdims`.
1452 Returns:
1453 The reduced SparseTensor.
1454 """
1455 keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims,
1456 "keep_dims", keep_dims)
1457 axis = deprecation.deprecated_argument_lookup("axis", axis, "reduction_axes",
1458 reduction_axes)
1459 if keepdims is None:
1460 keepdims = False
1462 output_ind, output_val, output_shape = (
1463 gen_sparse_ops.sparse_reduce_max_sparse(
1464 sp_input.indices, sp_input.values, sp_input.dense_shape,
1465 math_ops._ReductionDims(sp_input, axis), keepdims))
1467 return sparse_tensor.SparseTensor(output_ind, output_val, output_shape)
1470@tf_export("sparse.reduce_sum", v1=[])
1471def sparse_reduce_sum_v2(
1472 sp_input, axis=None, keepdims=None, output_is_sparse=False, name=None):
1473 """Computes `tf.sparse.add` of elements across dimensions of a SparseTensor.
1475 This is the reduction operation for the elementwise `tf.sparse.add` op.
1477 This Op takes a SparseTensor and is the sparse counterpart to
1478 `tf.reduce_sum()`. In particular, this Op also returns a dense `Tensor`
1479 if `output_is_sparse` is `False`, or a `SparseTensor` if `output_is_sparse`
1480 is `True`.
1482 Note: if `output_is_sparse` is True, a gradient is not defined for this
1483 function, so it can't be used in training models that need gradient descent.
1485 Reduces `sp_input` along the dimensions given in `axis`. Unless `keepdims` is
1486 true, the rank of the tensor is reduced by 1 for each entry in `axis`. If
1487 `keepdims` is true, the reduced dimensions are retained with length 1.
1489 If `axis` has no entries, all dimensions are reduced, and a tensor
1490 with a single element is returned. Additionally, the axes can be negative,
1491 similar to the indexing rules in Python.
1493 For example:
1495 # 'x' represents [[1, ?, 1]
1496 # [?, 1, ?]]
1497 # where ? is implicitly-zero.
1499 >>> x = tf.sparse.SparseTensor([[0, 0], [0, 2], [1, 1]], [1, 1, 1], [2, 3])
1500 >>> tf.sparse.reduce_sum(x)
1501 <tf.Tensor: shape=(), dtype=int32, numpy=3>
1502 >>> tf.sparse.reduce_sum(x, 0)
1503 <tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 1, 1], dtype=int32)>
1504 >>> tf.sparse.reduce_sum(x, 1) # Can also use -1 as the axis
1505 <tf.Tensor: shape=(2,), dtype=int32, numpy=array([2, 1], dtype=int32)>
1506 >>> tf.sparse.reduce_sum(x, 1, keepdims=True)
1507 <tf.Tensor: shape=(2, 1), dtype=int32, numpy=
1508 array([[2],
1509 [1]], dtype=int32)>
1510 >>> tf.sparse.reduce_sum(x, [0, 1])
1511 <tf.Tensor: shape=(), dtype=int32, numpy=3>
1513 Args:
1514 sp_input: The SparseTensor to reduce. Should have numeric type.
1515 axis: The dimensions to reduce; list or scalar. If `None` (the
1516 default), reduces all dimensions.
1517 keepdims: If true, retain reduced dimensions with length 1.
1518 output_is_sparse: If true, returns a `SparseTensor` instead of a dense
1519 `Tensor` (the default).
1520 name: A name for the operation (optional).
1522 Returns:
1523 The reduced Tensor or the reduced SparseTensor if `output_is_sparse` is
1524 True.
1525 """
1526 if keepdims is None:
1527 keepdims = False
1529 if output_is_sparse:
1530 output_ind, output_val, output_shape = (
1531 gen_sparse_ops.sparse_reduce_sum_sparse(
1532 sp_input.indices,
1533 sp_input.values,
1534 sp_input.dense_shape,
1535 math_ops._ReductionDims(sp_input, axis),
1536 keepdims,
1537 name=name))
1538 return sparse_tensor.SparseTensor(output_ind, output_val, output_shape)
1540 return gen_sparse_ops.sparse_reduce_sum(
1541 sp_input.indices,
1542 sp_input.values,
1543 sp_input.dense_shape,
1544 math_ops._ReductionDims(sp_input, axis),
1545 keepdims,
1546 name=name)
1549@tf_export(v1=["sparse.reduce_sum", "sparse_reduce_sum"])
1550@deprecation.deprecated_endpoints("sparse_reduce_sum")
1551@deprecation.deprecated_args(
1552 None, "keep_dims is deprecated, use keepdims instead", "keep_dims")
1553@deprecation.deprecated_args(
1554 None, "reduction_axes is deprecated, use axis instead",
1555 "reduction_axes")
1556def sparse_reduce_sum(sp_input, axis=None, keepdims=None,
1557 reduction_axes=None, keep_dims=None):
1558 """Computes `tf.sparse.add` of elements across dimensions of a SparseTensor.
1560 This is the reduction operation for the elementwise `tf.sparse.add` op.
1562 This Op takes a SparseTensor and is the sparse counterpart to
1563 `tf.reduce_sum()`. In particular, this Op also returns a dense `Tensor`
1564 instead of a sparse one.
1566 Reduces `sp_input` along the dimensions given in `reduction_axes`. Unless
1567 `keepdims` is true, the rank of the tensor is reduced by 1 for each entry in
1568 `reduction_axes`. If `keepdims` is true, the reduced dimensions are retained
1569 with length 1.
1571 If `reduction_axes` has no entries, all dimensions are reduced, and a tensor
1572 with a single element is returned. Additionally, the axes can be negative,
1573 similar to the indexing rules in Python.
1575 For example:
1577 # 'x' represents [[1, ?, 1]
1578 # [?, 1, ?]]
1579 # where ? is implicitly-zero.
1581 >>> x = tf.sparse.SparseTensor([[0, 0], [0, 2], [1, 1]], [1, 1, 1], [2, 3])
1582 >>> tf.sparse.reduce_sum(x)
1583 <tf.Tensor: shape=(), dtype=int32, numpy=3>
1584 >>> tf.sparse.reduce_sum(x, 0)
1585 <tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 1, 1], dtype=int32)>
1586 >>> tf.sparse.reduce_sum(x, 1) # Can also use -1 as the axis
1587 <tf.Tensor: shape=(2,), dtype=int32, numpy=array([2, 1], dtype=int32)>
1588 >>> tf.sparse.reduce_sum(x, 1, keepdims=True)
1589 <tf.Tensor: shape=(2, 1), dtype=int32, numpy=
1590 array([[2],
1591 [1]], dtype=int32)>
1592 >>> tf.sparse.reduce_sum(x, [0, 1])
1593 <tf.Tensor: shape=(), dtype=int32, numpy=3>
1595 Args:
1596 sp_input: The SparseTensor to reduce. Should have numeric type.
1597 axis: The dimensions to reduce; list or scalar. If `None` (the
1598 default), reduces all dimensions.
1599 keepdims: If true, retain reduced dimensions with length 1.
1600 reduction_axes: Deprecated name of `axis`.
1601 keep_dims: Deprecated alias for `keepdims`.
1603 Returns:
1604 The reduced Tensor.
1605 """
1606 keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims,
1607 "keep_dims", keep_dims)
1608 axis = deprecation.deprecated_argument_lookup("axis", axis, "reduction_axes",
1609 reduction_axes)
1610 if keepdims is None:
1611 keepdims = False
1613 return gen_sparse_ops.sparse_reduce_sum(
1614 sp_input.indices, sp_input.values, sp_input.dense_shape,
1615 math_ops._ReductionDims(sp_input, axis), keepdims)
1618@tf_export(v1=["sparse.reduce_sum_sparse", "sparse_reduce_sum_sparse"])
1619@deprecation.deprecated_endpoints("sparse_reduce_sum_sparse")
1620@deprecation.deprecated_args(
1621 None, "keep_dims is deprecated, use keepdims instead", "keep_dims")
1622def sparse_reduce_sum_sparse(sp_input,
1623 axis=None,
1624 keepdims=None,
1625 reduction_axes=None,
1626 keep_dims=None):
1627 """Computes the sum of elements across dimensions of a SparseTensor.
1629 This Op takes a SparseTensor and is the sparse counterpart to
1630 `tf.reduce_sum()`. In contrast to SparseReduceSum, this Op returns a
1631 SparseTensor.
1633 Note: A gradient is not defined for this function, so it can't be used
1634 in training models that need gradient descent.
1636 Reduces `sp_input` along the dimensions given in `reduction_axes`. Unless
1637 `keepdims` is true, the rank of the tensor is reduced by 1 for each entry in
1638 `reduction_axes`. If `keepdims` is true, the reduced dimensions are retained
1639 with length 1.
1641 If `reduction_axes` has no entries, all dimensions are reduced, and a tensor
1642 with a single element is returned. Additionally, the axes can be negative,
1643 which are interpreted according to the indexing rules in Python.
1645 Args:
1646 sp_input: The SparseTensor to reduce. Should have numeric type.
1647 axis: The dimensions to reduce; list or scalar. If `None` (the
1648 default), reduces all dimensions.
1649 keepdims: If true, retain reduced dimensions with length 1.
1650 reduction_axes: Deprecated name of axis.
1651 keep_dims: Deprecated alias for `keepdims`.
1653 Returns:
1654 The reduced SparseTensor.
1655 """
1656 keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims,
1657 "keep_dims", keep_dims)
1658 axis = deprecation.deprecated_argument_lookup("axis", axis, "reduction_axes",
1659 reduction_axes)
1660 if keepdims is None:
1661 keepdims = False
1663 output_ind, output_val, output_shape = (
1664 gen_sparse_ops.sparse_reduce_sum_sparse(
1665 sp_input.indices, sp_input.values, sp_input.dense_shape,
1666 math_ops._ReductionDims(sp_input, axis), keepdims))
1668 return sparse_tensor.SparseTensor(output_ind, output_val, output_shape)
1671@tf_export("sparse.to_dense", v1=["sparse.to_dense", "sparse_tensor_to_dense"])
1672@deprecation.deprecated_endpoints("sparse_tensor_to_dense")
1673def sparse_tensor_to_dense(sp_input,
1674 default_value=None,
1675 validate_indices=True,
1676 name=None):
1677 """Converts a `SparseTensor` into a dense tensor.
1679 For this sparse tensor with three non-empty values:
1681 >>> sp_input = tf.sparse.SparseTensor(
1682 ... dense_shape=[3, 5],
1683 ... values=[7, 8, 9],
1684 ... indices =[[0, 1],
1685 ... [0, 3],
1686 ... [2, 0]])
1688 The output will be a dense `[3, 5]` tensor with values:
1690 >>> tf.sparse.to_dense(sp_input).numpy()
1691 array([[0, 7, 0, 8, 0],
1692 [0, 0, 0, 0, 0],
1693 [9, 0, 0, 0, 0]], dtype=int32)
1695 Note: Indices must be without repeats. This is only tested if
1696 `validate_indices` is `True`.
1698 Args:
1699 sp_input: The input `SparseTensor`.
1700 default_value: Scalar value to set for indices not specified in
1701 `sp_input`. Defaults to zero.
1702 validate_indices: A boolean value. If `True`, indices are checked to make
1703 sure they are sorted in lexicographic order and that there are no repeats.
1704 name: A name prefix for the returned tensors (optional).
1706 Returns:
1707 A dense tensor with shape `sp_input.dense_shape` and values specified by
1708 the non-empty values in `sp_input`. Indices not in `sp_input` are assigned
1709 `default_value`.
1711 Raises:
1712 TypeError: If `sp_input` is not a `SparseTensor`.
1713 """
1714 sp_input = _convert_to_sparse_tensor(sp_input)
1715 if default_value is None:
1716 default_value = array_ops.zeros([], dtype=sp_input.dtype)
1718 return gen_sparse_ops.sparse_to_dense(
1719 sp_input.indices,
1720 sp_input.dense_shape,
1721 sp_input.values,
1722 default_value=default_value,
1723 validate_indices=validate_indices,
1724 name=name)
1727@tf_export(
1728 "sparse.to_indicator", v1=["sparse.to_indicator", "sparse_to_indicator"])
1729@deprecation.deprecated_endpoints("sparse_to_indicator")
1730def sparse_to_indicator(sp_input, vocab_size, name=None):
1731 """Converts a `SparseTensor` of ids into a dense bool indicator tensor.
1733 The last dimension of `sp_input.indices` is discarded and replaced with
1734 the values of `sp_input`. If `sp_input.dense_shape = [D0, D1, ..., Dn, K]`,
1735 then `output.shape = [D0, D1, ..., Dn, vocab_size]`, where
1737 output[d_0, d_1, ..., d_n, sp_input[d_0, d_1, ..., d_n, k]] = True
1739 and False elsewhere in `output`.
1741 For example, if `sp_input.dense_shape = [2, 3, 4]` with non-empty values:
1743 [0, 0, 0]: 0
1744 [0, 1, 0]: 10
1745 [1, 0, 3]: 103
1746 [1, 1, 1]: 150
1747 [1, 1, 2]: 149
1748 [1, 1, 3]: 150
1749 [1, 2, 1]: 121
1751 and `vocab_size = 200`, then the output will be a `[2, 3, 200]` dense bool
1752 tensor with False everywhere except at positions
1754 (0, 0, 0), (0, 1, 10), (1, 0, 103), (1, 1, 149), (1, 1, 150),
1755 (1, 2, 121).
1757 Note that repeats are allowed in the input SparseTensor.
1758 This op is useful for converting `SparseTensor`s into dense formats for
1759 compatibility with ops that expect dense tensors.
1761 The input `SparseTensor` must be in row-major order.
1763 Args:
1764 sp_input: A `SparseTensor` with `values` property of type `int32` or
1765 `int64`.
1766 vocab_size: A scalar int64 Tensor (or Python int) containing the new size
1767 of the last dimension, `all(0 <= sp_input.values < vocab_size)`.
1768 name: A name prefix for the returned tensors (optional)
1770 Returns:
1771 A dense bool indicator tensor representing the indices with specified value.
1773 Raises:
1774 TypeError: If `sp_input` is not a `SparseTensor`.
1775 """
1776 sp_input = _convert_to_sparse_tensor(sp_input)
1778 with ops.name_scope(name, "SparseToIndicator", [sp_input]) as name:
1779 num_entries = array_ops.shape(sp_input.indices)[0]
1780 new_values = array_ops.fill(array_ops.expand_dims(num_entries, 0), True)
1781 sp_values = sparse_tensor.SparseTensor(sp_input.indices, new_values,
1782 sp_input.dense_shape)
1784 sp_new = sparse_merge_impl(sp_input, sp_values, vocab_size, name)
1786 # validate_indices may be False because we allow duplicates in new_indices:
1787 # repeated indices are allowed when creating an indicator matrix.
1788 return sparse_tensor_to_dense(
1789 sp_new, default_value=False, validate_indices=False, name=name)
1792@tf_export(v1=["sparse.merge", "sparse_merge"])
1793@deprecation.deprecated(None, "No similar op available at this time.")
1794def sparse_merge(sp_ids, sp_values, vocab_size, name=None,
1795 already_sorted=False):
1796 """Combines a batch of feature ids and values into a single `SparseTensor`.
1798 The most common use case for this function occurs when feature ids and
1799 their corresponding values are stored in `Example` protos on disk.
1800 `parse_example` will return a batch of ids and a batch of values, and this
1801 function joins them into a single logical `SparseTensor` for use in
1802 functions such as `sparse_tensor_dense_matmul`, `sparse_to_dense`, etc.
1804 The `SparseTensor` returned by this function has the following properties:
1806 - `indices` is equivalent to `sp_ids.indices` with the last
1807 dimension discarded and replaced with `sp_ids.values`.
1808 - `values` is simply `sp_values.values`.
1809 - If `sp_ids.dense_shape = [D0, D1, ..., Dn, K]`, then
1810 `output.shape = [D0, D1, ..., Dn, vocab_size]`.
1812 For example, consider the following feature vectors:
1814 ```python
1815 vector1 = [-3, 0, 0, 0, 0, 0]
1816 vector2 = [ 0, 1, 0, 4, 1, 0]
1817 vector3 = [ 5, 0, 0, 9, 0, 0]
1818 ```
1820 These might be stored sparsely in the following Example protos by storing
1821 only the feature ids (column number if the vectors are treated as a matrix)
1822 of the non-zero elements and the corresponding values:
1824 ```python
1825 examples = [Example(features={
1826 "ids": Feature(int64_list=Int64List(value=[0])),
1827 "values": Feature(float_list=FloatList(value=[-3]))}),
1828 Example(features={
1829 "ids": Feature(int64_list=Int64List(value=[1, 4, 3])),
1830 "values": Feature(float_list=FloatList(value=[1, 1, 4]))}),
1831 Example(features={
1832 "ids": Feature(int64_list=Int64List(value=[0, 3])),
1833 "values": Feature(float_list=FloatList(value=[5, 9]))})]
1834 ```
1836 The result of calling parse_example on these examples will produce a
1837 dictionary with entries for "ids" and "values". Passing those two objects
1838 to this function along with vocab_size=6, will produce a `SparseTensor` that
1839 sparsely represents all three instances. Namely, the `indices` property will
1840 contain the coordinates of the non-zero entries in the feature matrix (the
1841 first dimension is the row number in the matrix, i.e., the index within the
1842 batch, and the second dimension is the column number, i.e., the feature id);
1843 `values` will contain the actual values. `shape` will be the shape of the
1844 original matrix, i.e., (3, 6). For our example above, the output will be
1845 equal to:
1847 ```python
1848 SparseTensor(indices=[[0, 0], [1, 1], [1, 3], [1, 4], [2, 0], [2, 3]],
1849 values=[-3, 1, 4, 1, 5, 9],
1850 dense_shape=[3, 6])
1851 ```
1853 This method generalizes to higher-dimensions by simply providing a list for
1854 both the sp_ids as well as the vocab_size.
1855 In this case the resulting `SparseTensor` has the following properties:
1856 - `indices` is equivalent to `sp_ids[0].indices` with the last
1857 dimension discarded and concatenated with
1858 `sp_ids[0].values, sp_ids[1].values, ...`.
1859 - `values` is simply `sp_values.values`.
1860 - If `sp_ids.dense_shape = [D0, D1, ..., Dn, K]`, then
1861 `output.shape = [D0, D1, ..., Dn] + vocab_size`.
1863 Args:
1864 sp_ids: A single `SparseTensor` with `values` property of type `int32`
1865 or `int64` or a Python list of such `SparseTensor`s or a list thereof.
1866 sp_values: A `SparseTensor` of any type.
1867 vocab_size: A scalar `int64` Tensor (or Python int) containing the new size
1868 of the last dimension, `all(0 <= sp_ids.values < vocab_size)`.
1869 Or a list thereof with `all(0 <= sp_ids[i].values < vocab_size[i])` for
1870 all `i`.
1871 name: A name prefix for the returned tensors (optional)
1872 already_sorted: A boolean to specify whether the per-batch values in
1873 `sp_values` are already sorted. If so skip sorting, False by default
1874 (optional).
1876 Returns:
1877 A `SparseTensor` compactly representing a batch of feature ids and values,
1878 useful for passing to functions that expect such a `SparseTensor`.
1880 Raises:
1881 TypeError: If `sp_values` is not a `SparseTensor`. Or if `sp_ids` is neither
1882 a `SparseTensor` nor a list thereof. Or if `vocab_size` is not a
1883 `Tensor` or a Python int and `sp_ids` is a `SparseTensor`. Or if
1884 `vocab_size` is not a or list thereof and `sp_ids` is a list.
1885 ValueError: If `sp_ids` and `vocab_size` are lists of different lengths.
1886 """
1887 return sparse_merge_impl(sp_ids, sp_values, vocab_size, name, already_sorted)
1890def sparse_merge_impl(sp_ids,
1891 sp_values,
1892 vocab_size,
1893 name=None,
1894 already_sorted=False):
1895 """Internal implementation for sparse_merge to avoid deprecation warnings."""
1896 if isinstance(sp_ids, sparse_tensor.SparseTensorValue) or isinstance(
1897 sp_ids, sparse_tensor.SparseTensor):
1898 sp_ids = [sp_ids]
1899 if not (isinstance(vocab_size, ops.Tensor) or
1900 isinstance(vocab_size, numbers.Integral)):
1901 raise TypeError("vocab_size has to be a Tensor or Python int. Found %s" %
1902 type(vocab_size))
1903 vocab_size = [vocab_size]
1904 else:
1905 if not isinstance(sp_ids, collections_abc.Iterable):
1906 raise TypeError("sp_ids has to be a SparseTensor or list thereof. "
1907 "Found %s" % type(sp_ids))
1908 if not isinstance(vocab_size, collections_abc.Iterable):
1909 raise TypeError("vocab_size has to be a list of Tensors or Python ints. "
1910 "Found %s" % type(vocab_size))
1911 for dim in vocab_size:
1912 if not (isinstance(dim, ops.Tensor) or isinstance(dim, numbers.Integral)):
1913 raise TypeError(
1914 "vocab_size has to be a list of Tensors or Python ints. Found %s" %
1915 type(dim))
1916 if len(sp_ids) != len(vocab_size):
1917 raise ValueError("sp_ids and vocab_size have to have equal lengths.")
1919 with ops.name_scope(name, "SparseMerge", [sp_ids, sp_values]):
1920 sp_ids = [_convert_to_sparse_tensor(sp_ids_dim) for sp_ids_dim in sp_ids]
1921 sp_values = _convert_to_sparse_tensor(sp_values)
1922 ids = []
1923 for sp_ids_dim in sp_ids:
1924 ids_dim = sp_ids_dim.values
1925 if sp_ids_dim.dtype != dtypes.int64:
1926 ids_dim = math_ops.cast(ids_dim, dtypes.int64)
1927 ids += [array_ops.expand_dims(ids_dim, axis=1)]
1929 vocab_size = [math_ops.cast(x, dtypes.int64) for x in vocab_size]
1931 # Slice off the last dimension of indices, then tack on the ids
1932 indices_columns_to_preserve = sp_ids[0].indices[:, :-1]
1933 new_indices = array_ops.concat([indices_columns_to_preserve] + ids, 1)
1935 new_values = sp_values.values
1936 new_shape = array_ops.concat([sp_ids[0].dense_shape[:-1], vocab_size], 0)
1938 result = sparse_tensor.SparseTensor(new_indices, new_values, new_shape)
1939 if already_sorted:
1940 return result
1941 sorted_result = sparse_reorder(result)
1942 return sparse_tensor.SparseTensor(
1943 sorted_result.indices, sorted_result.values, new_shape)
1946@tf_export("sparse.retain", v1=["sparse.retain", "sparse_retain"])
1947@deprecation.deprecated_endpoints("sparse_retain")
1948def sparse_retain(sp_input, to_retain):
1949 """Retains specified non-empty values within a `SparseTensor`.
1951 For example, if `sp_input` has shape `[4, 5]` and 4 non-empty string values:
1953 [0, 1]: a
1954 [0, 3]: b
1955 [2, 0]: c
1956 [3, 1]: d
1958 and `to_retain = [True, False, False, True]`, then the output will
1959 be a `SparseTensor` of shape `[4, 5]` with 2 non-empty values:
1961 [0, 1]: a
1962 [3, 1]: d
1964 Args:
1965 sp_input: The input `SparseTensor` with `N` non-empty elements.
1966 to_retain: A bool vector of length `N` with `M` true values.
1968 Returns:
1969 A `SparseTensor` with the same shape as the input and `M` non-empty
1970 elements corresponding to the true positions in `to_retain`.
1972 Raises:
1973 TypeError: If `sp_input` is not a `SparseTensor`.
1974 """
1975 sp_input = _convert_to_sparse_tensor(sp_input)
1977 to_retain = ops.convert_to_tensor(to_retain)
1979 # Shape checking, if shape is known at graph construction time
1980 retain_shape = to_retain.get_shape()
1981 retain_shape.assert_has_rank(1)
1982 if sp_input.values.get_shape().dims is not None:
1983 sp_input.values.get_shape().dims[0].assert_is_compatible_with(
1984 tensor_shape.dimension_at_index(retain_shape, 0))
1986 where_true = array_ops.reshape(array_ops.where_v2(to_retain), [-1])
1987 new_indices = array_ops.gather(sp_input.indices, where_true)
1988 new_values = array_ops.gather(sp_input.values, where_true)
1989 return sparse_tensor.SparseTensor(new_indices, new_values,
1990 array_ops.identity(sp_input.dense_shape))
1993@tf_export(
1994 "sparse.reset_shape", v1=["sparse.reset_shape", "sparse_reset_shape"])
1995@deprecation.deprecated_endpoints("sparse_reset_shape")
1996def sparse_reset_shape(sp_input, new_shape=None):
1997 """Resets the shape of a `SparseTensor` with indices and values unchanged.
1999 If `new_shape` is None, returns a copy of `sp_input` with its shape reset
2000 to the tight bounding box of `sp_input`. This will be a shape consisting of
2001 all zeros if sp_input has no values.
2003 If `new_shape` is provided, then it must be larger or equal in all dimensions
2004 compared to the shape of `sp_input`. When this condition is met, the returned
2005 SparseTensor will have its shape reset to `new_shape` and its indices and
2006 values unchanged from that of `sp_input.`
2008 For example:
2010 Consider a `sp_input` with shape [2, 3, 5]:
2012 [0, 0, 1]: a
2013 [0, 1, 0]: b
2014 [0, 2, 2]: c
2015 [1, 0, 3]: d
2017 - It is an error to set `new_shape` as [3, 7] since this represents a
2018 rank-2 tensor while `sp_input` is rank-3. This is either a ValueError
2019 during graph construction (if both shapes are known) or an OpError during
2020 run time.
2022 - Setting `new_shape` as [2, 3, 6] will be fine as this shape is larger or
2023 equal in every dimension compared to the original shape [2, 3, 5].
2025 - On the other hand, setting new_shape as [2, 3, 4] is also an error: The
2026 third dimension is smaller than the original shape [2, 3, 5] (and an
2027 `InvalidArgumentError` will be raised).
2029 - If `new_shape` is None, the returned SparseTensor will have a shape
2030 [2, 3, 4], which is the tight bounding box of `sp_input`.
2032 Args:
2033 sp_input: The input `SparseTensor`.
2034 new_shape: None or a vector representing the new shape for the returned
2035 `SparseTensor`.
2037 Returns:
2038 A `SparseTensor` indices and values unchanged from `sp_input`. Its shape is
2039 `new_shape` if that is set. Otherwise it is the tight bounding box of
2040 `sp_input`
2042 Raises:
2043 TypeError: If `sp_input` is not a `SparseTensor`.
2044 ValueError: If `new_shape` represents a tensor with a different rank from
2045 that of `sp_input` (if shapes are known when graph is constructed).
2046 ValueError: If `new_shape` is determined during graph build to have
2047 dimension sizes that are too small.
2048 OpError:
2049 - If `new_shape` has dimension sizes that are too small.
2050 - If shapes are not known during graph construction time, and during run
2051 time it is found out that the ranks do not match.
2052 """
2053 sp_input = _convert_to_sparse_tensor(sp_input)
2055 in_indices = array_ops.identity(sp_input.indices)
2056 in_values = array_ops.identity(sp_input.values)
2057 in_shape = array_ops.identity(sp_input.dense_shape)
2059 if new_shape is None:
2060 dim_low_bound = math_ops.reduce_max(in_indices, axis=0)
2061 output_shape_tensor = math_ops.maximum(
2062 array_ops.constant(0, dtype=dtypes.int64),
2063 math_ops.add(dim_low_bound, array_ops.ones_like(in_shape)))
2064 else:
2065 output_shape_tensor = ops.convert_to_tensor(new_shape)
2066 output_shape_tensor.get_shape().assert_has_rank(1)
2067 output_shape_tensor = math_ops.cast(output_shape_tensor, dtypes.int64)
2068 # For cases when shape is known during graph construction, this catches the
2069 # error before the sparse_tensor.SparseTensor catches it.
2070 if output_shape_tensor.get_shape().rank is not None:
2071 output_shape_tensor.get_shape().dims[0].assert_is_compatible_with(
2072 in_shape.get_shape().dims[0])
2074 output_shape_tensor_const = tensor_util.constant_value(output_shape_tensor)
2075 # For cases where all shapes are known during graph construction
2076 if (output_shape_tensor_const is not None and
2077 sp_input.get_shape().is_fully_defined()):
2078 in_shape_const = np.array(sp_input.get_shape().as_list())
2079 if not np.all(in_shape_const <= output_shape_tensor_const):
2080 raise ValueError(
2081 "Requested new_shape should have dimension sizes >= sp_input.shape."
2082 " Found new_shape (%s), sp_input.shape (%s)." %
2083 (in_shape_const, output_shape_tensor_const))
2084 output_shape_tensor = output_shape_tensor_const
2085 else:
2086 # For cases where shape is not known during graph construction.
2087 output_shape_tensor = control_flow_ops.with_dependencies([
2088 check_ops.assert_equal(
2089 array_ops.shape(in_shape), array_ops.shape(output_shape_tensor))
2090 ], output_shape_tensor)
2091 output_shape_tensor = control_flow_ops.with_dependencies(
2092 [check_ops.assert_less_equal(in_shape, output_shape_tensor)],
2093 output_shape_tensor)
2095 return sparse_tensor.SparseTensor(in_indices, in_values, output_shape_tensor)
2098@tf_export(
2099 "sparse.fill_empty_rows",
2100 v1=["sparse.fill_empty_rows", "sparse_fill_empty_rows"])
2101@deprecation.deprecated_endpoints("sparse_fill_empty_rows")
2102def sparse_fill_empty_rows(sp_input, default_value, name=None):
2103 """Fills empty rows in the input 2-D `SparseTensor` with a default value.
2105 This op adds entries with the specified `default_value` at index
2106 `[row, 0]` for any row in the input that does not already have a value.
2108 For example, suppose `sp_input` has shape `[5, 6]` and non-empty values:
2110 [0, 1]: a
2111 [0, 3]: b
2112 [2, 0]: c
2113 [3, 1]: d
2115 Rows 1 and 4 are empty, so the output will be of shape `[5, 6]` with values:
2117 [0, 1]: a
2118 [0, 3]: b
2119 [1, 0]: default_value
2120 [2, 0]: c
2121 [3, 1]: d
2122 [4, 0]: default_value
2124 Note that the input may have empty columns at the end, with no effect on
2125 this op.
2127 The output `SparseTensor` will be in row-major order and will have the
2128 same shape as the input.
2130 This op also returns an indicator vector such that
2132 empty_row_indicator[i] = True iff row i was an empty row.
2134 Args:
2135 sp_input: A `SparseTensor` with shape `[N, M]`.
2136 default_value: The value to fill for empty rows, with the same type as
2137 `sp_input.`
2138 name: A name prefix for the returned tensors (optional)
2140 Returns:
2141 sp_ordered_output: A `SparseTensor` with shape `[N, M]`, and with all empty
2142 rows filled in with `default_value`.
2143 empty_row_indicator: A bool vector of length `N` indicating whether each
2144 input row was empty.
2146 Raises:
2147 TypeError: If `sp_input` is not a `SparseTensor`.
2148 """
2149 sp_input = _convert_to_sparse_tensor(sp_input)
2150 with ops.name_scope(name, "SparseFillEmptyRows", [sp_input]):
2151 default_value = ops.convert_to_tensor(
2152 default_value, dtype=sp_input.values.dtype)
2153 (output_indices, output_values, empty_row_indicator,
2154 unused_reverse_index_map) = gen_sparse_ops.sparse_fill_empty_rows(
2155 indices=sp_input.indices,
2156 values=sp_input.values,
2157 dense_shape=sp_input.dense_shape,
2158 default_value=default_value)
2159 return (sparse_tensor.SparseTensor(
2160 indices=output_indices,
2161 values=output_values,
2162 dense_shape=sp_input.dense_shape), empty_row_indicator)
2165@tf_export(v1=["io.serialize_sparse", "serialize_sparse"])
2166@dispatch.add_dispatch_support
2167@deprecation.deprecated_endpoints("serialize_sparse")
2168def serialize_sparse(sp_input, name=None, out_type=dtypes.string):
2169 """Serialize a `SparseTensor` into a 3-vector (1-D `Tensor`) object.
2171 Args:
2172 sp_input: The input `SparseTensor`.
2173 name: A name prefix for the returned tensors (optional).
2174 out_type: The `dtype` to use for serialization.
2176 Returns:
2177 A 3-vector (1-D `Tensor`), with each column representing the serialized
2178 `SparseTensor`'s indices, values, and shape (respectively).
2180 Raises:
2181 TypeError: If `sp_input` is not a `SparseTensor`.
2182 """
2183 return serialize_sparse_v2(sp_input, out_type, name)
2186@tf_export("io.serialize_sparse", v1=[])
2187@dispatch.add_dispatch_support
2188def serialize_sparse_v2(sp_input, out_type=dtypes.string, name=None):
2189 """Serialize a `SparseTensor` into a 3-vector (1-D `Tensor`) object.
2191 Args:
2192 sp_input: The input `SparseTensor`.
2193 out_type: The `dtype` to use for serialization.
2194 name: A name prefix for the returned tensors (optional).
2196 Returns:
2197 A 3-vector (1-D `Tensor`), with each column representing the serialized
2198 `SparseTensor`'s indices, values, and shape (respectively).
2200 Raises:
2201 TypeError: If `sp_input` is not a `SparseTensor`.
2202 """
2203 sp_input = _convert_to_sparse_tensor(sp_input)
2205 return gen_sparse_ops.serialize_sparse(
2206 sp_input.indices,
2207 sp_input.values,
2208 sp_input.dense_shape,
2209 name=name,
2210 out_type=out_type)
2213@tf_export(v1=["io.serialize_many_sparse", "serialize_many_sparse"])
2214@dispatch.add_dispatch_support
2215@deprecation.deprecated_endpoints("serialize_many_sparse")
2216def serialize_many_sparse(sp_input, name=None, out_type=dtypes.string):
2217 """Serialize `N`-minibatch `SparseTensor` into an `[N, 3]` `Tensor`.
2219 The `SparseTensor` must have rank `R` greater than 1, and the first dimension
2220 is treated as the minibatch dimension. Elements of the `SparseTensor`
2221 must be sorted in increasing order of this first dimension. The serialized
2222 `SparseTensor` objects going into each row of the output `Tensor` will have
2223 rank `R-1`.
2225 The minibatch size `N` is extracted from `sparse_shape[0]`.
2227 Args:
2228 sp_input: The input rank `R` `SparseTensor`.
2229 name: A name prefix for the returned tensors (optional).
2230 out_type: The `dtype` to use for serialization.
2232 Returns:
2233 A matrix (2-D `Tensor`) with `N` rows and `3` columns. Each column
2234 represents serialized `SparseTensor`'s indices, values, and shape
2235 (respectively).
2237 Raises:
2238 TypeError: If `sp_input` is not a `SparseTensor`.
2239 """
2240 return serialize_many_sparse_v2(sp_input, out_type, name)
2243@tf_export("io.serialize_many_sparse", v1=[])
2244@dispatch.add_dispatch_support
2245def serialize_many_sparse_v2(sp_input, out_type=dtypes.string, name=None):
2246 """Serialize `N`-minibatch `SparseTensor` into an `[N, 3]` `Tensor`.
2248 The `SparseTensor` must have rank `R` greater than 1, and the first dimension
2249 is treated as the minibatch dimension. Elements of the `SparseTensor`
2250 must be sorted in increasing order of this first dimension. The serialized
2251 `SparseTensor` objects going into each row of the output `Tensor` will have
2252 rank `R-1`.
2254 The minibatch size `N` is extracted from `sparse_shape[0]`.
2256 Args:
2257 sp_input: The input rank `R` `SparseTensor`.
2258 out_type: The `dtype` to use for serialization.
2259 name: A name prefix for the returned tensors (optional).
2261 Returns:
2262 A matrix (2-D `Tensor`) with `N` rows and `3` columns. Each column
2263 represents serialized `SparseTensor`'s indices, values, and shape
2264 (respectively).
2266 Raises:
2267 TypeError: If `sp_input` is not a `SparseTensor`.
2268 """
2269 sp_input = _convert_to_sparse_tensor(sp_input)
2271 return gen_sparse_ops.serialize_many_sparse(
2272 sp_input.indices,
2273 sp_input.values,
2274 sp_input.dense_shape,
2275 name=name,
2276 out_type=out_type)
2279def deserialize_sparse(serialized_sparse, dtype, rank=None, name=None):
2280 """Deserialize `SparseTensor` objects.
2282 The input `serialized_sparse` must have the shape `[?, ?, ..., ?, 3]` where
2283 the last dimension stores serialized `SparseTensor` objects and the other N
2284 dimensions (N >= 0) correspond to a batch. The ranks of the original
2285 `SparseTensor` objects must all match. When the final `SparseTensor` is
2286 created, its rank is the rank of the incoming `SparseTensor` objects plus N;
2287 the sparse tensors have been concatenated along new dimensions, one for each
2288 batch.
2290 The output `SparseTensor` object's shape values for the original dimensions
2291 are the max across the input `SparseTensor` objects' shape values for the
2292 corresponding dimensions. The new dimensions match the size of the batch.
2294 The input `SparseTensor` objects' indices are assumed ordered in
2295 standard lexicographic order. If this is not the case, after this
2296 step run `SparseReorder` to restore index ordering.
2298 For example, if the serialized input is a `[2 x 3]` matrix representing two
2299 original `SparseTensor` objects:
2301 index = [ 0]
2302 [10]
2303 [20]
2304 values = [1, 2, 3]
2305 shape = [50]
2307 and
2309 index = [ 2]
2310 [10]
2311 values = [4, 5]
2312 shape = [30]
2314 then the final deserialized `SparseTensor` will be:
2316 index = [0 0]
2317 [0 10]
2318 [0 20]
2319 [1 2]
2320 [1 10]
2321 values = [1, 2, 3, 4, 5]
2322 shape = [2 50]
2324 Args:
2325 serialized_sparse: The serialized `SparseTensor` objects.
2326 The last dimension must have 3 columns.
2327 dtype: The `dtype` of the serialized `SparseTensor` objects.
2328 rank: (optional) Python int, the rank of the `SparseTensor` objects.
2329 name: A name prefix for the returned tensors (optional).
2331 Returns:
2332 A `SparseTensor` representing the deserialized `SparseTensor` objects.
2334 """
2335 output_indices, output_values, output_shape = (
2336 gen_sparse_ops.deserialize_sparse(serialized_sparse, dtype, name=name))
2338 # Feed rank data back in, if available
2339 output_indices.set_shape([None, rank])
2340 output_shape.set_shape([rank])
2342 return sparse_tensor.SparseTensor(output_indices, output_values, output_shape)
2345@tf_export(
2346 "io.deserialize_many_sparse",
2347 v1=["io.deserialize_many_sparse", "deserialize_many_sparse"])
2348@dispatch.add_dispatch_support
2349@deprecation.deprecated_endpoints("deserialize_many_sparse")
2350def deserialize_many_sparse(serialized_sparse, dtype, rank=None, name=None):
2351 """Deserialize and concatenate `SparseTensors` from a serialized minibatch.
2353 The input `serialized_sparse` must be a string matrix of shape `[N x 3]` where
2354 `N` is the minibatch size and the rows correspond to packed outputs of
2355 `serialize_sparse`. The ranks of the original `SparseTensor` objects
2356 must all match. When the final `SparseTensor` is created, it has rank one
2357 higher than the ranks of the incoming `SparseTensor` objects (they have been
2358 concatenated along a new row dimension).
2360 The output `SparseTensor` object's shape values for all dimensions but the
2361 first are the max across the input `SparseTensor` objects' shape values
2362 for the corresponding dimensions. Its first shape value is `N`, the minibatch
2363 size.
2365 The input `SparseTensor` objects' indices are assumed ordered in
2366 standard lexicographic order. If this is not the case, after this
2367 step run `sparse.reorder` to restore index ordering.
2369 For example, if the serialized input is a `[2, 3]` matrix representing two
2370 original `SparseTensor` objects:
2372 index = [ 0]
2373 [10]
2374 [20]
2375 values = [1, 2, 3]
2376 shape = [50]
2378 and
2380 index = [ 2]
2381 [10]
2382 values = [4, 5]
2383 shape = [30]
2385 then the final deserialized `SparseTensor` will be:
2387 index = [0 0]
2388 [0 10]
2389 [0 20]
2390 [1 2]
2391 [1 10]
2392 values = [1, 2, 3, 4, 5]
2393 shape = [2 50]
2395 Args:
2396 serialized_sparse: 2-D `Tensor` of type `string` of shape `[N, 3]`.
2397 The serialized and packed `SparseTensor` objects.
2398 dtype: The `dtype` of the serialized `SparseTensor` objects.
2399 rank: (optional) Python int, the rank of the `SparseTensor` objects.
2400 name: A name prefix for the returned tensors (optional)
2402 Returns:
2403 A `SparseTensor` representing the deserialized `SparseTensor`s,
2404 concatenated along the `SparseTensor`s' first dimension.
2406 All of the serialized `SparseTensor`s must have had the same rank and type.
2407 """
2408 output_indices, output_values, output_shape = (
2409 gen_sparse_ops.deserialize_many_sparse(
2410 serialized_sparse, dtype, name=name))
2412 # Feed rank data back in, if available
2413 output_indices.set_shape([None, rank])
2414 output_shape.set_shape([rank])
2416 return sparse_tensor.SparseTensor(output_indices, output_values, output_shape)
2419@tf_export("sparse.sparse_dense_matmul",
2420 v1=["sparse.sparse_dense_matmul", "sparse.matmul",
2421 "sparse_tensor_dense_matmul"])
2422@deprecation.deprecated_endpoints("sparse_tensor_dense_matmul")
2423def sparse_tensor_dense_matmul(sp_a,
2424 b,
2425 adjoint_a=False,
2426 adjoint_b=False,
2427 name=None):
2428 # pylint: disable=line-too-long
2429 """Multiply SparseTensor (or dense Matrix) (of rank 2) "A" by dense matrix
2431 (or SparseTensor) "B". Please note that one and only one of the inputs MUST
2432 be a SparseTensor and the other MUST be a dense matrix.
2434 The following input format is recommended (but not required) for optimal
2435 performance:
2437 * If `adjoint_a == false`: `A` should be sorted in lexicographically
2438 increasing order. Use `sparse.reorder` if you're not sure.
2439 * If `adjoint_a == true`: `A` should be sorted in order of increasing
2440 dimension 1 (i.e., "column major" order instead of "row major" order).
2442 Args:
2443 sp_a: SparseTensor (or dense Matrix) A, of rank 2.
2444 b: dense Matrix (or SparseTensor) B, with the same dtype as sp_a.
2445 adjoint_a: Use the adjoint of A in the matrix multiply. If A is complex,
2446 this is transpose(conj(A)). Otherwise it's transpose(A).
2447 adjoint_b: Use the adjoint of B in the matrix multiply. If B is complex,
2448 this is transpose(conj(B)). Otherwise it's transpose(B).
2449 name: A name prefix for the returned tensors (optional)
2451 Returns:
2452 A dense matrix (pseudo-code in dense np.matrix notation):
2453 `A = A.H if adjoint_a else A`
2454 `B = B.H if adjoint_b else B`
2455 `return A*B`
2457 Notes:
2459 Using `tf.nn.embedding_lookup_sparse` for sparse multiplication:
2461 It's not obvious but you can consider `embedding_lookup_sparse` as another
2462 sparse and dense multiplication. In some situations, you may prefer to use
2463 `embedding_lookup_sparse` even though you're not dealing with embeddings.
2465 There are two questions to ask in the decision process: Do you need gradients
2466 computed as sparse too? Is your sparse data represented as two
2467 `SparseTensor`s: ids and values? There is more explanation about data format
2468 below. If you answer any of these questions as yes, consider using
2469 `tf.nn.embedding_lookup_sparse`.
2471 Following explains differences between the expected SparseTensors:
2472 For example if dense form of your sparse data has shape `[3, 5]` and values:
2474 [[ a ]
2475 [b c]
2476 [ d ]]
2479 `SparseTensor` format expected by `sparse_tensor_dense_matmul`:
2480 `sp_a` (indices, values):
2482 [0, 1]: a
2483 [1, 0]: b
2484 [1, 4]: c
2485 [2, 2]: d
2487 `SparseTensor` format expected by `embedding_lookup_sparse`:
2488 `sp_ids` `sp_weights`
2490 [0, 0]: 1 [0, 0]: a
2491 [1, 0]: 0 [1, 0]: b
2492 [1, 1]: 4 [1, 1]: c
2493 [2, 0]: 2 [2, 0]: d
2496 Deciding when to use `sparse_tensor_dense_matmul` vs.
2497 `matmul`(a_is_sparse=True):
2499 There are a number of questions to ask in the decision process, including:
2501 * Will the SparseTensor `A` fit in memory if densified?
2502 * Is the column count of the product large (>> 1)?
2503 * Is the density of `A` larger than approximately 15%?
2505 If the answer to several of these questions is yes, consider
2506 converting the `SparseTensor` to a dense one and using `tf.matmul` with
2507 `a_is_sparse=True`.
2509 This operation tends to perform well when `A` is more sparse, if the column
2510 size of the product is small (e.g. matrix-vector multiplication), if
2511 `sp_a.dense_shape` takes on large values.
2513 Below is a rough speed comparison between `sparse_tensor_dense_matmul`,
2514 labeled 'sparse', and `matmul`(a_is_sparse=True), labeled 'dense'. For
2515 purposes of the comparison, the time spent converting from a `SparseTensor` to
2516 a dense `Tensor` is not included, so it is overly conservative with respect to
2517 the time ratio.
2519 Benchmark system:
2520 CPU: Intel Ivybridge with HyperThreading (6 cores) dL1:32KB dL2:256KB dL3:12MB
2521 GPU: NVidia Tesla k40c
2523 Compiled with:
2524 `-c opt --config=cuda --copt=-mavx`
2526 ```
2527 tensorflow/python/sparse_tensor_dense_matmul_op_test --benchmarks
2528 A sparse [m, k] with % nonzero values between 1% and 80%
2529 B dense [k, n]
2531 % nnz n gpu m k dt(dense) dt(sparse) dt(sparse)/dt(dense)
2532 0.01 1 True 100 100 0.000221166 0.00010154 0.459112
2533 0.01 1 True 100 1000 0.00033858 0.000109275 0.322745
2534 0.01 1 True 1000 100 0.000310557 9.85661e-05 0.317385
2535 0.01 1 True 1000 1000 0.0008721 0.000100875 0.115669
2536 0.01 1 False 100 100 0.000208085 0.000107603 0.51711
2537 0.01 1 False 100 1000 0.000327112 9.51118e-05 0.290762
2538 0.01 1 False 1000 100 0.000308222 0.00010345 0.335635
2539 0.01 1 False 1000 1000 0.000865721 0.000101397 0.117124
2540 0.01 10 True 100 100 0.000218522 0.000105537 0.482958
2541 0.01 10 True 100 1000 0.000340882 0.000111641 0.327506
2542 0.01 10 True 1000 100 0.000315472 0.000117376 0.372064
2543 0.01 10 True 1000 1000 0.000905493 0.000123263 0.136128
2544 0.01 10 False 100 100 0.000221529 9.82571e-05 0.44354
2545 0.01 10 False 100 1000 0.000330552 0.000112615 0.340687
2546 0.01 10 False 1000 100 0.000341277 0.000114097 0.334324
2547 0.01 10 False 1000 1000 0.000819944 0.000120982 0.147549
2548 0.01 25 True 100 100 0.000207806 0.000105977 0.509981
2549 0.01 25 True 100 1000 0.000322879 0.00012921 0.400181
2550 0.01 25 True 1000 100 0.00038262 0.00014158 0.370035
2551 0.01 25 True 1000 1000 0.000865438 0.000202083 0.233504
2552 0.01 25 False 100 100 0.000209401 0.000104696 0.499979
2553 0.01 25 False 100 1000 0.000321161 0.000130737 0.407076
2554 0.01 25 False 1000 100 0.000377012 0.000136801 0.362856
2555 0.01 25 False 1000 1000 0.000861125 0.00020272 0.235413
2556 0.2 1 True 100 100 0.000206952 9.69219e-05 0.46833
2557 0.2 1 True 100 1000 0.000348674 0.000147475 0.422959
2558 0.2 1 True 1000 100 0.000336908 0.00010122 0.300439
2559 0.2 1 True 1000 1000 0.001022 0.000203274 0.198898
2560 0.2 1 False 100 100 0.000207532 9.5412e-05 0.459746
2561 0.2 1 False 100 1000 0.000356127 0.000146824 0.41228
2562 0.2 1 False 1000 100 0.000322664 0.000100918 0.312764
2563 0.2 1 False 1000 1000 0.000998987 0.000203442 0.203648
2564 0.2 10 True 100 100 0.000211692 0.000109903 0.519165
2565 0.2 10 True 100 1000 0.000372819 0.000164321 0.440753
2566 0.2 10 True 1000 100 0.000338651 0.000144806 0.427596
2567 0.2 10 True 1000 1000 0.00108312 0.000758876 0.70064
2568 0.2 10 False 100 100 0.000215727 0.000110502 0.512231
2569 0.2 10 False 100 1000 0.000375419 0.0001613 0.429653
2570 0.2 10 False 1000 100 0.000336999 0.000145628 0.432132
2571 0.2 10 False 1000 1000 0.00110502 0.000762043 0.689618
2572 0.2 25 True 100 100 0.000218705 0.000129913 0.594009
2573 0.2 25 True 100 1000 0.000394794 0.00029428 0.745402
2574 0.2 25 True 1000 100 0.000404483 0.0002693 0.665788
2575 0.2 25 True 1000 1000 0.0012002 0.00194494 1.62052
2576 0.2 25 False 100 100 0.000221494 0.0001306 0.589632
2577 0.2 25 False 100 1000 0.000396436 0.000297204 0.74969
2578 0.2 25 False 1000 100 0.000409346 0.000270068 0.659754
2579 0.2 25 False 1000 1000 0.00121051 0.00193737 1.60046
2580 0.5 1 True 100 100 0.000214981 9.82111e-05 0.456836
2581 0.5 1 True 100 1000 0.000415328 0.000223073 0.537101
2582 0.5 1 True 1000 100 0.000358324 0.00011269 0.314492
2583 0.5 1 True 1000 1000 0.00137612 0.000437401 0.317851
2584 0.5 1 False 100 100 0.000224196 0.000101423 0.452386
2585 0.5 1 False 100 1000 0.000400987 0.000223286 0.556841
2586 0.5 1 False 1000 100 0.000368825 0.00011224 0.304318
2587 0.5 1 False 1000 1000 0.00136036 0.000429369 0.31563
2588 0.5 10 True 100 100 0.000222125 0.000112308 0.505608
2589 0.5 10 True 100 1000 0.000461088 0.00032357 0.701753
2590 0.5 10 True 1000 100 0.000394624 0.000225497 0.571422
2591 0.5 10 True 1000 1000 0.00158027 0.00190898 1.20801
2592 0.5 10 False 100 100 0.000232083 0.000114978 0.495418
2593 0.5 10 False 100 1000 0.000454574 0.000324632 0.714146
2594 0.5 10 False 1000 100 0.000379097 0.000227768 0.600817
2595 0.5 10 False 1000 1000 0.00160292 0.00190168 1.18638
2596 0.5 25 True 100 100 0.00023429 0.000151703 0.647501
2597 0.5 25 True 100 1000 0.000497462 0.000598873 1.20386
2598 0.5 25 True 1000 100 0.000460778 0.000557038 1.20891
2599 0.5 25 True 1000 1000 0.00170036 0.00467336 2.74845
2600 0.5 25 False 100 100 0.000228981 0.000155334 0.678371
2601 0.5 25 False 100 1000 0.000496139 0.000620789 1.25124
2602 0.5 25 False 1000 100 0.00045473 0.000551528 1.21287
2603 0.5 25 False 1000 1000 0.00171793 0.00467152 2.71927
2604 0.8 1 True 100 100 0.000222037 0.000105301 0.47425
2605 0.8 1 True 100 1000 0.000410804 0.000329327 0.801664
2606 0.8 1 True 1000 100 0.000349735 0.000131225 0.375212
2607 0.8 1 True 1000 1000 0.00139219 0.000677065 0.48633
2608 0.8 1 False 100 100 0.000214079 0.000107486 0.502085
2609 0.8 1 False 100 1000 0.000413746 0.000323244 0.781261
2610 0.8 1 False 1000 100 0.000348983 0.000131983 0.378193
2611 0.8 1 False 1000 1000 0.00136296 0.000685325 0.50282
2612 0.8 10 True 100 100 0.000229159 0.00011825 0.516017
2613 0.8 10 True 100 1000 0.000498845 0.000532618 1.0677
2614 0.8 10 True 1000 100 0.000383126 0.00029935 0.781336
2615 0.8 10 True 1000 1000 0.00162866 0.00307312 1.88689
2616 0.8 10 False 100 100 0.000230783 0.000124958 0.541452
2617 0.8 10 False 100 1000 0.000493393 0.000550654 1.11606
2618 0.8 10 False 1000 100 0.000377167 0.000298581 0.791642
2619 0.8 10 False 1000 1000 0.00165795 0.00305103 1.84024
2620 0.8 25 True 100 100 0.000233496 0.000175241 0.75051
2621 0.8 25 True 100 1000 0.00055654 0.00102658 1.84458
2622 0.8 25 True 1000 100 0.000463814 0.000783267 1.68875
2623 0.8 25 True 1000 1000 0.00186905 0.00755344 4.04132
2624 0.8 25 False 100 100 0.000240243 0.000175047 0.728625
2625 0.8 25 False 100 1000 0.000578102 0.00104499 1.80763
2626 0.8 25 False 1000 100 0.000485113 0.000776849 1.60138
2627 0.8 25 False 1000 1000 0.00211448 0.00752736 3.55992
2628 ```
2630 """
2631 # pylint: enable=line-too-long
2633 if isinstance(b, sparse_tensor.SparseTensor) \
2634 or isinstance(b, sparse_tensor.SparseTensorValue):
2635 # We can do C * D where C is sparse but if we want to do A * B when
2636 # B is sparse we have to transpose. But AB = (B'A')' so we have to feed in
2637 # the transpose of the arguments as well.
2638 if adjoint_a != adjoint_b:
2639 return array_ops.transpose(
2640 sparse_tensor_dense_matmul(b, sp_a, adjoint_a, adjoint_b))
2641 else:
2642 return array_ops.transpose(
2643 sparse_tensor_dense_matmul(
2644 b, sp_a, adjoint_a=not adjoint_a, adjoint_b=not adjoint_b))
2646 else:
2647 sp_a = _convert_to_sparse_tensor(sp_a)
2648 with ops.name_scope(name, "SparseTensorDenseMatMul",
2649 [sp_a.indices, sp_a.values, b]) as name:
2650 b = ops.convert_to_tensor(b, name="b")
2651 return gen_sparse_ops.sparse_tensor_dense_mat_mul(
2652 a_indices=sp_a.indices,
2653 a_values=sp_a.values,
2654 a_shape=sp_a.dense_shape,
2655 b=b,
2656 adjoint_a=adjoint_a,
2657 adjoint_b=adjoint_b)
2660@tf_export("sparse.softmax", v1=["sparse.softmax", "sparse_softmax"])
2661@deprecation.deprecated_endpoints("sparse_softmax")
2662def sparse_softmax(sp_input, name=None):
2663 """Applies softmax to a batched N-D `SparseTensor`.
2665 The inputs represent an N-D SparseTensor with logical shape `[..., B, C]`
2666 (where `N >= 2`), and with indices sorted in the canonical lexicographic
2667 order.
2669 This op is equivalent to applying the normal `tf.nn.softmax()` to each
2670 innermost logical submatrix with shape `[B, C]`, but with the catch that *the
2671 implicitly zero elements do not participate*. Specifically, the algorithm is
2672 equivalent to:
2674 (1) Applies `tf.nn.softmax()` to a densified view of each innermost
2675 submatrix with shape `[B, C]`, along the size-C dimension;
2676 (2) Masks out the original implicitly-zero locations;
2677 (3) Renormalizes the remaining elements.
2679 Hence, the `SparseTensor` result has exactly the same non-zero indices and
2680 shape.
2682 Example using a 3-D SparseTensor:
2684 >>> st = tf.sparse.from_dense(
2685 ... [[[0., np.e],
2686 ... [1., 0.]],
2687 ...
2688 ... [[np.e, 0.],
2689 ... [np.e, np.e]]])
2690 >>> res = tf.sparse.softmax(st)
2691 >>> res.indices
2692 <tf.Tensor: shape=(5, 3), dtype=int64, numpy=
2693 array([[0, 0, 1],
2694 [0, 1, 0],
2695 [1, 0, 0],
2696 [1, 1, 0],
2697 [1, 1, 1]])>
2698 >>> res.values
2699 <tf.Tensor: ... numpy=array([1. , 1. , 1. , 0.5, 0.5], dtype=float32)>
2700 >>> res.dense_shape
2701 <tf.Tensor: shape=(3,), dtype=int64, numpy=array([2, 2, 2])>
2702 >>> tf.sparse.to_dense(res)
2703 <tf.Tensor: shape=(2, 2, 2), dtype=float32, numpy=
2704 array([[[0. , 1. ],
2705 [1. , 0. ]],
2706 [[1. , 0. ],
2707 [0.5, 0.5]]], dtype=float32)>
2709 Args:
2710 sp_input: N-D `SparseTensor`, where `N >= 2`.
2711 name: optional name of the operation.
2712 Returns:
2713 output: N-D `SparseTensor` representing the results.
2714 """
2715 with ops.name_scope(name, "SparseSoftmax",
2716 [sp_input.indices, sp_input.values]) as name:
2717 out_vals = gen_sparse_ops.sparse_softmax(sp_input.indices, sp_input.values,
2718 sp_input.dense_shape)
2719 return sparse_tensor.SparseTensor(sp_input.indices, out_vals,
2720 sp_input.dense_shape)
2723@tf_export("sparse.maximum", v1=["sparse.maximum", "sparse_maximum"])
2724@deprecation.deprecated_endpoints("sparse_maximum")
2725def sparse_maximum(sp_a, sp_b, name=None):
2726 """Returns the element-wise max of two SparseTensors.
2728 Assumes the two SparseTensors have the same shape, i.e., no broadcasting.
2730 Example:
2732 >>> sp_zero = tf.sparse.SparseTensor([[0]], [0], [7])
2733 >>> sp_one = tf.sparse.SparseTensor([[1]], [1], [7])
2734 >>> res = tf.sparse.maximum(sp_zero, sp_one)
2735 >>> res.indices
2736 <tf.Tensor: shape=(2, 1), dtype=int64, numpy=
2737 array([[0],
2738 [1]])>
2739 >>> res.values
2740 <tf.Tensor: shape=(2,), dtype=int32, numpy=array([0, 1], dtype=int32)>
2741 >>> res.dense_shape
2742 <tf.Tensor: shape=(1,), dtype=int64, numpy=array([7])>
2744 The reduction version of this elementwise operation is `tf.sparse.reduce_max`
2746 Args:
2747 sp_a: a `SparseTensor` operand whose dtype is real, and indices
2748 lexicographically ordered.
2749 sp_b: the other `SparseTensor` operand with the same requirements (and the
2750 same shape).
2751 name: optional name of the operation.
2752 Returns:
2753 output: the output SparseTensor.
2754 """
2755 with ops.name_scope(
2756 name, "SparseSparseMaximum",
2757 [sp_a.indices, sp_a.values, sp_b.indices, sp_b.values]) as name:
2758 out_indices, out_values = gen_sparse_ops.sparse_sparse_maximum(
2759 sp_a.indices,
2760 sp_a.values,
2761 sp_a.dense_shape,
2762 sp_b.indices,
2763 sp_b.values,
2764 sp_b.dense_shape,
2765 name=name)
2766 return sparse_tensor.SparseTensor(out_indices, out_values, sp_a.dense_shape)
2769@tf_export("sparse.minimum", v1=["sparse.minimum", "sparse_minimum"])
2770@deprecation.deprecated_endpoints("sparse_minimum")
2771def sparse_minimum(sp_a, sp_b, name=None):
2772 """Returns the element-wise min of two SparseTensors.
2774 Assumes the two SparseTensors have the same shape, i.e., no broadcasting.
2776 Example:
2778 >>> sp_zero = tf.sparse.SparseTensor([[0]], [0], [7])
2779 >>> sp_one = tf.sparse.SparseTensor([[1]], [1], [7])
2780 >>> res = tf.sparse.minimum(sp_zero, sp_one)
2781 >>> res.indices
2782 <tf.Tensor: shape=(2, 1), dtype=int64, numpy=
2783 array([[0],
2784 [1]])>
2785 >>> res.values
2786 <tf.Tensor: shape=(2,), dtype=int32, numpy=array([0, 0], dtype=int32)>
2787 >>> res.dense_shape
2788 <tf.Tensor: shape=(1,), dtype=int64, numpy=array([7])>
2790 Args:
2791 sp_a: a `SparseTensor` operand whose dtype is real, and indices
2792 lexicographically ordered.
2793 sp_b: the other `SparseTensor` operand with the same requirements (and the
2794 same shape).
2795 name: optional name of the operation.
2796 Returns:
2797 output: the output SparseTensor.
2798 """
2799 with ops.name_scope(
2800 name, "SparseSparseMinimum",
2801 [sp_a.indices, sp_a.values, sp_b.indices, sp_b.values]) as name:
2802 out_indices, out_values = gen_sparse_ops.sparse_sparse_minimum(
2803 sp_a.indices,
2804 sp_a.values,
2805 sp_a.dense_shape,
2806 sp_b.indices,
2807 sp_b.values,
2808 sp_b.dense_shape,
2809 name=name)
2810 return sparse_tensor.SparseTensor(out_indices, out_values, sp_a.dense_shape)
2813@tf_export("sparse.transpose", v1=["sparse.transpose", "sparse_transpose"])
2814@deprecation.deprecated_endpoints("sparse_transpose")
2815def sparse_transpose(sp_input, perm=None, name=None):
2816 """Transposes a `SparseTensor`.
2818 Permutes the dimensions according to the value of `perm`. This is the sparse
2819 version of `tf.transpose`.
2821 The returned tensor's dimension `i` will correspond to the input dimension
2822 `perm[i]`. If `perm` is not given, it is set to (n-1...0), where n is the rank
2823 of the input tensor. Hence, by default, this operation performs a regular
2824 matrix transpose on 2-D input Tensors.
2826 For example:
2828 >>> x = tf.SparseTensor(indices=[[0, 1], [0, 3], [2, 3], [3, 1]],
2829 ... values=[1.1, 2.2, 3.3, 4.4],
2830 ... dense_shape=[4, 5])
2831 >>> print('x =', tf.sparse.to_dense(x))
2832 x = tf.Tensor(
2833 [[0. 1.1 0. 2.2 0. ]
2834 [0. 0. 0. 0. 0. ]
2835 [0. 0. 0. 3.3 0. ]
2836 [0. 4.4 0. 0. 0. ]], shape=(4, 5), dtype=float32)
2838 >>> x_transpose = tf.sparse.transpose(x)
2839 >>> print('x_transpose =', tf.sparse.to_dense(x_transpose))
2840 x_transpose = tf.Tensor(
2841 [[0. 0. 0. 0. ]
2842 [1.1 0. 0. 4.4]
2843 [0. 0. 0. 0. ]
2844 [2.2 0. 3.3 0. ]
2845 [0. 0. 0. 0. ]], shape=(5, 4), dtype=float32)
2847 Equivalently, you could call `tf.sparse.transpose(x, perm=[1, 0])`. The
2848 `perm` argument is more useful for n-dimensional tensors where n > 2.
2850 >>> x = tf.SparseTensor(indices=[[0, 0, 1], [0, 0, 3], [1, 2, 3], [1, 3, 1]],
2851 ... values=[1.1, 2.2, 3.3, 4.4],
2852 ... dense_shape=[2, 4, 5])
2853 >>> print('x =', tf.sparse.to_dense(x))
2854 x = tf.Tensor(
2855 [[[0. 1.1 0. 2.2 0. ]
2856 [0. 0. 0. 0. 0. ]
2857 [0. 0. 0. 0. 0. ]
2858 [0. 0. 0. 0. 0. ]]
2859 [[0. 0. 0. 0. 0. ]
2860 [0. 0. 0. 0. 0. ]
2861 [0. 0. 0. 3.3 0. ]
2862 [0. 4.4 0. 0. 0. ]]], shape=(2, 4, 5), dtype=float32)
2864 As above, simply calling `tf.sparse.transpose` will default to `perm=[2,1,0]`.
2866 To take the transpose of a batch of sparse matrices, where 0 is the batch
2867 dimension, you would set `perm=[0,2,1]`.
2869 >>> x_transpose = tf.sparse.transpose(x, perm=[0, 2, 1])
2870 >>> print('x_transpose =', tf.sparse.to_dense(x_transpose))
2871 x_transpose = tf.Tensor(
2872 [[[0. 0. 0. 0. ]
2873 [1.1 0. 0. 0. ]
2874 [0. 0. 0. 0. ]
2875 [2.2 0. 0. 0. ]
2876 [0. 0. 0. 0. ]]
2877 [[0. 0. 0. 0. ]
2878 [0. 0. 0. 4.4]
2879 [0. 0. 0. 0. ]
2880 [0. 0. 3.3 0. ]
2881 [0. 0. 0. 0. ]]], shape=(2, 5, 4), dtype=float32)
2883 Args:
2884 sp_input: The input `SparseTensor`.
2885 perm: A permutation vector of the dimensions of `sp_input`.
2886 name: A name prefix for the returned tensors (optional).
2888 Returns:
2889 A transposed `SparseTensor`.
2891 Raises:
2892 TypeError: If `sp_input` is not a `SparseTensor`.
2893 """
2894 with ops.name_scope(name, "SparseTranspose", [sp_input]) as name:
2895 if perm is None:
2896 if sp_input.shape.rank is not None:
2897 rank = sp_input.shape.rank
2898 perm = (rank - 1) - np.arange(0, rank, 1)
2899 else:
2900 rank = array_ops.rank(sp_input)
2901 perm = (rank - 1) - math_ops.range(0, rank, 1)
2902 indices = sp_input.indices
2903 transposed_indices = array_ops.transpose(
2904 array_ops.gather(array_ops.transpose(indices), perm))
2906 perm_ = tensor_util.constant_value(ops.convert_to_tensor(perm))
2907 if perm_ is not None and sp_input.get_shape().is_fully_defined():
2908 old_shape_ = sp_input.get_shape().as_list()
2909 transposed_dense_shape = list(old_shape_) # Copy.
2910 for i, p in enumerate(perm_):
2911 transposed_dense_shape[i] = old_shape_[p]
2912 else:
2913 dense_shape = sp_input.dense_shape
2914 transposed_dense_shape = array_ops.gather(dense_shape, perm)
2915 transposed_st = sparse_tensor.SparseTensor(
2916 transposed_indices, sp_input.values, transposed_dense_shape)
2917 transposed_st = sparse_reorder(transposed_st)
2918 return transposed_st
2921@tf_export("sparse.map_values", v1=[])
2922@dispatch.add_dispatch_support
2923def map_values(op, *args, **kwargs):
2924 """Applies `op` to the `.values` tensor of one or more `SparseTensor`s.
2926 Replaces any `SparseTensor` in `args` or `kwargs` with its `values`
2927 tensor (which contains the non-default values for the SparseTensor),
2928 and then calls `op`. Returns a `SparseTensor` that is constructed
2929 from the input `SparseTensor`s' `indices`, `dense_shape`, and the
2930 value returned by the `op`.
2932 If the input arguments contain multiple `SparseTensor`s, then they must have
2933 equal `indices` and dense shapes.
2935 Examples:
2937 >>> s = tf.sparse.from_dense([[1, 2, 0],
2938 ... [0, 4, 0],
2939 ... [1, 0, 0]])
2940 >>> tf.sparse.to_dense(tf.sparse.map_values(tf.ones_like, s)).numpy()
2941 array([[1, 1, 0],
2942 [0, 1, 0],
2943 [1, 0, 0]], dtype=int32)
2945 >>> tf.sparse.to_dense(tf.sparse.map_values(tf.multiply, s, s)).numpy()
2946 array([[ 1, 4, 0],
2947 [ 0, 16, 0],
2948 [ 1, 0, 0]], dtype=int32)
2950 >>> tf.sparse.to_dense(tf.sparse.map_values(tf.add, s, 5)).numpy()
2951 array([[6, 7, 0],
2952 [0, 9, 0],
2953 [6, 0, 0]], dtype=int32)
2955 Note: even though `tf.add(0, 5) != 0`, implicit zeros
2956 will remain unchanged. However, if the sparse tensor contains any explicit
2957 zeros, these will be affected by the mapping!
2959 Args:
2960 op: The operation that should be applied to the SparseTensor `values`. `op`
2961 is typically an element-wise operation (such as math_ops.add), but any
2962 operation that preserves the shape can be used.
2963 *args: Arguments for `op`.
2964 **kwargs: Keyword arguments for `op`.
2966 Returns:
2967 A `SparseTensor` whose `indices` and `dense_shape` matches the `indices`
2968 and `dense_shape` of all input `SparseTensor`s.
2969 Raises:
2970 ValueError: If args contains no `SparseTensor`, or if the `indices`
2971 or `dense_shape`s of the input `SparseTensor`s are not equal.
2972 """
2973 sparse_list = []
2974 inner_args = _replace_sparse_with_values(args, sparse_list)
2975 inner_kwargs = _replace_sparse_with_values(kwargs, sparse_list)
2976 if not sparse_list:
2977 raise ValueError("No SparseTensor in argument list of map_values")
2979 with ops.control_dependencies(_assert_sparse_compatible(sparse_list)):
2980 # Delegate to op, and then compose the result from the transformed values
2981 # and the known indices/dense shape. Since we ensure that indices and shape
2982 # are identical, we can just use the first one.
2983 return sparse_tensor.SparseTensor(sparse_list[0].indices,
2984 op(*inner_args, **inner_kwargs),
2985 sparse_list[0].dense_shape)
2988def _assert_sparse_compatible(sparse_tensors):
2989 """Check that all of `sparse_tensors` have same `indices` and `dense_shape`.
2991 Args:
2992 sparse_tensors: A list of sparse tensors.
2994 Returns:
2995 An op to be used as a control dependency.
2996 """
2997 checks = []
2998 first = sparse_tensors[0]
2999 for t in sparse_tensors[1:]:
3000 checks.append(
3001 check_ops.assert_equal(
3002 first.dense_shape, t.dense_shape, message="Mismatched shapes!"))
3003 checks.append(
3004 check_ops.assert_equal(
3005 first.indices, t.indices, message="Mismatched indices!"))
3006 return checks
3009def _replace_sparse_with_values(value, sparse_list):
3010 """Replace `SparseTensor`s with their values in `value`
3012 Each `SparseTensor` in `value` is replaced by its `values` tensor, and
3013 collects all `SparseTensor`s in `sparse_list`.
3015 Args:
3016 value: A structure of `Tensor`s and `SparseTensor`s
3017 sparse_list: A list. Output parameter that collects all `SparseTensor`s in
3018 `value`.
3020 Returns:
3021 `value` with each SparseTensor replaced by its `.value` attribute.
3022 """
3023 flat_vals = nest.flatten(value, expand_composites=False)
3024 new_vals = []
3025 for v in flat_vals:
3026 if isinstance(v, sparse_tensor.SparseTensor):
3027 sparse_list.append(v)
3028 new_vals.append(v.values)
3029 else:
3030 new_vals.append(v)
3031 return nest.pack_sequence_as(value, new_vals, expand_composites=False)
3034def _add_sparse_to_tensors_map(sp_input,
3035 container=None,
3036 shared_name=None,
3037 name=None):
3038 """Add a `SparseTensor` to a `SparseTensorsMap` and return its handle.
3040 Args:
3041 sp_input: The input `SparseTensor`.
3042 container: The container for the underlying `SparseTensorsMap` (optional).
3043 shared_name: The shared name for the underlying `SparseTensorsMap`
3044 (optional, defaults to the name of the newly created op).
3045 name: A name prefix for the returned tensors (optional).
3047 Returns:
3048 A string 1-vector (1D `Tensor`), with the single element representing the
3049 a unique handle to a `SparseTensor` stored by the `SparseTensorMap`
3050 underlying this op.
3052 Raises:
3053 TypeError: If `sp_input` is not a `SparseTensor`.
3054 """
3055 sp_input = _convert_to_sparse_tensor(sp_input)
3057 return gen_sparse_ops.add_sparse_to_tensors_map(
3058 sp_input.indices,
3059 sp_input.values,
3060 sp_input.dense_shape,
3061 container=container,
3062 shared_name=shared_name,
3063 name=name)
3066def _add_many_sparse_to_tensors_map(sp_input,
3067 container=None,
3068 shared_name=None,
3069 name=None):
3070 """Add a minibatch `SparseTensor` to a `SparseTensorsMap`, return `N` handles.
3072 The `SparseTensor` must have rank `R` greater than 1, and the first dimension
3073 is treated as the minibatch dimension. Elements of the `SparseTensor`
3074 must be sorted in increasing order of this first dimension. The serialized
3075 `SparseTensor` objects going into each row of the output `Tensor` will have
3076 rank `R-1`.
3078 The minibatch size `N` is extracted from `sparse_shape[0]`.
3080 Args:
3081 sp_input: The input rank `R` `SparseTensor`.
3082 container: The container for the underlying `SparseTensorsMap` (optional).
3083 shared_name: The shared name for the underlying `SparseTensorsMap`
3084 (optional, defaults to the name of the newly created op).
3085 name: A name prefix for the returned tensors (optional).
3087 Returns:
3088 A string matrix (2-D `Tensor`) with `N` rows and `1` column.
3089 Each row represents a unique handle to a `SparseTensor` stored by
3090 the `SparseTensorMap` underlying this op.
3092 Raises:
3093 TypeError: If `sp_input` is not a `SparseTensor`.
3094 """
3095 sp_input = _convert_to_sparse_tensor(sp_input)
3097 return gen_sparse_ops.add_many_sparse_to_tensors_map(
3098 sp_input.indices,
3099 sp_input.values,
3100 sp_input.dense_shape,
3101 container=container,
3102 shared_name=shared_name,
3103 name=name)
3106def _take_many_sparse_from_tensors_map(sparse_map_op,
3107 sparse_handles,
3108 rank=None,
3109 name=None):
3110 """Read `SparseTensors` from a `SparseTensorsMap` and concatenate them.
3112 The input `sparse_handles` must be a string matrix of shape `[N, 1]` where
3113 `N` is the minibatch size and the rows correspond to packed outputs of
3114 `add_sparse_to_tensors_map`. The ranks of the original `SparseTensor` objects
3115 must all match. When the final `SparseTensor` is created, it has rank one
3116 higher than the ranks of the incoming `SparseTensor` objects (they have been
3117 concatenated along a new row dimension).
3119 The output `SparseTensor` object's shape values for all dimensions but the
3120 first are the max across the input `SparseTensor` objects' shape values
3121 for the corresponding dimensions. Its first shape value is `N`, the minibatch
3122 size.
3124 The input `SparseTensor` objects' indices are assumed ordered in
3125 standard lexicographic order. If this is not the case, after this
3126 step run `sparse.reorder` to restore index ordering.
3128 For example, if the serialized input is a `[2, 3]` matrix representing two
3129 original `SparseTensor` objects:
3131 index = [ 0]
3132 [10]
3133 [20]
3134 values = [1, 2, 3]
3135 shape = [50]
3137 and
3139 index = [ 2]
3140 [10]
3141 values = [4, 5]
3142 shape = [30]
3144 then the final deserialized `SparseTensor` will be:
3146 index = [0 0]
3147 [0 10]
3148 [0 20]
3149 [1 2]
3150 [1 10]
3151 values = [1, 2, 3, 4, 5]
3152 shape = [2 50]
3154 Args:
3155 sparse_map_op: The `Operation` that created the original handles.
3156 Usually this is, e.g., `add_sparse_to_tensors_map(...).op`.
3157 sparse_handles: 2-D `Tensor` of type `string` of shape `[N, 1]`.
3158 The serialized and packed `SparseTensor` objects.
3159 rank: (optional) Python int, the rank of the `SparseTensor` objects.
3160 name: A name prefix for the returned tensors (optional)
3162 Returns:
3163 A `SparseTensor` representing the deserialized `SparseTensor`s,
3164 concatenated along the `SparseTensor`s' first dimension.
3166 All of the serialized `SparseTensor`s must have had the same rank and type.
3167 """
3168 if not isinstance(sparse_map_op, ops.Operation):
3169 raise TypeError("sparse_map_op be an Operation")
3170 if sparse_map_op.type not in ("AddSparseToTensorsMap",
3171 "AddManySparseToTensorsMap"):
3172 raise TypeError(
3173 "sparse_map_op must be one of AddSparseToTensorsMap or "
3174 "AddSparseToTensorsMap. Instead, found `%s`." % sparse_map_op.type)
3175 with ops.colocate_with(sparse_map_op):
3176 shared_name = sparse_map_op.get_attr("shared_name") or sparse_map_op.name
3177 output_indices, output_values, output_shape = (
3178 gen_sparse_ops.take_many_sparse_from_tensors_map(
3179 sparse_handles,
3180 dtype=sparse_map_op.get_attr("T"),
3181 container=sparse_map_op.get_attr("container"),
3182 shared_name=shared_name,
3183 name=name))
3185 # Feed rank data back in, if available
3186 output_indices.set_shape([None, rank])
3187 output_shape.set_shape([rank])
3189 return sparse_tensor.SparseTensor(output_indices, output_values, output_shape)
3192class _UnaryMapValueDispatcher(dispatch.OpDispatcher):
3193 """OpDispatcher for unary ops that maps base function across sparse values."""
3195 def __init__(self, original_func):
3196 self._original_func = original_func
3197 func_name = get_canonical_name_for_symbol(original_func)
3198 arg_names = tf_inspect.getfullargspec(original_func)[0]
3199 self._x = arg_names[0]
3200 original_func.__doc__ = (
3201 original_func.__doc__.rstrip() + "\n\n" +
3202 (" If `{x}` is a `SparseTensor`, returns\n"
3203 " `SparseTensor({x}.indices, tf.{func}({x}.values, ...), "
3204 "{x}.dense_shape)`").format(x=self._x, func=func_name))
3206 def handle(self, args, kwargs):
3207 if args:
3208 x, args = args[0], args[1:]
3209 else:
3210 kwargs = kwargs.copy()
3211 x = kwargs.pop(self._x, None)
3212 if isinstance(x, sparse_tensor.SparseTensor):
3213 return sparse_tensor.SparseTensor(
3214 indices=x.indices,
3215 values=self._original_func(x.values, *args, **kwargs),
3216 dense_shape=x.dense_shape)
3217 else:
3218 return self.NOT_SUPPORTED
3221_UNARY_OPS = [
3222 # TODO(b/120307967) Add dispatchers for additional TensorFlow ops.
3223 math_ops.abs,
3224 math_ops.negative,
3225 math_ops.sign,
3226 math_ops.square,
3227 math_ops.sqrt,
3228 math_ops.erf,
3229 math_ops.tanh,
3230 # TODO(b/157272291) Add dispatchers for rest of special functions.
3231 special_math_ops.bessel_i0e,
3232 special_math_ops.bessel_i1e,
3233]
3234for unary_op in _UNARY_OPS:
3235 _UnaryMapValueDispatcher(unary_op).register(unary_op)