Skip to content

Symmetric and Non-Symmetric Polynomial Filters with Meixner Basis [ex122.1]

Applies CompositeCost instances with AlssmPolyMeixner of degrees 0 through 7 to a rectangular test signal.

The Meixner basis is orthogonal under the exponential (geometric) window weight, giving a well-conditioned Gram matrix for semi-infinite windows. This makes it the preferred polynomial basis for recursive least-squares filtering with exponential windows, especially at high polynomial degrees where the monomial (Pascal) basis of AlssmPoly becomes numerically ill-conditioned.

Two filter configurations are shown for each degree:

  • Symmetric filter — forward left window and backward right window.
  • Left (causal) filter — forward window on the left side only.

Plot

Plot

Code

"""
Symmetric and Non-Symmetric Polynomial Filters with Meixner Basis [ex122.1]
===========================================================================

Applies [`CompositeCost`][lmlib.statespace.cost.CompositeCost] instances with [`AlssmPolyMeixner`][lmlib.statespace.model.AlssmPolyMeixner] of
degrees 0 through 7 to a rectangular test signal.

The Meixner basis is orthogonal under the exponential (geometric) window
weight, giving a well-conditioned Gram matrix for semi-infinite windows.
This makes it the preferred polynomial basis for recursive least-squares
filtering with exponential windows, especially at high polynomial degrees
where the monomial (Pascal) basis of [`AlssmPoly`][lmlib.statespace.model.AlssmPoly] becomes numerically
ill-conditioned.

Two filter configurations are shown for each degree:

* **Symmetric filter** — forward left window and backward right window.
* **Left (causal) filter** — forward window on the left side only.

"""

import matplotlib.pyplot as plt
import numpy as np
import lmlib as lm
from lmlib.utils.generator import gen_rect

# --- Generating test signal ---
K = 2000
k = np.arange(K)
y = gen_rect(K, 500, 250)

# --- ALSSM Filtering ---
y_hats_sym = []
y_hats_left = []

for i in range(0, 8):

    # Segments
    segment_left = lm.Segment(a=-np.inf, b=-1, direction=lm.FORWARD, g=20)
    segment_right = lm.Segment(a=0, b=np.inf, direction=lm.BACKWARD, g=20)

    # Polynomial ALSSM
    alssm_L = lm.AlssmPolyMeixner(i, segment=segment_left)
    alssm_R = lm.AlssmPolyMeixner(i, segment=segment_right)

    # -- Symmetric Filter --
    # Composite Cost
    F =  [[1,0],[0,1]]
    cost    = lm.CompositeCost((alssm_L, alssm_R), (segment_left, segment_right), F=F)

    # filter signal and take the approximation
    rls = lm.RLSAlssm(cost)

    # extracts filtered signal
    y_hats_sym.append(rls.fit(y,H=cost.spline_H(max_continuity=i),eval_alssm_weights=[0,1]))   

    # -- Left-Sided Filter --
    # CompositeCost
    costs = lm.CompositeCost((alssm_L,), (segment_left, ), F=[[1]])

    # filter signal and take the approximation
    rls = lm.RLSAlssm(costs)

    # extracts filtered signal
    y_hats_left.append(rls.fit(y))

# --- Plotting ----
fig, ax = plt.subplots(2, sharex='all', figsize=(10, 6))
ax[0].plot(k, y, lw=0.6, c='gray', label=r'$y$')
for (i, y_hat) in enumerate(y_hats_sym):
    ax[0].plot(k, y_hat, lw=1, label=r'$\hat y, N=' + str(i) + '$')
ax[0].legend(loc='upper right')
ax[0].set_title('Left- and Right-Sided CostSegment (Symmetric)')

ax[1].plot(k, y, lw=0.6, c='gray', label=r'$y$')
for (i, y_hat) in enumerate(y_hats_left):
    ax[1].plot(k, y_hat,  lw=1, label=r'$\hat y, N=' + str(i) + '$')
ax[1].legend(loc='upper right')
ax[1].set_title('Left-Sided CostSegment only (non-symmetric)')
ax[1].set_xlabel('k')

for _ax in ax:
    _ax.spines['top'].set_visible(False)
    _ax.spines['right'].set_visible(False)


plt.show()