Elliptic Filter (Cauer Filter): Design Principles and Python Implementation

Elliptic (Cauer) IIR filter design in Python with scipy.signal.ellip, scipy.signal.ellipord, scipy.signal.freqz, and scipy.signal.filtfilt. Covers Jacobi elliptic functions sn/cn/dn and equiripple behavior, lowpass / highpass / bandpass / bandstop design, minimum-order comparison against Butterworth and Chebyshev I/II via buttord, cheb1ord, and cheb2ord, and choosing filtfilt vs lfilter for zero-phase filtering.

Introduction

The elliptic filter (also known as the Cauer filter) is an IIR filter that exhibits equiripple behavior in both the passband and the stopband. Among all IIR filters of the same order, it achieves the steepest transition band:

\[\text{Elliptic} > \text{Chebyshev (Type I \& II)} > \text{Butterworth}\]

The trade-off is that both the passband and stopband contain ripples. This article covers the mathematical background and practical design using SciPy.

This dual-band equiripple property traces back to a problem solved in 1877 by the Russian mathematician Yegor Zolotarev (1847–1878), a student of Chebyshev: the best uniform rational approximation on two separated intervals of the real line, now known as the Zolotarev problem. Zolotarev learned elliptic function theory from Weierstrass during a visit to Berlin and applied it to solve this problem. The first application of this solution to electrical filter design was by Wilhelm Cauer in 1933 — hence the alternative name “Cauer filter.”

Frequency Response

The squared magnitude response of an elliptic filter is:

\[|H(j\Omega)|^2 = \frac{1}{1 + \varepsilon^2 R_n^2(\xi, \Omega/\Omega_p)} \tag{1}\]

where:

  • \(\varepsilon\) : passband ripple parameter (\(\varepsilon^2 = 10^{R_p/10} - 1\) )
  • \(R_n(\xi, x)\) : Jacobi elliptic rational function of order \(n\)
  • \(\xi\) : selectivity parameter (\(\xi = \Omega_p / \Omega_s > 1\) )
  • \(n\) : filter order

Jacobi Elliptic Rational Function: Why Both Bands Become Equiripple

Type I Chebyshev filters achieve passband equiripple using the Chebyshev polynomial \(T_n(x) = \cos(n \arccos x)\) . The crux of this construction is the substitution \(x = \cos\theta\) . As long as \(\theta\) is real, \(\cos\) has only a single real period (\(2\pi\) ), so for \(|x| \le 1\) (real \(\theta\) ) it oscillates with equal amplitude, but for \(|x| > 1\) , \(\theta\) becomes purely imaginary and \(\cos\theta \to \cosh u\) , which grows monotonically. This is exactly why Chebyshev Type I’s stopband can only be monotonically decreasing. Type II applies the same construction to the reciprocal variable \(\Omega_s/\Omega\) , moving the equiripple region to the stopband — but the passband then becomes monotonic. Either way, the same trigonometric substitution can only deliver equiripple behavior on one side, never both.

The elliptic filter breaks this constraint simultaneously. Achieving equiripple on both \(|x|\le 1\) and \(|x|\ge \xi\) requires replacing \(\cos\theta\) (a function with only a single real period) with a substitution based on a doubly-periodic (elliptic) function, which has both a real and an imaginary period. The Jacobi elliptic function \(\text{sn}(u,k)\) has a real period \(4K(k)\) and an imaginary period \(2iK'(k)\) (where \(K'(k)=K(\sqrt{1-k^2})\) is the complementary complete elliptic integral); the periodicity along the real axis produces the passband ripple, while the periodicity along the imaginary axis produces the stopband ripple. Using this double periodicity, one constructs the degree-\(n\) rational function

\[ R_n(\xi, x) = \text{cd}\!\left(n\, \frac{K(1/\xi)}{K(k)}\, \text{cd}^{-1}(x, k),\ k\right) \]

(where \(\text{cd} = \text{cn}/\text{dn}\) is a Jacobi elliptic function, and \(k\) is a modulus determined by \(\xi\) ), which satisfies:

  • \(|x| \le 1 \Rightarrow |R_n(\xi, x)| \le 1\) , oscillating with equal amplitude \(n\) times inside the interval (passband equiripple)
  • \(|x| \ge \xi \Rightarrow |R_n(\xi, x)| \ge \xi\) , oscillating with equal amplitude inside the interval (stopband equiripple)

This \(R_n(\xi,x)\) is the Zolotarev rational function — a generalization of Chebyshev’s “single-period, polynomial” equiripple theory to a “doubly-periodic, rational-function” setting (Orfanidis, 2006). Substituting this \(R_n\) into eq. (1) yields an elliptic filter’s amplitude response with equiripple behavior in both bands.

Order Determination

The minimum order required to meet specifications (\(\Omega_p\) , \(\Omega_s\) , \(R_p\) , \(R_s\) ) is:

\[n \ge \frac{K(1/\xi) \cdot K(\sqrt{1-1/\xi_s^2})}{K(\xi_s) \cdot K(\sqrt{1-1/\xi^2})} \tag{2}\]

where \(K(\cdot)\) is the complete elliptic integral of the first kind. In practice, scipy.signal.ellipord computes this automatically.

Deriving Eq. (2): The Order Formula via the Elliptic Nome

We can derive why eq. (2) holds using a quantity called the nome of an elliptic function. Define:

  • Selectivity factor: \(k = \Omega_p / \Omega_s\) (\(0 < k < 1\) ; corresponds to \(1/\xi\) in eq. (2))
  • Discrimination factor:
\[ k_1 = \frac{\varepsilon}{\varepsilon_1}, \qquad \varepsilon = \sqrt{10^{R_p/10}-1}, \qquad \varepsilon_1 = \sqrt{10^{R_s/10}-1} \]
  • Complementary modulus \(k' = \sqrt{1-k^2}\) , \(K'(k) = K(k')\)
  • Elliptic nome: \(q(k) = \exp(-\pi K'(k)/K(k))\)

The Zolotarev rational function \(R_n(\xi,x)\) from the previous section maps an “input” configuration with modulus \(k\) to an “output” configuration with modulus \(k_1\) through an \(n\) -fold composition. This \(n\) -fold composition is the elliptic-function analogue of the repeated double-angle identity \(\cos(n\theta) = T_n(\cos\theta)\) (equivalent to \(n\) applications of the Landen transformation), and acting on the nome it takes the simple form of a power law:

\[q(k_1) = \big[q(k)\big]^{\,n}\]

(Orfanidis, 2006). Solving this for \(n\) gives:

\[ n = \frac{\ln q(k_1)}{\ln q(k)} = \frac{K(k)\, K'(k_1)}{K'(k)\, K(k_1)} \]

which matches eq. (2) once we substitute \(\xi = 1/k\) and \(\xi_s = 1/k_1\) . Since equality gives the (real-valued) order that exactly meets specifications, the order actually used is the smallest integer \(\ge n\) (i.e., \(\lceil n \rceil\) ).

Numerical verification: for \(f_p=100\) Hz, \(f_s=150\) Hz, \(R_p=1\) dB, \(R_s=60\) dB, we have \(k = 100/150 = 0.6667\) , \(\varepsilon = 0.5088\) , \(\varepsilon_1 = 999.9995\) , giving \(k_1 = \varepsilon/\varepsilon_1 = 5.088 \times 10^{-4}\) . Computing \(K, K'\) with scipy.special.ellipk:

import numpy as np
from scipy.special import ellipk
from scipy import signal

rp, rs = 1.0, 60.0
fp, fs_stop = 100, 150

eps  = np.sqrt(10**(rp/10) - 1)
eps1 = np.sqrt(10**(rs/10) - 1)
k  = fp / fs_stop
k1 = eps / eps1

def K(m):
    return ellipk(m**2)  # scipy.special.ellipk takes m = k^2

Kk, Kpk   = K(k),  K(np.sqrt(1 - k**2))
Kk1, Kpk1 = K(k1), K(np.sqrt(1 - k1**2))

n_formula = (Kk * Kpk1) / (Kpk * Kk1)
print(f"k={k:.4f}  k1={k1:.6e}")
print(f"n (real-valued) = {n_formula:.4f}  -> order used = {int(np.ceil(n_formula))}")

n_ellipord, _ = signal.ellipord(fp/500, fs_stop/500, rp, rs)
print(f"order from scipy.signal.ellipord = {n_ellipord}")

Output:

k=0.6667  k1=5.088473e-04
n (real-valued) = 5.4267  -> order used = 6
order from scipy.signal.ellipord = 6

The theoretical value \(n=5.4267\) from eq. (2) (derived from the nome power law), rounded up to order 6, matches exactly the order 6 actually returned by scipy.signal.ellipord.

Pole/Zero Placement: Finite Transmission Zeros

The steep transition band is achieved through the zero placement of the transfer function \(H(s)\) . Because the Zolotarev rational function \(R_n(\xi,x)\) vanishes at finite points \(x=\xi_1, \xi_2,\dots\) (points at or beyond \(\xi\) ), the corresponding \(H(s)\) has zeros at finite points \(s=j\Omega_1, j\Omega_2,\dots\) on the imaginary axis. These are called finite transmission zeros. At each such frequency the gain is theoretically \(-\infty\) dB, and these deep notches are precisely what create the stopband equiripple valleys.

It’s worth noting that Butterworth, Chebyshev Type I, Chebyshev Type II, and elliptic filters do not all share the same zero structure. Let’s check with SciPy’s analog-prototype functions.

from scipy import signal

N = 6
z_butter, _, _ = signal.buttap(N)
z_cheby1, _, _ = signal.cheb1ap(N, 1.0)
z_cheby2, _, _ = signal.cheb2ap(N, 60.0)
z_ellip,  _, _ = signal.ellipap(N, 1.0, 60.0)

print("Butterworth  finite zero count:", len(z_butter))
print("Chebyshev I  finite zero count:", len(z_cheby1))
print("Chebyshev II finite zero count:", len(z_cheby2))
print("Elliptic     finite zero count:", len(z_ellip))

Output:

Butterworth  finite zero count: 0
Chebyshev I  finite zero count: 0
Chebyshev II finite zero count: 6
Elliptic     finite zero count: 6

Butterworth and Chebyshev Type I build their amplitude response from a polynomial (\(T_n\) itself), so they have no finite zeros — all zeros sit at \(s=\infty\) (one reason their stopbands can only decay monotonically). Chebyshev Type II, on the other hand, applies the Type I construction to a reciprocal variable, making it a rational function — and it therefore has finite zeros in its stopband, just like the elliptic filter. In other words, the real dividing line for “does it have finite transmission zeros” is not “Butterworth/Chebyshev vs. elliptic” but “polynomial approximation (Butterworth, Chebyshev I) vs. rational-function approximation (Chebyshev II, elliptic).” What makes the elliptic filter special is that it applies this rational-function approximation to both bands simultaneously, making both sides equiripple.

The count of finite transmission zeros also has an even/odd-order edge case. For an \(n\) -th order elliptic filter, the number of finite zero pairs is \(n/2\) when \(n\) is even (every pole pair has a matching finite zero), and \((n-1)/2\) when \(n\) is odd (one pole has no matching finite zero and effectively sits at \(s=\infty\) ).

from scipy import signal
for N in [5, 6, 7]:
    z, p, k = signal.ellipap(N, 1.0, 60.0)
    print(f"n={N}: poles={len(p)}  finite zeros={len(z)}")
n=5: poles=5  finite zeros=4
n=6: poles=6  finite zeros=6
n=7: poles=7  finite zeros=6

This asymmetry has an important practical consequence. For even orders, the number of zero pairs equals the number of pole pairs, so as \(\Omega\to\infty\) the gain asymptotes to the stopband ripple floor (\(-R_s\) dB) and goes no lower. For odd orders, one extra pole means the gain keeps decaying monotonically beyond the last finite zero.

Let’s compute and visualize the finite transmission-zero frequencies for \(n=6\) (the order returned by scipy.signal.ellipord(100, 150, 1, 60, fs=1000)).

import numpy as np
from scipy import signal

fs = 1000
rp, rs = 1.0, 60.0
n, wn = signal.ellipord(100, 150, rp, rs, fs=fs)  # keep everything in Hz (wn is also in Hz when fs= is passed)

z, p, k = signal.ellip(n, rp, rs, wn, btype='low', fs=fs, output='zpk')
b, a = signal.zpk2tf(z, p, k)

print("Zero magnitudes (checking unit circle):", np.round(np.abs(z), 6))

# Evaluate H(z) exactly at each zero's own angular frequency to confirm the depth
uniq_ang = sorted(set(round(abs(np.angle(zz)), 10) for zz in z))
_, h_exact = signal.freqz(b, a, worN=np.array(uniq_ang))
for wa, hh in zip(uniq_ang, h_exact):
    f_hz = wa * fs / (2 * np.pi)
    print(f"f={f_hz:6.2f} Hz   |H|={abs(hh):.2e}   ({20*np.log10(abs(hh)+1e-300):.1f} dB)")

Output:

Zero magnitudes (checking unit circle): [1. 1. 1. 1. 1. 1.]
f=133.73 Hz   |H|=1.31e-12   (-237.6 dB)
f=163.41 Hz   |H|=3.02e-13   (-250.4 dB)
f=303.88 Hz   |H|=1.85e-14   (-274.6 dB)

Every zero lies on the unit circle (\(|z|=1\) , i.e., on the imaginary axis analytically), and the 3 finite zero pairs are distributed from just past the passband/stopband edges (100 Hz / 150 Hz) across the stopband. The theoretical gain there is \(-\infty\) dB, but due to floating-point rounding the measured depth is around \(-238\) to \(-275\) dB — effectively infinite for any practical purpose. Interestingly, the first zero (133.73 Hz) falls inside the nominal transition band, before the specified stopband edge of 150 Hz. This is because ellipord only guarantees “at least \(R_s\) dB of attenuation from 150 Hz onward” — it does not forbid the attenuation from momentarily dipping even deeper before that point.

The figure below shows the overall response of this elliptic filter (\(n=6\) ) (top) alongside a zoomed view of the passband’s equiripple structure (bottom). The red dashed lines in the top panel mark the finite transmission zeros, each coinciding with the bottom of a stopband ripple valley.

Finite transmission zeros and equiripple structure of an elliptic lowpass filter

Comparison with Butterworth and Chebyshev

FilterPassbandStopbandTransition Band (same order)Phase Response
ButterworthMaximally flatMonotoneWidestBest
Chebyshev Type IEquirippleMonotoneModerateModerate
Chebyshev Type IIMaximally flatEquirippleModerateModerate
EllipticEquirippleEquirippleNarrowestWorst

The elliptic filter maximizes transition band steepness at the cost of nonlinear phase response (poor group delay flatness).

Group Delay and the Pulse-Response Trade-off

Let’s quantify the cost of the steep amplitude response. Group delay is the negative derivative of the phase response with respect to frequency:

\[\tau(\omega) = -\frac{d\phi(\omega)}{d\omega}\]

If group delay is constant across frequency, the filter has linear phase and preserves pulse waveform shape. The Bessel filter is designed to make this group delay as flat as possible in the passband — the exact opposite design philosophy from the elliptic filter. The elliptic filter introduces rapid phase rotation near the passband edge (a consequence of its rational-function construction), causing large group-delay variation there.

Let’s measure the passband (1–90 Hz) group-delay variation and the unit-step-response overshoot for Butterworth, Chebyshev I, Elliptic, and Bessel filters, all at the same order (\(n=4\) ).

import numpy as np
from scipy import signal

fs = 1000
fc = 100
N = 4
rp, rs = 1.0, 60.0

filters = {
    'Butterworth': signal.butter(N, fc, btype='low', fs=fs),
    'Chebyshev I': signal.cheby1(N, rp, fc, btype='low', fs=fs),
    'Elliptic':    signal.ellip(N, rp, rs, fc, btype='low', fs=fs),
    'Bessel':      signal.bessel(N, fc, btype='low', fs=fs, norm='delay'),
}

print("=== Passband (1-90Hz) group delay variation ===")
for name, ba in filters.items():
    w, gd_samples = signal.group_delay(ba, w=4096, fs=fs)
    gd_ms = gd_samples / fs * 1000.0          # samples -> milliseconds
    mask = (w >= 1) & (w <= 90)
    gd = gd_ms[mask]
    print(f"{name:12s}: mean={gd.mean():.3f}ms  "
          f"min={gd.min():.3f}ms  max={gd.max():.3f}ms  "
          f"variation={(gd.max()-gd.min()):.3f}ms")

print()
print("=== Unit step response overshoot ===")
for name, ba in filters.items():
    b, a = ba
    t = np.arange(0, 0.3, 1/fs)
    y = signal.lfilter(b, a, np.ones(len(t)))
    steady = y[-50:].mean()
    overshoot = (y.max() - steady) / steady * 100
    print(f"{name:12s}: steady={steady:.4f}  peak={y.max():.4f}  overshoot={overshoot:.2f}%")

Output:

=== Passband (1-90Hz) group delay variation ===
Butterworth : mean=4.848ms  min=4.021ms  max=6.522ms  variation=2.501ms
Chebyshev I : mean=5.697ms  min=4.148ms  max=10.450ms  variation=6.302ms
Elliptic    : mean=5.539ms  min=3.903ms  max=10.598ms  variation=6.694ms
Bessel      : mean=1.582ms  min=1.539ms  max=1.669ms  variation=0.130ms

=== Unit step response overshoot ===
Butterworth : steady=1.0000  peak=1.1191  overshoot=11.91%
Chebyshev I : steady=0.8913  peak=1.0923  overshoot=22.56%
Elliptic    : steady=0.8913  peak=1.1017  overshoot=23.61%
Bessel      : steady=1.0000  peak=1.0807  overshoot=8.07%

Even at the same 4th order, the passband group-delay variation is 0.130 ms for Bessel versus 6.694 ms for elliptic — more than 50x larger. This confirms with actual numbers the qualitative “phase response: worst” entry in the table above. Step-response overshoot is likewise largest for elliptic (23.61%) versus Bessel’s 8.07%, showing that the steep amplitude response comes at the cost of strong ringing (oscillatory overshoot) on rectangular-type inputs. Note that Chebyshev I (equiripple passband only) and Elliptic (equiripple both bands) land close together here, suggesting that passband non-flatness itself is the dominant driver of the deviation from linear phase. For applications where waveform preservation is paramount (measurement instruments, pulse transmission), avoid the elliptic filter and choose Bessel instead.

SciPy Implementation

import numpy as np
import matplotlib.pyplot as plt
from scipy import signal

# ===== Design Parameters =====
fs = 1000          # Sampling frequency [Hz]
f_pass = 100       # Passband edge [Hz]
f_stop = 150       # Stopband edge [Hz]
rp = 1.0           # Passband ripple [dB]
rs = 60.0          # Stopband attenuation [dB]

# Normalize to Nyquist frequency
nyq = fs / 2
wp = f_pass / nyq
ws = f_stop / nyq

# ===== Compute Minimum Order =====
n, wn = signal.ellipord(wp, ws, rp, rs)
print(f"Minimum elliptic filter order: {n}")

# ===== Design Lowpass Elliptic Filter =====
b, a = signal.ellip(n, rp, rs, wn, btype='low')

# ===== Compute Frequency Response =====
w, h = signal.freqz(b, a, worN=2048, fs=fs)

# ===== Plot =====
fig, axes = plt.subplots(2, 1, figsize=(10, 8))

# Magnitude response
axes[0].plot(w, 20 * np.log10(np.abs(h)), label=f'Elliptic (n={n})', color='royalblue')
axes[0].axvline(f_pass, color='green', linestyle='--', alpha=0.7, label=f'Passband edge {f_pass}Hz')
axes[0].axvline(f_stop, color='red', linestyle='--', alpha=0.7, label=f'Stopband edge {f_stop}Hz')
axes[0].axhline(-rp, color='orange', linestyle=':', alpha=0.7, label=f'Ripple -{rp}dB')
axes[0].axhline(-rs, color='purple', linestyle=':', alpha=0.7, label=f'Attenuation -{rs}dB')
axes[0].set_ylim(-80, 5)
axes[0].set_xlabel('Frequency [Hz]')
axes[0].set_ylabel('Gain [dB]')
axes[0].set_title('Elliptic Filter: Magnitude Response')
axes[0].legend()
axes[0].grid(True)

# Phase response
angles = np.unwrap(np.angle(h))
axes[1].plot(w, np.degrees(angles), color='royalblue')
axes[1].set_xlabel('Frequency [Hz]')
axes[1].set_ylabel('Phase [degrees]')
axes[1].set_title('Elliptic Filter: Phase Response')
axes[1].grid(True)

plt.tight_layout()
plt.show()

Order Comparison Across Filter Types

Using the same specification (\(f_p=100\) Hz, \(f_s=150\) Hz, \(R_p=1\) dB, \(R_s=60\) dB):

from scipy import signal

fs = 1000
nyq = fs / 2
wp = 100 / nyq
ws = 150 / nyq
rp, rs = 1.0, 60.0

n_butter, _ = signal.buttord(wp, ws, rp, rs)
n_cheby1, _ = signal.cheb1ord(wp, ws, rp, rs)
n_cheby2, _ = signal.cheb2ord(wp, ws, rp, rs)
n_ellip, _ = signal.ellipord(wp, ws, rp, rs)

print(f"Butterworth  : order {n_butter}")
print(f"Chebyshev I  : order {n_cheby1}")
print(f"Chebyshev II : order {n_cheby2}")
print(f"Elliptic     : order {n_ellip}")

Actual output (SciPy 1.18.0):

Butterworth  : order 17
Chebyshev I  : order 9
Chebyshev II : order 9
Elliptic     : order 6

The elliptic filter achieves the same specification with less than 1/3 the order of a Butterworth filter. Order translates almost directly into the degree of the transfer function’s denominator polynomial — i.e., the required computation and number of state variables (memory) — so on resource-constrained embedded platforms this order gap is a direct implementation-cost gap. This order-6 result also matches exactly the ceiling of the theoretical value \(n=5.4267\) derived from the nome power law in eq. (2) above.

Highpass, Bandpass, and Bandstop Design

from scipy import signal

fs = 1000
nyq = fs / 2
rp, rs = 1.0, 60.0

# ===== Highpass Filter =====
n_hp, wn_hp = signal.ellipord(200/nyq, 100/nyq, rp, rs)
b_hp, a_hp = signal.ellip(n_hp, rp, rs, wn_hp, btype='high')

# ===== Bandpass Filter =====
n_bp, wn_bp = signal.ellipord(
    [100/nyq, 300/nyq], [50/nyq, 400/nyq], rp, rs
)
b_bp, a_bp = signal.ellip(n_bp, rp, rs, wn_bp, btype='bandpass')

# ===== Bandstop Filter =====
n_bs, wn_bs = signal.ellipord(
    [50/nyq, 400/nyq], [100/nyq, 300/nyq], rp, rs
)
b_bs, a_bs = signal.ellip(n_bs, rp, rs, wn_bs, btype='bandstop')

Numerical Stability: Use SOS Format

For higher-order filters (roughly n > 6), direct-form coefficients (b, a) can suffer from numerical precision issues. Use Second-Order Sections (SOS) format instead:

# SOS format: more numerically stable
sos = signal.ellip(n, rp, rs, wn, btype='low', output='sos')

# Zero-phase filtering (offline use)
y = signal.sosfiltfilt(sos, x)

# Causal filtering (real-time use)
y = signal.sosfilt(sos, x)
FunctionPhase delayReal-timeRecommended when
signal.lfilterYesYesReal-time, low-order filters
signal.filtfiltNone (zero-phase)NoOffline analysis
signal.sosfiltYesYesHigher-order, real-time
signal.sosfiltfiltNoneNoHigher-order, offline

When to Choose Elliptic Filters

ScenarioRecommended Filter
Steepest possible transition bandElliptic
Minimize filter order (computation cost)Elliptic
Passband ripple must be zeroChebyshev Type II or Butterworth
Flat group delay (linear phase)Butterworth or Bessel
Low latency real-time processingButterworth (low order)

Classical analog-prototype design (Butterworth, Chebyshev, elliptic, Bessel) remains the standard approach in signal processing, audio hardware, and communication systems for meeting a fixed set of specifications — passband ripple, stopband attenuation, and transition bandwidth — with a closed-form solution. This is exactly the design SciPy and MATLAB provide.

Since around 2023, however, research has increasingly explored embedding the basic IIR building block (especially the second-order section, or biquad) inside deep-learning pipelines. Lutati, Zimerman, and Wolf (EMNLP 2023), “Focus Your Attention (with Adaptive IIR Filters),” use input-dependent (adaptive) second-order IIR filters as a preprocessing layer ahead of attention, improving efficiency on long-sequence tasks. Separately, Malek, Schulz, and Wuebbelmann (DAFx25, 2025) report using Kolmogorov-Arnold Networks (KANs) to directly optimize biquad filter coefficients via learning.

It’s important not to read these as replacing the classical design covered in this article — deriving a minimum-order closed-form solution from given passband/stopband specifications. Rather, they represent a direction where the IIR filter structure itself becomes a learnable building block inside a neural network, freed from a fixed specification. At present, classical analog-prototype design and neural-network-based approaches seem less like competitors and more like complementary techniques suited to different settings — fixed frequency-domain specifications versus input-dependent, task-dependent adaptive behavior.

References

  • Zolotarev, E. I. (1877). Application of elliptic functions to questions of functions deviating least and most from zero. Zapiski Imperatorskoi Akademii Nauk, 30(5), 1-71.
  • Cauer, W. (1933). Ein Interpolationsproblem mit Funktionen mit positivem Realteil. Mathematische Zeitschrift, 38, 1-44.
  • Orfanidis, S. J. (2006). Lecture Notes on Elliptic Filter Design. Rutgers University, Department of Electrical & Computer Engineering.
  • Proakis, J. G., & Manolakis, D. G. (2007). Digital Signal Processing (4th ed.). Pearson.
  • Oppenheim, A. V., & Schafer, R. W. (2009). Discrete-Time Signal Processing (3rd ed.). Prentice Hall.
  • Lutati, S., Zimerman, I., & Wolf, L. (2023). Focus Your Attention (with Adaptive IIR Filters). Proceedings of EMNLP 2023.
  • Malek, A., Schulz, D., & Wuebbelmann, F. (2025). Biquad Coefficients Optimization via Kolmogorov-Arnold Networks. Proceedings of the 28th International Conference on Digital Audio Effects (DAFx25).
  • SciPy scipy.signal.ellip documentation
  • SciPy scipy.signal.ellipord documentation