WDM Walkthrough¶
This notebook provides a quick walkthrough of the WDM transform API. It demonstrates how to
- create a
TimeSeries - transform it into packed
WDMcoefficients - invert back to the time domain
- compare the reconstruction error
- show a few demo plots
- report simple timing numbers
In [1]:
Copied!
import subprocess
import sys
from time import perf_counter
import matplotlib.pyplot as plt
import numpy as np
if "google.colab" in sys.modules:
subprocess.run(
[
sys.executable,
"-m",
"pip",
"install",
"-q",
"git+https://github.com/pywavelet/wdm_transform.git",
],
check=True,
)
from wdm_transform import TimeSeries, WDM
from wdm_transform.plotting import plot_spectrogram
nt = 32
nf = 16
dt = 0.1
n_total = nt * nf
times = np.arange(n_total) * dt
signal = np.sin(2 * np.pi * times * 0.08) + 0.6 * np.exp(-((times - times.max() / 2.0) ** 2) / (times.max() / 6.0) ** 2) * np.cos(2 * np.pi * times * (0.05 + 0.04 * times / times.max()))
series = TimeSeries(signal, dt=dt)
coeffs = WDM.from_time_series(series, nt=nt)
recovered = coeffs.to_time_series()
max_abs_error = np.max(np.abs(recovered.data - series.data))
coeff_shape = coeffs.shape
print(f"WDM coefficient shape: {coeff_shape}")
print(f"Max abs reconstruction error: {max_abs_error:.3e}")
import subprocess
import sys
from time import perf_counter
import matplotlib.pyplot as plt
import numpy as np
if "google.colab" in sys.modules:
subprocess.run(
[
sys.executable,
"-m",
"pip",
"install",
"-q",
"git+https://github.com/pywavelet/wdm_transform.git",
],
check=True,
)
from wdm_transform import TimeSeries, WDM
from wdm_transform.plotting import plot_spectrogram
nt = 32
nf = 16
dt = 0.1
n_total = nt * nf
times = np.arange(n_total) * dt
signal = np.sin(2 * np.pi * times * 0.08) + 0.6 * np.exp(-((times - times.max() / 2.0) ** 2) / (times.max() / 6.0) ** 2) * np.cos(2 * np.pi * times * (0.05 + 0.04 * times / times.max()))
series = TimeSeries(signal, dt=dt)
coeffs = WDM.from_time_series(series, nt=nt)
recovered = coeffs.to_time_series()
max_abs_error = np.max(np.abs(recovered.data - series.data))
coeff_shape = coeffs.shape
print(f"WDM coefficient shape: {coeff_shape}")
print(f"Max abs reconstruction error: {max_abs_error:.3e}")
WDM coefficient shape: (32, 17) Max abs reconstruction error: 5.107e-15
The same packed coefficients can also be reconstructed back into the FFT domain:
In [2]:
Copied!
original_fft = series.to_frequency_series()
reconstructed_fft = coeffs.to_frequency_series()
fft_error = np.max(np.abs(reconstructed_fft.data - original_fft.data))
print(f"Max abs FFT reconstruction error: {fft_error:.3e}")
original_fft = series.to_frequency_series()
reconstructed_fft = coeffs.to_frequency_series()
fft_error = np.max(np.abs(reconstructed_fft.data - original_fft.data))
print(f"Max abs FFT reconstruction error: {fft_error:.3e}")
Max abs FFT reconstruction error: 4.590e-13
Demo plots¶
In [3]:
Copied!
fig, axes = plt.subplots(3, 1, figsize=(10, 10))
series.plot(ax=axes[0], color="tab:blue")
axes[0].set_title("Input Time Series")
series.to_frequency_series().plot(ax=axes[1], color="tab:red")
axes[1].set_title("FFT Magnitude")
coeffs.plot(ax=axes[2])
axes[2].set_title("Packed WDM Grid")
fig.tight_layout()
fig, axes = plt.subplots(3, 1, figsize=(10, 10))
series.plot(ax=axes[0], color="tab:blue")
axes[0].set_title("Input Time Series")
series.to_frequency_series().plot(ax=axes[1], color="tab:red")
axes[1].set_title("FFT Magnitude")
coeffs.plot(ax=axes[2])
axes[2].set_title("Packed WDM Grid")
fig.tight_layout()
For comparison, here is a standard spectrogram of the same signal.
In [4]:
Copied!
fig, ax = plt.subplots(figsize=(10, 4))
plot_spectrogram(series, ax=ax, spec_kwargs={"nperseg": 64, "noverlap": 48})
ax.set_title("Reference Spectrogram")
fig.tight_layout()
fig, ax = plt.subplots(figsize=(10, 4))
plot_spectrogram(series, ax=ax, spec_kwargs={"nperseg": 64, "noverlap": 48})
ax.set_title("Reference Spectrogram")
fig.tight_layout()
Timing snapshot¶
These are small local timings from the docs build environment. They are just a quick smoke check, not a benchmark suite.
In [5]:
Copied!
repeats = 20
start = perf_counter()
for _ in range(repeats):
coeffs_bench = WDM.from_time_series(series, nt=nt)
forward_avg_ms = (perf_counter() - start) * 1e3 / repeats
start = perf_counter()
for _ in range(repeats):
_ = coeffs_bench.to_time_series()
inverse_avg_ms = (perf_counter() - start) * 1e3 / repeats
print(f"Average forward transform time over {repeats} runs: {forward_avg_ms:.3f} ms")
print(f"Average inverse transform time over {repeats} runs: {inverse_avg_ms:.3f} ms")
repeats = 20
start = perf_counter()
for _ in range(repeats):
coeffs_bench = WDM.from_time_series(series, nt=nt)
forward_avg_ms = (perf_counter() - start) * 1e3 / repeats
start = perf_counter()
for _ in range(repeats):
_ = coeffs_bench.to_time_series()
inverse_avg_ms = (perf_counter() - start) * 1e3 / repeats
print(f"Average forward transform time over {repeats} runs: {forward_avg_ms:.3f} ms")
print(f"Average inverse transform time over {repeats} runs: {inverse_avg_ms:.3f} ms")
Average forward transform time over 20 runs: 0.534 ms Average inverse transform time over 20 runs: 0.266 ms