Recursive Least Squares Stacking
My favorite part about linear algebra is taking an iterative function and stacking it into a beautiful vectorized algorithm. In Recursive Least Squares you must choose a forgetting factor that defines how sensitive the algorithm is to changes in signal statistics, and in practice this usually amounts to a line search on that parameter. In non-standard form, the update equations are
\[\begin{align} \bf{r}_{xx}(n) =& \sum_{k=0}^n f^{n-k}\bf{x}(k)\left[\bf{x}(k)\right]^T\cr \bf{r}_{dx}(n) =& \sum_{k=0}^n f^{n-k}d(k)\bf{x}(k)\cr \bf{h}(n) =& \bf{r}_x(n)^{-1}\bf{r}_{dx}(n)\cr y(n) =& \left[\bf{h}(n)\right]^T\bf{x}(n)\cr \end{align}\] where \(f\) is the forgetting factor, \(\bf{x}(n)\) is a \(L \times 1\) vector of filter inputs, \(d(n)\) is the desired signal, \(\bf{r}_{dx}(n)\) is the \(L \times 1\) cross-correlation vector, \(\bf{r}_{xx}(n)\) is the \(L \times L\) autocorrelation matrix of \(\bf{x}(n)\), \(\bf{h}(n)\) is the \(L \times 1\) coefficient solution, and \(y(n)\) is the scalar output (all varying with time \(n\)). To avoid divison by \(0\), \(\bf{r}_{xx}(n)\) is initialized as \(\delta\bf{I}\) where \(\delta\) is a small constant.
Let us consider the case where \(L=1\). We can begin by stacking all of the values of \(y(n)\) into an \(N\times 1\) matrix \(\bf{Y}\), which is just a convolution between \(\bf{x}(n)\) and \(\bf{h}(n)\). We can define \(\bf{X}\) as a convolution matrix of \(\bf{x}(n)\), which is just
\[\bf{X} = \text{diag}(\bf{x}(n))\] and we can let \(\bf{H}_1\) be an \(N\times 1\) vector containing all values of \(\bf{h}_1(n)\) (remember, we are only using one coefficient), giving us
\[\bf{Y} = \bf{X}\bf{H}_1.\]
\(\bf{H}_1\) can be factored into
\[\bf{H}_1 = \bf{D_1}\bf{F}\bf{P},\]
where \(P\) is the inverse of \(\text{diag}(\bf{R}_1\bf{F})\) and
\[\begin{align} \bf{D}_1 =& \left[\begin{array}{cccccc} &d(0)x_1(0) & 0 & 0 & \ldots & \cr &d(1)x_1(1) & d(0)x_1(0) & 0 &\ldots&\cr &d(2)x_1(2) & d(1)x_1(1) & d(0)x_1(0) &\ldots& \cr &\vdots & & & & \end{array}\right]\cr \bf{R}_1 =& \left[\begin{array}{cccccc} &x_1(0)^2 & \delta & 0 & \ldots & \cr &x_1(1)^2 & x_1(0)^2 & \delta &\ldots&\cr &x_1(2)^2 & x_1(1)^2 & x_1(0)^2 &\ldots& \cr &\vdots & & & & \end{array}\right]\cr \bf{F} =& \left[\begin{array}{ccccccc} &1 & f & f^2 & \ldots & f^n & \end{array}\right]^T\end{align}\]
giving us
\[\bf{Y} = \bf{X}\bf{D}_1\bf{F}\bf{P}.\]
This formulation gives us a way to alter the forgetting factor without recalculating all of the statistics. This method is definitely less memory-efficient, and in a fast language like C this might be less processor efficient too, but in vectorized languages like MATLAB this reformulation increases speed by quite a bit, depending on the problem size and the size of your parameter space. Sometimes it makes more sense to iterate with a for-loop, other times the vectorized method is best. Here are some results computing the basic adaptive least squares solution with 196 different values of \(f\). The for-loop algorithm is calculated using the recursive definitions of the statistics (because for filter length 1 it's faster than the more complicated realizations of the filter).
\[\begin{array}{ccc} \text{N (samples)} & \text{For-Loop} & \text{Vectorized} \cr 1000 & 3.097021 & 0.450168 \cr 2000 & 6.134475 & 1.986143 \cr 3000 & 9.314577 & 4.969422 \cr 4000 & 12.453965 & 9.253738 \cr 5000 & 15.420053 & 15.147522. \cr \end{array}\]
The numerical differences were, at worst, on the order of \(10^{-7}\). This method works well for filters of length 1, and it even generalizes to filters with equality constraints, but that comprises an extremely small subset of all the possible filter lengths. The "numerator" (everything except \(\bf{P}\)) can be generalized for an arbitrary length filter, giving
\[\bf{Y}_{\text{num}} = (\bf{X}_1\bf{D}_1 + \bf{X}_2\bf{D}_2 + \ldots + \bf{X}_L\bf{D}_L)\bf{F}\]
where \(\bf{X}_i = \text{diag}(\bf{x}(n-i+1))\) and
\[\bf{D}_i = \left[\begin{array}{cccccc} &d(0)x(0-i+1) & 0 & 0 & \ldots & \cr &d(1)x(1-i+1) & d(0)x(0-i+1) & 0 &\ldots&\cr &d(2)x(2-i+1) & d(1)x(1-i+1) & d(0)x(0-i+1) &\ldots& \cr &\vdots & & & & \end{array}\right]\]
assuming all initial conditions of \(\bf{x}(n)\) (for \( n < 0 \) ) are \(0\).
The issue comes with inverting the autocorrelation matrix at each time \(n\): apart from any strange linear algebra that I am ignorant of (maybe tensors) I have no way of compactly representing this operation. A possible solution to this is pre-whitening the inputs: that is essentially the role of the autocorrelation inverse, but instead of whitening for every new sample and changing slightly with every statistical nuance of the data you will have a one-shot whitening step over the average statistics of all of the input data. This is now no longer an exact approach, but will get a "ballpark" estimate.












