Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
LookAheadLevinson.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// This Source Code Form is subject to the terms of the Mozilla
5// Public License v. 2.0. If a copy of the MPL was not distributed
6// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
7// SPDX-FileCopyrightText: The Eigen Authors
8// SPDX-License-Identifier: MPL-2.0
9
10#ifndef EIGEN_STRUCTURED_LOOK_AHEAD_LEVINSON_H
11#define EIGEN_STRUCTURED_LOOK_AHEAD_LEVINSON_H
12
13// IWYU pragma: private
14#include "./InternalHeaderCheck.h"
15
16namespace Eigen {
17
18template <typename Scalar_>
20
21namespace internal {
22
23template <typename Scalar_>
24struct traits<LookAheadLevinson<Scalar_>> : traits<Matrix<Scalar_, Dynamic, Dynamic>> {
25 using XprKind = MatrixXpr;
26 using StorageKind = SolverStorage;
27 using StorageIndex = int;
28 using BaseTraits = traits<Matrix<Scalar_, Dynamic, Dynamic>>;
29 static constexpr int Flags = BaseTraits::Flags & RowMajorBit;
30 static constexpr int CoeffReadCost = Dynamic;
31};
32
33// Persymmetry E_n T_n E_n = T_n^T requires sum_i a_i b_i, not the conjugating dot().
34template <typename D1, typename D2>
35typename D1::Scalar structured_tdot(const MatrixBase<D1>& a, const MatrixBase<D2>& b) {
36 return a.cwiseProduct(b).sum();
37}
38
39template <typename Scalar>
40Matrix<Scalar, Dynamic, 1> structured_upshift(const Matrix<Scalar, Dynamic, 1>& v) {
41 const Index k = v.size();
42 Matrix<Scalar, Dynamic, 1> w = Matrix<Scalar, Dynamic, 1>::Zero(k);
43 if (k > 1) w.head(k - 1) = v.tail(k - 1);
44 return w;
45}
46
47} // namespace internal
48
83template <typename Scalar_>
84class LookAheadLevinson : public SolverBase<LookAheadLevinson<Scalar_>> {
85 public:
87 friend class SolverBase<LookAheadLevinson>;
88 EIGEN_GENERIC_PUBLIC_INTERFACE(LookAheadLevinson)
89 using DenseVector = Matrix<Scalar, Dynamic, 1>;
90 using DenseMatrix = Matrix<Scalar, Dynamic, Dynamic>;
91
93 LookAheadLevinson() : m_maxBlockSize(4), m_n(0), m_isInitialized(false), m_info(InvalidInput) {}
94
97 template <int Rows_, int Cols_>
99 : m_maxBlockSize(4), m_n(0), m_isInitialized(false), m_info(InvalidInput) {
100 compute(T);
101 }
102
107 eigen_assert(p >= 1);
108 m_maxBlockSize = p;
109 return *this;
110 }
111
113 Index maxBlockSize() const { return m_maxBlockSize; }
114
115 Index rows() const noexcept { return m_n; }
116 Index cols() const noexcept { return m_n; }
117
119 template <int Rows_, int Cols_>
121
122#ifdef EIGEN_PARSED_BY_DOXYGEN
128 template <typename Rhs>
130#endif
131
132#ifndef EIGEN_PARSED_BY_DOXYGEN
140 template <typename RhsType, typename DstType>
141 void _solve_impl(const RhsType& rhs, DstType& dst) const {
142 dst.noalias() = solveForward(rhs);
143 }
144
149 template <bool Conjugate, typename RhsType, typename DstType>
150 void _solve_impl_transposed(const RhsType& rhs, DstType& dst) const {
151 const DenseMatrix reversed = rhs.template conjugateIf<Conjugate>().colwise().reverse();
152 dst = solveForward(reversed).colwise().reverse().template conjugateIf<Conjugate>();
153 }
154#endif
155
159 eigen_assert(m_isInitialized && "LookAheadLevinson is not initialized.");
160 return m_info;
161 }
162
165 RealScalar conditionEstimate() const {
166 eigen_assert(m_isInitialized && "LookAheadLevinson is not initialized.");
167 return m_sMin > RealScalar(0) ? m_normEst / m_sMin : NumTraits<RealScalar>::infinity();
168 }
169
170 private:
173 template <typename Rhs>
174 DenseMatrix solveForward(const Rhs& b) const {
175 const Index nrhs = b.cols();
176 DenseMatrix x = m_luInit.solve(b.topRows(m_k0));
177 DenseMatrix rhs, a, xn;
178 for (const Step& s : m_steps) {
179 rhs.noalias() = b.middleRows(s.k, s.p) - s.WspEk * x; // (27): b_p - S_p^T E_k x_k
180 a = s.luGamma.solve(rhs);
181 xn.resize(s.k + s.p, nrhs);
182 xn.topRows(s.k).noalias() = x + s.EkYp * a; // [E_k Y_p; I_p] a_p
183 xn.bottomRows(s.p) = a;
184 x = xn;
185 }
186 return x;
187 }
188
189 // Per accepted block step, the b-independent data needed to advance x in solve().
190 struct Step {
191 Index k; // order before the step
192 Index p; // block size
193 DenseMatrix EkYp; // E_k Y_p (k x p)
194 DenseMatrix WspEk; // S_p^T E_k (p x k)
195 FullPivLU<DenseMatrix> luGamma;
196 };
197
198 // Leading p x p principal submatrix built directly from the generators.
199 static DenseMatrix leadingBlock(const DenseVector& c, const DenseVector& r, Index p) {
200 DenseMatrix B(p, p);
201 for (Index j = 0; j < p; ++j) {
202 B.col(j).head(j) = r.segment(1, j).reverse();
203 B.col(j).tail(p - j) = c.head(p - j);
204 }
205 return B;
206 }
207
208 // Builds a (len x cols) block whose column j is the length-len generator segment
209 // v.segment(first + j, len); used to assemble the S_p and R_p shift blocks.
210 static DenseMatrix shiftBlock(const DenseVector& v, Index first, Index len, Index cols) {
211 DenseMatrix B(len, cols);
212 for (Index j = 0; j < cols; ++j) B.col(j) = v.segment(first + j, len);
213 return B;
214 }
215
216 static RealScalar smallestSingularValue(const DenseMatrix& M) {
217 if (M.rows() == 1) return numext::abs(M(0, 0));
218 JacobiSVD<DenseMatrix> svd(M);
219 return svd.singularValues()(svd.singularValues().size() - 1);
220 }
221
222 Index m_maxBlockSize;
223 Index m_n;
224 Index m_k0;
225 FullPivLU<DenseMatrix> m_luInit; // LU of the leading T_{k0}
226 std::vector<Step> m_steps;
227 bool m_isInitialized;
228 ComputationInfo m_info;
229 RealScalar m_sMin;
230 RealScalar m_normEst;
231};
232
233template <typename Scalar_>
234template <int Rows_, int Cols_>
236 using internal::structured_tdot;
237 using internal::structured_upshift;
238
239 EIGEN_STATIC_ASSERT(Rows_ == Dynamic || Cols_ == Dynamic || Rows_ == Cols_, YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES)
240 eigen_assert(T.rows() == T.cols() && "LookAheadLevinson requires a square Toeplitz matrix");
241 const Index n = T.rows();
242 const DenseVector c = T.column(); // (rho_0, sigma_1, ..., sigma_{n-1})
243 const DenseVector r = T.row(); // (rho_0, rho_1, ..., rho_{n-1})
244 const Scalar rho0 = c[0];
245
246 m_n = n;
247 m_steps.clear();
248 m_info = Success;
249 m_normEst = c.cwiseAbs().sum() + r.cwiseAbs().sum(); // cheap upper bound on ||T||_2
250
251 Index k;
252 DenseVector y, z;
253 Scalar gamma(0);
254 {
255 Index kbest = 1;
256 RealScalar best = RealScalar(-1);
257 const Index kmax = numext::mini<Index>(m_maxBlockSize, n);
258 for (Index i = 1; i <= kmax; ++i) {
259 RealScalar sv = smallestSingularValue(leadingBlock(c, r, i));
260 if (sv > best) {
261 best = sv;
262 kbest = i;
263 }
264 }
265 k = m_k0 = kbest;
266 m_luInit.compute(leadingBlock(c, r, k));
267 m_sMin = best;
268 }
269
270 bool lastWasBlock = false;
271 DenseVector yPrev, zPrev;
272 Scalar gammaPrev(0);
273 Index kp = 0, pp = 0;
274 DenseMatrix Ypp, Zpp;
275 FullPivLU<DenseMatrix> luGammaPP;
276 DenseVector y_kp_1, z_kp_1;
277
278 if (k < n) {
279 y = m_luInit.transpose().solve((-r.segment(1, k)).eval()); // T_k^T y = -r_k
280 z = m_luInit.solve((-c.segment(1, k)).eval()); // T_k z = -s_k
281 gamma = rho0 + structured_tdot(c.segment(1, k), y);
282 if (k == 1) {
283 lastWasBlock = false;
284 yPrev = DenseVector::Zero(0);
285 zPrev = DenseVector::Zero(0);
286 gammaPrev = rho0; // gamma^{(0)}
287 } else {
288 lastWasBlock = true; // treat init as a block step of size k from order 0
289 kp = 0;
290 pp = k;
291 Ypp = DenseMatrix::Zero(0, k);
292 Zpp = DenseMatrix::Zero(0, k);
293 luGammaPP = m_luInit;
294 y_kp_1 = DenseVector::Zero(0);
295 z_kp_1 = DenseVector::Zero(0);
296 }
297 }
298
299 while (k < n) {
300 const Index pcap = numext::mini<Index>(m_maxBlockSize, n - k);
301 const DenseVector Ek_y = y.reverse(), Ek_z = z.reverse();
302
303 DenseVector g_k, h_k;
304 bool gh_ready = false;
305 auto ensure_gh = [&]() {
306 if (gh_ready) return;
307 if (!lastWasBlock) {
308 // Theorem 1, eq (33)
309 g_k.resize(k);
310 h_k.resize(k);
311 g_k.head(k - 1) = zPrev.reverse();
312 g_k[k - 1] = Scalar(1);
313 g_k /= gammaPrev;
314 h_k.head(k - 1) = yPrev.reverse();
315 h_k[k - 1] = Scalar(1);
316 h_k /= gammaPrev;
317 } else {
318 // Theorem 2: y_{k,1}, z_{k,1} (eqs 47-49), then g_k, h_k (eq 50).
319 DenseVector rhs_y = -r.segment(kp + 2, pp);
320 DenseVector rhs_z = -c.segment(kp + 2, pp);
321 if (kp > 0) {
322 const DenseMatrix Rpp = shiftBlock(r, 1, kp, pp);
323 const DenseMatrix Spp = shiftBlock(c, 1, kp, pp);
324 rhs_y.noalias() -= Rpp.transpose() * y_kp_1.reverse();
325 rhs_z.noalias() -= Spp.transpose() * z_kp_1.reverse();
326 }
327 const DenseVector a_y = luGammaPP.transpose().solve(rhs_y);
328 const DenseVector a_z = luGammaPP.solve(rhs_z);
329 DenseVector yk1 = DenseVector::Zero(k), zk1 = DenseVector::Zero(k);
330 if (kp > 0) {
331 yk1.head(kp) = y_kp_1 + (Zpp * a_y).reverse();
332 zk1.head(kp) = z_kp_1 + (Ypp * a_z).reverse();
333 }
334 yk1.tail(pp) = a_y;
335 zk1.tail(pp) = a_z;
336 const Scalar c1 = -r[k + 1] - structured_tdot(r.segment(1, k), Ek_y);
337 const Scalar d1 = -c[k + 1] - structured_tdot(c.segment(1, k), Ek_z);
338 g_k = (yk1 - structured_upshift<Scalar>(y) + y[0] * y) / c1;
339 h_k = (zk1 - structured_upshift<Scalar>(z) + z[0] * z) / d1;
340 }
341 gh_ready = true;
342 };
343
344 // Build Y_p, Z_p incrementally and pick the block size p (eqs 31-32, 46, 39).
345 DenseMatrix Yp(k, pcap), Zp(k, pcap);
346 Yp.col(0) = y;
347 Zp.col(0) = z;
348 Index chosen = -1;
349 Index bestP = 1;
350 RealScalar bestPsi = RealScalar(-1);
351 DenseMatrix bestGamma;
352
353 for (Index p = 1; p <= pcap; ++p) {
354 if (p >= 2) {
355 ensure_gh();
356 const Index i = p - 1;
357 const Scalar ci = -r[k + i] - structured_tdot(r.segment(i, k), Ek_y);
358 const Scalar di = -c[k + i] - structured_tdot(c.segment(i, k), Ek_z);
359 Yp.col(i) = structured_upshift<Scalar>(Yp.col(i - 1)) - Yp.col(i - 1)[0] * y + ci * g_k;
360 Zp.col(i) = structured_upshift<Scalar>(Zp.col(i - 1)) - Zp.col(i - 1)[0] * z + di * h_k;
361 }
362 const DenseMatrix Sp = shiftBlock(c, 1, k, p);
363 DenseMatrix Gamma = leadingBlock(c, r, p) + Sp.transpose() * Yp.leftCols(p); // (20)
364 const RealScalar muY = Yp.leftCols(p).cwiseAbs().maxCoeff();
365 const RealScalar muZ = Zp.leftCols(p).cwiseAbs().maxCoeff();
366 const RealScalar denom = numext::maxi(numext::maxi(RealScalar(1), muY), numext::maxi(muZ, muY * muZ));
367 const RealScalar psi = smallestSingularValue(Gamma) / denom; // (46)
368 if (psi > bestPsi) {
369 bestPsi = psi;
370 bestP = p;
371 bestGamma = Gamma;
372 }
373 if (psi > RealScalar(0.1) * m_sMin) { // (39)
374 chosen = p;
375 bestGamma = Gamma;
376 break;
377 }
378 }
379
380 DenseMatrix Gamma = bestGamma;
381 Index p;
382 if (chosen > 0) {
383 p = chosen;
384 } else {
385 p = bestP;
386 m_sMin = bestPsi; // pmax too small to skip cleanly; record the degraded conditioning
387 }
388
389 const Index K = k + p;
390 const bool finalStep = (K == n);
391
392 const DenseMatrix Sp = shiftBlock(c, 1, k, p);
393 const DenseMatrix Rp = shiftBlock(r, 1, k, p);
394 FullPivLU<DenseMatrix> luG(Gamma);
395
396 // Record the b-independent data for solve(): rhs = b_p - (S_p^T E_k) x_k ; x update via E_k Y_p.
397 Step step;
398 step.k = k;
399 step.p = p;
400 step.EkYp = Yp.leftCols(p).colwise().reverse(); // E_k Y_p
401 step.WspEk = Sp.transpose().rowwise().reverse(); // S_p^T E_k
402 step.luGamma = luG;
403 m_steps.push_back(step);
404
405 if (!finalStep) {
406 const DenseVector rhs_e = -r.segment(k + 1, p) - Rp.transpose() * Ek_y; // (28)
407 const DenseVector rhs_f = -c.segment(k + 1, p) - Sp.transpose() * Ek_z; // (29)
408 const DenseVector e_p = luG.transpose().solve(rhs_e);
409 const DenseVector f_p = luG.solve(rhs_f);
410
411 DenseVector y_new(K), z_new(K);
412 y_new.head(k) = y + (Zp.leftCols(p) * e_p).reverse(); // (25)
413 y_new.tail(p) = e_p;
414 z_new.head(k) = z + (Yp.leftCols(p) * f_p).reverse(); // (26)
415 z_new.tail(p) = f_p;
416
417 const DenseVector y_save = y, z_save = z;
418 const Scalar gamma_save = gamma;
419 if (p == 1)
420 gamma = (Scalar(1) - e_p[0] * f_p[0]) * gamma; // (15)
421 else
422 gamma = rho0 + structured_tdot(c.segment(1, K), y_new); // (5)
423 y = y_new;
424 z = z_new;
425
426 if (p == 1) {
427 lastWasBlock = false;
428 yPrev = y_save;
429 zPrev = z_save;
430 gammaPrev = gamma_save;
431 } else {
432 lastWasBlock = true;
433 kp = k;
434 pp = p;
435 Ypp = Yp.leftCols(p);
436 Zpp = Zp.leftCols(p);
437 luGammaPP = luG;
438 y_kp_1 = Yp.col(1);
439 z_kp_1 = Zp.col(1);
440 }
441 }
442 k = K;
443 }
444
445 const RealScalar kappa = m_sMin > RealScalar(0) ? m_normEst / m_sMin : NumTraits<RealScalar>::infinity();
446 if (!(kappa * NumTraits<RealScalar>::epsilon() < RealScalar(1))) m_info = NumericalIssue;
447 m_isInitialized = true;
448 return *this;
449}
450
451} // namespace Eigen
452
453#endif // EIGEN_STRUCTURED_LOOK_AHEAD_LEVINSON_H
Look-ahead Levinson direct solver for general Toeplitz systems.
Definition LookAheadLevinson.h:84
ComputationInfo info() const
Definition LookAheadLevinson.h:158
LookAheadLevinson(const Toeplitz< Scalar, Rows_, Cols_ > &T)
Definition LookAheadLevinson.h:98
LookAheadLevinson()
Definition LookAheadLevinson.h:93
LookAheadLevinson & setMaxBlockSize(Index p)
Definition LookAheadLevinson.h:106
Index maxBlockSize() const
Definition LookAheadLevinson.h:113
RealScalar conditionEstimate() const
Definition LookAheadLevinson.h:165
LookAheadLevinson & compute(const Toeplitz< Scalar, Rows_, Cols_ > &T)
const Solve< LookAheadLevinson, Rhs > solve(const MatrixBase< Rhs > &b) const
An m x n Toeplitz matrix represented by its first column and row.
Definition Toeplitz.h:81
ComputationInfo
NumericalIssue
constexpr unsigned int RowMajorBit
Namespace containing all symbols from the Eigen library.
constexpr Index size() const noexcept