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