chore: import upstream snapshot with attribution
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:14:16 +08:00
commit 8a852e4b4e
36502 changed files with 9277225 additions and 0 deletions
@@ -0,0 +1,395 @@
# Tests of TensorFlow sparse ops written using the Python API.
load("@xla//third_party/rules_python/python:py_library.bzl", "py_library")
load("//tensorflow:tensorflow.default.bzl", "cuda_py_strict_test", "tf_py_strict_test")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:license"],
default_visibility = ["//tensorflow:internal"],
licenses = ["notice"],
)
tf_py_strict_test(
name = "sparse_add_op_test",
size = "small",
srcs = ["sparse_add_op_test.py"],
deps = [
"//tensorflow/python/client:session",
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:sparse_tensor",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:gradient_checker",
"//tensorflow/python/ops:math_ops",
"//tensorflow/python/ops:sparse_grad",
"//tensorflow/python/ops:sparse_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
tf_py_strict_test(
name = "sparse_concat_op_test",
size = "small",
srcs = ["sparse_concat_op_test.py"],
deps = [
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:sparse_tensor",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:sparse_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
tf_py_strict_test(
name = "sparse_conditional_accumulator_test",
size = "small",
srcs = ["sparse_conditional_accumulator_test.py"],
deps = [
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:dtypes",
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:indexed_slices",
"//tensorflow/python/framework:ops",
"//tensorflow/python/framework:tensor",
"//tensorflow/python/framework:tensor_shape",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:data_flow_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
tf_py_strict_test(
name = "sparse_cross_op_test",
size = "small",
srcs = ["sparse_cross_op_test.py"],
tags = ["no_windows"],
deps = [
"//tensorflow/python/client:session",
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:sparse_tensor",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:sparse_ops",
"//tensorflow/python/ops:sparse_ops_gen",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "sparse_matmul_op_test",
size = "medium",
srcs = ["sparse_matmul_op_test.py"],
tags = ["no_windows"],
deps = [
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:gradient_checker",
"//tensorflow/python/ops:math_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "sparse_ops_test",
size = "medium",
srcs = ["sparse_ops_test.py"],
shard_count = 5,
tags = [
"optonly", # b/77589990
],
deps = [
"//tensorflow/python/eager:def_function",
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:sparse_tensor",
"//tensorflow/python/framework:tensor_spec",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:gradient_checker",
"//tensorflow/python/ops:nn_ops",
"//tensorflow/python/ops:sparse_grad",
"//tensorflow/python/ops:sparse_ops",
"//tensorflow/python/ops:sparse_ops_gen",
"//tensorflow/python/ops:variables",
"//tensorflow/python/platform:client_testlib",
"//tensorflow/python/platform:test",
"//third_party/py/numpy",
],
)
tf_py_strict_test(
name = "sparse_reorder_op_test",
size = "small",
srcs = ["sparse_reorder_op_test.py"],
deps = [
"//tensorflow/python/eager:def_function",
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:sparse_tensor",
"//tensorflow/python/framework:tensor_spec",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:gradient_checker",
"//tensorflow/python/ops:sparse_grad",
"//tensorflow/python/ops:sparse_ops",
"//tensorflow/python/ops:sparse_ops_gen",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
"@absl_py//absl/testing:parameterized",
],
)
tf_py_strict_test(
name = "sparse_reshape_op_test",
size = "small",
srcs = ["sparse_reshape_op_test.py"],
deps = [
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:sparse_tensor",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:sparse_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
"@absl_py//absl/testing:parameterized",
],
)
tf_py_strict_test(
name = "sparse_serialization_ops_test",
size = "small",
srcs = ["sparse_serialization_ops_test.py"],
deps = [
"//tensorflow/python/eager:def_function",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:sparse_tensor",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:array_ops_stack",
"//tensorflow/python/ops:resource_variable_ops_gen",
"//tensorflow/python/ops:sparse_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
tf_py_strict_test(
name = "sparse_slice_op_test",
size = "small",
srcs = ["sparse_slice_op_test.py"],
deps = [
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:sparse_tensor",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:gradient_checker",
"//tensorflow/python/ops:sparse_grad",
"//tensorflow/python/ops:sparse_ops",
"//tensorflow/python/ops:sparse_ops_gen",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "sparse_split_op_test",
size = "small",
srcs = ["sparse_split_op_test.py"],
deps = [
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:sparse_tensor",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:sparse_ops",
"//tensorflow/python/ops:sparse_ops_gen",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "sparse_tensor_dense_matmul_grad_test",
size = "small",
srcs = ["sparse_tensor_dense_matmul_grad_test.py"],
xla_tags = [
"no_cuda_asan", # b/182392418 times out
],
deps = [
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:sparse_tensor",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:gradient_checker",
"//tensorflow/python/ops:sparse_grad",
"//tensorflow/python/ops:sparse_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
# Test name is shortened to workaround Windows Bazel's 259 char path limit.
name = "sparse_tensor_dense_matmul_op_d9m_test",
size = "medium",
srcs = ["sparse_tensor_dense_matmul_op_d9m_test.py"],
deps = [
"//tensorflow/python/framework:config",
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:sparse_tensor",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:sparse_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "sparse_tensor_dense_matmul_op_test",
size = "medium",
srcs = ["sparse_tensor_dense_matmul_op_test.py"],
deps = [
"//tensorflow/core:protos_all_py",
"//tensorflow/python/client:session",
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:sparse_tensor",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:math_ops",
"//tensorflow/python/ops:sparse_ops",
"//tensorflow/python/ops:while_loop",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
"@absl_py//absl:app",
],
)
tf_py_strict_test(
name = "sparse_tensors_map_ops_test",
size = "small",
srcs = ["sparse_tensors_map_ops_test.py"],
deps = [
"//tensorflow/python/client:session",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:sparse_tensor",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:array_ops_stack",
"//tensorflow/python/ops:sparse_ops",
"//tensorflow/python/ops:variables",
"//tensorflow/python/platform:benchmark",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "sparse_to_dense_op_py_test",
size = "small",
srcs = ["sparse_to_dense_op_py_test.py"],
deps = [
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:math_ops",
"//tensorflow/python/ops:sparse_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
"@absl_py//absl/testing:parameterized",
],
)
cuda_py_strict_test(
name = "sparse_xent_op_d9m_test",
size = "medium",
srcs = ["sparse_xent_op_d9m_test.py"],
tags = ["no_windows"], # Fails as SegmentSum is nondeterministic on Windows
xla_enable_strict_auto_jit = False,
deps = [
":sparse_xent_op_test_base",
"//tensorflow/python/eager:backprop",
"//tensorflow/python/framework:config",
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:dtypes",
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:nn_grad",
"//tensorflow/python/ops:nn_ops",
"//tensorflow/python/ops:nn_ops_gen",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "sparse_xent_op_test",
size = "small",
srcs = ["sparse_xent_op_test.py"],
deps = [
":sparse_xent_op_test_base",
"//tensorflow/core:protos_all_py",
"//tensorflow/python/client:session",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:array_ops_stack",
"//tensorflow/python/ops:gradients_impl",
"//tensorflow/python/ops:math_ops",
"//tensorflow/python/ops:nn_grad",
"//tensorflow/python/ops:sparse_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
"@absl_py//absl:app",
],
)
py_library(
name = "sparse_xent_op_test_base",
srcs = ["sparse_xent_op_test_base.py"],
strict_deps = True,
deps = [
"//tensorflow/python/eager:backprop",
"//tensorflow/python/eager:context",
"//tensorflow/python/framework:config",
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:dtypes",
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:gradient_checker_v2",
"//tensorflow/python/ops:nn_grad",
"//tensorflow/python/ops:nn_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
tf_py_strict_test(
name = "sparsemask_op_test",
size = "small",
srcs = ["sparsemask_op_test.py"],
deps = [
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:indexed_slices",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
@@ -0,0 +1,288 @@
# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for SparseAdd."""
import timeit
import numpy as np
from tensorflow.python.client import session
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import errors_impl
from tensorflow.python.framework import ops
from tensorflow.python.framework import sparse_tensor
from tensorflow.python.framework import test_util
from tensorflow.python.ops import gradient_checker
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import sparse_ops
import tensorflow.python.ops.sparse_grad # pylint: disable=unused-import
from tensorflow.python.platform import test
def _sparsify(x, thresh=0.5, index_dtype=np.int64):
x[x < thresh] = 0
non_zero = np.where(x)
x_indices = np.vstack(non_zero).astype(index_dtype).T
x_values = x[non_zero]
x_shape = x.shape
return sparse_tensor.SparseTensor(
indices=x_indices, values=x_values, dense_shape=x_shape), len(x_values)
class SparseAddTest(test.TestCase):
def _randomTensor(self, size, np_dtype, sparse=True):
n, m = size
x = np.random.randn(n, m).astype(np_dtype)
return _sparsify(x) if sparse else x
def _SparseTensorValue_3x3(self, negate=False):
# [ 1]
# [2 ]
# [3 4]
# ...or its cwise negation, if `negate`
ind = np.array([[0, 1], [1, 0], [2, 0], [2, 1]])
val = np.array([1, 2, 3, 4])
if negate:
val = -np.array([1, 2, 3, 4])
shape = np.array([3, 3])
return sparse_tensor.SparseTensorValue(
np.array(ind, np.int64),
np.array(val, np.float32), np.array(shape, np.int64))
def _SparseTensor_3x3(self, negate=False):
return sparse_tensor.SparseTensor.from_value(
self._SparseTensorValue_3x3(negate))
def _SparseTensor_3x3_v2(self):
# [ 1]
# [-1.9 ]
# [ 3 -4.2]
ind = np.array([[0, 1], [1, 0], [2, 0], [2, 1]])
val = np.array([1, -1.9, 3, -4.2])
shape = np.array([3, 3])
return sparse_tensor.SparseTensor(
constant_op.constant(ind, dtypes.int64),
constant_op.constant(val, dtypes.float32),
constant_op.constant(shape, dtypes.int64))
def testAddSelf(self):
with test_util.force_cpu():
for sp_a in (self._SparseTensorValue_3x3(), self._SparseTensor_3x3()):
for sp_b in (self._SparseTensorValue_3x3(), self._SparseTensor_3x3()):
sp_sum = sparse_ops.sparse_add(sp_a, sp_b)
self.assertAllEqual((3, 3), sp_sum.get_shape())
sum_out = self.evaluate(sp_sum)
self.assertEqual(sp_sum.dense_shape.get_shape(), [2])
self.assertAllEqual(sum_out.indices, [[0, 1], [1, 0], [2, 0], [2, 1]])
self.assertAllEqual(sum_out.values, [2, 4, 6, 8])
self.assertAllEqual(sum_out.dense_shape, [3, 3])
def testAddSelfAndNegation(self):
with test_util.force_cpu():
sp_a = self._SparseTensor_3x3()
sp_b = self._SparseTensor_3x3(negate=True)
sp_sum = sparse_ops.sparse_add(sp_a, sp_b, 0.1)
sum_out = self.evaluate(sp_sum)
self.assertEqual(sp_sum.dense_shape.get_shape(), [2])
self.assertAllEqual(sum_out.indices, np.empty([0, 2]))
self.assertAllEqual(sum_out.values, [])
self.assertAllEqual(sum_out.dense_shape, [3, 3])
def testSmallValuesShouldVanish(self):
with test_util.force_cpu():
sp_a = self._SparseTensor_3x3()
sp_b = self._SparseTensor_3x3_v2()
# sum:
# [ 2]
# [.1 ]
# [ 6 -.2]
# two values should vanish: |.1| < .21, and |-.2| < .21
sp_sum = sparse_ops.sparse_add(sp_a, sp_b, thresh=0.21)
sum_out = self.evaluate(sp_sum)
self.assertEqual(sp_sum.dense_shape.get_shape(), [2])
self.assertAllEqual(sum_out.indices, [[0, 1], [2, 0]])
self.assertAllEqual(sum_out.values, [2, 6])
self.assertAllEqual(sum_out.dense_shape, [3, 3])
# only .1 vanishes
sp_sum = sparse_ops.sparse_add(sp_a, sp_b, thresh=0.11)
sum_out = self.evaluate(sp_sum)
self.assertEqual(sp_sum.dense_shape.get_shape(), [2])
self.assertAllEqual(sum_out.indices, [[0, 1], [2, 0], [2, 1]])
self.assertAllClose(sum_out.values, [2, 6, -.2])
self.assertAllEqual(sum_out.dense_shape, [3, 3])
@test_util.run_deprecated_v1
def testGradients(self):
np.random.seed(1618) # Make it reproducible.
with self.session(use_gpu=False):
for n in [10, 31]:
for m in [4, 17]:
sp_a, nnz_a = self._randomTensor([n, m], np.float32)
sp_b, nnz_b = self._randomTensor([n, m], np.float32)
sp_sum = sparse_ops.sparse_add(sp_a, sp_b)
nnz_sum = len(self.evaluate(sp_sum.values))
err = gradient_checker.compute_gradient_error(
[sp_a.values, sp_b.values], [(nnz_a,), (nnz_b,)], sp_sum.values,
(nnz_sum,))
self.assertLess(err, 1e-3)
def testAddSparseDense(self):
np.random.seed(1618) # Make it reproducible.
n, m = np.random.randint(30, size=2)
for dtype in [np.float32, np.float64, np.int64, np.complex64]:
for index_dtype in [np.int32, np.int64]:
rand_vals_np = np.random.randn(n, m).astype(dtype)
dense_np = np.random.randn(n, m).astype(dtype)
with test_util.force_cpu():
sparse, unused_nnz = _sparsify(rand_vals_np, index_dtype=index_dtype)
s = self.evaluate(
sparse_ops.sparse_add(sparse, constant_op.constant(dense_np)))
self.assertAllEqual(dense_np + rand_vals_np, s)
self.assertTrue(s.dtype == dtype)
# check commutativity
s = self.evaluate(
sparse_ops.sparse_add(constant_op.constant(dense_np), sparse))
self.assertAllEqual(dense_np + rand_vals_np, s)
self.assertTrue(s.dtype == dtype)
@test_util.run_deprecated_v1
def testSparseTensorDenseAddGradients(self):
np.random.seed(1618) # Make it reproducible.
n, m = np.random.randint(30, size=2)
rand_vals_np = np.random.randn(n, m).astype(np.float32)
dense_np = np.random.randn(n, m).astype(np.float32)
with self.session(use_gpu=False):
sparse, nnz = _sparsify(rand_vals_np)
dense = constant_op.constant(dense_np, dtype=dtypes.float32)
s = sparse_ops.sparse_add(sparse, dense)
err = gradient_checker.compute_gradient_error([sparse.values, dense],
[(nnz,), (n, m)], s, (n, m))
self.assertLess(err, 1e-3)
def testInvalidSparseTensor(self):
with test_util.force_cpu():
shape = [2, 2]
val = [0]
dense = constant_op.constant(np.zeros(shape, dtype=np.int32))
for bad_idx in [
[[-1, 0]], # -1 is invalid.
[[1, 3]], # ...so is 3.
]:
sparse = sparse_tensor.SparseTensorValue(bad_idx, val, shape)
with self.assertRaisesRegex(
(ValueError, errors_impl.InvalidArgumentError), "invalid index"):
s = sparse_ops.sparse_add(sparse, dense)
self.evaluate(s)
def _testSparseDenseInvalidInputs(self,
a_indices,
a_values,
a_shape,
b,
expected_error=""):
# Public API call to sparse-dense add.
with self.assertRaisesRegex((ValueError, errors_impl.InvalidArgumentError),
expected_error):
a = sparse_tensor.SparseTensor(a_indices, a_values, a_shape)
self.evaluate(sparse_ops.sparse_add(a, b))
# Directly call generated kernel, by-passing SparseTensor validation.
with self.assertRaisesRegex((ValueError, errors_impl.InvalidArgumentError),
expected_error):
self.evaluate(
sparse_ops.gen_sparse_ops.sparse_tensor_dense_add(
a_indices, a_values, a_shape, b))
def testSparseDenseInvalidInputs(self):
self._testSparseDenseInvalidInputs(
a_indices=constant_op.constant(0, shape=[17, 2], dtype=dtypes.int64),
a_values=constant_op.constant(0, shape=[5], dtype=dtypes.float32),
a_shape=constant_op.constant([3, 4], dtype=dtypes.int64),
b=constant_op.constant(1, shape=[3, 4], dtype=dtypes.float32),
expected_error="Dimensions 17 and 5 are not compatible")
self._testSparseDenseInvalidInputs(
a_indices=constant_op.constant(0, shape=[17, 4], dtype=dtypes.int64),
a_values=constant_op.constant(0, shape=[17], dtype=dtypes.float32),
a_shape=constant_op.constant([3, 4], dtype=dtypes.int64),
b=constant_op.constant(1, shape=[3, 4], dtype=dtypes.float32),
expected_error="Dimensions 4 and 2 are not compatible")
self._testSparseDenseInvalidInputs(
a_indices=constant_op.constant(7, shape=[17, 2], dtype=dtypes.int64),
a_values=constant_op.constant(0, shape=[17], dtype=dtypes.float32),
a_shape=constant_op.constant([3, 4], dtype=dtypes.int64),
b=constant_op.constant(1, shape=[3, 4], dtype=dtypes.float32),
expected_error="invalid index")
######################## Benchmarking code
def _s2d_add_vs_sparse_add(sparsity, n, m, num_iters=50):
np.random.seed(1618)
with session.Session(graph=ops.Graph()) as sess:
sp_vals = np.random.rand(n, m).astype(np.float32)
sp_t, unused_nnz = _sparsify(sp_vals, thresh=sparsity, index_dtype=np.int32)
vals = np.random.rand(n, m).astype(np.float32)
s2d = math_ops.add(
sparse_ops.sparse_tensor_to_dense(sp_t), constant_op.constant(vals))
sa = sparse_ops.sparse_add(sp_t, constant_op.constant(vals))
timeit.timeit(lambda: sess.run(s2d), number=3)
timeit.timeit(lambda: sess.run(sa), number=3)
s2d_total = timeit.timeit(lambda: sess.run(s2d), number=num_iters)
sa_total = timeit.timeit(lambda: sess.run(sa), number=num_iters)
# per-iter latency; secs to millis
return s2d_total * 1e3 / num_iters, sa_total * 1e3 / num_iters
class SparseAddBenchmark(test.Benchmark):
def benchmarkSparseAddDense(self):
print("SparseAddDense: add with sparse_to_dense vs. sparse_add")
print("%nnz \t n \t m \t millis(s2d) \t millis(sparse_add) \t speedup")
for sparsity in [0.99, 0.5, 0.01]:
for n in [1, 256, 50000]:
for m in [100, 1000]:
s2d_dt, sa_dt = _s2d_add_vs_sparse_add(sparsity, n, m)
print("%.2f \t %d \t %d \t %.4f \t %.4f \t %.2f" % (sparsity, n, m,
s2d_dt, sa_dt,
s2d_dt / sa_dt))
if __name__ == "__main__":
test.main()
@@ -0,0 +1,417 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for SparseConcat."""
import numpy as np
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import sparse_tensor
from tensorflow.python.framework import test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import sparse_ops
from tensorflow.python.platform import test
class SparseConcatTest(test.TestCase):
def _SparseTensor_UnknownShape(self,
ind_shape=None,
val_shape=None,
shape_shape=None):
return sparse_tensor.SparseTensor(
array_ops.placeholder(
dtypes.int64, shape=ind_shape),
array_ops.placeholder(
dtypes.float32, shape=val_shape),
array_ops.placeholder(
dtypes.int64, shape=shape_shape))
def _SparseTensorValue_3x3(self):
# [ 1]
# [2 ]
# [3 4]
ind = np.array([[0, 2], [1, 0], [2, 0], [2, 2]])
val = np.array([1, 2, 3, 4])
shape = np.array([3, 3])
return sparse_tensor.SparseTensorValue(
np.array(ind, np.int64),
np.array(val, np.float32), np.array(shape, np.int64))
def _SparseTensor_3x3(self):
return sparse_tensor.SparseTensor.from_value(self._SparseTensorValue_3x3())
def _SparseTensorValue_3x5(self):
# [ ]
# [ 1 ]
# [2 1 0]
ind = np.array([[1, 1], [2, 0], [2, 3], [2, 4]])
val = np.array([1, 2, 1, 0])
shape = np.array([3, 5])
return sparse_tensor.SparseTensorValue(
np.array(ind, np.int64),
np.array(val, np.float32), np.array(shape, np.int64))
def _SparseTensor_3x5(self):
return sparse_tensor.SparseTensor.from_value(self._SparseTensorValue_3x5())
def _SparseTensor_3x2(self):
# [ ]
# [1 ]
# [2 ]
ind = np.array([[1, 0], [2, 0]])
val = np.array([1, 2])
shape = np.array([3, 2])
return sparse_tensor.SparseTensor(
constant_op.constant(ind, dtypes.int64),
constant_op.constant(val, dtypes.float32),
constant_op.constant(shape, dtypes.int64))
def _SparseTensor_2x3(self):
# [ 1 ]
# [1 2]
ind = np.array([[0, 1], [1, 0], [1, 2]])
val = np.array([1, 1, 2])
shape = np.array([2, 3])
return sparse_tensor.SparseTensor(
constant_op.constant(ind, dtypes.int64),
constant_op.constant(val, dtypes.float32),
constant_op.constant(shape, dtypes.int64))
def _SparseTensor_2x3x4(self):
ind = np.array([
[0, 0, 1],
[0, 1, 0], [0, 1, 2],
[1, 0, 3],
[1, 1, 1], [1, 1, 3],
[1, 2, 2]])
val = np.array([1, 10, 12, 103, 111, 113, 122])
shape = np.array([2, 3, 4])
return sparse_tensor.SparseTensor(
constant_op.constant(ind, dtypes.int64),
constant_op.constant(val, dtypes.float32),
constant_op.constant(shape, dtypes.int64))
def _SparseTensor_NoNonZeros(self, dense_shape):
ind = np.empty(shape=(0, len(dense_shape)))
val = np.array([])
shape = np.array(dense_shape)
return sparse_tensor.SparseTensor(
constant_op.constant(ind, dtypes.int64),
constant_op.constant(val, dtypes.float32),
constant_op.constant(shape, dtypes.int64))
def _SparseTensor_String3x3(self):
# [ a]
# [b ]
# [c d]
ind = np.array([[0, 2], [1, 0], [2, 0], [2, 2]])
val = np.array(["a", "b", "c", "d"])
shape = np.array([3, 3])
return sparse_tensor.SparseTensor(
constant_op.constant(ind, dtypes.int64),
constant_op.constant(val, dtypes.string),
constant_op.constant(shape, dtypes.int64))
def _SparseTensor_String3x5(self):
# [ ]
# [ e ]
# [f g h]
ind = np.array([[1, 1], [2, 0], [2, 3], [2, 4]])
val = np.array(["e", "f", "g", "h"])
shape = np.array([3, 5])
return sparse_tensor.SparseTensor(
constant_op.constant(ind, dtypes.int64),
constant_op.constant(val, dtypes.string),
constant_op.constant(shape, dtypes.int64))
def testConcat1(self):
with self.session() as sess:
# concat(A):
# [ 1]
# [2 ]
# [3 4]
for sp_a in (self._SparseTensorValue_3x3(), self._SparseTensor_3x3()):
# Note that we ignore concat_dim in this case since we short-circuit the
# single-input case in python.
for concat_dim in (-2000, 1, 2000):
sp_concat = sparse_ops.sparse_concat(concat_dim, [sp_a])
self.assertEqual(sp_concat.indices.get_shape(), [4, 2])
self.assertEqual(sp_concat.values.get_shape(), [4])
self.assertEqual(sp_concat.dense_shape.get_shape(), [2])
concat_out = self.evaluate(sp_concat)
self.assertAllEqual(concat_out.indices,
[[0, 2], [1, 0], [2, 0], [2, 2]])
self.assertAllEqual(concat_out.values, [1, 2, 3, 4])
self.assertAllEqual(concat_out.dense_shape, [3, 3])
def testConcat2(self):
with self.session() as sess:
# concat(A, B):
# [ 1 ]
# [2 1 ]
# [3 4 2 1 0]
for sp_a in (self._SparseTensorValue_3x3(), self._SparseTensor_3x3()):
for sp_b in (self._SparseTensorValue_3x5(), self._SparseTensor_3x5()):
for concat_dim in (-1, 1):
sp_concat = sparse_ops.sparse_concat(concat_dim, [sp_a, sp_b])
self.assertEqual(sp_concat.indices.get_shape(), [8, 2])
self.assertEqual(sp_concat.values.get_shape(), [8])
self.assertEqual(sp_concat.dense_shape.get_shape(), [2])
concat_out = self.evaluate(sp_concat)
self.assertAllEqual(concat_out.indices, [[0, 2], [1, 0], [1, 4],
[2, 0], [2, 2], [2, 3],
[2, 6], [2, 7]])
self.assertAllEqual(concat_out.values, [1, 2, 1, 3, 4, 2, 1, 0])
self.assertAllEqual(concat_out.dense_shape, [3, 8])
def testConcatDim0(self):
with self.session() as sess:
# concat(A, D):
# [ 1]
# [2 ]
# [3 4]
# [ 1 ]
# [1 2]
sp_a = self._SparseTensor_3x3()
sp_d = self._SparseTensor_2x3()
for concat_dim in (-2, 0):
sp_concat = sparse_ops.sparse_concat(concat_dim, [sp_a, sp_d])
self.assertEqual(sp_concat.indices.get_shape(), [7, 2])
self.assertEqual(sp_concat.values.get_shape(), [7])
self.assertEqual(sp_concat.dense_shape.get_shape(), [2])
concat_out = self.evaluate(sp_concat)
self.assertAllEqual(
concat_out.indices,
[[0, 2], [1, 0], [2, 0], [2, 2], [3, 1], [4, 0], [4, 2]])
self.assertAllEqual(concat_out.values, np.array([1, 2, 3, 4, 1, 1, 2]))
self.assertAllEqual(concat_out.dense_shape, np.array([5, 3]))
def testConcat3(self):
with self.session() as sess:
# concat(A, B, C):
# [ 1 ]
# [2 1 1 ]
# [3 4 2 1 0 2 ]
sp_a = self._SparseTensor_3x3()
sp_b = self._SparseTensor_3x5()
sp_c = self._SparseTensor_3x2()
for concat_dim in (-1, 1):
sp_concat = sparse_ops.sparse_concat(concat_dim, [sp_a, sp_b, sp_c])
self.assertEqual(sp_concat.indices.get_shape(), [10, 2])
self.assertEqual(sp_concat.values.get_shape(), [10])
self.assertEqual(sp_concat.dense_shape.get_shape(), [2])
concat_out = self.evaluate(sp_concat)
self.assertAllEqual(concat_out.indices, [[0, 2], [1, 0], [1, 4], [1, 8],
[2, 0], [2, 2], [2, 3], [2, 6],
[2, 7], [2, 8]])
self.assertAllEqual(concat_out.values, [1, 2, 1, 1, 3, 4, 2, 1, 0, 2])
self.assertAllEqual(concat_out.dense_shape, [3, 10])
def testConcatNoNonZeros(self):
sp_a = self._SparseTensor_NoNonZeros((2, 3, 4))
sp_b = self._SparseTensor_NoNonZeros((2, 7, 4))
sp_c = self._SparseTensor_NoNonZeros((2, 5, 4))
with self.session() as sess:
concat_dim = 1
sp_concat = sparse_ops.sparse_concat(concat_dim, [sp_a, sp_b, sp_c])
self.assertEqual(sp_concat.indices.get_shape(), [0, 3])
self.assertEqual(sp_concat.values.get_shape(), [0])
self.assertEqual(sp_concat.dense_shape.get_shape(), [3])
concat_out = self.evaluate(sp_concat)
self.assertEqual(concat_out.indices.shape, (0, 3))
self.assertEqual(concat_out.values.shape, (0,))
self.assertAllEqual(concat_out.dense_shape, [2, 15, 4])
def testConcatSomeNoNonZeros(self):
sp_a = self._SparseTensor_NoNonZeros((2, 7, 4))
sp_b = self._SparseTensor_2x3x4()
sp_c = self._SparseTensor_NoNonZeros((2, 5, 4))
output_nnz = sp_b.indices.get_shape()[0]
with self.session() as sess:
concat_dim = 1
sp_concat = sparse_ops.sparse_concat(concat_dim, [sp_a, sp_b, sp_c])
self.assertEqual(sp_concat.indices.get_shape(), [output_nnz, 3])
self.assertEqual(sp_concat.values.get_shape(), [output_nnz])
self.assertEqual(sp_concat.dense_shape.get_shape(), [3])
concat_out = self.evaluate(sp_concat)
self.assertAllEqual(concat_out.indices,
sp_b.indices + [0, sp_a.dense_shape[1], 0])
self.assertAllEqual(concat_out.values, sp_b.values)
self.assertAllEqual(concat_out.dense_shape, [2, 15, 4])
def testConcatNonNumeric(self):
with self.session(use_gpu=False) as sess:
# concat(A, B):
# [ a ]
# [b e ]
# [c d f g h]
sp_a = self._SparseTensor_String3x3()
sp_b = self._SparseTensor_String3x5()
for concat_dim in (-1, 1):
sp_concat = sparse_ops.sparse_concat(concat_dim, [sp_a, sp_b])
self.assertEqual(sp_concat.indices.get_shape(), [8, 2])
self.assertEqual(sp_concat.values.get_shape(), [8])
self.assertEqual(sp_concat.dense_shape.get_shape(), [2])
concat_out = self.evaluate(sp_concat)
self.assertAllEqual(
concat_out.indices,
[[0, 2], [1, 0], [1, 4], [2, 0], [2, 2], [2, 3], [2, 6], [2, 7]])
self.assertAllEqual(concat_out.values,
[b"a", b"b", b"e", b"c", b"d", b"f", b"g", b"h"])
self.assertAllEqual(concat_out.dense_shape, [3, 8])
@test_util.run_deprecated_v1
def testMismatchedRank(self):
with self.session():
sp_a = self._SparseTensor_3x3()
sp_e = self._SparseTensor_2x3x4()
# Rank mismatches can be caught at shape-inference time
for concat_dim in (-1, 1):
with self.assertRaises(ValueError):
sparse_ops.sparse_concat(concat_dim, [sp_a, sp_e])
@test_util.run_deprecated_v1
def testMismatchedRankExpandNonconcatDim(self):
with self.session():
sp_a = self._SparseTensor_3x3()
sp_e = self._SparseTensor_2x3x4()
# Rank mismatches should be caught at shape-inference time, even for
# expand_nonconcat_dim=True.
for concat_dim in (-1, 1):
with self.assertRaises(ValueError):
sparse_ops.sparse_concat(
concat_dim, [sp_a, sp_e], expand_nonconcat_dim=True)
@test_util.run_deprecated_v1
def testMismatchedShapes(self):
with self.session() as sess:
sp_a = self._SparseTensor_3x3()
sp_b = self._SparseTensor_3x5()
sp_c = self._SparseTensor_3x2()
sp_d = self._SparseTensor_2x3()
for concat_dim in (-1, 1):
sp_concat = sparse_ops.sparse_concat(concat_dim,
[sp_a, sp_b, sp_c, sp_d])
# Shape mismatches can only be caught when the op is run
with self.assertRaisesOpError("Input shapes must match"):
self.evaluate(sp_concat)
def testMismatchedShapesExpandNonconcatDim(self):
with self.session() as sess:
sp_a = self._SparseTensor_3x3()
sp_b = self._SparseTensor_3x5()
sp_c = self._SparseTensor_3x2()
sp_d = self._SparseTensor_2x3()
for concat_dim0 in (-2, 0):
for concat_dim1 in (-1, 1):
sp_concat_dim0 = sparse_ops.sparse_concat(
concat_dim0, [sp_a, sp_b, sp_c, sp_d], expand_nonconcat_dim=True)
sp_concat_dim1 = sparse_ops.sparse_concat(
concat_dim1, [sp_a, sp_b, sp_c, sp_d], expand_nonconcat_dim=True)
sp_concat_dim0_out = self.evaluate(sp_concat_dim0)
sp_concat_dim1_out = self.evaluate(sp_concat_dim1)
self.assertAllEqual(sp_concat_dim0_out.indices,
[[0, 2], [1, 0], [2, 0], [2, 2], [4, 1], [5, 0],
[5, 3], [5, 4], [7, 0], [8, 0], [9, 1], [10, 0],
[10, 2]])
self.assertAllEqual(sp_concat_dim0_out.values,
[1, 2, 3, 4, 1, 2, 1, 0, 1, 2, 1, 1, 2])
self.assertAllEqual(sp_concat_dim0_out.dense_shape, [11, 5])
self.assertAllEqual(sp_concat_dim1_out.indices,
[[0, 2], [0, 11], [1, 0], [1, 4], [1, 8], [1, 10],
[1, 12], [2, 0], [2, 2], [2, 3], [2, 6], [2, 7],
[2, 8]])
self.assertAllEqual(sp_concat_dim1_out.values,
[1, 1, 2, 1, 1, 1, 2, 3, 4, 2, 1, 0, 2])
self.assertAllEqual(sp_concat_dim1_out.dense_shape, [3, 13])
@test_util.run_deprecated_v1
def testShapeInferenceUnknownShapes(self):
with self.session():
sp_inputs = [
self._SparseTensor_UnknownShape(),
self._SparseTensor_UnknownShape(val_shape=[3]),
self._SparseTensor_UnknownShape(ind_shape=[1, 3]),
self._SparseTensor_UnknownShape(shape_shape=[3])
]
for concat_dim in (-2, 0):
sp_concat = sparse_ops.sparse_concat(concat_dim, sp_inputs)
self.assertEqual(sp_concat.indices.get_shape().as_list(), [None, 3])
self.assertEqual(sp_concat.values.get_shape().as_list(), [None])
self.assertEqual(sp_concat.dense_shape.get_shape(), [3])
def testConcatShape(self):
# Test case for GitHub 21964.
x = sparse_tensor.SparseTensor(
indices=[[0, 0], [1, 1]], values=[1, 2], dense_shape=[2, 2])
y = sparse_tensor.SparseTensor(
indices=[[0, 0], [1, 1]], values=[1, 2], dense_shape=[2, 2])
z = sparse_ops.sparse_concat(-1, [x, y])
self.assertEqual(z.get_shape().as_list(), [2, 4])
@test_util.run_deprecated_v1
def testDivisionByZeroMalformedShape(self):
with self.session():
sp1 = sparse_tensor.SparseTensor(
indices=[[0, 0]], values=[1.0], dense_shape=[0, 1]
)
sp2 = sparse_tensor.SparseTensor(
indices=[[0, 0]], values=[2.0], dense_shape=[0, 1]
)
concat_op = sparse_ops.sparse_concat(axis=1, sp_inputs=[sp1, sp2])
with self.assertRaisesOpError(
"SparseTensor cannot have non-zero indices if dense shape has zero"
" volume"
):
self.evaluate(concat_op)
if __name__ == "__main__":
test.main()
@@ -0,0 +1,700 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
import time
import numpy as np
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes as dtypes_lib
from tensorflow.python.framework import errors_impl
from tensorflow.python.framework import indexed_slices
from tensorflow.python.framework import ops
from tensorflow.python.framework import tensor
from tensorflow.python.framework import tensor_shape
from tensorflow.python.framework import test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import data_flow_ops
from tensorflow.python.platform import test
def _indexedslice(x, noshape=False):
x = np.array(x)
dense_shape = x.shape
ndim = len(dense_shape)
indices = np.where(np.sum(x, tuple(range(1, ndim))))[0]
values = x[indices]
if noshape:
dense_shape = None
return indexed_slices.IndexedSlices(
indices=indices.tolist(), values=values, dense_shape=dense_shape)
class IndexedSlicesConditionalAccumulatorTest(test.TestCase):
def _assertEqual_indexedslices(self, expected_tensor, result):
self.assertAllEqual(expected_tensor.indices, result.indices)
self.assertAllEqual(expected_tensor.values, result.values)
if (result.dense_shape is not None and
expected_tensor.dense_shape is not None):
self.assertAllEqual(expected_tensor.dense_shape, result.dense_shape)
def _assertEqual_nparray(self, expected_array, result, sess):
expected_tensor = _indexedslice(expected_array)
self._assertEqual_indexedslices(expected_tensor, result)
def testConstructor(self):
with ops.Graph().as_default():
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q")
self.assertTrue(isinstance(q.accumulator_ref, tensor.Tensor))
self.assertProtoEquals(
"""
name:'Q' op:'SparseConditionalAccumulator'
attr { key: 'dtype' value { type: DT_FLOAT } }
attr { key: 'shape' value { shape { unknown_rank: true} } }
attr { key: 'container' value { s: '' } }
attr { key: 'shared_name' value { s: '' } }
attr { key: 'reduction_type' value {s: 'MEAN'} }
""", q.accumulator_ref.op.node_def)
def testConstructorWithInvalidArg(self):
with ops.Graph().as_default():
with self.assertRaises(ValueError):
data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", reduction_type="Invalid")
def testConstructorWithShape(self):
with ops.Graph().as_default():
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32,
name="Q",
shape=tensor_shape.TensorShape([1, 5, 2, 8]))
self.assertTrue(isinstance(q.accumulator_ref, tensor.Tensor))
self.assertProtoEquals(
"""
name:'Q' op:'SparseConditionalAccumulator'
attr { key: 'dtype' value { type: DT_FLOAT } }
attr { key: 'shape' value { shape { dim {size: 1 }
dim {size: 5 }
dim {size: 2 }
dim {size: 8 }
} } }
attr { key: 'container' value { s: '' } }
attr { key: 'shared_name' value { s: '' } }
attr { key: 'reduction_type' value {s: 'MEAN'} }
""", q.accumulator_ref.op.node_def)
@test_util.run_deprecated_v1
def testAccumulatorSizeEmpty(self):
with self.cached_session():
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q")
self.assertEqual(q.num_accumulated().eval(), 0)
@test_util.run_deprecated_v1
def testAccumulatorSetGlobalStep(self):
with self.cached_session():
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=tensor_shape.TensorShape([1]))
set_global_step_op = q.set_global_step(1)
set_global_step_op.run()
@test_util.run_deprecated_v1
def testAccumulatorApplyGradFloat32(self):
with self.cached_session():
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=tensor_shape.TensorShape([3, 3]))
accum_op = q.apply_indexed_slices_grad(
indexed_slices.IndexedSlices(
indices=[0, 2],
values=np.array([[0, 0, 1], [3, 0, 4]]).astype(np.float32)))
accum_op.run()
self.assertEqual(q.num_accumulated().eval(), 1)
@test_util.run_deprecated_v1
def testDtypes(self):
with self.cached_session() as sess:
dtypes = [dtypes_lib.float16, dtypes_lib.float32, dtypes_lib.float64]
for i in range(len(dtypes)):
dtype = dtypes[i]
q = data_flow_ops.SparseConditionalAccumulator(
dtype, shape=tensor_shape.TensorShape([3, 3, 3]))
elems = np.arange(2)
sum_elems = np.zeros([3, 3, 3]).astype(dtype.as_numpy_dtype)
for e in elems:
mat_to_add = np.zeros([3, 3, 3]).astype(dtype.as_numpy_dtype)
mat_to_add[i, i, i] = e + 1
sum_elems += mat_to_add
t = _indexedslice(mat_to_add)
q.apply_indexed_slices_grad(t).run()
result = self.evaluate(q.take_indexed_slices_grad(1))
self._assertEqual_nparray(sum_elems / len(elems), result, sess)
@test_util.run_deprecated_v1
def testAccumulatorMultipleAccumulators(self):
with self.cached_session() as sess:
q_f32_0 = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=tensor_shape.TensorShape([2, 2]))
q_f32_1 = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=tensor_shape.TensorShape([2, 2]))
q_f16_0 = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float16, name="Q", shape=tensor_shape.TensorShape([2, 2]))
q_f16_1 = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float16, name="Q", shape=tensor_shape.TensorShape([2, 2]))
accums = [q_f16_0, q_f16_1, q_f32_0, q_f32_1]
elems = [[[1, 0], [0, 0]], [[0, 1], [0, 0]], [[0, 0], [1, 0]], [[0, 0],
[0, 1]]]
expected_tensors = []
for i in range(len(accums)):
tensor_to_add = np.array(elems[i]).astype(accums[i]
.dtype.as_numpy_dtype)
expected_tensor = _indexedslice(tensor_to_add)
expected_tensors.append(expected_tensor)
st = _indexedslice(tensor_to_add)
accums[i].apply_indexed_slices_grad(st).run()
for i in range(len(accums)):
result = sess.run(accums[i].take_indexed_slices_grad(1))
self._assertEqual_indexedslices(expected_tensors[i], result)
@test_util.run_deprecated_v1
def testAccumulatorTakeGradMean(self):
with self.cached_session() as sess:
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=())
grad_indexed_slices = indexed_slices.IndexedSlices(
indices=[0, 1], values=np.array([[1, 0], [0, 2]]).astype(np.float32))
accum_op = q.apply_indexed_slices_grad(grad_indexed_slices)
accum_op.run()
accum_op = q.apply_grad([0, 2],
np.array([[0, 1], [3, 0]]).astype(np.float32),
[3, 2])
accum_op.run()
takeg_t = q.take_indexed_slices_grad(1)
val = self.evaluate(takeg_t)
self.assertAllEqual([0, 1, 2], val.indices)
self.assertAllEqual([[0.5, 0.5], [0, 2], [3, 0]], val.values)
self.assertAllEqual([-1, 2], val.dense_shape)
@test_util.run_deprecated_v1
def testAccumulatorTakeGradSum(self):
with self.cached_session() as sess:
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=(), reduction_type="SUM")
grad_indexed_slices = indexed_slices.IndexedSlices(
indices=[0, 1], values=np.array([[1, 0], [0, 2]]).astype(np.float32))
accum_op = q.apply_indexed_slices_grad(grad_indexed_slices)
accum_op.run()
accum_op = q.apply_grad([0, 2],
np.array([[0, 1], [3, 0]]).astype(np.float32),
[3, 2])
accum_op.run()
takeg_t = q.take_indexed_slices_grad(1)
val = self.evaluate(takeg_t)
self.assertAllEqual([0, 1, 2], val.indices)
self.assertAllEqual([[1, 1], [0, 2], [3, 0]], val.values)
self.assertAllEqual([-1, 2], val.dense_shape)
@test_util.run_deprecated_v1
def testAccumulatorTakeGradInvalidReductionType(self):
with self.assertRaises(ValueError):
data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=(), reduction_type="Invalid")
@test_util.run_deprecated_v1
def testAccumulatorRepeatedTakeGrad(self):
with self.cached_session() as sess:
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=())
grad_indexed_slices = indexed_slices.IndexedSlices(
indices=[0, 1], values=np.array([[1, 0], [0, 2]]).astype(np.float32))
accum_op = q.apply_indexed_slices_grad(grad_indexed_slices, local_step=0)
accum_op.run()
accum_op = q.apply_grad(
[0, 2],
np.array([[0, 1], [3, 0]]).astype(np.float32), [3, 2],
local_step=0)
accum_op.run()
takeg_t = q.take_indexed_slices_grad(1)
val = self.evaluate(takeg_t)
self.assertAllEqual(val.indices, [0, 1, 2])
self.assertAllEqual(val.values, [[0.5, 0.5], [0, 2], [3, 0]])
self.assertAllEqual(val.dense_shape, [-1, 2])
grad_indexed_slices = indexed_slices.IndexedSlices(
indices=[0, 1],
values=np.array([[10, 0], [0, 20]]).astype(np.float32))
accum_op = q.apply_indexed_slices_grad(grad_indexed_slices, local_step=1)
accum_op.run()
accum_op = q.apply_grad(
[0, 2],
np.array([[0, 10], [30, 0]]).astype(np.float32), [3, 2],
local_step=1)
accum_op.run()
takeg_t = q.take_indexed_slices_grad(1)
val = self.evaluate(takeg_t)
self.assertAllEqual(val.indices, [0, 1, 2])
self.assertAllEqual(val.values, [[5, 5], [0, 20], [30, 0]])
self.assertAllEqual(val.dense_shape, [-1, 2])
@test_util.run_v1_only("b/120545219")
def testParallelApplyGradMean(self):
# We need each thread to keep its own device stack or the device scopes
# won't be properly nested.
ops.get_default_graph().switch_to_thread_local()
with self.cached_session() as sess:
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=tensor_shape.TensorShape([2, 2]))
elems = [10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0]
accum_ops = []
for x in elems:
x = _indexedslice(np.array([[x, 0], [0, x]]).astype(np.float32))
accum_ops.append(q.apply_indexed_slices_grad(x, local_step=0))
takeg_t = q.take_indexed_slices_grad(1)
def apply_indexed_slices_grad(accum_op):
self.evaluate(accum_op)
threads = [
self.checkedThread(
target=apply_indexed_slices_grad, args=(o,)) for o in accum_ops
]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
val = self.evaluate(takeg_t)
expected_val = sum(elems) / len(elems)
self._assertEqual_nparray(
np.array([[expected_val, 0], [0, expected_val]]).astype(np.float32),
val, sess)
@test_util.run_v1_only("b/120545219")
def testParallelApplyGradSum(self):
# We need each thread to keep its own device stack or the device scopes
# won't be properly nested.
ops.get_default_graph().switch_to_thread_local()
with self.cached_session() as sess:
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32,
name="Q",
shape=tensor_shape.TensorShape([2, 2]),
reduction_type="SUM")
elems = [10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0]
accum_ops = []
for x in elems:
x = _indexedslice(np.array([[x, 0], [0, x]]).astype(np.float32))
accum_ops.append(q.apply_indexed_slices_grad(x, local_step=0))
takeg_t = q.take_indexed_slices_grad(1)
def apply_indexed_slices_grad(accum_op):
self.evaluate(accum_op)
threads = [
self.checkedThread(target=apply_indexed_slices_grad, args=(o,))
for o in accum_ops
]
for thread in threads:
thread.start()
for thread in threads:
thread.join()
val = self.evaluate(takeg_t)
expected_val = 550.0
self._assertEqual_nparray(
np.array([[expected_val, 0], [0, expected_val]]).astype(np.float32),
val, sess)
@test_util.run_v1_only("b/120545219")
def testParallelTakeGrad(self):
# We need each thread to keep its own device stack or the device scopes
# won't be properly nested.
ops.get_default_graph().switch_to_thread_local()
with self.cached_session() as sess:
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=tensor_shape.TensorShape([2, 2]))
elems = [e + 1 for e in range(10)]
accum_ops = []
for e in elems:
v = _indexedslice(np.array([[0, 0], [e, 0]]).astype(np.float32))
accum_ops.append(q.apply_indexed_slices_grad(v, local_step=e - 1))
takeg_t = q.take_indexed_slices_grad(1)
results = []
def apply_indexed_slices_grad():
for accum_op in accum_ops:
time.sleep(1.0)
self.evaluate(accum_op)
apply_indexed_slices_grad_thread = self.checkedThread(
target=apply_indexed_slices_grad)
def take_grad():
t = self.evaluate(takeg_t)
results.append(t)
threads = [self.checkedThread(target=take_grad) for _ in range(10)]
for thread in threads:
thread.start()
apply_indexed_slices_grad_thread.start()
for thread in threads:
thread.join()
apply_indexed_slices_grad_thread.join()
for i in range(len(accum_ops)):
self._assertEqual_nparray(
np.array([[0, 0], [elems[i], 0]]), results[i], sess)
@test_util.run_v1_only("b/120545219")
def testAccumulatorApplyAndBlockingTake(self):
# We need each thread to keep its own device stack or the device scopes
# won't be properly nested.
ops.get_default_graph().switch_to_thread_local()
with self.cached_session() as sess:
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=tensor_shape.TensorShape([2, 2]))
elems = [10.0, 20.0, 30.0]
elems_ave = sum(elems) / len(elems)
accum_ops = []
for x in elems:
x = _indexedslice(np.array([[0, x], [0, 0]]).astype(np.float32))
accum_ops.append(q.apply_indexed_slices_grad(x, local_step=0))
takeg_t = q.take_indexed_slices_grad(3)
results = []
def apply_indexed_slices_grad():
for accum_op in accum_ops:
self.evaluate(accum_op)
def take_grad():
results.append(self.evaluate(takeg_t))
accum_thread = self.checkedThread(target=apply_indexed_slices_grad)
takeg_thread = self.checkedThread(target=take_grad)
accum_thread.start()
takeg_thread.start()
accum_thread.join()
takeg_thread.join()
self._assertEqual_nparray([[0, elems_ave], [0, 0]], results[0], sess)
def _blocking_takeg(self, sess, takeg_op):
with self.assertRaisesOpError("was cancelled"):
self.evaluate(takeg_op)
@test_util.run_v1_only("b/120545219")
def testAccumulatorCancel(self):
# We need each thread to keep its own device stack or the device scopes
# won't be properly nested.
ops.get_default_graph().switch_to_thread_local()
with self.cached_session() as sess:
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32,
name="Q",
shape=tensor_shape.TensorShape([1, 2, 3]))
takeg_t = q.take_indexed_slices_grad(1)
takeg_thread = self.checkedThread(
self._blocking_takeg, args=(sess, takeg_t))
takeg_thread.start()
time.sleep(1.0)
sess.close() # Will cancel blocked operation
takeg_thread.join()
@test_util.run_v1_only("b/120545219")
def testNonVectorIndices(self):
with self.cached_session():
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=tensor_shape.TensorShape([3, 3]))
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
"Input indices should be vector but received shape:"):
q.apply_grad(
grad_indices=[[0, 1], [1, 0]],
grad_values=np.array([1, 2]).astype(np.float32)).run()
@test_util.run_v1_only("b/120545219")
def testZeroDimensionValues(self):
with self.cached_session():
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=tensor_shape.TensorShape([3, 3]))
with self.assertRaisesRegex(errors_impl.InvalidArgumentError,
"Values cannot be 0-dimensional."):
q.apply_grad(
grad_indices=[0], grad_values=np.array(1).astype(np.float32)).run()
@test_util.run_v1_only("b/120545219")
def testWrongNonEmptyInputValues(self):
with self.cached_session():
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=tensor_shape.TensorShape([3, 3]))
with self.assertRaisesRegex(errors_impl.InvalidArgumentError,
" non-empty input values, got "):
q.apply_grad(
grad_indices=[0, 1],
grad_values=np.array([[0, 1, 1]]).astype(np.float32)).run()
@test_util.run_v1_only("b/120545219")
def testDynamicNonVectorIndices(self):
with self.cached_session() as sess:
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=tensor_shape.TensorShape([3, 3]))
x_indices = array_ops.placeholder(dtypes_lib.int64)
x_values = array_ops.placeholder(dtypes_lib.float32)
accum_op = q.apply_grad(grad_indices=x_indices, grad_values=x_values)
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
"Input indices should be vector but received shape:"):
sess.run(accum_op,
feed_dict={
x_indices: [[0, 1], [1, 0]],
x_values: np.array([1, 2]).astype(np.float32)
})
@test_util.run_v1_only("b/120545219")
def testDynamicWrongNonEmptyInputValues(self):
with self.cached_session() as sess:
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=tensor_shape.TensorShape([3, 3]))
x_indices = array_ops.placeholder(dtypes_lib.int64)
x_values = array_ops.placeholder(dtypes_lib.float32)
accum_op = q.apply_grad(grad_indices=x_indices, grad_values=x_values)
with self.assertRaisesRegex(errors_impl.InvalidArgumentError,
" non-empty input values, got "):
sess.run(accum_op,
feed_dict={
x_indices: [0, 1],
x_values: np.array([[0, 1, 1]]).astype(np.float32)
})
@test_util.run_v1_only("b/120545219")
def testEmptyShapeApply(self):
with self.cached_session():
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=tensor_shape.TensorShape([]))
with self.assertRaisesRegex(errors_impl.InvalidArgumentError,
"Input indices should be vector"):
q.apply_grad(grad_indices=0, grad_values=[1.0], grad_shape=[]).run()
with self.assertRaisesRegex(errors_impl.InvalidArgumentError,
"Input indices should be vector"):
q.apply_grad(grad_indices=0, grad_values=[1.0]).run()
with self.assertRaisesRegex(errors_impl.InvalidArgumentError,
"Values cannot be 0-dimensional."):
q.apply_grad(grad_indices=[0], grad_values=1.0, grad_shape=[]).run()
with self.assertRaisesRegex(errors_impl.InvalidArgumentError,
"Values cannot be 0-dimensional."):
q.apply_grad(grad_indices=[0], grad_values=1.0).run()
# The right way to apply a scalar
q.apply_grad(grad_indices=[0], grad_values=[1.0], grad_shape=[]).run()
q.apply_grad(grad_indices=[0], grad_values=[1.0]).run()
@test_util.run_v1_only("b/120545219")
def testValidateShape(self):
with self.cached_session() as sess:
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=[2, 2, None])
# Provided shape has wrong rank
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
"Shape mismatch: expected shape rank at least 3, got 2"):
q.apply_grad(
grad_indices=[0],
grad_values=np.array([[1, 2]]).astype(np.float32),
grad_shape=[2, 2]).run()
# Provided shape has wrong dim
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
"Shape mismatch: expected shape dim 1 to be 2, got 3"):
q.apply_grad(
grad_indices=[0],
grad_values=np.array([[[1, 2], [3, 4], [5, 6]]]).astype(np.float32),
grad_shape=[2, 3, 2]).run()
# Indices exceeded accumulator's shape's limits
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
"Shape mismatch: index of slice 0 exceeded limits of shape;"
" index is 3 exceeded 2"):
q.apply_grad(
grad_indices=[3],
grad_values=np.array([[[1, 2], [3, 4]]]).astype(np.float32)).run()
# Values' rank does not match shape
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
"Shape mismatch: expected values rank at least 3, got 2"):
q.apply_grad(
grad_indices=[0, 1],
grad_values=np.array([[1, 2], [3, 4]]).astype(np.float32)).run()
# Values' dim does not match shape
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
"Shape mismatch: expected values dim 1 to be 2, got 3"):
q.apply_grad(
grad_indices=[0],
grad_values=np.array(
[[[1, 2], [3, 4], [5, 6]]]).astype(np.float32)).run()
# First successful gradient creates additional constraints
# Shape will be additionally be constrained to [None,2,2,2] hereafter.
q.apply_grad(
grad_indices=[0],
grad_values=np.array(
[[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]]).astype(np.float32)).run()
# Values' rank does not match accumulated gradient
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
"Shape mismatch: expected values rank 4, got 3"):
q.apply_grad(
grad_indices=[0],
grad_values=np.array([[[1, 2], [3, 4]]]).astype(np.float32)).run()
# Values' dim does not match accumulated gradient
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
"Shape mismatch: expected values dim 3 to be 2, got 3"):
q.apply_grad(
grad_indices=[0],
grad_values=np.array(
[[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]]).astype(
np.float32)).run()
# After take grad, constraints on accumulated gradient are removed
self.evaluate(q.take_grad(1))
# First successful gradient imposes new constraints.
# Hereafter, shape will additionally constrained to [None,2,2,3]
q.apply_grad(
grad_indices=[0],
grad_values=np.array(
[[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]]).astype(
np.float32),
local_step=1).run()
with self.assertRaisesRegex(
errors_impl.InvalidArgumentError,
"Shape mismatch: expected values dim 3 to be 3, got 2"):
q.apply_grad(
grad_indices=[0],
grad_values=np.array(
[[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]]).astype(np.float32),
local_step=1).run()
@test_util.run_deprecated_v1
def testReturnShape(self):
with self.cached_session() as sess:
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=[2, None])
q.apply_grad(
grad_indices=[0],
grad_values=np.array(
[[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]]).astype(np.float32)).run()
val = self.evaluate(q.take_indexed_slices_grad(1))
self.assertAllEqual(val.dense_shape, [2, 2, 2, 2])
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=[None, 2])
q.apply_grad(
grad_indices=[0],
grad_values=np.array(
[[[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]]]).astype(
np.float32)).run()
val = self.evaluate(q.take_indexed_slices_grad(1))
self.assertAllEqual(val.dense_shape, [-1, 2, 2, 3])
@test_util.run_deprecated_v1
def testApplyGradtInt32IndicesAndShape(self):
with self.cached_session() as sess:
q = data_flow_ops.SparseConditionalAccumulator(
dtypes_lib.float32, name="Q", shape=tensor_shape.TensorShape([3, 3]))
accum_op = q.apply_grad(
grad_indices=constant_op.constant(
[0, 2], dtype=dtypes_lib.int32),
grad_values=constant_op.constant(
[[0, 0, 1], [3, 0, 4]], dtype=dtypes_lib.float32),
grad_shape=constant_op.constant(
[3, 3], dtype=dtypes_lib.int32))
accum_op.run()
accum_op = q.apply_indexed_slices_grad(
indexed_slices.IndexedSlices(
indices=constant_op.constant(
[0, 2], dtype=dtypes_lib.int32),
values=constant_op.constant(
[[0, 0, 1], [3, 0, 4]], dtype=dtypes_lib.float32),
dense_shape=constant_op.constant(
[3, 3], dtype=dtypes_lib.int32)))
accum_op.run()
self.assertEqual(q.num_accumulated().eval(), 2)
val = self.evaluate(q.take_indexed_slices_grad(1))
self.assertAllEqual(val.indices, [0, 2])
self.assertAllEqual(val.values, [[0, 0, 1], [3, 0, 4]])
self.assertAllEqual(val.dense_shape, [3, 3])
if __name__ == "__main__":
test.main()
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,185 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for tensorflow.ops.tf.matmul."""
import numpy as np
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import test_util
from tensorflow.python.ops import gradient_checker
from tensorflow.python.ops import math_ops
from tensorflow.python.platform import test
def RandMatrix(rows, cols, tr, round_bfloat=False):
if tr:
rows, cols = cols, rows
rand_func = np.random.randint if round_bfloat else np.random.uniform
return (np.clip(
rand_func(
low=-256.0, high=256.0, size=rows * cols), -64,
64) / 128.0).reshape([rows, cols]).astype(np.float32)
class SparseMatMulTest(test.TestCase):
def _testCpuMatmul(self,
x,
y,
tr_a=False,
tr_b=False,
sp_a=True,
sp_b=False,
x_dtype=dtypes.float32,
y_dtype=dtypes.float32):
with self.cached_session(use_gpu=False):
tf_x = math_ops.cast(x, x_dtype)
tf_y = math_ops.cast(y, y_dtype)
tf_ans = math_ops.matmul(
tf_x,
tf_y,
transpose_a=tr_a,
transpose_b=tr_b,
a_is_sparse=sp_a,
b_is_sparse=sp_b)
out = self.evaluate(tf_ans)
np_x = math_ops.cast(tf_x, dtypes.float32).eval()
np_y = math_ops.cast(tf_y, dtypes.float32).eval()
if tr_a:
np_x = np.transpose(np_x)
if tr_b:
np_y = np.transpose(np_y)
np_ans = np.matrix(np_x) * np.matrix(np_y)
self.assertShapeEqual(np_ans, tf_ans)
self.assertAllCloseAccordingToType(np_ans, out, rtol=1e-4, atol=1e-4)
@test_util.run_deprecated_v1
def testBasic(self):
x = np.arange(0., 4.).reshape([4, 1]).astype(np.float32)
y = np.arange(-1., 1.).reshape([1, 2]).astype(np.float32)
for x_dtype in (dtypes.float32, dtypes.bfloat16):
for y_dtype in (dtypes.float32, dtypes.bfloat16):
self._testCpuMatmul(x, y, x_dtype=x_dtype, y_dtype=y_dtype)
@test_util.run_deprecated_v1
def testZeroDim(self):
x = np.ones((4, 0)).astype(np.float32)
y = np.ones((0, 3)).astype(np.float32)
for x_dtype in (dtypes.float32, dtypes.bfloat16):
for y_dtype in (dtypes.float32, dtypes.bfloat16):
self._testCpuMatmul(x, y, x_dtype=x_dtype, y_dtype=y_dtype)
@test_util.run_deprecated_v1
def testEmpty(self):
x = np.ones((0, 0)).astype(np.float32)
y = np.ones((0, 0)).astype(np.float32)
for x_dtype in (dtypes.float32, dtypes.bfloat16):
for y_dtype in (dtypes.float32, dtypes.bfloat16):
self._testCpuMatmul(x, y, x_dtype=x_dtype, y_dtype=y_dtype)
# Tests setting one dimension to be a high value.
@test_util.run_deprecated_v1
def testLarge(self):
r1 = np.random.randint(6000, 20000)
r2 = np.random.randint(1, 10)
r3 = np.random.randint(1, 10)
for m, k, n in [(r1, r2, r3), (r2, r1, r3), (r2, r3, r1)]:
for x_dtype in (dtypes.float32, dtypes.bfloat16):
for y_dtype in (dtypes.float32, dtypes.bfloat16):
x = RandMatrix(m, k, False)
y = RandMatrix(k, n, False)
self._testCpuMatmul(x, y, x_dtype=x_dtype, y_dtype=y_dtype)
# Tests random sized matrices.
@test_util.run_deprecated_v1
def testRandom(self):
for tr_a in [True, False]:
for tr_b in [True, False]:
for sp_a in [True, False]:
for sp_b in [True, False]:
for x_dtype in (dtypes.float32, dtypes.bfloat16):
for y_dtype in (dtypes.float32, dtypes.bfloat16):
n, k, m = np.random.randint(1, 100, size=3)
x = RandMatrix(n, k, tr_a)
y = RandMatrix(k, m, tr_b)
self._testCpuMatmul(
x,
y,
tr_a,
tr_b,
sp_a,
sp_b,
x_dtype=x_dtype,
y_dtype=y_dtype)
class MatMulGradientTest(test.TestCase):
def _testGradients(self, tr_a, tr_b, sp_a, sp_b, a_dtype, b_dtype, delta,
name):
with self.cached_session():
a = constant_op.constant(
RandMatrix(
3, 2, tr_a, round_bfloat=True), dtype=dtypes.float32)
b = constant_op.constant(
RandMatrix(
2, 4, tr_b, round_bfloat=True), dtype=dtypes.float32)
tf_a = math_ops.cast(a, a_dtype) if a_dtype != dtypes.float32 else a
tf_b = math_ops.cast(b, b_dtype) if b_dtype != dtypes.float32 else b
m = math_ops.matmul(
tf_a,
tf_b,
name=name,
transpose_a=tr_a,
transpose_b=tr_b,
a_is_sparse=sp_a,
b_is_sparse=sp_b)
err = (
gradient_checker.compute_gradient_error(
a, [2, 3] if tr_a else [3, 2],
m, [3, 4],
x_init_value=self.evaluate(a),
delta=delta) + gradient_checker.compute_gradient_error(
b, [4, 2] if tr_b else [2, 4],
m, [3, 4],
x_init_value=self.evaluate(b),
delta=delta))
self.assertLessEqual(err, delta / 2.)
@test_util.run_deprecated_v1
def testGradientInput(self):
for tr_a in [True, False]:
for tr_b in [True, False]:
for sp_a in [True, False]:
for sp_b in [True, False]:
for a_dtype in (dtypes.float32, dtypes.bfloat16):
for b_dtype in (dtypes.float32, dtypes.bfloat16):
# Note: bfloat16 only has 7 mantissa bits, versus float32 with
# 10. Hence, we shift by 2 bits to pass the test.
if a_dtype == dtypes.bfloat16 and b_dtype == dtypes.bfloat16:
delta = 1 / 16.
else:
delta = 1 / 64.
name = "sparse_matmul_%s_%s_%s_%s" % (tr_a, tr_b, sp_a, sp_b)
self._testGradients(tr_a, tr_b, sp_a, sp_b, a_dtype, b_dtype,
delta, name)
if __name__ == "__main__":
test.main()
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,186 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for SparseReorder."""
from absl.testing import parameterized
import numpy as np
from tensorflow.python.eager import def_function
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import errors
from tensorflow.python.framework import sparse_tensor
from tensorflow.python.framework import tensor_spec
from tensorflow.python.framework import test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import gen_sparse_ops
from tensorflow.python.ops import gradient_checker
from tensorflow.python.ops import sparse_ops
import tensorflow.python.ops.sparse_grad # pylint: disable=unused-import
from tensorflow.python.platform import test
class SparseReorderTest(test.TestCase, parameterized.TestCase):
def _SparseTensorPlaceholder(self):
return sparse_tensor.SparseTensor(
array_ops.placeholder(dtypes.int64),
array_ops.placeholder(dtypes.float64),
array_ops.placeholder(dtypes.int64))
def _SparseTensorValue_5x6(self, permutation, dtype=dtypes.float64):
ind = np.array([[0, 0], [1, 0], [1, 3], [1, 4], [3, 2],
[3, 3]]).astype(np.int64)
val = np.array([0, 10, 13, 14, 32, 33]).astype(dtype.as_numpy_dtype)
ind = ind[permutation]
val = val[permutation]
shape = np.array([5, 6]).astype(np.int64)
return sparse_tensor.SparseTensorValue(ind, val, shape)
def testStaticShapeInfoPreserved(self):
sp_input = sparse_tensor.SparseTensor.from_value(
self._SparseTensorValue_5x6(np.arange(6)))
self.assertAllEqual((5, 6), sp_input.get_shape())
sp_output = sparse_ops.sparse_reorder(sp_input)
self.assertAllEqual((5, 6), sp_output.get_shape())
@parameterized.named_parameters([
("FullyStatic", [5, 6], None, [5, 6]),
("Partial1", [None, 6], None, [None, 6]),
("Partial2", [5, None], None, [5, None]),
("RankFromStaticDenseShape", [None, None], None, [None, None]),
("RankFromDenseShapeDim", None, [2], [None, None]),
("FullyDynamic", None, [None], None),
])
def testStaticShapeInfoPreservedInFunction(self,
static_dense_shape,
dense_shape_shape,
expected_output_shape):
@def_function.function
def func(indices, values, dense_shape):
if static_dense_shape:
dense_shape = [
static_dim or dense_shape[i]
for i, static_dim in enumerate(static_dense_shape)
]
sp_input = sparse_tensor.SparseTensor(indices, values, dense_shape)
sp_output = sparse_ops.sparse_reorder(sp_input)
self.assertEqual(expected_output_shape, sp_output.get_shape())
return sp_output
_ = func.get_concrete_function(
tensor_spec.TensorSpec([6, 2], dtypes.int64),
tensor_spec.TensorSpec([6], dtypes.float64),
tensor_spec.TensorSpec(dense_shape_shape, dtypes.int64))
def testAlreadyInOrder(self):
with self.session() as sess:
input_val = self._SparseTensorValue_5x6(np.arange(6))
sp_output = sparse_ops.sparse_reorder(input_val)
output_val = self.evaluate(sp_output)
self.assertAllEqual(output_val.indices, input_val.indices)
self.assertAllEqual(output_val.values, input_val.values)
self.assertAllEqual(output_val.dense_shape, input_val.dense_shape)
@test_util.run_deprecated_v1
def testFeedAlreadyInOrder(self):
with self.session() as sess:
sp_input = self._SparseTensorPlaceholder()
input_val = self._SparseTensorValue_5x6(np.arange(6))
sp_output = sparse_ops.sparse_reorder(sp_input)
output_val = sess.run(sp_output, {sp_input: input_val})
self.assertAllEqual(output_val.indices, input_val.indices)
self.assertAllEqual(output_val.values, input_val.values)
self.assertAllEqual(output_val.dense_shape, input_val.dense_shape)
@parameterized.parameters(dtypes.bfloat16, dtypes.float64)
def testOutOfOrder(self, dtype):
expected_output_val = self._SparseTensorValue_5x6(np.arange(6), dtype)
with self.session() as sess:
for _ in range(5): # To test various random permutations
input_val = self._SparseTensorValue_5x6(np.random.permutation(6), dtype)
sp_output = sparse_ops.sparse_reorder(input_val)
output_val = self.evaluate(sp_output)
self.assertAllEqual(output_val.indices, expected_output_val.indices)
self.assertAllEqual(output_val.values, expected_output_val.values)
self.assertAllEqual(output_val.dense_shape,
expected_output_val.dense_shape)
@test_util.run_deprecated_v1
def testFeedOutOfOrder(self):
expected_output_val = self._SparseTensorValue_5x6(np.arange(6))
with self.session() as sess:
for _ in range(5): # To test various random permutations
sp_input = self._SparseTensorPlaceholder()
input_val = self._SparseTensorValue_5x6(np.random.permutation(6))
sp_output = sparse_ops.sparse_reorder(sp_input)
output_val = sess.run(sp_output, {sp_input: input_val})
self.assertAllEqual(output_val.indices, expected_output_val.indices)
self.assertAllEqual(output_val.values, expected_output_val.values)
self.assertAllEqual(output_val.dense_shape,
expected_output_val.dense_shape)
@test_util.run_deprecated_v1
def testGradients(self):
with self.session():
for _ in range(5): # To test various random permutations
input_val = self._SparseTensorValue_5x6(np.random.permutation(6))
sp_input = sparse_tensor.SparseTensor(input_val.indices,
input_val.values,
input_val.dense_shape)
sp_output = sparse_ops.sparse_reorder(sp_input)
err = gradient_checker.compute_gradient_error(
sp_input.values,
input_val.values.shape,
sp_output.values,
input_val.values.shape,
x_init_value=input_val.values)
self.assertLess(err, 1e-11)
def testShapeOverflow(self):
# Test case for GitHub issue 45392
sp_input = sparse_tensor.SparseTensor(
indices=[[0, 0, 0, 0, 0, 0]],
values=[0.0],
dense_shape=[4096, 4096, 4096, 4096, 4096, 4096])
self.assertAllEqual((4096, 4096, 4096, 4096, 4096, 4096),
sp_input.get_shape())
sp_output = sparse_ops.sparse_reorder(sp_input)
self.assertAllEqual((4096, 4096, 4096, 4096, 4096, 4096),
sp_output.get_shape())
def testInvalidSparseInput(self):
with self.assertRaisesRegex(
(ValueError, errors.InvalidArgumentError),
"Number of elements .* do not match",
):
self.evaluate(
gen_sparse_ops.sparse_reorder(
input_indices=[[0, 0, 0]],
input_values=[0, 1, 2],
input_shape=[3, 3],
)
)
if __name__ == "__main__":
test.main()
@@ -0,0 +1,420 @@
# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for SparseReshape."""
from absl.testing import parameterized
import numpy as np
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import errors
from tensorflow.python.framework import sparse_tensor
from tensorflow.python.framework import test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import sparse_ops
from tensorflow.python.platform import test
class SparseReshapeTest(test.TestCase):
def _SparseTensorPlaceholder(self):
return sparse_tensor.SparseTensor(
array_ops.placeholder(dtypes.int64),
array_ops.placeholder(dtypes.float64),
array_ops.placeholder(dtypes.int64))
def _SparseTensorValue_5x6(self):
ind = np.array([[0, 0], [1, 0], [1, 3], [1, 4], [3, 2],
[3, 3]]).astype(np.int64)
val = np.array([0, 10, 13, 14, 32, 33]).astype(np.float64)
shape = np.array([5, 6]).astype(np.int64)
return sparse_tensor.SparseTensorValue(ind, val, shape)
def _SparseTensorValue_2x3x4(self):
ind = np.array([[0, 0, 1], [0, 1, 0], [0, 1, 2], [1, 0, 3], [1, 1, 1],
[1, 1, 3], [1, 2, 2]])
val = np.array([1, 10, 12, 103, 111, 113, 122])
shape = np.array([2, 3, 4])
return sparse_tensor.SparseTensorValue(ind, val, shape)
def testStaticShapeInfoPreserved(self):
sp_input = sparse_tensor.SparseTensor.from_value(
self._SparseTensorValue_5x6())
self.assertAllEqual((5, 6), sp_input.get_shape())
sp_output = sparse_ops.sparse_reshape(sp_input, shape=(1, 5, 2, 3))
self.assertAllEqual((1, 5, 2, 3), sp_output.get_shape())
def testStaticShapeInfoPreservedWithInferredDims(self):
sp_input = sparse_tensor.SparseTensor.from_value(
self._SparseTensorValue_2x3x4())
self.assertAllEqual((2, 3, 4), sp_input.get_shape())
sp_output = sparse_ops.sparse_reshape(sp_input, shape=(2, -1))
self.assertAllEqual((2, 3 * 4), sp_output.get_shape())
@test_util.run_deprecated_v1
def testRaisesIfMoreThanOneInferredDim(self):
sp_input = sparse_tensor.SparseTensor.from_value(
self._SparseTensorValue_2x3x4())
with self.assertRaisesRegex(ValueError, "At most one dimension can"):
sparse_ops.sparse_reshape(sp_input, shape=(-1, 2, -1))
@test_util.run_deprecated_v1
def testRaisesIfInferredShapeNotPossible(self):
sp_input = sparse_tensor.SparseTensor.from_value(
self._SparseTensorValue_2x3x4())
with self.assertRaisesRegex(ValueError, "Cannot reshape"):
sparse_ops.sparse_reshape(sp_input, shape=(-1, 7))
@test_util.run_deprecated_v1
def testPropagatesFullyKnownDenseShapeWhenShapePartiallyKnown(self):
sp_input = sparse_tensor.SparseTensor.from_value(
self._SparseTensorValue_2x3x4())
self.assertAllEqual((2, 3, 4), sp_input.shape)
sp_output = sparse_ops.sparse_reshape(
sp_input, shape=array_ops.concat(
(constant_op.constant([2], dtype=dtypes.int64),
array_ops.placeholder(dtype=dtypes.int64, shape=[1])),
axis=0))
self.assertAllEqual((2, 3 * 4), sp_output.shape)
def testSameShape(self):
with self.session() as sess:
input_val = self._SparseTensorValue_5x6()
sp_output = sparse_ops.sparse_reshape(input_val, [5, 6])
output_val = self.evaluate(sp_output)
self.assertAllEqual(output_val.indices, input_val.indices)
self.assertAllEqual(output_val.values, input_val.values)
self.assertAllEqual(output_val.dense_shape, input_val.dense_shape)
def testReshapeIntegeroverflow(self):
with self.session():
with self.assertRaises(errors.InvalidArgumentError):
sp_output = sparse_ops.gen_sparse_ops.sparse_reshape(
input_indices=[[0, 0]],
input_shape=[2**32, 2**31],
new_shape=[1, 1],
name=None)
self.evaluate(sp_output)
def testReshapeNegativeShape(self):
with self.session():
with self.assertRaises(errors.InvalidArgumentError):
sp_output = sparse_ops.gen_sparse_ops.sparse_reshape(
input_indices=[[0, 0]],
input_shape=[1, -1],
new_shape=[1, 1],
name=None)
self.evaluate(sp_output)
@test_util.run_deprecated_v1
def testFeedSameShape(self):
with self.session() as sess:
sp_input = self._SparseTensorPlaceholder()
input_val = self._SparseTensorValue_5x6()
sp_output = sparse_ops.sparse_reshape(sp_input, [5, 6])
output_val = sess.run(sp_output, {sp_input: input_val})
self.assertAllEqual(output_val.indices, input_val.indices)
self.assertAllEqual(output_val.values, input_val.values)
self.assertAllEqual(output_val.dense_shape, input_val.dense_shape)
@test_util.run_deprecated_v1
def testWorksWellWithTfShape(self):
with self.session() as sess:
sp_input = self._SparseTensorPlaceholder()
input_val = self._SparseTensorValue_5x6()
shape = array_ops.shape(sp_input) # tf.shape generates int32 output
sp_output = sparse_ops.sparse_reshape(sp_input, shape)
output_val = sess.run(sp_output, {sp_input: input_val})
self.assertAllEqual(output_val.indices, input_val.indices)
self.assertAllEqual(output_val.values, input_val.values)
self.assertAllEqual(output_val.dense_shape, input_val.dense_shape)
@test_util.run_deprecated_v1
def testFeedSameShapeWithInferredDim(self):
with self.session() as sess:
sp_input = self._SparseTensorPlaceholder()
input_val = self._SparseTensorValue_5x6()
sp_output = sparse_ops.sparse_reshape(sp_input, [-1, 6])
output_val = sess.run(sp_output, {sp_input: input_val})
self.assertAllEqual(output_val.indices, input_val.indices)
self.assertAllEqual(output_val.values, input_val.values)
self.assertAllEqual(output_val.dense_shape, input_val.dense_shape)
@test_util.run_deprecated_v1
def testFeedNewShapeSameRank(self):
with self.session() as sess:
sp_input = self._SparseTensorPlaceholder()
input_val = self._SparseTensorValue_5x6()
sp_output = sparse_ops.sparse_reshape(sp_input, [3, 10])
output_val = sess.run(sp_output, {sp_input: input_val})
self.assertAllEqual(output_val.indices,
np.array([[0, 0], [0, 6], [0, 9], [1, 0], [2, 0],
[2, 1]]))
self.assertAllEqual(output_val.values, input_val.values)
self.assertAllEqual(output_val.dense_shape, [3, 10])
@test_util.run_deprecated_v1
def testFeedNewShapeSameRankWithInferredDim(self):
with self.session() as sess:
sp_input = self._SparseTensorPlaceholder()
input_val = self._SparseTensorValue_5x6()
sp_output = sparse_ops.sparse_reshape(sp_input, [3, -1])
output_val = sess.run(sp_output, {sp_input: input_val})
self.assertAllEqual(output_val.indices,
np.array([[0, 0], [0, 6], [0, 9], [1, 0], [2, 0],
[2, 1]]))
self.assertAllEqual(output_val.values, input_val.values)
self.assertAllEqual(output_val.dense_shape, [3, 10])
def testUpRank(self):
with self.session() as sess:
input_val = self._SparseTensorValue_5x6()
sp_output = sparse_ops.sparse_reshape(input_val, [2, 3, 5])
output_val = self.evaluate(sp_output)
self.assertAllEqual(output_val.indices,
np.array([[0, 0, 0], [0, 1, 1], [0, 1, 4], [0, 2, 0],
[1, 1, 0], [1, 1, 1]]))
self.assertAllEqual(output_val.values, input_val.values)
self.assertAllEqual(output_val.dense_shape, [2, 3, 5])
@test_util.run_deprecated_v1
def testFeedUpRank(self):
with self.session() as sess:
sp_input = self._SparseTensorPlaceholder()
input_val = self._SparseTensorValue_5x6()
sp_output = sparse_ops.sparse_reshape(sp_input, [2, 3, 5])
output_val = sess.run(sp_output, {sp_input: input_val})
self.assertAllEqual(output_val.indices,
np.array([[0, 0, 0], [0, 1, 1], [0, 1, 4], [0, 2, 0],
[1, 1, 0], [1, 1, 1]]))
self.assertAllEqual(output_val.values, input_val.values)
self.assertAllEqual(output_val.dense_shape, [2, 3, 5])
@test_util.run_deprecated_v1
def testFeedUpRankWithInferredDim(self):
with self.session() as sess:
sp_input = self._SparseTensorPlaceholder()
input_val = self._SparseTensorValue_5x6()
sp_output = sparse_ops.sparse_reshape(sp_input, [2, -1, 5])
output_val = sess.run(sp_output, {sp_input: input_val})
self.assertAllEqual(output_val.indices,
np.array([[0, 0, 0], [0, 1, 1], [0, 1, 4], [0, 2, 0],
[1, 1, 0], [1, 1, 1]]))
self.assertAllEqual(output_val.values, input_val.values)
self.assertAllEqual(output_val.dense_shape, [2, 3, 5])
@test_util.run_deprecated_v1
def testFeedDownRank(self):
with self.session() as sess:
sp_input = self._SparseTensorPlaceholder()
input_val = self._SparseTensorValue_2x3x4()
sp_output = sparse_ops.sparse_reshape(sp_input, [6, 4])
output_val = sess.run(sp_output, {sp_input: input_val})
self.assertAllEqual(output_val.indices,
np.array([[0, 1], [1, 0], [1, 2], [3, 3], [4, 1],
[4, 3], [5, 2]]))
self.assertAllEqual(output_val.values, input_val.values)
self.assertAllEqual(output_val.dense_shape, [6, 4])
@test_util.run_deprecated_v1
def testFeedDownRankWithInferredDim(self):
with self.session() as sess:
sp_input = self._SparseTensorPlaceholder()
input_val = self._SparseTensorValue_2x3x4()
sp_output = sparse_ops.sparse_reshape(sp_input, [6, -1])
output_val = sess.run(sp_output, {sp_input: input_val})
self.assertAllEqual(output_val.indices,
np.array([[0, 1], [1, 0], [1, 2], [3, 3], [4, 1],
[4, 3], [5, 2]]))
self.assertAllEqual(output_val.values, input_val.values)
self.assertAllEqual(output_val.dense_shape, [6, 4])
@test_util.run_deprecated_v1
def testFeedMultipleInferredDims(self):
with self.session() as sess:
sp_input = self._SparseTensorPlaceholder()
input_val = self._SparseTensorValue_5x6()
sp_output = sparse_ops.sparse_reshape(sp_input, [4, -1, -1])
with self.assertRaisesOpError("only one output dimension may be -1"):
sess.run(sp_output, {sp_input: input_val})
@test_util.run_deprecated_v1
def testProvideStaticallyMismatchedSizes(self):
input_val = self._SparseTensorValue_5x6()
sp_input = sparse_tensor.SparseTensor.from_value(input_val)
with self.assertRaisesRegex(ValueError, "Cannot reshape"):
sparse_ops.sparse_reshape(sp_input, [4, 7])
@test_util.run_deprecated_v1
def testFeedMismatchedSizes(self):
with self.session() as sess:
sp_input = self._SparseTensorPlaceholder()
input_val = self._SparseTensorValue_5x6()
sp_output = sparse_ops.sparse_reshape(sp_input, [4, 7])
with self.assertRaisesOpError(
"Input to reshape is a tensor with 30 dense values"):
sess.run(sp_output, {sp_input: input_val})
@test_util.run_deprecated_v1
def testFeedMismatchedSizesWithInferredDim(self):
with self.session() as sess:
sp_input = self._SparseTensorPlaceholder()
input_val = self._SparseTensorValue_5x6()
sp_output = sparse_ops.sparse_reshape(sp_input, [4, -1])
with self.assertRaisesOpError("requested shape requires a multiple"):
sess.run(sp_output, {sp_input: input_val})
@test_util.run_deprecated_v1
def testFeedPartialShapes(self):
with self.session():
# Incorporate new rank into shape information if known
sp_input = self._SparseTensorPlaceholder()
sp_output = sparse_ops.sparse_reshape(sp_input, [2, 3, 5])
self.assertListEqual(sp_output.indices.get_shape().as_list(), [None, 3])
self.assertListEqual(sp_output.dense_shape.get_shape().as_list(), [3])
# Incorporate known shape information about input indices in output
# indices
sp_input = self._SparseTensorPlaceholder()
sp_input.indices.set_shape([5, None])
sp_output = sparse_ops.sparse_reshape(sp_input, [2, 3, 5])
self.assertListEqual(sp_output.indices.get_shape().as_list(), [5, 3])
self.assertListEqual(sp_output.dense_shape.get_shape().as_list(), [3])
# Even if new_shape has no shape information, we know the ranks of
# output indices and shape
sp_input = self._SparseTensorPlaceholder()
sp_input.indices.set_shape([5, None])
new_shape = array_ops.placeholder(dtypes.int64)
sp_output = sparse_ops.sparse_reshape(sp_input, new_shape)
self.assertListEqual(sp_output.indices.get_shape().as_list(), [5, None])
self.assertListEqual(sp_output.dense_shape.get_shape().as_list(), [None])
@test_util.run_deprecated_v1
def testFeedDenseReshapeSemantics(self):
with self.session() as sess:
# Compute a random rank-5 initial shape and new shape, randomly sparsify
# it, and check that the output of SparseReshape has the same semantics
# as a dense reshape.
factors = np.array([2] * 4 + [3] * 4 + [5] * 4) # 810k total elements
orig_rank = np.random.randint(2, 7)
orig_map = np.random.randint(orig_rank, size=factors.shape)
orig_shape = [np.prod(factors[orig_map == d]) for d in range(orig_rank)]
new_rank = np.random.randint(2, 7)
new_map = np.random.randint(new_rank, size=factors.shape)
new_shape = [np.prod(factors[new_map == d]) for d in range(new_rank)]
orig_dense = np.random.uniform(size=orig_shape)
orig_indices = np.transpose(np.nonzero(orig_dense < 0.5))
orig_values = orig_dense[orig_dense < 0.5]
new_dense = np.reshape(orig_dense, new_shape)
new_indices = np.transpose(np.nonzero(new_dense < 0.5))
new_values = new_dense[new_dense < 0.5]
sp_input = self._SparseTensorPlaceholder()
input_val = sparse_tensor.SparseTensorValue(orig_indices, orig_values,
orig_shape)
sp_output = sparse_ops.sparse_reshape(sp_input, new_shape)
output_val = sess.run(sp_output, {sp_input: input_val})
self.assertAllEqual(output_val.indices, new_indices)
self.assertAllEqual(output_val.values, new_values)
self.assertAllEqual(output_val.dense_shape, new_shape)
class EmptySparseTensorReshapeTest(test.TestCase, parameterized.TestCase):
"""Tests for reshaping 0-sized SparseTensors, compared w/ dense tensors."""
def _MakeAndReshapeTensor(self, tensor_class, original_shape, target_shape):
if tensor_class == "sparse":
ind = np.zeros([0, len(original_shape)]).astype(np.int64)
val = np.array([]).astype(np.float64)
shape = np.array(original_shape).astype(np.int64)
sp_input = sparse_tensor.SparseTensorValue(ind, val, shape)
sp_output = self.evaluate(
sparse_ops.sparse_reshape(sp_input, target_shape))
return sp_output.dense_shape
else:
dense_input = array_ops.zeros(original_shape)
dense_output = self.evaluate(array_ops.reshape(dense_input, target_shape))
return dense_output.shape
@parameterized.named_parameters([
("Dense", "dense"),
("Sparse", "sparse"),
])
def testImpliedReshapeEmpty1DTensor(self, tensor_class):
self.assertAllEqual(
self._MakeAndReshapeTensor(tensor_class, [0], [-1, 1]), [0, 1])
self.assertAllEqual(
self._MakeAndReshapeTensor(tensor_class, [0], [-1, 1, 2]), [0, 1, 2])
@parameterized.named_parameters([
("Dense", "dense"),
("Sparse", "sparse"),
])
def testImpliedReshapeEmpty2DTensor(self, tensor_class):
self.assertAllEqual(
self._MakeAndReshapeTensor(tensor_class, [1, 0], [-1, 1]), [0, 1])
self.assertAllEqual(
self._MakeAndReshapeTensor(tensor_class, [1, 0], [-1, 2, 3]), [0, 2, 3])
@parameterized.named_parameters([
("Dense", "dense"),
("Sparse", "sparse"),
])
def testImpliedReshapeEmpty3DTensor(self, tensor_class):
self.assertAllEqual(
self._MakeAndReshapeTensor(tensor_class, [1, 0, 0], [-1, 2, 3]),
[0, 2, 3])
@parameterized.named_parameters([
("Dense", "dense"),
("Sparse", "sparse"),
])
def testImpliedReshapeEmpty4DTensor(self, tensor_class):
self.assertAllEqual(
self._MakeAndReshapeTensor(tensor_class, [2, 4, 0, 6], [-1, 4, 6, 2]),
[0, 4, 6, 2])
def testImpliedDimTogetherWithZeroDimCausesError(self):
# NOTE: When implied dimensions and zero dimensions coexist in the target
# shape, the behavior currently differs between sparse and regular tensors.
with self.assertRaises(errors.InvalidArgumentError):
self._MakeAndReshapeTensor("sparse", [0], [-1, 0])
with self.assertRaises(errors.InvalidArgumentError):
self._MakeAndReshapeTensor("sparse", [1, 0], [-1, 0])
with self.assertRaises(errors.InvalidArgumentError):
self._MakeAndReshapeTensor("sparse", [1, 2, 0], [2, -1, 0])
with self.assertRaises(errors.InvalidArgumentError):
self._MakeAndReshapeTensor("sparse", [1, 2, 3, 0], [2, 0, -1, 3])
if __name__ == "__main__":
test.main()
@@ -0,0 +1,480 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for SerializeSparse."""
import numpy as np
from tensorflow.python.eager import def_function
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import sparse_tensor as sparse_tensor_lib
from tensorflow.python.framework import test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import array_ops_stack
from tensorflow.python.ops import gen_resource_variable_ops
from tensorflow.python.ops import sparse_ops
from tensorflow.python.platform import test
class SerializeSparseTest(test.TestCase):
def _SparseTensorPlaceholder(self, dtype=None):
if dtype is None:
dtype = dtypes.int32
return sparse_tensor_lib.SparseTensor(
array_ops.placeholder(dtypes.int64),
array_ops.placeholder(dtype), array_ops.placeholder(dtypes.int64))
def _SparseTensorValue_5x6(self, permutation):
ind = np.array([[0, 0], [1, 0], [1, 3], [1, 4], [3, 2],
[3, 3]]).astype(np.int64)
val = np.array([0, 10, 13, 14, 32, 33]).astype(np.int32)
ind = ind[permutation]
val = val[permutation]
shape = np.array([5, 6]).astype(np.int64)
return sparse_tensor_lib.SparseTensorValue(ind, val, shape)
def _SparseTensorValue_3x4(self, permutation):
ind = np.array([[0, 0], [1, 0], [1, 2], [1, 3], [2, 2],
[2, 3]]).astype(np.int64)
val = np.array([0, 10, 13, 14, 32, 33]).astype(np.int32)
ind = ind[permutation]
val = val[permutation]
shape = np.array([3, 4]).astype(np.int64)
return sparse_tensor_lib.SparseTensorValue(ind, val, shape)
def _SparseTensorValue_1x1x1(self):
ind = np.array([[0, 0, 0]]).astype(np.int64)
val = np.array([0]).astype(np.int32)
shape = np.array([3, 4, 5]).astype(np.int64)
return sparse_tensor_lib.SparseTensorValue(ind, val, shape)
def _testSerializeDeserializeHelper(self,
serialize_fn,
deserialize_fn,
out_type=dtypes.string):
with self.cached_session(use_gpu=False) as sess:
sp_input = self._SparseTensorValue_5x6(np.arange(6))
serialized = serialize_fn(sp_input, out_type=out_type)
sp_deserialized = deserialize_fn(serialized, dtype=dtypes.int32)
indices, values, shape = self.evaluate(sp_deserialized)
self.assertAllEqual(indices, sp_input[0])
self.assertAllEqual(values, sp_input[1])
self.assertAllEqual(shape, sp_input[2])
def testSerializeDeserialize(self):
self._testSerializeDeserializeHelper(sparse_ops.serialize_sparse,
sparse_ops.deserialize_sparse)
def testVariantSerializeDeserialize(self):
self._testSerializeDeserializeHelper(sparse_ops.serialize_sparse,
sparse_ops.deserialize_sparse,
dtypes.variant)
def _testSerializeDeserializeBatchHelper(self,
serialize_fn,
deserialize_fn,
out_type=dtypes.string):
with self.cached_session(use_gpu=False) as sess:
sp_input = self._SparseTensorValue_5x6(np.arange(6))
serialized = serialize_fn(sp_input, out_type=out_type)
serialized = array_ops_stack.stack([serialized, serialized])
sp_deserialized = deserialize_fn(serialized, dtype=dtypes.int32)
combined_indices, combined_values, combined_shape = sess.run(
sp_deserialized)
self.assertAllEqual(combined_indices[:6, 0], [0] * 6) # minibatch 0
self.assertAllEqual(combined_indices[:6, 1:], sp_input[0])
self.assertAllEqual(combined_indices[6:, 0], [1] * 6) # minibatch 1
self.assertAllEqual(combined_indices[6:, 1:], sp_input[0])
self.assertAllEqual(combined_values[:6], sp_input[1])
self.assertAllEqual(combined_values[6:], sp_input[1])
self.assertAllEqual(combined_shape, [2, 5, 6])
@test_util.run_deprecated_v1
def testSerializeDeserializeBatch(self):
self._testSerializeDeserializeBatchHelper(sparse_ops.serialize_sparse,
sparse_ops.deserialize_sparse)
@test_util.run_deprecated_v1
def testSerializeDeserializeManyBatch(self):
self._testSerializeDeserializeBatchHelper(
sparse_ops.serialize_sparse, sparse_ops.deserialize_many_sparse)
@test_util.run_deprecated_v1
def testVariantSerializeDeserializeBatch(self):
self._testSerializeDeserializeBatchHelper(sparse_ops.serialize_sparse,
sparse_ops.deserialize_sparse,
dtypes.variant)
def _testSerializeDeserializeBatchInconsistentShapeHelper(
self, serialize_fn, deserialize_fn, out_type=dtypes.string):
with self.cached_session(use_gpu=False) as sess:
sp_input0 = self._SparseTensorValue_5x6(np.arange(6))
sp_input1 = self._SparseTensorValue_3x4(np.arange(6))
serialized0 = serialize_fn(sp_input0, out_type=out_type)
serialized1 = serialize_fn(sp_input1, out_type=out_type)
serialized = array_ops_stack.stack([serialized0, serialized1])
sp_deserialized = deserialize_fn(serialized, dtype=dtypes.int32)
combined_indices, combined_values, combined_shape = sess.run(
sp_deserialized)
self.assertAllEqual(combined_indices[:6, 0], [0] * 6) # minibatch 0
self.assertAllEqual(combined_indices[:6, 1:], sp_input0[0])
self.assertAllEqual(combined_indices[6:, 0], [1] * 6) # minibatch 1
self.assertAllEqual(combined_indices[6:, 1:], sp_input1[0])
self.assertAllEqual(combined_values[:6], sp_input0[1])
self.assertAllEqual(combined_values[6:], sp_input1[1])
self.assertAllEqual(combined_shape, [2, 5, 6])
@test_util.run_deprecated_v1
def testSerializeDeserializeBatchInconsistentShape(self):
self._testSerializeDeserializeBatchInconsistentShapeHelper(
sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse)
@test_util.run_deprecated_v1
def testVariantSerializeDeserializeBatchInconsistentShape(self):
self._testSerializeDeserializeBatchInconsistentShapeHelper(
sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse,
dtypes.variant)
def _testSerializeDeserializeNestedBatchHelper(self,
serialize_fn,
deserialize_fn,
out_type=dtypes.string):
with self.cached_session(use_gpu=False) as sess:
sp_input = self._SparseTensorValue_5x6(np.arange(6))
serialized = serialize_fn(sp_input, out_type=out_type)
serialized = array_ops_stack.stack([serialized, serialized])
serialized = array_ops_stack.stack([serialized, serialized])
sp_deserialized = deserialize_fn(serialized, dtype=dtypes.int32)
combined_indices, combined_values, combined_shape = sess.run(
sp_deserialized)
# minibatch 0
self.assertAllEqual(combined_indices[:6, :2], [[0, 0]] * 6)
self.assertAllEqual(combined_indices[:6, 2:], sp_input[0])
self.assertAllEqual(combined_values[:6], sp_input[1])
# minibatch 1
self.assertAllEqual(combined_indices[6:12, :2], [[0, 1]] * 6)
self.assertAllEqual(combined_indices[6:12, 2:], sp_input[0])
self.assertAllEqual(combined_values[6:12], sp_input[1])
# minibatch 2
self.assertAllEqual(combined_indices[12:18, :2], [[1, 0]] * 6)
self.assertAllEqual(combined_indices[12:18, 2:], sp_input[0])
self.assertAllEqual(combined_values[12:18], sp_input[1])
# minibatch 3
self.assertAllEqual(combined_indices[18:, :2], [[1, 1]] * 6)
self.assertAllEqual(combined_indices[18:, 2:], sp_input[0])
self.assertAllEqual(combined_values[18:], sp_input[1])
self.assertAllEqual(combined_shape, [2, 2, 5, 6])
@test_util.run_deprecated_v1
def testSerializeDeserializeNestedBatch(self):
self._testSerializeDeserializeNestedBatchHelper(
sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse)
@test_util.run_deprecated_v1
def testVariantSerializeDeserializeNestedBatch(self):
self._testSerializeDeserializeNestedBatchHelper(
sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse,
dtypes.variant)
def _testFeedSerializeDeserializeBatchHelper(self,
serialize_fn,
deserialize_fn,
out_type=dtypes.string):
with self.cached_session(use_gpu=False) as sess:
sp_input0 = self._SparseTensorPlaceholder()
sp_input1 = self._SparseTensorPlaceholder()
input0_val = self._SparseTensorValue_5x6(np.arange(6))
input1_val = self._SparseTensorValue_3x4(np.arange(6))
serialized0 = serialize_fn(sp_input0, out_type=out_type)
serialized1 = serialize_fn(sp_input1, out_type=out_type)
serialized_concat = array_ops_stack.stack([serialized0, serialized1])
sp_deserialized = deserialize_fn(serialized_concat, dtype=dtypes.int32)
combined_indices, combined_values, combined_shape = sess.run(
sp_deserialized, {sp_input0: input0_val,
sp_input1: input1_val})
self.assertAllEqual(combined_indices[:6, 0], [0] * 6) # minibatch 0
self.assertAllEqual(combined_indices[:6, 1:], input0_val[0])
self.assertAllEqual(combined_indices[6:, 0], [1] * 6) # minibatch 1
self.assertAllEqual(combined_indices[6:, 1:], input1_val[0])
self.assertAllEqual(combined_values[:6], input0_val[1])
self.assertAllEqual(combined_values[6:], input1_val[1])
self.assertAllEqual(combined_shape, [2, 5, 6])
@test_util.run_deprecated_v1
def testFeedSerializeDeserializeBatch(self):
self._testFeedSerializeDeserializeBatchHelper(sparse_ops.serialize_sparse,
sparse_ops.deserialize_sparse)
@test_util.run_deprecated_v1
def testFeedSerializeDeserializeManyBatch(self):
self._testFeedSerializeDeserializeBatchHelper(
sparse_ops.serialize_sparse, sparse_ops.deserialize_many_sparse)
@test_util.run_deprecated_v1
def testFeedVariantSerializeDeserializeBatch(self):
self._testFeedSerializeDeserializeBatchHelper(sparse_ops.serialize_sparse,
sparse_ops.deserialize_sparse,
dtypes.variant)
def _testSerializeManyShapeHelper(self,
serialize_many_fn,
out_type=dtypes.string):
with self.cached_session(use_gpu=False) as sess:
# N == 4 because shape_value == [4, 5]
indices_value = np.array([[0, 0], [0, 1], [2, 0]], dtype=np.int64)
values_value = np.array([b"a", b"b", b"c"])
shape_value = np.array([4, 5], dtype=np.int64)
sparse_tensor = self._SparseTensorPlaceholder(dtype=dtypes.string)
serialized = serialize_many_fn(sparse_tensor, out_type=out_type)
serialized_value = sess.run(
serialized,
feed_dict={
sparse_tensor.indices: indices_value,
sparse_tensor.values: values_value,
sparse_tensor.dense_shape: shape_value
})
self.assertEqual(serialized_value.shape, (4, 3))
@test_util.run_deprecated_v1
def testSerializeManyShape(self):
self._testSerializeManyShapeHelper(sparse_ops.serialize_many_sparse)
def testVariantSerializeManyShape(self):
# NOTE: The following test is a no-op as it is currently not possible to
# convert the serialized variant value to a numpy value.
pass
def _testSerializeManyDeserializeBatchHelper(self,
serialize_many_fn,
deserialize_fn,
out_type=dtypes.string):
with self.cached_session(use_gpu=False) as sess:
# N == 4 because shape_value == [4, 5]
indices_value = np.array([[0, 0], [0, 1], [2, 0]], dtype=np.int64)
values_value = np.array([b"a", b"b", b"c"])
shape_value = np.array([4, 5], dtype=np.int64)
sparse_tensor = self._SparseTensorPlaceholder(dtype=dtypes.string)
serialized = serialize_many_fn(sparse_tensor, out_type=out_type)
deserialized = deserialize_fn(serialized, dtype=dtypes.string)
deserialized_value = sess.run(
deserialized,
feed_dict={
sparse_tensor.indices: indices_value,
sparse_tensor.values: values_value,
sparse_tensor.dense_shape: shape_value
})
self.assertAllEqual(deserialized_value.indices, indices_value)
self.assertAllEqual(deserialized_value.values, values_value)
self.assertAllEqual(deserialized_value.dense_shape, shape_value)
@test_util.run_deprecated_v1
def testSerializeManyDeserializeBatch(self):
self._testSerializeManyDeserializeBatchHelper(
sparse_ops.serialize_many_sparse, sparse_ops.deserialize_sparse)
@test_util.run_deprecated_v1
def testSerializeManyDeserializeManyBatch(self):
self._testSerializeManyDeserializeBatchHelper(
sparse_ops.serialize_many_sparse, sparse_ops.deserialize_many_sparse)
@test_util.run_deprecated_v1
def testVariantSerializeManyDeserializeBatch(self):
self._testSerializeManyDeserializeBatchHelper(
sparse_ops.serialize_many_sparse, sparse_ops.deserialize_sparse,
dtypes.variant)
@test_util.run_deprecated_v1
def testVariantSerializeDeserializeScalar(self):
with self.session(use_gpu=False) as sess:
indices_value = np.array([[]], dtype=np.int64)
values_value = np.array([37], dtype=np.int32)
shape_value = np.array([], dtype=np.int64)
sparse_tensor = self._SparseTensorPlaceholder()
serialized = sparse_ops.serialize_sparse(
sparse_tensor, out_type=dtypes.variant)
deserialized = sparse_ops.deserialize_sparse(
serialized, dtype=dtypes.int32)
deserialized_value = sess.run(
deserialized,
feed_dict={
sparse_tensor.indices: indices_value,
sparse_tensor.values: values_value,
sparse_tensor.dense_shape: shape_value
})
self.assertAllEqual(deserialized_value.indices, indices_value)
self.assertAllEqual(deserialized_value.values, values_value)
self.assertAllEqual(deserialized_value.dense_shape, shape_value)
@test_util.run_deprecated_v1
def testVariantSerializeDeserializeScalarBatch(self):
with self.session(use_gpu=False) as sess:
indices_value = np.array([[]], dtype=np.int64)
values_value = np.array([37], dtype=np.int32)
shape_value = np.array([], dtype=np.int64)
sparse_tensor = self._SparseTensorPlaceholder()
serialized = sparse_ops.serialize_sparse(
sparse_tensor, out_type=dtypes.variant)
stacked = array_ops_stack.stack([serialized, serialized])
deserialized = sparse_ops.deserialize_sparse(stacked, dtype=dtypes.int32)
deserialized_value = sess.run(
deserialized,
feed_dict={
sparse_tensor.indices: indices_value,
sparse_tensor.values: values_value,
sparse_tensor.dense_shape: shape_value
})
self.assertAllEqual(deserialized_value.indices,
np.array([[0], [1]], dtype=np.int64))
self.assertAllEqual(deserialized_value.values,
np.array([37, 37], dtype=np.int32))
self.assertAllEqual(deserialized_value.dense_shape,
np.array([2], dtype=np.int64))
def _testDeserializeFailsWrongTypeHelper(self,
serialize_fn,
deserialize_fn,
out_type=dtypes.string):
with self.cached_session(use_gpu=False) as sess:
sp_input0 = self._SparseTensorPlaceholder()
sp_input1 = self._SparseTensorPlaceholder()
input0_val = self._SparseTensorValue_5x6(np.arange(6))
input1_val = self._SparseTensorValue_3x4(np.arange(6))
serialized0 = serialize_fn(sp_input0, out_type=out_type)
serialized1 = serialize_fn(sp_input1, out_type=out_type)
serialized_concat = array_ops_stack.stack([serialized0, serialized1])
sp_deserialized = deserialize_fn(serialized_concat, dtype=dtypes.int64)
with self.assertRaisesOpError(
r"Requested SparseTensor of type int64 but "
r"SparseTensor\[0\].values.dtype\(\) == int32"):
sess.run(sp_deserialized,
{sp_input0: input0_val,
sp_input1: input1_val})
@test_util.run_deprecated_v1
def testDeserializeFailsWrongType(self):
self._testDeserializeFailsWrongTypeHelper(sparse_ops.serialize_sparse,
sparse_ops.deserialize_sparse)
@test_util.run_deprecated_v1
def testDeserializeManyFailsWrongType(self):
self._testDeserializeFailsWrongTypeHelper(
sparse_ops.serialize_sparse, sparse_ops.deserialize_many_sparse)
@test_util.run_deprecated_v1
def testVariantDeserializeFailsWrongType(self):
self._testDeserializeFailsWrongTypeHelper(sparse_ops.serialize_sparse,
sparse_ops.deserialize_sparse,
dtypes.variant)
def _testDeserializeFailsInconsistentRankHelper(self,
serialize_fn,
deserialize_fn,
out_type=dtypes.string):
with self.cached_session(use_gpu=False) as sess:
sp_input0 = self._SparseTensorPlaceholder()
sp_input1 = self._SparseTensorPlaceholder()
input0_val = self._SparseTensorValue_5x6(np.arange(6))
input1_val = self._SparseTensorValue_1x1x1()
serialized0 = serialize_fn(sp_input0, out_type=out_type)
serialized1 = serialize_fn(sp_input1, out_type=out_type)
serialized_concat = array_ops_stack.stack([serialized0, serialized1])
sp_deserialized = deserialize_fn(serialized_concat, dtype=dtypes.int32)
with self.assertRaisesOpError(
r"Inconsistent shape across SparseTensors: rank prior to "
r"SparseTensor\[1\] was: 2 but rank of SparseTensor\[1\] is: 3"):
sess.run(sp_deserialized,
{sp_input0: input0_val,
sp_input1: input1_val})
@test_util.run_deprecated_v1
def testDeserializeFailsInconsistentRank(self):
self._testDeserializeFailsInconsistentRankHelper(
sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse)
@test_util.run_deprecated_v1
def testDeserializeManyFailsInconsistentRank(self):
self._testDeserializeFailsInconsistentRankHelper(
sparse_ops.serialize_sparse, sparse_ops.deserialize_many_sparse)
@test_util.run_deprecated_v1
def testVariantDeserializeFailsInconsistentRank(self):
self._testDeserializeFailsInconsistentRankHelper(
sparse_ops.serialize_sparse, sparse_ops.deserialize_sparse,
dtypes.variant)
def _testDeserializeFailsInvalidProtoHelper(self,
serialize_fn,
deserialize_fn,
out_type=dtypes.string):
with self.cached_session(use_gpu=False) as sess:
sp_input0 = self._SparseTensorPlaceholder()
input0_val = self._SparseTensorValue_5x6(np.arange(6))
serialized0 = serialize_fn(sp_input0, out_type=out_type)
serialized1 = ["a", "b", "c"]
serialized_concat = array_ops_stack.stack([serialized0, serialized1])
sp_deserialized = deserialize_fn(serialized_concat, dtype=dtypes.int32)
with self.assertRaisesOpError(r"Could not parse serialized proto"):
sess.run(sp_deserialized, {sp_input0: input0_val})
@test_util.run_deprecated_v1
def testDeserializeFailsInvalidProto(self):
self._testDeserializeFailsInvalidProtoHelper(sparse_ops.serialize_sparse,
sparse_ops.deserialize_sparse)
@test_util.run_deprecated_v1
def testDeserializeManyFailsInvalidProto(self):
self._testDeserializeFailsInvalidProtoHelper(
sparse_ops.serialize_sparse, sparse_ops.deserialize_many_sparse)
def testDeserializeInvalidVariant(self):
mu = gen_resource_variable_ops.mutex_v2()
mu_lock = gen_resource_variable_ops.mutex_lock(mutex=mu)
@def_function.function
def f():
return sparse_ops.deserialize_sparse(
serialized_sparse=mu_lock, dtype=dtypes.int32)
with self.assertRaisesRegex(ValueError, r"Shape must be at least rank 1"):
f()
if __name__ == "__main__":
test.main()
@@ -0,0 +1,384 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for SparseReorder."""
import numpy as np
from tensorflow.python.framework import errors
from tensorflow.python.framework import sparse_tensor
from tensorflow.python.framework import test_util
from tensorflow.python.ops import gen_sparse_ops
from tensorflow.python.ops import gradient_checker
from tensorflow.python.ops import sparse_ops
import tensorflow.python.ops.sparse_grad # pylint: disable=unused-import
from tensorflow.python.platform import test
class SparseSliceOpTest(test.TestCase):
def _SparseTensor_4x6(self, val_dtype=np.int64):
# [0 | |2 | |4 |5 ]
# [ |11| |13|14| ]
# [20| | |23| |25]
# [30| |32|33| |35]
ind = np.array([[0, 0], [0, 2], [0, 4], [0, 5], [1, 1], [1, 3], [1,
4], [2, 0],
[2, 3], [2, 5], [3, 0], [3, 2], [3, 3], [3, 5]]).astype(
np.int64)
val = np.array([0, 2, 4, 5, 11, 13, 14, 20, 23, 25, 30, 32, 33, 35]).astype(
val_dtype)
shape = np.array([4, 6]).astype(np.int64)
return sparse_tensor.SparseTensor(ind, val, shape)
def _SparseTensor_5x7(self):
# [0 | |2 | |4 |5 | ]
# [ |11| |13|14| |16]
# [20| | |23| |25| ]
# [30| |32|33| |35| ]
# [ |41| | |44| |46]
ind = np.array([[0, 0], [0, 2], [0, 4], [0, 5], [1, 1], [1, 3], [1, 4],
[1, 6], [2, 0], [2, 3], [2, 5], [3, 0], [3, 2], [3, 3],
[3, 5], [4, 1], [4, 4], [4, 6]]).astype(np.int64)
val = np.array(
[0, 2, 4, 5, 11, 13, 14, 16, 20, 23, 25, 30, 32, 33, 35, 41, 44,
46]).astype(np.int64)
shape = np.array([5, 7]).astype(np.int64)
return sparse_tensor.SparseTensor(ind, val, shape)
def _SparseTensorValue_3x4x2(self):
# slice(:,:, 0)
# ['a0'| |'b0'| ]
# [ |'c0'| |'d0']
# [ | |'e0'| ]
# slice(:,:, 1)
# ['a1'| |'b1'| ]
# [ |'c1'| |'d1']
# [ | |'e1'| ]
ind = np.array([[0, 0, 0], [0, 0, 1], [0, 2, 0], [0, 2, 1], [1, 1, 0],
[1, 1, 1], [1, 3, 0], [1, 3, 1], [2, 2, 0], [2, 2,
1]]).astype(
np.int64)
val = np.array(['a0', 'a1', 'b0', 'b1', 'c0', 'c1', 'd0', 'd1', 'e0', 'e1'])
shape = np.array([3, 4, 2]).astype(np.int64)
return sparse_tensor.SparseTensorValue(ind, val, shape)
def _SparseTensor_3x4x2(self):
return sparse_tensor.SparseTensor.from_value(
self._SparseTensorValue_3x4x2())
def _SparseTensor_4x6_empty(self, val_dtype=np.int64):
ind = np.empty(shape=(0, 2), dtype=np.int64)
val = np.array([]).astype(val_dtype)
shape = np.array([4, 6]).astype(np.int64)
return sparse_tensor.SparseTensor(ind, val, shape)
@test_util.run_deprecated_v1
def testSliceMatrixRows(self):
with self.session():
sp_input = self._SparseTensor_4x6()
sp_tensor0 = sparse_ops.sparse_slice(sp_input, [0, 0], [2, 6])
sp_tensor1 = sparse_ops.sparse_slice(sp_input, [2, 0], [3, 7])
self.assertAllEqual(
sp_tensor0.indices,
[[0, 0], [0, 2], [0, 4], [0, 5], [1, 1], [1, 3], [1, 4]])
self.assertAllEqual(sp_tensor0.values, [0, 2, 4, 5, 11, 13, 14])
self.assertAllEqual(sp_tensor0.dense_shape, [2, 6])
self.assertAllEqual(
sp_tensor1.indices,
[[0, 0], [0, 3], [0, 5], [1, 0], [1, 2], [1, 3], [1, 5]])
self.assertAllEqual(sp_tensor1.values, [20, 23, 25, 30, 32, 33, 35])
self.assertAllEqual(sp_tensor1.dense_shape, [2, 6])
@test_util.run_deprecated_v1
def testSliceMatrixUnevenCols(self):
with self.session():
sp_input = self._SparseTensor_5x7()
sp_tensor0 = sparse_ops.sparse_slice(sp_input, [0, 0], [5, 3])
sp_tensor1 = sparse_ops.sparse_slice(sp_input, [0, 3], [5, 2])
sp_tensor2 = sparse_ops.sparse_slice(sp_input, [0, 5], [5, 2])
self.assertAllEqual(
sp_tensor0.indices,
[[0, 0], [0, 2], [1, 1], [2, 0], [3, 0], [3, 2], [4, 1]])
self.assertAllEqual(sp_tensor0.values, [0, 2, 11, 20, 30, 32, 41])
self.assertAllEqual(sp_tensor0.dense_shape, [5, 3])
self.assertAllEqual(sp_tensor1.indices,
[[0, 1], [1, 0], [1, 1], [2, 0], [3, 0], [4, 1]])
self.assertAllEqual(sp_tensor1.values, [4, 13, 14, 23, 33, 44])
self.assertAllEqual(sp_tensor1.dense_shape, [5, 2])
self.assertAllEqual(sp_tensor2.indices,
[[0, 0], [1, 1], [2, 0], [3, 0], [4, 1]])
self.assertAllEqual(sp_tensor2.values, [5, 16, 25, 35, 46])
self.assertAllEqual(sp_tensor2.dense_shape, [5, 2])
sp_tensor0 = sparse_ops.sparse_slice(sp_input, [0, 0], [5, 2])
sp_tensor1 = sparse_ops.sparse_slice(sp_input, [0, 2], [5, 2])
sp_tensor2 = sparse_ops.sparse_slice(sp_input, [0, 4], [5, 2])
sp_tensor3 = sparse_ops.sparse_slice(sp_input, [0, 6], [5, 2])
self.assertAllEqual(sp_tensor0.indices,
[[0, 0], [1, 1], [2, 0], [3, 0], [4, 1]])
self.assertAllEqual(sp_tensor0.values, [0, 11, 20, 30, 41])
self.assertAllEqual(sp_tensor0.dense_shape, [5, 2])
self.assertAllEqual(sp_tensor1.indices,
[[0, 0], [1, 1], [2, 1], [3, 0], [3, 1]])
self.assertAllEqual(sp_tensor1.values, [2, 13, 23, 32, 33])
self.assertAllEqual(sp_tensor1.dense_shape, [5, 2])
self.assertAllEqual(sp_tensor2.indices,
[[0, 0], [0, 1], [1, 0], [2, 1], [3, 1], [4, 0]])
self.assertAllEqual(sp_tensor2.values, [4, 5, 14, 25, 35, 44])
self.assertAllEqual(sp_tensor2.dense_shape, [5, 2])
self.assertAllEqual(sp_tensor3.indices, [[1, 0], [4, 0]])
self.assertAllEqual(sp_tensor3.values, [16, 46])
self.assertAllEqual(sp_tensor3.dense_shape, [5, 1])
@test_util.run_deprecated_v1
def testSliceMatrixUnevenRows(self):
with self.session():
sp_input = self._SparseTensor_5x7()
sp_tensor0 = sparse_ops.sparse_slice(sp_input, [0, 0], [3, 7])
sp_tensor1 = sparse_ops.sparse_slice(sp_input, [3, 0], [3, 7])
self.assertAllEqual(sp_tensor0.indices,
[[0, 0], [0, 2], [0, 4], [0, 5], [1, 1], [1, 3],
[1, 4], [1, 6], [2, 0], [2, 3], [2, 5]])
self.assertAllEqual(sp_tensor0.values,
[0, 2, 4, 5, 11, 13, 14, 16, 20, 23, 25])
self.assertAllEqual(sp_tensor0.dense_shape, [3, 7])
self.assertAllEqual(
sp_tensor1.indices,
[[0, 0], [0, 2], [0, 3], [0, 5], [1, 1], [1, 4], [1, 6]])
self.assertAllEqual(sp_tensor1.values, [30, 32, 33, 35, 41, 44, 46])
self.assertAllEqual(sp_tensor1.dense_shape, [2, 7])
sp_tensor0 = sparse_ops.sparse_slice(sp_input, [0, 0], [2, 7])
sp_tensor1 = sparse_ops.sparse_slice(sp_input, [2, 0], [2, 7])
sp_tensor2 = sparse_ops.sparse_slice(sp_input, [4, 0], [2, 7])
self.assertAllEqual(
sp_tensor0.indices,
[[0, 0], [0, 2], [0, 4], [0, 5], [1, 1], [1, 3], [1, 4], [1, 6]])
self.assertAllEqual(sp_tensor0.values, [0, 2, 4, 5, 11, 13, 14, 16])
self.assertAllEqual(sp_tensor0.dense_shape, [2, 7])
self.assertAllEqual(sp_tensor1.values, [20, 23, 25, 30, 32, 33, 35])
self.assertAllEqual(sp_tensor1.dense_shape, [2, 7])
self.assertAllEqual(sp_tensor2.indices, [[0, 1], [0, 4], [0, 6]])
self.assertAllEqual(sp_tensor2.values, [41, 44, 46])
self.assertAllEqual(sp_tensor2.dense_shape, [1, 7])
return
@test_util.run_deprecated_v1
def testSliceAllRows(self):
with self.session():
sp_input = self._SparseTensor_4x6()
sp_tensor0 = sparse_ops.sparse_slice(sp_input, [0, 0], [1, 6])
sp_tensor1 = sparse_ops.sparse_slice(sp_input, [1, 0], [1, 6])
sp_tensor2 = sparse_ops.sparse_slice(sp_input, [2, 0], [1, 7])
sp_tensor3 = sparse_ops.sparse_slice(sp_input, [3, 0], [2, 7])
self.assertAllEqual(sp_tensor0.indices, [[0, 0], [0, 2], [0, 4], [0, 5]])
self.assertAllEqual(sp_tensor0.values, [0, 2, 4, 5])
self.assertAllEqual(sp_tensor0.dense_shape, [1, 6])
self.assertAllEqual(sp_tensor1.indices, [[0, 1], [0, 3], [0, 4]])
self.assertAllEqual(sp_tensor1.values, [11, 13, 14])
self.assertAllEqual(sp_tensor1.dense_shape, [1, 6])
self.assertAllEqual(sp_tensor2.indices, [[0, 0], [0, 3], [0, 5]])
self.assertAllEqual(sp_tensor2.values, [20, 23, 25])
self.assertAllEqual(sp_tensor2.dense_shape, [1, 6])
self.assertAllEqual(sp_tensor3.indices, [[0, 0], [0, 2], [0, 3], [0, 5]])
self.assertAllEqual(sp_tensor3.values, [30, 32, 33, 35])
self.assertAllEqual(sp_tensor3.dense_shape, [1, 6])
@test_util.run_deprecated_v1
def testSliceColumns(self):
with self.session():
sp_input = self._SparseTensor_4x6()
sparse_tensor0 = sparse_ops.sparse_slice(sp_input, [0, 0], [4, 2])
sparse_tensor1 = sparse_ops.sparse_slice(sp_input, [0, 2], [5, 2])
sparse_tensor2 = sparse_ops.sparse_slice(sp_input, [0, 4], [5, 3])
self.assertAllEqual(sparse_tensor0.indices,
[[0, 0], [1, 1], [2, 0], [3, 0]])
self.assertAllEqual(sparse_tensor0.values, [0, 11, 20, 30])
self.assertAllEqual(sparse_tensor0.dense_shape, [4, 2])
self.assertAllEqual(sparse_tensor1.indices,
[[0, 0], [1, 1], [2, 1], [3, 0], [3, 1]])
self.assertAllEqual(sparse_tensor1.values, [2, 13, 23, 32, 33])
self.assertAllEqual(sparse_tensor1.dense_shape, [4, 2])
self.assertAllEqual(sparse_tensor2.indices,
[[0, 0], [0, 1], [1, 0], [2, 1], [3, 1]])
self.assertAllEqual(sparse_tensor2.values, [4, 5, 14, 25, 35])
self.assertAllEqual(sparse_tensor2.dense_shape, [4, 2])
@test_util.run_deprecated_v1
def testSliceAllColumns(self):
with self.session():
sp_input = self._SparseTensor_4x6()
sparse_tensor0 = sparse_ops.sparse_slice(sp_input, [0, 0], [4, 1])
sparse_tensor1 = sparse_ops.sparse_slice(sp_input, [0, 1], [4, 1])
sparse_tensor2 = sparse_ops.sparse_slice(sp_input, [0, 2], [4, 1])
sparse_tensor3 = sparse_ops.sparse_slice(sp_input, [0, 3], [4, 1])
sparse_tensor4 = sparse_ops.sparse_slice(sp_input, [0, 4], [5, 1])
sparse_tensor5 = sparse_ops.sparse_slice(sp_input, [0, 5], [6, 3])
self.assertAllEqual(sparse_tensor0.indices, [[0, 0], [2, 0], [3, 0]])
self.assertAllEqual(sparse_tensor0.values, [0, 20, 30])
self.assertAllEqual(sparse_tensor0.dense_shape, [4, 1])
self.assertAllEqual(sparse_tensor1.indices, [[1, 0]])
self.assertAllEqual(sparse_tensor1.values, [11])
self.assertAllEqual(sparse_tensor1.dense_shape, [4, 1])
self.assertAllEqual(sparse_tensor2.indices, [[0, 0], [3, 0]])
self.assertAllEqual(sparse_tensor2.values, [2, 32])
self.assertAllEqual(sparse_tensor2.dense_shape, [4, 1])
self.assertAllEqual(sparse_tensor3.indices, [[1, 0], [2, 0], [3, 0]])
self.assertAllEqual(sparse_tensor3.dense_shape, [4, 1])
self.assertAllEqual(sparse_tensor3.values, [13, 23, 33])
self.assertAllEqual(sparse_tensor4.indices, [[0, 0], [1, 0]])
self.assertAllEqual(sparse_tensor4.values, [4, 14])
self.assertAllEqual(sparse_tensor4.dense_shape, [4, 1])
self.assertAllEqual(sparse_tensor5.indices, [[0, 0], [2, 0], [3, 0]])
self.assertAllEqual(sparse_tensor5.values, [5, 25, 35])
self.assertAllEqual(sparse_tensor5.dense_shape, [4, 1])
def testSliceEmpty(self):
with test_util.use_gpu():
sp_empty = self._SparseTensor_4x6_empty()
sp_input = self._SparseTensor_4x6()
sparse_tensor0 = sparse_ops.sparse_slice(sp_empty, [0, 0], [4, 1])
sparse_tensor1 = sparse_ops.sparse_slice(sp_input, [1, 1], [0, 0])
sparse_tensor2 = sparse_ops.sparse_slice(sp_input, [2, 1], [2, 1])
empty_inds = np.empty(shape=(0, 2), dtype=np.int64)
self.assertAllEqual(sparse_tensor0.indices, empty_inds)
self.assertAllEqual(sparse_tensor0.values, [])
self.assertAllEqual(sparse_tensor0.dense_shape, [4, 1])
self.assertAllEqual(sparse_tensor1.indices, empty_inds)
self.assertAllEqual(sparse_tensor1.values, [])
self.assertAllEqual(sparse_tensor1.dense_shape, [0, 0])
self.assertAllEqual(sparse_tensor2.indices, empty_inds)
self.assertAllEqual(sparse_tensor2.values, [])
self.assertAllEqual(sparse_tensor2.dense_shape, [2, 1])
@test_util.run_deprecated_v1
def testGradients(self):
sp_input = self._SparseTensor_4x6(val_dtype=np.float32)
start_and_size = [([0, 0], [4, 2]),
([0, 2], [5, 2]),
([0, 4], [5, 3])]
with self.session():
for start, size in start_and_size:
sp_output = sparse_ops.sparse_slice(sp_input, start, size)
nnz_in = len(self.evaluate(sp_input.values))
nnz_out = len(self.evaluate(sp_output.values))
err = gradient_checker.compute_gradient_error(
[sp_input.values], [(nnz_in,)], sp_output.values, (nnz_out,))
self.assertLess(err, 1e-3)
def testGradientsExplicit(self):
sp_input = self._SparseTensor_4x6()
start, size = [0, 0], [4, 1]
sp_output = sparse_ops.sparse_slice(sp_input, start, size)
input_grad_vals = sparse_ops.sparse_slice_grad(sp_output.values,
sp_input.indices, start,
sp_output.indices)
# pyformat: disable
self.assertAllEqual(input_grad_vals, [0, 0, 0, 0,
0, 0, 0,
20, 0, 0,
30, 0, 0, 0])
# pyformat: enable
start, size = [0, 1], [4, 1]
sp_output = sparse_ops.sparse_slice(sp_input, start, size)
input_grad_vals = sparse_ops.sparse_slice_grad(sp_output.values,
sp_input.indices, start,
sp_output.indices)
# pyformat: disable
self.assertAllEqual(input_grad_vals, [0, 0, 0, 0,
11, 0, 0,
0, 0, 0,
0, 0, 0, 0])
# pyformat: enable
start, size = [1, 3], [3, 1]
sp_output = sparse_ops.sparse_slice(sp_input, start, size)
input_grad_vals = sparse_ops.sparse_slice_grad(sp_output.values,
sp_input.indices, start,
sp_output.indices)
# pyformat: disable
self.assertAllEqual(input_grad_vals, [0, 0, 0, 0,
0, 13, 0,
0, 23, 0,
0, 0, 33, 0])
# pyformat: enable
# Test empty slice of non-empty input.
start, size = [2, 1], [2, 1]
sp_output = sparse_ops.sparse_slice(sp_input, start, size)
input_grad_vals = sparse_ops.sparse_slice_grad(sp_output.values,
sp_input.indices, start,
sp_output.indices)
# pyformat: disable
self.assertAllEqual(input_grad_vals, [0, 0, 0, 0,
0, 0, 0,
0, 0, 0,
0, 0, 0, 0])
# pyformat: enable
sp_input = self._SparseTensor_4x6_empty()
start, size = [0, 0], [4, 1]
sp_output = sparse_ops.sparse_slice(sp_input, start, size)
input_grad_vals = sparse_ops.sparse_slice_grad(sp_output.values,
sp_input.indices, start,
sp_output.indices)
self.assertAllEqual(input_grad_vals, [])
def testNegativeSize(self):
with self.session(use_gpu=False):
with self.assertRaises(errors.InvalidArgumentError):
res = sparse_ops.gen_sparse_ops.sparse_slice(
indices=[[0, 0]],
values=[0],
shape=[1, 1],
start=[10, 10],
size=[-100, 100])
self.evaluate(res)
def testLargeSize(self):
with self.session(use_gpu=False):
# Confirm potential integer overflow due to size is handled by op.
res = sparse_ops.gen_sparse_ops.sparse_slice(
indices=[[0, 0]],
values=[0],
shape=[1, 1],
start=[2**62, -1],
size=[2**62, 2**62])
self.evaluate(res)
def testInvalidSparseInput(self):
with self.assertRaisesRegex(
(ValueError, errors.InvalidArgumentError),
'Number of elements .* do not match',
):
self.evaluate(
gen_sparse_ops.sparse_slice(
indices=[[0, 0, 0]],
values=[0, 1, 2],
shape=[3, 3],
start=[0, 0],
size=[1, 1],
)
)
if __name__ == '__main__':
test.main()
@@ -0,0 +1,340 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for SparseReorder."""
import numpy as np
from tensorflow.python.framework import errors
from tensorflow.python.framework import sparse_tensor
from tensorflow.python.framework import test_util
from tensorflow.python.ops import gen_sparse_ops
from tensorflow.python.ops import sparse_ops
from tensorflow.python.platform import test
class SparseSplitOpTest(test.TestCase):
def _SparseTensor_4x6(self):
# [0 | |2 | |4 |5 ]
# [ |11| |13|14| ]
# [20| | |23| |25]
# [30| |32|33| |35]
ind = np.array([[0, 0], [0, 2], [0, 4], [0, 5], [1, 1], [1, 3], [1, 4],
[2, 0], [2, 3], [2, 5], [3, 0], [3, 2], [3, 3],
[3, 5]]).astype(np.int64)
val = np.array(
[0, 2, 4, 5, 11, 13, 14, 20, 23, 25, 30, 32, 33, 35]).astype(np.int64)
shape = np.array([4, 6]).astype(np.int64)
return sparse_tensor.SparseTensor(ind, val, shape)
def _SparseTensor_5x7(self):
# [0 | |2 | |4 |5 | ]
# [ |11| |13|14| |16]
# [20| | |23| |25| ]
# [30| |32|33| |35| ]
# [ |41| | |44| |46]
ind = np.array([[0, 0], [0, 2], [0, 4], [0, 5], [1, 1], [1, 3], [1, 4],
[1, 6], [2, 0], [2, 3], [2, 5], [3, 0], [3, 2], [3, 3],
[3, 5], [4, 1], [4, 4], [4, 6]]).astype(np.int64)
val = np.array(
[0, 2, 4, 5, 11, 13, 14, 16, 20, 23, 25, 30, 32, 33, 35, 41, 44,
46]).astype(np.int64)
shape = np.array([5, 7]).astype(np.int64)
return sparse_tensor.SparseTensor(ind, val, shape)
def _SparseTensorValue_3x4x2(self):
# slice(:,:, 0)
# ['a0'| |'b0'| ]
# [ |'c0'| |'d0']
# [ | |'e0'| ]
# slice(:,:, 1)
# ['a1'| |'b1'| ]
# [ |'c1'| |'d1']
# [ | |'e1'| ]
ind = np.array([[0, 0, 0], [0, 0, 1], [0, 2, 0], [0, 2, 1], [1, 1, 0],
[1, 1, 1], [1, 3, 0], [1, 3, 1], [2, 2, 0],
[2, 2, 1]]).astype(np.int64)
val = np.array(['a0', 'a1', 'b0', 'b1', 'c0', 'c1', 'd0', 'd1', 'e0', 'e1'])
shape = np.array([3, 4, 2]).astype(np.int64)
return sparse_tensor.SparseTensorValue(ind, val, shape)
def _SparseTensor_3x4x2(self):
return sparse_tensor.SparseTensor.from_value(self._SparseTensorValue_3x4x2(
))
def _SparseTensor_4x6_empty(self, val_dtype=np.int64):
ind = np.empty(shape=(0, 2), dtype=np.int64)
val = np.array([]).astype(val_dtype)
shape = np.array([4, 6]).astype(np.int64)
return sparse_tensor.SparseTensor(ind, val, shape)
def testSplitMatrixRows(self):
for axis in (0, -2):
sp_tensors = self.evaluate(
sparse_ops.sparse_split(
sp_input=self._SparseTensor_4x6(), num_split=2, axis=axis))
self.assertAllEqual(len(sp_tensors), 2)
self.assertAllEqual(
sp_tensors[0].indices,
[[0, 0], [0, 2], [0, 4], [0, 5], [1, 1], [1, 3], [1, 4]])
self.assertAllEqual(sp_tensors[0].values, [0, 2, 4, 5, 11, 13, 14])
self.assertAllEqual(sp_tensors[0].dense_shape, [2, 6])
self.assertAllEqual(
sp_tensors[1].indices,
[[0, 0], [0, 3], [0, 5], [1, 0], [1, 2], [1, 3], [1, 5]])
self.assertAllEqual(sp_tensors[1].values, [20, 23, 25, 30, 32, 33, 35])
self.assertAllEqual(sp_tensors[1].dense_shape, [2, 6])
def testSplitMatrixUnevenCols(self):
for axis in (1, -1):
sp_tensors_3 = self.evaluate(
sparse_ops.sparse_split(
sp_input=self._SparseTensor_5x7(), num_split=3, axis=axis))
self.assertAllEqual(len(sp_tensors_3), 3)
self.assertAllEqual(
sp_tensors_3[0].indices,
[[0, 0], [0, 2], [1, 1], [2, 0], [3, 0], [3, 2], [4, 1]])
self.assertAllEqual(sp_tensors_3[0].values, [0, 2, 11, 20, 30, 32, 41])
self.assertAllEqual(sp_tensors_3[0].dense_shape, [5, 3])
self.assertAllEqual(sp_tensors_3[1].indices,
[[0, 1], [1, 0], [1, 1], [2, 0], [3, 0], [4, 1]])
self.assertAllEqual(sp_tensors_3[1].values, [4, 13, 14, 23, 33, 44])
self.assertAllEqual(sp_tensors_3[1].dense_shape, [5, 2])
self.assertAllEqual(sp_tensors_3[2].indices,
[[0, 0], [1, 1], [2, 0], [3, 0], [4, 1]])
self.assertAllEqual(sp_tensors_3[2].values, [5, 16, 25, 35, 46])
self.assertAllEqual(sp_tensors_3[2].dense_shape, [5, 2])
sp_tensors_4 = sparse_ops.sparse_split(
sp_input=self._SparseTensor_5x7(), num_split=4, axis=axis)
self.assertAllEqual(len(sp_tensors_4), 4)
self.assertAllEqual(sp_tensors_4[0].indices,
[[0, 0], [1, 1], [2, 0], [3, 0], [4, 1]])
self.assertAllEqual(sp_tensors_4[0].values, [0, 11, 20, 30, 41])
self.assertAllEqual(sp_tensors_4[0].dense_shape, [5, 2])
self.assertAllEqual(sp_tensors_4[1].indices,
[[0, 0], [1, 1], [2, 1], [3, 0], [3, 1]])
self.assertAllEqual(sp_tensors_4[1].values, [2, 13, 23, 32, 33])
self.assertAllEqual(sp_tensors_4[1].dense_shape, [5, 2])
self.assertAllEqual(sp_tensors_4[2].indices,
[[0, 0], [0, 1], [1, 0], [2, 1], [3, 1], [4, 0]])
self.assertAllEqual(sp_tensors_4[2].values, [4, 5, 14, 25, 35, 44])
self.assertAllEqual(sp_tensors_4[2].dense_shape, [5, 2])
self.assertAllEqual(sp_tensors_4[3].indices, [[1, 0], [4, 0]])
self.assertAllEqual(sp_tensors_4[3].values, [16, 46])
self.assertAllEqual(sp_tensors_4[3].dense_shape, [5, 1])
def testSplitMatrixUnevenRows(self):
for axis in (0, -2):
sp_tensors_2 = self.evaluate(
sparse_ops.sparse_split(
sp_input=self._SparseTensor_5x7(), num_split=2, axis=axis))
self.assertAllEqual(sp_tensors_2[0].indices,
[[0, 0], [0, 2], [0, 4], [0, 5], [1, 1], [1, 3],
[1, 4], [1, 6], [2, 0], [2, 3], [2, 5]])
self.assertAllEqual(sp_tensors_2[0].values,
[0, 2, 4, 5, 11, 13, 14, 16, 20, 23, 25])
self.assertAllEqual(sp_tensors_2[0].dense_shape, [3, 7])
self.assertAllEqual(
sp_tensors_2[1].indices,
[[0, 0], [0, 2], [0, 3], [0, 5], [1, 1], [1, 4], [1, 6]])
self.assertAllEqual(sp_tensors_2[1].values, [30, 32, 33, 35, 41, 44, 46])
self.assertAllEqual(sp_tensors_2[1].dense_shape, [2, 7])
self.assertAllEqual(len(sp_tensors_2), 2)
sp_tensors_3 = sparse_ops.sparse_split(
sp_input=self._SparseTensor_5x7(), num_split=3, axis=axis)
self.assertAllEqual(len(sp_tensors_3), 3)
self.assertAllEqual(
sp_tensors_3[0].indices,
[[0, 0], [0, 2], [0, 4], [0, 5], [1, 1], [1, 3], [1, 4], [1, 6]])
self.assertAllEqual(sp_tensors_3[0].values, [0, 2, 4, 5, 11, 13, 14, 16])
self.assertAllEqual(sp_tensors_3[0].dense_shape, [2, 7])
self.assertAllEqual(sp_tensors_3[1].values, [20, 23, 25, 30, 32, 33, 35])
self.assertAllEqual(sp_tensors_3[1].dense_shape, [2, 7])
self.assertAllEqual(sp_tensors_3[2].indices, [[0, 1], [0, 4], [0, 6]])
self.assertAllEqual(sp_tensors_3[2].values, [41, 44, 46])
self.assertAllEqual(sp_tensors_3[2].dense_shape, [1, 7])
def testSplitAllRows(self):
for axis in (0, -2):
sp_tensors = self.evaluate(
sparse_ops.sparse_split(
sp_input=self._SparseTensor_4x6(), num_split=4, axis=axis))
self.assertAllEqual(len(sp_tensors), 4)
self.assertAllEqual(sp_tensors[0].indices,
[[0, 0], [0, 2], [0, 4], [0, 5]])
self.assertAllEqual(sp_tensors[0].values, [0, 2, 4, 5])
self.assertAllEqual(sp_tensors[0].dense_shape, [1, 6])
self.assertAllEqual(sp_tensors[1].indices, [[0, 1], [0, 3], [0, 4]])
self.assertAllEqual(sp_tensors[1].values, [11, 13, 14])
self.assertAllEqual(sp_tensors[1].dense_shape, [1, 6])
self.assertAllEqual(sp_tensors[2].indices, [[0, 0], [0, 3], [0, 5]])
self.assertAllEqual(sp_tensors[2].values, [20, 23, 25])
self.assertAllEqual(sp_tensors[2].dense_shape, [1, 6])
self.assertAllEqual(sp_tensors[3].indices,
[[0, 0], [0, 2], [0, 3], [0, 5]])
self.assertAllEqual(sp_tensors[3].values, [30, 32, 33, 35])
self.assertAllEqual(sp_tensors[3].dense_shape, [1, 6])
def testSplitColumns(self):
for axis in (1, -1):
sparse_tensors = self.evaluate(
sparse_ops.sparse_split(
sp_input=self._SparseTensor_4x6(), num_split=3, axis=axis))
self.assertAllEqual(len(sparse_tensors), 3)
self.assertAllEqual(sparse_tensors[0].indices,
[[0, 0], [1, 1], [2, 0], [3, 0]])
self.assertAllEqual(sparse_tensors[0].values, [0, 11, 20, 30])
self.assertAllEqual(sparse_tensors[0].dense_shape, [4, 2])
self.assertAllEqual(sparse_tensors[1].indices,
[[0, 0], [1, 1], [2, 1], [3, 0], [3, 1]])
self.assertAllEqual(sparse_tensors[1].values, [2, 13, 23, 32, 33])
self.assertAllEqual(sparse_tensors[1].dense_shape, [4, 2])
self.assertAllEqual(sparse_tensors[2].indices,
[[0, 0], [0, 1], [1, 0], [2, 1], [3, 1]])
self.assertAllEqual(sparse_tensors[2].values, [4, 5, 14, 25, 35])
self.assertAllEqual(sparse_tensors[2].dense_shape, [4, 2])
def testSplitAllColumns(self):
for axis in (1, -1):
sparse_tensors = self.evaluate(
sparse_ops.sparse_split(
sp_input=self._SparseTensor_4x6(), num_split=6, axis=axis))
self.assertAllEqual(len(sparse_tensors), 6)
self.assertAllEqual(sparse_tensors[0].indices, [[0, 0], [2, 0], [3, 0]])
self.assertAllEqual(sparse_tensors[0].values, [0, 20, 30])
self.assertAllEqual(sparse_tensors[0].dense_shape, [4, 1])
self.assertAllEqual(sparse_tensors[1].indices, [[1, 0]])
self.assertAllEqual(sparse_tensors[1].values, [11])
self.assertAllEqual(sparse_tensors[1].dense_shape, [4, 1])
self.assertAllEqual(sparse_tensors[2].indices, [[0, 0], [3, 0]])
self.assertAllEqual(sparse_tensors[2].values, [2, 32])
self.assertAllEqual(sparse_tensors[2].dense_shape, [4, 1])
self.assertAllEqual(sparse_tensors[3].indices, [[1, 0], [2, 0], [3, 0]])
self.assertAllEqual(sparse_tensors[3].dense_shape, [4, 1])
self.assertAllEqual(sparse_tensors[3].values, [13, 23, 33])
self.assertAllEqual(sparse_tensors[4].indices, [[0, 0], [1, 0]])
self.assertAllEqual(sparse_tensors[4].values, [4, 14])
self.assertAllEqual(sparse_tensors[4].dense_shape, [4, 1])
self.assertAllEqual(sparse_tensors[5].indices, [[0, 0], [2, 0], [3, 0]])
self.assertAllEqual(sparse_tensors[5].values, [5, 25, 35])
self.assertAllEqual(sparse_tensors[5].dense_shape, [4, 1])
def testSliceConcat(self):
for sp_input in (self._SparseTensorValue_3x4x2(),
self._SparseTensor_3x4x2()):
for axis in (1, -2):
sparse_tensors = sparse_ops.sparse_split(
sp_input=sp_input, num_split=2, axis=axis)
concat_tensor = self.evaluate(
sparse_ops.sparse_concat(1, sparse_tensors))
expected_output = self._SparseTensor_3x4x2()
self.assertAllEqual(concat_tensor.indices, expected_output.indices)
def testInvalidAxis(self):
for axis in (-3, 2):
with self.assertRaisesRegex(errors.InvalidArgumentError,
r'axis should be in range \[-2, 2\)'):
self.evaluate(
sparse_ops.sparse_split(
sp_input=self._SparseTensor_4x6(), num_split=3, axis=axis))
def testArgumentErrors(self):
with self.assertRaisesRegex(ValueError, 'Keyword arguments are required'):
sparse_ops.sparse_split(3, 2, 1)
with self.assertRaisesRegex(ValueError, 'sp_input is required'):
sparse_ops.sparse_split()
with self.assertRaisesRegex(ValueError, 'num_split is required'):
sparse_ops.sparse_split(sp_input=1)
with self.assertRaisesRegex(ValueError, 'axis is required'):
sparse_ops.sparse_split(num_split=2, sp_input=1)
def testSplitEmpty(self):
sp_empty = self._SparseTensor_4x6_empty()
sparse_splits0 = sparse_ops.sparse_split(
sp_input=sp_empty, num_split=2, axis=0)
sparse_splits1 = sparse_ops.sparse_split(
sp_input=sp_empty, num_split=2, axis=1)
empty_inds = np.empty(shape=(0, 2), dtype=np.int64)
self.assertAllEqual(sparse_splits0[0].indices, empty_inds)
self.assertAllEqual(sparse_splits0[0].values, [])
self.assertAllEqual(sparse_splits0[0].dense_shape, [2, 6])
self.assertAllEqual(sparse_splits0[1].indices, empty_inds)
self.assertAllEqual(sparse_splits0[1].values, [])
self.assertAllEqual(sparse_splits0[1].dense_shape, [2, 6])
self.assertAllEqual(sparse_splits1[0].indices, empty_inds)
self.assertAllEqual(sparse_splits1[0].values, [])
self.assertAllEqual(sparse_splits1[0].dense_shape, [4, 3])
self.assertAllEqual(sparse_splits1[1].indices, empty_inds)
self.assertAllEqual(sparse_splits1[1].values, [])
self.assertAllEqual(sparse_splits1[1].dense_shape, [4, 3])
def testInvalidArgumentError(self):
# Test case for GitHub issue 53660.
axis = [1, 2]
with self.assertRaisesRegex(errors.InvalidArgumentError,
r'axis should be a scalar'):
self.evaluate(
sparse_ops.sparse_split(
sp_input=self._SparseTensor_4x6(), num_split=3, axis=axis))
def testBig(self):
# This test was added after discovering a memory allocation bug in the GPU
# kernel that the existing tests did not catch due to being too small.
for n in [250, 2500, 25000]:
indices = np.zeros([n, 2], dtype=np.int64)
indices[:, 0] = np.arange(n)
values = np.zeros([n], dtype=np.float32)
dense_shape = np.array([n, 3], dtype=np.int64)
sp_input = sparse_tensor.SparseTensor(indices, values, dense_shape)
sp_tensors = self.evaluate(
sparse_ops.sparse_split(sp_input=sp_input, num_split=2, axis=0))
self.assertAllEqual(sp_tensors[0].indices, indices[:n // 2])
self.assertAllEqual(sp_tensors[1].indices, indices[n // 2:] - [n // 2, 0])
self.assertAllEqual(sp_tensors[0].values, values[:n // 2])
self.assertAllEqual(sp_tensors[1].values, values[n // 2:])
self.assertAllEqual(sp_tensors[0].dense_shape, [n // 2, 3])
self.assertAllEqual(sp_tensors[1].dense_shape, [n // 2, 3])
def testSparseIndexOutOfBounds(self):
if test_util.is_gpu_available():
# On GPU, out-of-bounds indices are simply ignored.
self.evaluate(
gen_sparse_ops.sparse_split(
split_dim=1,
indices=[[0, 0], [1, 10], [-1, 2]],
values=[1.0, 2.0, 3.0],
shape=[3, 5],
num_split=2,
)
)
else:
# On CPU, out-of-bounds index raises error.
with self.assertRaisesRegex(
(ValueError, errors.InvalidArgumentError), 'out of bounds'
):
self.evaluate(
gen_sparse_ops.sparse_split(
split_dim=1,
indices=[[0, 0], [1, 10], [-1, 2]],
values=[1.0, 2.0, 3.0],
shape=[3, 5],
num_split=2,
)
)
if __name__ == '__main__':
test.main()
@@ -0,0 +1,107 @@
# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for the gradient of `tf.sparse.sparse_dense_matmul()`."""
import numpy as np
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import sparse_tensor
from tensorflow.python.framework import test_util
from tensorflow.python.ops import gradient_checker
from tensorflow.python.ops import sparse_ops
import tensorflow.python.ops.sparse_grad # pylint: disable=unused-import
from tensorflow.python.platform import test
class SparseTensorDenseMatMulGradientTest(test.TestCase):
def _sparsify(self, x, indices_dtype=np.int64):
x[x < 0.5] = 0
non_zero = np.where(x)
x_indices = np.vstack(non_zero).astype(indices_dtype).T
x_values = x[non_zero]
x_shape = x.shape
return sparse_tensor.SparseTensor(
indices=x_indices, values=x_values, dense_shape=x_shape), len(x_values)
def _randomTensor(self,
size,
values_dtype,
adjoint=False,
sparse=False,
indices_dtype=np.int64):
n, m = size
x = np.random.randn(n, m).astype(values_dtype)
if values_dtype in (np.complex64, np.complex128):
x.imag = np.random.randn(n, m)
if adjoint:
x = x.transpose().conj()
if sparse:
return self._sparsify(x, indices_dtype=indices_dtype)
else:
return constant_op.constant(x, dtype=values_dtype)
def _testGradients(self, adjoint_a, adjoint_b, name, values_dtype,
indices_dtype):
n, k, m = np.random.randint(1, 10, size=3)
sp_t, nnz = self._randomTensor(
[n, k],
values_dtype,
adjoint=adjoint_a,
sparse=True,
indices_dtype=indices_dtype)
dense_t = self._randomTensor([k, m], values_dtype, adjoint=adjoint_b)
matmul = sparse_ops.sparse_tensor_dense_matmul(
sp_t, dense_t, adjoint_a=adjoint_a, adjoint_b=adjoint_b, name=name)
with self.cached_session():
dense_t_shape = [m, k] if adjoint_b else [k, m]
sp_t_val_shape = [nnz]
delta = 1 / 16. if values_dtype == np.float16 else 1e-3
tolerance = delta / 2. if values_dtype == np.float16 else 1e-3
err = gradient_checker.compute_gradient_error(
[dense_t, sp_t.values], [dense_t_shape, sp_t_val_shape],
matmul, [n, m],
delta=delta)
print("%s gradient err = %s" % (name, err))
self.assertLess(err, tolerance)
def _testGradientsType(self, values_dtype, indices_dtype):
for adjoint_a in [True, False]:
for adjoint_b in [True, False]:
name = "sparse_tensor_dense_matmul_%s_%s_%s_%s" % (
adjoint_a, adjoint_b, values_dtype.__name__, indices_dtype.__name__)
self._testGradients(adjoint_a, adjoint_b, name, values_dtype,
indices_dtype)
@test_util.run_deprecated_v1
def testGradients(self):
np.random.seed(5) # Fix seed to avoid flakiness
self._testGradientsType(np.float16, np.int64)
self._testGradientsType(np.float32, np.int64)
self._testGradientsType(np.float64, np.int64)
self._testGradientsType(np.complex64, np.int64)
self._testGradientsType(np.complex128, np.int64)
self._testGradientsType(np.float32, np.int32)
self._testGradientsType(np.complex64, np.int32)
if __name__ == "__main__":
test.main()
@@ -0,0 +1,132 @@
# Copyright 2021 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========================================================================
"""Functional tests for deterministic SparseTensorDenseMatMul."""
import random
import numpy as np
from tensorflow.python.framework import config
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import errors
from tensorflow.python.framework import sparse_tensor
from tensorflow.python.framework import test_util
from tensorflow.python.ops import sparse_ops
from tensorflow.python.platform import test
def _gen_data(m, k, n, nnz, row_occupied_rate, data_type, seed):
"""Generate valid input data for tf.sparse.sparse_dense_matmul
returns sparse matrix a (type SparseTensor), dense matrix b (type Tensor)
Parameters:
m: row count of dense version of matrix a / row count of output matrix
k: col count of dense version of matrix a / row count of matrix b
n: col could of matrix b / col count of output matrix
nnz: number of non-zero elements in matrix a
row_occupied_rate: prob that row in a has one or more non-zero element
"""
random.seed(seed)
np.random.seed(seed)
occupied_rows = random.sample(range(m), int(m * row_occupied_rate))
sparse_input_dense_shape = [m, k]
dense_input_shape = (k, n)
indices = []
for _ in range(nnz):
row = random.choice(occupied_rows)
col = random.randint(0, k - 1)
indices.append([row, col])
def maybe_complex(x):
if x.dtype.kind == "c": # complex
return (x + 1j * x) / 2
return x
sparse_values = maybe_complex(
np.random.normal(size=len(indices)).astype(data_type))
dense_values = maybe_complex(
np.random.normal(size=dense_input_shape).astype(data_type))
sparse_input = sparse_tensor.SparseTensor(indices, sparse_values,
sparse_input_dense_shape)
dense_input = constant_op.constant(dense_values)
return sparse_input, dense_input
class SparseTensorDenseMatmulOpDeterminismExceptionsTest(test.TestCase):
"""Test d9m-unimplemented exceptions from SparseTensorDenseMatmulOp.
Test that tf.errors.UnimplementedError is thrown, as appropriate, by the
GPU-specific code-paths through SparseTensorDenseMatmulOp when deterministic
ops are enabled.
This test assumes that sparse_tensor_dense_matmul_op_test.py runs equivalent
test cases when deterministic ops are not enabled and will therefore detect
erroneous exception throwing in those cases.
"""
@test_util.run_gpu_only
@test_util.run_in_graph_and_eager_modes
def testExceptionThrowing(self):
with self.session(), test_util.force_gpu():
for data_type in [
np.float16, np.float32, np.float64, np.complex64, np.complex128
]:
sparse_input, dense_input = _gen_data(
m=5,
k=10,
n=7,
nnz=20,
row_occupied_rate=0.9,
data_type=data_type,
seed=456)
with self.assertRaisesRegex(
errors.UnimplementedError,
"A deterministic GPU implementation of SparseTensorDenseMatmulOp" +
" is not currently available."):
result = sparse_ops.sparse_tensor_dense_matmul(
sparse_input, dense_input)
self.evaluate(result)
class SparseTensorDenseMatmulOpDeterministicTest(test.TestCase):
"""Test that SparseTensorDenseMatul operates reproducibly (on CPU only)."""
@test_util.run_in_graph_and_eager_modes
def testForward(self):
for data_type in [
np.float16, np.float32, np.float64, np.complex64, np.complex128
]: # skipping int32 and bfloat16
sparse_input, dense_input = _gen_data(
m=2430,
k=615,
n=857,
nnz=(1 << 16) + 243,
row_occupied_rate=0.02,
data_type=data_type,
seed=123)
with self.session(), test_util.force_cpu():
result_a = sparse_ops.sparse_tensor_dense_matmul(
sparse_input, dense_input)
for _ in range(5):
result_b = sparse_ops.sparse_tensor_dense_matmul(
sparse_input, dense_input)
self.assertAllEqual(result_a, result_b)
if __name__ == "__main__":
# TODO(reedwm): Merge this file with sparse_tensor_dense_matmul_test.py
config.enable_op_determinism()
test.main()
@@ -0,0 +1,422 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for sparse_ops.sparse_tensor_dense_matmul."""
import sys
import time
from absl import app
import numpy as np
from tensorflow.core.protobuf import config_pb2
from tensorflow.python.client import session
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import ops
from tensorflow.python.framework import sparse_tensor
from tensorflow.python.framework import tensor_shape
from tensorflow.python.framework import test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import sparse_ops
from tensorflow.python.ops import while_loop
from tensorflow.python.platform import test
def _maybe_complex(x):
if x.dtype.kind == "c": # complex
return x + 1j * x
return x
class SparseTensorDenseMatMulTest(test.TestCase):
def _testMatmul(self,
x,
y,
adjoint_a=False,
adjoint_b=False,
indices_dtype=np.int64):
x_mat = np.array(x)
if adjoint_a:
x_mat = x_mat.T.conj()
y_mat = np.array(y)
if adjoint_b:
y_mat = y_mat.T.conj()
np_ans = x_mat.dot(y_mat)
x_indices = np.vstack(np.where(x)).astype(indices_dtype).T
x_values = x[np.where(x)]
x_shape = x.shape
with self.cached_session():
sp_x_value = sparse_tensor.SparseTensorValue(
indices=x_indices, values=x_values, dense_shape=x_shape)
tf_value_ans = sparse_ops.sparse_tensor_dense_matmul(
sp_x_value, y, adjoint_a=adjoint_a, adjoint_b=adjoint_b)
tf_tensor_ans = sparse_ops.sparse_tensor_dense_matmul(
sparse_tensor.SparseTensor.from_value(sp_x_value),
y,
adjoint_a=adjoint_a,
adjoint_b=adjoint_b)
# Ensure that the RHS shape is known at least.
self.assertEqual(tf_value_ans.get_shape()[1], np_ans.shape[1])
self.assertEqual(tf_tensor_ans.get_shape()[1], np_ans.shape[1])
for out in (self.evaluate(tf_value_ans), self.evaluate(tf_tensor_ans)):
if x.dtype == np.float32:
self.assertAllClose(np_ans, out, rtol=1e-4, atol=1e-4)
elif x.dtype == np.float64:
self.assertAllClose(np_ans, out, rtol=1e-6, atol=1e-6)
elif x.dtype == np.float16:
self.assertAllClose(np_ans, out, rtol=1e-3, atol=1e-3)
else:
self.assertAllClose(np_ans, out, rtol=1e-3, atol=1e-3)
def _testBasic(self, value_dtype, indices_dtype=np.int64):
x = _maybe_complex(np.random.rand(10, 10).astype(value_dtype))
x[np.abs(x) < 0.5] = 0 # Make it sparse
y = _maybe_complex(np.random.randn(10, 20).astype(value_dtype))
self._testMatmul(x, y, indices_dtype=indices_dtype)
def testBasic(self):
np.random.seed(127) # Repeatable results
self._testBasic(np.int32)
self._testBasic(np.float16)
self._testBasic(np.float32)
self._testBasic(np.float64)
self._testBasic(np.complex64)
self._testBasic(np.complex128)
self._testBasic(np.int32, indices_dtype=np.int32)
self._testBasic(np.float32, indices_dtype=np.int32)
def testShapeInference(self):
x = np.random.rand(10, 10)
x[np.abs(x) < 0.5] = 0 # Make it sparse
y = np.random.randn(10, 20)
x_indices = np.vstack(np.where(x)).astype(np.int64).T
x_values = x[np.where(x)]
x_shape = x.shape
with ops.Graph().as_default():
x_st = sparse_tensor.SparseTensor(x_indices, x_values, x_shape)
result = sparse_ops.sparse_tensor_dense_matmul(x_st, y)
self.assertEqual(result.get_shape(), (10, 20))
x_shape_unknown = array_ops.placeholder(dtype=dtypes.int64, shape=None)
x_st_shape_unknown = sparse_tensor.SparseTensor(x_indices, x_values,
x_shape_unknown)
result_left_shape_unknown = sparse_ops.sparse_tensor_dense_matmul(
x_st_shape_unknown, y)
self.assertEqual(result_left_shape_unknown.get_shape().as_list(),
[None, 20])
x_shape_inconsistent = [10, 15]
x_st_shape_inconsistent = sparse_tensor.SparseTensor(
x_indices, x_values, x_shape_inconsistent)
with self.assertRaisesRegex(ValueError, "Dimensions must be equal"):
sparse_ops.sparse_tensor_dense_matmul(x_st_shape_inconsistent, y)
@test_util.run_in_graph_and_eager_modes(use_gpu=False)
def testInvalidIndicesForSparseTensorDenseMatmul(self):
# TODO(b/169813429): Make GPU kernel return nice errors too.
indices = np.array([[1, 10]]).astype(np.int64)
values = np.array([10]).astype(np.float32)
shape = [3, 2]
sparse_t = sparse_tensor.SparseTensor(indices, values, shape)
# Test multiplying by both a small and large dense matrix, to hit
# both cases in the kernel.
dense_t = np.array([[1] * 5, [2] * 5], dtype=np.float32)
with self.assertRaisesOpError("k .10. from index.0,1. out of bounds .>=2."):
self.evaluate(sparse_ops.sparse_tensor_dense_matmul(sparse_t, dense_t))
dense_t = np.array([[1] * 500, [2] * 500], dtype=np.float32)
with self.assertRaisesOpError("k .10. from index.0,1. out of bounds .>=2."):
self.evaluate(sparse_ops.sparse_tensor_dense_matmul(sparse_t, dense_t))
# Repeat with adjoint_a, to get a different error.
dense_t = np.array([[1] * 5, [2] * 5, [3] * 5], dtype=np.float32)
with self.assertRaisesOpError("m .10. from index.0,1. out of bounds .>=2."):
self.evaluate(
sparse_ops.sparse_tensor_dense_matmul(
sparse_t, dense_t, adjoint_a=True))
dense_t = np.array([[1] * 500, [2] * 500, [3] * 500], dtype=np.float32)
with self.assertRaisesOpError("m .10. from index.0,1. out of bounds .>=2."):
self.evaluate(
sparse_ops.sparse_tensor_dense_matmul(
sparse_t, dense_t, adjoint_a=True))
def testUnorderedIndicesForSparseTensorDenseMatmul(self):
indices = np.array([(2, 1), (0, 0)]).astype(np.int64)
values = np.array([10, 11]).astype(np.float32)
shape = [3, 2]
sparse_t = sparse_tensor.SparseTensor(indices, values, shape)
dense_t = np.array([[1] * 500, [2] * 500], dtype=np.float32)
expected_t = np.array([[11] * 500, [0] * 500, [20] * 500], dtype=np.float32)
self.assertAllClose(
expected_t, sparse_ops.sparse_tensor_dense_matmul(sparse_t, dense_t))
@test_util.run_gpu_only
def testInvalidIndicesForSparseTensorDenseMatmulOnGPU(self):
indices = np.array([[1, 10]]).astype(np.int64)
values = np.array([10]).astype(np.float32)
shape = [3, 2]
sparse_t = sparse_tensor.SparseTensor(indices, values, shape)
# Test multiplying by both a small and large dense matrix, to hit
# both cases in the kernel.
dense_t = np.array([[1] * 5, [2] * 5], dtype=np.float32)
expected_t = np.array([[0] * 5, [np.nan] * 5, [0] * 5], dtype=np.float32)
self.assertAllClose(
expected_t, sparse_ops.sparse_tensor_dense_matmul(sparse_t, dense_t))
dense_t = np.array([[1] * 500, [2] * 500], dtype=np.float32)
expected_t = np.array([[0] * 500, [np.nan] * 500, [0] * 500],
dtype=np.float32)
self.assertAllClose(
expected_t, sparse_ops.sparse_tensor_dense_matmul(sparse_t, dense_t))
# Repeat with adjoint_a, now the error is that the sparse index
# is OOO w.r.t. the output. The GPU kernel can't do much here,
# so it just doesn't accumulate.
dense_t = np.array([[1] * 5, [2] * 5, [3] * 5], dtype=np.float32)
expected_t = np.array([[0] * 5, [0] * 5], dtype=np.float32)
self.assertAllClose(
expected_t,
sparse_ops.sparse_tensor_dense_matmul(
sparse_t, dense_t, adjoint_a=True))
dense_t = np.array([[1] * 500, [2] * 500, [3] * 500], dtype=np.float32)
expected_t = np.array([[0] * 500, [0] * 500], dtype=np.float32)
self.assertAllClose(
expected_t,
sparse_ops.sparse_tensor_dense_matmul(
sparse_t, dense_t, adjoint_a=True))
def _testLarge(self, np_dtype):
r1 = np.random.randint(6000, 20000)
r2 = np.random.randint(1, 10)
r3 = np.random.randint(1, 10)
for m, k, n in [(r1, r2, r3),
(r2, r1, r3),
(r2, r3, r1)]:
x = _maybe_complex(np.random.rand(m, k).astype(np_dtype))
x[np.abs(x) < 0.8] = 0
y = _maybe_complex(np.random.randn(k, n).astype(np_dtype))
self._testMatmul(x, y, adjoint_a=False, adjoint_b=False)
self._testMatmul(x.transpose(), y, adjoint_a=True, adjoint_b=False)
self._testMatmul(x, y.transpose(), adjoint_a=False, adjoint_b=True)
self._testMatmul(
x.transpose(), y.transpose(), adjoint_a=True, adjoint_b=True)
# Tests setting one dimension to be a high value.
def testLarge(self):
np.random.seed(127) # Repeatable results
self._testLarge(np.float32)
self._testLarge(np.float64)
self._testLarge(np.complex64)
self._testLarge(np.complex128)
# Tests random sized matrices.
def testFloatRandom(self):
np.random.seed(127) # Repeatable results
for _ in range(8):
for adjoint_a in [True, False]:
for adjoint_b in [True, False]:
for thresh in [0.0, 0.2, 0.8, 1.0]:
n, k, m = np.random.randint(1, 100, size=3)
x = np.random.rand(n, k).astype(np.float32)
x[x < thresh] = 0 # Make it sparse
y = np.random.randn(k, m).astype(np.float32)
x = x.transpose() if adjoint_a else x
y = y.transpose() if adjoint_b else y
self._testMatmul(x, y, adjoint_a, adjoint_b)
def _sparse_tensor_dense_vs_dense_matmul_benchmark_dense(x, y, adjoint_a,
adjoint_b):
def body(t, prev):
with ops.control_dependencies([prev]):
return (t + 1, math_ops.matmul(
x,
y,
transpose_a=adjoint_a,
transpose_b=adjoint_b,
a_is_sparse=True,
b_is_sparse=False))
t0 = constant_op.constant(0)
v0 = constant_op.constant(0.0)
def _timeit(iterations, _):
(_, final) = while_loop.while_loop(
lambda t, _: t < iterations,
body, (t0, v0),
parallel_iterations=1,
back_prop=False,
shape_invariants=(tensor_shape.TensorShape(
()), tensor_shape.TensorShape(None)))
return [final]
return _timeit
def _sparse_tensor_dense_vs_dense_matmul_benchmark_sparse(x_ind, x_val, x_shape,
y, adjoint_a,
adjoint_b):
sp_x = sparse_tensor.SparseTensor(
indices=x_ind, values=x_val, dense_shape=x_shape)
def body(t, prev):
with ops.control_dependencies([prev]):
return (t + 1, sparse_ops.sparse_tensor_dense_matmul(
sp_x, y, adjoint_a=adjoint_a, adjoint_b=adjoint_b))
t0 = constant_op.constant(0)
v0 = constant_op.constant(0.0)
def _timeit(iterations, _):
(_, final) = while_loop.while_loop(
lambda t, _: t < iterations,
body, (t0, v0),
parallel_iterations=1,
back_prop=False,
shape_invariants=(tensor_shape.TensorShape(
()), tensor_shape.TensorShape(None)))
return [final]
return _timeit
def sparse_tensor_dense_vs_dense_matmul_benchmark(thresh,
m,
k,
n,
adjoint_a,
adjoint_b,
use_gpu,
skip_dense=False):
config = config_pb2.ConfigProto()
config.allow_soft_placement = True
# Configurable for benchmarking:
# config.intra_op_parallelism_threads = 100
# config.gpu_options.per_process_gpu_memory_fraction = 0.3
np.random.seed([6, 117]) # Reproducibility
x = np.random.rand(m, k).astype(np.float32)
x[x < thresh] = 0
y = np.random.randn(k, n).astype(np.float32)
if adjoint_a:
x = x.T
if adjoint_b:
y = y.T
def _timer(sess, ops_fn, iterations):
# Warm in
sess.run(ops_fn(10, sess))
# Timing run
start = time.time()
sess.run(ops_fn(iterations, sess))
end = time.time()
return (end - start) / (1.0 * iterations) # Average runtime per iteration
# Using regular matmul, marking one of the matrices as dense.
if skip_dense:
delta_dense = float("nan")
else:
with session.Session(config=config, graph=ops.Graph()) as sess:
if not use_gpu:
with ops.device("/cpu:0"):
x_t = constant_op.constant(x)
y_t = constant_op.constant(y)
ops_fn = _sparse_tensor_dense_vs_dense_matmul_benchmark_dense(
x_t, y_t, adjoint_a, adjoint_b)
else:
with ops.device("/device:GPU:0"):
x_t = constant_op.constant(x)
y_t = constant_op.constant(y)
ops_fn = _sparse_tensor_dense_vs_dense_matmul_benchmark_dense(
x_t, y_t, adjoint_a, adjoint_b)
delta_dense = _timer(sess, ops_fn, 200)
# Using sparse_tensor_dense_matmul.
with session.Session("", config=config, graph=ops.Graph()) as sess:
if not use_gpu:
with ops.device("/cpu:0"):
x_ind = constant_op.constant(np.vstack(np.where(x)).astype(np.int64).T)
x_val = constant_op.constant(x[np.where(x)])
x_shape = constant_op.constant(np.array(x.shape).astype(np.int64))
y_t = constant_op.constant(y)
ops_fn = _sparse_tensor_dense_vs_dense_matmul_benchmark_sparse(
x_ind, x_val, x_shape, y_t, adjoint_a, adjoint_b)
else:
with ops.device("/device:GPU:0"):
x_ind = constant_op.constant(np.vstack(np.where(x)).astype(np.int64).T)
x_val = constant_op.constant(x[np.where(x)])
x_shape = constant_op.constant(np.array(x.shape).astype(np.int64))
y_t = constant_op.constant(y)
ops_fn = _sparse_tensor_dense_vs_dense_matmul_benchmark_sparse(
x_ind, x_val, x_shape, y_t, adjoint_a, adjoint_b)
delta_sparse = _timer(sess, ops_fn, 200)
print("%g \t %d \t %s \t %d \t %d \t %g \t %g \t %g" %
(1 - thresh, n, use_gpu, m, k, delta_dense, delta_sparse,
delta_sparse / delta_dense))
def main(_):
print("DenseDense MatMul (w/ Sparse Flag) vs. SparseTensorDense MatMul")
print("Matrix sizes:")
print(" A sparse [m, k] with % nonzero values between 1% and 80%")
print(" B dense [k, n]")
print("")
print("% nnz \t n \t gpu \t m \t k \t dt(dense) \t dt(sparse) "
"\t dt(sparse)/dt(dense)")
for thresh in (0.99, 0.8, 0.5, 0.2):
for n in (50, 100):
for use_gpu in (True, False):
for m in (100, 1000):
for k in (100, 1000):
sparse_tensor_dense_vs_dense_matmul_benchmark(
thresh, m, k, n, False, False, use_gpu=use_gpu)
# Enable for large scale benchmarks, these ones take a long time to run.
#
# for use_gpu in (True, False):
# sparse_tensor_dense_vs_dense_matmul_benchmark(
# thresh=0.99, m=1000000, k=1000, n=100, adjoint_a=False,
# adjoint_b=False, use_gpu=use_gpu, skip_dense=True)
if __name__ == "__main__":
if "--benchmarks" in sys.argv:
sys.argv.remove("--benchmarks")
app.run() # pylint: disable=no-value-for-parameter
else:
test.main()
@@ -0,0 +1,241 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for SparseTensorsMap."""
import numpy as np
from tensorflow.python.client import session
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import ops
from tensorflow.python.framework import sparse_tensor as sparse_tensor_lib
from tensorflow.python.framework import test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import array_ops_stack
from tensorflow.python.ops import sparse_ops
from tensorflow.python.ops import variables
from tensorflow.python.platform import benchmark
from tensorflow.python.platform import test
# pylint: disable=protected-access
add_sparse_to_tensors_map = sparse_ops._add_sparse_to_tensors_map
add_many_sparse_to_tensors_map = sparse_ops._add_many_sparse_to_tensors_map
take_many_sparse_from_tensors_map = (
sparse_ops._take_many_sparse_from_tensors_map)
# pylint: enable=protected-access
class SparseTensorsMapTest(test.TestCase):
def _SparseTensorPlaceholder(self, dtype=None):
if dtype is None:
dtype = dtypes.int32
return sparse_tensor_lib.SparseTensor(
array_ops.placeholder(dtypes.int64),
array_ops.placeholder(dtype), array_ops.placeholder(dtypes.int64))
def _SparseTensorValue_5x6(self, permutation):
ind = np.array([[0, 0], [1, 0], [1, 3], [1, 4], [3, 2],
[3, 3]]).astype(np.int64)
val = np.array([0, 10, 13, 14, 32, 33]).astype(np.int32)
ind = ind[permutation]
val = val[permutation]
shape = np.array([5, 6]).astype(np.int64)
return sparse_tensor_lib.SparseTensorValue(ind, val, shape)
def _SparseTensorValue_3x4(self, permutation):
ind = np.array([[0, 0], [1, 0], [1, 2], [1, 3], [2, 2],
[2, 3]]).astype(np.int64)
val = np.array([0, 10, 13, 14, 32, 33]).astype(np.int32)
ind = ind[permutation]
val = val[permutation]
shape = np.array([3, 4]).astype(np.int64)
return sparse_tensor_lib.SparseTensorValue(ind, val, shape)
def _SparseTensorValue_1x1x1(self):
ind = np.array([[0, 0, 0]]).astype(np.int64)
val = np.array([0]).astype(np.int32)
shape = np.array([3, 4, 5]).astype(np.int64)
return sparse_tensor_lib.SparseTensorValue(ind, val, shape)
@test_util.run_deprecated_v1
def testAddTakeMany(self):
with self.session(graph=ops.Graph(), use_gpu=False) as sess:
sp_input0 = self._SparseTensorValue_5x6(np.arange(6))
sp_input1 = self._SparseTensorValue_3x4(np.arange(6))
handle0 = add_sparse_to_tensors_map(sp_input0, shared_name="a")
handle1 = add_sparse_to_tensors_map(sp_input1, shared_name="a")
self.assertEqual(handle0.get_shape(), ())
handles_concat = array_ops_stack.stack([handle0, handle1])
sp_out = take_many_sparse_from_tensors_map(
sparse_map_op=handle0.op, sparse_handles=handles_concat)
combined_indices, combined_values, combined_shape = self.evaluate(sp_out)
self.assertAllEqual(combined_indices[:6, 0], [0] * 6) # minibatch 0
self.assertAllEqual(combined_indices[:6, 1:], sp_input0[0])
self.assertAllEqual(combined_indices[6:, 0], [1] * 6) # minibatch 1
self.assertAllEqual(combined_indices[6:, 1:], sp_input1[0])
self.assertAllEqual(combined_values[:6], sp_input0[1])
self.assertAllEqual(combined_values[6:], sp_input1[1])
self.assertAllEqual(combined_shape, [2, 5, 6])
@test_util.run_deprecated_v1
def testFeedAddTakeMany(self):
with self.session(use_gpu=False) as sess:
sp_input = self._SparseTensorPlaceholder()
input0_val = self._SparseTensorValue_5x6(np.arange(6))
input1_val = self._SparseTensorValue_3x4(np.arange(6))
handle = add_sparse_to_tensors_map(sp_input)
handle0_value = sess.run(handle, feed_dict={sp_input: input0_val})
handle1_value = sess.run(handle, feed_dict={sp_input: input1_val})
sparse_handles = ops.convert_to_tensor(
[handle0_value, handle1_value], dtype=dtypes.int64)
sp_roundtrip = take_many_sparse_from_tensors_map(
sparse_map_op=handle.op, sparse_handles=sparse_handles)
combined_indices, combined_values, combined_shape = self.evaluate(
sp_roundtrip)
self.assertAllEqual(combined_indices[:6, 0], [0] * 6) # minibatch 0
self.assertAllEqual(combined_indices[:6, 1:], input0_val[0])
self.assertAllEqual(combined_indices[6:, 0], [1] * 6) # minibatch 1
self.assertAllEqual(combined_indices[6:, 1:], input1_val[0])
self.assertAllEqual(combined_values[:6], input0_val[1])
self.assertAllEqual(combined_values[6:], input1_val[1])
self.assertAllEqual(combined_shape, [2, 5, 6])
@test_util.run_deprecated_v1
def testAddManyTakeManyRoundTrip(self):
with self.session(use_gpu=False) as sess:
# N == 4 because shape_value == [4, 5]
indices_value = np.array([[0, 0], [0, 1], [2, 0]], dtype=np.int64)
values_value = np.array([b"a", b"b", b"c"])
shape_value = np.array([4, 5], dtype=np.int64)
sparse_tensor = self._SparseTensorPlaceholder(dtype=dtypes.string)
handles = add_many_sparse_to_tensors_map(sparse_tensor)
roundtrip = take_many_sparse_from_tensors_map(
sparse_map_op=handles.op, sparse_handles=handles)
handles_value, roundtrip_value = sess.run(
[handles, roundtrip],
feed_dict={
sparse_tensor.indices: indices_value,
sparse_tensor.values: values_value,
sparse_tensor.dense_shape: shape_value
})
self.assertEqual(handles_value.shape, (4,))
self.assertAllEqual(roundtrip_value.indices, indices_value)
self.assertAllEqual(roundtrip_value.values, values_value)
self.assertAllEqual(roundtrip_value.dense_shape, shape_value)
@test_util.run_deprecated_v1
def testDeserializeFailsInconsistentRank(self):
with self.session(use_gpu=False) as sess:
sp_input = self._SparseTensorPlaceholder()
input0_val = self._SparseTensorValue_5x6(np.arange(6))
input1_val = self._SparseTensorValue_1x1x1()
handle = add_sparse_to_tensors_map(sp_input)
handle0_value = sess.run(handle, feed_dict={sp_input: input0_val})
handle1_value = sess.run(handle, feed_dict={sp_input: input1_val})
handle_concat = ops.convert_to_tensor(
[handle0_value, handle1_value], dtype=dtypes.int64)
sp_roundtrip = take_many_sparse_from_tensors_map(
sparse_map_op=handle.op, sparse_handles=handle_concat)
with self.assertRaisesOpError(
r"Inconsistent rank across SparseTensors: rank prior to "
r"SparseTensor\[1\] was: 3 but rank of SparseTensor\[1\] is: 4"):
self.evaluate(sp_roundtrip)
@test_util.run_deprecated_v1
def testTakeManyFailsWrongInputOp(self):
with self.session(use_gpu=False) as sess:
input_val = self._SparseTensorValue_5x6(np.arange(6))
handle = add_sparse_to_tensors_map(input_val)
handle_value = self.evaluate(handle)
bad_handle = handle_value + 10
sp_roundtrip = take_many_sparse_from_tensors_map(
sparse_map_op=handle.op, sparse_handles=[handle_value, bad_handle])
with self.assertRaisesOpError(r"Unable to find SparseTensor: 10"):
self.evaluate(sp_roundtrip)
class BenchmarkSparseTensorsMapVsSerialization(test.Benchmark):
def benchmarkVeryLarge2DFloatSparseTensor(self):
np.random.seed(127)
num_elements = 10000
batch_size = 64
indices_batch = np.random.randint(
batch_size, size=num_elements, dtype=np.int64)
indices_value = np.arange(num_elements, dtype=np.int64)
indices = np.asarray(
sorted(zip(indices_batch, indices_value)), dtype=np.int64)
values = ["feature_value_for_embedding_lookup"] * num_elements
shape = np.asarray([batch_size, num_elements], dtype=np.int64)
with session.Session(config=benchmark.benchmark_config()) as sess:
with ops.device("/cpu:0"):
indices = variables.Variable(indices)
values = variables.Variable(values)
shape = variables.Variable(shape)
st = sparse_tensor_lib.SparseTensor(indices, values, shape)
st_handles = add_many_sparse_to_tensors_map(st)
st_roundtrip = take_many_sparse_from_tensors_map(
sparse_map_op=st_handles.op, sparse_handles=st_handles)
st_roundtrip_op = st_roundtrip.values.op
st_serialized = sparse_ops.serialize_many_sparse(st)
st_deserialized = sparse_ops.deserialize_many_sparse(
st_serialized, dtype=values.dtype)
st_deserialized_op = st_deserialized.values.op
self.evaluate(variables.global_variables_initializer())
st_roundtrip_values = self.evaluate(st_roundtrip)
st_deserialized_values = self.evaluate(st_deserialized)
np.testing.assert_equal(st_roundtrip_values.values,
st_deserialized_values.values)
np.testing.assert_equal(st_roundtrip_values.indices,
st_deserialized_values.indices)
np.testing.assert_equal(st_roundtrip_values.dense_shape,
st_deserialized_values.dense_shape)
self.run_op_benchmark(
sess,
st_roundtrip_op,
min_iters=2000,
name="benchmark_very_large_2d_float_st_tensor_maps")
self.run_op_benchmark(
sess,
st_deserialized_op,
min_iters=2000,
name="benchmark_very_large_2d_float_st_serialization")
if __name__ == "__main__":
test.main()
@@ -0,0 +1,193 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for tensorflow.kernels.sparse_op."""
from absl.testing import parameterized
import numpy as np
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import errors
from tensorflow.python.framework import ops
from tensorflow.python.framework import test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import sparse_ops
from tensorflow.python.platform import test
@test_util.with_eager_op_as_function
class SparseToDenseTest(test.TestCase, parameterized.TestCase):
def testInt(self):
tf_ans = sparse_ops.sparse_to_dense([1, 3], [5], 1, 0)
np_ans = np.array([0, 1, 0, 1, 0]).astype(np.int32)
self.assertAllClose(np_ans, tf_ans)
@parameterized.parameters(
dtypes.bfloat16, dtypes.float16, dtypes.float32, dtypes.float64
)
def testFloatTypes(self, dtype):
tf_ans = sparse_ops.sparse_to_dense(
[1, 3], [5], array_ops.constant(1.0, dtype=dtype), 0.0
)
np_ans = np.array([0, 1, 0, 1, 0]).astype(dtype.as_numpy_dtype)
self.assertAllClose(np_ans, tf_ans)
def testComplex(self):
for dtype in [dtypes.complex64, dtypes.complex128]:
tf_val = math_ops.cast(
constant_op.constant([1.0 + 1.0j, 2.0 - 2.0j]), dtypes.complex128)
tf_ans = sparse_ops.sparse_tensor_to_dense(sparse_ops.from_dense(tf_val))
self.assertAllClose(tf_val, tf_ans)
def testEmptyNonZeros(self):
indices = array_ops.constant([], dtype=dtypes.int32)
values = array_ops.constant([], dtype=dtypes.float32)
tf_ans = sparse_ops.sparse_to_dense(indices, [5], values, 0.0)
np_ans = np.array([0, 0, 0, 0, 0]).astype(np.float32)
self.assertAllClose(np_ans, tf_ans)
def testString(self):
tf_ans = sparse_ops.sparse_to_dense([1, 3], [5], "a", "b")
np_ans = np.array(["b", "a", "b", "a", "b"]).astype(np.bytes_)
self.assertAllEqual(np_ans, tf_ans)
def testSetValue(self):
tf_ans = sparse_ops.sparse_to_dense([1, 3], [5], [1, 2], -1)
np_ans = np.array([-1, 1, -1, 2, -1]).astype(np.int32)
self.assertAllClose(np_ans, tf_ans)
def testSetSingleValue(self):
tf_ans = sparse_ops.sparse_to_dense([1, 3], [5], 1, -1)
np_ans = np.array([-1, 1, -1, 1, -1]).astype(np.int32)
self.assertAllClose(np_ans, tf_ans)
def test2d(self):
tf_ans = sparse_ops.sparse_to_dense([[1, 3], [2, 0]], [3, 4], 1, -1)
np_ans = np.array([[-1, -1, -1, -1],
[-1, -1, -1, 1],
[1, -1, -1, -1]]).astype(np.int32)
self.assertAllClose(np_ans, tf_ans)
def testZeroDefault(self):
x = sparse_ops.sparse_to_dense(2, [4], 7)
self.assertAllEqual(x, [0, 0, 7, 0])
def test3d(self):
tf_ans = sparse_ops.sparse_to_dense([[1, 3, 0], [2, 0, 1]], [3, 4, 2], 1,
-1)
np_ans = np.ones((3, 4, 2), dtype=np.int32) * -1
np_ans[1, 3, 0] = 1
np_ans[2, 0, 1] = 1
self.assertAllClose(np_ans, tf_ans)
def testBadShape(self):
with self.assertRaisesRegex((ValueError, errors.InvalidArgumentError),
"must be rank 1"):
sparse_ops.sparse_to_dense([1, 3], [[5], [3]], 1, -1)
def testBadValue(self):
with self.assertRaisesRegex((ValueError, errors.InvalidArgumentError),
r"sparse_values has incorrect shape \[2,1\], "
r"should be \[\] or \[2\]"):
self.evaluate(sparse_ops.sparse_to_dense([1, 3], [5], [[5], [3]], -1))
def testBadNumValues(self):
with self.assertRaisesRegex(
(ValueError, errors.InvalidArgumentError),
r"sparse_values has incorrect shape \[3\], should be \[\] or \[2\]"):
self.evaluate(sparse_ops.sparse_to_dense([1, 3], [5], [1, 2, 3], -1))
def testBadDefault(self):
with self.assertRaisesRegex((ValueError, errors.InvalidArgumentError),
"default_value should be a scalar"):
self.evaluate(sparse_ops.sparse_to_dense([1, 3], [5], [1, 2], [0]))
@test_util.disable_xla("XLA does not check validity for SparseToDense")
def testOutOfBoundsIndicesWithWithoutValidation(self):
# The GPU implementation doesn't print the contents of the invalid inputs,
# since the overhead of memory copy between device to host is large.
# Therefore, the following three tests on invalid inputs will distinguish
# the reference error messages between GPUs and CPUs.
error_msg = (r"out of bounds" if test_util.is_gpu_available() else
r"indices\[1\] = \[10\] is out of bounds: need 0 <= "
"index < \[5\]")
with self.assertRaisesRegex((ValueError, errors.InvalidArgumentError),
error_msg):
self.evaluate(
sparse_ops.sparse_to_dense([[1], [10]], [5], [1.0, 1.0], 0.0))
# When validate_indices=False, the GPU kernel won't check out-of-bound
# access. Therefore, we skip the following test.
if not test_util.is_gpu_available():
# Disable checks, the allocation should still fail.
with self.assertRaisesRegex((ValueError, errors.InvalidArgumentError),
"out of bounds"):
self.evaluate(
sparse_ops.sparse_to_dense([[1], [10]], [5], [-1.0, 1.0],
0.0,
validate_indices=False))
@test_util.disable_xla("XLA does not check validity for SparseToDense")
def testRepeatingIndicesWithWithoutValidation(self):
error_msg = (r"indices\[1\] is repeated" if test_util.is_gpu_available()
else r"indices\[1\] = \[1\] is repeated")
with self.assertRaisesRegex((ValueError, errors.InvalidArgumentError),
error_msg):
self.evaluate(
sparse_ops.sparse_to_dense([[1], [1]], [5], [-1.0, 1.0], 0.0))
# Disable checks
self.evaluate(
sparse_ops.sparse_to_dense([[1], [1]], [5], [-1.0, 1.0],
0.0,
validate_indices=False))
@test_util.disable_xla("XLA does not check validity for SparseToDense")
def testUnsortedIndicesWithWithoutValidation(self):
error_msg = (r"indices\[1\] is out of order"
if test_util.is_gpu_available() else
r"indices\[1\] = \[1\] is out of order")
with self.assertRaisesRegex((ValueError, errors.InvalidArgumentError),
error_msg):
self.evaluate(
sparse_ops.sparse_to_dense([[2], [1]], [5], [-1.0, 1.0], 0.0))
# Disable checks
self.evaluate(
sparse_ops.sparse_to_dense([[2], [1]], [5], [-1.0, 1.0],
0.0,
validate_indices=False))
def testShapeInferenceKnownShape(self):
with ops.Graph().as_default():
indices = array_ops.placeholder(dtypes.int64)
shape = [4, 5, 6]
output = sparse_ops.sparse_to_dense(indices, shape, 1, 0)
self.assertEqual(output.get_shape(), [4, 5, 6])
shape = array_ops.placeholder(dtypes.int64, shape=(3,))
output = sparse_ops.sparse_to_dense(indices, shape, 1, 0)
self.assertEqual(output.get_shape().as_list(), [None, None, None])
def testShapeInferenceUnknownShape(self):
with ops.Graph().as_default():
indices = array_ops.placeholder(dtypes.int64)
shape = array_ops.placeholder(dtypes.int64)
output = sparse_ops.sparse_to_dense(indices, shape, 1, 0)
self.assertIsNone(output.get_shape().ndims)
if __name__ == "__main__":
test.main()
@@ -0,0 +1,192 @@
# Copyright 2021 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for deterministic functionality of SparseSoftmaxCrossEntropyWithLogits op."""
import numpy as np
from tensorflow.python.eager import backprop
from tensorflow.python.framework import config
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import errors_impl
from tensorflow.python.framework import test_util
from tensorflow.python.kernel_tests.sparse_ops import sparse_xent_op_test_base
# The following import is required to register the gradient function.
from tensorflow.python.ops.nn_grad import _SparseSoftmaxCrossEntropyWithLogitsGrad # pylint: disable=unused-import
from tensorflow.python.ops import gen_nn_ops
from tensorflow.python.ops import nn_ops
from tensorflow.python.platform import test
class SparseXentOpDeterminismExceptionsTest(test.TestCase):
"""Test d9m-unimplemented exceptions from SparseSoftmaxXentWithLogitsOp.
Test that tf.errors.UnimplementedError is thrown, as
appropriate, by the GPU code-paths through SparseSoftmaxXentWithLogitsOp when
deterministic ops are enabled.
This test assumes that sparse_xent_op_test.py runs equivalent test cases
when deterministic ops are not enabled and will therefore detect erroneous
exception throwing in those cases.
"""
@test_util.run_gpu_only
@test_util.run_in_graph_and_eager_modes
def testExceptionThrowing(self):
with self.session(), test_util.force_gpu():
for features_dtype in [dtypes.float16, dtypes.float32]:
for labels_dtype in [dtypes.int32, dtypes.int64]:
features = constant_op.constant([[0.3, 0.5], [0.2, 0.6]],
dtype=features_dtype)
labels = constant_op.constant([1, 0], dtype=labels_dtype)
with self.assertRaisesRegex(
errors_impl.UnimplementedError,
"The GPU implementation of SparseSoftmaxCrossEntropyWithLogits " +
"that would have been executed is not deterministic. Note that " +
"the Python API uses an alternative, deterministic, " +
"GPU-accelerated path when determinsim is enabled."):
result = gen_nn_ops.sparse_softmax_cross_entropy_with_logits(
features=features, labels=labels)
self.evaluate(result)
class SparseXentOpDeterministicTest(
sparse_xent_op_test_base.SparseXentOpTestBase):
"""Test that SparseSoftmaxCrossEntropyWithLogits operates reproducibly.
Inheriting from sparse_xent_op_test_base.SparseXentOpTestBase ensures that
regular op functionality is correct when the deterministic code-path is
selected.
Note that because nn_ops.sparse_softmax_cross_entropy_with_logits_v2 calls
nn_ops.sparse_softmax_cross_entropy_with_logits directly, the focus of
testing is on the former in order to test both.
"""
def _randomInts(self, shape, high, dtype):
return constant_op.constant(
np.random.randint(low=0, high=high, size=shape).astype(dtype))
def _randomFloats(self, shape, dtype):
return constant_op.constant(
(2 * np.random.random_sample(shape) - 1).astype(dtype))
def _generateInputs(self, labels_dtype, logits_dtype, seed):
batch_size = 1024
classes_count = 1000
np.random.seed(seed)
labels_shape = (batch_size)
labels = self._randomInts(
labels_shape, high=classes_count, dtype=labels_dtype)
logits_shape = (batch_size, classes_count)
logits = self._randomFloats(logits_shape, logits_dtype)
return labels, logits
@test_util.run_in_graph_and_eager_modes
def testForward(self):
with self.cached_session():
for logits_dtype in [np.float16, np.float32, np.float64, \
dtypes.bfloat16.as_numpy_dtype]:
for labels_dtype in [np.int32, np.int64]:
for trial in range(5):
seed = 123 + trial
labels, logits = self._generateInputs(
labels_dtype, logits_dtype, seed=seed)
result_a = nn_ops.sparse_softmax_cross_entropy_with_logits_v2(
labels=labels, logits=logits)
result_b = nn_ops.sparse_softmax_cross_entropy_with_logits_v2(
labels=labels, logits=logits)
self.assertAllEqual(result_a, result_b)
@test_util.run_in_graph_and_eager_modes
def testBackward(self):
with self.cached_session():
for logits_dtype in [np.float16, np.float32, np.float64, \
dtypes.bfloat16.as_numpy_dtype]:
for labels_dtype in [np.int32, np.int64]:
labels, logits = self._generateInputs(
labels_dtype, logits_dtype, seed=456)
output_shape = labels.shape[0]
def gradients(seed):
np.random.seed(seed)
upstream_gradients = self._randomFloats(output_shape, logits_dtype)
with backprop.GradientTape(persistent=True) as tape:
tape.watch(logits)
op_output = nn_ops.sparse_softmax_cross_entropy_with_logits_v2(
labels=labels, logits=logits)
gradient_injector_output = op_output * upstream_gradients
return tape.gradient(gradient_injector_output, logits)
for trial in range(5):
seed = 456 + trial
result_a = gradients(seed=seed)
result_b = gradients(seed=seed)
self.assertAllEqual(result_a, result_b)
# Modifications to the parent class
# (sparse_xent_op_test_base.SparseXentOpTestBase) follow
def testInvalidLabelGPU(self):
"""Modified test for invalid labels on GPU.
When running on GPU, the pre-existing, nondeterministic implementation
produces NaN (in both the forward and backward directions) for results
associated with invalid labels (less than zero or greater than the number of
classes minus one). However, while the deterministic implementation also
produces NaN in the forward direction, it produces zeros in the backward
direction.
"""
self._testInvalidLabelGPU(invalid_label_gradient=0.0)
def testInvalidLabelCPU(self):
"""Modified test for invalid labels on CPU.
When running on CPU, the pre-existing, nondeterministic implementation
throws a custom exception when any of the label values are invalid (less
than zero or greater than the number of classes minus one). However, in the
deterministic implementation, tf.gather throws an exception instead.
"""
self._testInvalidLabelCPU(
expected_regex="indices\[0\] = 4 is not in \[0, 4\)")
def testLabelsPlaceholderScalar(self):
"""Test exception-throwing for non-statically-shaped, zero-rank labels.
The deterministic implementation cannot check for this case because it does
not have a specific implementation of SparseSoftmaxXentWithLogitsOp.
Instead tf.gather, which is used to create the deterministic implementation,
throws an error.
"""
self._testLabelsPlaceholderScalar(
expected_error_message="Expected batch_dims in the range \[0, 0\], " +
"but got 1")
def testScalarHandling(self):
"""Test exception-throwing for non-statically-shaped, zero-rank labels.
The deterministic implementation cannot check for this case because it does
not have a specific implementation of SparseSoftmaxXentWithLogitsOp.
Instead tf.gather, which is used to create the deterministic implementation,
throws an error.
"""
self._testScalarHandling(
expected_regex="Expected batch_dims in the range \[0, 0\], but got 1.*")
if __name__ == "__main__":
# TODO(reedwm): Merge this test with sparse_xent_op_test.py.
config.enable_op_determinism()
test.main()
@@ -0,0 +1,132 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for SparseSoftmaxCrossEntropyWithLogits op."""
import sys
import time
from absl import app
import numpy as np
from tensorflow.core.protobuf import config_pb2
from tensorflow.python.client import session
from tensorflow.python.framework import ops as ops_lib
from tensorflow.python.framework import test_util
from tensorflow.python.kernel_tests.sparse_ops import sparse_xent_op_test_base
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import array_ops_stack
from tensorflow.python.ops import gradients_impl
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import sparse_ops
from tensorflow.python.ops.nn_grad import _SparseSoftmaxCrossEntropyWithLogitsGrad # pylint: disable=unused-import
from tensorflow.python.platform import test
SparseXentOpTest = sparse_xent_op_test_base.SparseXentOpTestBase
def _sparse_vs_dense_xent_benchmark_dense(labels, logits):
labels = array_ops.identity(labels)
logits = array_ops.identity(logits)
with ops_lib.device("/cpu:0"): # Sparse-to-dense must be on CPU
batch_size = array_ops.shape(logits)[0]
num_entries = array_ops.shape(logits)[1]
length = batch_size * num_entries
labels += num_entries * math_ops.range(batch_size)
target = sparse_ops.sparse_to_dense(
labels, array_ops_stack.stack([length]), 1.0, 0.0)
target = array_ops.reshape(target, array_ops_stack.stack([-1, num_entries]))
crossent = nn_ops.softmax_cross_entropy_with_logits(
labels=target, logits=logits, name="SequenceLoss/CrossEntropy")
crossent_sum = math_ops.reduce_sum(crossent)
grads = gradients_impl.gradients([crossent_sum], [logits])[0]
return (crossent_sum, grads)
def _sparse_vs_dense_xent_benchmark_sparse(labels, logits):
# Using sparse_softmax_cross_entropy_with_logits
labels = labels.astype(np.int64)
labels = array_ops.identity(labels)
logits = array_ops.identity(logits)
crossent = nn_ops.sparse_softmax_cross_entropy_with_logits(
logits, labels, name="SequenceLoss/CrossEntropy")
crossent_sum = math_ops.reduce_sum(crossent)
grads = gradients_impl.gradients([crossent_sum], [logits])[0]
return (crossent_sum, grads)
def sparse_vs_dense_xent_benchmark(batch_size, num_entries, use_gpu):
config = config_pb2.ConfigProto()
config.allow_soft_placement = True
config.gpu_options.per_process_gpu_memory_fraction = 0.3
labels = np.random.randint(num_entries, size=batch_size).astype(np.int32)
logits = np.random.randn(batch_size, num_entries).astype(np.float32)
def _timer(sess, ops):
# Warm in
for _ in range(20):
sess.run(ops)
# Timing run
start = time.time()
for _ in range(20):
sess.run(ops)
end = time.time()
return (end - start) / 20.0 # Average runtime per iteration
# Using sparse_to_dense and softmax_cross_entropy_with_logits
with session.Session(config=config) as sess:
if not use_gpu:
with ops_lib.device("/cpu:0"):
ops = _sparse_vs_dense_xent_benchmark_dense(labels, logits)
else:
ops = _sparse_vs_dense_xent_benchmark_dense(labels, logits)
delta_dense = _timer(sess, ops)
# Using sparse_softmax_cross_entropy_with_logits
with session.Session(config=config) as sess:
if not use_gpu:
with test_util.device("/cpu:0"):
ops = _sparse_vs_dense_xent_benchmark_sparse(labels, logits)
else:
ops = _sparse_vs_dense_xent_benchmark_sparse(labels, logits)
delta_sparse = _timer(sess, ops)
print("%d \t %d \t %s \t %f \t %f \t %f" % (batch_size, num_entries, use_gpu,
delta_dense, delta_sparse,
delta_sparse / delta_dense))
def main(_):
print("Sparse Xent vs. SparseToDense + Xent")
print("batch \t depth \t gpu \t dt(dense) \t dt(sparse) "
"\t dt(sparse)/dt(dense)")
for use_gpu in (False, True):
for batch_size in (32, 64, 128):
for num_entries in (100, 1000, 10000):
sparse_vs_dense_xent_benchmark(batch_size, num_entries, use_gpu)
sparse_vs_dense_xent_benchmark(32, 100000, use_gpu)
sparse_vs_dense_xent_benchmark(8, 1000000, use_gpu)
if __name__ == "__main__":
if "--benchmarks" in sys.argv:
sys.argv.remove("--benchmarks")
app.run() # pylint: disable=no-value-for-parameter
else:
test.main()
@@ -0,0 +1,318 @@
# Copyright 2015-2021 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Tests for SparseSoftmaxCrossEntropyWithLogits op."""
import numpy as np
from tensorflow.python.eager import backprop as backprop_lib
from tensorflow.python.eager import context
from tensorflow.python.framework import config
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import errors_impl
from tensorflow.python.framework import ops as ops_lib
from tensorflow.python.framework import test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import gradient_checker_v2
from tensorflow.python.ops import nn_ops
import tensorflow.python.ops.nn_grad # pylint: disable=unused-import
from tensorflow.python.platform import test
class SparseXentOpTestBase(test.TestCase):
def _opFwdBwd(self, labels, logits):
"""Runs the op-under-test both forwards and backwards"""
logits = ops_lib.convert_to_tensor(logits) # needed for the gradient tape
with backprop_lib.GradientTape() as tape:
tape.watch(logits)
loss = nn_ops.sparse_softmax_cross_entropy_with_logits_v2(
labels=labels, logits=logits)
return loss, tape.gradient(loss, logits)
def _npXent(self, labels, logits):
logits = np.reshape(logits, [-1, logits.shape[-1]])
labels = np.reshape(labels, [-1])
batch_dim = 0
class_dim = 1
batch_size = logits.shape[batch_dim]
e = np.exp(logits -
np.reshape(np.amax(logits, axis=class_dim), [batch_size, 1]))
probs = e / np.reshape(np.sum(e, axis=class_dim), [batch_size, 1])
labels_mat = np.zeros_like(probs).astype(probs.dtype)
labels_mat[np.arange(batch_size), labels] = 1.0
gradient = (probs - labels_mat)
loss = -np.sum(labels_mat * np.log(probs + 1.0e-20), axis=1)
return loss, gradient
def _testXent(self, np_labels, np_logits):
np_loss, np_gradient = self._npXent(labels=np_labels, logits=np_logits)
tf_loss, tf_gradient = self._opFwdBwd(labels=np_labels, logits=np_logits)
self.assertAllCloseAccordingToType(np_loss, tf_loss)
self.assertAllCloseAccordingToType(np_gradient, tf_gradient)
def testSingleClass(self):
for label_dtype in np.int32, np.int64:
tf_loss, tf_gradient = self._opFwdBwd(
labels=np.array([0, 0, 0]).astype(label_dtype),
logits=np.array([[1.], [-1.], [0.]]).astype(np.float32))
self.assertAllClose([0.0, 0.0, 0.0], tf_loss)
self.assertAllClose([[0.0], [0.0], [0.0]], tf_gradient)
@test_util.run_gpu_only
def _testInvalidLabelGPU(self, invalid_label_gradient=np.nan):
labels = [4, 3, 0, -1]
logits = [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 2., 3., 4.],
[1., 2., 3., 4.]]
loss, gradient = self._opFwdBwd(labels=labels, logits=logits)
self.assertAllClose([np.nan, 1.3862, 3.4420, np.nan],
loss,
rtol=1e-3,
atol=1e-3)
self.assertAllClose(
[[invalid_label_gradient] * 4, [0.25, 0.25, 0.25, -0.75],
[-0.968, 0.087, 0.237, 0.6439], [invalid_label_gradient] * 4],
gradient,
rtol=1e-3,
atol=1e-3)
def testInvalidLabelGPU(self):
"""This method is structured to be easily overridden by a child class."""
self._testInvalidLabelGPU()
@test_util.run_in_graph_and_eager_modes(use_gpu=False)
@test_util.disable_xla("XLA cannot assert inside of a kernel.")
def _testInvalidLabelCPU(self, expected_regex="Received a label value of"):
labels = [4, 3, 0, -1]
logits = [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 2., 3., 4.],
[1., 2., 3., 4.]]
with self.assertRaisesRegex(
(errors_impl.InvalidArgumentError, errors_impl.UnknownError),
expected_regex):
self.evaluate(
nn_ops.sparse_softmax_cross_entropy_with_logits_v2(
labels=labels, logits=logits))
def testInvalidLabelCPU(self):
"""This method is structured to be easily overridden by a child class."""
self._testInvalidLabelCPU()
def testNpXent(self):
# We create 2 batches of logits for testing.
# batch 0 is the boring uniform distribution: 1, 1, 1, 1, with target 3.
# batch 1 has a bit of difference: 1, 2, 3, 4, with target 0.
labels = [3, 0]
logits = [[1., 1., 1., 1.], [1., 2., 3., 4.]]
# For batch 0, we expect the uniform distribution: 0.25, 0.25, 0.25, 0.25
# With a hard target 3, the gradient is [0.25, 0.25, 0.25, -0.75]
# The loss for this batch is -log(0.25) = 1.386
#
# For batch 1, we have:
# exp(0) = 1
# exp(1) = 2.718
# exp(2) = 7.389
# exp(3) = 20.085
# SUM = 31.192
# So we have as probabilities:
# exp(0) / SUM = 0.032
# exp(1) / SUM = 0.087
# exp(2) / SUM = 0.237
# exp(3) / SUM = 0.644
# With a hard 1, the gradient is [0.032 - 1.0 = -0.968, 0.087, 0.237, 0.644]
# The loss for this batch is [1.0 * -log(0.25), 1.0 * -log(0.032)]
# = [1.3862, 3.4420]
np_loss, np_gradient = self._npXent(
labels=np.array(labels), logits=np.array(logits))
self.assertAllClose(
np.array([[0.25, 0.25, 0.25, -0.75], [-0.968, 0.087, 0.237, 0.6439]]),
np_gradient,
rtol=1.e-3,
atol=1.e-3)
self.assertAllClose(
np.array([1.3862, 3.4420]), np_loss, rtol=1.e-3, atol=1.e-3)
def testShapeMismatch(self):
with self.assertRaisesRegex(
ValueError, "`labels.shape.rank` must equal `logits.shape.rank - 1`"):
nn_ops.sparse_softmax_cross_entropy_with_logits_v2(
labels=[[0, 2]], logits=[[0., 1.], [2., 3.], [2., 3.]])
def testScalar(self):
with self.assertRaisesRegex(ValueError, "`logits` cannot be a scalar"):
nn_ops.sparse_softmax_cross_entropy_with_logits_v2(
labels=constant_op.constant(0), logits=constant_op.constant(1.0))
def _testLabelsPlaceholderScalar(self, expected_error_message):
with ops_lib.Graph().as_default(), self.session():
labels = array_ops.placeholder(np.int32)
y = nn_ops.sparse_softmax_cross_entropy_with_logits_v2(
labels=labels, logits=[[7.]])
with self.assertRaisesOpError(expected_error_message):
y.eval(feed_dict={labels: 0})
def testLabelsPlaceholderScalar(self):
"""This method is structured to be easily overridden by a child class."""
self._testLabelsPlaceholderScalar(
expected_error_message="labels must be 1-D")
def testVector(self):
loss = nn_ops.sparse_softmax_cross_entropy_with_logits_v2(
labels=constant_op.constant(0), logits=constant_op.constant([1.0]))
self.assertAllClose(0.0, loss)
def testFloat(self):
for label_dtype in np.int32, np.int64:
self._testXent(
np_labels=np.array([3, 0]).astype(label_dtype),
np_logits=np.array([[1., 1., 1., 1.], [1., 2., 3.,
4.]]).astype(np.float32))
def testDouble(self):
for label_dtype in np.int32, np.int64:
self._testXent(
np_labels=np.array([0, 3]).astype(label_dtype),
np_logits=np.array([[1., 1., 1., 1.], [1., 2., 3.,
4.]]).astype(np.float64))
def testHalf(self):
for label_dtype in np.int32, np.int64:
self._testXent(
np_labels=np.array([3, 0]).astype(label_dtype),
np_logits=np.array([[1., 1., 1., 1.], [1., 2., 3.,
4.]]).astype(np.float16))
def testBfloat16(self):
for label_dtype in np.int32, np.int64:
self._testXent(
np_labels=np.array([3, 0]).astype(label_dtype),
np_logits=np.array([[1., 1., 1., 1.],
[1., 2., 3.,
4.]]).astype(dtypes.bfloat16.as_numpy_dtype))
def testEmpty(self):
self._testXent(
np_labels=np.zeros((0,), dtype=np.int32), np_logits=np.zeros((0, 3)))
@test_util.run_in_graph_and_eager_modes()
def testGradient(self):
with self.session() as sess:
labels = constant_op.constant([3, 0, 1], name="labels")
logits = constant_op.constant(
[0.1, 0.2, 0.3, 0.4, 0.1, 0.4, 0.9, 1.6, 0.1, 0.8, 2.7, 6.4],
shape=[3, 4],
dtype=dtypes.float64,
name="logits")
def xent(logits):
# gradient_checker_v2.computee_gradient doesn't take int32/int64.
# labels must be of type int32/int64, so passing them separately here.
return nn_ops.sparse_softmax_cross_entropy_with_logits_v2(
labels=labels, logits=logits, name="xent")
analytical, numerical = gradient_checker_v2.compute_gradient(
xent, [logits])
if not context.executing_eagerly():
# Check that no extra computation performed. When only first derivative
# is requested, second derivative must not be computed. So when there is
# no second derivative, there is no `BatchMatMul` op in the graph.
op_names = [
op.op_def.name for op in sess.graph.get_operations() if op.op_def
]
self.assertNotIn("BatchMatMul", op_names)
self.assertNotIn("BatchMatMulV2", op_names)
tol = 5e-8
self.assertAllClose(analytical, numerical, atol=tol, rtol=tol)
@test_util.run_in_graph_and_eager_modes()
def testSecondGradient(self):
with self.session() as sess:
labels = constant_op.constant([3, 0, 1], name="labels")
logits = constant_op.constant(
[0.3, 0.4, 0.1, 1.2, 0.1, 1.9, 0.1, 0.7, 0.8, 0.2, 1.3, 1.3],
shape=[3, 4],
dtype=dtypes.float64,
name="logits")
def xent_grad(logits):
with backprop_lib.GradientTape() as tape:
tape.watch(logits)
return tape.gradient(
nn_ops.sparse_softmax_cross_entropy_with_logits_v2(
labels=labels, logits=logits, name="xent"), [logits])[0]
analytical, numerical = gradient_checker_v2.compute_gradient(
xent_grad, [logits])
if (not context.executing_eagerly() and
not config.is_op_determinism_enabled()):
# Check that second derivative is calculated.
# (it is equivalent to being `BatchMatMul` op in the graph because of
# implementation of xentropy grad)
op_names = [
op.op_def.name for op in sess.graph.get_operations() if op.op_def
]
self.assertIn("BatchMatMulV2", op_names)
tol = 5e-8
self.assertAllClose(analytical, numerical, atol=tol, rtol=tol)
@test_util.run_in_graph_and_eager_modes()
def _testHighDim(self, labels, logits):
np_loss, np_gradient = self._npXent(
labels=np.array(labels), logits=np.array(logits))
# manually reshape loss
np_loss = np.reshape(np_loss, np.array(labels).shape)
tf_loss = nn_ops.sparse_softmax_cross_entropy_with_logits_v2(
labels=labels, logits=logits)
with backprop_lib.GradientTape() as tape:
logits = constant_op.constant(logits)
tape.watch(logits)
tf_gradient = tape.gradient(
nn_ops.sparse_softmax_cross_entropy_with_logits_v2(
labels=labels, logits=logits), [logits])[0]
tf_gradient = array_ops.reshape(tf_gradient, np_gradient.shape)
self.assertAllCloseAccordingToType(np_loss, tf_loss)
self.assertAllCloseAccordingToType(np_gradient, tf_gradient)
def testHighDim(self):
labels = [[3], [0]]
logits = [[[1., 1., 1., 1.]], [[1., 2., 3., 4.]]]
self._testHighDim(labels, logits)
def testHighDim2(self):
labels = [[3, 2], [0, 3]]
logits = [[[1., 1., 1., 1.], [2., 2., 2., 2.]],
[[1., 2., 3., 4.], [5., 6., 7., 8.]]]
self._testHighDim(labels, logits)
def _testScalarHandling(self, expected_regex):
with ops_lib.Graph().as_default(), self.session(use_gpu=False) as sess:
with self.assertRaisesRegex(errors_impl.InvalidArgumentError,
expected_regex):
labels = array_ops.placeholder(dtypes.int32, shape=[None, 1])
logits = array_ops.placeholder(dtypes.float32, shape=[None, 3])
ce = nn_ops.sparse_softmax_cross_entropy_with_logits_v2(
labels=array_ops.squeeze(labels), logits=logits)
labels_v2 = np.zeros((1, 1), dtype=np.int32)
logits_v2 = np.random.randn(1, 3)
sess.run([ce], feed_dict={labels: labels_v2, logits: logits_v2})
def testScalarHandling(self):
"""This method is structured to be easily overridden by a child class."""
self._testScalarHandling(expected_regex=".*labels must be 1-D.*")
@@ -0,0 +1,52 @@
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
import numpy as np
from tensorflow.python.framework import indexed_slices
from tensorflow.python.framework import ops
from tensorflow.python.framework import test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.platform import test
class SparseMaskTest(test.TestCase):
@test_util.run_deprecated_v1
def testBasic(self):
values = np.random.rand(4, 4).astype(np.single)
indices = np.array([0, 2, 3, 4], dtype=np.int32)
mask_indices = np.array([0], dtype=np.int32)
out_values = values[1:, :]
out_indices = np.array([2, 3, 4], dtype=np.int32)
with self.cached_session() as sess:
values_tensor = ops.convert_to_tensor(values)
indices_tensor = ops.convert_to_tensor(indices)
mask_indices_tensor = ops.convert_to_tensor(mask_indices)
t = indexed_slices.IndexedSlices(values_tensor, indices_tensor)
masked_t = array_ops.sparse_mask(t, mask_indices_tensor)
tf_out_values, tf_out_indices = sess.run(
[masked_t.values, masked_t.indices])
self.assertAllEqual(tf_out_values, out_values)
self.assertAllEqual(tf_out_indices, out_indices)
if __name__ == "__main__":
test.main()