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