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
+323
View File
@@ -0,0 +1,323 @@
# Tests of TensorFlow kernels 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"],
)
# CPU only tests should use tf_py_test, GPU tests use cuda_py_test
# Please avoid the py_tests and cuda_py_tests (plural) while we
# fix the shared/overbroad dependencies.
tf_py_strict_test(
name = "candidate_sampler_ops_test",
size = "small",
srcs = ["candidate_sampler_ops_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:candidate_sampling_ops",
"//tensorflow/python/ops:math_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "multinomial_op_big_test",
size = "medium",
srcs = ["multinomial_op_big_test.py"],
shard_count = 3,
deps = [
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:random_seed",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:random_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "multinomial_op_test",
size = "small",
srcs = ["multinomial_op_test.py"],
deps = [
"//tensorflow/core:protos_all_py",
"//tensorflow/python/client:session",
"//tensorflow/python/eager:context",
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:random_seed",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:control_flow_ops",
"//tensorflow/python/ops:math_ops",
"//tensorflow/python/ops:random_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "parameterized_truncated_normal_op_test",
size = "medium",
srcs = ["parameterized_truncated_normal_op_test.py"],
deps = [
"//tensorflow/core:protos_all_py",
"//tensorflow/python/client:session",
"//tensorflow/python/eager:backprop",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:random_seed",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:control_flow_ops",
"//tensorflow/python/ops:random_ops",
"//tensorflow/python/ops:stateless_random_ops",
"//tensorflow/python/ops:variables",
"//tensorflow/python/platform:client_testlib",
"//tensorflow/python/platform:tf_logging",
"//third_party/py/numpy",
],
)
# TODO(b/130359919): Reenable test when it becomes stable
tf_py_strict_test(
name = "random_binomial_test",
size = "medium",
srcs = ["random_binomial_test.py"],
shard_count = 3,
tags = ["no_oss"],
deps = [
":util",
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:stateful_random_ops",
"//tensorflow/python/ops:stateless_random_ops",
"//tensorflow/python/platform:client_testlib",
"//tensorflow/python/platform:tf_logging",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "random_crop_test",
size = "small",
srcs = ["random_crop_test.py"],
xla_tags = [
"no_cuda_asan", # times out
],
deps = [
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:random_crop_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "random_gamma_test",
size = "medium",
srcs = ["random_gamma_test.py"],
shard_count = 4,
tags = [
"no_windows", # TODO(b/218876472)
"nozapfhahn",
],
xla_tags = [
"no_cuda_asan", # times out
"nomsan", # b/216320808
],
deps = [
":util",
"//tensorflow/python/eager:context",
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:random_seed",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:math_ops",
"//tensorflow/python/ops:random_ops",
"//tensorflow/python/platform:client_testlib",
"//tensorflow/python/platform:tf_logging",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "random_grad_test",
size = "small",
srcs = ["random_grad_test.py"],
xla_tags = [
"no_cuda_asan", # times out
],
deps = [
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:gradients_impl",
"//tensorflow/python/ops:math_ops",
"//tensorflow/python/ops:random_grad",
"//tensorflow/python/ops:random_ops",
"//tensorflow/python/ops:stateless_random_ops_v2_gen",
"//tensorflow/python/platform:client_testlib",
"//tensorflow/python/platform:tf_logging",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "random_ops_test",
size = "medium",
srcs = ["random_ops_test.py"],
deps = [
"//tensorflow/python/eager:context",
"//tensorflow/python/framework:config",
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:random_seed",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:random_ops",
"//tensorflow/python/ops:random_ops_gen",
"//tensorflow/python/ops:variables",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "random_poisson_test",
size = "medium",
srcs = ["random_poisson_test.py"],
deps = [
":util",
"//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:random_ops",
"//tensorflow/python/platform:client_testlib",
"//tensorflow/python/platform:tf_logging",
"//third_party/py/numpy",
],
)
tf_py_strict_test(
name = "random_shuffle_queue_test",
size = "medium",
srcs = ["random_shuffle_queue_test.py"],
tags = [
"no_cuda_on_cpu_tap", # TODO(b/171060960) flakyly broken assertions
],
deps = [
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:random_seed",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:data_flow_ops",
"//tensorflow/python/platform:client_testlib",
"//tensorflow/python/platform:tf_logging",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "stateless_random_ops_test",
size = "medium",
srcs = ["stateless_random_ops_test.py"],
shard_count = 10,
xla_tags = [
"no_cuda_asan", # times-out
],
deps = [
"//tensorflow/python/compat",
"//tensorflow/python/eager:context",
"//tensorflow/python/eager:def_function",
"//tensorflow/python/framework:config",
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:dtypes",
"//tensorflow/python/framework:ops",
"//tensorflow/python/framework:random_seed",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:array_ops_stack",
"//tensorflow/python/ops:math_ops",
"//tensorflow/python/ops:random_ops",
"//tensorflow/python/ops:stateless_random_ops",
"//tensorflow/python/ops:stateless_random_ops_v2_gen",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
"@absl_py//absl/testing:parameterized",
],
)
cuda_py_strict_test(
name = "stateful_random_ops_test",
size = "medium",
srcs = ["stateful_random_ops_test.py"],
xla_enable_strict_auto_jit = False,
xla_enabled = True,
deps = [
":util",
"//tensorflow/python/checkpoint",
"//tensorflow/python/eager:context",
"//tensorflow/python/eager:def_function",
"//tensorflow/python/framework:config",
"//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:logging_ops",
"//tensorflow/python/ops:random_ops_gen",
"//tensorflow/python/ops:random_ops_util",
"//tensorflow/python/ops:stateful_random_ops",
"//tensorflow/python/ops:stateful_random_ops_gen",
"//tensorflow/python/ops:variables",
"//tensorflow/python/platform:client_testlib",
"@absl_py//absl/testing:parameterized",
],
)
tf_py_strict_test(
name = "random_index_shuffle_test",
srcs = ["random_index_shuffle_test.py"],
shard_count = 10,
deps = [
"//tensorflow/python/eager:def_function",
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:dtypes",
"//tensorflow/python/framework:errors",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:math_ops",
"//tensorflow/python/ops:random_index_shuffle_ops_gen",
"//tensorflow/python/ops:stateless_random_ops",
"//tensorflow/python/platform:client_testlib",
"@absl_py//absl/testing:parameterized",
],
)
py_library(
name = "util",
srcs = ["util.py"],
strict_deps = True,
deps = [
"//tensorflow/python/ops/distributions:special_math",
"//third_party/py/numpy",
],
)
@@ -0,0 +1,154 @@
# 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 CandidateSamplerOp."""
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 test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import candidate_sampling_ops
from tensorflow.python.ops import math_ops
from tensorflow.python.platform import test
class RangeSamplerOpsTest(test.TestCase):
BATCH_SIZE = 3
NUM_TRUE = 2
RANGE = 5
NUM_SAMPLED = RANGE
TRUE_LABELS = [[1, 2], [0, 4], [3, 3]]
@test_util.run_deprecated_v1
def testTrueCandidates(self):
with self.cached_session() as sess:
indices = constant_op.constant([0, 0, 1, 1, 2, 2])
true_candidates_vec = constant_op.constant([1, 2, 0, 4, 3, 3])
true_candidates_matrix = array_ops.reshape(
true_candidates_vec, [self.BATCH_SIZE, self.NUM_TRUE])
indices_val, true_candidates_val = sess.run(
[indices, true_candidates_matrix])
self.assertAllEqual(indices_val, [0, 0, 1, 1, 2, 2])
self.assertAllEqual(true_candidates_val, self.TRUE_LABELS)
def testSampledCandidates(self):
with self.cached_session():
true_classes = constant_op.constant(
[[1, 2], [0, 4], [3, 3]], dtype=dtypes.int64)
sampled_candidates, _, _ = candidate_sampling_ops.all_candidate_sampler(
true_classes, self.NUM_TRUE, self.NUM_SAMPLED, True)
result = self.evaluate(sampled_candidates)
expected_ids = [0, 1, 2, 3, 4]
self.assertAllEqual(result, expected_ids)
self.assertEqual(sampled_candidates.get_shape(), [self.NUM_SAMPLED])
def testTrueLogExpectedCount(self):
with self.cached_session():
true_classes = constant_op.constant(
[[1, 2], [0, 4], [3, 3]], dtype=dtypes.int64)
_, true_expected_count, _ = candidate_sampling_ops.all_candidate_sampler(
true_classes, self.NUM_TRUE, self.NUM_SAMPLED, True)
true_log_expected_count = math_ops.log(true_expected_count)
result = self.evaluate(true_log_expected_count)
self.assertAllEqual(result, [[0.0] * self.NUM_TRUE] * self.BATCH_SIZE)
self.assertEqual(true_expected_count.get_shape(),
[self.BATCH_SIZE, self.NUM_TRUE])
self.assertEqual(true_log_expected_count.get_shape(),
[self.BATCH_SIZE, self.NUM_TRUE])
def testSampledLogExpectedCount(self):
with self.cached_session():
true_classes = constant_op.constant(
[[1, 2], [0, 4], [3, 3]], dtype=dtypes.int64)
_, _, sampled_expected_count = candidate_sampling_ops.all_candidate_sampler( # pylint: disable=line-too-long
true_classes, self.NUM_TRUE, self.NUM_SAMPLED, True)
sampled_log_expected_count = math_ops.log(sampled_expected_count)
result = self.evaluate(sampled_log_expected_count)
self.assertAllEqual(result, [0.0] * self.NUM_SAMPLED)
self.assertEqual(sampled_expected_count.get_shape(), [self.NUM_SAMPLED])
self.assertEqual(sampled_log_expected_count.get_shape(), [self.NUM_SAMPLED])
def testAccidentalHits(self):
with self.cached_session() as sess:
true_classes = constant_op.constant(
[[1, 2], [0, 4], [3, 3]], dtype=dtypes.int64)
sampled_candidates, _, _ = candidate_sampling_ops.all_candidate_sampler(
true_classes, self.NUM_TRUE, self.NUM_SAMPLED, True)
accidental_hits = candidate_sampling_ops.compute_accidental_hits(
true_classes, sampled_candidates, self.NUM_TRUE)
indices, ids, weights = self.evaluate(accidental_hits)
self.assertEqual(1, accidental_hits[0].get_shape().ndims)
self.assertEqual(1, accidental_hits[1].get_shape().ndims)
self.assertEqual(1, accidental_hits[2].get_shape().ndims)
for index, id_, weight in zip(indices, ids, weights):
self.assertTrue(id_ in self.TRUE_LABELS[index])
self.assertLess(weight, -1.0e37)
@test_util.run_deprecated_v1
def testSeed(self):
def draw(seed):
with self.cached_session():
true_classes = constant_op.constant(
[[1, 2], [0, 4], [3, 3]], dtype=dtypes.int64)
sampled, _, _ = candidate_sampling_ops.log_uniform_candidate_sampler(
true_classes, self.NUM_TRUE, self.NUM_SAMPLED, True, 5, seed=seed)
return self.evaluate(sampled)
# Non-zero seed. Repeatable.
for seed in [1, 12, 123, 1234]:
self.assertAllEqual(draw(seed), draw(seed))
# Seed=0 means random seeds.
num_same = 0
for _ in range(10):
if np.allclose(draw(None), draw(None)):
num_same += 1
# Accounts for the fact that the same random seed may be picked
# twice very rarely.
self.assertLessEqual(num_same, 2)
def testCandidateOutOfRange(self):
with self.assertRaisesRegex((ValueError, errors.InvalidArgumentError),
"out of range"):
self.evaluate(
candidate_sampling_ops.log_uniform_candidate_sampler(
true_classes=[[0, 10]],
num_true=2,
num_sampled=1000,
unique=False,
range_max=2))
with self.assertRaisesRegex((ValueError, errors.InvalidArgumentError),
"out of range"):
self.evaluate(
candidate_sampling_ops.log_uniform_candidate_sampler(
true_classes=[[0, -10]],
num_true=2,
num_sampled=1000,
unique=False,
range_max=2))
if __name__ == "__main__":
test.main()
@@ -0,0 +1,90 @@
# Copyright 2017 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.
# ==============================================================================
"""Long tests for Multinomial."""
import numpy as np
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import random_seed
from tensorflow.python.framework import test_util
from tensorflow.python.ops import random_ops
from tensorflow.python.platform import test
class MultinomialTest(test.TestCase):
# check that events with tiny probabilities are not over-sampled
def testLargeDynamicRange(self):
random_seed.set_random_seed(10)
counts_by_indices = {}
with self.test_session():
samples = random_ops.multinomial(
constant_op.constant([[-30, 0]], dtype=dtypes.float32),
num_samples=1000000,
seed=15)
for _ in range(100):
x = self.evaluate(samples)
indices, counts = np.unique(x, return_counts=True) # pylint: disable=unexpected-keyword-arg
for index, count in zip(indices, counts):
if index in counts_by_indices.keys():
counts_by_indices[index] += count
else:
counts_by_indices[index] = count
self.assertEqual(counts_by_indices[1], 100000000)
def testLargeDynamicRange2(self):
random_seed.set_random_seed(10)
counts_by_indices = {}
with self.test_session():
samples = random_ops.multinomial(
constant_op.constant([[0, -30]], dtype=dtypes.float32),
num_samples=1000000,
seed=15)
for _ in range(100):
x = self.evaluate(samples)
indices, counts = np.unique(x, return_counts=True) # pylint: disable=unexpected-keyword-arg
for index, count in zip(indices, counts):
if index in counts_by_indices.keys():
counts_by_indices[index] += count
else:
counts_by_indices[index] = count
self.assertEqual(counts_by_indices[0], 100000000)
@test_util.run_deprecated_v1
def testLargeDynamicRange3(self):
random_seed.set_random_seed(10)
counts_by_indices = {}
# here the cpu undersamples and won't pass this test either
with self.test_session():
samples = random_ops.multinomial(
constant_op.constant([[0, -17]], dtype=dtypes.float32),
num_samples=1000000,
seed=22)
# we'll run out of memory if we try to draw 1e9 samples directly
# really should fit in 12GB of memory...
for _ in range(100):
x = self.evaluate(samples)
indices, counts = np.unique(x, return_counts=True) # pylint: disable=unexpected-keyword-arg
for index, count in zip(indices, counts):
if index in counts_by_indices.keys():
counts_by_indices[index] += count
else:
counts_by_indices[index] = count
self.assertGreater(counts_by_indices[1], 0)
if __name__ == "__main__":
test.main()
@@ -0,0 +1,275 @@
# 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 Multinomial."""
import collections
import timeit
import numpy as np
from tensorflow.core.protobuf import config_pb2
from tensorflow.python.client import session
from tensorflow.python.eager import context
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import random_seed
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 control_flow_ops
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import random_ops
from tensorflow.python.platform import test
def composed_sampler(logits, num_samples):
# [batch size, num classes, num samples]
unif = random_ops.random_uniform(logits.get_shape().concatenate(
tensor_shape.TensorShape([num_samples])))
noise = -math_ops.log(-math_ops.log(unif))
# [batch size, num classes, 1]
logits = array_ops.expand_dims(logits, -1)
# [batch size, num samples]
return math_ops.argmax(logits + noise, axis=1)
native_sampler = random_ops.multinomial
class MultinomialTest(test.TestCase):
@test_util.run_in_graph_and_eager_modes
def testSmallEntropy(self):
random_seed.set_random_seed(1618)
for output_dtype in [np.int32, np.int64]:
with test_util.device(use_gpu=True):
# A logit value of -10 corresponds to a probability of ~5e-5.
logits = constant_op.constant([[-10., 10., -10.], [-10., -10., 10.]])
num_samples = 1000
samples = self.evaluate(random_ops.multinomial(
logits, num_samples, output_dtype=output_dtype))
self.assertAllEqual([[1] * num_samples, [2] * num_samples], samples)
@test_util.run_deprecated_v1
def testOneOpMultipleStepsIndependent(self):
with test_util.use_gpu():
sample_op1, _ = self._make_ops(10)
# Consecutive runs shouldn't yield identical output.
sample1a = self.evaluate(sample_op1)
sample1b = self.evaluate(sample_op1)
self.assertFalse(np.equal(sample1a, sample1b).all())
def testEagerOneOpMultipleStepsIndependent(self):
with context.eager_mode(), test_util.device(use_gpu=True):
sample1, sample2 = self._make_ops(10)
# Consecutive runs shouldn't yield identical output.
self.assertFalse(np.equal(sample1.numpy(), sample2.numpy()).all())
@test_util.run_deprecated_v1
def testBfloat16(self):
with test_util.use_gpu():
sample_op1, _ = self._make_ops(10, dtype=dtypes.bfloat16)
self.evaluate(sample_op1)
def testEagerBfloat16(self):
with context.eager_mode(), test_util.device(use_gpu=True):
self._make_ops(10, dtype=dtypes.bfloat16)
def testTwoOpsIndependent(self):
with test_util.use_gpu():
sample_op1, sample_op2 = self._make_ops(32)
sample1, sample2 = self.evaluate([sample_op1, sample_op2])
# We expect sample1 and sample2 to be independent.
# 1 in 2^32 chance of this assertion failing.
self.assertFalse(np.equal(sample1, sample2).all())
@test_util.run_deprecated_v1
def testTwoOpsSameSeedDrawSameSequences(self):
with test_util.use_gpu():
sample_op1, sample_op2 = self._make_ops(1000, seed=1)
sample1, sample2 = self.evaluate([sample_op1, sample_op2])
self.assertAllEqual(sample1, sample2)
def testLargeLogits(self):
for neg in [True, False]:
with test_util.use_gpu():
logits = np.array([[1000.] * 5])
if neg:
logits *= -1
samples = self.evaluate(random_ops.multinomial(logits, 10))
# Sampled classes should be in-range.
self.assertTrue((samples >= 0).all())
self.assertTrue((samples < 5).all())
def testSamplingCorrectness(self):
np.random.seed(1618) # Make it reproducible.
num_samples = 21000
rand_probs = self._normalize(np.random.random_sample((10,)))
rand_probs2 = self._normalize(np.random.random_sample((3, 5))) # batched
for probs in [[.5, .5], [.85, .05, .1], rand_probs, rand_probs2]:
probs = np.asarray(probs)
if len(probs.shape) == 1:
probs = probs.reshape(1, probs.size) # singleton batch
logits = np.log(probs).astype(np.float32)
composed_freqs = self._do_sampling(logits, num_samples, composed_sampler)
native_freqs = self._do_sampling(logits, num_samples, native_sampler)
# the test here is similar to core/lib/random/distribution_sampler_test.cc
composed_chi2 = self._chi2(probs, composed_freqs)
native_chi2 = self._chi2(probs, native_freqs)
composed_native_chi2 = self._chi2(composed_freqs, native_freqs)
def check(chi2s):
for chi2 in chi2s:
self.assertLess(chi2, 1e-3)
check(composed_chi2)
check(native_chi2)
check(composed_native_chi2)
def _make_ops(self, num_samples, seed=None, dtype=dtypes.float32):
prob_dist = constant_op.constant([[0.15, 0.5, 0.3, 0.05]], dtype=dtype)
logits = math_ops.log(prob_dist)
# Two independent sets of samples from the same distribution
sample_op1 = random_ops.multinomial(logits, num_samples, seed)
sample_op2 = random_ops.multinomial(logits, num_samples, seed)
return (sample_op1, sample_op2)
def _normalize(self, vec):
batched = (len(vec.shape) == 2)
return vec / vec.sum(axis=1, keepdims=True) if batched else vec / vec.sum()
def _do_sampling(self, logits, num_samples, sampler):
"""Samples using the supplied sampler and inputs.
Args:
logits: Numpy ndarray of shape [batch_size, num_classes].
num_samples: Int; number of samples to draw.
sampler: A sampler function that takes (1) a [batch_size, num_classes]
Tensor, (2) num_samples and returns a [batch_size, num_samples] Tensor.
Returns:
Frequencies from sampled classes; shape [batch_size, num_classes].
"""
with test_util.use_gpu():
random_seed.set_random_seed(1618)
op = sampler(constant_op.constant(logits), num_samples)
d = self.evaluate(op)
batch_size, num_classes = logits.shape
freqs_mat = []
for i in range(batch_size):
cnts = dict(collections.Counter(d[i, :]))
# Requires drawn class labels be in range.
self.assertLess(max(cnts.keys()), num_classes)
self.assertGreaterEqual(min(cnts.keys()), 0)
freqs = [(cnts[k] * 1. / num_samples if k in cnts else 0)
for k in range(num_classes)]
freqs_mat.append(freqs)
return freqs_mat
def _chi2(self, expected, actual):
actual = np.asarray(actual)
expected = np.asarray(expected)
diff = actual - expected
chi2 = np.sum(diff * diff / expected, axis=0)
return chi2
def testEmpty(self):
classes = 5
with test_util.use_gpu():
for batch in 0, 3:
for samples in 0, 7:
x = self.evaluate(
random_ops.multinomial(
array_ops.zeros([batch, classes]), samples))
self.assertEqual(x.shape, (batch, samples))
@test_util.run_deprecated_v1
def testEmptyClasses(self):
with test_util.use_gpu():
x = random_ops.multinomial(array_ops.zeros([5, 0]), 7)
with self.assertRaisesOpError("num_classes should be positive"):
self.evaluate(x)
def testNegativeMinLogits(self):
random_seed.set_random_seed(78844)
with test_util.use_gpu():
logits = constant_op.constant([[np.finfo(np.float32).min] * 1023 + [0]])
num_samples = 1000
samples = self.evaluate(random_ops.multinomial(logits, num_samples))
self.assertAllEqual([[1023] * num_samples], samples)
# Benchmarking code
def native_op_vs_composed_ops(batch_size, num_classes, num_samples, num_iters):
np.random.seed(1618) # Make it reproducible.
shape = [batch_size, num_classes]
logits_np = np.random.randn(*shape).astype(np.float32)
# No CSE/CF.
optimizer_options = config_pb2.OptimizerOptions(
opt_level=config_pb2.OptimizerOptions.L0)
config = config_pb2.ConfigProto(graph_options=config_pb2.GraphOptions(
optimizer_options=optimizer_options))
with session.Session(config=config) as sess:
logits = constant_op.constant(logits_np, shape=shape)
native_op = control_flow_ops.group(native_sampler(logits, num_samples))
composed_op = control_flow_ops.group(composed_sampler(logits, num_samples))
native_dt = timeit.timeit(lambda: sess.run(native_op), number=num_iters)
composed_dt = timeit.timeit(lambda: sess.run(composed_op), number=num_iters)
return native_dt, composed_dt
class MultinomialBenchmark(test.Benchmark):
def benchmarkNativeOpVsComposedOps(self):
num_iters = 50
print("Composition of existing ops vs. Native Multinomial op [%d iters]" %
num_iters)
print("BatchSize\tNumClasses\tNumSamples\tsec(composed)\tsec(native)\t"
"speedup")
for batch_size in [32, 128]:
for num_classes in [10000, 100000]:
for num_samples in [1, 4, 32]:
n_dt, c_dt = native_op_vs_composed_ops(batch_size, num_classes,
num_samples, num_iters)
print("%d\t%d\t%d\t%.3f\t%.3f\t%.2f" % (batch_size, num_classes,
num_samples, c_dt, n_dt,
c_dt / n_dt))
self.report_benchmark(
name="native_batch%d_classes%d_s%d" %
(batch_size, num_classes, num_samples),
iters=num_iters,
wall_time=n_dt)
self.report_benchmark(
name="composed_batch%d_classes%d_s%d" %
(batch_size, num_classes, num_samples),
iters=num_iters,
wall_time=c_dt)
if __name__ == "__main__":
test.main()
@@ -0,0 +1,538 @@
# 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 ParameterizedTruncatedNormalOp."""
import functools
import math
import timeit
import numpy as np
from tensorflow.core.protobuf import config_pb2
from tensorflow.python.client import session
from tensorflow.python.eager import backprop
from tensorflow.python.framework import ops
from tensorflow.python.framework import random_seed
from tensorflow.python.framework import test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import control_flow_ops
from tensorflow.python.ops import random_ops
from tensorflow.python.ops import stateless_random_ops as stateless
from tensorflow.python.ops import variables
from tensorflow.python.platform import test
from tensorflow.python.platform import tf_logging
def _get_stddev_inside_bounds_before_using_randn(gpu):
# The boundary where the randn sampler is used varies between CPU and GPU.
if gpu:
return 1.3
else:
return 1.7
class TruncatedNormalMoments:
memoized_moments = None
mean = None
stddev = None
minval = None
maxval = None
def __init__(self, mean, stddev, minval, maxval):
self.memoized_moments = [1.0] # 0th moment
self.mean = np.double(mean)
self.stddev = np.double(stddev)
# NOTE(ringwalt): The formula doesn't handle infinite values.
self.minval = np.double(max(-10, minval))
self.maxval = np.double(min(10, maxval))
def __getitem__(self, moment):
"""Calculates the truncated normal moments.
Args:
moment: The number for the moment.
Returns:
The value for the given moment.
Uses the recurrence relation described in:
http://www.smp.uq.edu.au/people/YoniNazarathy/teaching_projects
/studentWork/EricOrjebin_TruncatedNormalMoments.pdf
"""
assert moment > 0
# The test case must ensure it can import scipy.stats before this point.
import scipy.stats # pylint: disable=g-import-not-at-top
dist = scipy.stats.norm(loc=self.mean, scale=self.stddev)
for k in range(len(self.memoized_moments), moment + 1):
m_k_minus_2 = self.memoized_moments[k - 2] if k > 1 else np.double(0.0)
m_k_minus_1 = self.memoized_moments[k - 1]
numerator = (np.power(self.maxval, k - 1) * dist.pdf(self.maxval) -
np.power(self.minval, k - 1) * dist.pdf(self.minval))
denominator = dist.cdf(self.maxval) - dist.cdf(self.minval)
m = ((k - 1) * self.stddev**2 * m_k_minus_2 + self.mean * m_k_minus_1 -
self.stddev * numerator / denominator)
assert abs(m) < 1e50 # ensure numerical accuracy
self.memoized_moments.append(m)
return self.memoized_moments[moment]
def calculate_moments(samples, max_moment):
moments = [0.0] * (max_moment + 1)
for k in range(len(moments)):
moments[k] = np.mean(samples**k, axis=0)
return moments
def z_test(real, expected, i, num_samples):
numerical_error = 1e-6 # per-operation error
moment_mean = expected[i]
moment_squared = expected[2 * i]
moment_var = moment_squared - moment_mean * moment_mean
error_per_moment = i * numerical_error
total_variance = moment_var / float(num_samples) + error_per_moment
return abs((real[i] - moment_mean) / math.sqrt(total_variance))
class ParameterizedTruncatedNormalTest(test.TestCase):
z_limit = 6.0
# Stop at moment 10 to avoid numerical errors in the theoretical moments.
max_moment = 10
def validateMoments(self,
shape,
mean,
stddev,
minval,
maxval,
use_stateless=False,
seed=1618):
try:
# TruncatedNormalMoments requires scipy.stats.
# Give up early if we are unable to import it.
random_seed.set_random_seed(seed)
with self.cached_session():
if use_stateless:
# Generate a seed that stateless ops can use.
new_seed = random_ops.random_uniform([2],
seed=seed,
minval=0,
maxval=(2**31 - 1),
dtype=np.int32)
samples = stateless.stateless_parameterized_truncated_normal(
shape, new_seed, mean, stddev, minval, maxval).eval()
else:
samples = random_ops.parameterized_truncated_normal(
shape, mean, stddev, minval, maxval).eval()
assert (~np.isnan(samples)).all()
moments = calculate_moments(samples, self.max_moment)
expected_moments = TruncatedNormalMoments(mean, stddev, minval, maxval)
num_samples = functools.reduce(lambda x, y: x * y, shape, 1)
for i in range(1, len(moments)):
self.assertLess(
z_test(moments, expected_moments, i, num_samples), self.z_limit)
except ImportError as e:
tf_logging.warn("Cannot test truncated normal op: %s" % str(e))
def validateKolmogorovSmirnov(self,
shape,
mean,
stddev,
minval,
maxval,
use_stateless=False,
seed=1618):
try:
import scipy.stats # pylint: disable=g-import-not-at-top
random_seed.set_random_seed(seed)
with self.cached_session():
if use_stateless:
new_seed = random_ops.random_uniform([2],
seed=seed,
minval=0,
maxval=(2**31 - 1),
dtype=np.int32)
samples = stateless.stateless_parameterized_truncated_normal(
shape, new_seed, mean, stddev, minval, maxval).eval()
else:
samples = random_ops.parameterized_truncated_normal(
shape, mean, stddev, minval, maxval).eval()
assert (~np.isnan(samples)).all()
minval = max(mean - stddev * 10, minval)
maxval = min(mean + stddev * 10, maxval)
dist = scipy.stats.norm(loc=mean, scale=stddev)
cdf_min = dist.cdf(minval)
cdf_max = dist.cdf(maxval)
def truncated_cdf(x):
return np.clip((dist.cdf(x) - cdf_min) / (cdf_max - cdf_min), 0.0, 1.0)
pvalue = scipy.stats.kstest(samples, truncated_cdf)[1]
self.assertGreater(pvalue, 1e-10)
except ImportError as e:
tf_logging.warn("Cannot test truncated normal op: %s" % str(e))
@test_util.run_deprecated_v1
def testDefaults(self):
self.validateMoments([int(1e5)], 0.0, 1.0, -2.0, 2.0)
self.validateMoments([int(1e5)], 0.0, 1.0, -2.0, 2.0, use_stateless=True)
@test_util.run_deprecated_v1
def testShifted(self):
self.validateMoments([int(1e5)], -1.0, 1.0, -2.0, 2.0)
self.validateMoments([int(1e5)], -1.0, 1.0, -2.0, 2.0, use_stateless=True)
@test_util.run_deprecated_v1
def testRightTail(self):
self.validateMoments([int(1e5)], 0.0, 1.0, 4.0, np.inf)
self.validateMoments([int(1e5)],
0.0,
1.0,
4.0,
np.inf,
use_stateless=True)
@test_util.run_deprecated_v1
def testLeftTail(self):
self.validateMoments([int(1e5)], 0.0, 1.0, -np.inf, -4.0)
self.validateMoments([int(1e5)],
0.0,
1.0,
-np.inf,
-4.0,
use_stateless=True)
@test_util.run_deprecated_v1
def testLeftTailTwoSidedBounds(self):
self.validateMoments([int(1e5)], 0.0, 1.0, -6.0, -3.0)
self.validateMoments([int(1e5)], 0.0, 1.0, -6.0, -3.0, use_stateless=True)
@test_util.run_deprecated_v1
@test_util.disable_xla("Low probability region")
def testTwoSidedLeftTailShifted(self):
self.validateKolmogorovSmirnov([int(1e5)], 6.0, 1.0, -1.0, 1.0)
self.validateKolmogorovSmirnov([int(1e5)],
6.0,
1.0,
-1.0,
1.0,
use_stateless=True)
@test_util.run_deprecated_v1
@test_util.disable_xla("Low probability region")
def testRightTailShifted(self):
self.validateMoments([int(1e5)], -5.0, 1.0, 2.0, np.inf)
self.validateMoments([int(1e5)],
-5.0,
1.0,
2.0,
np.inf,
use_stateless=True)
# Take the normal distribution around the mean, but truncating the left tail
# far from the mean.
@test_util.run_deprecated_v1
def testTruncateOnLeft_entireTailOnRight(self):
self.validateKolmogorovSmirnov([int(1e5)], 10.0, 1.0, 4.0, np.inf)
self.validateKolmogorovSmirnov([int(1e5)],
10.0,
1.0,
4.0,
np.inf,
use_stateless=True)
# Take the normal distribution around the mean, but truncating the right tail.
@test_util.run_deprecated_v1
def testTruncateOnRight_entireTailOnLeft(self):
self.validateKolmogorovSmirnov([int(1e5)], -8, 1.0, -np.inf, -4.0)
self.validateKolmogorovSmirnov([int(1e5)],
-8.,
1.0,
-np.inf,
-4.0,
use_stateless=True)
@test_util.run_deprecated_v1
def testSmallStddev(self):
self.validateKolmogorovSmirnov([int(1e5)], 0.0, 0.1, 0.05, 0.10)
self.validateKolmogorovSmirnov([int(1e5)],
0.0,
0.1,
0.05,
0.10,
use_stateless=True)
@test_util.run_deprecated_v1
def testSamplingWithSmallStdDevFarFromBound(self):
sample_op = random_ops.parameterized_truncated_normal(
shape=(int(1e5),), means=0.8, stddevs=0.05, minvals=-1., maxvals=1.)
new_seed = random_ops.random_uniform([2],
seed=1234,
minval=0,
maxval=(2**31 - 1),
dtype=np.int32)
sample_op_stateless = stateless.stateless_parameterized_truncated_normal(
shape=(int(1e5),),
seed=new_seed,
means=0.8,
stddevs=0.05,
minvals=-1.,
maxvals=1.)
with self.session() as sess:
samples, samples_stateless = sess.run([sample_op, sample_op_stateless])
# 0. is more than 16 standard deviations from the mean, and
# should have a likelihood < 1e-57.
assert (~np.isnan(samples)).all()
assert (~np.isnan(samples_stateless)).all()
self.assertAllGreater(samples, 0.)
self.assertAllGreater(samples_stateless, 0.)
def testShapeTypes(self):
for shape_dtype in [np.int32, np.int64]:
shape = np.array([1000], dtype=shape_dtype)
sample_op = random_ops.parameterized_truncated_normal(
shape=shape, means=0.0, stddevs=0.1, minvals=-1., maxvals=1.)
new_seed = random_ops.random_uniform([2],
seed=1234,
minval=0,
maxval=(2**31 - 1),
dtype=np.int32)
sample_op_stateless = stateless.stateless_parameterized_truncated_normal(
shape=shape,
seed=new_seed,
means=0.0,
stddevs=0.1,
minvals=-1.,
maxvals=1.)
samples = self.evaluate(sample_op)
stateless_samples = self.evaluate(sample_op_stateless)
self.assertAllEqual(samples.shape, shape)
self.assertAllEqual(stateless_samples.shape, shape)
def testStatelessParameterizedTruncatedNormalHasGrads(self):
mean = variables.Variable(0.01)
stddev = variables.Variable(1.)
minval = variables.Variable(-1.)
maxval = variables.Variable(1.)
with self.cached_session() as sess:
with backprop.GradientTape(persistent=True) as tape:
samples = stateless.stateless_parameterized_truncated_normal(
[1], [1, 2], mean, stddev, minval, maxval)
sess.run(variables.variables_initializer([mean, stddev, minval, maxval]))
[mean_grad, std_grad], mean_actual_grad, std_actual_grad = sess.run([
tape.gradient(samples, [mean, stddev]),
array_ops.ones_like(mean),
(samples - mean) / stddev])
self.assertAllClose(mean_grad, mean_actual_grad)
self.assertAllClose(std_grad, std_actual_grad[0])
try:
import scipy.stats # pylint:disable=g-import-not-at-top
truncnorm = scipy.stats.truncnorm(a=-1., b=1., loc=0., scale=1.)
samples_np, [minval_grad, maxval_grad] = sess.run([
samples, tape.gradient(samples, [minval, maxval])])
sample_cdf = truncnorm.cdf(samples_np)
# These come from the implicit reparameterization trick.
scipy_maxval_grad = np.exp(
0.5 * (samples_np ** 2 - ((1. - 0.01) / 1.) ** 2) +
np.log(sample_cdf))
scipy_minval_grad = np.exp(
0.5 * (samples_np ** 2 - ((-1. - 0.01) / 1.) ** 2) +
np.log1p(-sample_cdf))
self.assertAllClose(minval_grad, scipy_minval_grad[0], rtol=1e-2)
self.assertAllClose(maxval_grad, scipy_maxval_grad[0], rtol=1e-2)
except ImportError as e:
tf_logging.warn("Cannot test truncated normal op: %s" % str(e))
@test_util.run_deprecated_v1
def testSamplingAtRandnSwitchover(self):
# The randn sampler is used as the bounds are moved farther from the mean,
# and the probability of accepting a sample increases the farther the
# bounds are from the mean.
# This test asserts that at the point of switchover, both samplers are
# working (not raising an error or returning nan) and returning the
# expected moments.
use_gpu = test.is_gpu_available()
stddev_inside_bounds_before_using_randn = (
_get_stddev_inside_bounds_before_using_randn(use_gpu))
epsilon = 0.001
self.validateMoments(
shape=[int(1e6)],
mean=0.,
stddev=1.0,
minval=-epsilon,
maxval=stddev_inside_bounds_before_using_randn - epsilon)
self.validateMoments(
shape=[int(1e6)],
mean=0.,
stddev=1.0,
minval=-epsilon,
maxval=stddev_inside_bounds_before_using_randn + epsilon)
self.validateMoments(
shape=[int(1e6)],
mean=0.,
stddev=1.0,
minval=-epsilon,
maxval=stddev_inside_bounds_before_using_randn - epsilon,
use_stateless=True)
self.validateMoments(
shape=[int(1e6)],
mean=0.,
stddev=1.0,
minval=-epsilon,
maxval=stddev_inside_bounds_before_using_randn + epsilon,
use_stateless=True)
# Benchmarking code
def parameterized_vs_naive(shape, num_iters, use_gpu=False):
np.random.seed(1618) # Make it reproducible.
# No CSE/CF.
optimizer_options = config_pb2.OptimizerOptions(
opt_level=config_pb2.OptimizerOptions.L0)
config = config_pb2.ConfigProto(graph_options=config_pb2.GraphOptions(
optimizer_options=optimizer_options))
with session.Session(config=config) as sess:
with ops.device("/cpu:0" if not use_gpu else None):
param_op = control_flow_ops.group(
random_ops.parameterized_truncated_normal(shape))
naive_op = control_flow_ops.group(random_ops.truncated_normal(shape))
# Burn-in to avoid session setup costs in the timing.
sess.run(param_op)
sess.run(param_op)
param_dt = timeit.timeit(lambda: sess.run(param_op), number=num_iters)
sess.run(naive_op)
sess.run(naive_op)
naive_dt = timeit.timeit(lambda: sess.run(naive_op), number=num_iters)
return param_dt, naive_dt
def randn_sampler_switchover(shape, num_iters, use_gpu=False):
# Benchmark by constructing samplers on the threshold of using the randn
# rejection sampling and check that this threshold is set correctly by
# benchmarking with bounds just above and below this threshold.
# The uniform and randn samplers should have about the same performance
# at this point.
stddev_inside_bounds_before_using_randn = (
_get_stddev_inside_bounds_before_using_randn(use_gpu))
epsilon = 0.001
np.random.seed(1618) # Make it reproducible.
# No CSE/CF.
optimizer_options = config_pb2.OptimizerOptions(
opt_level=config_pb2.OptimizerOptions.L0)
config = config_pb2.ConfigProto(
graph_options=config_pb2.GraphOptions(
optimizer_options=optimizer_options))
with session.Session(config=config) as sess:
with ops.device("/cpu:0" if not use_gpu else "/gpu:0"):
uniform_sampler_op = control_flow_ops.group(
random_ops.parameterized_truncated_normal(
shape,
means=0.,
stddevs=1.0,
minvals=-stddev_inside_bounds_before_using_randn + epsilon,
maxvals=0.01))
randn_sampler_op = control_flow_ops.group(
random_ops.parameterized_truncated_normal(
shape,
means=0.,
stddevs=1.0,
minvals=-stddev_inside_bounds_before_using_randn - epsilon,
maxvals=0.01))
# Burn-in to avoid session setup costs in the timing.
sess.run(uniform_sampler_op)
sess.run(uniform_sampler_op)
uniform_dt = timeit.timeit(
lambda: sess.run(uniform_sampler_op), number=num_iters)
sess.run(randn_sampler_op)
sess.run(randn_sampler_op)
randn_dt = timeit.timeit(
lambda: sess.run(randn_sampler_op), number=num_iters)
return randn_dt, uniform_dt
class TruncatedNormalBenchmark(test.Benchmark):
def benchmarkParameterizedOpVsNaiveOpCpu(self):
self._benchmarkParameterizedOpVsNaiveOp(False)
def benchmarkParameterizedOpVsNaiveOpGpu(self):
self._benchmarkParameterizedOpVsNaiveOp(True)
def _benchmarkParameterizedOpVsNaiveOp(self, use_gpu):
num_iters = 50
print(("Composition of new ParameterizedTruncatedNormalOp vs. "
"naive TruncatedNormalOp [%d iters]") % num_iters)
print("Shape\tsec(parameterized)\tsec(naive)\tspeedup")
for shape in [[10000, 100], [1000, 1000], [1000000], [100, 100, 100],
[20, 20, 20, 20]]:
p_dt, n_dt = parameterized_vs_naive(shape, num_iters, use_gpu)
print("%s\t%.3f\t%.3f\t%.2f" % (shape, p_dt, n_dt, p_dt / n_dt))
shape_str = "-".join(map(str, shape))
self.report_benchmark(
name="parameterized_shape" + shape_str,
iters=num_iters,
wall_time=p_dt)
self.report_benchmark(
name="naive_shape" + shape_str, iters=num_iters, wall_time=n_dt)
def benchmarkRandnSamplerCPU(self):
self._benchmarkRandnSampler(False)
def benchmarkRandnSamplerGPU(self):
self._benchmarkRandnSampler(True)
def _benchmarkRandnSampler(self, use_gpu):
num_iters = 100
shape = [int(1e6)]
randn_dt, uniform_dt = randn_sampler_switchover(shape, num_iters, use_gpu)
print(("Randn Sampler vs uniform samplers [%d iters]\t%.4f\t%.4f") %
(num_iters, randn_dt, uniform_dt))
gpu_str = "_gpu" if use_gpu else "_cpu"
self.report_benchmark(
name="randn_sampler" + gpu_str, iters=num_iters, wall_time=randn_dt)
self.report_benchmark(
name="uniform_sampler" + gpu_str, iters=num_iters, wall_time=uniform_dt)
if __name__ == "__main__":
test.main()
@@ -0,0 +1,223 @@
# Copyright 2019 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.stateful_random_ops.binomial."""
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.kernel_tests.random import util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import stateful_random_ops
from tensorflow.python.ops import stateless_random_ops
from tensorflow.python.platform import test
from tensorflow.python.platform import tf_logging
# All supported dtypes for binomial().
_SUPPORTED_DTYPES = (dtypes.float16, dtypes.float32, dtypes.float64,
dtypes.int32, dtypes.int64)
class RandomBinomialTest(test.TestCase):
"""This is a large test due to the moments computation taking some time."""
def _Sampler(
self, num, counts, probs, dtype, gen=None, sample_shape=None, seed=None):
def func():
shape = [10 * num] if sample_shape is None else sample_shape
generator = gen if gen is not None else (
stateful_random_ops.Generator.from_seed(seed))
return generator.binomial(
shape=shape, counts=counts, probs=probs, dtype=dtype)
return func
@test_util.run_v2_only
def testMoments(self):
try:
from scipy import stats # pylint: disable=g-import-not-at-top
except ImportError as e:
tf_logging.warn("Cannot test moments: %s", e)
return
# The moments test is a z-value test. This is the largest z-value
# we want to tolerate. Since the z-test approximates a unit normal
# distribution, it should almost definitely never exceed 6.
z_limit = 6.0
gen = stateful_random_ops.Generator.from_seed(seed=12345)
for dt in _SUPPORTED_DTYPES:
# Test when n * p > 10, and n * p < 10
for stride in 0, 4, 10:
for counts in (1., 10., 22., 50.):
for prob in (0.1, 0.5, 0.8):
sampler = self._Sampler(int(5e4), counts, prob, dt, gen=gen)
z_scores = util.test_moment_matching(
# Use float64 samples.
self.evaluate(sampler()).astype(np.float64),
number_moments=6,
dist=stats.binom(counts, prob),
stride=stride,
)
self.assertAllLess(z_scores, z_limit)
@test_util.run_v2_only
def testSeed(self):
for dt in dtypes.float16, dtypes.float32, dtypes.float64:
sx = self._Sampler(1000, counts=10., probs=0.4, dtype=dt, seed=345)
sy = self._Sampler(1000, counts=10., probs=0.4, dtype=dt, seed=345)
self.assertAllEqual(self.evaluate(sx()), self.evaluate(sy()))
def testStateless(self):
for dt in dtypes.float16, dtypes.float32, dtypes.float64:
sx = stateless_random_ops.stateless_random_binomial(
shape=[1000], seed=[12, 34], counts=10., probs=0.4, output_dtype=dt)
sy = stateless_random_ops.stateless_random_binomial(
shape=[1000], seed=[12, 34], counts=10., probs=0.4, output_dtype=dt)
sx0, sx1 = self.evaluate(sx), self.evaluate(sx)
sy0, sy1 = self.evaluate(sy), self.evaluate(sy)
self.assertAllEqual(sx0, sx1)
self.assertAllEqual(sx0, sy0)
self.assertAllEqual(sy0, sy1)
def testZeroShape(self):
rnd = stateful_random_ops.Generator.from_seed(12345).binomial([0], [], [])
self.assertEqual([0], rnd.shape.as_list())
def testShape(self):
rng = stateful_random_ops.Generator.from_seed(12345)
# Scalar parameters.
rnd = rng.binomial(shape=[10], counts=np.float32(2.), probs=np.float32(0.5))
self.assertEqual([10], rnd.shape.as_list())
rnd = rng.binomial(shape=[], counts=np.float32(2.), probs=np.float32(0.5))
self.assertEqual([], rnd.shape.as_list())
# Vector parameters.
rnd = rng.binomial(
shape=[10],
counts=array_ops.ones([10], dtype=np.float32),
probs=0.3 * array_ops.ones([10], dtype=np.float32))
self.assertEqual([10], rnd.shape.as_list())
rnd = rng.binomial(
shape=[5, 2],
counts=array_ops.ones([2], dtype=np.float32),
probs=0.4 * array_ops.ones([2], dtype=np.float32))
self.assertEqual([5, 2], rnd.shape.as_list())
# Scalar counts, vector probs.
rnd = rng.binomial(
shape=[10],
counts=np.float32(5.),
probs=0.8 * array_ops.ones([10], dtype=np.float32))
self.assertEqual([10], rnd.shape.as_list())
# Vector counts, scalar probs.
rnd = rng.binomial(
shape=[10],
counts=array_ops.ones([10], dtype=np.float32),
probs=np.float32(0.9))
self.assertEqual([10], rnd.shape.as_list())
# Tensor parameters
rnd = rng.binomial(
shape=[10, 2, 3],
counts=array_ops.ones([2, 1], dtype=np.float32),
probs=0.9 * array_ops.ones([1, 3], dtype=np.float32))
self.assertEqual([10, 2, 3], rnd.shape.as_list())
# Tensor parameters
rnd = rng.binomial(
shape=[10, 2, 3, 5],
counts=array_ops.ones([2, 1, 5], dtype=np.float32),
probs=0.9 * array_ops.ones([1, 3, 1], dtype=np.float32))
self.assertEqual([10, 2, 3, 5], rnd.shape.as_list())
@test_util.run_v2_only
def testCornerCases(self):
rng = stateful_random_ops.Generator.from_seed(12345)
counts = np.array([5, 5, 5, 0, 0, 0], dtype=np.float32)
probs = np.array([0, 1, float("nan"), -10, 10, float("nan")],
dtype=np.float32)
expected = np.array([0, 5, float("nan"), 0, 0, 0], dtype=np.float32)
result = rng.binomial(
shape=[6], counts=counts, probs=probs, dtype=np.float32)
self.assertAllEqual(expected, self.evaluate(result))
@test_util.run_v2_only
def testMomentsForTensorInputs(self):
try:
from scipy import stats # pylint: disable=g-import-not-at-top
except ImportError as e:
tf_logging.warn("Cannot test moments: %s", e)
return
# The moments test is a z-value test. This is the largest z-value
# we want to tolerate. Since the z-test approximates a unit normal
# distribution, it should almost definitely never exceed 6.
z_limit = 6.0
class ScipyBinomialWrapper(object):
"""Wrapper for stats.binom to support broadcasting."""
def __init__(self, counts, probs):
self.counts = counts
self.probs = probs
def moment(self, i):
counts, probs = np.broadcast_arrays(self.counts, self.probs)
broadcast_shape = counts.shape
counts = np.reshape(counts, (-1,))
probs = np.reshape(probs, (-1,))
counts_and_probs = np.stack([counts, probs], axis=-1)
moments = np.fromiter(
(stats.binom(cp[0], cp[1]).moment(i) for cp in counts_and_probs),
dtype=np.float64)
return np.reshape(moments, broadcast_shape)
gen = stateful_random_ops.Generator.from_seed(seed=23455)
for dt in _SUPPORTED_DTYPES:
# Test when n * p > 10, and n * p < 10
for stride in 0, 4, 10:
counts = np.float64(np.random.randint(low=1, high=20, size=(2, 1, 4)))
probs = np.random.uniform(size=(1, 3, 4))
sampler = self._Sampler(
int(5e4),
counts,
probs,
dt,
gen=gen,
sample_shape=[10 * int(5e4), 2, 3, 4])
# Use float64 samples.
samples = self.evaluate(sampler()).astype(np.float64)
z_scores = util.test_moment_matching(
samples,
number_moments=6,
dist=ScipyBinomialWrapper(counts, probs),
stride=stride,
)
self.assertAllLess(z_scores, z_limit)
def testStatelessDtypeInt64(self):
srb = stateless_random_ops.stateless_random_binomial(
shape=[constant_op.constant(1, dtype=dtypes.int64)],
seed=[12, 34],
counts=10.0,
probs=0.4,
output_dtype=dtypes.float16)
out = self.evaluate(srb)
self.assertEqual(out, [5.0])
if __name__ == "__main__":
test.main()
@@ -0,0 +1,162 @@
# 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 random_crop."""
import numpy as np
from tensorflow.python.framework import test_util
from tensorflow.python.ops import random_crop_ops
from tensorflow.python.platform import test
class RandomCropTest(test.TestCase):
@test_util.run_deprecated_v1
def testNoOp(self):
# No random cropping is performed since the size is value.shape.
for shape in (2, 1, 1), (2, 1, 3), (4, 5, 3):
value = np.arange(0, np.prod(shape), dtype=np.int32).reshape(shape)
with self.cached_session():
crop = random_crop_ops.random_crop(value, shape).eval()
self.assertAllEqual(crop, value)
def testContains(self):
with self.cached_session():
shape = (3, 5, 7)
target = (2, 3, 4)
value = np.random.randint(1000000, size=shape)
value_set = set(
tuple(value[i:i + 2, j:j + 3, k:k + 4].ravel())
for i in range(2) for j in range(3) for k in range(4))
crop = random_crop_ops.random_crop(value, size=target)
for _ in range(20):
y = self.evaluate(crop)
self.assertAllEqual(y.shape, target)
self.assertTrue(tuple(y.ravel()) in value_set)
@test_util.run_deprecated_v1
def testRandomization(self):
# Run 1x1 crop num_samples times in an image and ensure that one finds each
# pixel 1/size of the time.
num_samples = 1000
shape = [5, 4, 1]
size = np.prod(shape)
single = [1, 1, 1]
value = np.arange(size).reshape(shape)
with self.cached_session():
crop = random_crop_ops.random_crop(value, single, seed=7)
counts = np.zeros(size, dtype=np.int32)
for _ in range(num_samples):
y = self.evaluate(crop)
self.assertAllEqual(y.shape, single)
counts[y] += 1
# Calculate the mean and 4 * standard deviation.
mean = np.repeat(num_samples / size, size)
four_stddev = 4.0 * np.sqrt(mean)
# Ensure that each entry is observed in 1/size of the samples
# within 4 standard deviations.
self.assertAllClose(counts, mean, atol=four_stddev)
class StatelessRandomCropTest(test.TestCase):
def testNoOp(self):
# No random cropping is performed since the size is value.shape.
for shape in (2, 1, 1), (2, 1, 3), (4, 5, 3):
value = np.arange(0, np.prod(shape), dtype=np.int32).reshape(shape)
crop = random_crop_ops.stateless_random_crop(value, shape, seed=(1, 2))
self.evaluate(crop)
self.assertAllEqual(crop, value)
def testContains(self):
with test_util.use_gpu():
shape = (3, 5, 7)
target = (2, 3, 4)
value = np.random.randint(1000000, size=shape)
iterations = 10
value_set = set(
tuple(value[i:i + 2, j:j + 3, k:k + 4].ravel()) # pylint: disable=g-complex-comprehension
for i in range(2) for j in range(3) for k in range(4))
test_seeds = [
tuple(map(lambda x, i=i: x + 1 * i, t))
for (i, t) in enumerate((1, 2) for _ in range(iterations))
]
# Check that the result is valid by making sure that it is one of all
# possible values for randomly cropping `value` with `target` shape.
for seed in test_seeds:
crop = random_crop_ops.stateless_random_crop(
value, size=target, seed=seed)
y = self.evaluate(crop)
self.assertAllEqual(y.shape, target)
self.assertIn(tuple(y.ravel()), value_set)
# TODO(b/162345082): stateless random op generates different random number
# with xla_gpu. Update tests such that there is a single ground truth result
# to test against.
def testRandomization(self):
with test_util.use_gpu():
shape = [5, 4, 1]
size = np.prod(shape)
single = [1, 1, 1]
value = np.arange(size).reshape(shape)
iterations = 5
num_samples = 5
# Test that the same result is returned given the same seed is provided
# for each round.
test_seed = (1, 2)
observations = [[] for _ in range(iterations)]
for observation in observations:
crop = random_crop_ops.stateless_random_crop(
value, single, seed=test_seed)
counts = np.zeros(size, dtype=np.int32)
for _ in range(num_samples):
y = self.evaluate(crop)
self.assertAllEqual(y.shape, single)
counts[y] += 1
observation.append(counts)
for i in range(1, iterations):
self.assertAllEqual(observations[0], observations[i])
# Test that the same sequence of results are returned given the same
# sequence of seeds provided.
test_seeds = [
tuple(map(lambda x, i=i: x + 1 * i, t))
for (i, t) in enumerate((1, 2) for _ in range(iterations))
]
observations = [[] for _ in range(iterations)]
for observation in observations:
counts = np.zeros(size, dtype=np.int32)
for seed in test_seeds:
crop = random_crop_ops.stateless_random_crop(
value, single, seed=seed)
y = self.evaluate(crop)
self.assertAllEqual(y.shape, single)
counts[y] += 1
observation.append(counts)
for i in range(1, iterations):
self.assertAllEqual(observations[0], observations[i])
if __name__ == "__main__":
test.main()
@@ -0,0 +1,234 @@
# 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 tensorflow.ops.random_ops.random_gamma."""
import numpy as np
from tensorflow.python.eager import context
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 random_seed
from tensorflow.python.framework import test_util
from tensorflow.python.kernel_tests.random import util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import random_ops
from tensorflow.python.platform import test
from tensorflow.python.platform import tf_logging
class RandomGammaTest(test.TestCase):
"""This is a medium test due to the moments computation taking some time."""
def setUp(self):
np.random.seed(137)
random_seed.set_random_seed(137)
def _Sampler(self, num, alpha, beta, dtype, use_gpu=True, seed=None):
def func():
with self.session(use_gpu=use_gpu, graph=ops.Graph()) as sess:
rng = random_ops.random_gamma(
[num], alpha, beta=beta, dtype=dtype, seed=seed)
ret = np.empty([10, num])
for i in range(10):
ret[i, :] = self.evaluate(rng)
return ret
return func
def testNpDtypes(self):
self.evaluate(random_ops.random_gamma(
[5], alpha=np.ones([2, 1, 3]), beta=np.ones([3]), dtype=np.float32))
def testEmptySamplingNoError(self):
self.evaluate(random_ops.random_gamma(
[5], alpha=np.ones([2, 0, 3]), beta=np.ones([3]), dtype=dtypes.float32))
@test_util.run_deprecated_v1
def testMomentsFloat32(self):
self._testMoments(dtypes.float32)
@test_util.run_deprecated_v1
def testMomentsFloat64(self):
self._testMoments(dtypes.float64)
def _testMoments(self, dt):
try:
from scipy import stats # pylint: disable=g-import-not-at-top
except ImportError as e:
tf_logging.warn("Cannot test moments: %s" % e)
return
# The moments test is a z-value test. This is the largest z-value
# we want to tolerate. Since the z-test approximates a unit normal
# distribution, it should almost definitely never exceed 6.
z_limit = 6.0
for stride in 0, 1, 4, 17:
alphas = [0.2, 1.0, 3.0]
if dt == dtypes.float64:
alphas = [0.01] + alphas
for alpha in alphas:
for scale in 9, 17:
# Gamma moments only defined for values less than the scale param.
max_moment = min(6, scale // 2)
sampler = self._Sampler(20000, alpha, 1 / scale, dt, seed=12345)
z_scores = util.test_moment_matching(
sampler(),
max_moment,
stats.gamma(alpha, scale=scale),
stride=stride,
)
self.assertAllLess(z_scores, z_limit)
def _testZeroDensity(self, alpha):
"""Zero isn't in the support of the gamma distribution.
But quantized floating point math has its limits.
TODO(bjp): Implement log-gamma sampler for small-shape distributions.
Args:
alpha: float shape value to test
"""
try:
from scipy import stats # pylint: disable=g-import-not-at-top
except ImportError as e:
tf_logging.warn("Cannot test zero density proportions: %s" % e)
return
allowable_zeros = {
dtypes.float16: stats.gamma(alpha).cdf(np.finfo(np.float16).tiny),
dtypes.float32: stats.gamma(alpha).cdf(np.finfo(np.float32).tiny),
dtypes.float64: stats.gamma(alpha).cdf(np.finfo(np.float64).tiny)
}
failures = []
for dt in dtypes.float16, dtypes.float32, dtypes.float64:
sampler = self._Sampler(10000, alpha, 1.0, dt, seed=12345)
x = sampler()
allowable = allowable_zeros[dt] * x.size
allowable = allowable * 2 if allowable < 10 else allowable * 1.05
if np.sum(x <= 0) > allowable:
failures += [dt]
self.assertEqual([], failures)
def testNonZeroSmallShape(self):
self._testZeroDensity(0.01)
def testNonZeroSmallishShape(self):
self._testZeroDensity(0.35)
# Asserts that different trials (1000 samples per trial) is unlikely
# to see the same sequence of values. Will catch buggy
# implementations which uses the same random number seed.
def testDistinct(self):
for dt in dtypes.float16, dtypes.float32, dtypes.float64:
sampler = self._Sampler(1000, 2.0, 1.0, dt)
x = sampler()
y = sampler()
# Number of different samples.
count = (x == y).sum()
count_limit = 20 if dt == dtypes.float16 else 10
self.assertLess(count, count_limit)
# Checks that the CPU and GPU implementation returns the same results,
# given the same random seed
@test_util.run_deprecated_v1
def testCPUGPUMatch(self):
for dt in dtypes.float16, dtypes.float32, dtypes.float64:
results = {}
for use_gpu in [False, True]:
sampler = self._Sampler(1000, 0.0, 1.0, dt, use_gpu=use_gpu, seed=12345)
results[use_gpu] = sampler()
if dt == dtypes.float16:
self.assertAllClose(results[False], results[True], rtol=1e-3, atol=1e-3)
else:
self.assertAllClose(results[False], results[True], rtol=1e-6, atol=1e-6)
def testSeed(self):
for dt in dtypes.float16, dtypes.float32, dtypes.float64:
sx = self._Sampler(1000, 0.0, 1.0, dt, seed=345)
sy = self._Sampler(1000, 0.0, 1.0, dt, seed=345)
self.assertAllEqual(sx(), sy())
@test_util.run_deprecated_v1
def testNoCSE(self):
"""CSE = constant subexpression eliminator.
SetIsStateful() should prevent two identical random ops from getting
merged.
"""
for dtype in dtypes.float16, dtypes.float32, dtypes.float64:
with self.cached_session():
rnd1 = random_ops.random_gamma([24], 2.0, dtype=dtype)
rnd2 = random_ops.random_gamma([24], 2.0, dtype=dtype)
diff = rnd2 - rnd1
self.assertGreater(np.linalg.norm(diff.eval()), 0.1)
@test_util.run_deprecated_v1
def testShape(self):
# Fully known shape.
rnd = random_ops.random_gamma([150], 2.0)
self.assertEqual([150], rnd.get_shape().as_list())
rnd = random_ops.random_gamma([150], 2.0, beta=[3.0, 4.0])
self.assertEqual([150, 2], rnd.get_shape().as_list())
rnd = random_ops.random_gamma([150], array_ops.ones([1, 2, 3]))
self.assertEqual([150, 1, 2, 3], rnd.get_shape().as_list())
rnd = random_ops.random_gamma([20, 30], array_ops.ones([1, 2, 3]))
self.assertEqual([20, 30, 1, 2, 3], rnd.get_shape().as_list())
rnd = random_ops.random_gamma(
[123], array_ops.placeholder(
dtypes.float32, shape=(2,)))
self.assertEqual([123, 2], rnd.get_shape().as_list())
# Partially known shape.
rnd = random_ops.random_gamma(
array_ops.placeholder(
dtypes.int32, shape=(1,)), array_ops.ones([7, 3]))
self.assertEqual([None, 7, 3], rnd.get_shape().as_list())
rnd = random_ops.random_gamma(
array_ops.placeholder(
dtypes.int32, shape=(3,)), array_ops.ones([9, 6]))
self.assertEqual([None, None, None, 9, 6], rnd.get_shape().as_list())
# Unknown shape.
rnd = random_ops.random_gamma(
array_ops.placeholder(dtypes.int32),
array_ops.placeholder(dtypes.float32))
self.assertIs(None, rnd.get_shape().ndims)
rnd = random_ops.random_gamma([50], array_ops.placeholder(dtypes.float32))
self.assertIs(None, rnd.get_shape().ndims)
@test_util.run_deprecated_v1
def testPositive(self):
n = int(10e3)
for dt in [dtypes.float16, dtypes.float32, dtypes.float64]:
with self.cached_session():
x = random_ops.random_gamma(shape=[n], alpha=0.001, dtype=dt, seed=0)
self.assertEqual(0, math_ops.reduce_sum(math_ops.cast(
math_ops.less_equal(x, 0.), dtype=dtypes.int64)).eval())
def testSizeTooLarge(self):
# Grappler asserts on size overflow, so this error is only caught when
# running eagerly.
if context.executing_eagerly():
with self.assertRaisesRegex((ValueError, errors.InvalidArgumentError),
"overflow"):
rate = constant_op.constant(1.0, shape=(4, 4, 4, 4, 4))
self.evaluate(
random_ops.random_gamma(
shape=[46902, 51188, 34063, 59195], alpha=rate))
if __name__ == "__main__":
test.main()
@@ -0,0 +1,279 @@
# Copyright 2018 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.random_grad."""
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 array_ops
from tensorflow.python.ops import gen_stateless_random_ops_v2
from tensorflow.python.ops import gradients_impl
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import random_grad
from tensorflow.python.ops import random_ops
from tensorflow.python.platform import test
from tensorflow.python.platform import tf_logging
class AddLeadingUnitDimensionsTest(test.TestCase):
def testBasic(self):
ret = random_grad.add_leading_unit_dimensions(array_ops.ones([3, 2, 1]), 3)
self.assertAllEqual(ret.shape, [1, 1, 1, 3, 2, 1])
def testZeroExtraDimensions(self):
ret = random_grad.add_leading_unit_dimensions(array_ops.ones([3, 2, 1]), 0)
self.assertAllEqual(ret.shape, [3, 2, 1])
def testScalarInput(self):
ret = random_grad.add_leading_unit_dimensions(1.0, 2)
self.assertAllEqual(ret.shape, [1, 1])
@test_util.run_deprecated_v1
def testUnknownShape(self):
x = array_ops.placeholder(dtypes.float32)
num_dimensions = array_ops.placeholder(dtypes.int32)
ret = random_grad.add_leading_unit_dimensions(x, num_dimensions)
with self.cached_session() as sess:
ret_val = sess.run(ret, {x: np.ones([2, 2]), num_dimensions: 2})
self.assertAllEqual(ret_val.shape, [1, 1, 2, 2])
class RandomGammaGradTest(test.TestCase):
"""Tests for derivative of a sample ~ Gamma(alpha, beta) wrt alpha and beta.
The sample is an "implicit" function of alpha, beta and the independent random
noise u. The derivatives we are looking for are
d sample(alpha, beta, u) / dalpha (and dbeta).
The derivative w.r.t. beta is computed by the standard automatic
differentiation, so we trust that it is computed correctly.
The derivative w.r.t. alpha is computed by Eigen function, so we test it in
several ways. Unfortunately, the standard derivative checking by perturbing
the parameter is impossible here, because we cannot fix the value of u
in the random sampler. Instead, we compare the derivative for the given pair
of (sample, alpha) to the values computed in various ways, and also check
some statistical properties of the derivative.
"""
@test_util.run_deprecated_v1
def testGradientsShape(self):
shape = [2, 3]
alpha = array_ops.ones([2, 2])
beta = array_ops.ones([1, 2])
sample = random_ops.random_gamma(shape, alpha, beta, seed=12345)
grads_alpha, grads_beta = gradients_impl.gradients(sample, [alpha, beta])
self.assertAllEqual(grads_alpha.shape, alpha.shape)
self.assertAllEqual(grads_beta.shape, beta.shape)
@test_util.run_deprecated_v1
def testGradientsShapeWithOneSamplePerParameter(self):
shape = []
alpha = array_ops.ones([2, 2])
beta = array_ops.ones([1, 2])
sample = random_ops.random_gamma(shape, alpha, beta, seed=12345)
grads_alpha, grads_beta = gradients_impl.gradients(sample, [alpha, beta])
self.assertAllEqual(grads_alpha.shape, alpha.shape)
self.assertAllEqual(grads_beta.shape, beta.shape)
@test_util.run_deprecated_v1
def testGradientsUnknownShape(self):
shape = array_ops.placeholder(dtypes.int32)
alpha = array_ops.placeholder(dtypes.float32)
beta = array_ops.placeholder(dtypes.float32)
sample = random_ops.random_gamma(shape, alpha, beta, seed=12345)
grads_alpha, grads_beta = gradients_impl.gradients(sample, [alpha, beta])
alpha_val = np.ones([1, 2])
beta_val = np.ones([2, 1])
with self.cached_session() as sess:
grads_alpha_val, grads_beta_val = sess.run(
[grads_alpha, grads_beta],
{alpha: alpha_val, beta: beta_val, shape: [2, 1]})
self.assertAllEqual(grads_alpha_val.shape, alpha_val.shape)
self.assertAllEqual(grads_beta_val.shape, beta_val.shape)
def _testCompareToExplicitDerivative(self, dtype):
"""Compare to the explicit reparameterization derivative.
Verifies that the computed derivative satisfies
dsample / dalpha = d igammainv(alpha, u) / dalpha,
where u = igamma(alpha, sample).
Args:
dtype: TensorFlow dtype to perform the computations in.
"""
delta = 1e-3
np_dtype = dtype.as_numpy_dtype
try:
from scipy import differentiate # pylint: disable=g-import-not-at-top
from scipy import special # pylint: disable=g-import-not-at-top
alpha_val = np.logspace(-2, 3, dtype=np_dtype)
alpha = constant_op.constant(alpha_val)
sample = random_ops.random_gamma(
[], alpha, np_dtype(1.0), dtype=dtype, seed=12345)
actual = gradients_impl.gradients(sample, alpha)[0]
(sample_val, actual_val) = self.evaluate((sample, actual))
u = special.gammainc(alpha_val, sample_val)
expected_val = differentiate.derivative(
special.gammaincinv,
alpha_val,
args=(u,),
initial_step=delta * alpha_val,
order=2,
preserve_shape=True,
).df
self.assertAllClose(actual_val, expected_val, rtol=1e-3, atol=1e-3)
except ImportError as e:
tf_logging.warn("Cannot use special functions in a test: %s" % str(e))
@test_util.run_deprecated_v1
def testCompareToExplicitDerivativeFloat(self):
self._testCompareToExplicitDerivative(dtypes.float32)
@test_util.run_deprecated_v1
def testCompareToExplicitDerivativeDouble(self):
self._testCompareToExplicitDerivative(dtypes.float64)
def _testCompareToImplicitDerivative(self, dtype):
"""Compare to the implicit reparameterization derivative.
Let's derive the formula we compare to.
Start from the fact that CDF maps a random variable to the Uniform
random variable:
igamma(alpha, sample) = u, where u ~ Uniform(0, 1).
Apply d / dalpha to both sides:
d igamma(alpha, sample) / dalpha
+ d igamma(alpha, sample) / dsample * dsample/dalpha = 0
d igamma(alpha, sample) / dalpha
+ d igamma(alpha, sample) / dsample * dsample / dalpha = 0
dsample/dalpha = - (d igamma(alpha, sample) / dalpha)
/ d igamma(alpha, sample) / dsample
This is the equation (8) of https://arxiv.org/abs/1805.08498
Args:
dtype: TensorFlow dtype to perform the computations in.
"""
np_dtype = dtype.as_numpy_dtype
alpha = constant_op.constant(np.logspace(-2, 3, dtype=np_dtype))
sample = random_ops.random_gamma(
[], alpha, np_dtype(1.0), dtype=dtype, seed=12345)
actual = gradients_impl.gradients(sample, alpha)[0]
sample_sg = array_ops.stop_gradient(sample)
cdf = math_ops.igamma(alpha, sample_sg)
dcdf_dalpha, dcdf_dsample = gradients_impl.gradients(
cdf, [alpha, sample_sg])
# Numerically unstable due to division, do not try at home.
expected = -dcdf_dalpha / dcdf_dsample
(actual_val, expected_val) = self.evaluate((actual, expected))
self.assertAllClose(actual_val, expected_val, rtol=1e-3, atol=1e-3)
@test_util.run_deprecated_v1
def testCompareToImplicitDerivativeFloat(self):
self._testCompareToImplicitDerivative(dtypes.float32)
@test_util.run_deprecated_v1
def testCompareToImplicitDerivativeDouble(self):
self._testCompareToImplicitDerivative(dtypes.float64)
@test_util.run_deprecated_v1
def testAverageAlphaGradient(self):
"""Statistical test for the gradient.
Using the equation (5) of https://arxiv.org/abs/1805.08498, we have
1 = d/dalpha E_{sample ~ Gamma(alpha, 1)} sample
= E_{sample ~ Gamma(alpha, 1)} dsample/dalpha.
Here we verify that the rhs is fairly close to one.
The convergence speed is not great, so we use many samples and loose bounds.
"""
num_samples = 10000
alpha = constant_op.constant([0.8, 1e1, 1e3], dtype=dtypes.float32)
sample = random_ops.random_gamma([num_samples], alpha, seed=12345)
# We need to average the gradients, which is equivalent to averaging the
# samples and then doing backprop.
mean_sample = math_ops.reduce_mean(sample, axis=0)
dsample_dalpha = gradients_impl.gradients(mean_sample, alpha)[0]
dsample_dalpha_val = self.evaluate(dsample_dalpha)
self.assertAllClose(dsample_dalpha_val, [1.0] * 3, atol=1e-1, rtol=1e-1)
@test_util.run_deprecated_v1
def testQuadraticLoss(self):
"""Statistical test for the gradient.
The equation (5) of https://arxiv.org/abs/1805.08498 says
d/dalpha E_{sample ~ Gamma(alpha, 1)} f(sample)
= E_{sample ~ Gamma(alpha, 1)} df(sample)/dalpha.
Choose a quadratic loss function f(sample) = (sample - t)^2.
Then, the lhs can be computed analytically:
d/dalpha E_{sample ~ Gamma(alpha, 1)} f(sample)
= d/dalpha [ (alpha + alpha^2) - 2 * t * alpha + t^2 ]
= 1 + 2 * alpha - 2 * t.
We compare the Monte-Carlo estimate of the expectation with the
true gradient.
"""
num_samples = 10000
t = 0.3
alpha = 0.5
expected = 1 + 2 * alpha - 2 * t
alpha = constant_op.constant(alpha)
sample = random_ops.random_gamma([num_samples], alpha, 1.0, seed=12345)
loss = math_ops.reduce_mean(math_ops.square(sample - t))
dloss_dalpha = gradients_impl.gradients(loss, alpha)[0]
dloss_dalpha_val = self.evaluate(dloss_dalpha)
self.assertAllClose(expected, dloss_dalpha_val, atol=1e-1, rtol=1e-1)
@test_util.run_deprecated_v1
def testQuadraticLossV3(self):
"""Statistical test for the gradient.
This is the same test as in testQuadraticLoss but for
StatelessRandomGammaV3.
"""
shape = constant_op.constant([10000])
t = 0.3
alpha = constant_op.constant(0.5, dtype=dtypes.float32)
key = constant_op.constant([0], dtype=dtypes.uint64)
counter = constant_op.constant([10, 20], dtype=dtypes.uint64)
# Use PHILOX algorithm
alg = constant_op.constant(1)
expected = 1 + 2 * alpha - 2 * t
sample = gen_stateless_random_ops_v2.stateless_random_gamma_v3(
shape=shape, key=key, counter=counter, alg=alg, alpha=alpha)
loss = math_ops.reduce_mean(math_ops.square(sample - t))
dloss_dalpha = gradients_impl.gradients(loss, alpha)[0]
dloss_dalpha_val = self.evaluate(dloss_dalpha)
self.assertAllClose(expected, dloss_dalpha_val, atol=1e-1, rtol=1e-1)
if __name__ == "__main__":
test.main()
@@ -0,0 +1,123 @@
# Copyright 2022 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 random index shuffle ops."""
import itertools
from absl.testing import parameterized
from tensorflow.python.eager import def_function
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import errors
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import gen_random_index_shuffle_ops
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import stateless_random_ops as stateless
from tensorflow.python.platform import test
_SEEDS = ((74, 117), (42, 5))
_MAX_INDEX = (129, 2_389)
_ROUNDS = (4, 8)
_DTYPES = (dtypes.int32, dtypes.uint32, dtypes.int64, dtypes.uint64)
class StatelessOpsTest(test.TestCase, parameterized.TestCase):
@parameterized.parameters(
itertools.product(_SEEDS, _DTYPES, _MAX_INDEX, _DTYPES, _ROUNDS))
def testRawOp(self, seed, seed_dtype, max_index, index_dtype, rounds):
if max_index > 200:
self.skipTest('Too slow in graph mode.')
seen = (max_index + 1) * [False]
seed = math_ops.cast([seed[0], seed[1], 42], seed_dtype)
for index in range(max_index + 1):
new_index = gen_random_index_shuffle_ops.random_index_shuffle(
math_ops.cast(index, index_dtype),
seed,
max_index=math_ops.cast(max_index, index_dtype),
rounds=rounds)
self.assertEqual(new_index.dtype, index_dtype)
new_index = self.evaluate(new_index)
self.assertGreaterEqual(new_index, 0)
self.assertLessEqual(new_index, max_index)
self.assertFalse(seen[new_index])
seen[new_index] = True
@parameterized.parameters(
itertools.product(_SEEDS, _DTYPES, _MAX_INDEX, _DTYPES))
def testUnbatched(self, seed, seed_dtype, max_index, index_dtype):
if max_index > 200:
self.skipTest('Too slow in graph mode.')
seen = (max_index + 1) * [False]
seed = math_ops.cast(seed, seed_dtype)
for index in range(max_index + 1):
new_index = stateless.index_shuffle(
math_ops.cast(index, index_dtype),
seed,
max_index=math_ops.cast(max_index, index_dtype))
self.assertEqual(new_index.dtype, index_dtype)
new_index = self.evaluate(new_index)
self.assertGreaterEqual(new_index, 0)
self.assertLessEqual(new_index, max_index)
self.assertFalse(seen[new_index])
seen[new_index] = True
@parameterized.parameters(
itertools.product(_SEEDS, _DTYPES, _MAX_INDEX, _DTYPES))
def testBatchedBroadcastSeedAndMaxval(self, seed, seed_dtype, max_index,
index_dtype):
seed = math_ops.cast(seed, seed_dtype)
index = math_ops.cast(range(max_index + 1), index_dtype)
new_index = stateless.index_shuffle(index, seed, max_index=max_index)
self.assertEqual(new_index.dtype, index_dtype)
new_index = self.evaluate(new_index)
self.assertAllGreaterEqual(new_index, 0)
self.assertAllLessEqual(new_index, max_index)
self.assertLen(new_index, max_index + 1)
self.assertLen(set(new_index), max_index + 1)
def test_unknown_shape(self):
@def_function.function
def shuffle(repeats):
indices = array_ops.repeat(2, repeats)
return stateless.index_shuffle(indices, seed=(1, 2), max_index=10)
new_index = shuffle(constant_op.constant(2))
self.assertAllGreaterEqual(new_index, 0)
self.assertAllLessEqual(new_index, 10)
def test_negative_index(self):
with self.assertRaisesRegex(
errors.InvalidArgumentError, 'index must be >= 0'
):
self.evaluate(stateless.index_shuffle(-1, seed=(1, 2), max_index=10))
def test_negative_max_index(self):
with self.assertRaisesRegex(
errors.InvalidArgumentError, 'max_index must be >= 0'
):
self.evaluate(stateless.index_shuffle(0, seed=(1, 2), max_index=-1))
def test_index_greater_than_max_index(self):
with self.assertRaisesRegex(
errors.InvalidArgumentError, 'max_index must be >= index'
):
self.evaluate(stateless.index_shuffle(5, seed=(1, 2), max_index=4))
if __name__ == '__main__':
test.main()
@@ -0,0 +1,519 @@
# 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.random_ops."""
import numpy as np
from tensorflow.python.eager import context
from tensorflow.python.framework import config
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import errors
from tensorflow.python.framework import ops
from tensorflow.python.framework import random_seed
from tensorflow.python.framework import test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import gen_random_ops
from tensorflow.python.ops import random_ops
from tensorflow.python.ops import variables
from tensorflow.python.platform import test
def get_float_types():
float_types = [dtypes.float16, dtypes.float32, dtypes.float64]
if test_util.is_gpu_available(
cuda_only=True, min_cuda_compute_capability=(8, 0)):
float_types += [dtypes.bfloat16]
return float_types
class RandomOpTestCommon(test.TestCase):
# Checks that executing the same rng_func multiple times rarely produces the
# same result.
def _testSingleSessionNotConstant(self,
rng_func,
num,
dtype,
min_or_mean,
max_or_stddev,
use_gpu,
op_seed=None,
graph_seed=None):
with self.session(use_gpu=use_gpu, graph=ops.Graph()) as sess:
if graph_seed is not None:
random_seed.set_random_seed(graph_seed)
x = rng_func([num], min_or_mean, max_or_stddev, dtype=dtype, seed=op_seed)
y = self.evaluate(x)
z = self.evaluate(x)
w = self.evaluate(x)
# We use exact equality here. If the random-number generator is producing
# the same output, all three outputs will be bitwise identical.
self.assertTrue((not np.array_equal(y, z)) or
(not np.array_equal(z, w)) or (not np.array_equal(y, w)))
class RandomNormalTest(RandomOpTestCommon):
def _Sampler(self, num, mu, sigma, dtype, use_gpu, seed=None):
def func():
with self.session(use_gpu=use_gpu, graph=ops.Graph()) as sess:
rng = random_ops.random_normal(
[num], mean=mu, stddev=sigma, dtype=dtype, seed=seed)
ret = np.empty([10, num])
for i in range(10):
ret[i, :] = self.evaluate(rng)
return ret
return func
# Asserts that different trials (1000 samples per trial) is unlikely
# to see the same sequence of values. Will catch buggy
# implementations which uses the same random number seed.
def testDistinct(self):
for dt in get_float_types():
sampler = self._Sampler(1000, 0.0, 1.0, dt, use_gpu=True)
x = sampler()
y = sampler()
# Number of different samples.
count = (x == y).sum()
if count >= 10:
print("x = ", x)
print("y = ", y)
print("count = ", count)
self.assertTrue(count < 10)
# Checks that the CPU and GPU implementation returns the same results,
# given the same random seed
@test_util.run_deprecated_v1
def testCPUGPUMatch(self):
for dt in get_float_types():
results = {}
for use_gpu in [False, True]:
sampler = self._Sampler(
1000000, 0.0, 1.0, dt, use_gpu=use_gpu, seed=12345)
results[use_gpu] = sampler()
rtol = atol = 1e-6
if dt == dtypes.float16:
rtol = atol = 1e-3
elif dt == dtypes.bfloat16:
rtol = atol = 1e-1
self.assertAllClose(results[False], results[True], rtol=rtol, atol=atol)
@test_util.run_deprecated_v1
def testSeed(self):
for dt in get_float_types():
sx = self._Sampler(1000, 0.0, 1.0, dt, use_gpu=True, seed=345)
sy = self._Sampler(1000, 0.0, 1.0, dt, use_gpu=True, seed=345)
self.assertAllEqual(sx(), sy())
@test_util.run_deprecated_v1
def testNoCSE(self):
for use_gpu in [False, True]:
with self.session(use_gpu=use_gpu):
shape = [2, 3, 4]
rnd1 = random_ops.random_normal(shape, 0.0, 1.0, dtypes.float32)
rnd2 = random_ops.random_normal(shape, 0.0, 1.0, dtypes.float32)
diff = rnd2 - rnd1
self.assertTrue(np.linalg.norm(diff.eval()) > 0.1)
@test_util.run_deprecated_v1
def testSingleSessionNotConstant(self):
for use_gpu in [False, True]:
for dt in get_float_types():
self._testSingleSessionNotConstant(
random_ops.random_normal, 100, dt, 0.0, 1.0, use_gpu=use_gpu)
@test_util.run_deprecated_v1
def testSingleSessionOpSeedNotConstant(self):
for use_gpu in [False, True]:
for dt in get_float_types():
self._testSingleSessionNotConstant(
random_ops.random_normal,
100,
dt,
0.0,
1.0,
use_gpu=use_gpu,
op_seed=1345)
@test_util.run_deprecated_v1
def testSingleSessionGraphSeedNotConstant(self):
for use_gpu in [False, True]:
for dt in get_float_types():
self._testSingleSessionNotConstant(
random_ops.random_normal,
100,
dt,
0.0,
1.0,
use_gpu=use_gpu,
graph_seed=965)
@test_util.with_eager_op_as_function
class TruncatedNormalTest(test.TestCase):
def _Sampler(self, num, mu, sigma, dtype, use_gpu, seed=None):
def func():
with self.session(use_gpu=use_gpu, graph=ops.Graph()) as sess:
rng = random_ops.truncated_normal(
[num], mean=mu, stddev=sigma, dtype=dtype, seed=seed)
ret = np.empty([10, num])
for i in range(10):
ret[i, :] = self.evaluate(rng)
return ret
return func
# Asserts that different trials (1000 samples per trial) is unlikely
# to see the same sequence of values. Will catch buggy
# implementations which uses the same random number seed.
def testDistinct(self):
# NOTE: TruncatedNormal on GPU is not supported.
if not test.is_gpu_available():
for dt in get_float_types():
sampler = self._Sampler(1000, 0.0, 1.0, dt, use_gpu=False)
x = sampler()
y = sampler()
# Number of different samples.
count = (x == y).sum()
if count >= 10:
print("x = ", x)
print("y = ", y)
print("count = ", count)
self.assertTrue(count < 10)
# Checks that the CPU and GPU implementation returns the same results,
# given the same random seed
@test_util.run_deprecated_v1
def testCPUGPUMatch(self):
# Skip the test if there is no GPU.
if not test.is_gpu_available():
return
for dt in get_float_types():
results = {}
for use_gpu in [False, True]:
# We need a particular larger number of samples to test multiple rounds
# on GPU
sampler = self._Sampler(
1000000, 0.0, 1.0, dt, use_gpu=use_gpu, seed=12345)
results[use_gpu] = sampler()
atol = rtol = 1e-6
if dt == dtypes.float16:
atol = rtol = 1e-3
if dt == dtypes.bfloat16:
atol = rtol = 1e-1
self.assertAllClose(results[False], results[True], rtol=rtol, atol=atol)
@test_util.run_deprecated_v1
def testSeed(self):
for dt in get_float_types():
sx = self._Sampler(1000, 0.0, 1.0, dt, use_gpu=True, seed=345)
sy = self._Sampler(1000, 0.0, 1.0, dt, use_gpu=True, seed=345)
self.assertAllEqual(sx(), sy())
# The effective standard deviation of truncated normal is 85% of the
# requested one.
def testStdDev(self):
for dt in get_float_types():
stddev = 3.0
sampler = self._Sampler(100000, 0.0, stddev, dt, use_gpu=True)
x = sampler()
print("std(x)", np.std(x), abs(np.std(x) / stddev - 0.85))
self.assertLess(abs(np.std(x) / stddev - 0.85), 0.04)
def testSuccessAfterError(self):
# Force an error on the TruncatedNormal kernel.
config.enable_op_determinism()
with self.assertRaisesRegex(
errors.InvalidArgumentError,
"When determinism is enabled, random ops must have a seed specified"):
self.evaluate(gen_random_ops.truncated_normal((1,), dtypes.float32))
config.disable_op_determinism()
# Ensure the StdDev of the TruncatedNormal works as intended.
self.testStdDev()
@test_util.run_deprecated_v1
def testLargeShape(self):
with self.session():
v = variables.Variable(
array_ops.zeros(dtype=dtypes.float32, shape=[2**33, 1]))
n = random_ops.truncated_normal(v.shape)
self.assertEqual([8589934592, 1], n.shape.as_list())
@test_util.run_deprecated_v1
def testNoCSE(self):
with self.session():
shape = [2, 3, 4]
rnd1 = random_ops.truncated_normal(shape, 0.0, 1.0, dtypes.float32)
rnd2 = random_ops.truncated_normal(shape, 0.0, 1.0, dtypes.float32)
diff = rnd2 - rnd1
self.assertTrue(np.linalg.norm(diff.eval()) > 0.1)
def testEagerSeed(self):
with context.eager_mode():
# Ensure a context has been created
random_ops.random_normal([])
# Set the same seed twice and check that the values match
context.set_global_seed(42)
rnd1 = random_ops.random_normal([])
context.set_global_seed(42)
rnd2 = random_ops.random_normal([])
self.assertAllEqual(rnd1, rnd2)
@test_util.with_eager_op_as_function
@test_util.for_all_test_methods(test_util.disable_xla,
"This never passed on XLA")
class RandomUniformTest(RandomOpTestCommon):
def _Sampler(self, num, minv, maxv, dtype, use_gpu, seed=None):
def func():
with self.session(use_gpu=use_gpu, graph=ops.Graph()) as sess:
rng = random_ops.random_uniform(
[num], minval=minv, maxval=maxv, dtype=dtype, seed=seed)
ret = np.empty([10, num])
for i in range(10):
ret[i, :] = self.evaluate(rng)
return ret
return func
def testRange(self):
for dt in get_float_types() + [dtypes.int32, dtypes.int64]:
sampler = self._Sampler(1000, minv=-2, maxv=8, dtype=dt, use_gpu=True)
x = sampler()
self.assertTrue(-2 <= np.min(x))
self.assertTrue(np.max(x) < 8)
# Asserts that different trials (1000 samples per trial) is unlikely
# to see the same sequence of values. Will catch buggy
# implementations which uses the same random number seed.
def testDistinct(self):
for dt in get_float_types() + [dtypes.int32, dtypes.int64]:
maxv = 1.0 if dt.is_floating else 1 << 30
sampler = self._Sampler(1000, minv=0, maxv=maxv, dtype=dt, use_gpu=True)
x = sampler()
y = sampler()
count = (x == y).sum()
count_limit = 10
if dt == dtypes.float16:
count_limit = 50
elif dt == dtypes.bfloat16:
count_limit = 90
if count >= count_limit:
print("x = ", x)
print("y = ", y)
print("count = ", count)
self.assertTrue(count < count_limit)
@test_util.run_deprecated_v1
def testUniformIntsWithInvalidShape(self):
for dtype in dtypes.int32, dtypes.int64:
with self.assertRaisesRegex(
ValueError, "minval must be a scalar; got a tensor of shape"):
random_ops.random_uniform(
[1000], minval=[1, 2], maxval=3, dtype=dtype)
with self.assertRaisesRegex(
ValueError, "maxval must be a scalar; got a tensor of shape"):
random_ops.random_uniform(
[1000], minval=1, maxval=[2, 3], dtype=dtype)
# Check that uniform ints actually follow a uniform distribution.
@test_util.run_deprecated_v1
def testUniformInts(self):
minv = -2
maxv = 15
n = 100000
p = 1 / (maxv - minv)
# The counts should follow an (n, p) binomial distribution.
mean = p * n
std = np.sqrt(n * p * (1 - p))
for dt in dtypes.int32, dtypes.int64:
# Use a fixed seed here to make the test deterministic.
# Without the fixed seed, the 5 * std bound will (very rarely) fail.
sampler = self._Sampler(
n // 10, minv=minv, maxv=maxv, dtype=dt, use_gpu=True, seed=17)
x = sampler().ravel()
self.assertEqual(x.shape, (n,))
counts, _ = np.histogram(x, bins=maxv - minv)
self.assertEqual(counts.shape, (maxv - minv,))
self.assertEqual(counts.sum(), n)
error = np.abs(counts - mean)
self.assertLess(error.max(), 5 * std)
# Check that minval = maxval is fine iff we're producing no numbers
def testUniformIntsDegenerate(self):
for dt in dtypes.int32, dtypes.int64:
def sample(n):
return self._Sampler(n, minv=0, maxv=0, dtype=dt, use_gpu=True)()
self.assertEqual(sample(0).shape, (10, 0))
with self.assertRaisesOpError('Need minval < maxval, got 0 >= 0'):
sample(1)
# Checks that the CPU and GPU implementation returns the same results,
# given the same random seed
@test_util.run_deprecated_v1
def testCPUGPUMatch(self):
for dt in get_float_types() + [dtypes.int32, dtypes.int64]:
maxv = 1.0 if dt.is_floating else 17
results = {}
for use_gpu in False, True:
sampler = self._Sampler(
1000000, minv=0, maxv=maxv, dtype=dt, use_gpu=use_gpu, seed=12345)
results[use_gpu] = sampler()
self.assertAllEqual(results[False], results[True])
@test_util.run_deprecated_v1
def testSeed(self):
for dt in get_float_types() + [dtypes.int32, dtypes.int64]:
for seed in [345, 2**100, -2**100]:
sx = self._Sampler(1000, 0, 17, dtype=dt, use_gpu=True, seed=seed)
sy = self._Sampler(1000, 0, 17, dtype=dt, use_gpu=True, seed=seed)
self.assertAllEqual(sx(), sy())
@test_util.run_deprecated_v1
def testNoCSE(self):
shape = [2, 3, 4]
for dtype in dtypes.float16, dtypes.float32, dtypes.int32:
with self.session():
rnd1 = random_ops.random_uniform(shape, 0, 17, dtype=dtype)
rnd2 = random_ops.random_uniform(shape, 0, 17, dtype=dtype)
diff = (rnd2 - rnd1).eval()
self.assertTrue(np.linalg.norm(diff) > 0.1)
@test_util.run_deprecated_v1
def testSingleSessionNotConstant(self):
for use_gpu in [False, True]:
for dt in get_float_types() + [dtypes.int32, dtypes.int64]:
self._testSingleSessionNotConstant(
random_ops.random_uniform, 100, dt, 0, 17, use_gpu=use_gpu)
@test_util.run_deprecated_v1
def testSingleSessionOpSeedNotConstant(self):
for use_gpu in [False, True]:
for dt in get_float_types() + [dtypes.int32, dtypes.int64]:
self._testSingleSessionNotConstant(
random_ops.random_uniform,
100,
dt,
10,
20,
use_gpu=use_gpu,
op_seed=1345)
@test_util.run_deprecated_v1
def testSingleSessionGraphSeedNotConstant(self):
for use_gpu in [False, True]:
for dt in get_float_types() + [dtypes.int32, dtypes.int64]:
self._testSingleSessionNotConstant(
random_ops.random_uniform,
100,
dt,
20,
200,
use_gpu=use_gpu,
graph_seed=965)
class RandomShapeTest(test.TestCase):
@test_util.run_deprecated_v1
def testTruncatedNormal(self):
# Fully known shape.
rnd1 = random_ops.truncated_normal([1, 2, 3])
self.assertEqual([1, 2, 3], rnd1.get_shape())
# Partially known shape.
rnd2 = random_ops.truncated_normal(
array_ops.placeholder(dtypes.int32, shape=(3,)))
self.assertEqual([None, None, None], rnd2.get_shape().as_list())
# Unknown shape.
rnd3 = random_ops.truncated_normal(array_ops.placeholder(dtypes.int32))
self.assertIs(None, rnd3.get_shape().ndims)
@test_util.run_deprecated_v1
def testRandomNormal(self):
# Fully known shape.
rnd1 = random_ops.random_normal([1, 2, 3])
self.assertEqual([1, 2, 3], rnd1.get_shape())
# Partially known shape.
rnd2 = random_ops.random_normal(
array_ops.placeholder(dtypes.int32, shape=(3,)))
self.assertEqual([None, None, None], rnd2.get_shape().as_list())
# Unknown shape.
rnd3 = random_ops.random_normal(array_ops.placeholder(dtypes.int32))
self.assertIs(None, rnd3.get_shape().ndims)
@test_util.run_deprecated_v1
def testRandomUniform(self):
# Fully known shape.
rnd1 = random_ops.random_uniform([1, 2, 3])
self.assertEqual([1, 2, 3], rnd1.get_shape())
# Partially known shape.
rnd2 = random_ops.random_uniform(
array_ops.placeholder(dtypes.int32, shape=(3,)))
self.assertEqual([None, None, None], rnd2.get_shape().as_list())
# Unknown shape.
rnd3 = random_ops.random_uniform(array_ops.placeholder(dtypes.int32))
self.assertIs(None, rnd3.get_shape().ndims)
class DeterministicOpsTest(test.TestCase):
def setUp(self):
super().setUp()
random_seed.set_random_seed(None)
config.enable_op_determinism()
def tearDown(self):
super().tearDown()
config.disable_op_determinism()
def testDeterministicOpsErrors(self):
with self.assertRaisesRegex(
RuntimeError,
"Random ops require a seed to be set when determinism is enabled."):
random_ops.random_normal((1,))
with self.assertRaisesRegex(
RuntimeError,
"Random ops require a seed to be set when determinism is enabled."):
random_ops.truncated_normal((1,))
with self.assertRaisesRegex(
RuntimeError,
"Random ops require a seed to be set when determinism is enabled."):
random_ops.random_uniform((1,))
with self.assertRaisesRegex(
errors.InvalidArgumentError,
"When determinism is enabled, random ops must have a seed specified"):
self.evaluate(gen_random_ops.random_standard_normal((1,), dtypes.float32))
def testErrorNotThrownWithSeed(self):
random_ops.random_normal((1,), seed=0)
random_seed.set_random_seed(0)
random_ops.random_normal((1,))
self.evaluate(gen_random_ops.random_standard_normal((1,), dtypes.float32,
seed=1))
self.evaluate(gen_random_ops.random_standard_normal((1,), dtypes.float32,
seed2=1))
if __name__ == "__main__":
test.main()
@@ -0,0 +1,185 @@
# 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 tensorflow.ops.random_ops.random_poisson."""
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.kernel_tests.random import util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import random_ops
from tensorflow.python.platform import test
from tensorflow.python.platform import tf_logging
# All supported dtypes for random_poisson().
_SUPPORTED_DTYPES = (dtypes.float16, dtypes.float32, dtypes.float64,
dtypes.int32, dtypes.int64)
class RandomPoissonTest(test.TestCase):
"""This is a large test due to the moments computation taking some time."""
def _Sampler(self, num, lam, dtype, use_gpu, seed=None):
def func():
with self.session(use_gpu=use_gpu, graph=ops.Graph()) as sess:
rng = random_ops.random_poisson(lam, [num], dtype=dtype, seed=seed)
ret = np.empty([10, num])
for i in range(10):
ret[i, :] = self.evaluate(rng)
return ret
return func
def testMoments(self):
try:
from scipy import stats # pylint: disable=g-import-not-at-top
except ImportError as e:
tf_logging.warn("Cannot test moments: %s", e)
return
# The moments test is a z-value test. This is the largest z-value
# we want to tolerate. Since the z-test approximates a unit normal
# distribution, it should almost definitely never exceed 6.
z_limit = 6.0
for dt in _SUPPORTED_DTYPES:
# Test when lam < 10 and when lam >= 10
for stride in 0, 4, 10:
for lam in (3., 20):
max_moment = 5
sampler = self._Sampler(10000, lam, dt, use_gpu=False, seed=12345)
z_scores = util.test_moment_matching(
sampler(),
max_moment,
stats.poisson(lam),
stride=stride,
)
self.assertAllLess(z_scores, z_limit)
# Checks that the CPU and GPU implementation returns the same results,
# given the same random seed
@test_util.run_deprecated_v1
def testCPUGPUMatch(self):
for dt in _SUPPORTED_DTYPES:
results = {}
for use_gpu in [False, True]:
sampler = self._Sampler(1000, 1.0, dt, use_gpu=use_gpu, seed=12345)
results[use_gpu] = sampler()
if dt == dtypes.float16:
self.assertAllClose(results[False], results[True], rtol=1e-3, atol=1e-3)
else:
self.assertAllClose(results[False], results[True], rtol=1e-6, atol=1e-6)
@test_util.run_deprecated_v1
def testSeed(self):
for dt in dtypes.float16, dtypes.float32, dtypes.float64:
sx = self._Sampler(1000, 1.0, dt, use_gpu=True, seed=345)
sy = self._Sampler(1000, 1.0, dt, use_gpu=True, seed=345)
self.assertAllEqual(sx(), sy())
@test_util.run_deprecated_v1
def testNoCSE(self):
"""CSE = constant subexpression eliminator.
SetIsStateful() should prevent two identical random ops from getting
merged.
"""
for dtype in dtypes.float16, dtypes.float32, dtypes.float64:
with self.cached_session():
rnd1 = random_ops.random_poisson(2.0, [24], dtype=dtype)
rnd2 = random_ops.random_poisson(2.0, [24], dtype=dtype)
diff = rnd2 - rnd1
# Since these are all positive integers, the norm will
# be at least 1 if they are different.
self.assertGreaterEqual(np.linalg.norm(diff.eval()), 1)
def testZeroShape(self):
with self.cached_session():
rnd = random_ops.random_poisson([], [], seed=12345)
self.assertEqual([0], rnd.get_shape().as_list())
self.assertAllClose(np.array([], dtype=np.float32), self.evaluate(rnd))
@test_util.run_deprecated_v1
def testShape(self):
# Fully known shape
rnd = random_ops.random_poisson(2.0, [150], seed=12345)
self.assertEqual([150], rnd.get_shape().as_list())
rnd = random_ops.random_poisson(
lam=array_ops.ones([1, 2, 3]),
shape=[150],
seed=12345)
self.assertEqual([150, 1, 2, 3], rnd.get_shape().as_list())
rnd = random_ops.random_poisson(
lam=array_ops.ones([1, 2, 3]),
shape=[20, 30],
seed=12345)
self.assertEqual([20, 30, 1, 2, 3], rnd.get_shape().as_list())
rnd = random_ops.random_poisson(
lam=array_ops.placeholder(dtypes.float32, shape=(2,)),
shape=[12],
seed=12345)
self.assertEqual([12, 2], rnd.get_shape().as_list())
# Partially known shape.
rnd = random_ops.random_poisson(
lam=array_ops.ones([7, 3]),
shape=array_ops.placeholder(dtypes.int32, shape=(1,)),
seed=12345)
self.assertEqual([None, 7, 3], rnd.get_shape().as_list())
rnd = random_ops.random_poisson(
lam=array_ops.ones([9, 6]),
shape=array_ops.placeholder(dtypes.int32, shape=(3,)),
seed=12345)
self.assertEqual([None, None, None, 9, 6], rnd.get_shape().as_list())
# Unknown shape.
rnd = random_ops.random_poisson(
lam=array_ops.placeholder(dtypes.float32),
shape=array_ops.placeholder(dtypes.int32),
seed=12345)
self.assertIs(None, rnd.get_shape().ndims)
rnd = random_ops.random_poisson(
lam=array_ops.placeholder(dtypes.float32),
shape=[50],
seed=12345)
self.assertIs(None, rnd.get_shape().ndims)
@test_util.run_deprecated_v1
def testDTypeCombinationsV2(self):
"""Tests random_poisson_v2() for all supported dtype combinations."""
with self.cached_session():
for lam_dt in _SUPPORTED_DTYPES:
for out_dt in _SUPPORTED_DTYPES:
random_ops.random_poisson(
constant_op.constant([1], dtype=lam_dt), [10],
dtype=out_dt).eval()
@test_util.run_deprecated_v1
def testInfRate(self):
sample = random_ops.random_poisson(shape=[2], lam=np.inf)
self.assertAllEqual([np.inf, np.inf], self.evaluate(sample))
def testSizeTooLarge(self):
with self.assertRaisesRegex((ValueError, errors.InvalidArgumentError),
"overflow"):
rate = constant_op.constant(1.0, shape=(4, 4, 4, 4, 4))
self.evaluate(
random_ops.random_poisson(
shape=[46902, 51188, 34063, 59195], lam=rate))
if __name__ == "__main__":
test.main()
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,773 @@
# Copyright 2019 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 stateful_random_ops.py."""
import os
import re
from absl.testing import parameterized
import numpy as np
from tensorflow.python.checkpoint import checkpoint as tracking_util
from tensorflow.python.eager import context
from tensorflow.python.eager import def_function
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
from tensorflow.python.framework import ops
from tensorflow.python.framework import test_util
from tensorflow.python.kernel_tests.random import util as random_test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import gen_random_ops
from tensorflow.python.ops import gen_stateful_random_ops
from tensorflow.python.ops import logging_ops
from tensorflow.python.ops import random_ops_util
from tensorflow.python.ops import stateful_random_ops as random
from tensorflow.python.ops import variables
from tensorflow.python.platform import test
g_seeded = None
g_unseeded = None
FLOATS = [dtypes.bfloat16, dtypes.float16, dtypes.float32, dtypes.float64]
INTS = [dtypes.int32, dtypes.int64]
class StatefulRandomOpsTest(test.TestCase, parameterized.TestCase):
def setUp(self):
super(StatefulRandomOpsTest, self).setUp()
physical_devices = config.list_physical_devices("CPU")
config.set_logical_device_configuration(
physical_devices[0], [
context.LogicalDeviceConfiguration(),
context.LogicalDeviceConfiguration()
])
def testCreateRNGStateIntSeed(self):
"""Tests `create_rng_state` when `seed` is int."""
# using leading 'F' to test overflow tolerance
state = random.create_rng_state(0xFFFF222233334444FFAA666677778888,
random.RNG_ALG_PHILOX)
self.assertAllEqual(
list(map(random._uint_to_int,
[0xFFAA666677778888, 0xFFFF222233334444] +
[0] * (random.PHILOX_STATE_SIZE - 2))),
state)
def assertAllDifferent(self, tensors):
"""Checks that there are no duplicate elements anywhere among the tensors.
Args:
tensors: a list of tensors. They can have different shapes.
"""
tensors = [array_ops.reshape(t, shape=[-1]) for t in tensors]
ls = array_ops.concat(tensors, axis=0).numpy().tolist()
self.assertAllEqual(len(ls), len(set(ls)))
@test_util.run_v2_only
def testNonDeterministicInts(self):
"""Tests that non_deterministic_ints returns different results every time.
This test is flaky, but with very low probability of failing.
"""
shape = [2, 3]
dtype = dtypes.int64
a = random.non_deterministic_ints(shape=shape, dtype=dtype)
self.assertAllEqual(shape, a.shape)
self.assertEqual(dtype, a.dtype)
b = random.non_deterministic_ints(shape, dtype=dtype)
self.assertAllDifferent([a, b])
def assertRegex(self, pattern, text):
self.assertTrue(
re.search(pattern, text),
"Can't find pattern '%s' in text '%s'" % (pattern, text))
@parameterized.parameters(list(random_ops_util.Algorithm))
@test_util.run_v2_only
def testBatchSeeds(self, alg):
"""Test for batch seeds."""
shape = [2, 3]
count = 6
gen = random.Generator.from_seed(1234, alg=alg)
if alg == random_ops_util.Algorithm.THREEFRY:
# We don't have CPU/GPU kernels for ThreeFry yet.
return
keys1 = gen._make_int64_keys(shape=shape)
keys2 = gen._make_int64_keys(shape=shape)
self.assertAllDifferent([keys1, keys2])
seeds1 = gen.make_seeds(count=count)
seeds2 = gen.make_seeds(count=count)
self.assertAllDifferent([seeds1[0, :], seeds2[0, :]])
gens = gen.split(count=count)
self.assertAllEqual(count, len(gens))
randoms = [g.uniform_full_int(shape=shape, dtype=dtypes.int32)
for g in gens]
self.assertAllDifferent(randoms)
# Tests graph mode.
@def_function.function
def f():
return gen.make_seeds(count=count)
for _ in range(3):
f()
@parameterized.parameters(list(random_ops_util.Algorithm))
@test_util.run_v2_only
@test_util.run_cuda_only
def testCrossDeviceSplit(self, alg):
"""Tests that a CPU RNG can split into RNGs on GPU."""
with ops.device("/device:CPU:0"):
gen = random.Generator.from_seed(1234, alg=alg) # gen is on CPU
self.assertRegex("CPU", gen.state.device)
if alg == random_ops_util.Algorithm.THREEFRY:
# We don't have CPU/GPU kernels for ThreeFry yet.
return
with ops.device(test_util.gpu_device_name()):
gens = gen.split(count=10) # gens are on GPU
self.assertRegex("GPU", gens[0].state.device)
@parameterized.parameters(list(random_ops_util.Algorithm))
@test_util.run_v2_only
def testSplitInFunction(self, alg):
g = random.Generator.from_seed(1, alg=alg)
if alg == random_ops_util.Algorithm.THREEFRY:
# We don't have CPU/GPU kernels for ThreeFry yet.
return
new_g = [None] # using list as mutable cells
@def_function.function
def f():
if new_g[0] is None: # avoid creating variable in 2nd trace
new_g[0] = g.split(2)
return [new_g[0][i].normal([]) for i in range(2)]
f()
def testFnVars(self):
"""Tests that RNG variable is added to ConcreteFunction.variables."""
rng = random.Generator.from_seed(0)
@def_function.function
def f():
return rng.normal([])
concrete = f.get_concrete_function()
self.assertIn(rng.state, concrete.variables)
@test_util.run_v2_only
def testReset(self):
shape = [2, 3]
gen = random.Generator.from_seed(0)
for resetter in [
lambda g: g.reset(state=[1, 2, 3]),
lambda g: g.reset_from_seed(1234),
lambda g: g.reset_from_key_counter(key=1, counter=[2, 3]),
]:
resetter(gen)
expected_normal = gen.normal(shape)
@def_function.function
def f(resetter):
resetter(gen)
return gen.normal(shape)
def check_results(expected_normal, v):
self.assertAllEqual(expected_normal, v)
check_results(expected_normal, f(resetter))
check_results(expected_normal, f(resetter))
@test_util.run_v2_only
def testGeneratorCreation(self):
"""Tests generator creation, in both eager and tf.function.
The interaction between Generator creation and defun should be the same as
tf.Variable.
"""
shape = [2, 3]
alg = random.RNG_ALG_PHILOX
for constructor in [
lambda: random.Generator(state=[1, 2, 3], alg=alg),
lambda: random.Generator.from_seed(1234),
lambda: random.Generator.from_key_counter( # pylint: disable=g-long-lambda
key=1, counter=[2, 3], alg=alg),
]:
gen = constructor()
# Tests tf.function
expected_normal1 = gen.normal(shape)
expected_normal2 = gen.normal(shape)
global g_seeded
g_seeded = None
@def_function.function
def f(constructor):
global g_seeded
# defun'ed function should only create variables once
if g_seeded is None:
g_seeded = constructor()
return g_seeded.normal(shape)
def check_results(expected_normal, v):
self.assertAllEqual(expected_normal, v)
check_results(expected_normal1, f(constructor))
check_results(expected_normal2, f(constructor))
@test_util.run_v2_only
def testCreateGeneratorFromSymbolic(self):
g = [None, None, None] # using list as mutable cells
@def_function.function
def f(scalar, vector2, vector3):
if g[0] is None: # avoid creating variable in 2nd trace
g[0] = random.Generator.from_seed(scalar)
g[0].reset_from_seed(scalar) # also test reset
g[1] = random.Generator.from_state(vector3, random.RNG_ALG_PHILOX)
g[1].reset(vector3)
g[2] = random.Generator.from_key_counter(
scalar, vector2, random.RNG_ALG_PHILOX)
g[2].reset_from_key_counter(scalar, vector2)
return [g[i].normal([]) for i in range(3)]
args = (1, [2, 2], [3, 3, 3])
args = [constant_op.constant(v) for v in args]
f(*args)
@parameterized.parameters([
(
"philox",
random_ops_util.Algorithm.PHILOX.value,
random_ops_util.Algorithm.PHILOX,
),
(
"threefry",
random_ops_util.Algorithm.THREEFRY.value,
random_ops_util.Algorithm.THREEFRY,
),
(
"auto_select",
random_ops_util.Algorithm.AUTO_SELECT.value,
random_ops_util.Algorithm.AUTO_SELECT,
),
])
@test_util.run_v2_only
def testAlg(self, name, int_id, enum_id):
g_by_name = random.Generator.from_seed(1234, name)
g_by_int = random.Generator.from_seed(1234, int_id)
g_by_enum = random.Generator.from_seed(1234, enum_id)
self.assertEqual(g_by_name.algorithm, g_by_int.algorithm)
self.assertEqual(g_by_name.algorithm, g_by_enum.algorithm)
if enum_id == random_ops_util.Algorithm.THREEFRY:
# We don't have CPU/GPU kernels for ThreeFry yet.
return
shape = [3]
output_by_name = g_by_name.normal(shape)
output_by_int = g_by_int.normal(shape)
output_by_enum = g_by_enum.normal(shape)
self.assertAllEqual(output_by_name, output_by_int)
self.assertAllEqual(output_by_name, output_by_enum)
@test_util.run_v2_only
def testGeneratorCreationWithVar(self):
"""Tests creating generator with a variable.
"""
alg = random.RNG_ALG_PHILOX
state = [1, 2, 3]
var = variables.Variable(state, dtype=random.STATE_TYPE)
g = random.Generator(state=state, alg=alg)
g_var = random.Generator(state=var, alg=alg)
shape = [2, 3]
g.normal(shape)
g_var.normal(shape)
self.assertAllEqual(g.state.read_value(), var.read_value())
@test_util.run_v2_only
def testGeneratorCreationUnseeded(self):
"""Tests generator creation, the unseeded case."""
shape = [2, 3]
global g_unseeded
g_unseeded = None
@def_function.function
def f():
global g_unseeded
# defun'ed function should only create variables once
if g_unseeded is None:
g_unseeded = random.Generator.from_non_deterministic_state()
return g_unseeded.normal(shape)
self.assertAllEqual(shape, f().shape)
@test_util.run_v2_only
def testGeneratorCopy(self):
"""Tests copying a generator."""
g = random.Generator.from_seed(0)
g_copy = random.Generator(g)
self.assertAllEqual(g.algorithm, g_copy.algorithm)
self.assertAllEqual(g.state.read_value(), g_copy.state.read_value())
# Tests tf.function
global g_seeded
g_seeded = None
# Do the same in tf.function
@def_function.function
def f():
global g_seeded
# defun'ed function should only create variables once
if g_seeded is None:
g_seeded = random.Generator(g)
self.assertAllEqual(g.algorithm, g_seeded.algorithm)
self.assertAllEqual(g.state.read_value(), g_seeded.state.read_value())
f()
@test_util.run_v1_only(
("This test is specifically for checking TF1 compatibility. "
"It cannot run under TF2."))
def testTF1(self):
seed = 1234
shape = [2, 3]
expected_normal1 = constant_op.constant(
[[0.9356609, 1.0854305, -0.93788373],
[-0.50615472, 1.31697023, 0.71375787]], dtype=dtypes.float32)
expected_normal2 = constant_op.constant(
[[-0.3964749, 0.8369565, -0.30946946],
[1.1206646, 1.00852597, -0.10185789]], dtype=dtypes.float32)
with self.cached_session() as sess:
gen1 = random.Generator.from_seed(seed)
gen2 = random.Generator.from_non_deterministic_state()
sess.run((gen1.state.initializer, gen2.state.initializer))
r1 = gen1.normal(shape, dtype=dtypes.float32)
r2 = gen2.normal(shape, dtype=dtypes.float32)
def f():
return sess.run((r1, r2))
def check_results(expected_normal, v1, v2):
self.assertAllClose(expected_normal, v1, rtol=1e-5, atol=1e-5)
self.assertAllEqual(shape, v2.shape)
check_results(expected_normal1, *f())
check_results(expected_normal2, *f())
@test_util.run_v2_only
@test_util.also_run_as_tf_function
def testEagerAndDefun(self):
"""A simple test to make sure the op works in eager and defunned mode."""
random.get_global_generator().normal((3,))
@test_util.run_v2_only
def testOpSeedSelectionAfterSetSeed(self):
"""Tests that op-seed selection is reset after reseting global generator.
Fixing GitHub issue 9171:
https://github.com/tensorflow/tensorflow/issues/9171
"""
shape = (3,)
random.get_global_generator().reset_from_seed(1)
a = random.get_global_generator().normal(shape)
random.get_global_generator().reset_from_seed(1)
b = random.get_global_generator().normal(shape)
self.assertAllEqual(a, b)
# Now do the above again using accelerated ('defun'ed) computation
@def_function.function
def f():
return random.get_global_generator().normal(shape)
random.get_global_generator().reset_from_seed(1)
c = f()
random.get_global_generator().reset_from_seed(1)
d = f()
self.assertAllEqual(c, d)
self.assertAllEqual(a, c)
@test_util.run_v2_only
def testOpSeedSelectionNotSensitive(self):
"""Test that op-seed selection is not sensitive to trivial changes.
Test that op-seed selection is not sensitive to trivial computation
(i.e. graph) changes.
Fixing b/32087099
"""
def f(include_print):
shape = constant_op.constant([5])
if include_print:
shape = logging_ops.Print(shape, [shape])
return random.get_global_generator().normal(shape)
def compare(fst_includes_print, snd_includes_print):
random.get_global_generator().reset_from_seed(50)
fst = f(fst_includes_print)
random.get_global_generator().reset_from_seed(50)
snd = f(snd_includes_print)
self.assertAllEqual(fst, snd)
# Now do the above again using accelerated (defunned) 'f'.
# Running 'f' with two different Boolean arguments should cause
# two different graphs to be generated, hence demonstrating the
# insensitivity to graph changes.
f_acc = def_function.function(f)
random.get_global_generator().reset_from_seed(50)
fst = f_acc(fst_includes_print)
random.get_global_generator().reset_from_seed(50)
snd = f_acc(snd_includes_print)
self.assertAllEqual(fst, snd)
compare(False, False)
compare(True, True)
compare(True, False)
@parameterized.parameters(list(random_ops_util.Algorithm))
@test_util.run_v2_only
def testKey(self, alg):
key = 1234
gen = random.Generator(
state=[0] * random._get_counter_size(alg.value) + [key], alg=alg)
got = gen.key
self.assertAllEqual(key, got)
@def_function.function
def f():
return gen.key
got = f()
self.assertAllEqual(key, got)
@test_util.run_v2_only
def testSkip(self):
key = 1234
counter = 5678
gen = random.Generator(state=[counter, 0, key], alg=random.RNG_ALG_PHILOX)
delta = 432
gen.skip(delta)
new_counter = gen.state[0]
self.assertAllEqual(counter + delta * 256, new_counter)
def _sameAsOldRandomOps(self, device, floats):
def compare(dtype, old, new):
seed1, seed2 = 79, 25
# note how the two seeds for the old op correspond to the seed for the new
# op
with ops.device(device):
gen = random.Generator(state=[0, seed2, seed1],
alg=random.RNG_ALG_PHILOX)
# create a graph for the old op in order to call it many times
@def_function.function
def run_old():
with ops.device(device):
return old(dtype, seed1, seed2)
def run_new():
with ops.device(device):
return new(dtype, gen)
for _ in range(5):
self.assertAllEqual(run_old(), run_new())
shape = constant_op.constant([4, 7])
minval = 128
maxval = 256
# passing `dtype` around to compress go/gpylint-faq#cell-var-from-loop and
# go/gpylint-faq#undefined-loop-variable
def old_normal(dtype, seed1, seed2):
return gen_random_ops.random_standard_normal(
shape, dtype=dtype, seed=seed1, seed2=seed2)
def new_normal(dtype, gen):
return gen._standard_normal(shape, dtype=dtype)
def old_truncated_normal(dtype, seed1, seed2):
return gen_random_ops.truncated_normal(
shape, dtype=dtype, seed=seed1, seed2=seed2)
def new_truncated_normal(dtype, gen):
return gen._truncated_normal(shape, dtype=dtype)
def old_uniform_int(dtype, seed1, seed2):
minval2 = constant_op.constant(minval, dtype=dtype)
maxval2 = constant_op.constant(maxval, dtype=dtype)
return gen_random_ops.random_uniform_int(
shape, minval=minval2, maxval=maxval2, seed=seed1, seed2=seed2)
def new_uniform_int(dtype, gen):
return gen.uniform(shape, minval=minval, maxval=maxval, dtype=dtype)
def old_uniform(dtype, seed1, seed2):
return gen_random_ops.random_uniform(
shape, dtype=dtype, seed=seed1, seed2=seed2)
def new_uniform(dtype, gen):
return gen._uniform(shape, dtype=dtype)
for dtype in floats:
compare(dtype, old_normal, new_normal)
compare(dtype, old_truncated_normal, new_truncated_normal)
compare(dtype, old_uniform, new_uniform)
for dtype in INTS:
compare(dtype, old_uniform_int, new_uniform_int)
@test_util.run_v2_only
def testSameAsOldRandomOpsCPU(self):
"""Tests that the generated numbers are the same as the old random_ops.py.
The CPU version.
"""
self._sameAsOldRandomOps("/device:CPU:0", FLOATS)
@test_util.run_v2_only
@test_util.run_cuda_only
def testSameAsOldRandomOpsGPU(self):
"""Tests that the generated numbers are the same as the old random_ops.py.
The GPU version.
"""
floats = [dtypes.float16, dtypes.float32, dtypes.float64]
if test_util.is_gpu_available(
cuda_only=True, min_cuda_compute_capability=(8, 0)):
floats += [dtypes.bfloat16]
self._sameAsOldRandomOps(test_util.gpu_device_name(), floats)
@parameterized.parameters(INTS + [dtypes.uint32, dtypes.uint64])
@test_util.run_v2_only
@test_util.run_cuda_only
def testGPUEqualsCPU(self, dtype):
"""Tests that GPU and CPU generate the same integer outputs."""
seed = 1234
shape = [315, 49]
with ops.device("/device:CPU:0"):
cpu = random.Generator.from_seed(seed).uniform_full_int(
shape=shape, dtype=dtype)
with ops.device(test_util.gpu_device_name()):
gpu = random.Generator.from_seed(seed).uniform_full_int(
shape=shape, dtype=dtype)
self.assertAllEqual(cpu, gpu)
@parameterized.parameters(FLOATS + INTS)
@test_util.run_v2_only
def testUniformIsInRange(self, dtype):
if dtype == dtypes.bfloat16 and not test_util.is_gpu_available(
cuda_only=True, min_cuda_compute_capability=(8, 0)):
self.skipTest("Bfloat16 requires compute capability 8.0")
minval = 2
maxval = 33
size = 1000
gen = random.Generator.from_seed(1234)
x = gen.uniform(
shape=[size], dtype=dtype, minval=minval, maxval=maxval).numpy()
self.assertTrue(np.all(x >= minval))
self.assertTrue(np.all(x < maxval))
@parameterized.parameters(FLOATS)
@test_util.run_v2_only
def testNormalIsFinite(self, dtype):
if dtype == dtypes.bfloat16 and not test_util.is_gpu_available(
cuda_only=True, min_cuda_compute_capability=(8, 0)):
self.skipTest("Bfloat16 requires compute capability 8.0")
gen = random.Generator.from_seed(1234)
x = gen.normal(shape=[10000], dtype=dtype).numpy()
self.assertTrue(np.all(np.isfinite(x)))
@parameterized.parameters(FLOATS + INTS)
@test_util.run_v2_only
def testDistributionOfUniform(self, dtype):
"""Use Pearson's Chi-squared test to test for uniformity."""
if dtype == dtypes.bfloat16 and not test_util.is_gpu_available(
cuda_only=True, min_cuda_compute_capability=(8, 0)):
self.skipTest("Bfloat16 requires compute capability 8.0")
n = 1000
seed = 123
gen = random.Generator.from_seed(seed)
maxval = 1
if dtype.is_integer:
maxval = 100
x = gen.uniform(shape=[n], maxval=maxval, dtype=dtype).numpy()
if maxval > 1:
# Normalize y to range [0, 1).
x = x.astype(float) / maxval
# Tests that the values are distributed amongst 10 bins with equal
# probability. 16.92 is the Chi^2 value for 9 degrees of freedom with
# p=0.05. This test is probabilistic and would be flaky if the random
# seed were not fixed.
val = random_test_util.chi_squared(x, 10)
self.assertLess(val, 16.92)
@parameterized.parameters(FLOATS)
@test_util.run_v2_only
def testDistributionOfNormal(self, dtype):
"""Use Anderson-Darling test to test distribution appears normal."""
if dtype == dtypes.bfloat16 and not test_util.is_gpu_available(
cuda_only=True, min_cuda_compute_capability=(8, 0)):
self.skipTest("Bfloat16 requires compute capability 8.0")
n = 1000
gen = random.Generator.from_seed(1234)
x = gen.normal(shape=[n], dtype=dtype).numpy()
# The constant 2.492 is the 5% critical value for the Anderson-Darling
# test where the mean and variance are known. This test is probabilistic
# so to avoid flakiness the seed is fixed.
self.assertLess(
random_test_util.anderson_darling(x.astype(float)), 2.492)
@test_util.run_v2_only
def testErrors(self):
"""Tests that proper errors are raised.
"""
shape = [2, 3]
gen = random.Generator.from_seed(1234)
with self.assertRaisesWithPredicateMatch(
errors.InvalidArgumentError,
r"must have shape \[\], not"):
gen_stateful_random_ops.stateful_standard_normal_v2(
gen.state.handle, [0, 0], shape)
with self.assertRaisesWithPredicateMatch(
errors.InvalidArgumentError,
r"must have shape \[\], not"):
gen_stateful_random_ops.rng_skip(
gen.state.handle, gen.algorithm, [0, 0])
with self.assertRaisesWithPredicateMatch(
TypeError, "EagerTensor of dtype int64"):
gen_stateful_random_ops.stateful_standard_normal_v2(
gen.state.handle, 1.1, shape)
with self.assertRaisesWithPredicateMatch(
errors.InvalidArgumentError,
"Unsupported algorithm id"):
gen_stateful_random_ops.stateful_standard_normal_v2(
gen.state.handle, 123, shape)
with self.assertRaisesWithPredicateMatch(errors.InvalidArgumentError,
"Unsupported algorithm id"):
gen_stateful_random_ops.rng_read_and_skip(
gen.state.handle, alg=123, delta=10)
var = variables.Variable([0, 0], dtype=dtypes.int32)
with self.assertRaisesWithPredicateMatch(
errors.InvalidArgumentError,
"dtype of RNG state variable must be int64, not"):
gen_stateful_random_ops.stateful_standard_normal_v2(
var.handle, random.RNG_ALG_PHILOX, shape)
var = variables.Variable([[0]], dtype=dtypes.int64)
with self.assertRaisesWithPredicateMatch(
errors.InvalidArgumentError,
"RNG state must have one and only one dimension, not"):
gen_stateful_random_ops.stateful_standard_normal_v2(
var.handle, random.RNG_ALG_PHILOX, shape)
var = variables.Variable([0], dtype=dtypes.int64)
with self.assertRaisesWithPredicateMatch(
errors.InvalidArgumentError,
"For the Philox algorithm, the size of state must be at least"):
gen_stateful_random_ops.stateful_standard_normal_v2(
var.handle, random.RNG_ALG_PHILOX, shape)
with self.assertRaisesWithPredicateMatch(
ValueError,
"minval must be a scalar; got a tensor of shape "):
@def_function.function
def f():
gen.uniform(shape=shape, minval=array_ops.zeros(shape, "int32"),
maxval=100, dtype="int32")
f()
with self.assertRaisesWithPredicateMatch(
ValueError,
"maxval must be a scalar; got a tensor of shape "):
@def_function.function
def f2():
gen.uniform(
shape=shape, minval=0, maxval=array_ops.ones(shape, "int32") * 100,
dtype="int32")
f2()
@test_util.run_v2_only
def testGetGlobalGeneratorWithXla(self):
"""Demonstrates using the global generator with XLA."""
# This test was passing before because soft placement silently picked the
# CPU kernel.
# TODO(wangpeng): Remove this skip
self.skipTest("NonDeterministicInts lacks XLA kernel.")
if not config.list_physical_devices("XLA_CPU"):
self.skipTest("No XLA_CPU device available.")
random.set_global_generator(None)
@def_function.function(jit_compile=True)
def make_seed():
generator = random.get_global_generator()
state = array_ops.identity(generator.state, name="state")
return generator.uniform_full_int((2,), dtypes.int32, name="seed"), state
with ops.device("/device:XLA_CPU:0"):
seed, state = make_seed()
self.assertTrue(np.all(np.isfinite(seed.numpy())))
random.get_global_generator().reset(state)
self.assertAllEqual(make_seed()[0], seed)
@test_util.run_v2_only
def testSetGlobalGeneratorBadWithDefun(self):
"""Demonstrates set_global_generator does not affect compiled tf.function."""
shape = (3,)
@def_function.function
def f():
return random.get_global_generator().normal(shape)
random.set_global_generator(random.Generator.from_seed(50))
samples = f()
# Resetting global generator has no effect to the compiled tf.function.
random.set_global_generator(random.Generator.from_seed(50))
# New samples are returned.
self.assertNotAllEqual(samples, f())
@test_util.run_v2_only
def testFunctionArg(self):
"""Tests that RNG can be used as tf.function's argument.
"""
shape = [2, 3]
@def_function.function
def f(gen):
return gen.normal(shape)
g1 = random.Generator.from_seed(1)
g2 = random.Generator.from_seed(1)
res1 = f(g1)
res2 = g2.normal(shape)
self.assertAllEqual(res1, res2)
self.assertAllEqual(g1.state.read_value(), g2.state.read_value())
@test_util.run_v2_only
def testUniformFullInt(self):
"""Tests full-range int uniform.
"""
shape = [3, 4]
dtype = dtypes.int32
g = random.Generator.from_seed(1)
r1 = g.uniform(shape=shape, dtype=dtype, minval=None)
g = random.Generator.from_seed(1)
r2 = g.uniform_full_int(shape=shape, dtype=dtype)
self.assertAllEqual(r1, r2)
@test_util.run_v2_only
def testRestore(self):
"""Tests save and restore.
"""
fname = os.path.join(self.get_temp_dir(), "checkpoint")
g = random.Generator.from_seed(1)
cp = tracking_util.Checkpoint(g=g)
def write_restore_compare():
cp.write(fname)
r1 = g.uniform([], dtype=dtypes.uint32, minval=None)
cp.restore(fname)
r2 = g.uniform([], dtype=dtypes.uint32, minval=None)
self.assertAllEqual(r1, r2)
# Run multiple times so that cp.write is called in various RNG states
for _ in range(2):
write_restore_compare()
@test_util.run_v2_only
def testDeterministicOpsErrors(self):
try:
config.enable_op_determinism()
random.set_global_generator(None)
with self.assertRaisesWithPredicateMatch(
RuntimeError,
'"get_global_generator" cannot be called if determinism is enabled'):
random.get_global_generator()
random.set_global_generator(random.Generator.from_seed(50))
random.get_global_generator()
with self.assertRaisesWithPredicateMatch(
RuntimeError,
'"from_non_deterministic_state" cannot be called when determinism '
"is enabled."):
random.Generator.from_non_deterministic_state()
finally:
config.disable_op_determinism()
if __name__ == "__main__":
config.set_soft_device_placement(False)
test.main()
@@ -0,0 +1,576 @@
# Copyright 2018 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 stateless random ops."""
import functools
from absl.testing import parameterized
import numpy as np
from tensorflow.python.compat import compat
from tensorflow.python.eager import context
from tensorflow.python.eager import def_function
from tensorflow.python.framework import config
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import ops
from tensorflow.python.framework import random_seed
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_stateless_random_ops_v2
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import random_ops
from tensorflow.python.ops import stateless_random_ops as stateless
from tensorflow.python.platform import test
# Note that in theory each test will reset the eager context and may choose to
# hide some devices, so we shouldn't cache this transient info. Tests in this
# file don't make those config changes, so caching is fine. It provides a good
# speed-up.
_cached_device = None
def get_device():
global _cached_device
if _cached_device is not None:
return _cached_device
# Precedence from high to low
for device_type in ('XLA_GPU', 'GPU', 'XLA_CPU', 'CPU'):
devices = config.list_logical_devices(device_type)
if devices:
_cached_device = devices[0]
return _cached_device
raise ValueError('Cannot find any suitable device. Available devices: %s' %
config.list_logical_devices())
BEFORE_EXPIRE = (2020, 10, 24)
AFTER_EXPIRE = (2020, 10, 26)
def invert_philox(key, value):
"""Invert the Philox bijection."""
key = np.array(key, dtype=np.uint64)
value = np.array(value, dtype=np.uint64)
step = np.array([0x9E3779B9, 0xBB67AE85], dtype=np.uint64)
for n in range(10)[::-1]:
key0, key1 = key + n * step
mask = np.uint64(0xffffffff)
v0_mul = np.uint64(0x991a7cdb)
v2_mul = np.uint64(0x6d7cae67)
v0 = value[3] * v0_mul & mask
v2 = value[1] * v2_mul & mask
hi0_mul = np.uint64(0xD2511F53)
hi1_mul = np.uint64(0xCD9E8D57)
shift_amount = np.uint64(32)
hi0 = v0 * hi0_mul >> shift_amount
hi1 = v2 * hi1_mul >> shift_amount
v1 = hi1 ^ value[0] ^ key0
v3 = hi0 ^ value[2] ^ key1
value = v0, v1, v2, v3
return np.array(value, dtype=np.uint64)
SEEDS = ((7, 17), (11, 5), (2, 3))
SEED_TYPES = [dtypes.int32, dtypes.int64]
def float_cases(shape_dtypes=(None,)):
cases = (
# Uniform distribution, with and without range
('uniform', stateless.stateless_random_uniform, random_ops.random_uniform,
{}),
('uniform2', stateless.stateless_random_uniform,
random_ops.random_uniform, dict(minval=2.2, maxval=7.1)),
# Normal distribution, with and without mean+stddev
('normal', stateless.stateless_random_normal, random_ops.random_normal,
{}),
('normal2', stateless.stateless_random_normal, random_ops.random_normal,
dict(mean=2, stddev=3)),
# Truncated normal distribution, with and without mean+stddev
('trnorm', stateless.stateless_truncated_normal,
random_ops.truncated_normal, {}),
('trnorm2', stateless.stateless_truncated_normal,
random_ops.truncated_normal, dict(mean=3, stddev=4)),
)
# Explicitly passing in params because capturing cell variable from loop is
# problematic in Python
def wrap(op: ops.Operation, dtype, shape, shape_dtype, seed, **kwargs):
device_type = get_device().device_type
# Some dtypes are not supported on some devices
if (dtype == dtypes.bfloat16 and device_type == 'GPU' and
not test_util.is_gpu_available(
cuda_only=True, min_cuda_compute_capability=(8, 0))):
dtype = dtypes.float32
shape_ = (constant_op.constant(shape, dtype=shape_dtype)
if shape_dtype is not None else shape)
return op(seed=seed, shape=shape_, dtype=dtype, **kwargs)
def _name(a):
if hasattr(a, 'name'):
return a.name
else:
return a
for dtype in dtypes.float16, dtypes.bfloat16, dtypes.float32, dtypes.float64:
for shape_dtype in shape_dtypes:
for shape in (), (3,), (2, 5):
for name, stateless_op, stateful_op, kwargs in cases:
yield (('%s_%s_%s_%s' %
(name, _name(dtype), shape, _name(shape_dtype))).replace(
' ', ''),
functools.partial(wrap, stateless_op, dtype, shape,
shape_dtype, **kwargs),
functools.partial(wrap, stateful_op, dtype, shape, shape_dtype,
**kwargs))
def int_cases(shape_dtypes=(None,), minval_maxval=None):
def wrap(op: ops.Operation, minval, maxval, shape, shape_dtype, dtype,
seed, **kwargs):
shape_ = (constant_op.constant(shape, dtype=shape_dtype)
if shape_dtype is not None else shape)
return op(
seed=seed, shape=shape_, minval=minval, maxval=maxval, dtype=dtype,
**kwargs)
if minval_maxval is None:
minval_maxval = ((2, 11111),)
for minval, maxval in minval_maxval:
for shape_dtype in shape_dtypes:
for shape in (), (3,), (2, 5):
for dtype in dtypes.int32, dtypes.int64:
yield ('uniform_%s_%s' % (minval, maxval),
functools.partial(wrap, stateless.stateless_random_uniform,
minval, maxval, shape, shape_dtype, dtype),
functools.partial(wrap, random_ops.random_uniform, minval,
maxval, shape, shape_dtype, dtype))
def multinomial_cases():
num_samples = 10
def wrap(op: ops.Operation, logits, logits_dtype, output_dtype, seed):
device_type = get_device().device_type
# Some dtypes are not supported on some devices
if (logits_dtype == dtypes.bfloat16 and device_type == 'GPU' and
not test_util.is_gpu_available(
cuda_only=True, min_cuda_compute_capability=(8, 0))):
logits_dtype = dtypes.float32
return op(seed=seed,
logits=constant_op.constant(logits, dtype=logits_dtype),
num_samples=num_samples, output_dtype=output_dtype)
for logits_dtype in (
np.float16,
dtypes.bfloat16.as_numpy_dtype,
np.float32,
np.float64,
):
for output_dtype in dtypes.int32, dtypes.int64:
for logits in ([[0.1, 0.25, 0.5, 0.15]], [[0.5, 0.5], [0.8, 0.2],
[0.25, 0.75]]):
yield ('multinomial',
functools.partial(wrap, stateless.stateless_multinomial, logits,
logits_dtype, output_dtype),
functools.partial(wrap, random_ops.multinomial, logits,
logits_dtype, output_dtype))
def gamma_cases():
def wrap(op: ops.Operation, alpha, dtype, shape, seed):
return op(seed=seed, shape=shape,
alpha=constant_op.constant(alpha, dtype=dtype), dtype=dtype)
for dtype in np.float16, np.float32, np.float64:
for alpha in ([[.5, 1., 2.]], [[0.5, 0.5], [0.8, 0.2], [0.25, 0.75]]):
yield ('gamma',
functools.partial(wrap, stateless.stateless_random_gamma, alpha,
dtype, (10,) + tuple(np.shape(alpha))),
functools.partial(wrap, random_ops.random_gamma, alpha, dtype,
(10,)))
def poisson_cases():
def wrap(op: ops.Operation, lam, lam_dtype, out_dtype, shape, seed):
return op(seed=seed, shape=shape,
lam=constant_op.constant(lam_dtype(lam), dtype=lam_dtype),
dtype=out_dtype)
for lam_dtype in np.float16, np.float32, np.float64, np.int32, np.int64:
for out_dtype in np.float16, np.float32, np.float64, np.int32, np.int64:
for lam in ([[5.5, 1., 2.]], [[7.5, 10.5], [3.8, 8.2], [1.25, 9.75]]):
yield ('poisson',
functools.partial(wrap, stateless.stateless_random_poisson, lam,
lam_dtype, out_dtype,
(10,) + tuple(np.shape(lam))),
functools.partial(wrap, random_ops.random_poisson, lam,
lam_dtype, out_dtype, (10,)))
def shuffle_cases():
for dtype in np.int32, np.int64, np.float32, np.float64:
# [], [0, ...] and [1, ...] are important corner cases
for shape in ([], [0], [1], [100], [0, 0], [1, 0], [0, 1], [1, 2], [5, 3],
[7, 5, 3, 2]):
value = np.arange(np.prod(shape)).reshape(shape).astype(dtype)
yield ('shuffle',
functools.partial(stateless.stateless_shuffle, value),
functools.partial(random_ops.random_shuffle, value))
@test_util.with_eager_op_as_function
class StatelessOpsTest(test.TestCase, parameterized.TestCase):
def _test_match(self, case, seed):
# Stateless ops should be the same as stateful ops on the first call
# after seed scrambling.
key = 0x3ec8f720, 0x02461e29
preseed = invert_philox(key, (seed[0], 0, seed[1], 0)).astype(np.uint64)
preseed = preseed[::2] | preseed[1::2] << 32
with ops.device(get_device().name):
_, stateless_op, stateful_op = case
random_seed.set_random_seed(seed[0])
stateful = stateful_op(seed=seed[1])
pure = stateless_op(seed=preseed)
self.assertAllEqual(stateful, pure)
def _test_match_stateless_cpu_gpu(self, case, seed):
# Stateless ops should produce the same result on CPUs and GPUs.
_, stateless_op, _ = case
with ops.device('CPU'):
result_cpu = stateless_op(seed=seed)
with ops.device(get_device().name):
result_gpu = stateless_op(seed=seed)
self.assertAllClose(result_cpu, result_gpu)
def _test_old_and_new_stateless_match(self, case, seed):
"""Tests that the new stateless ops match the old stateless ones."""
with ops.device(get_device().name):
_, stateless_op, _ = case
with compat.forward_compatibility_horizon(*BEFORE_EXPIRE):
old = stateless_op(seed=seed)
with compat.forward_compatibility_horizon(*AFTER_EXPIRE):
new = stateless_op(seed=seed)
self.assertAllClose(old, new)
def _test_explicit_alg(self, case, seed):
"""Tests that alg=philox and alg=None are the same (on CPU/GPU)."""
with ops.device(get_device().name):
_, stateless_op, _ = case
implicit_alg = stateless_op(seed=seed)
# All device types allowed in this test will result in Philox
explicit_alg = stateless_op(seed=seed, alg='philox')
self.assertAllClose(implicit_alg, explicit_alg)
def _test_determinism(self, case, seed_type):
# Stateless values should be equal iff the seeds are equal (roughly)
seeds = [(x, y) for x in range(5) for y in range(5)] * 3 # pylint: disable=g-complex-comprehension
with self.test_session(), ops.device(get_device().name):
_, stateless_op, _ = case
if context.executing_eagerly():
values = [
(seed, stateless_op(seed=constant_op.constant(seed, seed_type)))
for seed in seeds]
else:
# Have this branch because the above branch is too slow in graph
# mode
seed_t = array_ops.placeholder(seed_type, shape=[2])
pure = stateless_op(seed=seed_t)
values = [
(seed, pure.eval(feed_dict={seed_t: seed})) for seed in seeds
]
for s0, v0 in values:
for s1, v1 in values:
if dtypes.as_dtype(v0.dtype) != dtypes.bfloat16:
self.assertEqual(s0 == s1, np.all(v0 == v1))
elif s0 == s1:
# Skip the s0 != s1 case because v0 and v1 can be either equal or
# unequal in that case due to bfloat16's low precision
self.assertAllEqual(v0, v1)
@parameterized.named_parameters(
('_%s_%s_%s' % (case[0], case_id, seed_id), case, seed) # pylint: disable=g-complex-comprehension
for seed_id, seed in enumerate(SEEDS)
for case_id, case in enumerate(float_cases()))
@test_util.disable_tfrt('tensorflow::DirectSession::Run crashes. b/156187396')
def testMatchFloat(self, case, seed):
if get_device().device_type in ('XLA_GPU', 'XLA_CPU'):
# This test was passing before because soft placement silently picked the
# CPU kernels.
self.skipTest('Skip on XLA because XLA kernels do not support int64 '
'seeds needed by this test.')
self._test_match(case, seed)
@parameterized.named_parameters(
('_%s_%s_%s' % (case[0], case_id, seed_id), case, seed) # pylint: disable=g-complex-comprehension
for seed_id, seed in enumerate(SEEDS)
for case_id, case in enumerate(int_cases()))
@test_util.disable_tfrt('tensorflow::DirectSession::Run crashes. b/156187396')
def testMatchInt(self, case, seed):
if get_device().device_type in ('XLA_GPU', 'XLA_CPU'):
# This test was passing before because soft placement silently picked the
# CPU kernels.
self.skipTest('Skip on XLA because XLA kernels do not support int64 '
'seeds needed by this test.')
self._test_match(case, seed)
@parameterized.named_parameters(
('_%s_%s_%s' % (case[0], case_id, seed_id), case, seed) # pylint: disable=g-complex-comprehension
for seed_id, seed in enumerate(SEEDS)
for case_id, case in enumerate(multinomial_cases()))
@test_util.disable_tfrt('tensorflow::DirectSession::Run crashes. b/156187396')
def testMatchMultinomial(self, case, seed):
if get_device().device_type in ('XLA_GPU', 'XLA_CPU'):
# This test was passing before because soft placement silently picked the
# CPU kernels.
self.skipTest('Lacking XLA kernel')
self._test_match(case, seed)
@parameterized.named_parameters(
('_%s_%s_%s' % (case[0], case_id, seed_id), case, seed) # pylint: disable=g-complex-comprehension
for seed_id, seed in enumerate(SEEDS)
for case_id, case in enumerate(gamma_cases()))
@test_util.disable_tfrt('tensorflow::DirectSession::Run crashes. b/156187396')
def testMatchGamma(self, case, seed):
if get_device().device_type == 'GPU':
# This test was passing before because soft placement silently picked the
# CPU kernels.
self.skipTest('Lacking GPU kernel')
if get_device().device_type in ('XLA_GPU', 'XLA_CPU'):
# This test was passing before because soft placement silently picked the
# CPU kernels.
self.skipTest('Lacking XLA kernel')
self._test_match(case, seed)
@parameterized.named_parameters(
('_%s_%s_%s' % (case[0], case_id, seed_id), case, seed) # pylint: disable=g-complex-comprehension
for seed_id, seed in enumerate(SEEDS)
for case_id, case in enumerate(gamma_cases()))
@test_util.disable_tfrt('tensorflow::DirectSession::Run crashes. b/156187396')
def testStatelessGammaCpuGpuMatch(self, case, seed):
if get_device().device_type != 'GPU':
# This test compares the numbers produced by the CPU and GPU kernel for
# stateless_random_gamma.
self.skipTest('This test requires GPU')
self._test_match_stateless_cpu_gpu(case, seed)
@parameterized.named_parameters(
('_%s_%s_%s' % (case[0], case_id, seed_id), case, seed) # pylint: disable=g-complex-comprehension
for seed_id, seed in enumerate(SEEDS)
for case_id, case in enumerate(poisson_cases()))
@test_util.disable_tfrt('tensorflow::DirectSession::Run crashes. b/156187396')
def testMatchPoisson(self, case, seed):
if get_device().device_type == 'GPU':
# This test was passing before because soft placement silently picked the
# CPU kernels.
self.skipTest('Lacking GPU kernel')
if get_device().device_type in ('XLA_GPU', 'XLA_CPU'):
# This test was passing before because soft placement silently picked the
# CPU kernels.
self.skipTest('Lacking XLA kernel')
self._test_match(case, seed)
@parameterized.named_parameters(
('_%s_%s_%s' % (case[0], case_id, seed_id), case, seed) # pylint: disable=g-complex-comprehension,undefined-variable
for seed_id, seed in enumerate(SEEDS)
for case_id, case in enumerate(shuffle_cases()))
def testMatchShuffle(self, case, seed):
if get_device().device_type == 'GPU':
self.skipTest('Lacking GPU kernel')
if get_device().device_type in ('XLA_GPU', 'XLA_CPU'):
self.skipTest('Lacking XLA kernel')
self._test_match(case, seed)
@parameterized.named_parameters(
('_%s_%s_%s' % (case[0], case_id, seed_id), case, seed) # pylint: disable=g-complex-comprehension
for seed_id, seed in enumerate(SEEDS)
for case_id, case in enumerate(float_cases()))
@test_util.disable_tfrt('tensorflow::DirectSession::Run crashes. b/156187396')
def testOldAndNewStatelessMatchFloat(self, case, seed):
self._test_old_and_new_stateless_match(case, seed)
@parameterized.named_parameters(
('_%s_%s_%s' % (case[0], case_id, seed_id), case, seed) # pylint: disable=g-complex-comprehension
for seed_id, seed in enumerate(SEEDS)
for case_id, case in enumerate(
int_cases(minval_maxval=((2, 11111), (None, None)))))
@test_util.disable_tfrt('tensorflow::DirectSession::Run crashes. b/156187396')
def testOldAndNewStatelessMatchInt(self, case, seed):
self._test_old_and_new_stateless_match(case, seed)
@parameterized.named_parameters(
('_%s_%s' % (case[0], case_id), case)
for case_id, case in enumerate(float_cases()))
@test_util.disable_tfrt('tensorflow::DirectSession::Run crashes. b/156187396')
def testExplicitAlgFloat(self, case):
seed = (7, 17)
self._test_explicit_alg(case, seed)
@parameterized.named_parameters(
('_%s_%s' % (case[0], case_id), case)
for case_id, case in enumerate(
int_cases(minval_maxval=((2, 11111), (None, None)))))
@test_util.disable_tfrt('tensorflow::DirectSession::Run crashes. b/156187396')
def testExplicitAlgInt(self, case):
seed = (7, 17)
self._test_explicit_alg(case, seed)
@parameterized.named_parameters(
('_%s_%s_%s' % (case[0], seed_type.name, case_id), case, seed_type) # pylint: disable=g-complex-comprehension
for seed_type in SEED_TYPES
for case_id, case in enumerate(
float_cases(shape_dtypes=(dtypes.int32, dtypes.int64))))
@test_util.disable_tfrt('tensorflow::DirectSession::Run crashes. b/156187396')
def testDeterminismFloat(self, case, seed_type):
if seed_type == dtypes.int64 and get_device().device_type in ('XLA_GPU',
'XLA_CPU'):
# This test was passing before because soft placement silently picked the
# CPU kernels.
self.skipTest(
'Skip on XLA because XLA kernels do not support int64 seeds.')
self._test_determinism(case, seed_type)
@parameterized.named_parameters(
('_%s_%s_%s' % (case[0], seed_type.name, case_id), case, seed_type) # pylint: disable=g-complex-comprehension
for seed_type in SEED_TYPES
for case_id, case in enumerate(
int_cases(shape_dtypes=(dtypes.int32, dtypes.int64))))
@test_util.disable_tfrt('tensorflow::DirectSession::Run crashes. b/156187396')
def testDeterminismInt(self, case, seed_type):
if seed_type == dtypes.int64 and get_device().device_type in ('XLA_GPU',
'XLA_CPU'):
# This test was passing before because soft placement silently picked the
# CPU kernels.
self.skipTest(
'Skip on XLA because XLA kernels do not support int64 seeds.')
self._test_determinism(case, seed_type)
@parameterized.named_parameters(
('_%s_%s_%s' % (case[0], seed_type.name, case_id), case, seed_type) # pylint: disable=g-complex-comprehension
for seed_type in SEED_TYPES
for case_id, case in enumerate(multinomial_cases()))
@test_util.disable_tfrt('tensorflow::DirectSession::Run crashes. b/156187396')
def testDeterminismMultinomial(self, case, seed_type):
if get_device().device_type in ('XLA_GPU', 'XLA_CPU'):
# This test was passing before because soft placement silently picked the
# CPU kernels.
self.skipTest('Lacking XLA kernel')
self._test_determinism(case, seed_type)
@parameterized.named_parameters(
('_%s_%s_%s' % (case[0], seed_type.name, case_id), case, seed_type) # pylint: disable=g-complex-comprehension
for seed_type in SEED_TYPES
for case_id, case in enumerate(gamma_cases()))
@test_util.disable_tfrt('tensorflow::DirectSession::Run crashes. b/156187396')
def testDeterminismGamma(self, case, seed_type):
if get_device().device_type in ('XLA_GPU', 'XLA_CPU'):
# This test was passing before because soft placement silently picked the
# CPU kernels.
self.skipTest('Lacking XLA kernel')
self._test_determinism(case, seed_type)
@parameterized.named_parameters(
('_%s_%s_%s' % (case[0], seed_type.name, case_id), case, seed_type) # pylint: disable=g-complex-comprehension
for seed_type in SEED_TYPES
for case_id, case in enumerate(poisson_cases()))
@test_util.disable_tfrt('tensorflow::DirectSession::Run crashes. b/156187396')
def testDeterminismPoisson(self, case, seed_type):
if get_device().device_type == 'GPU':
# This test was passing before because soft placement silently picked the
# CPU kernels.
self.skipTest('Lacking GPU kernel')
if get_device().device_type in ('XLA_GPU', 'XLA_CPU'):
# This test was passing before because soft placement silently picked the
# CPU kernels.
self.skipTest('Lacking XLA kernel')
self._test_determinism(case, seed_type)
@test_util.run_v2_only
def testGetKeyCounterAlg(self):
seed = [1, 2]
key, counter = gen_stateless_random_ops_v2.stateless_random_get_key_counter(
seed)
self.assertAllEqual(key.shape, [1])
self.assertAllEqual(counter.shape, [2])
alg = gen_stateless_random_ops_v2.stateless_random_get_alg()
self.assertAllEqual(alg.shape, [])
def assertDTypeEqual(self, a, b):
self.assertEqual(dtypes.as_dtype(a), dtypes.as_dtype(b))
def assertNoEqualPair(self, ls):
for i in range(len(ls)):
for j in range(i + 1, len(ls)):
self.assertFalse(math_ops.reduce_all(ls[i] == ls[j]))
@parameterized.parameters(['int32', 'int64'])
@test_util.run_v2_only
def testSplit(self, dtype):
"""Test for `split`."""
seed = constant_op.constant([1, 2], dtype=dtype)
new_seed = stateless.split(seed, 3)
self.assertEqual(new_seed.shape, [3, 2])
self.assertDTypeEqual(new_seed.dtype, dtype)
self.assertNoEqualPair([seed] + array_ops_stack.unstack(new_seed))
@parameterized.parameters(['int32', 'int64'])
@test_util.run_v2_only
def testFoldIn(self, dtype):
"""Test for `fold_in`."""
orig_seed = constant_op.constant([1, 2], dtype='int32')
seed = stateless.fold_in(orig_seed, constant_op.constant(3, dtype=dtype))
new_seeds = []
new_seeds.append(seed)
seed = stateless.fold_in(seed, constant_op.constant(4, dtype=dtype))
new_seeds.append(seed)
for s in new_seeds:
self.assertEqual(s.shape, [2])
self.assertDTypeEqual(s.dtype, dtype)
self.assertNoEqualPair([math_ops.cast(orig_seed, dtype)] + new_seeds)
@test_util.run_v2_only
def testErrors(self):
"""Tests that proper errors are raised.
"""
shape = [2, 3]
with self.assertRaisesWithPredicateMatch(
ValueError,
'minval must be a scalar; got a tensor of shape '):
@def_function.function
def f():
stateless.stateless_random_uniform(
shape=shape, seed=[1, 2], minval=array_ops.zeros(shape, 'int32'),
maxval=100, dtype='int32')
f()
with self.assertRaisesWithPredicateMatch(
ValueError,
'maxval must be a scalar; got a tensor of shape '):
@def_function.function
def f2():
stateless.stateless_random_uniform(
shape=shape, seed=[1, 2], minval=0,
maxval=array_ops.ones(shape, 'int32') * 100,
dtype='int32')
f2()
if __name__ == '__main__':
config.set_soft_device_placement(False)
context.context().enable_xla_devices()
test.main()
@@ -0,0 +1,164 @@
# Copyright 2018 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.
# ==============================================================================
"""Utilities for testing random variables."""
import math
import numpy as np
from tensorflow.python.ops.distributions import special_math
def test_moment_matching(
samples,
number_moments,
dist,
stride=0):
"""Return z-test scores for sample moments to match analytic moments.
Given `samples`, check that the first sample `number_moments` match
the given `dist` moments by doing a z-test.
Args:
samples: Samples from target distribution.
number_moments: Python `int` describing how many sample moments to check.
dist: SciPy distribution object that provides analytic moments.
stride: Distance between samples to check for statistical properties.
A stride of 0 means to use all samples, while other strides test for
spatial correlation.
Returns:
Array of z_test scores.
"""
sample_moments = []
expected_moments = []
variance_sample_moments = []
for i in range(1, number_moments + 1):
if len(samples.shape) == 2:
strided_range = samples.flat[::(i - 1) * stride + 1]
else:
strided_range = samples[::(i - 1) * stride + 1, ...]
sample_moments.append(np.mean(strided_range**i, axis=0))
expected_moments.append(dist.moment(i))
variance_sample_moments.append(
(dist.moment(2 * i) - dist.moment(i) ** 2) / len(strided_range))
z_test_scores = []
for i in range(1, number_moments + 1):
# Assume every operation has a small numerical error.
# It takes i multiplications to calculate one i-th moment.
total_variance = (
variance_sample_moments[i - 1] +
i * np.finfo(samples.dtype).eps)
tiny = np.finfo(samples.dtype).tiny
assert np.all(total_variance > 0)
total_variance = np.where(total_variance < tiny, tiny, total_variance)
# z_test is approximately a unit normal distribution.
z_test_scores.append(abs(
(sample_moments[i - 1] - expected_moments[i - 1]) / np.sqrt(
total_variance)))
return z_test_scores
def chi_squared(x, bins):
"""Pearson's Chi-squared test."""
x = np.ravel(x)
n = len(x)
histogram, _ = np.histogram(x, bins=bins, range=(0, 1))
expected = n / float(bins)
return np.sum(np.square(histogram - expected) / expected)
def normal_cdf(x):
"""Cumulative distribution function for a standard normal distribution."""
return 0.5 + 0.5 * np.vectorize(math.erf)(x / math.sqrt(2))
def anderson_darling(x):
"""Anderson-Darling test for a standard normal distribution."""
x = np.sort(np.ravel(x))
n = len(x)
i = np.linspace(1, n, n)
z = np.sum((2 * i - 1) * np.log(normal_cdf(x)) +
(2 * (n - i) + 1) * np.log(1 - normal_cdf(x)))
return -n - z / n
def test_truncated_normal(assert_equal,
assert_all_close,
n,
y,
means=None,
stddevs=None,
minvals=None,
maxvals=None,
mean_atol=5e-4,
median_atol=8e-4,
variance_rtol=1e-3):
"""Tests truncated normal distribution's statistics."""
def _normal_cdf(x):
return .5 * math.erfc(-x / math.sqrt(2))
def normal_pdf(x):
return math.exp(-(x**2) / 2.) / math.sqrt(2 * math.pi)
def probit(x):
return special_math.ndtri(x)
a = -2.
b = 2.
mu = 0.
sigma = 1.
if minvals is not None:
a = minvals
if maxvals is not None:
b = maxvals
if means is not None:
mu = means
if stddevs is not None:
sigma = stddevs
alpha = (a - mu) / sigma
beta = (b - mu) / sigma
z = _normal_cdf(beta) - _normal_cdf(alpha)
assert_equal((y >= a).sum(), n)
assert_equal((y <= b).sum(), n)
# For more information on these calculations, see:
# Burkardt, John. "The Truncated Normal Distribution".
# Department of Scientific Computing website. Florida State University.
expected_mean = mu + (normal_pdf(alpha) - normal_pdf(beta)) / z * sigma
y = y.astype(float)
actual_mean = np.mean(y)
assert_all_close(actual_mean, expected_mean, atol=mean_atol)
expected_median = mu + probit(
(_normal_cdf(alpha) + _normal_cdf(beta)) / 2.) * sigma
actual_median = np.median(y)
assert_all_close(actual_median, expected_median, atol=median_atol)
expected_variance = sigma**2 * (1 + (
(alpha * normal_pdf(alpha) - beta * normal_pdf(beta)) / z) - (
(normal_pdf(alpha) - normal_pdf(beta)) / z)**2)
actual_variance = np.var(y)
assert_all_close(
actual_variance,
expected_variance,
rtol=variance_rtol)