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