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