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