Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorflow/python/ops/sparse_ops.py: 30%

584 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-03 07:57 +0000

1# Copyright 2015 The TensorFlow Authors. All Rights Reserved. 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14# ============================================================================== 

15 

16# pylint: disable=g-short-docstring-punctuation 

17"""Sparse Tensor Representation. 

18 

19See also `tf.sparse.SparseTensor`. 

20""" 

21 

22import numbers 

23 

24import numpy as np 

25 

26from tensorflow.python.framework import constant_op 

27from tensorflow.python.framework import dtypes 

28from tensorflow.python.framework import ops 

29from tensorflow.python.framework import sparse_tensor 

30from tensorflow.python.framework import tensor_shape 

31from tensorflow.python.framework import tensor_util 

32from tensorflow.python.ops import array_ops 

33from tensorflow.python.ops import array_ops_stack 

34from tensorflow.python.ops import check_ops 

35from tensorflow.python.ops import control_flow_ops 

36from tensorflow.python.ops import gen_sparse_ops 

37from tensorflow.python.ops import math_ops 

38from tensorflow.python.ops import special_math_ops 

39# go/tf-wildcard-import 

40# pylint: disable=wildcard-import 

41from tensorflow.python.ops.gen_sparse_ops import * 

42# pylint: enable=wildcard-import 

43from tensorflow.python.util import compat 

44from tensorflow.python.util import deprecation 

45from tensorflow.python.util import dispatch 

46from tensorflow.python.util import nest 

47from tensorflow.python.util import tf_inspect 

48from tensorflow.python.util.compat import collections_abc 

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.from_dense") 

104def from_dense(tensor, name=None): 

105 """Converts a dense tensor into a sparse tensor. 

106 

107 Only elements not equal to zero will be present in the result. The resulting 

108 `SparseTensor` has the same dtype and shape as the input. 

109 

110 >>> sp = tf.sparse.from_dense([0, 0, 3, 0, 1]) 

111 >>> sp.shape.as_list() 

112 [5] 

113 >>> sp.values.numpy() 

114 array([3, 1], dtype=int32) 

115 >>> sp.indices.numpy() 

116 array([[2], 

117 [4]]) 

118 

119 Args: 

120 tensor: A dense `Tensor` to be converted to a `SparseTensor`. 

121 name: Optional name for the op. 

122 

123 Returns: 

124 The `SparseTensor`. 

125 """ 

126 with ops.name_scope(name, "dense_to_sparse"): 

127 tensor = ops.convert_to_tensor(tensor) 

128 indices = array_ops.where_v2( 

129 math_ops.not_equal(tensor, array_ops.zeros_like(tensor))) 

130 values = array_ops.gather_nd(tensor, indices) 

131 shape = array_ops.shape(tensor, out_type=dtypes.int64) 

132 return sparse_tensor.SparseTensor(indices, values, shape) 

133 

134 

135@tf_export("sparse.expand_dims") 

136def sparse_expand_dims(sp_input, axis=None, name=None): 

137 """Returns a tensor with an length 1 axis inserted at index `axis`. 

138 

139 Given a tensor `input`, this operation inserts a dimension of length 1 at the 

140 dimension index `axis` of `input`'s shape. The dimension index follows python 

141 indexing rules: It's zero-based, a negative index it is counted backward 

142 from the end. 

143 

144 This operation is useful to: 

145 

146 * Add an outer "batch" dimension to a single element. 

147 * Align axes for broadcasting. 

148 * To add an inner vector length axis to a tensor of scalars. 

149 

150 For example: 

151 

152 If you have a sparse tensor with shape `[height, width, depth]`: 

153 

154 >>> sp = tf.sparse.SparseTensor(indices=[[3,4,1]], values=[7,], 

155 ... dense_shape=[10,10,3]) 

156 

157 You can add an outer `batch` axis by passing `axis=0`: 

158 

159 >>> tf.sparse.expand_dims(sp, axis=0).shape.as_list() 

160 [1, 10, 10, 3] 

161 

162 The new axis location matches Python `list.insert(axis, 1)`: 

163 

164 >>> tf.sparse.expand_dims(sp, axis=1).shape.as_list() 

165 [10, 1, 10, 3] 

166 

167 Following standard python indexing rules, a negative `axis` counts from the 

168 end so `axis=-1` adds an inner most dimension: 

169 

170 >>> tf.sparse.expand_dims(sp, axis=-1).shape.as_list() 

171 [10, 10, 3, 1] 

172 

173 Note: Unlike `tf.expand_dims` this function includes a default value for the 

174 `axis`: `-1`. So if `axis is not specified, an inner dimension is added. 

175 

176 >>> sp.shape.as_list() 

177 [10, 10, 3] 

178 >>> tf.sparse.expand_dims(sp).shape.as_list() 

179 [10, 10, 3, 1] 

180 

181 This operation requires that `axis` is a valid index for `input.shape`, 

182 following python indexing rules: 

183 

184 ``` 

185 -1-tf.rank(input) <= axis <= tf.rank(input) 

186 ``` 

187 

188 This operation is related to: 

189 

190 * `tf.expand_dims`, which provides this functionality for dense tensors. 

191 * `tf.squeeze`, which removes dimensions of size 1, from dense tensors. 

192 * `tf.sparse.reshape`, which provides more flexible reshaping capability. 

193 

194 Args: 

195 sp_input: A `SparseTensor`. 

196 axis: 0-D (scalar). Specifies the dimension index at which to expand the 

197 shape of `input`. Must be in the range `[-rank(sp_input) - 1, 

198 rank(sp_input)]`. Defaults to `-1`. 

199 name: The name of the output `SparseTensor`. 

200 

201 Returns: 

202 A `SparseTensor` with the same data as `sp_input`, but its shape has an 

203 additional dimension of size 1 added. 

204 """ 

205 rank = sp_input.dense_shape.get_shape()[0] 

206 if rank is None: 

207 rank = array_ops.shape(sp_input.dense_shape)[0] 

208 axis = -1 if axis is None else axis 

209 

210 with ops.name_scope(name, default_name="expand_dims", values=[sp_input]): 

211 if isinstance(axis, compat.integral_types): 

212 axis = ops.convert_to_tensor(axis, name="axis", dtype=dtypes.int32) 

213 elif not isinstance(axis, ops.Tensor): 

214 raise TypeError("axis must be an integer value in range [-rank(sp_input)" 

215 " - 1, rank(sp_input)]") 

216 

217 # Convert axis to a positive value if it is negative. 

218 axis = array_ops.where_v2(axis >= 0, axis, axis + rank + 1) 

219 

220 # Create the new column of indices for the sparse tensor by slicing 

221 # the indices and inserting a new column of indices for the new dimension. 

222 column_size = array_ops.shape(sp_input.indices)[0] 

223 new_index = array_ops.zeros([column_size, 1], dtype=dtypes.int64) 

224 indices_before = array_ops.slice(sp_input.indices, [0, 0], [-1, axis]) 

225 indices_after = array_ops.slice(sp_input.indices, [0, axis], [-1, -1]) 

226 indices = array_ops.concat( 

227 [indices_before, new_index, indices_after], axis=1) 

228 

229 # Create the new dense shape by splicing the tensor [1] in the correct 

230 # dimension of the existing shape. 

231 shape_before = array_ops.slice(sp_input.dense_shape, [0], [axis]) 

232 shape_after = array_ops.slice(sp_input.dense_shape, [axis], [-1]) 

233 new_shape = ops.convert_to_tensor([1], name="new_shape", dtype=dtypes.int64) 

234 shape = array_ops.concat([shape_before, new_shape, shape_after], axis=0) 

235 

236 # Create the output sparse tensor. 

237 return sparse_tensor.SparseTensor( 

238 indices=indices, values=sp_input.values, dense_shape=shape) 

239 

240 

241@tf_export("sparse.eye") 

242def sparse_eye(num_rows, 

243 num_columns=None, 

244 dtype=dtypes.float32, 

245 name=None): 

246 """Creates a two-dimensional sparse tensor with ones along the diagonal. 

247 

248 Args: 

249 num_rows: Non-negative integer or `int32` scalar `tensor` giving the number 

250 of rows in the resulting matrix. 

251 num_columns: Optional non-negative integer or `int32` scalar `tensor` giving 

252 the number of columns in the resulting matrix. Defaults to `num_rows`. 

253 dtype: The type of element in the resulting `Tensor`. 

254 name: A name for this `Op`. Defaults to "eye". 

255 

256 Returns: 

257 A `SparseTensor` of shape [num_rows, num_columns] with ones along the 

258 diagonal. 

259 """ 

260 with ops.name_scope(name, default_name="eye", values=[num_rows, num_columns]): 

261 num_rows = _make_int64_tensor(num_rows, "num_rows") 

262 num_columns = num_rows if num_columns is None else _make_int64_tensor( 

263 num_columns, "num_columns") 

264 

265 # Create the sparse tensor. 

266 diag_size = math_ops.minimum(num_rows, num_columns) 

267 diag_range = math_ops.range(diag_size, dtype=dtypes.int64) 

268 

269 return sparse_tensor.SparseTensor( 

270 indices=array_ops_stack.stack([diag_range, diag_range], axis=1), 

271 values=array_ops.ones(diag_size, dtype=dtype), 

272 dense_shape=[num_rows, num_columns]) 

273 

274 

275# pylint: disable=protected-access 

276@tf_export(v1=["sparse.concat", "sparse_concat"]) 

277@deprecation.deprecated_endpoints("sparse_concat") 

278@deprecation.deprecated_args( 

279 None, "concat_dim is deprecated, use axis instead", "concat_dim") 

280def sparse_concat(axis, 

281 sp_inputs, 

282 name=None, 

283 expand_nonconcat_dim=False, 

284 concat_dim=None, 

285 expand_nonconcat_dims=None): 

286 """Concatenates a list of `SparseTensor` along the specified dimension. 

287 

288 Concatenation is with respect to the dense versions of each sparse input. 

289 It is assumed that each inputs is a `SparseTensor` whose elements are ordered 

290 along increasing dimension number. 

291 

292 If expand_nonconcat_dim is False, all inputs' shapes must match, except for 

293 the concat dimension. If expand_nonconcat_dim is True, then inputs' shapes are 

294 allowed to vary among all inputs. 

295 

296 The `indices`, `values`, and `shapes` lists must have the same length. 

297 

298 If expand_nonconcat_dim is False, then the output shape is identical to the 

299 inputs', except along the concat dimension, where it is the sum of the inputs' 

300 sizes along that dimension. 

301 

302 If expand_nonconcat_dim is True, then the output shape along the non-concat 

303 dimensions will be expand to be the largest among all inputs, and it is the 

304 sum of the inputs sizes along the concat dimension. 

305 

306 The output elements will be resorted to preserve the sort order along 

307 increasing dimension number. 

308 

309 This op runs in `O(M log M)` time, where `M` is the total number of non-empty 

310 values across all inputs. This is due to the need for an internal sort in 

311 order to concatenate efficiently across an arbitrary dimension. 

312 

313 For example, if `axis = 1` and the inputs are 

314 

315 sp_inputs[0]: shape = [2, 3] 

316 [0, 2]: "a" 

317 [1, 0]: "b" 

318 [1, 1]: "c" 

319 

320 sp_inputs[1]: shape = [2, 4] 

321 [0, 1]: "d" 

322 [0, 2]: "e" 

323 

324 then the output will be 

325 

326 shape = [2, 7] 

327 [0, 2]: "a" 

328 [0, 4]: "d" 

329 [0, 5]: "e" 

330 [1, 0]: "b" 

331 [1, 1]: "c" 

332 

333 Graphically this is equivalent to doing 

334 

335 [ a] concat [ d e ] = [ a d e ] 

336 [b c ] [ ] [b c ] 

337 

338 Another example, if 'axis = 1' and the inputs are 

339 

340 sp_inputs[0]: shape = [3, 3] 

341 [0, 2]: "a" 

342 [1, 0]: "b" 

343 [2, 1]: "c" 

344 

345 sp_inputs[1]: shape = [2, 4] 

346 [0, 1]: "d" 

347 [0, 2]: "e" 

348 

349 if expand_nonconcat_dim = False, this will result in an error. But if 

350 expand_nonconcat_dim = True, this will result in: 

351 

352 shape = [3, 7] 

353 [0, 2]: "a" 

354 [0, 4]: "d" 

355 [0, 5]: "e" 

356 [1, 0]: "b" 

357 [2, 1]: "c" 

358 

359 Graphically this is equivalent to doing 

360 

361 [ a] concat [ d e ] = [ a d e ] 

362 [b ] [ ] [b ] 

363 [ c ] [ c ] 

364 

365 

366 Args: 

367 axis: Dimension to concatenate along. Must be in range [-rank, rank), 

368 where rank is the number of dimensions in each input `SparseTensor`. 

369 sp_inputs: List of `SparseTensor` to concatenate. 

370 name: A name prefix for the returned tensors (optional). 

371 expand_nonconcat_dim: Whether to allow the expansion in the non-concat 

372 dimensions. Defaulted to False. 

373 concat_dim: The old (deprecated) name for axis. 

374 expand_nonconcat_dims: alias for expand_nonconcat_dim 

375 

376 Returns: 

377 A `SparseTensor` with the concatenated output. 

378 

379 Raises: 

380 TypeError: If `sp_inputs` is not a list of `SparseTensor`. 

381 """ 

382 expand_nonconcat_dim = deprecation.deprecated_argument_lookup( 

383 "expand_nonconcat_dims", expand_nonconcat_dims, 

384 "expand_nonconcat_dim", expand_nonconcat_dim) 

385 if expand_nonconcat_dims is not None: 

386 expand_nonconcat_dim = expand_nonconcat_dims 

387 axis = deprecation.deprecated_argument_lookup("axis", axis, "concat_dim", 

388 concat_dim) 

389 return sparse_concat_v2(axis, sp_inputs, expand_nonconcat_dim, name) 

390 

391 

392@tf_export("sparse.concat", v1=[]) 

393def sparse_concat_v2(axis, sp_inputs, expand_nonconcat_dims=False, name=None): # pylint: disable=missing-docstring 

394 sp_inputs = _convert_to_sparse_tensors(sp_inputs) 

395 

396 if len(sp_inputs) == 1: # Degenerate case of one tensor. 

397 return sp_inputs[0] 

398 

399 inds = [sp_input.indices for sp_input in sp_inputs] 

400 vals = [sp_input.values for sp_input in sp_inputs] 

401 shapes = [sp_input.dense_shape for sp_input in sp_inputs] 

402 

403 if expand_nonconcat_dims: 

404 max_shape = math_ops.reduce_max( 

405 array_ops.concat( 

406 [array_ops.reshape(shape, [1, -1]) for shape in shapes], 0), 0) 

407 shapes = [ 

408 array_ops.concat([ 

409 max_shape[:axis], shape[-1:] 

410 if axis == -1 else shape[axis:axis + 1], [] 

411 if axis == -1 else max_shape[axis + 1:] 

412 ], 0) for shape in shapes 

413 ] 

414 

415 output_ind, output_val, output_shape = ( 

416 gen_sparse_ops.sparse_concat(inds, vals, shapes, axis, name=name)) 

417 

418 input_shapes = [inp.shape for inp in sp_inputs] 

419 if all(shape.rank is not None for shape in input_shapes): 

420 if expand_nonconcat_dims: 

421 static_output_shape = [] 

422 for dim in range(input_shapes[0].rank): 

423 static_output_shape.append( 

424 max(tensor_shape.dimension_at_index(shape, dim) 

425 for shape in input_shapes)) 

426 else: 

427 static_output_shape = input_shapes[0].as_list() 

428 static_output_shape[axis] = sum( 

429 tensor_shape.dimension_at_index(shape, axis) 

430 for shape in input_shapes) 

431 else: 

432 static_output_shape = tensor_shape.unknown_shape() 

433 if all(shape.is_fully_defined() for shape in input_shapes): 

434 output_shape = ops.convert_to_tensor(static_output_shape, 

435 dtype=dtypes.int64) 

436 return sparse_tensor.SparseTensor(output_ind, output_val, output_shape) 

437 else: 

438 # In case there are partially defined shape, we couldn't update the 

439 # output_shape tensor value. We update the output._dense_shape_default, 

440 # which populate output.shape as the best effort. 

441 output = sparse_tensor.SparseTensor(output_ind, output_val, output_shape) 

442 output.set_shape(tensor_shape.TensorShape(static_output_shape)) 

443 return output 

444 

445 

446sparse_concat_v2.__doc__ = sparse_concat.__doc__.replace( 

447 " concat_dim: The old (deprecated) name for axis.\n", 

448 "").replace(" expand_nonconcat_dims: alias for expand_nonconcat_dim\n", 

449 "") 

450 

451 

452@tf_export(v1=["sparse.add", "sparse_add"]) 

453@deprecation.deprecated_endpoints("sparse_add") 

454@deprecation.deprecated_args( 

455 None, "thresh is deprecated, use threshold instead", "thresh") 

456def sparse_add(a, b, threshold=None, thresh=None): 

457 """Adds two tensors, at least one of each is a `SparseTensor`. 

458 

459 If one `SparseTensor` and one `Tensor` are passed in, returns a `Tensor`. If 

460 both arguments are `SparseTensor`s, this returns a `SparseTensor`. The order 

461 of arguments does not matter. Use vanilla `tf.add()` for adding two dense 

462 `Tensor`s. 

463 

464 The shapes of the two operands must match: broadcasting is not supported. 

465 

466 The indices of any input `SparseTensor` are assumed ordered in standard 

467 lexicographic order. If this is not the case, before this step run 

468 `SparseReorder` to restore index ordering. 

469 

470 If both arguments are sparse, we perform "clipping" as follows. By default, 

471 if two values sum to zero at some index, the output `SparseTensor` would still 

472 include that particular location in its index, storing a zero in the 

473 corresponding value slot. To override this, callers can specify `thresh`, 

474 indicating that if the sum has a magnitude strictly smaller than `thresh`, its 

475 corresponding value and index would then not be included. In particular, 

476 `thresh == 0.0` (default) means everything is kept and actual thresholding 

477 happens only for a positive value. 

478 

479 For example, suppose the logical sum of two sparse operands is (densified): 

480 

481 [ 2] 

482 [.1 0] 

483 [ 6 -.2] 

484 

485 Then, 

486 

487 * `thresh == 0` (the default): all 5 index/value pairs will be returned. 

488 * `thresh == 0.11`: only .1 and 0 will vanish, and the remaining three 

489 index/value pairs will be returned. 

490 * `thresh == 0.21`: .1, 0, and -.2 will vanish. 

491 

492 Args: 

493 a: The first operand; `SparseTensor` or `Tensor`. 

494 b: The second operand; `SparseTensor` or `Tensor`. At least one operand 

495 must be sparse. 

496 threshold: An optional 0-D `Tensor` (defaults to `0`). The magnitude 

497 threshold that determines if an output value/index pair takes space. Its 

498 dtype should match that of the values if they are real; if the latter are 

499 complex64/complex128, then the dtype should be float32/float64, 

500 correspondingly. 

501 thresh: Deprecated alias for `threshold`. 

502 

503 Returns: 

504 A `SparseTensor` or a `Tensor`, representing the sum. 

505 

506 Raises: 

507 TypeError: If both `a` and `b` are `Tensor`s. Use `tf.add()` instead. 

508 """ 

509 threshold = deprecation.deprecated_argument_lookup("threshold", threshold, 

510 "thresh", thresh) 

511 if threshold is None: 

512 threshold = 0 

513 return sparse_add_v2(a, b, threshold) 

514 

515 

516@tf_export("sparse.add", v1=[]) 

517def sparse_add_v2(a, b, threshold=0): 

518 """Adds two tensors, at least one of each is a `SparseTensor`. 

519 

520 If one `SparseTensor` and one `Tensor` are passed in, returns a `Tensor`. If 

521 both arguments are `SparseTensor`s, this returns a `SparseTensor`. The order 

522 of arguments does not matter. Use vanilla `tf.add()` for adding two dense 

523 `Tensor`s. 

524 

525 The shapes of the two operands must match: broadcasting is not supported. 

526 

527 The indices of any input `SparseTensor` are assumed ordered in standard 

528 lexicographic order. If this is not the case, before this step run 

529 `SparseReorder` to restore index ordering. 

530 

531 If both arguments are sparse, we perform "clipping" as follows. By default, 

532 if two values sum to zero at some index, the output `SparseTensor` would still 

533 include that particular location in its index, storing a zero in the 

534 corresponding value slot. To override this, callers can specify `threshold`, 

535 indicating that if the sum has a magnitude strictly smaller than `threshold`, 

536 its corresponding value and index would then not be included. In particular, 

537 `threshold == 0.0` (default) means everything is kept and actual thresholding 

538 happens only for a positive value. 

539 

540 For example, suppose the logical sum of two sparse operands is (densified): 

541 

542 [ 2] 

543 [.1 0] 

544 [ 6 -.2] 

545 

546 Then, 

547 

548 * `threshold == 0` (the default): all 5 index/value pairs will be 

549 returned. 

550 * `threshold == 0.11`: only .1 and 0 will vanish, and the remaining three 

551 index/value pairs will be returned. 

552 * `threshold == 0.21`: .1, 0, and -.2 will vanish. 

553 

554 Args: 

555 a: The first operand; `SparseTensor` or `Tensor`. 

556 b: The second operand; `SparseTensor` or `Tensor`. At least one operand 

557 must be sparse. 

558 threshold: A 0-D `Tensor`. The magnitude threshold that determines if an 

559 output value/index pair takes space. Its dtype should match that of the 

560 values if they are real; if the latter are complex64/complex128, then the 

561 dtype should be float32/float64, correspondingly. 

562 

563 Returns: 

564 A `SparseTensor` or a `Tensor`, representing the sum. 

565 

566 Raises: 

567 TypeError: If both `a` and `b` are `Tensor`s. Use `tf.add()` instead. 

568 """ 

569 sparse_classes = (sparse_tensor.SparseTensor, sparse_tensor.SparseTensorValue) 

570 if not any(isinstance(inp, sparse_classes) for inp in [a, b]): 

571 raise TypeError("At least one input should be SparseTensor; do you mean to" 

572 " use tf.add()?") 

573 

574 if all(isinstance(inp, sparse_classes) for inp in [a, b]): 

575 a = _convert_to_sparse_tensor(a) 

576 b = _convert_to_sparse_tensor(b) 

577 threshold = ops.convert_to_tensor( 

578 threshold, dtype=a.values.dtype.real_dtype.base_dtype, name="threshold") 

579 output_ind, output_val, output_shape = ( 

580 gen_sparse_ops.sparse_add(a.indices, a.values, a.dense_shape, 

581 b.indices, b.values, b.dense_shape, 

582 threshold)) 

583 

584 # Attempt to get output_shape statically. 

585 a.get_shape().assert_is_compatible_with(b.get_shape()) 

586 static_shape = array_ops.broadcast_static_shape(a.get_shape(), 

587 b.get_shape()) 

588 if static_shape.is_fully_defined(): 

589 output_shape = static_shape.as_list() 

590 

591 return sparse_tensor.SparseTensor(output_ind, output_val, output_shape) 

592 else: 

593 # swap to make `a` the SparseTensor. 

594 if isinstance(b, sparse_classes): 

595 a, b = b, a 

596 return gen_sparse_ops.sparse_tensor_dense_add(a.indices, a.values, 

597 a.dense_shape, b) 

598 

599 

600@tf_export("sparse.cross") 

601def sparse_cross(inputs, name=None, separator=None): 

602 """Generates sparse cross from a list of sparse and dense tensors. 

603 

604 For example, if the inputs are 

605 

606 * inputs[0]: SparseTensor with shape = [2, 2] 

607 [0, 0]: "a" 

608 [1, 0]: "b" 

609 [1, 1]: "c" 

610 * inputs[1]: SparseTensor with shape = [2, 1] 

611 [0, 0]: "d" 

612 [1, 0]: "e" 

613 * inputs[2]: Tensor [["f"], ["g"]] 

614 

615 then the output will be: 

616 

617 shape = [2, 2] 

618 [0, 0]: "a_X_d_X_f" 

619 [1, 0]: "b_X_e_X_g" 

620 [1, 1]: "c_X_e_X_g" 

621 

622 Customized separator "_Y_": 

623 

624 >>> inp_0 = tf.constant([['a'], ['b']]) 

625 >>> inp_1 = tf.constant([['c'], ['d']]) 

626 >>> output = tf.sparse.cross([inp_0, inp_1], separator='_Y_') 

627 >>> output.values 

628 <tf.Tensor: shape=(2,), dtype=string, numpy=array([b'a_Y_c', b'b_Y_d'], 

629 dtype=object)> 

630 

631 

632 Args: 

633 inputs: An iterable of `Tensor` or `SparseTensor`. 

634 name: Optional name for the op. 

635 separator: A string added between each string being joined. Defaults to 

636 '_X_'. 

637 

638 Returns: 

639 A `SparseTensor` of type `string`. 

640 """ 

641 if separator is None: 

642 separator = "_X_" 

643 separator = ops.convert_to_tensor(separator, dtypes.string) 

644 indices, values, shapes, dense_inputs = _sparse_cross_internal_v2(inputs) 

645 indices_out, values_out, shape_out = gen_sparse_ops.sparse_cross_v2( 

646 indices=indices, 

647 values=values, 

648 shapes=shapes, 

649 dense_inputs=dense_inputs, 

650 sep=separator, 

651 name=name) 

652 return sparse_tensor.SparseTensor(indices_out, values_out, shape_out) 

653 

654 

655_sparse_cross = sparse_cross 

656 

657 

658@tf_export("sparse.cross_hashed") 

659def sparse_cross_hashed(inputs, num_buckets=0, hash_key=None, name=None): 

660 """Generates hashed sparse cross from a list of sparse and dense tensors. 

661 

662 For example, if the inputs are 

663 

664 * inputs[0]: SparseTensor with shape = [2, 2] 

665 [0, 0]: "a" 

666 [1, 0]: "b" 

667 [1, 1]: "c" 

668 * inputs[1]: SparseTensor with shape = [2, 1] 

669 [0, 0]: "d" 

670 [1, 0]: "e" 

671 * inputs[2]: Tensor [["f"], ["g"]] 

672 

673 then the output will be: 

674 

675 shape = [2, 2] 

676 [0, 0]: FingerprintCat64( 

677 Fingerprint64("f"), FingerprintCat64( 

678 Fingerprint64("d"), Fingerprint64("a"))) 

679 [1, 0]: FingerprintCat64( 

680 Fingerprint64("g"), FingerprintCat64( 

681 Fingerprint64("e"), Fingerprint64("b"))) 

682 [1, 1]: FingerprintCat64( 

683 Fingerprint64("g"), FingerprintCat64( 

684 Fingerprint64("e"), Fingerprint64("c"))) 

685 

686 Args: 

687 inputs: An iterable of `Tensor` or `SparseTensor`. 

688 num_buckets: An `int` that is `>= 0`. 

689 output = hashed_value%num_buckets if num_buckets > 0 else hashed_value. 

690 hash_key: Integer hash_key that will be used by the `FingerprintCat64` 

691 function. If not given, will use a default key. 

692 name: Optional name for the op. 

693 

694 Returns: 

695 A `SparseTensor` of type `int64`. 

696 """ 

697 return _sparse_cross_internal( 

698 inputs=inputs, 

699 hashed_output=True, 

700 num_buckets=num_buckets, 

701 hash_key=hash_key, 

702 name=name) 

703 

704 

705_sparse_cross_hashed = sparse_cross_hashed 

706 

707_DEFAULT_HASH_KEY = 0xDECAFCAFFE 

708 

709 

710def _sparse_cross_internal_v2(inputs): 

711 """See gen_sparse_ops.sparse_cross_v2.""" 

712 if not isinstance(inputs, (tuple, list)): 

713 raise TypeError("Inputs must be a list") 

714 if not all( 

715 isinstance(i, sparse_tensor.SparseTensor) or isinstance(i, ops.Tensor) 

716 for i in inputs): 

717 raise TypeError("All inputs must be Tensor or SparseTensor.") 

718 sparse_inputs = [ 

719 i for i in inputs if isinstance(i, sparse_tensor.SparseTensor) 

720 ] 

721 dense_inputs = [ 

722 i for i in inputs if not isinstance(i, sparse_tensor.SparseTensor) 

723 ] 

724 indices = [sp_input.indices for sp_input in sparse_inputs] 

725 values = [sp_input.values for sp_input in sparse_inputs] 

726 shapes = [sp_input.dense_shape for sp_input in sparse_inputs] 

727 for i in range(len(values)): 

728 if values[i].dtype != dtypes.string: 

729 values[i] = math_ops.cast(values[i], dtypes.int64) 

730 for i in range(len(dense_inputs)): 

731 if dense_inputs[i].dtype != dtypes.string: 

732 dense_inputs[i] = math_ops.cast(dense_inputs[i], dtypes.int64) 

733 return indices, values, shapes, dense_inputs 

734 

735 

736def _sparse_cross_internal(inputs, 

737 hashed_output=False, 

738 num_buckets=0, 

739 hash_key=None, 

740 name=None): 

741 """See gen_sparse_ops.sparse_cross.""" 

742 if not isinstance(inputs, (tuple, list)): 

743 raise TypeError("Inputs must be a list") 

744 if not all( 

745 isinstance(i, sparse_tensor.SparseTensor) or isinstance(i, ops.Tensor) 

746 for i in inputs): 

747 raise TypeError("All inputs must be SparseTensors") 

748 

749 sparse_inputs = [ 

750 i for i in inputs if isinstance(i, sparse_tensor.SparseTensor) 

751 ] 

752 dense_inputs = [ 

753 i for i in inputs if not isinstance(i, sparse_tensor.SparseTensor) 

754 ] 

755 

756 indices = [sp_input.indices for sp_input in sparse_inputs] 

757 values = [sp_input.values for sp_input in sparse_inputs] 

758 shapes = [sp_input.dense_shape for sp_input in sparse_inputs] 

759 out_type = dtypes.int64 if hashed_output else dtypes.string 

760 

761 internal_type = dtypes.string 

762 for i in range(len(values)): 

763 if values[i].dtype != dtypes.string: 

764 values[i] = math_ops.cast(values[i], dtypes.int64) 

765 internal_type = dtypes.int64 

766 for i in range(len(dense_inputs)): 

767 if dense_inputs[i].dtype != dtypes.string: 

768 dense_inputs[i] = math_ops.cast(dense_inputs[i], dtypes.int64) 

769 internal_type = dtypes.int64 

770 

771 indices_out, values_out, shape_out = gen_sparse_ops.sparse_cross( 

772 indices=indices, 

773 values=values, 

774 shapes=shapes, 

775 dense_inputs=dense_inputs, 

776 hashed_output=hashed_output, 

777 num_buckets=num_buckets, 

778 hash_key=hash_key or _DEFAULT_HASH_KEY, 

779 out_type=out_type, 

780 internal_type=internal_type, 

781 name=name) 

782 

783 return sparse_tensor.SparseTensor(indices_out, values_out, shape_out) 

784 

785 

786def sparse_dense_cwise_add(sp_t, dense_t): 

787 """Adds up a SparseTensor and a dense Tensor, using these special rules: 

788 

789 (1) Broadcasts the dense side to have the same shape as the sparse side, if 

790 eligible; 

791 (2) Then, only the dense values pointed to by the indices of the SparseTensor 

792 participate in the cwise addition. 

793 

794 By the rules, the result is a logical SparseTensor with exactly the same 

795 indices and shape, but possibly with different non-zero values. The output of 

796 this Op is the resultant non-zero values. 

797 

798 Args: 

799 sp_t: the SparseTensor operand. 

800 dense_t: the dense Tensor operand; must have the same dtype and a 

801 broadcast-compatible shape as `sp_t`. 

802 

803 Returns: 

804 output: the SparseTensor output. 

805 """ 

806 result = gen_sparse_ops.sparse_dense_cwise_add(sp_t.indices, sp_t.values, 

807 sp_t.dense_shape, dense_t) 

808 return sparse_tensor.SparseTensor(sp_t.indices, result, sp_t.dense_shape) 

809 

810 

811@tf_export("sparse.reorder", v1=["sparse.reorder", "sparse_reorder"]) 

812@deprecation.deprecated_endpoints("sparse_reorder") 

813def sparse_reorder(sp_input, name=None): 

814 """Reorders a `SparseTensor` into the canonical, row-major ordering. 

815 

816 Note that by convention, all sparse ops preserve the canonical ordering 

817 along increasing dimension number. The only time ordering can be violated 

818 is during manual manipulation of the indices and values to add entries. 

819 

820 Reordering does not affect the shape of the `SparseTensor`. 

821 

822 For example, if `sp_input` has shape `[4, 5]` and `indices` / `values`: 

823 

824 [0, 3]: b 

825 [0, 1]: a 

826 [3, 1]: d 

827 [2, 0]: c 

828 

829 then the output will be a `SparseTensor` of shape `[4, 5]` and 

830 `indices` / `values`: 

831 

832 [0, 1]: a 

833 [0, 3]: b 

834 [2, 0]: c 

835 [3, 1]: d 

836 

837 Args: 

838 sp_input: The input `SparseTensor`. 

839 name: A name prefix for the returned tensors (optional) 

840 

841 Returns: 

842 A `SparseTensor` with the same shape and non-empty values, but in 

843 canonical ordering. 

844 

845 Raises: 

846 TypeError: If `sp_input` is not a `SparseTensor`. 

847 """ 

848 sp_input = _convert_to_sparse_tensor(sp_input) 

849 

850 reordered_ind, reordered_val = ( 

851 gen_sparse_ops.sparse_reorder( 

852 sp_input.indices, sp_input.values, sp_input.dense_shape, name=name)) 

853 

854 if sp_input.get_shape().is_fully_defined(): 

855 dense_shape = sp_input.get_shape().as_list() 

856 return sparse_tensor.SparseTensor(reordered_ind, reordered_val, dense_shape) 

857 else: 

858 dense_shape = array_ops.identity(sp_input.dense_shape) 

859 sp_output = sparse_tensor.SparseTensor(reordered_ind, reordered_val, 

860 dense_shape) 

861 # propagate the static shape 

862 sp_output.set_shape(sp_input.shape) 

863 return sp_output 

864 

865 

866@tf_export("sparse.reshape", v1=["sparse.reshape", "sparse_reshape"]) 

867@deprecation.deprecated_endpoints("sparse_reshape") 

868@dispatch.add_dispatch_support 

869def sparse_reshape(sp_input, shape, name=None): 

870 """Reshapes a `SparseTensor` to represent values in a new dense shape. 

871 

872 This operation has the same semantics as `reshape` on the represented dense 

873 tensor. The indices of non-empty values in `sp_input` are recomputed based 

874 on the new dense shape, and a new `SparseTensor` is returned containing the 

875 new indices and new shape. The order of non-empty values in `sp_input` is 

876 unchanged. 

877 

878 If one component of `shape` is the special value -1, the size of that 

879 dimension is computed so that the total dense size remains constant. At 

880 most one component of `shape` can be -1. The number of dense elements 

881 implied by `shape` must be the same as the number of dense elements 

882 originally represented by `sp_input`. 

883 

884 For example, if `sp_input` has shape `[2, 3, 6]` and `indices` / `values`: 

885 

886 [0, 0, 0]: a 

887 [0, 0, 1]: b 

888 [0, 1, 0]: c 

889 [1, 0, 0]: d 

890 [1, 2, 3]: e 

891 

892 and `shape` is `[9, -1]`, then the output will be a `SparseTensor` of 

893 shape `[9, 4]` and `indices` / `values`: 

894 

895 [0, 0]: a 

896 [0, 1]: b 

897 [1, 2]: c 

898 [4, 2]: d 

899 [8, 1]: e 

900 

901 Args: 

902 sp_input: The input `SparseTensor`. 

903 shape: A 1-D (vector) int64 `Tensor` specifying the new dense shape of the 

904 represented `SparseTensor`. 

905 name: A name prefix for the returned tensors (optional) 

906 

907 Returns: 

908 A `SparseTensor` with the same non-empty values but with indices calculated 

909 by the new dense shape. 

910 

911 Raises: 

912 TypeError: If `sp_input` is not a `SparseTensor`. 

913 ValueError: If argument `shape` requests a `SparseTensor` with a different 

914 number of elements than `sp_input`. 

915 ValueError: If `shape` has more than one inferred (== -1) dimension. 

916 """ 

917 sp_input = _convert_to_sparse_tensor(sp_input) 

918 shape = math_ops.cast(shape, dtype=dtypes.int64) 

919 

920 with ops.name_scope(name, "SparseReshape", [sp_input]) as name: 

921 reshaped_ind, reshaped_shape = gen_sparse_ops.sparse_reshape( 

922 sp_input.indices, sp_input.dense_shape, shape, name=name) 

923 

924 reshaped_shape_const = tensor_util.constant_value_as_shape(shape) 

925 reshaped_shape_const = ( 

926 reshaped_shape_const.as_list() if reshaped_shape_const.ndims is not None 

927 else None) 

928 

929 if (reshaped_shape_const is not None 

930 and sp_input.shape.is_fully_defined()): 

931 # constant_value_as_shape tends to get more information about the partial 

932 # shape values, but here we specifically need to know if the *user* passed 

933 # a shape with 2+ unknown dimensions; and for that constant_value 

934 # provides either the user's direct value or None if only partial elements 

935 # are known via the python shape inference code. 

936 shape_const_by_user = tensor_util.constant_value(shape) 

937 if shape_const_by_user is not None: 

938 num_implied_by_user = sum(d == -1 for d in shape_const_by_user) 

939 if num_implied_by_user > 1: 

940 raise ValueError( 

941 "At most one dimension can be inferred (-1). Found: %s" 

942 % shape_const_by_user) 

943 original_reshaped_shape = list(reshaped_shape_const) # A copy 

944 in_shape_size = np.prod(sp_input.shape.as_list()) 

945 num_implied = sum(dim is None for dim in reshaped_shape_const) 

946 

947 # If there is a 0 dim in the user-provided shape, we cannot infer the 

948 # unknown dim reliably. This is why we skip the `if` branch below when 

949 # a 0 is present in `reshaped_shape_const`. Same below. 

950 if num_implied == 1 and 0 not in reshaped_shape_const: 

951 implied_idx = original_reshaped_shape.index(None) 

952 non_implied_idx = ( 

953 original_reshaped_shape[:implied_idx] + 

954 original_reshaped_shape[implied_idx + 1:]) 

955 reshaped_shape_const[implied_idx] = int( 

956 in_shape_size // np.prod(non_implied_idx)) 

957 if num_implied == 0 or (num_implied == 1 and 

958 0 not in reshaped_shape_const): 

959 reshaped_size = np.prod(reshaped_shape_const) 

960 if reshaped_size != in_shape_size: 

961 raise ValueError( 

962 "Cannot reshape a tensor with %d elements to shape %s " 

963 "(%d elements)." % 

964 (in_shape_size, original_reshaped_shape, reshaped_size)) 

965 reshaped_shape = constant_op.constant( 

966 reshaped_shape_const, dtype=dtypes.int64) 

967 

968 return sparse_tensor.SparseTensor(reshaped_ind, 

969 array_ops.identity(sp_input.values), 

970 reshaped_shape) 

971 

972 

973# TODO(aselle): Remove keyword required once for 1.0 final 

974class KeywordRequired: 

975 

976 def __repr__(self): 

977 # This is needed to make documentation without fully qualified module paths 

978 return "KeywordRequired()" 

979 

980 

981@tf_export(v1=["sparse.split", "sparse_split"]) 

982@deprecation.deprecated_endpoints("sparse_split") 

983@deprecation.deprecated_args( 

984 None, "split_dim is deprecated, use axis instead", "split_dim") 

985def sparse_split(keyword_required=KeywordRequired(), 

986 sp_input=None, 

987 num_split=None, 

988 axis=None, 

989 name=None, 

990 split_dim=None): 

991 """Split a `SparseTensor` into `num_split` tensors along `axis`. 

992 

993 If the `sp_input.dense_shape[axis]` is not an integer multiple of `num_split` 

994 each slice starting from 0:`shape[axis] % num_split` gets extra one 

995 dimension. For example, if `axis = 1` and `num_split = 2` and the 

996 input is: 

997 

998 input_tensor = shape = [2, 7] 

999 [ a d e ] 

1000 [b c ] 

1001 

1002 Graphically the output tensors are: 

1003 

1004 output_tensor[0] = 

1005 [ a ] 

1006 [b c ] 

1007 

1008 output_tensor[1] = 

1009 [ d e ] 

1010 [ ] 

1011 

1012 Args: 

1013 keyword_required: Python 2 standin for * (temporary for argument reorder) 

1014 sp_input: The `SparseTensor` to split. 

1015 num_split: A Python integer. The number of ways to split. 

1016 axis: A 0-D `int32` `Tensor`. The dimension along which to split. Must be in 

1017 range [-rank, rank), where rank is the number of dimensions in the input 

1018 `SparseTensor`. 

1019 name: A name for the operation (optional). 

1020 split_dim: Deprecated old name for axis. 

1021 

1022 Returns: 

1023 `num_split` `SparseTensor` objects resulting from splitting `value`. 

1024 

1025 Raises: 

1026 TypeError: If `sp_input` is not a `SparseTensor`. 

1027 ValueError: If the deprecated `split_dim` and `axis` are both non None. 

1028 """ 

1029 if not isinstance(keyword_required, KeywordRequired): 

1030 raise ValueError("Keyword arguments are required for this function.") 

1031 if sp_input is None: 

1032 raise ValueError("sp_input is required") 

1033 if num_split is None: 

1034 raise ValueError("num_split is required") 

1035 if axis is None: 

1036 raise ValueError("axis is required") 

1037 axis = deprecation.deprecated_argument_lookup("axis", axis, "split_dim", 

1038 split_dim) 

1039 sp_input = _convert_to_sparse_tensor(sp_input) 

1040 

1041 output_inds, output_vals, output_shapes = ( 

1042 gen_sparse_ops.sparse_split( 

1043 axis, 

1044 sp_input.indices, 

1045 sp_input.values, 

1046 sp_input.dense_shape, 

1047 num_split, 

1048 name=name)) 

1049 sparse_tensors = [] 

1050 for i in range(0, num_split): 

1051 sparse_tensors.append( 

1052 sparse_tensor.SparseTensor(output_inds[i], output_vals[i], 

1053 output_shapes[i])) 

1054 return sparse_tensors 

1055 

1056 

1057@tf_export("sparse.split", v1=[]) 

1058def sparse_split_v2(sp_input=None, 

1059 num_split=None, 

1060 axis=None, 

1061 name=None): 

1062 """Split a `SparseTensor` into `num_split` tensors along `axis`. 

1063 

1064 If the `sp_input.dense_shape[axis]` is not an integer multiple of `num_split` 

1065 each slice starting from 0:`shape[axis] % num_split` gets extra one 

1066 dimension. For example: 

1067 

1068 >>> indices = [[0, 2], [0, 4], [0, 5], [1, 0], [1, 1]] 

1069 >>> values = [1, 2, 3, 4, 5] 

1070 >>> t = tf.sparse.SparseTensor(indices=indices, values=values, 

1071 ... dense_shape=[2, 7]) 

1072 >>> tf.sparse.to_dense(t) 

1073 <tf.Tensor: shape=(2, 7), dtype=int32, numpy= 

1074 array([[0, 0, 1, 0, 2, 3, 0], 

1075 [4, 5, 0, 0, 0, 0, 0]], dtype=int32)> 

1076 

1077 >>> output = tf.sparse.split(sp_input=t, num_split=2, axis=1) 

1078 >>> tf.sparse.to_dense(output[0]) 

1079 <tf.Tensor: shape=(2, 4), dtype=int32, numpy= 

1080 array([[0, 0, 1, 0], 

1081 [4, 5, 0, 0]], dtype=int32)> 

1082 >>> tf.sparse.to_dense(output[1]) 

1083 <tf.Tensor: shape=(2, 3), dtype=int32, numpy= 

1084 array([[2, 3, 0], 

1085 [0, 0, 0]], dtype=int32)> 

1086 

1087 >>> output = tf.sparse.split(sp_input=t, num_split=2, axis=0) 

1088 >>> tf.sparse.to_dense(output[0]) 

1089 <tf.Tensor: shape=(1, 7), dtype=int32, numpy=array([[0, 0, 1, 0, 2, 3, 0]], 

1090 dtype=int32)> 

1091 >>> tf.sparse.to_dense(output[1]) 

1092 <tf.Tensor: shape=(1, 7), dtype=int32, numpy=array([[4, 5, 0, 0, 0, 0, 0]], 

1093 dtype=int32)> 

1094 

1095 >>> output = tf.sparse.split(sp_input=t, num_split=2, axis=-1) 

1096 >>> tf.sparse.to_dense(output[0]) 

1097 <tf.Tensor: shape=(2, 4), dtype=int32, numpy= 

1098 array([[0, 0, 1, 0], 

1099 [4, 5, 0, 0]], dtype=int32)> 

1100 >>> tf.sparse.to_dense(output[1]) 

1101 <tf.Tensor: shape=(2, 3), dtype=int32, numpy= 

1102 array([[2, 3, 0], 

1103 [0, 0, 0]], dtype=int32)> 

1104 

1105 Args: 

1106 sp_input: The `SparseTensor` to split. 

1107 num_split: A Python integer. The number of ways to split. 

1108 axis: A 0-D `int32` `Tensor`. The dimension along which to split. Must be in 

1109 range [-rank, rank), where rank is the number of dimensions in the input 

1110 `SparseTensor`. 

1111 name: A name for the operation (optional). 

1112 

1113 Returns: 

1114 `num_split` `SparseTensor` objects resulting from splitting `value`. 

1115 

1116 Raises: 

1117 TypeError: If `sp_input` is not a `SparseTensor`. 

1118 """ 

1119 return sparse_split(sp_input=sp_input, 

1120 num_split=num_split, 

1121 axis=axis, 

1122 name=name, 

1123 split_dim=None) 

1124 

1125 

1126@tf_export("sparse.slice", v1=["sparse.slice", "sparse_slice"]) 

1127@deprecation.deprecated_endpoints("sparse_slice") 

1128def sparse_slice(sp_input, start, size, name=None): 

1129 """Slice a `SparseTensor` based on the `start` and `size`. 

1130 

1131 For example, if the input is 

1132 

1133 input_tensor = shape = [2, 7] 

1134 [ a d e ] 

1135 [b c ] 

1136 

1137 Graphically the output tensors are: 

1138 

1139 sparse.slice([0, 0], [2, 4]) = shape = [2, 4] 

1140 [ a ] 

1141 [b c ] 

1142 

1143 sparse.slice([0, 4], [2, 3]) = shape = [2, 3] 

1144 [ d e ] 

1145 [ ] 

1146 

1147 Args: 

1148 sp_input: The `SparseTensor` to split. 

1149 start: 1-D. tensor represents the start of the slice. 

1150 size: 1-D. tensor represents the size of the slice. 

1151 name: A name for the operation (optional). 

1152 

1153 Returns: 

1154 A `SparseTensor` objects resulting from splicing. 

1155 

1156 Raises: 

1157 TypeError: If `sp_input` is not a `SparseTensor`. 

1158 """ 

1159 sp_input = _convert_to_sparse_tensor(sp_input) 

1160 start = ops.convert_to_tensor(start, dtypes.int64) 

1161 size = ops.convert_to_tensor(size, dtypes.int64) 

1162 

1163 with ops.name_scope(name, "SparseSlice", [sp_input]) as name: 

1164 output_indices, output_values, output_shape = gen_sparse_ops.sparse_slice( 

1165 sp_input.indices, 

1166 sp_input.values, 

1167 sp_input.dense_shape, 

1168 start, 

1169 size, 

1170 name=name) 

1171 

1172 return sparse_tensor.SparseTensor(output_indices, output_values, 

1173 output_shape) 

1174 

1175 

1176@tf_export(v1=["sparse_to_dense"]) 

1177@dispatch.add_dispatch_support 

1178@deprecation.deprecated( 

1179 None, 

1180 "Create a `tf.sparse.SparseTensor` and use `tf.sparse.to_dense` instead.") 

1181def sparse_to_dense(sparse_indices, 

1182 output_shape, 

1183 sparse_values, 

1184 default_value=0, 

1185 validate_indices=True, 

1186 name=None): 

1187 """Converts a sparse representation into a dense tensor. 

1188 

1189 Builds an array `dense` with shape `output_shape` such that 

1190 

1191 ```python 

1192 # If sparse_indices is scalar 

1193 dense[i] = (i == sparse_indices ? sparse_values : default_value) 

1194 

1195 # If sparse_indices is a vector, then for each i 

1196 dense[sparse_indices[i]] = sparse_values[i] 

1197 

1198 # If sparse_indices is an n by d matrix, then for each i in [0, n) 

1199 dense[sparse_indices[i][0], ..., sparse_indices[i][d-1]] = sparse_values[i] 

1200 ``` 

1201 

1202 All other values in `dense` are set to `default_value`. If `sparse_values` 

1203 is a scalar, all sparse indices are set to this single value. 

1204 

1205 Indices should be sorted in lexicographic order, and indices must not 

1206 contain any repeats. If `validate_indices` is True, these properties 

1207 are checked during execution. 

1208 

1209 Args: 

1210 sparse_indices: A 0-D, 1-D, or 2-D `Tensor` of type `int32` or `int64`. 

1211 `sparse_indices[i]` contains the complete index where `sparse_values[i]` 

1212 will be placed. 

1213 output_shape: A 1-D `Tensor` of the same type as `sparse_indices`. Shape 

1214 of the dense output tensor. 

1215 sparse_values: A 0-D or 1-D `Tensor`. Values corresponding to each row of 

1216 `sparse_indices`, or a scalar value to be used for all sparse indices. 

1217 default_value: A 0-D `Tensor` of the same type as `sparse_values`. Value 

1218 to set for indices not specified in `sparse_indices`. Defaults to zero. 

1219 validate_indices: A boolean value. If True, indices are checked to make 

1220 sure they are sorted in lexicographic order and that there are no repeats. 

1221 name: A name for the operation (optional). 

1222 

1223 Returns: 

1224 Dense `Tensor` of shape `output_shape`. Has the same type as 

1225 `sparse_values`. 

1226 """ 

1227 return gen_sparse_ops.sparse_to_dense( 

1228 sparse_indices, 

1229 output_shape, 

1230 sparse_values, 

1231 default_value=default_value, 

1232 validate_indices=validate_indices, 

1233 name=name) 

1234 

1235 

1236@tf_export("sparse.reduce_max", v1=[]) 

1237def sparse_reduce_max_v2( 

1238 sp_input, axis=None, keepdims=None, output_is_sparse=False, name=None): 

1239 """Computes `tf.sparse.maximum` of elements across dimensions of a SparseTensor. 

1240 

1241 This is the reduction operation for the elementwise `tf.sparse.maximum` op. 

1242 

1243 This Op takes a SparseTensor and is the sparse counterpart to 

1244 `tf.reduce_max()`. In particular, this Op also returns a dense `Tensor` 

1245 if `output_is_sparse` is `False`, or a `SparseTensor` if `output_is_sparse` 

1246 is `True`. 

1247 

1248 Note: A gradient is not defined for this function, so it can't be used 

1249 in training models that need gradient descent. 

1250 

1251 Reduces `sp_input` along the dimensions given in `axis`. Unless 

1252 `keepdims` is true, the rank of the tensor is reduced by 1 for each entry in 

1253 `axis`. If `keepdims` is true, the reduced dimensions are retained 

1254 with length 1. 

1255 

1256 If `axis` has no entries, all dimensions are reduced, and a tensor 

1257 with a single element is returned. Additionally, the axes can be negative, 

1258 similar to the indexing rules in Python. 

1259 

1260 The values not defined in `sp_input` don't participate in the reduce max, 

1261 as opposed to be implicitly assumed 0 -- hence it can return negative values 

1262 for sparse `axis`. But, in case there are no values in 

1263 `axis`, it will reduce to 0. See second example below. 

1264 

1265 For example: 

1266 

1267 # 'x' represents [[1, ?, 2] 

1268 # [?, 3, ?]] 

1269 # where ? is implicitly-zero. 

1270 

1271 >>> x = tf.sparse.SparseTensor([[0, 0], [0, 2], [1, 1]], [1, 2, 3], [2, 3]) 

1272 >>> tf.sparse.reduce_max(x) 

1273 <tf.Tensor: shape=(), dtype=int32, numpy=3> 

1274 >>> tf.sparse.reduce_max(x, 0) 

1275 <tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 3, 2], dtype=int32)> 

1276 >>> tf.sparse.reduce_max(x, 1) 

1277 <tf.Tensor: shape=(2,), dtype=int32, numpy=array([2, 3], dtype=int32)> 

1278 >>> tf.sparse.reduce_max(x, 1, keepdims=True) 

1279 <tf.Tensor: shape=(2, 1), dtype=int32, numpy= 

1280 array([[2], 

1281 [3]], dtype=int32)> 

1282 >>> tf.sparse.reduce_max(x, [0, 1]) 

1283 <tf.Tensor: shape=(), dtype=int32, numpy=3> 

1284 

1285 # 'y' represents [[-7, ?] 

1286 # [ 4, 3] 

1287 # [ ?, ?] 

1288 

1289 >>> y = tf.sparse.SparseTensor([[0, 0,], [1, 0], [1, 1]], [-7, 4, 3], 

1290 ... [3, 2]) 

1291 >>> tf.sparse.reduce_max(y, 1) 

1292 <tf.Tensor: shape=(3,), dtype=int32, numpy=array([-7, 4, 0], dtype=int32)> 

1293 

1294 Args: 

1295 sp_input: The SparseTensor to reduce. Should have numeric type. 

1296 axis: The dimensions to reduce; list or scalar. If `None` (the 

1297 default), reduces all dimensions. 

1298 keepdims: If true, retain reduced dimensions with length 1. 

1299 output_is_sparse: If true, returns a `SparseTensor` instead of a dense 

1300 `Tensor` (the default). 

1301 name: A name for the operation (optional). 

1302 

1303 Returns: 

1304 The reduced Tensor or the reduced SparseTensor if `output_is_sparse` is 

1305 True. 

1306 """ 

1307 if keepdims is None: 

1308 keepdims = False 

1309 

1310 if output_is_sparse: 

1311 output_ind, output_val, output_shape = ( 

1312 gen_sparse_ops.sparse_reduce_max_sparse( 

1313 sp_input.indices, 

1314 sp_input.values, 

1315 sp_input.dense_shape, 

1316 math_ops._ReductionDims(sp_input, axis), 

1317 keepdims, 

1318 name=name)) 

1319 

1320 return sparse_tensor.SparseTensor(output_ind, output_val, output_shape) 

1321 

1322 return gen_sparse_ops.sparse_reduce_max( 

1323 sp_input.indices, 

1324 sp_input.values, 

1325 sp_input.dense_shape, 

1326 math_ops._ReductionDims(sp_input, axis), 

1327 keepdims, 

1328 name=name) 

1329 

1330 

1331@tf_export(v1=["sparse.reduce_max", "sparse_reduce_max"]) 

1332@deprecation.deprecated_endpoints("sparse_reduce_max") 

1333@deprecation.deprecated_args( 

1334 None, "keep_dims is deprecated, use keepdims instead", "keep_dims") 

1335@deprecation.deprecated_args( 

1336 None, "reduction_axes is deprecated, use axis instead", 

1337 "reduction_axes") 

1338def sparse_reduce_max(sp_input, axis=None, keepdims=None, 

1339 reduction_axes=None, keep_dims=None): 

1340 """Computes `tf.sparse.maximum` of elements across dimensions of a SparseTensor. 

1341 

1342 This is the reduction operation for the elementwise `tf.sparse.maximum` op. 

1343 

1344 This Op takes a SparseTensor and is the sparse counterpart to 

1345 `tf.reduce_max()`. In particular, this Op also returns a dense `Tensor` 

1346 instead of a sparse one. 

1347 

1348 Note: A gradient is not defined for this function, so it can't be used 

1349 in training models that need gradient descent. 

1350 

1351 Reduces `sp_input` along the dimensions given in `reduction_axes`. Unless 

1352 `keepdims` is true, the rank of the tensor is reduced by 1 for each entry in 

1353 `reduction_axes`. If `keepdims` is true, the reduced dimensions are retained 

1354 with length 1. 

1355 

1356 If `reduction_axes` has no entries, all dimensions are reduced, and a tensor 

1357 with a single element is returned. Additionally, the axes can be negative, 

1358 similar to the indexing rules in Python. 

1359 

1360 The values not defined in `sp_input` don't participate in the reduce max, 

1361 as opposed to be implicitly assumed 0 -- hence it can return negative values 

1362 for sparse `reduction_axes`. But, in case there are no values in 

1363 `reduction_axes`, it will reduce to 0. See second example below. 

1364 

1365 For example: 

1366 

1367 # 'x' represents [[1, ?, 2] 

1368 # [?, 3, ?]] 

1369 # where ? is implicitly-zero. 

1370 

1371 >>> x = tf.sparse.SparseTensor([[0, 0], [0, 2], [1, 1]], [1, 2, 3], [2, 3]) 

1372 >>> tf.sparse.reduce_max(x) 

1373 <tf.Tensor: shape=(), dtype=int32, numpy=3> 

1374 >>> tf.sparse.reduce_max(x, 0) 

1375 <tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 3, 2], dtype=int32)> 

1376 >>> tf.sparse.reduce_max(x, 1) 

1377 <tf.Tensor: shape=(2,), dtype=int32, numpy=array([2, 3], dtype=int32)> 

1378 >>> tf.sparse.reduce_max(x, 1, keepdims=True) 

1379 <tf.Tensor: shape=(2, 1), dtype=int32, numpy= 

1380 array([[2], 

1381 [3]], dtype=int32)> 

1382 >>> tf.sparse.reduce_max(x, [0, 1]) 

1383 <tf.Tensor: shape=(), dtype=int32, numpy=3> 

1384 

1385 # 'y' represents [[-7, ?] 

1386 # [ 4, 3] 

1387 # [ ?, ?] 

1388 

1389 >>> y = tf.sparse.SparseTensor([[0, 0,], [1, 0], [1, 1]], [-7, 4, 3], 

1390 ... [3, 2]) 

1391 >>> tf.sparse.reduce_max(y, 1) 

1392 <tf.Tensor: shape=(3,), dtype=int32, numpy=array([-7, 4, 0], dtype=int32)> 

1393 

1394 Args: 

1395 sp_input: The SparseTensor to reduce. Should have numeric type. 

1396 axis: The dimensions to reduce; list or scalar. If `None` (the 

1397 default), reduces all dimensions. 

1398 keepdims: If true, retain reduced dimensions with length 1. 

1399 reduction_axes: Deprecated name of `axis`. 

1400 keep_dims: Deprecated alias for `keepdims`. 

1401 

1402 Returns: 

1403 The reduced Tensor. 

1404 """ 

1405 keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims, 

1406 "keep_dims", keep_dims) 

1407 axis = deprecation.deprecated_argument_lookup("axis", axis, "reduction_axes", 

1408 reduction_axes) 

1409 if keepdims is None: 

1410 keepdims = False 

1411 

1412 return gen_sparse_ops.sparse_reduce_max( 

1413 sp_input.indices, sp_input.values, sp_input.dense_shape, 

1414 math_ops._ReductionDims(sp_input, axis), keepdims) 

1415 

1416 

1417@tf_export(v1=["sparse.reduce_max_sparse", "sparse_reduce_max_sparse"]) 

1418@deprecation.deprecated_endpoints("sparse_reduce_max_sparse") 

1419@deprecation.deprecated_args( 

1420 None, "keep_dims is deprecated, use keepdims instead", "keep_dims") 

1421def sparse_reduce_max_sparse(sp_input, 

1422 axis=None, 

1423 keepdims=None, 

1424 reduction_axes=None, 

1425 keep_dims=None): 

1426 """Computes the max of elements across dimensions of a SparseTensor. 

1427 

1428 This Op takes a SparseTensor and is the sparse counterpart to 

1429 `tf.reduce_max()`. In contrast to SparseReduceSum, this Op returns a 

1430 SparseTensor. 

1431 

1432 Note: A gradient is not defined for this function, so it can't be used 

1433 in training models that need gradient descent. 

1434 

1435 Reduces `sp_input` along the dimensions given in `reduction_axes`. Unless 

1436 `keepdims` is true, the rank of the tensor is reduced by 1 for each entry in 

1437 `reduction_axes`. If `keepdims` is true, the reduced dimensions are retained 

1438 with length 1. 

1439 

1440 If `reduction_axes` has no entries, all dimensions are reduced, and a tensor 

1441 with a single element is returned. Additionally, the axes can be negative, 

1442 which are interpreted according to the indexing rules in Python. 

1443 

1444 Args: 

1445 sp_input: The SparseTensor to reduce. Should have numeric type. 

1446 axis: The dimensions to reduce; list or scalar. If `None` (the 

1447 default), reduces all dimensions. 

1448 keepdims: If true, retain reduced dimensions with length 1. 

1449 reduction_axes: Deprecated name of axis. 

1450 keep_dims: Deprecated alias for `keepdims`. 

1451 

1452 Returns: 

1453 The reduced SparseTensor. 

1454 """ 

1455 keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims, 

1456 "keep_dims", keep_dims) 

1457 axis = deprecation.deprecated_argument_lookup("axis", axis, "reduction_axes", 

1458 reduction_axes) 

1459 if keepdims is None: 

1460 keepdims = False 

1461 

1462 output_ind, output_val, output_shape = ( 

1463 gen_sparse_ops.sparse_reduce_max_sparse( 

1464 sp_input.indices, sp_input.values, sp_input.dense_shape, 

1465 math_ops._ReductionDims(sp_input, axis), keepdims)) 

1466 

1467 return sparse_tensor.SparseTensor(output_ind, output_val, output_shape) 

1468 

1469 

1470@tf_export("sparse.reduce_sum", v1=[]) 

1471def sparse_reduce_sum_v2( 

1472 sp_input, axis=None, keepdims=None, output_is_sparse=False, name=None): 

1473 """Computes `tf.sparse.add` of elements across dimensions of a SparseTensor. 

1474 

1475 This is the reduction operation for the elementwise `tf.sparse.add` op. 

1476 

1477 This Op takes a SparseTensor and is the sparse counterpart to 

1478 `tf.reduce_sum()`. In particular, this Op also returns a dense `Tensor` 

1479 if `output_is_sparse` is `False`, or a `SparseTensor` if `output_is_sparse` 

1480 is `True`. 

1481 

1482 Note: if `output_is_sparse` is True, a gradient is not defined for this 

1483 function, so it can't be used in training models that need gradient descent. 

1484 

1485 Reduces `sp_input` along the dimensions given in `axis`. Unless `keepdims` is 

1486 true, the rank of the tensor is reduced by 1 for each entry in `axis`. If 

1487 `keepdims` is true, the reduced dimensions are retained with length 1. 

1488 

1489 If `axis` has no entries, all dimensions are reduced, and a tensor 

1490 with a single element is returned. Additionally, the axes can be negative, 

1491 similar to the indexing rules in Python. 

1492 

1493 For example: 

1494 

1495 # 'x' represents [[1, ?, 1] 

1496 # [?, 1, ?]] 

1497 # where ? is implicitly-zero. 

1498 

1499 >>> x = tf.sparse.SparseTensor([[0, 0], [0, 2], [1, 1]], [1, 1, 1], [2, 3]) 

1500 >>> tf.sparse.reduce_sum(x) 

1501 <tf.Tensor: shape=(), dtype=int32, numpy=3> 

1502 >>> tf.sparse.reduce_sum(x, 0) 

1503 <tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 1, 1], dtype=int32)> 

1504 >>> tf.sparse.reduce_sum(x, 1) # Can also use -1 as the axis 

1505 <tf.Tensor: shape=(2,), dtype=int32, numpy=array([2, 1], dtype=int32)> 

1506 >>> tf.sparse.reduce_sum(x, 1, keepdims=True) 

1507 <tf.Tensor: shape=(2, 1), dtype=int32, numpy= 

1508 array([[2], 

1509 [1]], dtype=int32)> 

1510 >>> tf.sparse.reduce_sum(x, [0, 1]) 

1511 <tf.Tensor: shape=(), dtype=int32, numpy=3> 

1512 

1513 Args: 

1514 sp_input: The SparseTensor to reduce. Should have numeric type. 

1515 axis: The dimensions to reduce; list or scalar. If `None` (the 

1516 default), reduces all dimensions. 

1517 keepdims: If true, retain reduced dimensions with length 1. 

1518 output_is_sparse: If true, returns a `SparseTensor` instead of a dense 

1519 `Tensor` (the default). 

1520 name: A name for the operation (optional). 

1521 

1522 Returns: 

1523 The reduced Tensor or the reduced SparseTensor if `output_is_sparse` is 

1524 True. 

1525 """ 

1526 if keepdims is None: 

1527 keepdims = False 

1528 

1529 if output_is_sparse: 

1530 output_ind, output_val, output_shape = ( 

1531 gen_sparse_ops.sparse_reduce_sum_sparse( 

1532 sp_input.indices, 

1533 sp_input.values, 

1534 sp_input.dense_shape, 

1535 math_ops._ReductionDims(sp_input, axis), 

1536 keepdims, 

1537 name=name)) 

1538 return sparse_tensor.SparseTensor(output_ind, output_val, output_shape) 

1539 

1540 return gen_sparse_ops.sparse_reduce_sum( 

1541 sp_input.indices, 

1542 sp_input.values, 

1543 sp_input.dense_shape, 

1544 math_ops._ReductionDims(sp_input, axis), 

1545 keepdims, 

1546 name=name) 

1547 

1548 

1549@tf_export(v1=["sparse.reduce_sum", "sparse_reduce_sum"]) 

1550@deprecation.deprecated_endpoints("sparse_reduce_sum") 

1551@deprecation.deprecated_args( 

1552 None, "keep_dims is deprecated, use keepdims instead", "keep_dims") 

1553@deprecation.deprecated_args( 

1554 None, "reduction_axes is deprecated, use axis instead", 

1555 "reduction_axes") 

1556def sparse_reduce_sum(sp_input, axis=None, keepdims=None, 

1557 reduction_axes=None, keep_dims=None): 

1558 """Computes `tf.sparse.add` of elements across dimensions of a SparseTensor. 

1559 

1560 This is the reduction operation for the elementwise `tf.sparse.add` op. 

1561 

1562 This Op takes a SparseTensor and is the sparse counterpart to 

1563 `tf.reduce_sum()`. In particular, this Op also returns a dense `Tensor` 

1564 instead of a sparse one. 

1565 

1566 Reduces `sp_input` along the dimensions given in `reduction_axes`. Unless 

1567 `keepdims` is true, the rank of the tensor is reduced by 1 for each entry in 

1568 `reduction_axes`. If `keepdims` is true, the reduced dimensions are retained 

1569 with length 1. 

1570 

1571 If `reduction_axes` has no entries, all dimensions are reduced, and a tensor 

1572 with a single element is returned. Additionally, the axes can be negative, 

1573 similar to the indexing rules in Python. 

1574 

1575 For example: 

1576 

1577 # 'x' represents [[1, ?, 1] 

1578 # [?, 1, ?]] 

1579 # where ? is implicitly-zero. 

1580 

1581 >>> x = tf.sparse.SparseTensor([[0, 0], [0, 2], [1, 1]], [1, 1, 1], [2, 3]) 

1582 >>> tf.sparse.reduce_sum(x) 

1583 <tf.Tensor: shape=(), dtype=int32, numpy=3> 

1584 >>> tf.sparse.reduce_sum(x, 0) 

1585 <tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 1, 1], dtype=int32)> 

1586 >>> tf.sparse.reduce_sum(x, 1) # Can also use -1 as the axis 

1587 <tf.Tensor: shape=(2,), dtype=int32, numpy=array([2, 1], dtype=int32)> 

1588 >>> tf.sparse.reduce_sum(x, 1, keepdims=True) 

1589 <tf.Tensor: shape=(2, 1), dtype=int32, numpy= 

1590 array([[2], 

1591 [1]], dtype=int32)> 

1592 >>> tf.sparse.reduce_sum(x, [0, 1]) 

1593 <tf.Tensor: shape=(), dtype=int32, numpy=3> 

1594 

1595 Args: 

1596 sp_input: The SparseTensor to reduce. Should have numeric type. 

1597 axis: The dimensions to reduce; list or scalar. If `None` (the 

1598 default), reduces all dimensions. 

1599 keepdims: If true, retain reduced dimensions with length 1. 

1600 reduction_axes: Deprecated name of `axis`. 

1601 keep_dims: Deprecated alias for `keepdims`. 

1602 

1603 Returns: 

1604 The reduced Tensor. 

1605 """ 

1606 keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims, 

1607 "keep_dims", keep_dims) 

1608 axis = deprecation.deprecated_argument_lookup("axis", axis, "reduction_axes", 

1609 reduction_axes) 

1610 if keepdims is None: 

1611 keepdims = False 

1612 

1613 return gen_sparse_ops.sparse_reduce_sum( 

1614 sp_input.indices, sp_input.values, sp_input.dense_shape, 

1615 math_ops._ReductionDims(sp_input, axis), keepdims) 

1616 

1617 

1618@tf_export(v1=["sparse.reduce_sum_sparse", "sparse_reduce_sum_sparse"]) 

1619@deprecation.deprecated_endpoints("sparse_reduce_sum_sparse") 

1620@deprecation.deprecated_args( 

1621 None, "keep_dims is deprecated, use keepdims instead", "keep_dims") 

1622def sparse_reduce_sum_sparse(sp_input, 

1623 axis=None, 

1624 keepdims=None, 

1625 reduction_axes=None, 

1626 keep_dims=None): 

1627 """Computes the sum of elements across dimensions of a SparseTensor. 

1628 

1629 This Op takes a SparseTensor and is the sparse counterpart to 

1630 `tf.reduce_sum()`. In contrast to SparseReduceSum, this Op returns a 

1631 SparseTensor. 

1632 

1633 Note: A gradient is not defined for this function, so it can't be used 

1634 in training models that need gradient descent. 

1635 

1636 Reduces `sp_input` along the dimensions given in `reduction_axes`. Unless 

1637 `keepdims` is true, the rank of the tensor is reduced by 1 for each entry in 

1638 `reduction_axes`. If `keepdims` is true, the reduced dimensions are retained 

1639 with length 1. 

1640 

1641 If `reduction_axes` has no entries, all dimensions are reduced, and a tensor 

1642 with a single element is returned. Additionally, the axes can be negative, 

1643 which are interpreted according to the indexing rules in Python. 

1644 

1645 Args: 

1646 sp_input: The SparseTensor to reduce. Should have numeric type. 

1647 axis: The dimensions to reduce; list or scalar. If `None` (the 

1648 default), reduces all dimensions. 

1649 keepdims: If true, retain reduced dimensions with length 1. 

1650 reduction_axes: Deprecated name of axis. 

1651 keep_dims: Deprecated alias for `keepdims`. 

1652 

1653 Returns: 

1654 The reduced SparseTensor. 

1655 """ 

1656 keepdims = deprecation.deprecated_argument_lookup("keepdims", keepdims, 

1657 "keep_dims", keep_dims) 

1658 axis = deprecation.deprecated_argument_lookup("axis", axis, "reduction_axes", 

1659 reduction_axes) 

1660 if keepdims is None: 

1661 keepdims = False 

1662 

1663 output_ind, output_val, output_shape = ( 

1664 gen_sparse_ops.sparse_reduce_sum_sparse( 

1665 sp_input.indices, sp_input.values, sp_input.dense_shape, 

1666 math_ops._ReductionDims(sp_input, axis), keepdims)) 

1667 

1668 return sparse_tensor.SparseTensor(output_ind, output_val, output_shape) 

1669 

1670 

1671@tf_export("sparse.to_dense", v1=["sparse.to_dense", "sparse_tensor_to_dense"]) 

1672@deprecation.deprecated_endpoints("sparse_tensor_to_dense") 

1673def sparse_tensor_to_dense(sp_input, 

1674 default_value=None, 

1675 validate_indices=True, 

1676 name=None): 

1677 """Converts a `SparseTensor` into a dense tensor. 

1678 

1679 For this sparse tensor with three non-empty values: 

1680 

1681 >>> sp_input = tf.sparse.SparseTensor( 

1682 ... dense_shape=[3, 5], 

1683 ... values=[7, 8, 9], 

1684 ... indices =[[0, 1], 

1685 ... [0, 3], 

1686 ... [2, 0]]) 

1687 

1688 The output will be a dense `[3, 5]` tensor with values: 

1689 

1690 >>> tf.sparse.to_dense(sp_input).numpy() 

1691 array([[0, 7, 0, 8, 0], 

1692 [0, 0, 0, 0, 0], 

1693 [9, 0, 0, 0, 0]], dtype=int32) 

1694 

1695 Note: Indices must be without repeats. This is only tested if 

1696 `validate_indices` is `True`. 

1697 

1698 Args: 

1699 sp_input: The input `SparseTensor`. 

1700 default_value: Scalar value to set for indices not specified in 

1701 `sp_input`. Defaults to zero. 

1702 validate_indices: A boolean value. If `True`, indices are checked to make 

1703 sure they are sorted in lexicographic order and that there are no repeats. 

1704 name: A name prefix for the returned tensors (optional). 

1705 

1706 Returns: 

1707 A dense tensor with shape `sp_input.dense_shape` and values specified by 

1708 the non-empty values in `sp_input`. Indices not in `sp_input` are assigned 

1709 `default_value`. 

1710 

1711 Raises: 

1712 TypeError: If `sp_input` is not a `SparseTensor`. 

1713 """ 

1714 sp_input = _convert_to_sparse_tensor(sp_input) 

1715 if default_value is None: 

1716 default_value = array_ops.zeros([], dtype=sp_input.dtype) 

1717 

1718 return gen_sparse_ops.sparse_to_dense( 

1719 sp_input.indices, 

1720 sp_input.dense_shape, 

1721 sp_input.values, 

1722 default_value=default_value, 

1723 validate_indices=validate_indices, 

1724 name=name) 

1725 

1726 

1727@tf_export( 

1728 "sparse.to_indicator", v1=["sparse.to_indicator", "sparse_to_indicator"]) 

1729@deprecation.deprecated_endpoints("sparse_to_indicator") 

1730def sparse_to_indicator(sp_input, vocab_size, name=None): 

1731 """Converts a `SparseTensor` of ids into a dense bool indicator tensor. 

1732 

1733 The last dimension of `sp_input.indices` is discarded and replaced with 

1734 the values of `sp_input`. If `sp_input.dense_shape = [D0, D1, ..., Dn, K]`, 

1735 then `output.shape = [D0, D1, ..., Dn, vocab_size]`, where 

1736 

1737 output[d_0, d_1, ..., d_n, sp_input[d_0, d_1, ..., d_n, k]] = True 

1738 

1739 and False elsewhere in `output`. 

1740 

1741 For example, if `sp_input.dense_shape = [2, 3, 4]` with non-empty values: 

1742 

1743 [0, 0, 0]: 0 

1744 [0, 1, 0]: 10 

1745 [1, 0, 3]: 103 

1746 [1, 1, 1]: 150 

1747 [1, 1, 2]: 149 

1748 [1, 1, 3]: 150 

1749 [1, 2, 1]: 121 

1750 

1751 and `vocab_size = 200`, then the output will be a `[2, 3, 200]` dense bool 

1752 tensor with False everywhere except at positions 

1753 

1754 (0, 0, 0), (0, 1, 10), (1, 0, 103), (1, 1, 149), (1, 1, 150), 

1755 (1, 2, 121). 

1756 

1757 Note that repeats are allowed in the input SparseTensor. 

1758 This op is useful for converting `SparseTensor`s into dense formats for 

1759 compatibility with ops that expect dense tensors. 

1760 

1761 The input `SparseTensor` must be in row-major order. 

1762 

1763 Args: 

1764 sp_input: A `SparseTensor` with `values` property of type `int32` or 

1765 `int64`. 

1766 vocab_size: A scalar int64 Tensor (or Python int) containing the new size 

1767 of the last dimension, `all(0 <= sp_input.values < vocab_size)`. 

1768 name: A name prefix for the returned tensors (optional) 

1769 

1770 Returns: 

1771 A dense bool indicator tensor representing the indices with specified value. 

1772 

1773 Raises: 

1774 TypeError: If `sp_input` is not a `SparseTensor`. 

1775 """ 

1776 sp_input = _convert_to_sparse_tensor(sp_input) 

1777 

1778 with ops.name_scope(name, "SparseToIndicator", [sp_input]) as name: 

1779 num_entries = array_ops.shape(sp_input.indices)[0] 

1780 new_values = array_ops.fill(array_ops.expand_dims(num_entries, 0), True) 

1781 sp_values = sparse_tensor.SparseTensor(sp_input.indices, new_values, 

1782 sp_input.dense_shape) 

1783 

1784 sp_new = sparse_merge_impl(sp_input, sp_values, vocab_size, name) 

1785 

1786 # validate_indices may be False because we allow duplicates in new_indices: 

1787 # repeated indices are allowed when creating an indicator matrix. 

1788 return sparse_tensor_to_dense( 

1789 sp_new, default_value=False, validate_indices=False, name=name) 

1790 

1791 

1792@tf_export(v1=["sparse.merge", "sparse_merge"]) 

1793@deprecation.deprecated(None, "No similar op available at this time.") 

1794def sparse_merge(sp_ids, sp_values, vocab_size, name=None, 

1795 already_sorted=False): 

1796 """Combines a batch of feature ids and values into a single `SparseTensor`. 

1797 

1798 The most common use case for this function occurs when feature ids and 

1799 their corresponding values are stored in `Example` protos on disk. 

1800 `parse_example` will return a batch of ids and a batch of values, and this 

1801 function joins them into a single logical `SparseTensor` for use in 

1802 functions such as `sparse_tensor_dense_matmul`, `sparse_to_dense`, etc. 

1803 

1804 The `SparseTensor` returned by this function has the following properties: 

1805 

1806 - `indices` is equivalent to `sp_ids.indices` with the last 

1807 dimension discarded and replaced with `sp_ids.values`. 

1808 - `values` is simply `sp_values.values`. 

1809 - If `sp_ids.dense_shape = [D0, D1, ..., Dn, K]`, then 

1810 `output.shape = [D0, D1, ..., Dn, vocab_size]`. 

1811 

1812 For example, consider the following feature vectors: 

1813 

1814 ```python 

1815 vector1 = [-3, 0, 0, 0, 0, 0] 

1816 vector2 = [ 0, 1, 0, 4, 1, 0] 

1817 vector3 = [ 5, 0, 0, 9, 0, 0] 

1818 ``` 

1819 

1820 These might be stored sparsely in the following Example protos by storing 

1821 only the feature ids (column number if the vectors are treated as a matrix) 

1822 of the non-zero elements and the corresponding values: 

1823 

1824 ```python 

1825 examples = [Example(features={ 

1826 "ids": Feature(int64_list=Int64List(value=[0])), 

1827 "values": Feature(float_list=FloatList(value=[-3]))}), 

1828 Example(features={ 

1829 "ids": Feature(int64_list=Int64List(value=[1, 4, 3])), 

1830 "values": Feature(float_list=FloatList(value=[1, 1, 4]))}), 

1831 Example(features={ 

1832 "ids": Feature(int64_list=Int64List(value=[0, 3])), 

1833 "values": Feature(float_list=FloatList(value=[5, 9]))})] 

1834 ``` 

1835 

1836 The result of calling parse_example on these examples will produce a 

1837 dictionary with entries for "ids" and "values". Passing those two objects 

1838 to this function along with vocab_size=6, will produce a `SparseTensor` that 

1839 sparsely represents all three instances. Namely, the `indices` property will 

1840 contain the coordinates of the non-zero entries in the feature matrix (the 

1841 first dimension is the row number in the matrix, i.e., the index within the 

1842 batch, and the second dimension is the column number, i.e., the feature id); 

1843 `values` will contain the actual values. `shape` will be the shape of the 

1844 original matrix, i.e., (3, 6). For our example above, the output will be 

1845 equal to: 

1846 

1847 ```python 

1848 SparseTensor(indices=[[0, 0], [1, 1], [1, 3], [1, 4], [2, 0], [2, 3]], 

1849 values=[-3, 1, 4, 1, 5, 9], 

1850 dense_shape=[3, 6]) 

1851 ``` 

1852 

1853 This method generalizes to higher-dimensions by simply providing a list for 

1854 both the sp_ids as well as the vocab_size. 

1855 In this case the resulting `SparseTensor` has the following properties: 

1856 - `indices` is equivalent to `sp_ids[0].indices` with the last 

1857 dimension discarded and concatenated with 

1858 `sp_ids[0].values, sp_ids[1].values, ...`. 

1859 - `values` is simply `sp_values.values`. 

1860 - If `sp_ids.dense_shape = [D0, D1, ..., Dn, K]`, then 

1861 `output.shape = [D0, D1, ..., Dn] + vocab_size`. 

1862 

1863 Args: 

1864 sp_ids: A single `SparseTensor` with `values` property of type `int32` 

1865 or `int64` or a Python list of such `SparseTensor`s or a list thereof. 

1866 sp_values: A `SparseTensor` of any type. 

1867 vocab_size: A scalar `int64` Tensor (or Python int) containing the new size 

1868 of the last dimension, `all(0 <= sp_ids.values < vocab_size)`. 

1869 Or a list thereof with `all(0 <= sp_ids[i].values < vocab_size[i])` for 

1870 all `i`. 

1871 name: A name prefix for the returned tensors (optional) 

1872 already_sorted: A boolean to specify whether the per-batch values in 

1873 `sp_values` are already sorted. If so skip sorting, False by default 

1874 (optional). 

1875 

1876 Returns: 

1877 A `SparseTensor` compactly representing a batch of feature ids and values, 

1878 useful for passing to functions that expect such a `SparseTensor`. 

1879 

1880 Raises: 

1881 TypeError: If `sp_values` is not a `SparseTensor`. Or if `sp_ids` is neither 

1882 a `SparseTensor` nor a list thereof. Or if `vocab_size` is not a 

1883 `Tensor` or a Python int and `sp_ids` is a `SparseTensor`. Or if 

1884 `vocab_size` is not a or list thereof and `sp_ids` is a list. 

1885 ValueError: If `sp_ids` and `vocab_size` are lists of different lengths. 

1886 """ 

1887 return sparse_merge_impl(sp_ids, sp_values, vocab_size, name, already_sorted) 

1888 

1889 

1890def sparse_merge_impl(sp_ids, 

1891 sp_values, 

1892 vocab_size, 

1893 name=None, 

1894 already_sorted=False): 

1895 """Internal implementation for sparse_merge to avoid deprecation warnings.""" 

1896 if isinstance(sp_ids, sparse_tensor.SparseTensorValue) or isinstance( 

1897 sp_ids, sparse_tensor.SparseTensor): 

1898 sp_ids = [sp_ids] 

1899 if not (isinstance(vocab_size, ops.Tensor) or 

1900 isinstance(vocab_size, numbers.Integral)): 

1901 raise TypeError("vocab_size has to be a Tensor or Python int. Found %s" % 

1902 type(vocab_size)) 

1903 vocab_size = [vocab_size] 

1904 else: 

1905 if not isinstance(sp_ids, collections_abc.Iterable): 

1906 raise TypeError("sp_ids has to be a SparseTensor or list thereof. " 

1907 "Found %s" % type(sp_ids)) 

1908 if not isinstance(vocab_size, collections_abc.Iterable): 

1909 raise TypeError("vocab_size has to be a list of Tensors or Python ints. " 

1910 "Found %s" % type(vocab_size)) 

1911 for dim in vocab_size: 

1912 if not (isinstance(dim, ops.Tensor) or isinstance(dim, numbers.Integral)): 

1913 raise TypeError( 

1914 "vocab_size has to be a list of Tensors or Python ints. Found %s" % 

1915 type(dim)) 

1916 if len(sp_ids) != len(vocab_size): 

1917 raise ValueError("sp_ids and vocab_size have to have equal lengths.") 

1918 

1919 with ops.name_scope(name, "SparseMerge", [sp_ids, sp_values]): 

1920 sp_ids = [_convert_to_sparse_tensor(sp_ids_dim) for sp_ids_dim in sp_ids] 

1921 sp_values = _convert_to_sparse_tensor(sp_values) 

1922 ids = [] 

1923 for sp_ids_dim in sp_ids: 

1924 ids_dim = sp_ids_dim.values 

1925 if sp_ids_dim.dtype != dtypes.int64: 

1926 ids_dim = math_ops.cast(ids_dim, dtypes.int64) 

1927 ids += [array_ops.expand_dims(ids_dim, axis=1)] 

1928 

1929 vocab_size = [math_ops.cast(x, dtypes.int64) for x in vocab_size] 

1930 

1931 # Slice off the last dimension of indices, then tack on the ids 

1932 indices_columns_to_preserve = sp_ids[0].indices[:, :-1] 

1933 new_indices = array_ops.concat([indices_columns_to_preserve] + ids, 1) 

1934 

1935 new_values = sp_values.values 

1936 new_shape = array_ops.concat([sp_ids[0].dense_shape[:-1], vocab_size], 0) 

1937 

1938 result = sparse_tensor.SparseTensor(new_indices, new_values, new_shape) 

1939 if already_sorted: 

1940 return result 

1941 sorted_result = sparse_reorder(result) 

1942 return sparse_tensor.SparseTensor( 

1943 sorted_result.indices, sorted_result.values, new_shape) 

1944 

1945 

1946@tf_export("sparse.retain", v1=["sparse.retain", "sparse_retain"]) 

1947@deprecation.deprecated_endpoints("sparse_retain") 

1948def sparse_retain(sp_input, to_retain): 

1949 """Retains specified non-empty values within a `SparseTensor`. 

1950 

1951 For example, if `sp_input` has shape `[4, 5]` and 4 non-empty string values: 

1952 

1953 [0, 1]: a 

1954 [0, 3]: b 

1955 [2, 0]: c 

1956 [3, 1]: d 

1957 

1958 and `to_retain = [True, False, False, True]`, then the output will 

1959 be a `SparseTensor` of shape `[4, 5]` with 2 non-empty values: 

1960 

1961 [0, 1]: a 

1962 [3, 1]: d 

1963 

1964 Args: 

1965 sp_input: The input `SparseTensor` with `N` non-empty elements. 

1966 to_retain: A bool vector of length `N` with `M` true values. 

1967 

1968 Returns: 

1969 A `SparseTensor` with the same shape as the input and `M` non-empty 

1970 elements corresponding to the true positions in `to_retain`. 

1971 

1972 Raises: 

1973 TypeError: If `sp_input` is not a `SparseTensor`. 

1974 """ 

1975 sp_input = _convert_to_sparse_tensor(sp_input) 

1976 

1977 to_retain = ops.convert_to_tensor(to_retain) 

1978 

1979 # Shape checking, if shape is known at graph construction time 

1980 retain_shape = to_retain.get_shape() 

1981 retain_shape.assert_has_rank(1) 

1982 if sp_input.values.get_shape().dims is not None: 

1983 sp_input.values.get_shape().dims[0].assert_is_compatible_with( 

1984 tensor_shape.dimension_at_index(retain_shape, 0)) 

1985 

1986 where_true = array_ops.reshape(array_ops.where_v2(to_retain), [-1]) 

1987 new_indices = array_ops.gather(sp_input.indices, where_true) 

1988 new_values = array_ops.gather(sp_input.values, where_true) 

1989 return sparse_tensor.SparseTensor(new_indices, new_values, 

1990 array_ops.identity(sp_input.dense_shape)) 

1991 

1992 

1993@tf_export( 

1994 "sparse.reset_shape", v1=["sparse.reset_shape", "sparse_reset_shape"]) 

1995@deprecation.deprecated_endpoints("sparse_reset_shape") 

1996def sparse_reset_shape(sp_input, new_shape=None): 

1997 """Resets the shape of a `SparseTensor` with indices and values unchanged. 

1998 

1999 If `new_shape` is None, returns a copy of `sp_input` with its shape reset 

2000 to the tight bounding box of `sp_input`. This will be a shape consisting of 

2001 all zeros if sp_input has no values. 

2002 

2003 If `new_shape` is provided, then it must be larger or equal in all dimensions 

2004 compared to the shape of `sp_input`. When this condition is met, the returned 

2005 SparseTensor will have its shape reset to `new_shape` and its indices and 

2006 values unchanged from that of `sp_input.` 

2007 

2008 For example: 

2009 

2010 Consider a `sp_input` with shape [2, 3, 5]: 

2011 

2012 [0, 0, 1]: a 

2013 [0, 1, 0]: b 

2014 [0, 2, 2]: c 

2015 [1, 0, 3]: d 

2016 

2017 - It is an error to set `new_shape` as [3, 7] since this represents a 

2018 rank-2 tensor while `sp_input` is rank-3. This is either a ValueError 

2019 during graph construction (if both shapes are known) or an OpError during 

2020 run time. 

2021 

2022 - Setting `new_shape` as [2, 3, 6] will be fine as this shape is larger or 

2023 equal in every dimension compared to the original shape [2, 3, 5]. 

2024 

2025 - On the other hand, setting new_shape as [2, 3, 4] is also an error: The 

2026 third dimension is smaller than the original shape [2, 3, 5] (and an 

2027 `InvalidArgumentError` will be raised). 

2028 

2029 - If `new_shape` is None, the returned SparseTensor will have a shape 

2030 [2, 3, 4], which is the tight bounding box of `sp_input`. 

2031 

2032 Args: 

2033 sp_input: The input `SparseTensor`. 

2034 new_shape: None or a vector representing the new shape for the returned 

2035 `SparseTensor`. 

2036 

2037 Returns: 

2038 A `SparseTensor` indices and values unchanged from `sp_input`. Its shape is 

2039 `new_shape` if that is set. Otherwise it is the tight bounding box of 

2040 `sp_input` 

2041 

2042 Raises: 

2043 TypeError: If `sp_input` is not a `SparseTensor`. 

2044 ValueError: If `new_shape` represents a tensor with a different rank from 

2045 that of `sp_input` (if shapes are known when graph is constructed). 

2046 ValueError: If `new_shape` is determined during graph build to have 

2047 dimension sizes that are too small. 

2048 OpError: 

2049 - If `new_shape` has dimension sizes that are too small. 

2050 - If shapes are not known during graph construction time, and during run 

2051 time it is found out that the ranks do not match. 

2052 """ 

2053 sp_input = _convert_to_sparse_tensor(sp_input) 

2054 

2055 in_indices = array_ops.identity(sp_input.indices) 

2056 in_values = array_ops.identity(sp_input.values) 

2057 in_shape = array_ops.identity(sp_input.dense_shape) 

2058 

2059 if new_shape is None: 

2060 dim_low_bound = math_ops.reduce_max(in_indices, axis=0) 

2061 output_shape_tensor = math_ops.maximum( 

2062 array_ops.constant(0, dtype=dtypes.int64), 

2063 math_ops.add(dim_low_bound, array_ops.ones_like(in_shape))) 

2064 else: 

2065 output_shape_tensor = ops.convert_to_tensor(new_shape) 

2066 output_shape_tensor.get_shape().assert_has_rank(1) 

2067 output_shape_tensor = math_ops.cast(output_shape_tensor, dtypes.int64) 

2068 # For cases when shape is known during graph construction, this catches the 

2069 # error before the sparse_tensor.SparseTensor catches it. 

2070 if output_shape_tensor.get_shape().rank is not None: 

2071 output_shape_tensor.get_shape().dims[0].assert_is_compatible_with( 

2072 in_shape.get_shape().dims[0]) 

2073 

2074 output_shape_tensor_const = tensor_util.constant_value(output_shape_tensor) 

2075 # For cases where all shapes are known during graph construction 

2076 if (output_shape_tensor_const is not None and 

2077 sp_input.get_shape().is_fully_defined()): 

2078 in_shape_const = np.array(sp_input.get_shape().as_list()) 

2079 if not np.all(in_shape_const <= output_shape_tensor_const): 

2080 raise ValueError( 

2081 "Requested new_shape should have dimension sizes >= sp_input.shape." 

2082 " Found new_shape (%s), sp_input.shape (%s)." % 

2083 (in_shape_const, output_shape_tensor_const)) 

2084 output_shape_tensor = output_shape_tensor_const 

2085 else: 

2086 # For cases where shape is not known during graph construction. 

2087 output_shape_tensor = control_flow_ops.with_dependencies([ 

2088 check_ops.assert_equal( 

2089 array_ops.shape(in_shape), array_ops.shape(output_shape_tensor)) 

2090 ], output_shape_tensor) 

2091 output_shape_tensor = control_flow_ops.with_dependencies( 

2092 [check_ops.assert_less_equal(in_shape, output_shape_tensor)], 

2093 output_shape_tensor) 

2094 

2095 return sparse_tensor.SparseTensor(in_indices, in_values, output_shape_tensor) 

2096 

2097 

2098@tf_export( 

2099 "sparse.fill_empty_rows", 

2100 v1=["sparse.fill_empty_rows", "sparse_fill_empty_rows"]) 

2101@deprecation.deprecated_endpoints("sparse_fill_empty_rows") 

2102def sparse_fill_empty_rows(sp_input, default_value, name=None): 

2103 """Fills empty rows in the input 2-D `SparseTensor` with a default value. 

2104 

2105 This op adds entries with the specified `default_value` at index 

2106 `[row, 0]` for any row in the input that does not already have a value. 

2107 

2108 For example, suppose `sp_input` has shape `[5, 6]` and non-empty values: 

2109 

2110 [0, 1]: a 

2111 [0, 3]: b 

2112 [2, 0]: c 

2113 [3, 1]: d 

2114 

2115 Rows 1 and 4 are empty, so the output will be of shape `[5, 6]` with values: 

2116 

2117 [0, 1]: a 

2118 [0, 3]: b 

2119 [1, 0]: default_value 

2120 [2, 0]: c 

2121 [3, 1]: d 

2122 [4, 0]: default_value 

2123 

2124 Note that the input may have empty columns at the end, with no effect on 

2125 this op. 

2126 

2127 The output `SparseTensor` will be in row-major order and will have the 

2128 same shape as the input. 

2129 

2130 This op also returns an indicator vector such that 

2131 

2132 empty_row_indicator[i] = True iff row i was an empty row. 

2133 

2134 Args: 

2135 sp_input: A `SparseTensor` with shape `[N, M]`. 

2136 default_value: The value to fill for empty rows, with the same type as 

2137 `sp_input.` 

2138 name: A name prefix for the returned tensors (optional) 

2139 

2140 Returns: 

2141 sp_ordered_output: A `SparseTensor` with shape `[N, M]`, and with all empty 

2142 rows filled in with `default_value`. 

2143 empty_row_indicator: A bool vector of length `N` indicating whether each 

2144 input row was empty. 

2145 

2146 Raises: 

2147 TypeError: If `sp_input` is not a `SparseTensor`. 

2148 """ 

2149 sp_input = _convert_to_sparse_tensor(sp_input) 

2150 with ops.name_scope(name, "SparseFillEmptyRows", [sp_input]): 

2151 default_value = ops.convert_to_tensor( 

2152 default_value, dtype=sp_input.values.dtype) 

2153 (output_indices, output_values, empty_row_indicator, 

2154 unused_reverse_index_map) = gen_sparse_ops.sparse_fill_empty_rows( 

2155 indices=sp_input.indices, 

2156 values=sp_input.values, 

2157 dense_shape=sp_input.dense_shape, 

2158 default_value=default_value) 

2159 return (sparse_tensor.SparseTensor( 

2160 indices=output_indices, 

2161 values=output_values, 

2162 dense_shape=sp_input.dense_shape), empty_row_indicator) 

2163 

2164 

2165@tf_export(v1=["io.serialize_sparse", "serialize_sparse"]) 

2166@dispatch.add_dispatch_support 

2167@deprecation.deprecated_endpoints("serialize_sparse") 

2168def serialize_sparse(sp_input, name=None, out_type=dtypes.string): 

2169 """Serialize a `SparseTensor` into a 3-vector (1-D `Tensor`) object. 

2170 

2171 Args: 

2172 sp_input: The input `SparseTensor`. 

2173 name: A name prefix for the returned tensors (optional). 

2174 out_type: The `dtype` to use for serialization. 

2175 

2176 Returns: 

2177 A 3-vector (1-D `Tensor`), with each column representing the serialized 

2178 `SparseTensor`'s indices, values, and shape (respectively). 

2179 

2180 Raises: 

2181 TypeError: If `sp_input` is not a `SparseTensor`. 

2182 """ 

2183 return serialize_sparse_v2(sp_input, out_type, name) 

2184 

2185 

2186@tf_export("io.serialize_sparse", v1=[]) 

2187@dispatch.add_dispatch_support 

2188def serialize_sparse_v2(sp_input, out_type=dtypes.string, name=None): 

2189 """Serialize a `SparseTensor` into a 3-vector (1-D `Tensor`) object. 

2190 

2191 Args: 

2192 sp_input: The input `SparseTensor`. 

2193 out_type: The `dtype` to use for serialization. 

2194 name: A name prefix for the returned tensors (optional). 

2195 

2196 Returns: 

2197 A 3-vector (1-D `Tensor`), with each column representing the serialized 

2198 `SparseTensor`'s indices, values, and shape (respectively). 

2199 

2200 Raises: 

2201 TypeError: If `sp_input` is not a `SparseTensor`. 

2202 """ 

2203 sp_input = _convert_to_sparse_tensor(sp_input) 

2204 

2205 return gen_sparse_ops.serialize_sparse( 

2206 sp_input.indices, 

2207 sp_input.values, 

2208 sp_input.dense_shape, 

2209 name=name, 

2210 out_type=out_type) 

2211 

2212 

2213@tf_export(v1=["io.serialize_many_sparse", "serialize_many_sparse"]) 

2214@dispatch.add_dispatch_support 

2215@deprecation.deprecated_endpoints("serialize_many_sparse") 

2216def serialize_many_sparse(sp_input, name=None, out_type=dtypes.string): 

2217 """Serialize `N`-minibatch `SparseTensor` into an `[N, 3]` `Tensor`. 

2218 

2219 The `SparseTensor` must have rank `R` greater than 1, and the first dimension 

2220 is treated as the minibatch dimension. Elements of the `SparseTensor` 

2221 must be sorted in increasing order of this first dimension. The serialized 

2222 `SparseTensor` objects going into each row of the output `Tensor` will have 

2223 rank `R-1`. 

2224 

2225 The minibatch size `N` is extracted from `sparse_shape[0]`. 

2226 

2227 Args: 

2228 sp_input: The input rank `R` `SparseTensor`. 

2229 name: A name prefix for the returned tensors (optional). 

2230 out_type: The `dtype` to use for serialization. 

2231 

2232 Returns: 

2233 A matrix (2-D `Tensor`) with `N` rows and `3` columns. Each column 

2234 represents serialized `SparseTensor`'s indices, values, and shape 

2235 (respectively). 

2236 

2237 Raises: 

2238 TypeError: If `sp_input` is not a `SparseTensor`. 

2239 """ 

2240 return serialize_many_sparse_v2(sp_input, out_type, name) 

2241 

2242 

2243@tf_export("io.serialize_many_sparse", v1=[]) 

2244@dispatch.add_dispatch_support 

2245def serialize_many_sparse_v2(sp_input, out_type=dtypes.string, name=None): 

2246 """Serialize `N`-minibatch `SparseTensor` into an `[N, 3]` `Tensor`. 

2247 

2248 The `SparseTensor` must have rank `R` greater than 1, and the first dimension 

2249 is treated as the minibatch dimension. Elements of the `SparseTensor` 

2250 must be sorted in increasing order of this first dimension. The serialized 

2251 `SparseTensor` objects going into each row of the output `Tensor` will have 

2252 rank `R-1`. 

2253 

2254 The minibatch size `N` is extracted from `sparse_shape[0]`. 

2255 

2256 Args: 

2257 sp_input: The input rank `R` `SparseTensor`. 

2258 out_type: The `dtype` to use for serialization. 

2259 name: A name prefix for the returned tensors (optional). 

2260 

2261 Returns: 

2262 A matrix (2-D `Tensor`) with `N` rows and `3` columns. Each column 

2263 represents serialized `SparseTensor`'s indices, values, and shape 

2264 (respectively). 

2265 

2266 Raises: 

2267 TypeError: If `sp_input` is not a `SparseTensor`. 

2268 """ 

2269 sp_input = _convert_to_sparse_tensor(sp_input) 

2270 

2271 return gen_sparse_ops.serialize_many_sparse( 

2272 sp_input.indices, 

2273 sp_input.values, 

2274 sp_input.dense_shape, 

2275 name=name, 

2276 out_type=out_type) 

2277 

2278 

2279def deserialize_sparse(serialized_sparse, dtype, rank=None, name=None): 

2280 """Deserialize `SparseTensor` objects. 

2281 

2282 The input `serialized_sparse` must have the shape `[?, ?, ..., ?, 3]` where 

2283 the last dimension stores serialized `SparseTensor` objects and the other N 

2284 dimensions (N >= 0) correspond to a batch. The ranks of the original 

2285 `SparseTensor` objects must all match. When the final `SparseTensor` is 

2286 created, its rank is the rank of the incoming `SparseTensor` objects plus N; 

2287 the sparse tensors have been concatenated along new dimensions, one for each 

2288 batch. 

2289 

2290 The output `SparseTensor` object's shape values for the original dimensions 

2291 are the max across the input `SparseTensor` objects' shape values for the 

2292 corresponding dimensions. The new dimensions match the size of the batch. 

2293 

2294 The input `SparseTensor` objects' indices are assumed ordered in 

2295 standard lexicographic order. If this is not the case, after this 

2296 step run `SparseReorder` to restore index ordering. 

2297 

2298 For example, if the serialized input is a `[2 x 3]` matrix representing two 

2299 original `SparseTensor` objects: 

2300 

2301 index = [ 0] 

2302 [10] 

2303 [20] 

2304 values = [1, 2, 3] 

2305 shape = [50] 

2306 

2307 and 

2308 

2309 index = [ 2] 

2310 [10] 

2311 values = [4, 5] 

2312 shape = [30] 

2313 

2314 then the final deserialized `SparseTensor` will be: 

2315 

2316 index = [0 0] 

2317 [0 10] 

2318 [0 20] 

2319 [1 2] 

2320 [1 10] 

2321 values = [1, 2, 3, 4, 5] 

2322 shape = [2 50] 

2323 

2324 Args: 

2325 serialized_sparse: The serialized `SparseTensor` objects. 

2326 The last dimension must have 3 columns. 

2327 dtype: The `dtype` of the serialized `SparseTensor` objects. 

2328 rank: (optional) Python int, the rank of the `SparseTensor` objects. 

2329 name: A name prefix for the returned tensors (optional). 

2330 

2331 Returns: 

2332 A `SparseTensor` representing the deserialized `SparseTensor` objects. 

2333 

2334 """ 

2335 output_indices, output_values, output_shape = ( 

2336 gen_sparse_ops.deserialize_sparse(serialized_sparse, dtype, name=name)) 

2337 

2338 # Feed rank data back in, if available 

2339 output_indices.set_shape([None, rank]) 

2340 output_shape.set_shape([rank]) 

2341 

2342 return sparse_tensor.SparseTensor(output_indices, output_values, output_shape) 

2343 

2344 

2345@tf_export( 

2346 "io.deserialize_many_sparse", 

2347 v1=["io.deserialize_many_sparse", "deserialize_many_sparse"]) 

2348@dispatch.add_dispatch_support 

2349@deprecation.deprecated_endpoints("deserialize_many_sparse") 

2350def deserialize_many_sparse(serialized_sparse, dtype, rank=None, name=None): 

2351 """Deserialize and concatenate `SparseTensors` from a serialized minibatch. 

2352 

2353 The input `serialized_sparse` must be a string matrix of shape `[N x 3]` where 

2354 `N` is the minibatch size and the rows correspond to packed outputs of 

2355 `serialize_sparse`. The ranks of the original `SparseTensor` objects 

2356 must all match. When the final `SparseTensor` is created, it has rank one 

2357 higher than the ranks of the incoming `SparseTensor` objects (they have been 

2358 concatenated along a new row dimension). 

2359 

2360 The output `SparseTensor` object's shape values for all dimensions but the 

2361 first are the max across the input `SparseTensor` objects' shape values 

2362 for the corresponding dimensions. Its first shape value is `N`, the minibatch 

2363 size. 

2364 

2365 The input `SparseTensor` objects' indices are assumed ordered in 

2366 standard lexicographic order. If this is not the case, after this 

2367 step run `sparse.reorder` to restore index ordering. 

2368 

2369 For example, if the serialized input is a `[2, 3]` matrix representing two 

2370 original `SparseTensor` objects: 

2371 

2372 index = [ 0] 

2373 [10] 

2374 [20] 

2375 values = [1, 2, 3] 

2376 shape = [50] 

2377 

2378 and 

2379 

2380 index = [ 2] 

2381 [10] 

2382 values = [4, 5] 

2383 shape = [30] 

2384 

2385 then the final deserialized `SparseTensor` will be: 

2386 

2387 index = [0 0] 

2388 [0 10] 

2389 [0 20] 

2390 [1 2] 

2391 [1 10] 

2392 values = [1, 2, 3, 4, 5] 

2393 shape = [2 50] 

2394 

2395 Args: 

2396 serialized_sparse: 2-D `Tensor` of type `string` of shape `[N, 3]`. 

2397 The serialized and packed `SparseTensor` objects. 

2398 dtype: The `dtype` of the serialized `SparseTensor` objects. 

2399 rank: (optional) Python int, the rank of the `SparseTensor` objects. 

2400 name: A name prefix for the returned tensors (optional) 

2401 

2402 Returns: 

2403 A `SparseTensor` representing the deserialized `SparseTensor`s, 

2404 concatenated along the `SparseTensor`s' first dimension. 

2405 

2406 All of the serialized `SparseTensor`s must have had the same rank and type. 

2407 """ 

2408 output_indices, output_values, output_shape = ( 

2409 gen_sparse_ops.deserialize_many_sparse( 

2410 serialized_sparse, dtype, name=name)) 

2411 

2412 # Feed rank data back in, if available 

2413 output_indices.set_shape([None, rank]) 

2414 output_shape.set_shape([rank]) 

2415 

2416 return sparse_tensor.SparseTensor(output_indices, output_values, output_shape) 

2417 

2418 

2419@tf_export("sparse.sparse_dense_matmul", 

2420 v1=["sparse.sparse_dense_matmul", "sparse.matmul", 

2421 "sparse_tensor_dense_matmul"]) 

2422@deprecation.deprecated_endpoints("sparse_tensor_dense_matmul") 

2423def sparse_tensor_dense_matmul(sp_a, 

2424 b, 

2425 adjoint_a=False, 

2426 adjoint_b=False, 

2427 name=None): 

2428 # pylint: disable=line-too-long 

2429 """Multiply SparseTensor (or dense Matrix) (of rank 2) "A" by dense matrix 

2430 

2431 (or SparseTensor) "B". Please note that one and only one of the inputs MUST 

2432 be a SparseTensor and the other MUST be a dense matrix. 

2433 

2434 The following input format is recommended (but not required) for optimal 

2435 performance: 

2436 

2437 * If `adjoint_a == false`: `A` should be sorted in lexicographically 

2438 increasing order. Use `sparse.reorder` if you're not sure. 

2439 * If `adjoint_a == true`: `A` should be sorted in order of increasing 

2440 dimension 1 (i.e., "column major" order instead of "row major" order). 

2441 

2442 Args: 

2443 sp_a: SparseTensor (or dense Matrix) A, of rank 2. 

2444 b: dense Matrix (or SparseTensor) B, with the same dtype as sp_a. 

2445 adjoint_a: Use the adjoint of A in the matrix multiply. If A is complex, 

2446 this is transpose(conj(A)). Otherwise it's transpose(A). 

2447 adjoint_b: Use the adjoint of B in the matrix multiply. If B is complex, 

2448 this is transpose(conj(B)). Otherwise it's transpose(B). 

2449 name: A name prefix for the returned tensors (optional) 

2450 

2451 Returns: 

2452 A dense matrix (pseudo-code in dense np.matrix notation): 

2453 `A = A.H if adjoint_a else A` 

2454 `B = B.H if adjoint_b else B` 

2455 `return A*B` 

2456 

2457 Notes: 

2458 

2459 Using `tf.nn.embedding_lookup_sparse` for sparse multiplication: 

2460 

2461 It's not obvious but you can consider `embedding_lookup_sparse` as another 

2462 sparse and dense multiplication. In some situations, you may prefer to use 

2463 `embedding_lookup_sparse` even though you're not dealing with embeddings. 

2464 

2465 There are two questions to ask in the decision process: Do you need gradients 

2466 computed as sparse too? Is your sparse data represented as two 

2467 `SparseTensor`s: ids and values? There is more explanation about data format 

2468 below. If you answer any of these questions as yes, consider using 

2469 `tf.nn.embedding_lookup_sparse`. 

2470 

2471 Following explains differences between the expected SparseTensors: 

2472 For example if dense form of your sparse data has shape `[3, 5]` and values: 

2473 

2474 [[ a ] 

2475 [b c] 

2476 [ d ]] 

2477 

2478 

2479 `SparseTensor` format expected by `sparse_tensor_dense_matmul`: 

2480 `sp_a` (indices, values): 

2481 

2482 [0, 1]: a 

2483 [1, 0]: b 

2484 [1, 4]: c 

2485 [2, 2]: d 

2486 

2487 `SparseTensor` format expected by `embedding_lookup_sparse`: 

2488 `sp_ids` `sp_weights` 

2489 

2490 [0, 0]: 1 [0, 0]: a 

2491 [1, 0]: 0 [1, 0]: b 

2492 [1, 1]: 4 [1, 1]: c 

2493 [2, 0]: 2 [2, 0]: d 

2494 

2495 

2496 Deciding when to use `sparse_tensor_dense_matmul` vs. 

2497 `matmul`(a_is_sparse=True): 

2498 

2499 There are a number of questions to ask in the decision process, including: 

2500 

2501 * Will the SparseTensor `A` fit in memory if densified? 

2502 * Is the column count of the product large (>> 1)? 

2503 * Is the density of `A` larger than approximately 15%? 

2504 

2505 If the answer to several of these questions is yes, consider 

2506 converting the `SparseTensor` to a dense one and using `tf.matmul` with 

2507 `a_is_sparse=True`. 

2508 

2509 This operation tends to perform well when `A` is more sparse, if the column 

2510 size of the product is small (e.g. matrix-vector multiplication), if 

2511 `sp_a.dense_shape` takes on large values. 

2512 

2513 Below is a rough speed comparison between `sparse_tensor_dense_matmul`, 

2514 labeled 'sparse', and `matmul`(a_is_sparse=True), labeled 'dense'. For 

2515 purposes of the comparison, the time spent converting from a `SparseTensor` to 

2516 a dense `Tensor` is not included, so it is overly conservative with respect to 

2517 the time ratio. 

2518 

2519 Benchmark system: 

2520 CPU: Intel Ivybridge with HyperThreading (6 cores) dL1:32KB dL2:256KB dL3:12MB 

2521 GPU: NVidia Tesla k40c 

2522 

2523 Compiled with: 

2524 `-c opt --config=cuda --copt=-mavx` 

2525 

2526 ``` 

2527 tensorflow/python/sparse_tensor_dense_matmul_op_test --benchmarks 

2528 A sparse [m, k] with % nonzero values between 1% and 80% 

2529 B dense [k, n] 

2530 

2531 % nnz n gpu m k dt(dense) dt(sparse) dt(sparse)/dt(dense) 

2532 0.01 1 True 100 100 0.000221166 0.00010154 0.459112 

2533 0.01 1 True 100 1000 0.00033858 0.000109275 0.322745 

2534 0.01 1 True 1000 100 0.000310557 9.85661e-05 0.317385 

2535 0.01 1 True 1000 1000 0.0008721 0.000100875 0.115669 

2536 0.01 1 False 100 100 0.000208085 0.000107603 0.51711 

2537 0.01 1 False 100 1000 0.000327112 9.51118e-05 0.290762 

2538 0.01 1 False 1000 100 0.000308222 0.00010345 0.335635 

2539 0.01 1 False 1000 1000 0.000865721 0.000101397 0.117124 

2540 0.01 10 True 100 100 0.000218522 0.000105537 0.482958 

2541 0.01 10 True 100 1000 0.000340882 0.000111641 0.327506 

2542 0.01 10 True 1000 100 0.000315472 0.000117376 0.372064 

2543 0.01 10 True 1000 1000 0.000905493 0.000123263 0.136128 

2544 0.01 10 False 100 100 0.000221529 9.82571e-05 0.44354 

2545 0.01 10 False 100 1000 0.000330552 0.000112615 0.340687 

2546 0.01 10 False 1000 100 0.000341277 0.000114097 0.334324 

2547 0.01 10 False 1000 1000 0.000819944 0.000120982 0.147549 

2548 0.01 25 True 100 100 0.000207806 0.000105977 0.509981 

2549 0.01 25 True 100 1000 0.000322879 0.00012921 0.400181 

2550 0.01 25 True 1000 100 0.00038262 0.00014158 0.370035 

2551 0.01 25 True 1000 1000 0.000865438 0.000202083 0.233504 

2552 0.01 25 False 100 100 0.000209401 0.000104696 0.499979 

2553 0.01 25 False 100 1000 0.000321161 0.000130737 0.407076 

2554 0.01 25 False 1000 100 0.000377012 0.000136801 0.362856 

2555 0.01 25 False 1000 1000 0.000861125 0.00020272 0.235413 

2556 0.2 1 True 100 100 0.000206952 9.69219e-05 0.46833 

2557 0.2 1 True 100 1000 0.000348674 0.000147475 0.422959 

2558 0.2 1 True 1000 100 0.000336908 0.00010122 0.300439 

2559 0.2 1 True 1000 1000 0.001022 0.000203274 0.198898 

2560 0.2 1 False 100 100 0.000207532 9.5412e-05 0.459746 

2561 0.2 1 False 100 1000 0.000356127 0.000146824 0.41228 

2562 0.2 1 False 1000 100 0.000322664 0.000100918 0.312764 

2563 0.2 1 False 1000 1000 0.000998987 0.000203442 0.203648 

2564 0.2 10 True 100 100 0.000211692 0.000109903 0.519165 

2565 0.2 10 True 100 1000 0.000372819 0.000164321 0.440753 

2566 0.2 10 True 1000 100 0.000338651 0.000144806 0.427596 

2567 0.2 10 True 1000 1000 0.00108312 0.000758876 0.70064 

2568 0.2 10 False 100 100 0.000215727 0.000110502 0.512231 

2569 0.2 10 False 100 1000 0.000375419 0.0001613 0.429653 

2570 0.2 10 False 1000 100 0.000336999 0.000145628 0.432132 

2571 0.2 10 False 1000 1000 0.00110502 0.000762043 0.689618 

2572 0.2 25 True 100 100 0.000218705 0.000129913 0.594009 

2573 0.2 25 True 100 1000 0.000394794 0.00029428 0.745402 

2574 0.2 25 True 1000 100 0.000404483 0.0002693 0.665788 

2575 0.2 25 True 1000 1000 0.0012002 0.00194494 1.62052 

2576 0.2 25 False 100 100 0.000221494 0.0001306 0.589632 

2577 0.2 25 False 100 1000 0.000396436 0.000297204 0.74969 

2578 0.2 25 False 1000 100 0.000409346 0.000270068 0.659754 

2579 0.2 25 False 1000 1000 0.00121051 0.00193737 1.60046 

2580 0.5 1 True 100 100 0.000214981 9.82111e-05 0.456836 

2581 0.5 1 True 100 1000 0.000415328 0.000223073 0.537101 

2582 0.5 1 True 1000 100 0.000358324 0.00011269 0.314492 

2583 0.5 1 True 1000 1000 0.00137612 0.000437401 0.317851 

2584 0.5 1 False 100 100 0.000224196 0.000101423 0.452386 

2585 0.5 1 False 100 1000 0.000400987 0.000223286 0.556841 

2586 0.5 1 False 1000 100 0.000368825 0.00011224 0.304318 

2587 0.5 1 False 1000 1000 0.00136036 0.000429369 0.31563 

2588 0.5 10 True 100 100 0.000222125 0.000112308 0.505608 

2589 0.5 10 True 100 1000 0.000461088 0.00032357 0.701753 

2590 0.5 10 True 1000 100 0.000394624 0.000225497 0.571422 

2591 0.5 10 True 1000 1000 0.00158027 0.00190898 1.20801 

2592 0.5 10 False 100 100 0.000232083 0.000114978 0.495418 

2593 0.5 10 False 100 1000 0.000454574 0.000324632 0.714146 

2594 0.5 10 False 1000 100 0.000379097 0.000227768 0.600817 

2595 0.5 10 False 1000 1000 0.00160292 0.00190168 1.18638 

2596 0.5 25 True 100 100 0.00023429 0.000151703 0.647501 

2597 0.5 25 True 100 1000 0.000497462 0.000598873 1.20386 

2598 0.5 25 True 1000 100 0.000460778 0.000557038 1.20891 

2599 0.5 25 True 1000 1000 0.00170036 0.00467336 2.74845 

2600 0.5 25 False 100 100 0.000228981 0.000155334 0.678371 

2601 0.5 25 False 100 1000 0.000496139 0.000620789 1.25124 

2602 0.5 25 False 1000 100 0.00045473 0.000551528 1.21287 

2603 0.5 25 False 1000 1000 0.00171793 0.00467152 2.71927 

2604 0.8 1 True 100 100 0.000222037 0.000105301 0.47425 

2605 0.8 1 True 100 1000 0.000410804 0.000329327 0.801664 

2606 0.8 1 True 1000 100 0.000349735 0.000131225 0.375212 

2607 0.8 1 True 1000 1000 0.00139219 0.000677065 0.48633 

2608 0.8 1 False 100 100 0.000214079 0.000107486 0.502085 

2609 0.8 1 False 100 1000 0.000413746 0.000323244 0.781261 

2610 0.8 1 False 1000 100 0.000348983 0.000131983 0.378193 

2611 0.8 1 False 1000 1000 0.00136296 0.000685325 0.50282 

2612 0.8 10 True 100 100 0.000229159 0.00011825 0.516017 

2613 0.8 10 True 100 1000 0.000498845 0.000532618 1.0677 

2614 0.8 10 True 1000 100 0.000383126 0.00029935 0.781336 

2615 0.8 10 True 1000 1000 0.00162866 0.00307312 1.88689 

2616 0.8 10 False 100 100 0.000230783 0.000124958 0.541452 

2617 0.8 10 False 100 1000 0.000493393 0.000550654 1.11606 

2618 0.8 10 False 1000 100 0.000377167 0.000298581 0.791642 

2619 0.8 10 False 1000 1000 0.00165795 0.00305103 1.84024 

2620 0.8 25 True 100 100 0.000233496 0.000175241 0.75051 

2621 0.8 25 True 100 1000 0.00055654 0.00102658 1.84458 

2622 0.8 25 True 1000 100 0.000463814 0.000783267 1.68875 

2623 0.8 25 True 1000 1000 0.00186905 0.00755344 4.04132 

2624 0.8 25 False 100 100 0.000240243 0.000175047 0.728625 

2625 0.8 25 False 100 1000 0.000578102 0.00104499 1.80763 

2626 0.8 25 False 1000 100 0.000485113 0.000776849 1.60138 

2627 0.8 25 False 1000 1000 0.00211448 0.00752736 3.55992 

2628 ``` 

2629 

2630 """ 

2631 # pylint: enable=line-too-long 

2632 

2633 if isinstance(b, sparse_tensor.SparseTensor) \ 

2634 or isinstance(b, sparse_tensor.SparseTensorValue): 

2635 # We can do C * D where C is sparse but if we want to do A * B when 

2636 # B is sparse we have to transpose. But AB = (B'A')' so we have to feed in 

2637 # the transpose of the arguments as well. 

2638 if adjoint_a != adjoint_b: 

2639 return array_ops.transpose( 

2640 sparse_tensor_dense_matmul(b, sp_a, adjoint_a, adjoint_b)) 

2641 else: 

2642 return array_ops.transpose( 

2643 sparse_tensor_dense_matmul( 

2644 b, sp_a, adjoint_a=not adjoint_a, adjoint_b=not adjoint_b)) 

2645 

2646 else: 

2647 sp_a = _convert_to_sparse_tensor(sp_a) 

2648 with ops.name_scope(name, "SparseTensorDenseMatMul", 

2649 [sp_a.indices, sp_a.values, b]) as name: 

2650 b = ops.convert_to_tensor(b, name="b") 

2651 return gen_sparse_ops.sparse_tensor_dense_mat_mul( 

2652 a_indices=sp_a.indices, 

2653 a_values=sp_a.values, 

2654 a_shape=sp_a.dense_shape, 

2655 b=b, 

2656 adjoint_a=adjoint_a, 

2657 adjoint_b=adjoint_b) 

2658 

2659 

2660@tf_export("sparse.softmax", v1=["sparse.softmax", "sparse_softmax"]) 

2661@deprecation.deprecated_endpoints("sparse_softmax") 

2662def sparse_softmax(sp_input, name=None): 

2663 """Applies softmax to a batched N-D `SparseTensor`. 

2664 

2665 The inputs represent an N-D SparseTensor with logical shape `[..., B, C]` 

2666 (where `N >= 2`), and with indices sorted in the canonical lexicographic 

2667 order. 

2668 

2669 This op is equivalent to applying the normal `tf.nn.softmax()` to each 

2670 innermost logical submatrix with shape `[B, C]`, but with the catch that *the 

2671 implicitly zero elements do not participate*. Specifically, the algorithm is 

2672 equivalent to: 

2673 

2674 (1) Applies `tf.nn.softmax()` to a densified view of each innermost 

2675 submatrix with shape `[B, C]`, along the size-C dimension; 

2676 (2) Masks out the original implicitly-zero locations; 

2677 (3) Renormalizes the remaining elements. 

2678 

2679 Hence, the `SparseTensor` result has exactly the same non-zero indices and 

2680 shape. 

2681 

2682 Example using a 3-D SparseTensor: 

2683 

2684 >>> st = tf.sparse.from_dense( 

2685 ... [[[0., np.e], 

2686 ... [1., 0.]], 

2687 ... 

2688 ... [[np.e, 0.], 

2689 ... [np.e, np.e]]]) 

2690 >>> res = tf.sparse.softmax(st) 

2691 >>> res.indices 

2692 <tf.Tensor: shape=(5, 3), dtype=int64, numpy= 

2693 array([[0, 0, 1], 

2694 [0, 1, 0], 

2695 [1, 0, 0], 

2696 [1, 1, 0], 

2697 [1, 1, 1]])> 

2698 >>> res.values 

2699 <tf.Tensor: ... numpy=array([1. , 1. , 1. , 0.5, 0.5], dtype=float32)> 

2700 >>> res.dense_shape 

2701 <tf.Tensor: shape=(3,), dtype=int64, numpy=array([2, 2, 2])> 

2702 >>> tf.sparse.to_dense(res) 

2703 <tf.Tensor: shape=(2, 2, 2), dtype=float32, numpy= 

2704 array([[[0. , 1. ], 

2705 [1. , 0. ]], 

2706 [[1. , 0. ], 

2707 [0.5, 0.5]]], dtype=float32)> 

2708 

2709 Args: 

2710 sp_input: N-D `SparseTensor`, where `N >= 2`. 

2711 name: optional name of the operation. 

2712 Returns: 

2713 output: N-D `SparseTensor` representing the results. 

2714 """ 

2715 with ops.name_scope(name, "SparseSoftmax", 

2716 [sp_input.indices, sp_input.values]) as name: 

2717 out_vals = gen_sparse_ops.sparse_softmax(sp_input.indices, sp_input.values, 

2718 sp_input.dense_shape) 

2719 return sparse_tensor.SparseTensor(sp_input.indices, out_vals, 

2720 sp_input.dense_shape) 

2721 

2722 

2723@tf_export("sparse.maximum", v1=["sparse.maximum", "sparse_maximum"]) 

2724@deprecation.deprecated_endpoints("sparse_maximum") 

2725def sparse_maximum(sp_a, sp_b, name=None): 

2726 """Returns the element-wise max of two SparseTensors. 

2727 

2728 Assumes the two SparseTensors have the same shape, i.e., no broadcasting. 

2729 

2730 Example: 

2731 

2732 >>> sp_zero = tf.sparse.SparseTensor([[0]], [0], [7]) 

2733 >>> sp_one = tf.sparse.SparseTensor([[1]], [1], [7]) 

2734 >>> res = tf.sparse.maximum(sp_zero, sp_one) 

2735 >>> res.indices 

2736 <tf.Tensor: shape=(2, 1), dtype=int64, numpy= 

2737 array([[0], 

2738 [1]])> 

2739 >>> res.values 

2740 <tf.Tensor: shape=(2,), dtype=int32, numpy=array([0, 1], dtype=int32)> 

2741 >>> res.dense_shape 

2742 <tf.Tensor: shape=(1,), dtype=int64, numpy=array([7])> 

2743 

2744 The reduction version of this elementwise operation is `tf.sparse.reduce_max` 

2745 

2746 Args: 

2747 sp_a: a `SparseTensor` operand whose dtype is real, and indices 

2748 lexicographically ordered. 

2749 sp_b: the other `SparseTensor` operand with the same requirements (and the 

2750 same shape). 

2751 name: optional name of the operation. 

2752 Returns: 

2753 output: the output SparseTensor. 

2754 """ 

2755 with ops.name_scope( 

2756 name, "SparseSparseMaximum", 

2757 [sp_a.indices, sp_a.values, sp_b.indices, sp_b.values]) as name: 

2758 out_indices, out_values = gen_sparse_ops.sparse_sparse_maximum( 

2759 sp_a.indices, 

2760 sp_a.values, 

2761 sp_a.dense_shape, 

2762 sp_b.indices, 

2763 sp_b.values, 

2764 sp_b.dense_shape, 

2765 name=name) 

2766 return sparse_tensor.SparseTensor(out_indices, out_values, sp_a.dense_shape) 

2767 

2768 

2769@tf_export("sparse.minimum", v1=["sparse.minimum", "sparse_minimum"]) 

2770@deprecation.deprecated_endpoints("sparse_minimum") 

2771def sparse_minimum(sp_a, sp_b, name=None): 

2772 """Returns the element-wise min of two SparseTensors. 

2773 

2774 Assumes the two SparseTensors have the same shape, i.e., no broadcasting. 

2775 

2776 Example: 

2777 

2778 >>> sp_zero = tf.sparse.SparseTensor([[0]], [0], [7]) 

2779 >>> sp_one = tf.sparse.SparseTensor([[1]], [1], [7]) 

2780 >>> res = tf.sparse.minimum(sp_zero, sp_one) 

2781 >>> res.indices 

2782 <tf.Tensor: shape=(2, 1), dtype=int64, numpy= 

2783 array([[0], 

2784 [1]])> 

2785 >>> res.values 

2786 <tf.Tensor: shape=(2,), dtype=int32, numpy=array([0, 0], dtype=int32)> 

2787 >>> res.dense_shape 

2788 <tf.Tensor: shape=(1,), dtype=int64, numpy=array([7])> 

2789 

2790 Args: 

2791 sp_a: a `SparseTensor` operand whose dtype is real, and indices 

2792 lexicographically ordered. 

2793 sp_b: the other `SparseTensor` operand with the same requirements (and the 

2794 same shape). 

2795 name: optional name of the operation. 

2796 Returns: 

2797 output: the output SparseTensor. 

2798 """ 

2799 with ops.name_scope( 

2800 name, "SparseSparseMinimum", 

2801 [sp_a.indices, sp_a.values, sp_b.indices, sp_b.values]) as name: 

2802 out_indices, out_values = gen_sparse_ops.sparse_sparse_minimum( 

2803 sp_a.indices, 

2804 sp_a.values, 

2805 sp_a.dense_shape, 

2806 sp_b.indices, 

2807 sp_b.values, 

2808 sp_b.dense_shape, 

2809 name=name) 

2810 return sparse_tensor.SparseTensor(out_indices, out_values, sp_a.dense_shape) 

2811 

2812 

2813@tf_export("sparse.transpose", v1=["sparse.transpose", "sparse_transpose"]) 

2814@deprecation.deprecated_endpoints("sparse_transpose") 

2815def sparse_transpose(sp_input, perm=None, name=None): 

2816 """Transposes a `SparseTensor`. 

2817 

2818 Permutes the dimensions according to the value of `perm`. This is the sparse 

2819 version of `tf.transpose`. 

2820 

2821 The returned tensor's dimension `i` will correspond to the input dimension 

2822 `perm[i]`. If `perm` is not given, it is set to (n-1...0), where n is the rank 

2823 of the input tensor. Hence, by default, this operation performs a regular 

2824 matrix transpose on 2-D input Tensors. 

2825 

2826 For example: 

2827 

2828 >>> x = tf.SparseTensor(indices=[[0, 1], [0, 3], [2, 3], [3, 1]], 

2829 ... values=[1.1, 2.2, 3.3, 4.4], 

2830 ... dense_shape=[4, 5]) 

2831 >>> print('x =', tf.sparse.to_dense(x)) 

2832 x = tf.Tensor( 

2833 [[0. 1.1 0. 2.2 0. ] 

2834 [0. 0. 0. 0. 0. ] 

2835 [0. 0. 0. 3.3 0. ] 

2836 [0. 4.4 0. 0. 0. ]], shape=(4, 5), dtype=float32) 

2837 

2838 >>> x_transpose = tf.sparse.transpose(x) 

2839 >>> print('x_transpose =', tf.sparse.to_dense(x_transpose)) 

2840 x_transpose = tf.Tensor( 

2841 [[0. 0. 0. 0. ] 

2842 [1.1 0. 0. 4.4] 

2843 [0. 0. 0. 0. ] 

2844 [2.2 0. 3.3 0. ] 

2845 [0. 0. 0. 0. ]], shape=(5, 4), dtype=float32) 

2846 

2847 Equivalently, you could call `tf.sparse.transpose(x, perm=[1, 0])`. The 

2848 `perm` argument is more useful for n-dimensional tensors where n > 2. 

2849 

2850 >>> x = tf.SparseTensor(indices=[[0, 0, 1], [0, 0, 3], [1, 2, 3], [1, 3, 1]], 

2851 ... values=[1.1, 2.2, 3.3, 4.4], 

2852 ... dense_shape=[2, 4, 5]) 

2853 >>> print('x =', tf.sparse.to_dense(x)) 

2854 x = tf.Tensor( 

2855 [[[0. 1.1 0. 2.2 0. ] 

2856 [0. 0. 0. 0. 0. ] 

2857 [0. 0. 0. 0. 0. ] 

2858 [0. 0. 0. 0. 0. ]] 

2859 [[0. 0. 0. 0. 0. ] 

2860 [0. 0. 0. 0. 0. ] 

2861 [0. 0. 0. 3.3 0. ] 

2862 [0. 4.4 0. 0. 0. ]]], shape=(2, 4, 5), dtype=float32) 

2863 

2864 As above, simply calling `tf.sparse.transpose` will default to `perm=[2,1,0]`. 

2865 

2866 To take the transpose of a batch of sparse matrices, where 0 is the batch 

2867 dimension, you would set `perm=[0,2,1]`. 

2868 

2869 >>> x_transpose = tf.sparse.transpose(x, perm=[0, 2, 1]) 

2870 >>> print('x_transpose =', tf.sparse.to_dense(x_transpose)) 

2871 x_transpose = tf.Tensor( 

2872 [[[0. 0. 0. 0. ] 

2873 [1.1 0. 0. 0. ] 

2874 [0. 0. 0. 0. ] 

2875 [2.2 0. 0. 0. ] 

2876 [0. 0. 0. 0. ]] 

2877 [[0. 0. 0. 0. ] 

2878 [0. 0. 0. 4.4] 

2879 [0. 0. 0. 0. ] 

2880 [0. 0. 3.3 0. ] 

2881 [0. 0. 0. 0. ]]], shape=(2, 5, 4), dtype=float32) 

2882 

2883 Args: 

2884 sp_input: The input `SparseTensor`. 

2885 perm: A permutation vector of the dimensions of `sp_input`. 

2886 name: A name prefix for the returned tensors (optional). 

2887 

2888 Returns: 

2889 A transposed `SparseTensor`. 

2890 

2891 Raises: 

2892 TypeError: If `sp_input` is not a `SparseTensor`. 

2893 """ 

2894 with ops.name_scope(name, "SparseTranspose", [sp_input]) as name: 

2895 if perm is None: 

2896 if sp_input.shape.rank is not None: 

2897 rank = sp_input.shape.rank 

2898 perm = (rank - 1) - np.arange(0, rank, 1) 

2899 else: 

2900 rank = array_ops.rank(sp_input) 

2901 perm = (rank - 1) - math_ops.range(0, rank, 1) 

2902 indices = sp_input.indices 

2903 transposed_indices = array_ops.transpose( 

2904 array_ops.gather(array_ops.transpose(indices), perm)) 

2905 

2906 perm_ = tensor_util.constant_value(ops.convert_to_tensor(perm)) 

2907 if perm_ is not None and sp_input.get_shape().is_fully_defined(): 

2908 old_shape_ = sp_input.get_shape().as_list() 

2909 transposed_dense_shape = list(old_shape_) # Copy. 

2910 for i, p in enumerate(perm_): 

2911 transposed_dense_shape[i] = old_shape_[p] 

2912 else: 

2913 dense_shape = sp_input.dense_shape 

2914 transposed_dense_shape = array_ops.gather(dense_shape, perm) 

2915 transposed_st = sparse_tensor.SparseTensor( 

2916 transposed_indices, sp_input.values, transposed_dense_shape) 

2917 transposed_st = sparse_reorder(transposed_st) 

2918 return transposed_st 

2919 

2920 

2921@tf_export("sparse.map_values", v1=[]) 

2922@dispatch.add_dispatch_support 

2923def map_values(op, *args, **kwargs): 

2924 """Applies `op` to the `.values` tensor of one or more `SparseTensor`s. 

2925 

2926 Replaces any `SparseTensor` in `args` or `kwargs` with its `values` 

2927 tensor (which contains the non-default values for the SparseTensor), 

2928 and then calls `op`. Returns a `SparseTensor` that is constructed 

2929 from the input `SparseTensor`s' `indices`, `dense_shape`, and the 

2930 value returned by the `op`. 

2931 

2932 If the input arguments contain multiple `SparseTensor`s, then they must have 

2933 equal `indices` and dense shapes. 

2934 

2935 Examples: 

2936 

2937 >>> s = tf.sparse.from_dense([[1, 2, 0], 

2938 ... [0, 4, 0], 

2939 ... [1, 0, 0]]) 

2940 >>> tf.sparse.to_dense(tf.sparse.map_values(tf.ones_like, s)).numpy() 

2941 array([[1, 1, 0], 

2942 [0, 1, 0], 

2943 [1, 0, 0]], dtype=int32) 

2944 

2945 >>> tf.sparse.to_dense(tf.sparse.map_values(tf.multiply, s, s)).numpy() 

2946 array([[ 1, 4, 0], 

2947 [ 0, 16, 0], 

2948 [ 1, 0, 0]], dtype=int32) 

2949 

2950 >>> tf.sparse.to_dense(tf.sparse.map_values(tf.add, s, 5)).numpy() 

2951 array([[6, 7, 0], 

2952 [0, 9, 0], 

2953 [6, 0, 0]], dtype=int32) 

2954 

2955 Note: even though `tf.add(0, 5) != 0`, implicit zeros 

2956 will remain unchanged. However, if the sparse tensor contains any explicit 

2957 zeros, these will be affected by the mapping! 

2958 

2959 Args: 

2960 op: The operation that should be applied to the SparseTensor `values`. `op` 

2961 is typically an element-wise operation (such as math_ops.add), but any 

2962 operation that preserves the shape can be used. 

2963 *args: Arguments for `op`. 

2964 **kwargs: Keyword arguments for `op`. 

2965 

2966 Returns: 

2967 A `SparseTensor` whose `indices` and `dense_shape` matches the `indices` 

2968 and `dense_shape` of all input `SparseTensor`s. 

2969 Raises: 

2970 ValueError: If args contains no `SparseTensor`, or if the `indices` 

2971 or `dense_shape`s of the input `SparseTensor`s are not equal. 

2972 """ 

2973 sparse_list = [] 

2974 inner_args = _replace_sparse_with_values(args, sparse_list) 

2975 inner_kwargs = _replace_sparse_with_values(kwargs, sparse_list) 

2976 if not sparse_list: 

2977 raise ValueError("No SparseTensor in argument list of map_values") 

2978 

2979 with ops.control_dependencies(_assert_sparse_compatible(sparse_list)): 

2980 # Delegate to op, and then compose the result from the transformed values 

2981 # and the known indices/dense shape. Since we ensure that indices and shape 

2982 # are identical, we can just use the first one. 

2983 return sparse_tensor.SparseTensor(sparse_list[0].indices, 

2984 op(*inner_args, **inner_kwargs), 

2985 sparse_list[0].dense_shape) 

2986 

2987 

2988def _assert_sparse_compatible(sparse_tensors): 

2989 """Check that all of `sparse_tensors` have same `indices` and `dense_shape`. 

2990 

2991 Args: 

2992 sparse_tensors: A list of sparse tensors. 

2993 

2994 Returns: 

2995 An op to be used as a control dependency. 

2996 """ 

2997 checks = [] 

2998 first = sparse_tensors[0] 

2999 for t in sparse_tensors[1:]: 

3000 checks.append( 

3001 check_ops.assert_equal( 

3002 first.dense_shape, t.dense_shape, message="Mismatched shapes!")) 

3003 checks.append( 

3004 check_ops.assert_equal( 

3005 first.indices, t.indices, message="Mismatched indices!")) 

3006 return checks 

3007 

3008 

3009def _replace_sparse_with_values(value, sparse_list): 

3010 """Replace `SparseTensor`s with their values in `value` 

3011 

3012 Each `SparseTensor` in `value` is replaced by its `values` tensor, and 

3013 collects all `SparseTensor`s in `sparse_list`. 

3014 

3015 Args: 

3016 value: A structure of `Tensor`s and `SparseTensor`s 

3017 sparse_list: A list. Output parameter that collects all `SparseTensor`s in 

3018 `value`. 

3019 

3020 Returns: 

3021 `value` with each SparseTensor replaced by its `.value` attribute. 

3022 """ 

3023 flat_vals = nest.flatten(value, expand_composites=False) 

3024 new_vals = [] 

3025 for v in flat_vals: 

3026 if isinstance(v, sparse_tensor.SparseTensor): 

3027 sparse_list.append(v) 

3028 new_vals.append(v.values) 

3029 else: 

3030 new_vals.append(v) 

3031 return nest.pack_sequence_as(value, new_vals, expand_composites=False) 

3032 

3033 

3034def _add_sparse_to_tensors_map(sp_input, 

3035 container=None, 

3036 shared_name=None, 

3037 name=None): 

3038 """Add a `SparseTensor` to a `SparseTensorsMap` and return its handle. 

3039 

3040 Args: 

3041 sp_input: The input `SparseTensor`. 

3042 container: The container for the underlying `SparseTensorsMap` (optional). 

3043 shared_name: The shared name for the underlying `SparseTensorsMap` 

3044 (optional, defaults to the name of the newly created op). 

3045 name: A name prefix for the returned tensors (optional). 

3046 

3047 Returns: 

3048 A string 1-vector (1D `Tensor`), with the single element representing the 

3049 a unique handle to a `SparseTensor` stored by the `SparseTensorMap` 

3050 underlying this op. 

3051 

3052 Raises: 

3053 TypeError: If `sp_input` is not a `SparseTensor`. 

3054 """ 

3055 sp_input = _convert_to_sparse_tensor(sp_input) 

3056 

3057 return gen_sparse_ops.add_sparse_to_tensors_map( 

3058 sp_input.indices, 

3059 sp_input.values, 

3060 sp_input.dense_shape, 

3061 container=container, 

3062 shared_name=shared_name, 

3063 name=name) 

3064 

3065 

3066def _add_many_sparse_to_tensors_map(sp_input, 

3067 container=None, 

3068 shared_name=None, 

3069 name=None): 

3070 """Add a minibatch `SparseTensor` to a `SparseTensorsMap`, return `N` handles. 

3071 

3072 The `SparseTensor` must have rank `R` greater than 1, and the first dimension 

3073 is treated as the minibatch dimension. Elements of the `SparseTensor` 

3074 must be sorted in increasing order of this first dimension. The serialized 

3075 `SparseTensor` objects going into each row of the output `Tensor` will have 

3076 rank `R-1`. 

3077 

3078 The minibatch size `N` is extracted from `sparse_shape[0]`. 

3079 

3080 Args: 

3081 sp_input: The input rank `R` `SparseTensor`. 

3082 container: The container for the underlying `SparseTensorsMap` (optional). 

3083 shared_name: The shared name for the underlying `SparseTensorsMap` 

3084 (optional, defaults to the name of the newly created op). 

3085 name: A name prefix for the returned tensors (optional). 

3086 

3087 Returns: 

3088 A string matrix (2-D `Tensor`) with `N` rows and `1` column. 

3089 Each row represents a unique handle to a `SparseTensor` stored by 

3090 the `SparseTensorMap` underlying this op. 

3091 

3092 Raises: 

3093 TypeError: If `sp_input` is not a `SparseTensor`. 

3094 """ 

3095 sp_input = _convert_to_sparse_tensor(sp_input) 

3096 

3097 return gen_sparse_ops.add_many_sparse_to_tensors_map( 

3098 sp_input.indices, 

3099 sp_input.values, 

3100 sp_input.dense_shape, 

3101 container=container, 

3102 shared_name=shared_name, 

3103 name=name) 

3104 

3105 

3106def _take_many_sparse_from_tensors_map(sparse_map_op, 

3107 sparse_handles, 

3108 rank=None, 

3109 name=None): 

3110 """Read `SparseTensors` from a `SparseTensorsMap` and concatenate them. 

3111 

3112 The input `sparse_handles` must be a string matrix of shape `[N, 1]` where 

3113 `N` is the minibatch size and the rows correspond to packed outputs of 

3114 `add_sparse_to_tensors_map`. The ranks of the original `SparseTensor` objects 

3115 must all match. When the final `SparseTensor` is created, it has rank one 

3116 higher than the ranks of the incoming `SparseTensor` objects (they have been 

3117 concatenated along a new row dimension). 

3118 

3119 The output `SparseTensor` object's shape values for all dimensions but the 

3120 first are the max across the input `SparseTensor` objects' shape values 

3121 for the corresponding dimensions. Its first shape value is `N`, the minibatch 

3122 size. 

3123 

3124 The input `SparseTensor` objects' indices are assumed ordered in 

3125 standard lexicographic order. If this is not the case, after this 

3126 step run `sparse.reorder` to restore index ordering. 

3127 

3128 For example, if the serialized input is a `[2, 3]` matrix representing two 

3129 original `SparseTensor` objects: 

3130 

3131 index = [ 0] 

3132 [10] 

3133 [20] 

3134 values = [1, 2, 3] 

3135 shape = [50] 

3136 

3137 and 

3138 

3139 index = [ 2] 

3140 [10] 

3141 values = [4, 5] 

3142 shape = [30] 

3143 

3144 then the final deserialized `SparseTensor` will be: 

3145 

3146 index = [0 0] 

3147 [0 10] 

3148 [0 20] 

3149 [1 2] 

3150 [1 10] 

3151 values = [1, 2, 3, 4, 5] 

3152 shape = [2 50] 

3153 

3154 Args: 

3155 sparse_map_op: The `Operation` that created the original handles. 

3156 Usually this is, e.g., `add_sparse_to_tensors_map(...).op`. 

3157 sparse_handles: 2-D `Tensor` of type `string` of shape `[N, 1]`. 

3158 The serialized and packed `SparseTensor` objects. 

3159 rank: (optional) Python int, the rank of the `SparseTensor` objects. 

3160 name: A name prefix for the returned tensors (optional) 

3161 

3162 Returns: 

3163 A `SparseTensor` representing the deserialized `SparseTensor`s, 

3164 concatenated along the `SparseTensor`s' first dimension. 

3165 

3166 All of the serialized `SparseTensor`s must have had the same rank and type. 

3167 """ 

3168 if not isinstance(sparse_map_op, ops.Operation): 

3169 raise TypeError("sparse_map_op be an Operation") 

3170 if sparse_map_op.type not in ("AddSparseToTensorsMap", 

3171 "AddManySparseToTensorsMap"): 

3172 raise TypeError( 

3173 "sparse_map_op must be one of AddSparseToTensorsMap or " 

3174 "AddSparseToTensorsMap. Instead, found `%s`." % sparse_map_op.type) 

3175 with ops.colocate_with(sparse_map_op): 

3176 shared_name = sparse_map_op.get_attr("shared_name") or sparse_map_op.name 

3177 output_indices, output_values, output_shape = ( 

3178 gen_sparse_ops.take_many_sparse_from_tensors_map( 

3179 sparse_handles, 

3180 dtype=sparse_map_op.get_attr("T"), 

3181 container=sparse_map_op.get_attr("container"), 

3182 shared_name=shared_name, 

3183 name=name)) 

3184 

3185 # Feed rank data back in, if available 

3186 output_indices.set_shape([None, rank]) 

3187 output_shape.set_shape([rank]) 

3188 

3189 return sparse_tensor.SparseTensor(output_indices, output_values, output_shape) 

3190 

3191 

3192class _UnaryMapValueDispatcher(dispatch.OpDispatcher): 

3193 """OpDispatcher for unary ops that maps base function across sparse values.""" 

3194 

3195 def __init__(self, original_func): 

3196 self._original_func = original_func 

3197 func_name = get_canonical_name_for_symbol(original_func) 

3198 arg_names = tf_inspect.getfullargspec(original_func)[0] 

3199 self._x = arg_names[0] 

3200 original_func.__doc__ = ( 

3201 original_func.__doc__.rstrip() + "\n\n" + 

3202 (" If `{x}` is a `SparseTensor`, returns\n" 

3203 " `SparseTensor({x}.indices, tf.{func}({x}.values, ...), " 

3204 "{x}.dense_shape)`").format(x=self._x, func=func_name)) 

3205 

3206 def handle(self, args, kwargs): 

3207 if args: 

3208 x, args = args[0], args[1:] 

3209 else: 

3210 kwargs = kwargs.copy() 

3211 x = kwargs.pop(self._x, None) 

3212 if isinstance(x, sparse_tensor.SparseTensor): 

3213 return sparse_tensor.SparseTensor( 

3214 indices=x.indices, 

3215 values=self._original_func(x.values, *args, **kwargs), 

3216 dense_shape=x.dense_shape) 

3217 else: 

3218 return self.NOT_SUPPORTED 

3219 

3220 

3221_UNARY_OPS = [ 

3222 # TODO(b/120307967) Add dispatchers for additional TensorFlow ops. 

3223 math_ops.abs, 

3224 math_ops.negative, 

3225 math_ops.sign, 

3226 math_ops.square, 

3227 math_ops.sqrt, 

3228 math_ops.erf, 

3229 math_ops.tanh, 

3230 # TODO(b/157272291) Add dispatchers for rest of special functions. 

3231 special_math_ops.bessel_i0e, 

3232 special_math_ops.bessel_i1e, 

3233] 

3234for unary_op in _UNARY_OPS: 

3235 _UnaryMapValueDispatcher(unary_op).register(unary_op)