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