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
+177
View File
@@ -0,0 +1,177 @@
load("@xla//third_party/rules_python/python:py_library.bzl", "py_library")
load("//tensorflow:tensorflow.default.bzl", "cuda_py_strict_test")
package(
# copybara:uncomment default_applicable_licenses = ["//tensorflow:license"],
default_visibility = ["//tensorflow:internal"],
licenses = ["notice"],
)
py_library(
name = "test_util",
srcs = ["test_util.py"],
strict_deps = True,
deps = [
"//tensorflow/core:protos_all_py",
"//tensorflow/python/grappler:tf_optimizer",
"//tensorflow/python/training:saver",
],
)
cuda_py_strict_test(
name = "dct_ops_test",
srcs = ["dct_ops_test.py"],
shard_count = 16,
deps = [
"//tensorflow/python/eager:def_function",
"//tensorflow/python/framework:tensor_spec",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops/signal:dct_ops",
"//tensorflow/python/platform:client_testlib",
"//tensorflow/python/platform:tf_logging",
"//third_party/py/numpy",
"@absl_py//absl/testing:parameterized",
],
)
cuda_py_strict_test(
name = "fft_ops_test",
size = "medium",
srcs = ["fft_ops_test.py"],
shard_count = 2,
tags = [
"optonly",
],
deps = [
"//tensorflow/core:protos_all_py",
"//tensorflow/python/eager:context",
"//tensorflow/python/framework:config",
"//tensorflow/python/framework:dtypes",
"//tensorflow/python/framework:errors",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:gradient_checker_v2",
"//tensorflow/python/ops:math_ops",
"//tensorflow/python/ops:spectral_ops_gen",
"//tensorflow/python/ops/signal:fft_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
"@absl_py//absl/testing:parameterized",
],
)
cuda_py_strict_test(
name = "mel_ops_test",
srcs = ["mel_ops_test.py"],
deps = [
":test_util",
"//tensorflow/python/eager:context",
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:dtypes",
"//tensorflow/python/framework:ops",
"//tensorflow/python/framework:tensor_util",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops/signal:mel_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
"@absl_py//absl/testing:parameterized",
],
)
cuda_py_strict_test(
name = "mfcc_ops_test",
srcs = ["mfcc_ops_test.py"],
deps = [
"//tensorflow/python/eager:context",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:random_ops",
"//tensorflow/python/ops/signal:mfcc_ops",
"//tensorflow/python/platform:client_testlib",
"@absl_py//absl/testing:parameterized",
],
)
cuda_py_strict_test(
name = "reconstruction_ops_test",
srcs = ["reconstruction_ops_test.py"],
deps = [
"//tensorflow/python/eager:context",
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:array_ops_stack",
"//tensorflow/python/ops:gradient_checker_v2",
"//tensorflow/python/ops:gradients_impl",
"//tensorflow/python/ops:math_ops",
"//tensorflow/python/ops/signal:reconstruction_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
"@absl_py//absl/testing:parameterized",
],
)
cuda_py_strict_test(
name = "shape_ops_test",
srcs = ["shape_ops_test.py"],
deps = [
":test_util",
"//tensorflow/python/eager:context",
"//tensorflow/python/framework:constant_op",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:math_ops",
"//tensorflow/python/ops/signal:shape_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
],
)
cuda_py_strict_test(
name = "spectral_ops_test",
size = "large",
srcs = ["spectral_ops_test.py"],
tags = [
"no_rocm",
"nomac",
],
deps = [
"//tensorflow/python/eager:context",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops:array_ops",
"//tensorflow/python/ops:gradient_checker_v2",
"//tensorflow/python/ops:gradients_impl",
"//tensorflow/python/ops:math_ops",
"//tensorflow/python/ops/signal:spectral_ops",
"//tensorflow/python/ops/signal:window_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
"@absl_py//absl/testing:parameterized",
],
)
cuda_py_strict_test(
name = "window_ops_test",
srcs = ["window_ops_test.py"],
shard_count = 4,
tags = [
"no_rocm",
"no_windows_gpu",
],
deps = [
":test_util",
"//tensorflow/python/eager:context",
"//tensorflow/python/framework:for_generated_wrappers",
"//tensorflow/python/framework:tensor_spec",
"//tensorflow/python/framework:test_lib",
"//tensorflow/python/ops/signal:window_ops",
"//tensorflow/python/platform:client_testlib",
"//third_party/py/numpy",
"@absl_py//absl/testing:parameterized",
],
)
@@ -0,0 +1,273 @@
# 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.
# ==============================================================================
"""Tests for DCT operations."""
import importlib
import itertools
from absl.testing import parameterized
import numpy as np
from tensorflow.python.eager import def_function
from tensorflow.python.framework import tensor_spec
from tensorflow.python.framework import test_util
from tensorflow.python.ops.signal import dct_ops
from tensorflow.python.platform import test
from tensorflow.python.platform import tf_logging
def try_import(name): # pylint: disable=invalid-name
module = None
try:
module = importlib.import_module(name)
except ImportError as e:
tf_logging.warning("Could not import %s: %s" % (name, str(e)))
return module
fftpack = try_import("scipy.fftpack")
def _modify_input_for_dct(signals, n=None):
"""Pad or trim the provided NumPy array's innermost axis to length n."""
signal = np.array(signals)
if n is None or n == signal.shape[-1]:
signal_mod = signal
elif n >= 1:
signal_len = signal.shape[-1]
if n <= signal_len:
signal_mod = signal[..., 0:n]
else:
output_shape = list(signal.shape)
output_shape[-1] = n
signal_mod = np.zeros(output_shape)
signal_mod[..., 0:signal.shape[-1]] = signal
if n:
assert signal_mod.shape[-1] == n
return signal_mod
def _np_dct1(signals, n=None, norm=None):
"""Computes the DCT-I manually with NumPy."""
# X_k = (x_0 + (-1)**k * x_{N-1} +
# 2 * sum_{n=0}^{N-2} x_n * cos(\frac{pi}{N-1} * n * k) k=0,...,N-1
del norm
signals_mod = _modify_input_for_dct(signals, n=n)
dct_size = signals_mod.shape[-1]
dct = np.zeros_like(signals_mod)
for k in range(dct_size):
phi = np.cos(np.pi * np.arange(1, dct_size - 1) * k / (dct_size - 1))
dct[..., k] = 2 * np.sum(
signals_mod[..., 1:-1] * phi, axis=-1) + (
signals_mod[..., 0] + (-1)**k * signals_mod[..., -1])
return dct
def _np_dct2(signals, n=None, norm=None):
"""Computes the DCT-II manually with NumPy."""
# X_k = sum_{n=0}^{N-1} x_n * cos(\frac{pi}{N} * (n + 0.5) * k) k=0,...,N-1
signals_mod = _modify_input_for_dct(signals, n=n)
dct_size = signals_mod.shape[-1]
dct = np.zeros_like(signals_mod)
for k in range(dct_size):
phi = np.cos(np.pi * (np.arange(dct_size) + 0.5) * k / dct_size)
dct[..., k] = np.sum(signals_mod * phi, axis=-1)
# SciPy's `dct` has a scaling factor of 2.0 which we follow.
# https://github.com/scipy/scipy/blob/v1.2.1/scipy/fftpack/src/dct.c.src
if norm == "ortho":
# The orthonormal scaling includes a factor of 0.5 which we combine with
# the overall scaling of 2.0 to cancel.
dct[..., 0] *= np.sqrt(1.0 / dct_size)
dct[..., 1:] *= np.sqrt(2.0 / dct_size)
else:
dct *= 2.0
return dct
def _np_dct3(signals, n=None, norm=None):
"""Computes the DCT-III manually with NumPy."""
# SciPy's `dct` has a scaling factor of 2.0 which we follow.
# https://github.com/scipy/scipy/blob/v1.2.1/scipy/fftpack/src/dct.c.src
signals_mod = _modify_input_for_dct(signals, n=n)
dct_size = signals_mod.shape[-1]
signals_mod = np.array(signals_mod) # make a copy so we can modify
if norm == "ortho":
signals_mod[..., 0] *= np.sqrt(4.0 / dct_size)
signals_mod[..., 1:] *= np.sqrt(2.0 / dct_size)
else:
signals_mod *= 2.0
dct = np.zeros_like(signals_mod)
# X_k = 0.5 * x_0 +
# sum_{n=1}^{N-1} x_n * cos(\frac{pi}{N} * n * (k + 0.5)) k=0,...,N-1
half_x0 = 0.5 * signals_mod[..., 0]
for k in range(dct_size):
phi = np.cos(np.pi * np.arange(1, dct_size) * (k + 0.5) / dct_size)
dct[..., k] = half_x0 + np.sum(signals_mod[..., 1:] * phi, axis=-1)
return dct
def _np_dct4(signals, n=None, norm=None):
"""Computes the DCT-IV manually with NumPy."""
# SciPy's `dct` has a scaling factor of 2.0 which we follow.
# https://github.com/scipy/scipy/blob/v1.2.1/scipy/fftpack/src/dct.c.src
signals_mod = _modify_input_for_dct(signals, n=n)
dct_size = signals_mod.shape[-1]
signals_mod = np.array(signals_mod) # make a copy so we can modify
if norm == "ortho":
signals_mod *= np.sqrt(2.0 / dct_size)
else:
signals_mod *= 2.0
dct = np.zeros_like(signals_mod)
# X_k = sum_{n=0}^{N-1}
# x_n * cos(\frac{pi}{4N} * (2n + 1) * (2k + 1)) k=0,...,N-1
for k in range(dct_size):
phi = np.cos(np.pi *
(2 * np.arange(0, dct_size) + 1) * (2 * k + 1) /
(4.0 * dct_size))
dct[..., k] = np.sum(signals_mod * phi, axis=-1)
return dct
NP_DCT = {1: _np_dct1, 2: _np_dct2, 3: _np_dct3, 4: _np_dct4}
NP_IDCT = {1: _np_dct1, 2: _np_dct3, 3: _np_dct2, 4: _np_dct4}
@test_util.run_all_in_graph_and_eager_modes
class DCTOpsTest(parameterized.TestCase, test.TestCase):
def _compare(self, signals, n, norm, dct_type, atol, rtol):
"""Compares (I)DCT to SciPy (if available) and a NumPy implementation."""
np_dct = NP_DCT[dct_type](signals, n=n, norm=norm)
tf_dct = dct_ops.dct(signals, n=n, type=dct_type, norm=norm)
self.assertEqual(tf_dct.dtype.as_numpy_dtype, signals.dtype)
self.assertAllClose(np_dct, tf_dct, atol=atol, rtol=rtol)
np_idct = NP_IDCT[dct_type](signals, n=None, norm=norm)
tf_idct = dct_ops.idct(signals, type=dct_type, norm=norm)
self.assertEqual(tf_idct.dtype.as_numpy_dtype, signals.dtype)
self.assertAllClose(np_idct, tf_idct, atol=atol, rtol=rtol)
if fftpack and dct_type != 4:
scipy_dct = fftpack.dct(signals, n=n, type=dct_type, norm=norm)
self.assertAllClose(scipy_dct, tf_dct, atol=atol, rtol=rtol)
scipy_idct = fftpack.idct(signals, type=dct_type, norm=norm)
self.assertAllClose(scipy_idct, tf_idct, atol=atol, rtol=rtol)
# Verify inverse(forward(s)) == s, up to a normalization factor.
# Since `n` is not implemented for IDCT operation, re-calculating tf_dct
# without n.
tf_dct = dct_ops.dct(signals, type=dct_type, norm=norm)
tf_idct_dct = dct_ops.idct(tf_dct, type=dct_type, norm=norm)
tf_dct_idct = dct_ops.dct(tf_idct, type=dct_type, norm=norm)
if norm is None:
if dct_type == 1:
tf_idct_dct *= 0.5 / (signals.shape[-1] - 1)
tf_dct_idct *= 0.5 / (signals.shape[-1] - 1)
else:
tf_idct_dct *= 0.5 / signals.shape[-1]
tf_dct_idct *= 0.5 / signals.shape[-1]
self.assertAllClose(signals, tf_idct_dct, atol=atol, rtol=rtol)
self.assertAllClose(signals, tf_dct_idct, atol=atol, rtol=rtol)
@parameterized.parameters(itertools.product(
[1, 2, 3, 4],
[None, "ortho"],
[[2], [3], [10], [2, 20], [2, 3, 25]],
[np.float32, np.float64]))
def test_random(self, dct_type, norm, shape, dtype):
"""Test randomly generated batches of data."""
# "ortho" normalization is not implemented for type I.
if dct_type == 1 and norm == "ortho":
return
with self.session():
tol = 5e-4 if dtype == np.float32 else 1e-7
signals = np.random.rand(*shape).astype(dtype)
n = np.random.randint(1, 2 * signals.shape[-1])
n = np.random.choice([None, n])
self._compare(signals, n, norm=norm, dct_type=dct_type,
rtol=tol, atol=tol)
@parameterized.parameters(itertools.product(
[1, 2, 3, 4],
[None, "ortho"],
[[2], [3], [10], [2, 20], [2, 3, 25]],
[np.float32, np.float64]))
def test_with_dynamic_dimensions(self, dct_type, norm, shape, dtype):
# "ortho" normalization is not implemented for type I.
if dct_type == 1 and norm == "ortho":
return
signals = np.random.rand(*shape).astype(dtype)
n = np.random.randint(1, 2 * shape[-1])
n = np.random.choice([None, n])
@def_function.function
def func(signals):
return dct_ops.dct(signals, n=n, type=dct_type, norm=norm)
# Trace with all undefined dimensions
signals_spec = tensor_spec.TensorSpec([None] * len(shape), dtype)
f = func.get_concrete_function(signals_spec)
# Run with actual shape
f(signals)
def test_error(self):
signals = np.random.rand(10)
# Unsupported type.
with self.assertRaises(ValueError):
dct_ops.dct(signals, type=5)
# Invalid n.
with self.assertRaises(ValueError):
dct_ops.dct(signals, n=-2)
# DCT-I normalization not implemented.
with self.assertRaises(ValueError):
dct_ops.dct(signals, type=1, norm="ortho")
# DCT-I requires at least two inputs.
with self.assertRaises(ValueError):
dct_ops.dct(np.random.rand(1), type=1)
# Unknown normalization.
with self.assertRaises(ValueError):
dct_ops.dct(signals, norm="bad")
with self.assertRaises(NotImplementedError):
dct_ops.dct(signals, axis=0)
def test_idct_n_argument_supported(self):
# Regression for https://github.com/tensorflow/tensorflow/issues/102418:
# the docstring of `idct` previously claimed that `n` "must be None",
# but the implementation routes through `_dct_internal` which has
# always supported an arbitrary positive `n`. The runtime contract is
# asserted here so a future change cannot silently re-introduce the
# mismatch the docstring used to advertise.
signals = np.random.rand(8).astype(np.float32)
# Same n as the input length — must not raise and must agree with the
# default-None call.
out_default = dct_ops.idct(signals, type=3, norm="ortho")
out_explicit = dct_ops.idct(signals, type=3, norm="ortho", n=8)
self.assertAllClose(out_default, out_explicit)
# Smaller n (truncate). The runtime accepts it; we only assert no
# exception and the expected output length.
out_trunc = dct_ops.idct(signals, type=3, norm="ortho", n=4)
self.assertEqual(out_trunc.shape[-1], 4)
# Larger n (zero-pad).
out_pad = dct_ops.idct(signals, type=3, norm="ortho", n=12)
self.assertEqual(out_pad.shape[-1], 12)
# Negative n still rejected by the shared validator.
with self.assertRaises(ValueError):
dct_ops.idct(signals, n=-1)
def test_idct_docstring_does_not_claim_n_must_be_none(self):
# Ensures the docstring stays in sync with the runtime contract
# (regression for the pure-doc issue tracked in #102418).
self.assertNotIn("Must be `None`", dct_ops.idct.__doc__)
if __name__ == "__main__":
test.main()
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,206 @@
# 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.
# ==============================================================================
"""Tests for mel_ops."""
from absl.testing import parameterized
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 ops
from tensorflow.python.framework import tensor_util
from tensorflow.python.framework import test_util as tf_test_util
from tensorflow.python.kernel_tests.signal import test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops.signal import mel_ops
from tensorflow.python.platform import test
# mel spectrum constants and functions.
_MEL_BREAK_FREQUENCY_HERTZ = 700.0
_MEL_HIGH_FREQUENCY_Q = 1127.0
def hertz_to_mel(frequencies_hertz):
"""Convert frequencies to mel scale using HTK formula.
Copied from
https://github.com/tensorflow/models/blob/master/research/audioset/mel_features.py.
Args:
frequencies_hertz: Scalar or np.array of frequencies in hertz.
Returns:
Object of same size as frequencies_hertz containing corresponding values
on the mel scale.
"""
return _MEL_HIGH_FREQUENCY_Q * np.log(
1.0 + (frequencies_hertz / _MEL_BREAK_FREQUENCY_HERTZ))
def spectrogram_to_mel_matrix(num_mel_bins=20,
num_spectrogram_bins=129,
audio_sample_rate=8000,
lower_edge_hertz=125.0,
upper_edge_hertz=3800.0,
unused_dtype=None):
"""Return a matrix that can post-multiply spectrogram rows to make mel.
Copied from
https://github.com/tensorflow/models/blob/master/research/audioset/mel_features.py.
Returns a np.array matrix A that can be used to post-multiply a matrix S of
spectrogram values (STFT magnitudes) arranged as frames x bins to generate a
"mel spectrogram" M of frames x num_mel_bins. M = S A.
The classic HTK algorithm exploits the complementarity of adjacent mel bands
to multiply each FFT bin by only one mel weight, then add it, with positive
and negative signs, to the two adjacent mel bands to which that bin
contributes. Here, by expressing this operation as a matrix multiply, we go
from num_fft multiplies per frame (plus around 2*num_fft adds) to around
num_fft^2 multiplies and adds. However, because these are all presumably
accomplished in a single call to np.dot(), it's not clear which approach is
faster in Python. The matrix multiplication has the attraction of being more
general and flexible, and much easier to read.
Args:
num_mel_bins: How many bands in the resulting mel spectrum. This is
the number of columns in the output matrix.
num_spectrogram_bins: How many bins there are in the source spectrogram
data, which is understood to be fft_size/2 + 1, i.e. the spectrogram
only contains the nonredundant FFT bins.
audio_sample_rate: Samples per second of the audio at the input to the
spectrogram. We need this to figure out the actual frequencies for
each spectrogram bin, which dictates how they are mapped into mel.
lower_edge_hertz: Lower bound on the frequencies to be included in the mel
spectrum. This corresponds to the lower edge of the lowest triangular
band.
upper_edge_hertz: The desired top edge of the highest frequency band.
Returns:
An np.array with shape (num_spectrogram_bins, num_mel_bins).
Raises:
ValueError: if frequency edges are incorrectly ordered.
"""
audio_sample_rate = tensor_util.constant_value(audio_sample_rate)
nyquist_hertz = audio_sample_rate / 2.
if lower_edge_hertz >= upper_edge_hertz:
raise ValueError("lower_edge_hertz %.1f >= upper_edge_hertz %.1f" %
(lower_edge_hertz, upper_edge_hertz))
spectrogram_bins_hertz = np.linspace(0.0, nyquist_hertz, num_spectrogram_bins)
spectrogram_bins_mel = hertz_to_mel(spectrogram_bins_hertz)
# The i'th mel band (starting from i=1) has center frequency
# band_edges_mel[i], lower edge band_edges_mel[i-1], and higher edge
# band_edges_mel[i+1]. Thus, we need num_mel_bins + 2 values in
# the band_edges_mel arrays.
band_edges_mel = np.linspace(hertz_to_mel(lower_edge_hertz),
hertz_to_mel(upper_edge_hertz), num_mel_bins + 2)
# Matrix to post-multiply feature arrays whose rows are num_spectrogram_bins
# of spectrogram values.
mel_weights_matrix = np.empty((num_spectrogram_bins, num_mel_bins))
for i in range(num_mel_bins):
lower_edge_mel, center_mel, upper_edge_mel = band_edges_mel[i:i + 3]
# Calculate lower and upper slopes for every spectrogram bin.
# Line segments are linear in the *mel* domain, not hertz.
lower_slope = ((spectrogram_bins_mel - lower_edge_mel) /
(center_mel - lower_edge_mel))
upper_slope = ((upper_edge_mel - spectrogram_bins_mel) /
(upper_edge_mel - center_mel))
# .. then intersect them with each other and zero.
mel_weights_matrix[:, i] = np.maximum(0.0, np.minimum(lower_slope,
upper_slope))
# HTK excludes the spectrogram DC bin; make sure it always gets a zero
# coefficient.
mel_weights_matrix[0, :] = 0.0
return mel_weights_matrix
@tf_test_util.run_all_in_graph_and_eager_modes
class LinearToMelTest(test.TestCase, parameterized.TestCase):
@parameterized.parameters(
# Defaults. Integer sample rate.
(20, 129, 8000, False, 125.0, 3800.0, dtypes.float64),
(20, 129, 8000, True, 125.0, 3800.0, dtypes.float64),
# Defaults. Float sample rate.
(20, 129, 8000.0, False, 125.0, 3800.0, dtypes.float64),
(20, 129, 8000.0, True, 125.0, 3800.0, dtypes.float64),
# Settings used by Tacotron (https://arxiv.org/abs/1703.10135).
(80, 1025, 24000.0, False, 80.0, 12000.0, dtypes.float64))
def test_matches_reference_implementation(
self, num_mel_bins, num_spectrogram_bins, sample_rate,
use_tensor_sample_rate, lower_edge_hertz, upper_edge_hertz, dtype):
if use_tensor_sample_rate:
sample_rate = constant_op.constant(sample_rate)
mel_matrix_np = spectrogram_to_mel_matrix(
num_mel_bins, num_spectrogram_bins, sample_rate, lower_edge_hertz,
upper_edge_hertz, dtype)
mel_matrix = mel_ops.linear_to_mel_weight_matrix(
num_mel_bins, num_spectrogram_bins, sample_rate, lower_edge_hertz,
upper_edge_hertz, dtype)
self.assertAllClose(mel_matrix_np, mel_matrix, atol=3e-6)
@parameterized.parameters(dtypes.float32, dtypes.float64)
def test_dtypes(self, dtype):
# LinSpace is not supported for tf.float16.
self.assertEqual(dtype,
mel_ops.linear_to_mel_weight_matrix(dtype=dtype).dtype)
def test_error(self):
# TODO(rjryan): Error types are different under eager.
if context.executing_eagerly():
return
with self.assertRaises(ValueError):
mel_ops.linear_to_mel_weight_matrix(num_mel_bins=0)
with self.assertRaises(ValueError):
mel_ops.linear_to_mel_weight_matrix(sample_rate=0.0)
with self.assertRaises(ValueError):
mel_ops.linear_to_mel_weight_matrix(lower_edge_hertz=-1)
with self.assertRaises(ValueError):
mel_ops.linear_to_mel_weight_matrix(lower_edge_hertz=100,
upper_edge_hertz=10)
with self.assertRaises(ValueError):
mel_ops.linear_to_mel_weight_matrix(upper_edge_hertz=1000,
sample_rate=800)
with self.assertRaises(ValueError):
mel_ops.linear_to_mel_weight_matrix(dtype=dtypes.int32)
@parameterized.parameters(dtypes.float32, dtypes.float64)
def test_constant_folding(self, dtype):
"""Mel functions should be constant foldable."""
if context.executing_eagerly():
return
# TODO(rjryan): tf.bfloat16 cannot be constant folded by Grappler.
g = ops.Graph()
with g.as_default():
mel_matrix = mel_ops.linear_to_mel_weight_matrix(
sample_rate=constant_op.constant(8000.0, dtype=dtypes.float32),
dtype=dtype)
rewritten_graph = test_util.grappler_optimize(g, [mel_matrix])
self.assertLen(rewritten_graph.node, 1)
def test_num_spectrogram_bins_dynamic(self):
num_spectrogram_bins = array_ops.placeholder_with_default(
ops.convert_to_tensor(129, dtype=dtypes.int32), shape=())
mel_matrix_np = spectrogram_to_mel_matrix(
20, 129, 8000.0, 125.0, 3800.0)
mel_matrix = mel_ops.linear_to_mel_weight_matrix(
20, num_spectrogram_bins, 8000.0, 125.0, 3800.0)
self.assertAllClose(mel_matrix_np, mel_matrix, atol=3e-6)
if __name__ == "__main__":
test.main()
@@ -0,0 +1,57 @@
# 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.
# ==============================================================================
"""Tests for mfcc_ops."""
from absl.testing import parameterized
from tensorflow.python.eager import context
from tensorflow.python.framework import dtypes
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 random_ops
from tensorflow.python.ops.signal import mfcc_ops
from tensorflow.python.platform import test
# TODO(rjryan): We have no open source tests for MFCCs at the moment. Internally
# at Google, this code is tested against a reference implementation that follows
# HTK conventions.
@test_util.run_all_in_graph_and_eager_modes
class MFCCTest(test.TestCase, parameterized.TestCase):
def test_error(self):
# num_mel_bins must be positive.
with self.assertRaises(ValueError):
signal = array_ops.zeros((2, 3, 0))
mfcc_ops.mfccs_from_log_mel_spectrograms(signal)
@parameterized.parameters(dtypes.float32, dtypes.float64)
def test_basic(self, dtype):
"""A basic test that the op runs on random input."""
signal = random_ops.random_normal((2, 3, 5), dtype=dtype)
self.evaluate(mfcc_ops.mfccs_from_log_mel_spectrograms(signal))
def test_unknown_shape(self):
"""A test that the op runs when shape and rank are unknown."""
if context.executing_eagerly():
return
signal = array_ops.placeholder_with_default(
random_ops.random_normal((2, 3, 5)), tensor_shape.TensorShape(None))
self.assertIsNone(signal.shape.ndims)
self.evaluate(mfcc_ops.mfccs_from_log_mel_spectrograms(signal))
if __name__ == "__main__":
test.main()
@@ -0,0 +1,218 @@
# 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.
# ==============================================================================
"""Tests for reconstruction_ops."""
from absl.testing import parameterized
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 test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import array_ops_stack
from tensorflow.python.ops import gradient_checker_v2
from tensorflow.python.ops import gradients_impl
from tensorflow.python.ops import math_ops
from tensorflow.python.ops.signal import reconstruction_ops
from tensorflow.python.platform import test
@test_util.run_all_in_graph_and_eager_modes
class ReconstructionOpsTest(test.TestCase, parameterized.TestCase):
def __init__(self, *args, **kwargs):
super(ReconstructionOpsTest, self).__init__(*args, **kwargs)
self.batch_size = 3
self.frames = 3
self.samples = 5
self.bases = np.array(range(2, 5))
exponents = np.array(range(self.frames * self.samples))
powers = np.power(self.bases[:, np.newaxis], exponents[np.newaxis, :])
self.powers = np.reshape(powers, [self.batch_size, self.frames,
self.samples])
self.frame_hop = 2
# Hand computed example using powers of unique numbers: this is easily
# verified.
self.expected_string = ["1", "10", "100100", "1001000", "10010010000",
"100100000000", "1001000000000", "10000000000000",
"100000000000000"]
def test_all_ones(self):
signal = array_ops.ones([3, 5])
reconstruction = reconstruction_ops.overlap_and_add(signal, 2)
self.assertEqual(reconstruction.shape.as_list(), [9])
expected_output = np.array([1, 1, 2, 2, 3, 2, 2, 1, 1])
self.assertAllClose(reconstruction, expected_output)
def test_unknown_shapes(self):
# This test uses placeholders and does not work in Eager mode.
if context.executing_eagerly():
return
signal = array_ops.placeholder_with_default(
np.ones((4, 3, 5)).astype(np.int32), shape=[None, None, None])
frame_step = array_ops.placeholder_with_default(2, shape=[])
reconstruction = reconstruction_ops.overlap_and_add(signal, frame_step)
self.assertEqual(reconstruction.shape.as_list(), [None, None])
expected_output = np.array([[1, 1, 2, 2, 3, 2, 2, 1, 1]] * 4)
self.assertAllClose(reconstruction, expected_output)
def test_unknown_rank(self):
# This test uses placeholders and does not work in eager mode.
if context.executing_eagerly():
return
signal = array_ops.placeholder_with_default(
np.ones((4, 3, 5)).astype(np.int32), shape=None)
frame_step = array_ops.placeholder_with_default(2, shape=[])
reconstruction = reconstruction_ops.overlap_and_add(signal, frame_step)
self.assertEqual(reconstruction.shape, None)
expected_output = np.array([[1, 1, 2, 2, 3, 2, 2, 1, 1]] * 4)
self.assertAllClose(reconstruction, expected_output)
def test_fast_path(self):
# This test uses tensor names and does not work in eager mode.
if context.executing_eagerly():
return
signal = array_ops.ones([3, 5])
frame_step = 5
reconstruction = reconstruction_ops.overlap_and_add(signal, frame_step)
self.assertEqual(reconstruction.name, "overlap_and_add/fast_path:0")
expected_output = np.ones([15])
self.assertAllClose(reconstruction, expected_output)
@parameterized.parameters(
# All hop lengths on a frame length of 2.
(2, [1, 5, 9, 6], 1),
(2, [1, 2, 3, 4, 5, 6], 2),
# All hop lengths on a frame length of 3.
(3, [1, 6, 15, 14, 9], 1),
(3, [1, 2, 7, 5, 13, 8, 9], 2),
(3, [1, 2, 3, 4, 5, 6, 7, 8, 9], 3),
# All hop lengths on a frame length of 4.
(4, [1, 7, 18, 21, 19, 12], 1),
(4, [1, 2, 8, 10, 16, 18, 11, 12], 2),
(4, [1, 2, 3, 9, 6, 7, 17, 10, 11, 12], 3),
(4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 4))
def test_simple(self, frame_length, expected, frame_hop):
def make_input(frame_length, num_frames=3):
"""Generate a tensor of num_frames frames of frame_length."""
return np.reshape(np.arange(1, num_frames * frame_length + 1),
(-1, frame_length))
signal = make_input(frame_length)
reconstruction = reconstruction_ops.overlap_and_add(
np.array(signal), frame_hop)
expected_output = np.array(expected)
self.assertAllClose(reconstruction, expected_output)
def test_powers(self):
signal = constant_op.constant(np.squeeze(self.powers[0, :, :]),
dtype=dtypes.int64)
reconstruction = reconstruction_ops.overlap_and_add(signal, self.frame_hop)
output = self.evaluate(reconstruction)
string_output = [np.base_repr(x, self.bases[0]) for x in output]
self.assertEqual(string_output, self.expected_string)
def test_batch(self):
signal = constant_op.constant(self.powers, dtype=dtypes.int64)
reconstruction = reconstruction_ops.overlap_and_add(signal, self.frame_hop)
output = self.evaluate(reconstruction)
accumulator = True
for i in range(self.batch_size):
string_output = [np.base_repr(x, self.bases[i]) for x in output[i, :]]
accumulator = accumulator and (string_output == self.expected_string)
self.assertTrue(accumulator)
def test_one_element_batch(self):
input_matrix = np.squeeze(self.powers[0, :, :])
input_matrix = input_matrix[np.newaxis, :, :].astype(float)
signal = constant_op.constant(input_matrix, dtype=dtypes.float32)
reconstruction = reconstruction_ops.overlap_and_add(signal, self.frame_hop)
output = self.evaluate(reconstruction)
string_output = [np.base_repr(int(x), self.bases[0]) for x in
np.squeeze(output)]
self.assertEqual(output.shape, (1, 9))
self.assertEqual(string_output, self.expected_string)
@parameterized.parameters(
((1, 128), 1),
((5, 35), 17),
((10, 128), 128),
((2, 10, 128), 127),
((2, 2, 10, 128), 126),
((2, 2, 2, 10, 128), 125))
def test_gradient(self, shape, frame_hop):
# TODO(rjryan): Eager gradient tests.
if context.executing_eagerly():
return
signal = array_ops.zeros(shape)
reconstruction = reconstruction_ops.overlap_and_add(signal, frame_hop)
loss = math_ops.reduce_sum(reconstruction)
# Increasing any sample in the input frames by one will increase the sum
# of all the samples in the reconstruction by 1, so the gradient should
# be all ones, no matter the shape or hop.
gradient = self.evaluate(gradients_impl.gradients([loss], [signal])[0])
self.assertTrue((gradient == 1.0).all())
def test_gradient_batch(self):
# TODO(rjryan): Eager gradient tests.
if context.executing_eagerly():
return
signal = array_ops.zeros((2, 10, 10))
frame_hop = 10
reconstruction = reconstruction_ops.overlap_and_add(signal, frame_hop)
# Multiply the first batch-item's reconstruction by zeros. This will block
# gradient from flowing into the first batch item from the loss. Multiply
# the second batch item by the integers from 0 to 99. Since there is zero
# overlap, the gradient for this batch item will be 0-99 shaped as (10,
# 10).
reconstruction *= array_ops_stack.stack(
[array_ops.zeros((100,)),
math_ops.cast(math_ops.range(100), dtypes.float32)])
loss = math_ops.reduce_sum(reconstruction)
# Verify that only the second batch item receives gradient.
gradient = self.evaluate(gradients_impl.gradients([loss], [signal])[0])
expected_gradient = np.stack([
np.zeros((10, 10)),
np.reshape(np.arange(100).astype(np.float32), (10, 10))])
self.assertAllEqual(expected_gradient, gradient)
def test_gradient_numerical(self):
shape = (2, 10, 10)
framed_signal = array_ops.zeros(shape)
frame_hop = 10
def f(signal):
return reconstruction_ops.overlap_and_add(signal, frame_hop)
((jacob_t,), (jacob_n,)) = gradient_checker_v2.compute_gradient(
f, [framed_signal])
self.assertAllClose(jacob_t, jacob_n)
if __name__ == "__main__":
test.main()
@@ -0,0 +1,353 @@
# 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 shape_ops."""
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 ops
from tensorflow.python.framework import tensor_shape
from tensorflow.python.framework import test_util as tf_test_util
from tensorflow.python.kernel_tests.signal import test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import math_ops
from tensorflow.python.ops.signal import shape_ops
from tensorflow.python.platform import test
@tf_test_util.run_all_in_graph_and_eager_modes
class FrameTest(test.TestCase):
def test_mapping_of_indices_without_padding(self):
tensor = constant_op.constant(np.arange(9152), dtypes.int32)
tensor = array_ops.expand_dims(tensor, 0)
result = shape_ops.frame(tensor, 512, 180, pad_end=False)
expected = np.tile(np.arange(512), (49, 1))
expected += np.tile(np.arange(49) * 180, (512, 1)).T
expected = np.expand_dims(expected, axis=0)
expected = np.array(expected, dtype=np.int32)
self.assertAllEqual(expected, result)
def test_mapping_of_indices_with_padding(self):
tensor = constant_op.constant(np.arange(10000), dtypes.int32)
tensor = array_ops.expand_dims(tensor, 0)
result = shape_ops.frame(tensor, 512, 192, pad_end=True)
expected = np.tile(np.arange(512), (53, 1))
expected += np.tile(np.arange(53) * 192, (512, 1)).T
expected[expected >= 10000] = 0
expected = np.expand_dims(expected, axis=0)
expected = np.array(expected, dtype=np.int32)
self.assertAllEqual(expected, result)
def test_invalid_inputs(self):
# Rank 0 input signal.
with self.assertRaises(ValueError):
shape_ops.frame(1, 1, 1)
if not context.executing_eagerly():
# If the rank is unknown, do not raise an exception.
shape_ops.frame(array_ops.placeholder_with_default(
1, shape=tensor_shape.TensorShape(None)), 1, 1)
# Non-scalar frame_length.
with self.assertRaises(ValueError):
shape_ops.frame([1], [1], 1)
# Non-scalar frame_step.
with self.assertRaises(ValueError):
shape_ops.frame([1], 1, [1])
# Non-scalar pad_value.
with self.assertRaises(ValueError):
shape_ops.frame([1], 1, 1, pad_end=True, pad_value=[1])
def test_length_zero(self):
signal = constant_op.constant([], dtype=dtypes.float32)
frame_length = 2
frame_step = 1
result = self.evaluate(shape_ops.frame(
signal, frame_length, frame_step, pad_end=True, pad_value=99))
self.assertEqual((0, 2), result.shape)
result = self.evaluate(
shape_ops.frame(signal, frame_length, frame_step, pad_end=False))
self.assertEqual((0, 2), result.shape)
def test_shape_inference(self):
if context.executing_eagerly():
return
signal = array_ops.zeros((1, 1), dtype=dtypes.int32)
frame_length = 2
frame_step = 1
# Shape inference is able to detect the rank and inner-most dimension
# if frame_length is known at graph definition time.
result = shape_ops.frame(signal, frame_length, frame_step,
pad_end=True, pad_value=99)
self.assertEqual([1, 1, 2], result.shape.as_list())
result = shape_ops.frame(signal, frame_length, frame_step,
pad_end=False)
self.assertEqual([1, 0, 2], result.shape.as_list())
# If frame_length is not known, rank and (known) outer and inner dimensions
# are inferred.
signal = array_ops.zeros([1, 2, 3, 4], dtype=dtypes.int32)
frame_length = array_ops.placeholder_with_default(
ops.convert_to_tensor(0, dtypes.int32), shape=[])
frame_step = 1
result = shape_ops.frame(signal, frame_length, frame_step,
pad_end=True, pad_value=99, axis=1)
self.assertEqual([1, 2, None, 3, 4], result.shape.as_list())
result = shape_ops.frame(signal, frame_length, frame_step,
pad_end=False, axis=1)
self.assertEqual([1, None, None, 3, 4], result.shape.as_list())
# If frame_length and inner-most dimension is known, rank, inner dimensions,
# and known outer dimensions are inferred.
signal = array_ops.placeholder_with_default(
array_ops.zeros((0, 5, 0, 20, 5, 3), dtype=dtypes.int32),
shape=[None, 5, None, 20, 5, 3])
frame_length = 4
frame_step = 3
result = shape_ops.frame(signal, frame_length, frame_step,
pad_end=True, pad_value=99, axis=3)
self.assertEqual([None, 5, None, 7, 4, 5, 3], result.shape.as_list())
result = shape_ops.frame(signal, frame_length, frame_step,
pad_end=False, axis=3)
self.assertEqual([None, 5, None, 6, 4, 5, 3], result.shape.as_list())
# Test that shape inference is consistent with actual returned shapes for
# small values of signal_length, frame_length, frame_step, and pad_end in
# [True, False].
frame_step = 1
for signal_length in range(2):
signal = [0] * signal_length
for frame_length in range(2):
for pad_end in [False, True]:
op = shape_ops.frame(signal, frame_length, frame_step,
pad_end=pad_end, pad_value=99)
result = self.evaluate(op)
self.assertEqual(op.shape.as_list(), list(result.shape))
def test_basic_mono(self):
signal = np.arange(6)
frame_length = 3
frame_step = 2
for rank in range(5):
nd_signal = np.reshape(signal, (1,) * rank + signal.shape)
# With padding, we pad the last frame with pad_value.
result = shape_ops.frame(nd_signal, frame_length, frame_step,
pad_end=True, pad_value=99)
expected_inner_frames = np.array([[0, 1, 2], [2, 3, 4], [4, 5, 99]])
expected = np.reshape(
expected_inner_frames, (1,) * rank + expected_inner_frames.shape)
self.assertAllEqual(expected, result)
# Without padding, we drop the last frame.
expected_inner_frames = np.array([[0, 1, 2], [2, 3, 4]])
expected = np.reshape(
expected_inner_frames, (1,) * rank + expected_inner_frames.shape)
result = shape_ops.frame(nd_signal, frame_length, frame_step,
pad_end=False)
self.assertAllEqual(expected, result)
def test_basic_stereo(self):
signal = np.vstack([np.arange(6),
np.arange(6) + 10])
frame_length = 3
frame_step = 2
for rank in range(5):
nd_signal = np.reshape(signal, (1,) * rank + signal.shape)
# With padding, we pad the last frame with pad_value.
result = shape_ops.frame(nd_signal, frame_length, frame_step,
pad_end=True, pad_value=99)
expected_inner_frames = np.array([
[[0, 1, 2], [2, 3, 4], [4, 5, 99]],
[[10, 11, 12], [12, 13, 14], [14, 15, 99]]])
expected = np.reshape(
expected_inner_frames, (1,) * rank + expected_inner_frames.shape)
self.assertAllEqual(expected, result)
# Without padding, we drop the last frame.
expected_inner_frames = np.array([[[0, 1, 2], [2, 3, 4]],
[[10, 11, 12], [12, 13, 14]]])
expected = np.reshape(
expected_inner_frames, (1,) * rank + expected_inner_frames.shape)
result = shape_ops.frame(nd_signal, frame_length, frame_step,
pad_end=False)
self.assertAllEqual(expected, result)
def test_complex_shape(self):
signal = np.vstack([np.arange(6),
np.arange(6) + 10,
np.arange(6) + 20,
np.arange(6) + 30,
np.arange(6) + 40,
np.arange(6) + 50])
signal = np.reshape(signal, (2, 1, 3, 1, 6))
frame_length = 3
frame_step = 2
# With padding, we pad the last frame with pad_value.
result = shape_ops.frame(signal, frame_length, frame_step,
pad_end=True, pad_value=99)
# Resulting shape is (2, 1, 3, 1, 3, 3).
expected = [[[[[[0, 1, 2], [2, 3, 4], [4, 5, 99]]],
[[[10, 11, 12], [12, 13, 14], [14, 15, 99]]],
[[[20, 21, 22], [22, 23, 24], [24, 25, 99]]]]],
[[[[[30, 31, 32], [32, 33, 34], [34, 35, 99]]],
[[[40, 41, 42], [42, 43, 44], [44, 45, 99]]],
[[[50, 51, 52], [52, 53, 54], [54, 55, 99]]]]]]
self.assertAllEqual(expected, result)
result = shape_ops.frame(signal, frame_length, frame_step,
pad_end=False)
# Resulting shape is (2, 1, 3, 1, 3, 2).
expected = [[[[[[0, 1, 2], [2, 3, 4]]],
[[[10, 11, 12], [12, 13, 14]]],
[[[20, 21, 22], [22, 23, 24]]]]],
[[[[[30, 31, 32], [32, 33, 34]]],
[[[40, 41, 42], [42, 43, 44]]],
[[[50, 51, 52], [52, 53, 54]]]]]]
self.assertAllEqual(expected, result)
def test_axis(self):
signal = np.reshape(np.arange(16), (2, 4, 2))
result = shape_ops.frame(signal, frame_length=2, frame_step=2,
pad_end=True, axis=1)
expected = np.reshape(np.arange(16), (2, 2, 2, 2))
self.assertAllEqual(expected, self.evaluate(result))
result = shape_ops.frame(signal, frame_length=2, frame_step=1,
pad_end=True, axis=1)
expected = [[[[0, 1], [2, 3]],
[[2, 3], [4, 5]],
[[4, 5], [6, 7]],
[[6, 7], [0, 0]]],
[[[8, 9], [10, 11]],
[[10, 11], [12, 13]],
[[12, 13], [14, 15]],
[[14, 15], [0, 0]]]]
self.assertAllEqual(expected, self.evaluate(result))
result = shape_ops.frame(signal, frame_length=3, frame_step=1,
pad_end=True, axis=1)
expected = [[[[0, 1], [2, 3], [4, 5]],
[[2, 3], [4, 5], [6, 7]],
[[4, 5], [6, 7], [0, 0]],
[[6, 7], [0, 0], [0, 0]]],
[[[8, 9], [10, 11], [12, 13]],
[[10, 11], [12, 13], [14, 15]],
[[12, 13], [14, 15], [0, 0]],
[[14, 15], [0, 0], [0, 0]]]]
self.assertAllEqual(expected, self.evaluate(result))
def test_window_larger_than_signal(self):
signal = constant_op.constant([[1, 2], [11, 12]], dtype=dtypes.float32)
frame_length = 4
frame_step = 1
result = shape_ops.frame(signal, frame_length, frame_step,
pad_end=True, pad_value=99)
self.assertAllClose([[[1, 2, 99, 99], [2, 99, 99, 99]],
[[11, 12, 99, 99], [12, 99, 99, 99]]], result)
result = shape_ops.frame(signal, frame_length, frame_step,
pad_end=False)
self.assertEqual((2, 0, 4), result.shape)
frame_step = 2
result = shape_ops.frame(signal, frame_length, frame_step,
pad_end=True, pad_value=99)
self.assertAllClose([[[1, 2, 99, 99]], [[11, 12, 99, 99]]], result)
result = shape_ops.frame(signal, frame_length, frame_step,
pad_end=False)
self.assertEqual((2, 0, 4), result.shape)
def test_preserves_type(self):
signal = math_ops.range(10, dtype=dtypes.float64)
frame_length = 2
frame_step = 3
result = shape_ops.frame(signal, frame_length, frame_step)
self.assertEqual(result.dtype, signal.dtype)
def test_dynamic_tensor(self):
if context.executing_eagerly():
return
# Show that frame works even when the dimensions of its input are
# not known at graph creation time.
input_signal = np.vstack([np.arange(4), np.arange(4) + 10,
np.arange(4) + 20])
frame_length = 2
frame_step = 2
signal_placeholder = array_ops.placeholder_with_default(
input_signal, shape=(None, None))
result = self.evaluate(
shape_ops.frame(signal_placeholder, frame_length, frame_step))
self.assertAllEqual([[[0, 1], [2, 3]],
[[10, 11], [12, 13]],
[[20, 21], [22, 23]]], result)
def test_gradient_numerical(self):
if context.executing_eagerly():
return
with self.session():
signal_shape = (2, 128)
signal = array_ops.ones(signal_shape)
frame_length = 33
frame_step = 9
frames = shape_ops.frame(signal, frame_length, frame_step)
error = test.compute_gradient_error(
signal, signal_shape, frames, frames.shape.as_list())
self.assertLess(error, 2e-5)
def test_constant_folding(self):
"""frame should be constant foldable for constant inputs."""
if context.executing_eagerly():
return
for pad_end in [True, False]:
g = ops.Graph()
with g.as_default():
frame_length, frame_step = 32, 16
signal_shape = (2, 128)
signal = array_ops.ones(signal_shape)
frames = shape_ops.frame(signal, frame_length, frame_step,
pad_end=pad_end)
rewritten_graph = test_util.grappler_optimize(g, [frames])
self.assertEqual(1, len(rewritten_graph.node))
if __name__ == "__main__":
test.main()
@@ -0,0 +1,395 @@
# 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.
# ==============================================================================
"""Tests for spectral_ops."""
import itertools
from absl.testing import parameterized
import numpy as np
from tensorflow.python.eager import context
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 gradient_checker_v2
from tensorflow.python.ops import gradients_impl
from tensorflow.python.ops import math_ops
from tensorflow.python.ops.signal import spectral_ops
from tensorflow.python.ops.signal import window_ops
from tensorflow.python.platform import test
@test_util.run_all_in_graph_and_eager_modes
class SpectralOpsTest(test.TestCase, parameterized.TestCase):
@staticmethod
def _np_hann_periodic_window(length):
if length == 1:
return np.ones(1)
odd = length % 2
if not odd:
length += 1
window = 0.5 - 0.5 * np.cos(2.0 * np.pi * np.arange(length) / (length - 1))
if not odd:
window = window[:-1]
return window
@staticmethod
def _np_frame(data, window_length, hop_length):
num_frames = 1 + int(np.floor((len(data) - window_length) // hop_length))
shape = (num_frames, window_length)
strides = (data.strides[0] * hop_length, data.strides[0])
return np.lib.stride_tricks.as_strided(data, shape=shape, strides=strides)
@staticmethod
def _np_stft(data, fft_length, hop_length, window_length):
frames = SpectralOpsTest._np_frame(data, window_length, hop_length)
window = SpectralOpsTest._np_hann_periodic_window(window_length)
return np.fft.rfft(frames * window, fft_length)
@staticmethod
def _np_inverse_stft(stft, fft_length, hop_length, window_length):
frames = np.fft.irfft(stft, fft_length)
# Pad or truncate frames's inner dimension to window_length.
frames = frames[..., :window_length]
frames = np.pad(frames, [[0, 0]] * (frames.ndim - 1) +
[[0, max(0, window_length - frames.shape[-1])]], "constant")
window = SpectralOpsTest._np_hann_periodic_window(window_length)
return SpectralOpsTest._np_overlap_add(frames * window, hop_length)
@staticmethod
def _np_overlap_add(stft, hop_length):
num_frames, window_length = np.shape(stft)
# Output length will be one complete window, plus another hop_length's
# worth of points for each additional window.
output_length = window_length + (num_frames - 1) * hop_length
output = np.zeros(output_length)
for i in range(num_frames):
output[i * hop_length:i * hop_length + window_length] += stft[i,]
return output
def _compare(self, signal, frame_length, frame_step, fft_length, tol):
actual_stft = spectral_ops.stft(
signal, frame_length, frame_step, fft_length, pad_end=False)
signal_ph = array_ops.placeholder_with_default(signal, shape=signal.shape)
actual_stft_from_ph = spectral_ops.stft(
signal_ph, frame_length, frame_step, fft_length, pad_end=False)
actual_inverse_stft = spectral_ops.inverse_stft(
actual_stft, frame_length, frame_step, fft_length)
actual_stft, actual_stft_from_ph, actual_inverse_stft = self.evaluate(
[actual_stft, actual_stft_from_ph, actual_inverse_stft])
actual_stft_ph = array_ops.placeholder_with_default(
actual_stft, shape=actual_stft.shape)
actual_inverse_stft_from_ph = self.evaluate(
spectral_ops.inverse_stft(
actual_stft_ph, frame_length, frame_step, fft_length))
# Confirm that there is no difference in output when shape/rank is fully
# unknown or known.
self.assertAllClose(actual_stft, actual_stft_from_ph)
self.assertAllClose(actual_inverse_stft, actual_inverse_stft_from_ph)
expected_stft = SpectralOpsTest._np_stft(
signal, fft_length, frame_step, frame_length)
self.assertAllClose(expected_stft, actual_stft, rtol=tol, atol=tol)
expected_inverse_stft = SpectralOpsTest._np_inverse_stft(
expected_stft, fft_length, frame_step, frame_length)
self.assertAllClose(
expected_inverse_stft, actual_inverse_stft, rtol=tol, atol=tol)
def test_shapes(self):
signal = np.zeros((512,)).astype(np.float32)
# If fft_length is not provided, the smallest enclosing power of 2 of
# frame_length (8) is used.
stft = spectral_ops.stft(signal, frame_length=7, frame_step=8,
pad_end=True)
self.assertAllEqual([64, 5], stft.shape.as_list())
self.assertAllEqual([64, 5], self.evaluate(stft).shape)
stft = spectral_ops.stft(signal, frame_length=8, frame_step=8,
pad_end=True)
self.assertAllEqual([64, 5], stft.shape.as_list())
self.assertAllEqual([64, 5], self.evaluate(stft).shape)
stft = spectral_ops.stft(signal, frame_length=8, frame_step=8,
fft_length=16, pad_end=True)
self.assertAllEqual([64, 9], stft.shape.as_list())
self.assertAllEqual([64, 9], self.evaluate(stft).shape)
stft = spectral_ops.stft(signal, frame_length=16, frame_step=8,
fft_length=8, pad_end=True)
self.assertAllEqual([64, 5], stft.shape.as_list())
self.assertAllEqual([64, 5], self.evaluate(stft).shape)
stft = np.zeros((32, 9)).astype(np.complex64)
inverse_stft = spectral_ops.inverse_stft(stft, frame_length=8,
fft_length=16, frame_step=8)
expected_length = (stft.shape[0] - 1) * 8 + 8
self.assertAllEqual([256], inverse_stft.shape.as_list())
self.assertAllEqual([expected_length], self.evaluate(inverse_stft).shape)
@parameterized.parameters(
(512, 64, 32, 64, np.float32, 1e-4),
(512, 64, 32, 64, np.float64, 1e-8),
(512, 64, 64, 64, np.float32, 1e-4),
(512, 64, 64, 64, np.float64, 1e-8),
(512, 72, 64, 64, np.float32, 1e-4),
(512, 72, 64, 64, np.float64, 1e-8),
(512, 64, 25, 64, np.float32, 1e-4),
(512, 64, 25, 64, np.float64, 1e-8),
(512, 25, 15, 36, np.float32, 1e-4),
(512, 25, 15, 36, np.float64, 1e-8),
(123, 23, 5, 42, np.float32, 1e-4),
(123, 23, 5, 42, np.float64, 1e-8))
def test_stft_and_inverse_stft(self, signal_length, frame_length,
frame_step, fft_length, np_rtype, tol):
"""Test that spectral_ops.stft/inverse_stft match a NumPy implementation."""
signal = np.random.random(signal_length).astype(np_rtype)
self._compare(signal, frame_length, frame_step, fft_length, tol)
@parameterized.parameters(
# 87.5% overlap.
(4096, 256, 32, 256, np.float32, 1e-5, 1e-6),
(4096, 256, 32, 256, np.float64, 1e-8, 1e-8),
# 75% overlap.
(4096, 256, 64, 256, np.float32, 1e-5, 1e-6),
(4096, 256, 64, 256, np.float64, 1e-8, 1e-8),
# Odd frame hop.
(4096, 128, 25, 128, np.float32, 1e-3, 1e-6),
(4096, 128, 25, 128, np.float64, 5e-4, 1e-8),
# Odd frame length.
(4096, 127, 32, 128, np.float32, 1e-3, 1e-6),
(4096, 127, 32, 128, np.float64, 1e-3, 1e-8),
# 50% overlap.
(4096, 128, 64, 128, np.float32, 0.4, 1e-6),
(4096, 128, 64, 128, np.float64, 0.4, 1e-8))
def test_stft_round_trip(self, signal_length, frame_length, frame_step,
fft_length, np_rtype, threshold,
corrected_threshold):
# Generate a random white Gaussian signal.
signal = np.random.normal(size=signal_length).astype(np_rtype)
stft = spectral_ops.stft(signal, frame_length, frame_step, fft_length,
pad_end=False)
inverse_stft = spectral_ops.inverse_stft(stft, frame_length, frame_step,
fft_length)
inverse_stft_corrected = spectral_ops.inverse_stft(
stft, frame_length, frame_step, fft_length,
window_fn=spectral_ops.inverse_stft_window_fn(frame_step))
inverse_stft, inverse_stft_corrected = self.evaluate(
[inverse_stft, inverse_stft_corrected])
# Truncate signal to the size of inverse stft.
signal = signal[:inverse_stft.shape[0]]
# Ignore the frame_length samples at either edge.
signal = signal[frame_length:-frame_length]
inverse_stft = inverse_stft[frame_length:-frame_length]
inverse_stft_corrected = inverse_stft_corrected[
frame_length:-frame_length]
# Check that the inverse and original signal are close up to a scale
# factor.
inverse_stft_scaled = inverse_stft / np.mean(np.abs(inverse_stft))
signal_scaled = signal / np.mean(np.abs(signal))
self.assertLess(np.std(inverse_stft_scaled - signal_scaled), threshold)
# Check that the inverse with correction and original signal are close.
self.assertLess(np.std(inverse_stft_corrected - signal),
corrected_threshold)
@parameterized.parameters(
(256, 32),
(256, 64),
(128, 25),
(127, 32),
(128, 64),
(64, 64),
(64, 128),
)
def test_inverse_stft_window_fn(self, frame_length, frame_step):
"""Test that inverse_stft_window_fn has unit gain at each window phase."""
hann_window = window_ops.hann_window(frame_length, dtype=dtypes.float32)
inverse_window_fn = spectral_ops.inverse_stft_window_fn(frame_step)
inverse_window = inverse_window_fn(frame_length, dtype=dtypes.float32)
hann_window, inverse_window = self.evaluate([hann_window, inverse_window])
# Expect unit gain at each phase of the window.
product_window = hann_window * inverse_window
for i in range(frame_step):
hann_window_i = hann_window[i::frame_step]
inverse_window_i = inverse_window[i::frame_step]
# Skip if both windows are 0 (can happen for frame_length <= frame_step).
if (hann_window_i == 0.0).all() and (inverse_window_i == 0.0).all():
continue
self.assertAllClose(1.0, np.sum(product_window[i::frame_step]))
@parameterized.parameters((256, 64), (128, 32))
def test_inverse_stft_window_fn_special_case(self, frame_length, frame_step):
"""Test inverse_stft_window_fn in special overlap = 3/4 case."""
# Cases in which frame_length is an integer multiple of 4 * frame_step are
# special because they allow exact reproduction of the waveform with a
# squared Hann window (Hann window in both forward and reverse transforms).
# In the case where frame_length = 4 * frame_step, that combination
# produces a constant gain of 1.5, and so the corrected window will be the
# Hann window / 1.5.
hann_window = window_ops.hann_window(frame_length, dtype=dtypes.float32)
inverse_window_fn = spectral_ops.inverse_stft_window_fn(frame_step)
inverse_window = inverse_window_fn(frame_length, dtype=dtypes.float32)
self.assertAllClose(hann_window, inverse_window * 1.5)
@staticmethod
def _compute_stft_gradient(signal, frame_length=32, frame_step=16,
fft_length=32):
"""Computes the gradient of the STFT with respect to `signal`."""
stft = spectral_ops.stft(signal, frame_length, frame_step, fft_length)
magnitude_stft = math_ops.abs(stft)
loss = math_ops.reduce_sum(magnitude_stft)
return gradients_impl.gradients([loss], [signal])[0]
def test_gradients(self):
"""Test that spectral_ops.stft has a working gradient."""
# TODO(rjryan): Update gradient tests for Eager.
if context.executing_eagerly():
return
with self.session() as sess:
signal_length = 512
# An all-zero signal has all zero gradients with respect to the sum of the
# magnitude STFT.
empty_signal = array_ops.zeros([signal_length], dtype=dtypes.float32)
empty_signal_gradient = sess.run(
self._compute_stft_gradient(empty_signal))
self.assertTrue((empty_signal_gradient == 0.0).all())
# A sinusoid will have non-zero components of its gradient with respect to
# the sum of the magnitude STFT.
sinusoid = math_ops.sin(
2 * np.pi * math_ops.linspace(0.0, 1.0, signal_length))
sinusoid_gradient = self.evaluate(self._compute_stft_gradient(sinusoid))
self.assertFalse((sinusoid_gradient == 0.0).all())
@parameterized.parameters(
(64, 16, 8, 16, np.float32, 2e-3, 5e-4),
(64, 16, 8, 16, np.float64, 1e-8, 1e-8),
(64, 16, 16, 16, np.float32, 2e-3, 5e-4),
(64, 16, 16, 16, np.float64, 1e-8, 1e-8),
(64, 16, 7, 16, np.float32, 2e-3, 5e-4),
(64, 16, 7, 16, np.float64, 1e-8, 1e-8),
(64, 7, 4, 9, np.float32, 2e-3, 5e-4),
(64, 7, 4, 9, np.float64, 1e-8, 1e-8),
(29, 5, 1, 10, np.float32, 2e-3, 5e-4),
(29, 5, 1, 10, np.float64, 1e-8, 1e-8))
@test.disable_with_predicate(
pred=test.is_built_with_rocm,
skip_message="On ROCm, this fails with mismatches at some locations "
"(possibly due to peculiarities of rocFFT - investigate)")
def test_gradients_numerical(self, signal_length, frame_length, frame_step,
fft_length, np_rtype, forward_tol, backward_tol):
# TODO(rjryan): Investigate why STFT gradient error is so high.
signal = np.random.rand(signal_length).astype(np_rtype) * 2 - 1
def forward(signal):
return spectral_ops.stft(
signal, frame_length, frame_step, fft_length, pad_end=False)
((f_jacob_t,), (f_jacob_n,)) = gradient_checker_v2.compute_gradient(
forward, [signal])
self.assertAllClose(f_jacob_t, f_jacob_n,
rtol=forward_tol, atol=forward_tol)
def backward(stft):
return spectral_ops.inverse_stft(
stft, frame_length, frame_step, fft_length)
stft = forward(signal)
((b_jacob_t,), (b_jacob_n,)) = gradient_checker_v2.compute_gradient(
backward, [stft])
self.assertAllClose(b_jacob_t, b_jacob_n,
rtol=backward_tol, atol=backward_tol)
@parameterized.parameters(
itertools.product(
(4000,),
(256,),
(np.float32, np.float64),
("ortho", None),
("vorbis", "kaiser_bessel_derived", None),
(False, True)))
def test_mdct_round_trip(self, signal_length, frame_length, np_rtype,
norm, window_type, pad_end):
if np_rtype == np.float32:
tol = 1e-5
else:
if window_type == "kaiser_bessel_derived":
tol = 1e-6
else:
tol = 1e-8
# Generate a random white Gaussian signal.
signal = np.random.normal(size=signal_length).astype(np_rtype)
if window_type == "vorbis":
window_fn = window_ops.vorbis_window
elif window_type == "kaiser_bessel_derived":
window_fn = window_ops.kaiser_bessel_derived_window
elif window_type is None:
window_fn = None
mdct = spectral_ops.mdct(signal, frame_length, norm=norm,
window_fn=window_fn, pad_end=pad_end)
inverse_mdct = spectral_ops.inverse_mdct(mdct, norm=norm,
window_fn=window_fn)
inverse_mdct = self.evaluate(inverse_mdct)
# Truncate signal and inverse_mdct to their minimum length.
min_length = np.minimum(signal.shape[0], inverse_mdct.shape[0])
# Ignore the half_len samples at either edge.
half_len = frame_length // 2
signal = signal[half_len:min_length-half_len]
inverse_mdct = inverse_mdct[half_len:min_length-half_len]
# Check that the inverse and original signal are close.
self.assertAllClose(inverse_mdct, signal, atol=tol, rtol=tol)
def test_inverse_stft_rejects_zero_frame_length(self):
# Regression for https://github.com/tensorflow/tensorflow/issues/117843:
# frame_length=0 (or stfts.shape[-1] == 1, which makes the inferred
# fft_length 0) used to slip through the Python validation and abort
# the process inside the DUCC FFT backend (`Assertion failure: no
# zero-sized FFTs`). The Python layer should now reject these cases
# with a clean ValueError before any kernel runs.
stfts = np.zeros((1, 2, 1), dtype=np.complex64)
with self.assertRaisesRegex(ValueError, "must be a positive integer"):
spectral_ops.inverse_stft(
stfts, frame_length=0, frame_step=1, fft_length=None
)
# Explicit zero fft_length is also rejected.
with self.assertRaisesRegex(ValueError, "must be a positive integer"):
spectral_ops.inverse_stft(
stfts, frame_length=4, frame_step=1, fft_length=0
)
# Same for stft, for symmetry.
signals = np.zeros((1, 4), dtype=np.float32)
with self.assertRaisesRegex(ValueError, "must be a positive integer"):
spectral_ops.stft(signals, frame_length=0, frame_step=1)
with self.assertRaisesRegex(ValueError, "must be a positive integer"):
spectral_ops.stft(signals, frame_length=4, frame_step=1, fft_length=0)
if __name__ == "__main__":
test.main()
@@ -0,0 +1,44 @@
# 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.
# ==============================================================================
"""Test utilities for tf.signal."""
from tensorflow.core.protobuf import config_pb2
from tensorflow.python.grappler import tf_optimizer
from tensorflow.python.training import saver
def grappler_optimize(graph, fetches=None, config_proto=None):
"""Tries to optimize the provided graph using grappler.
Args:
graph: A `tf.Graph` instance containing the graph to optimize.
fetches: An optional list of `Tensor`s to fetch (i.e. not optimize away).
Grappler uses the 'train_op' collection to look for fetches, so if not
provided this collection should be non-empty.
config_proto: An optional `tf.compat.v1.ConfigProto` to use when rewriting
the graph.
Returns:
A `tf.compat.v1.GraphDef` containing the rewritten graph.
"""
if config_proto is None:
config_proto = config_pb2.ConfigProto()
config_proto.graph_options.rewrite_options.min_graph_nodes = -1
if fetches is not None:
for fetch in fetches:
graph.add_to_collection('train_op', fetch)
metagraph = saver.export_meta_graph(graph_def=graph.as_graph_def())
return tf_optimizer.OptimizeGraph(config_proto, metagraph)
@@ -0,0 +1,210 @@
# 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.
# ==============================================================================
"""Tests for window_ops."""
import functools
import itertools
from absl.testing import parameterized
import numpy as np
from tensorflow.python.eager import context
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import ops
from tensorflow.python.framework import test_util as tf_test_util
from tensorflow.python.kernel_tests.signal import test_util
from tensorflow.python.ops.signal import window_ops
from tensorflow.python.platform import test
_TF_DTYPE_TOLERANCE = [(dtypes.float16, 1e-2),
(dtypes.float32, 1e-6),
(dtypes.float64, 1e-9)]
_WINDOW_LENGTHS = [1, 2, 3, 4, 5, 31, 64, 128]
_MDCT_WINDOW_LENGTHS = [4, 16, 256]
def _scipy_raised_cosine(length, symmetric=True, a=0.5, b=0.5):
"""A simple implementation of a raised cosine window that matches SciPy.
https://en.wikipedia.org/wiki/Window_function#Hann_window
https://github.com/scipy/scipy/blob/v0.14.0/scipy/signal/windows.py#L615
Args:
length: The window length.
symmetric: Whether to create a symmetric window.
a: The alpha parameter of the raised cosine window.
b: The beta parameter of the raised cosine window.
Returns:
A raised cosine window of length `length`.
"""
if length == 1:
return np.ones(1)
odd = length % 2
if not symmetric and not odd:
length += 1
window = a - b * np.cos(2.0 * np.pi * np.arange(length) / (length - 1))
if not symmetric and not odd:
window = window[:-1]
return window
@tf_test_util.run_all_in_graph_and_eager_modes
class WindowOpsTest(test.TestCase, parameterized.TestCase):
def _check_mdct_window(self, window, tol=1e-6):
"""Check that an MDCT window satisfies necessary conditions."""
# We check that the length of the window is a multiple of 4 and
# for symmetry of the window and also Princen-Bradley condition which
# requires that w[n]^2 + w[n + N//2]^2 = 1 for an N length window.
wlen = int(np.shape(window)[0])
assert wlen % 4 == 0
half_len = wlen // 2
squared_sums = window[:half_len]**2 + window[half_len:]**2
self.assertAllClose(squared_sums, np.ones((half_len,)),
tol, tol)
sym_diff = window[:half_len] - window[-1:half_len-1:-1]
self.assertAllClose(sym_diff, np.zeros((half_len,)),
tol, tol)
def _compare_window_fns(self, np_window_fn, tf_window_fn, window_length,
periodic, tf_dtype_tol):
tf_dtype, tol = tf_dtype_tol
np_dtype = tf_dtype.as_numpy_dtype
expected = np_window_fn(window_length,
symmetric=not periodic).astype(np_dtype)
actual = tf_window_fn(window_length, periodic=periodic,
dtype=tf_dtype)
self.assertAllClose(expected, actual, tol, tol)
@parameterized.parameters(
itertools.product(
_WINDOW_LENGTHS,
(4., 8., 10., 12.),
_TF_DTYPE_TOLERANCE))
def test_kaiser_window(self, window_length, beta, tf_dtype_tol):
"""Check that kaiser_window matches np.kaiser behavior."""
self.assertAllClose(
np.kaiser(window_length, beta),
window_ops.kaiser_window(window_length, beta, tf_dtype_tol[0]),
tf_dtype_tol[1], tf_dtype_tol[1])
@parameterized.parameters(
itertools.product(
_WINDOW_LENGTHS, (4.0, 8.0, 10.0, 12.0), _TF_DTYPE_TOLERANCE
)
)
def test_kaiser_window_negative_beta(self, window_length, beta, tf_dtype_tol):
"""Check that kaiser_window handles negative beta same as positive beta."""
pos_beta_win = window_ops.kaiser_window(
window_length, beta, tf_dtype_tol[0]
)
neg_beta_win = window_ops.kaiser_window(
window_length, -beta, tf_dtype_tol[0]
)
self.assertAllClose(
pos_beta_win, neg_beta_win, tf_dtype_tol[1], tf_dtype_tol[1]
)
@parameterized.parameters(
itertools.product(_WINDOW_LENGTHS, (False, True), _TF_DTYPE_TOLERANCE)
)
def test_hann_window(self, window_length, periodic, tf_dtype_tol):
"""Check that hann_window matches scipy.signal.hann behavior."""
# The Hann window is a raised cosine window with parameters alpha=0.5 and
# beta=0.5.
# https://en.wikipedia.org/wiki/Window_function#Hann_window
self._compare_window_fns(
functools.partial(_scipy_raised_cosine, a=0.5, b=0.5),
window_ops.hann_window, window_length, periodic, tf_dtype_tol)
@parameterized.parameters(
itertools.product(
_WINDOW_LENGTHS,
(False, True),
_TF_DTYPE_TOLERANCE))
def test_hamming_window(self, window_length, periodic, tf_dtype_tol):
"""Check that hamming_window matches scipy.signal.hamming's behavior."""
# The Hamming window is a raised cosine window with parameters alpha=0.54
# and beta=0.46.
# https://en.wikipedia.org/wiki/Window_function#Hamming_window
self._compare_window_fns(
functools.partial(_scipy_raised_cosine, a=0.54, b=0.46),
window_ops.hamming_window, window_length, periodic, tf_dtype_tol)
@parameterized.parameters(
itertools.product(
(window_ops.hann_window, window_ops.hamming_window,
window_ops.kaiser_window, window_ops.kaiser_bessel_derived_window,
window_ops.vorbis_window),
(False, True),
_TF_DTYPE_TOLERANCE))
def test_constant_folding(self, window_fn, periodic, tf_dtype_tol):
"""Window functions should be constant foldable for constant inputs."""
if context.executing_eagerly():
return
g = ops.Graph()
with g.as_default():
try:
window = window_fn(100, periodic=periodic, dtype=tf_dtype_tol[0])
except TypeError:
window = window_fn(100, dtype=tf_dtype_tol[0])
rewritten_graph = test_util.grappler_optimize(g, [window])
self.assertLen(rewritten_graph.node, 1)
@parameterized.parameters(
itertools.product(
_MDCT_WINDOW_LENGTHS,
_TF_DTYPE_TOLERANCE))
def test_vorbis_window(self, window_length, tf_dtype_tol):
"""Check if vorbis windows satisfy MDCT window conditions."""
self._check_mdct_window(window_ops.vorbis_window(window_length,
dtype=tf_dtype_tol[0]),
tol=tf_dtype_tol[1])
@parameterized.parameters(
itertools.product(
_MDCT_WINDOW_LENGTHS,
(4., 8., 10., 12.),
_TF_DTYPE_TOLERANCE))
def test_kaiser_bessel_derived_window(self, window_length, beta,
tf_dtype_tol):
"""Check if Kaiser-Bessel derived windows satisfy MDCT window conditions."""
self._check_mdct_window(window_ops.kaiser_bessel_derived_window(
window_length, beta=beta, dtype=tf_dtype_tol[0]), tol=tf_dtype_tol[1])
@parameterized.parameters(
itertools.product(
_MDCT_WINDOW_LENGTHS, (4.0, 8.0, 10.0, 12.0), _TF_DTYPE_TOLERANCE
)
)
def test_kaiser_bessel_derived_window_negative_beta(
self, window_length, beta, tf_dtype_tol
):
"""Check that it handles negative beta same as positive beta."""
pos_beta_win = window_ops.kaiser_bessel_derived_window(
window_length, beta, tf_dtype_tol[0]
)
neg_beta_win = window_ops.kaiser_bessel_derived_window(
window_length, -beta, tf_dtype_tol[0]
)
self.assertAllClose(
pos_beta_win, neg_beta_win, tf_dtype_tol[1], tf_dtype_tol[1]
)
if __name__ == '__main__':
test.main()