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