AlssmPolyLegendre
¶
Bases: ModelBase
flowchart TD
lmlib.statespace.model.AlssmPolyLegendre[AlssmPolyLegendre]
lmlib.statespace.model.ModelBase[ModelBase]
lmlib.statespace.model.ModelBase --> lmlib.statespace.model.AlssmPolyLegendre
click lmlib.statespace.model.AlssmPolyLegendre href "" "lmlib.statespace.model.AlssmPolyLegendre"
click lmlib.statespace.model.ModelBase href "" "lmlib.statespace.model.ModelBase"
ALSSM whose output basis is the discrete Legendre polynomials on a finite window.
Unlike AlssmPoly (Pascal/monomial basis) and AlssmPolyJordan
(Jordan/binomial basis), the Legendre ALSSM maps time indices to the interval
\([-1, +1]\) and uses the classical Legendre polynomials
\(P_0, P_1, \ldots, P_D\) as its basis functions. This keeps the Gram
matrix \(W\) well-conditioned regardless of the window length \(W_{\rm size}\):
For a window of 500 samples and polynomial degree 4, the Gram matrix condition number is \(\approx 9\) instead of \(\approx 10^{22}\), an improvement of more than 20 orders of magnitude.
State-space parametrisation
The window of \(W_{\rm size}\) samples is mapped to \(t_{\rm sc} \in [-1, +1]\) via
The state vector \(x \in \mathbb{R}^N\) (with \(N = D+1\)) holds the Legendre expansion coefficients \([c_0, \ldots, c_D]\) of the fitted polynomial:
Transition matrix \(A\)
Advancing one step from the newest sample toward the past corresponds to shifting \(t_{\rm sc}\) by \(h = 2/(W_{\rm size}-1)\). The resulting constant upper-triangular shift matrix \(L\) satisfies
and is computed analytically via a term-by-term Taylor expansion in the
Legendre basis using legder:
\(\kappa(L) \approx 1\) for all practical window sizes.
Output vector \(C\)
The newest sample (reference point, \(j = 0 \Rightarrow t_{\rm sc} = -1\)) is evaluated by
Compatibility
The state vector is in Legendre coefficient space, not monomial coefficient
space, so the numerical values of \(x[k]\) differ from those returned by
AlssmPoly. The output \(\hat{y}[k] = Cx[k]\) and the
full-window trajectory via eval_output with js are identical in
meaning (predicted signal value at each lag).
Notes
N : ALSSM system order, corresponding to the number of state variables
The Legendre polynomials used here are the standard (unnormalised)
Legendre polynomials satisfying \(P_n(1) = 1\), identical to those
returned by legval. They are not
normalised to \(\|P_n\|_{L^2} = 1\); the orthonormality factor is
\(\sqrt{(2n+1)/2}\). Because the RLS filter works with
\(W = V^\top V\) (where \(V\) contains the Legendre design-matrix
rows), the normalisation cancels out in the coefficient recovery and does
not need to be applied explicitly.
To convert recovered Legendre coefficients \(c\) back to standard monomial coefficients, premultiply by the change-of-basis matrix \(T^{-1}\) where \(T\) satisfies \(V_{\rm pascal}\,T = V_{\rm Legendre}\).
Example
Setting up a degree-3 Legendre ALSSM for a 500-sample window:
import lmlib as lm alssm = lm.AlssmPolyLegendre(poly_degree=3, a_seg=0, b_seg=499, label='legendre') print(alssm) AlssmPolyLegendre(A=..., C=..., label=legendre)
Using it inside a cost segment (drop-in replacement for AlssmPoly): ```python >>> import numpy as np, lmlib as lm >>> from lmlib.utils.generator import gen_wgn, gen_rect >>> K = 1000 >>> y = gen_rect(K, 300, 100) + gen_wgn(K, 0.01) >>> alssm = lm.AlssmPolyLegendre(poly_degree=3, a_seg=0,b_seg=199) >>> cost = lm.CostSegment(alssm, lm.Segment(0, 199, lm.BW, 500)) >>> rls = lm.RLSAlssm(cost) >>> rls.filter(y) >>> xs = rls.minimize_x() >>> y_hat = alssm.eval_output(xs)
Parameters:
-
poly_degree(int) –Polynomial degree \(D \geq 0\). The model order is \(N = D+1\).
-
a_seg(int, default:0) –Left boundary of the target segment (default
0). Together withb_segthis defines the window \([a, b]\):- The step size \(h = 2 / (b - a)\) maps \([a, b]\) to \([-1, +1]\) in the Legendre domain.
- The output vector is shifted to \(C_{\rm new} = \phi(-1)\,A^{-a}\) so that the filter naturally accumulates the segment-relative Gram matrix \(W_{\rm rel}\) with \(\kappa(W_{\rm rel}) \approx 2D+1\) — regardless of where \([a, b]\) sits relative to \(j=0\).
- The output \(C_{\rm new}\,x[k]\) evaluates the polynomial at \(j = 0\) (the current sample \(y[k]\)).
-
b_seg(int, default:None) –Right boundary of the target segment. Must satisfy
b_seg > a_seg. The window width isb_seg - a_seg + 1. -
**kwargs–Forwarded to
ModelBase.
Examples:
Standard backward window of 501 samples aligned at the current sample::
alssm = lm.AlssmPolyLegendre(poly_degree=3, a_seg=0, b_seg=500)
seg = lm.Segment(0, 500, lm.BW, g=100)
Backward window shifted 200 samples into the past — same window size, same \(h\), same \(\kappa(W) \approx 2D+1\)::
alssm = lm.AlssmPolyLegendre(poly_degree=3, a_seg=-200, b_seg=300)
seg = lm.Segment(-200, 300, lm.BW, g=100)
Forward window entirely in the past::
alssm = lm.AlssmPolyLegendre(poly_degree=3, a_seg=-501, b_seg=-1)
seg = lm.Segment(-501, -1, lm.FW, g=100)
Methods:
-
update–Recompute \(A\) and \(C\) from
poly_degree,a_seg,b_seg. -
eval_output–Evaluate the ALSSM output for one or more state vectors.
-
dump_tree–Return the internal ALSSM tree structure as a string.
-
set_state_var_label–Register a label for one or more state vector indices.
-
get_state_var_labels–Return all registered state-variable labels together with their index tuples.
-
get_state_var_indices–Return the state-vector indices for a state variable identified by its label.
-
get_alssm_output_dimension–Return the ALSSM output dimension \(Q\) (number of output channels).
Attributes:
-
steady_state_basis– -
poly_degree–int : Polynomial degree \(D\).
-
a_seg–int : Left boundary of the target segment.
-
b_seg–int : Right boundary of the target segment.
-
h–float : Legendre step size \(h = 2\,/\,(b\_seg - a\_seg)\).
-
label–str : Label of the model
-
C_init–ndarray, shape=([Q,] N) : Initialized Output matrix \(C \in \mathbb{R}^{Q \times N}\) -
force_MC–bool : If True, a 1-D output vector
Cis broadcast to a 2-D array of shape(1, N)(multi-channel form). -
A–ndarray, shape=(N, N) : State matrix \(A \in \mathbb{R}^{N \times N}\) -
C–ndarray, shape=([Q,] N) : Output matrix \(C \in \mathbb{R}^{Q \times N}\) -
N–int : Model order \(N\)
-
Q–int : Number of output channels \(Q\).
-
alssms–list : Sub-ALSSMs that compose this model (empty for leaf nodes such as
Alssm). -
lambdas–ndarray: Per-ALSSM scalar output scaling factors \(\lambda_m\) applied to each sub-model's output matrix \(C_m\). -
is_MC–bool : True if the output matrix
Cis 2-D (multi-channel form), False if 1-D (scalar output).
Source code in lmlib/statespace/model.py
Methods¶
update
¶
Recompute \(A\) and \(C\) from poly_degree, a_seg, b_seg.
Step 1 — build shift matrix \(A = L\) for step size \(h = 2/(b-a)\).
Step 2 — set \(C = \phi(-1) = [1,\,-1,\,1,\,-1,\,\ldots]\).
Step 3 — apply segment-relative shift (only when a_seg != 0):
For a_seg < 0 (common backward window case) this is a positive
power of \(A\) — always stable.
Source code in lmlib/statespace/model.py
eval_output
¶
Evaluate the ALSSM output for one or more state vectors.
Without evaluation index (js=None):
With evaluation indices (js provided):
Parameters:
-
xs(array_like of shape (..., N)) –State vector(s). The last dimension must equal the model order N.
-
js(array_like of shape (J,) or None, default:None) –Sequence of integer evaluation indices. If None, evaluates at \(j = 0\) only (i.e. returns \(Cx\)).
Returns:
-
s(ndarray) –If
jsis None: shape(..., [Q]). Ifjsis provided: shape(J, ..., [Q]). The[Q]dimension is present only whenis_MCis True.
Source code in lmlib/statespace/model.py
dump_tree
¶
dump_tree() -> str
Return the internal ALSSM tree structure as a string.
Returns:
-
out(str) –Multi-line string representing the nested ALSSM structure.
Example
>>> import lmlib as lm
>>> import numpy as np
>>> alssm_poly = lm.AlssmPoly(4, label="high order polynomial")
>>> A = [[1, 1], [0, 1]]
>>> C = [[1, 0]]
>>> alssm_line = lm.Alssm(A, C, label="line")
>>> stacked_alssm = lm.AlssmStacked((alssm_poly, alssm_line), label='stacked model')
>>> print(stacked_alssm.dump_tree())
└-AlssmStacked, A: (7, 7), C: (2, 7), label: stacked model
└-AlssmPoly, A: (5, 5), C: (5,), label: high order polynomial
└-Alssm, A: (2, 2), C: (1, 2), label: line
Source code in lmlib/statespace/model.py
set_state_var_label
¶
Register a label for one or more state vector indices.
Labels allow state components to be referenced by name rather than by
numeric index; see get_state_var_indices.
Parameters:
-
label(str) –Label name to register.
-
indices(tuple of int) –State vector indices associated with this label.
Example
Source code in lmlib/statespace/model.py
get_state_var_labels
¶
Return all registered state-variable labels together with their index tuples.
Labels are accumulated recursively from all nested sub-ALSSMs, with
each label prefixed by the current model's label. The state
indices are adjusted to reflect the position within the combined
(block-diagonal) state vector.
Returns:
-
out(list of (str, tuple of int)) –List of
(label_string, indices)pairs.label_stringis a dot-separated path (e.g.'stacked.poly.x0') andindicesis the corresponding tuple of integer state-vector positions.
Source code in lmlib/statespace/model.py
get_state_var_indices
¶
Return the state-vector indices for a state variable identified by its label.
Parameters:
-
label(str) –Fully qualified state label (dot-separated path), as returned by
get_state_var_labels.
Returns:
-
out(tuple of int or list of int) –State-vector indices associated with
label. Returns an empty list iflabelis not found.