torchwavelets
Differentiable discrete wavelet transforms for PyTorch.
torchwavelets provides 1D and 2D discrete wavelet transforms (DWT), their
inverses, the 2D dual-tree complex wavelet transform (DTCWT) with native
complex tensors, DTCWT scattering networks, the stationary wavelet transform
(SWT) and wavelet packets — built to respect PyTorch's precision, device and
autocast semantics.
Why another wavelet library?
The reference implementation, fbcotter/pytorch_wavelets,
is unmaintained (last commit 2023) and has well-known problems:
- AMP / mixed precision is broken — the custom
autograd.Functions run their backward in float32 against float16 gradients (expected scalar type Half but found Float, issue #63). - Filters are baked in at construction time via
torch.get_default_dtype(); feeding a float16 tensor to a float32-built transform fails. - Boundary handling does not match PyWavelets exactly and fails for small signals; the stationary wavelet inverse is dead code.
- No 1D/3D DTCWT, no wavelet packets, no native complex tensors, stale CI.
torchwavelets fixes these at the design level:
Design principles
- Output dtype == input dtype. fp16/bf16/fp32/fp64 in → the same out.
Filters are resolved lazily per
(dtype, device)and cached; nothing is mutated during forward, sotorch.compile, DDP and mixed-precision training all behave. (One documented exception: the DTCWT family computes half and bfloat16 inputs in float32 — complex16 kernels do not exist — and runs in an autocast-disabled scope, returning float32.) - Autocast-native. The transforms are built from plain
conv2d/conv_transpose2dops (no customautograd.Function), sotorch.autocastandtorch.amp.GradScalerwork without any special-casing. The DTCWT family computes in an autocast-disabled scope (its complex bandpasses have no bf16 kernels) and returns float32 for half/bf16 inputs. - PyWavelets-exact boundary handling. All modes (
zero,symmetric,reflect,periodization,periodic,constant,replicate) matchpywt.dwt/pywt.idwtbit-for-bit, including odd-length and tiny signals (index-based padding, no size limits). - Pure torch ops. Index-based padding keeps everything differentiable,
higher-order differentiable, and
torch.compile-friendly.
Installation
pip install -e .[test,examples]
Usage
import torch
import torchwavelets as tw
x = torch.randn(4, 3, 64, 64) # (N, C, H, W)
# Functional API
yl, yh = tw.dwt2d(x, wave="db4", mode="symmetric", level=3)
xr = tw.idwt2d((yl, yh), wave="db4", mode="symmetric")
# Module API
dwt = tw.DWT2D(wave="db4", J=3, mode="symmetric")
yl, yh = dwt(x)
xr = tw.IDWT2D(wave="db4", mode="symmetric")((yl, yh))
# 1D
y = torch.randn(4, 3, 1024)
yl, yh = tw.dwt1d(y, wave="db6", level=3)
# Mixed precision just works
x16 = x.half()
yl16, _ = dwt(x16) # -> float16 output
with torch.autocast("cuda", dtype=torch.bfloat16):
yl_bf, _ = dwt(x.float()) # -> bfloat16 output (on CUDA)
Compatibility with pytorch_wavelets
from torchwavelets.compat import DWTForward, DWTInverse, DWT1DForward, DWT1DInverse
xfm = DWTForward(J=3, wave="db3", mode="zero")
The compat layer keeps the old API but fixes the old semantics: a float16 input works regardless of the dtype used when the transform was constructed.
Status
- DWT 1D / 2D, analysis + synthesis, all boundary modes
- float16 / bfloat16 / float32 / float64
- PyWavelets numerical parity tests, gradient checks, AMP tests
-
torch.compile-compatible module API (fullgraph=True) - Device-portable: CPU, CUDA, multi-GPU, hybrid and Grace (GH200) nodes
- DTCWT 1D / 2D / 3D (native complex tensors,
near_sym_*/qshift_*banks) - DTCWT scattering layers (
ScatLayer,ScatLayerj2) - General arbitrary-order scattering network (
DTCWTScattering) - Stationary (undecimated) wavelet transform 1D / 2D
- Wavelet packets 1D / 2D (
WaveletPacket,WaveletPacket2D) - Scattering network (deeper, arbitrary-order)
Examples and applications
Self-contained, reproducible demos with real data (photograph, CIFAR-10,
MIT-BIH ECG) — run python examples/<script>.py from the repository root
(data is cached in ~/data/torchwavelets, figures land in docs/figures/).
See examples/README.md for the quick reference and
docs/examples.md for the full illustrated walkthrough
with results and figures.
Application 1 — image denoising (DWT vs DTCWT). A photograph corrupted with Gaussian noise (sigma = 25/255, PSNR 20.3 dB) is restored by per-level 3-sigma soft thresholding of the wavelet coefficients. The redundant, nearly shift-invariant DTCWT gains +3.1 dB PSNR over the critically sampled DWT (29.3 dB vs 26.2 dB).
Application 2 — ECG denoising (SWT vs DWT). Real 360 Hz ECG (MIT-BIH Arrhythmia record 100) plus 50 uV noise (SNR 10.6 dB), restored by the same per-level soft thresholding. The undecimated SWT gains +1.9 dB SNR over the DWT (14.9 dB vs 13.0 dB).
Application 3 — CIFAR-10 classification with scattering features. A fixed
(untrained) DTCWT scattering front-end (DTCWTScattering(J=2, max_order=2))
extracts 588 shift-invariant dimensions per 32x32 image (147 invariants,
2x2-average pooled); a logistic regression reaches 56.5 % test accuracy,
versus 31.6 % on 3072 raw pixels — with 5x fewer dimensions and no
learned front-end.
torch.compile
The module API (DWT2D, IDWT2D, DWT1D, IDWT1D) resolves its filters
into buffers at construction time and runs only torch ops in forward, so it
is fully traceable:
import torch
import torchwavelets as tw
dwt = torch.compile(tw.DWT2D(wave="db4", J=3, mode="symmetric"), fullgraph=True)
yl, yh = dwt(x)
The functional API (tw.dwt2d(x, wave=...)) is eager-only for the filter
resolution step; use the module API under torch.compile.
Ecosystem notes
- Serialization: all transforms are plain
nn.Modules —state_dictsave/load andtorch.save/torch.loadwork as usual. - DataLoader / datasets: the module API takes batched
(N, C, ...)tensors, so it slots directly intoDataLoaderpipelines. torch.func:jacrev/jacfwdand double-backward work through the transforms (all plain torch ops).torch.distributed(DDP): modules hold no state beyond filter buffers and never mutate inputs, so DDP training works (smoke-tested with gloo).torch.jit.scriptis not supported (the transforms use dynamic loops over levels); usetorch.compileinstead, which is fully supported.torch.vmap: the API is already batched (N, C, ...required), so vmap is unnecessary; the batch dimension plays that role.torch.inference_mode: supported everywhere.
Device portability
Tests parametrize over every available device (CPU plus all CUDA devices),
so the suite automatically covers hybrid CPU/GPU nodes and NVIDIA
Grace-Hopper (ARM64) machines; GPU results are reported in the CHANGELOG.
Credits and acknowledgements
torchwavelets is an independent implementation, but it stands on the
shoulders of the wavelet community. The parts of the code that are ports or
adaptations are credited here; everything else was written from the
mathematics and the public conventions.
- Nick Kingsbury (Cambridge University) — the dual-tree complex wavelet
transform itself. The filter banks (
near_sym_a/b,qshift_a/b, ..., shipped intorchwavelets/dtcwt/data/), the quarter-sample-shift design, the orientation convention and thesymmetric(half-sample) boundary handling originate from his DTCWT toolbox, which is free for research use only. The 3-D DTCWT follows the same work-grid construction. fbcotter/pytorch_wavelets— the unmaintained reference implementation this package replaces. Its low-level DTCWT operations (colfilter,rowfilter,coldfilt,rowdfilt,colifilt,rowifilt,q2c,c2q, orientation assembly), the two-scale scattering layers (ScatLayer,ScatLayerj2) and thepytorch_wavelets-compatible API (torchwavelets.compat) are ports of that codebase, rewritten on plain torch ops to fix its AMP, dtype and boundary problems.- PyWavelets — the parity oracle
for the DWT, SWT and wavelet packets. The boundary modes, coefficient
shapes,
swtconventions, packet paths/sequency ordering and the "drop extra sample" reconstruction rule all mirror PyWavelets exactly; the test suite is bit-exact against it. dtcwt(numpy reference implementation) — the DTCWT parity oracle for 1-D/2-D/3-D; the test suite is bit-exact against it to ~1e-15.- Stéphane Mallat — the scattering-transform theory. The scattering
layers and the arbitrary-order
DTCWTScatteringnetwork implement the Mallat recipe (wavelet modulus + smoothing) with a DTCWT front-end. - PyTorch ecosystem —
torch.compile, autocast andnn.Moduleconventions; the tests follow the standard PyTorch-ecosystem usage patterns (serialization, DataLoader,torch.func, DDP).
If you use this package in research, please cite it — see
CITATION.cff.
Primary references
- N. G. Kingsbury, The dual-tree complex wavelet transform: a new efficient tool for image restoration and enhancement, EUSIPCO 1998.
- N. G. Kingsbury, Complex wavelets for shift invariant analysis and filtering of signals, Applied and Computational Harmonic Analysis, 2001.
- I. W. Selesnick, R. G. Baraniuk, N. G. Kingsbury, The dual-tree complex wavelet transform, IEEE Signal Processing Magazine, 2005.
- S. Mallat, Group invariant scattering, Communications on Pure and Applied Mathematics, 2012.
- I. Daubechies, Ten Lectures on Wavelets, SIAM, 1992.
License
BSD-3-Clause (see LICENSE). See
Credits and acknowledgements for the
upstream sources: the DTCWT filter banks originate from Nick Kingsbury's
MATLAB toolbox, which is free for research use only — relevant if you
plan commercial use of the DTCWT parts.
Metadata
Release files for torchwavelets 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| torchwavelets-0.1.0.tar.gz | 9.6 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| torchwavelets-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 9.7 MB
Release files / torchwavelets-0.1.0.tar.gz
| Download URL | torchwavelets-0.1.0.tar.gz |
|---|---|
| Size | 9.6 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0b604593fe5a1e80008379aabb3805cad9711b9acb593e23aca5f2f9e8259c23
|
|
BLAKE2b-256 checksum How to use checksums |
af7141b2e078fff2332b4c70638755db207b048928d4a3c0052eb0d54700dc98
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency logRelease files / torchwavelets-0.1.0-py3-none-any.whl
| Download URL | torchwavelets-0.1.0-py3-none-any.whl |
|---|---|
| Size | 68.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b67a587c1a90904f5b14e91649f4463389ab80d0424ce25ea07cd24419c81df7
|
|
BLAKE2b-256 checksum How to use checksums |
fb25fd98f4cebc2e34e2c046f995d5f0e2c1f1cb4f61e1ddbc762eb129135d42
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency log