Quickstart¶
This quickstart shows the core idea of lattice-dsp: represent recursive
filters with reflection/PARCOR coefficients and lattice-ladder structures rather
than treating IIR denominator coefficients as unconstrained parameters.
For a full guided walkthrough, build the generated tutorial gallery:
./scripts/build_docs_with_results.sh
xdg-open docs/_build/html/examples/index.html
Reflection coefficients to a stable IIR denominator¶
Reflection coefficients satisfy abs(k_i) < 1. For scalar all-pole
denominators, this gives a stability-aware parameterization: bounded reflection
coefficients map to stable denominators.
import numpy as np
import lattice_dsp as ld
k = np.array([0.7, -0.4, 0.25])
a = ld.reflection_to_denominator(k)
restored = ld.denominator_to_reflection(a)
print("denominator", a)
print("restored", restored)
Lattice-ladder realization¶
A lattice-ladder filter uses reflection coefficients for the recursive part and ladder taps for the numerator/output combination. This is the scalar IIR path that most directly motivates the package.
filt = ld.LatticeLadderIIR([0.35, -0.2, 0.1], [0.1, -0.05, 0.2, 0.8])
x = np.random.default_rng(0).standard_normal(1024)
y = filt.process(x)
Adaptive recursive filtering¶
The adaptive examples are intended to demonstrate stable recursive identification. They are not generic black-box optimizers; the point is to update a recursive model while controlling the denominator through lattice coordinates.
model = ld.LatticeLadderRLS(order=3, forgetting_factor=0.99, delta=100.0)
x = np.random.default_rng(0).standard_normal(4096)
d = np.convolve(x, [0.25, -0.15, 0.65], mode="full")[: len(x)]
y, e = model.process(x, d)
print("tail MSE", np.mean(e[-512:] ** 2))
Matrix/MIMO lattice example¶
The matrix lattice utilities explore multichannel all-pass and paraunitary behavior. They are useful for experiments in compact MIMO responses, energy-preserving transforms, multichannel AR diagnostics, and ML-inspired unitary layers.
rng = np.random.default_rng(0)
K = ld.contractive_matrix_from_raw(
0.2 * (rng.standard_normal((4, 4)) + 1j * rng.standard_normal((4, 4))),
margin=1e-6,
)
R = ld.unitary_polar_factor(rng.standard_normal((4, 4)) + 1j * rng.standard_normal((4, 4)))
filt = ld.MatrixLatticeAllPass([K], R)
H = filt.frequency_response(np.linspace(0, np.pi, 128))
y = filt.to_online_filter().process(np.ones((64, 3)))
# H[w]^H H[w] should be close to identity for every frequency.
I = np.eye(4)
err = max(np.linalg.norm(h.conj().T @ h - I) for h in H)
print(err)
Causal MIMO lattice prediction¶
The multichannel AR layer can also run as a sample-by-sample lattice predictor after its matrix reflection coefficients have been fitted.
x_train = np.random.default_rng(1).standard_normal((4096, 3))
r = ld.multichannel_autocorrelation(x_train, order=3)
fit = ld.block_levinson_durbin(r, order=3)
pred = ld.MIMOLatticePredictor.from_levinson(fit)
y_hat = pred.predict() # before observing y_t
err = pred.update(x_train[0]) # after observing y_t
Flagship robustness tutorial¶
After installing the docs/examples extras, build the generated tutorials and open the LMS/H∞ robustness tutorial:
./scripts/build_docs_with_results.sh
xdg-open docs/_build/html/examples/generated/hinf_lms_reproduction.html
The rendered page is also available from the docs navigation at Examples tutorials / LMS as a minimax robust filter. This tutorial explains why LMS can be interpreted as a minimax robust filter, not only as an approximate least-squares recursion.