Eigen  5.0.1
 
Loading...
Searching...
No Matches
ComplexQZ.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2012 Alexey Korepanov
5// Copyright (C) 2025 Ludwig Striet <ludwig.striet@mathematik.uni-freiburg.de>
6//
7// This Source Code Form is subject to the terms of the
8// Mozilla Public License v. 2.0. If a copy of the MPL
9// was not distributed with this file, You can obtain one at
10// https://mozilla.org/MPL/2.0/.
11//
12// Derived from: Eigen/src/Eigenvalues/RealQZ.h
13// SPDX-License-Identifier: MPL-2.0
14
15#ifndef EIGEN_COMPLEX_QZ_H_
16#define EIGEN_COMPLEX_QZ_H_
17
18// IWYU pragma: private
19#include "./InternalHeaderCheck.h"
20
48
49namespace Eigen {
50
51template <typename MatrixType_>
52class ComplexQZ {
53 public:
54 using MatrixType = MatrixType_;
55 using Scalar = typename MatrixType_::Scalar;
56 using RealScalar = typename MatrixType_::RealScalar;
57
58 enum {
59 RowsAtCompileTime = MatrixType::RowsAtCompileTime,
60 ColsAtCompileTime = MatrixType::ColsAtCompileTime,
61 Options = internal::plain_object_options<MatrixType>::value,
62 MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
63 MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
64 };
65
68 using PlainMatrixType =
69 Matrix<Scalar, RowsAtCompileTime, ColsAtCompileTime, Options, MaxRowsAtCompileTime, MaxColsAtCompileTime>;
70
71 using Vec = Matrix<Scalar, Dynamic, 1>;
72 using Vec2 = Matrix<Scalar, 2, 1>;
73 using Vec3 = Matrix<Scalar, 3, 1>;
74 using Row2 = Matrix<Scalar, 1, 2>;
75 using Mat2 = Matrix<Scalar, 2, 2>;
76
81 const PlainMatrixType& matrixQ() const {
82 eigen_assert(m_isInitialized && "ComplexQZ is not initialized.");
83 eigen_assert(m_computeQZ && "The matrices Q and Z have not been computed during the QZ decomposition.");
84 return m_Q;
85 }
86
91 const PlainMatrixType& matrixZ() const {
92 eigen_assert(m_isInitialized && "ComplexQZ is not initialized.");
93 eigen_assert(m_computeQZ && "The matrices Q and Z have not been computed during the QZ decomposition.");
94 return m_Z;
95 }
96
101 const MatrixType& matrixS() const {
102 eigen_assert(m_isInitialized && "ComplexQZ is not initialized.");
103 return m_S;
104 }
105
110 const MatrixType& matrixT() const {
111 eigen_assert(m_isInitialized && "ComplexQZ is not initialized.");
112 return m_T;
113 }
114
123 ComplexQZ(Index n, bool computeQZ = true, unsigned int maxIters = 400)
124 : m_n(n),
125 m_maxIters(maxIters),
126 m_computeQZ(computeQZ),
127 m_S(n, n),
128 m_T(n, n),
129 m_Q(computeQZ ? n : (MatrixType::RowsAtCompileTime == Eigen::Dynamic ? 0 : MatrixType::RowsAtCompileTime),
130 computeQZ ? n : (MatrixType::ColsAtCompileTime == Eigen::Dynamic ? 0 : MatrixType::ColsAtCompileTime)),
131 m_Z(computeQZ ? n : (MatrixType::RowsAtCompileTime == Eigen::Dynamic ? 0 : MatrixType::RowsAtCompileTime),
132 computeQZ ? n : (MatrixType::ColsAtCompileTime == Eigen::Dynamic ? 0 : MatrixType::ColsAtCompileTime)),
133 m_ws(2 * n),
134 m_hCoeffs(n) {}
135
147 template <typename InputTypeA, typename InputTypeB>
148 ComplexQZ(const EigenBase<InputTypeA>& A, const EigenBase<InputTypeB>& B, bool computeQZ = true,
149 unsigned int maxIters = 400)
150 : m_n(A.rows()),
151 m_maxIters(maxIters),
152 m_computeQZ(computeQZ),
153 m_S(A.derived()),
154 m_T(B.derived()),
155 m_Q(computeQZ ? m_n : (MatrixType::RowsAtCompileTime == Eigen::Dynamic ? 0 : MatrixType::RowsAtCompileTime),
156 computeQZ ? m_n : (MatrixType::ColsAtCompileTime == Eigen::Dynamic ? 0 : MatrixType::ColsAtCompileTime)),
157 m_Z(computeQZ ? m_n : (MatrixType::RowsAtCompileTime == Eigen::Dynamic ? 0 : MatrixType::RowsAtCompileTime),
158 computeQZ ? m_n : (MatrixType::ColsAtCompileTime == Eigen::Dynamic ? 0 : MatrixType::ColsAtCompileTime)),
159 m_ws(2 * m_n),
160 m_hCoeffs(m_n) {
161 computeInPlace(computeQZ);
162 }
163
175 template <typename InputTypeA, typename InputTypeB>
176 ComplexQZ(EigenBase<InputTypeA>& A, EigenBase<InputTypeB>& B, bool computeQZ = true, unsigned int maxIters = 400)
177 : m_n(A.rows()),
178 m_maxIters(maxIters),
179 m_computeQZ(computeQZ),
180 m_S(A.derived()),
181 m_T(B.derived()),
182 m_Q(computeQZ ? m_n : (MatrixType::RowsAtCompileTime == Eigen::Dynamic ? 0 : MatrixType::RowsAtCompileTime),
183 computeQZ ? m_n : (MatrixType::ColsAtCompileTime == Eigen::Dynamic ? 0 : MatrixType::ColsAtCompileTime)),
184 m_Z(computeQZ ? m_n : (MatrixType::RowsAtCompileTime == Eigen::Dynamic ? 0 : MatrixType::RowsAtCompileTime),
185 computeQZ ? m_n : (MatrixType::ColsAtCompileTime == Eigen::Dynamic ? 0 : MatrixType::ColsAtCompileTime)),
186 m_ws(2 * m_n),
187 m_hCoeffs(m_n) {
188 computeInPlace(computeQZ);
189 }
190
197 template <typename InputTypeA, typename InputTypeB>
198 void compute(const EigenBase<InputTypeA>& A, const EigenBase<InputTypeB>& B, bool computeQZ = true);
199
208 template <typename SparseMatrixType_>
209 void computeSparse(const SparseMatrixType_& A, const SparseMatrixType_& B, bool computeQZ = true);
210
215 ComputationInfo info() const {
216 eigen_assert(m_isInitialized && "ComplexQZ is not initialized.");
217 return m_info;
218 }
219
222 unsigned int iterations() const {
223 eigen_assert(m_isInitialized && "ComplexQZ is not initialized.");
224 return m_global_iter;
225 }
226
227 private:
228 Index m_n;
229 const unsigned int m_maxIters;
230 unsigned int m_global_iter = 0;
231 bool m_isInitialized = false;
232 bool m_computeQZ;
233 ComputationInfo m_info = InvalidInput;
234 MatrixType m_S, m_T;
235 PlainMatrixType m_Q, m_Z;
236 RealScalar m_normOfT, m_normOfS;
237 Vec m_ws;
238 Vec m_hCoeffs;
239
240 // Test if a Scalar is 0 up to a certain tolerance
241 static bool is_negligible(const Scalar x, const RealScalar tol = NumTraits<RealScalar>::epsilon()) {
242 return numext::abs(x) <= tol;
243 }
244
245 void do_QZ_step(Index p, Index q, unsigned int iter);
246
247 JacobiRotation<Scalar> computeZk2(const Row2& b);
248
249 void computeInPlace(bool computeQZ);
250
251 // This is basically taken from Eigen3::RealQZ
252 void hessenbergTriangular();
253
254 // This function can be called when m_Q and m_Z are initialized and m_S, m_T
255 // are in hessenberg-triangular form
256 void reduceHessenbergTriangular();
257
258 // Sparse variant of the above method.
259 template <typename SparseMatrixType_>
260 void hessenbergTriangularSparse(const SparseMatrixType_& A, const SparseMatrixType_& B);
261
262 void computeNorms();
263
264 Index findSmallSubdiagEntry(Index l);
265 Index findSmallDiagEntry(Index f, Index l);
266
267 void push_down_zero_ST(Index k, Index l);
268
269 void reduceDiagonal2x2block(Index i);
270};
271
272template <typename MatrixType_>
273template <typename InputTypeA, typename InputTypeB>
274void ComplexQZ<MatrixType_>::compute(const EigenBase<InputTypeA>& A, const EigenBase<InputTypeB>& B, bool computeQZ) {
275 eigen_assert(A.rows() == A.cols() && "A is not a square matrix");
276 eigen_assert(A.rows() == B.rows() && A.rows() == B.cols() &&
277 "B is not a square matrix or B is not of the same size as A");
278 // Copy A and B, these will be the matrices on which we operate later
279 m_S = A.derived();
280 m_T = B.derived();
281 computeInPlace(computeQZ);
282}
283
285template <typename MatrixType_>
286void ComplexQZ<MatrixType_>::computeInPlace(bool computeQZ) {
287 m_computeQZ = computeQZ;
288 m_n = m_S.rows();
289
290 eigen_assert(m_n == m_S.cols() && "A is not a square matrix");
291 eigen_assert(m_n == m_T.rows() && m_n == m_T.cols() && "B is not a square matrix or B is not of the same size as A");
292
293 m_isInitialized = true;
294 m_global_iter = 0;
295 m_info = Success;
296
297 // This will initialize m_Q and m_Z and bring m_S, m_T to hessenberg-triangular form
298 hessenbergTriangular();
299
300 // We assume that we already have that S is upper-Hessenberg and T is
301 // upper-triangular. This is what the hessenbergTriangular(...) method does
302 reduceHessenbergTriangular();
303}
304
305// This is basically taken from Eigen3::RealQZ
306template <typename MatrixType_>
308 // Perform the QR decomposition of T in place: T holds R above the Householder vectors Q is formed from
309 m_ws.resize(2 * m_n);
310 m_hCoeffs.resize(m_n);
311 internal::householder_qr_inplace_blocked<MatrixType, Vec>::run(m_T, m_hCoeffs, 48, m_ws.data());
312 Map<Vec> workspace(m_ws.data(), m_n);
313 const auto householderQ = householderSequence(m_T, m_hCoeffs.conjugate());
314 if (m_computeQZ) householderQ.evalTo(m_Q, workspace);
315
316 // overwrite S with Q* x S
317 householderQ.adjoint().applyThisOnTheLeft(m_S, workspace);
318
319 m_T.template triangularView<StrictlyLower>().setZero();
320
321 if (m_computeQZ) m_Z = PlainMatrixType::Identity(m_n, m_n);
322
323 // reduce S to upper Hessenberg with Givens rotations
324 for (Index j = 0; j <= m_n - 3; j++) {
325 for (Index i = m_n - 1; i >= j + 2; i--) {
326 JacobiRotation<Scalar> G;
327 // delete S(i,j)
328 if (!numext::is_exactly_zero(m_S.coeff(i, j))) {
329 G.makeGivens(m_S.coeff(i - 1, j), m_S.coeff(i, j), &m_S.coeffRef(i - 1, j));
330 m_S.coeffRef(i, j) = Scalar(0);
331 m_T.rightCols(m_n - i + 1).applyOnTheLeft(i - 1, i, G.adjoint());
332 m_S.rightCols(m_n - j - 1).applyOnTheLeft(i - 1, i, G.adjoint());
333 // This is what we want to achieve
334 if (!is_negligible(m_S(i, j)))
336 else
337 m_S(i, j) = Scalar(0);
338 // update Q
339 if (m_computeQZ) m_Q.applyOnTheRight(i - 1, i, G);
340 }
341
342 if (!numext::is_exactly_zero(m_T.coeff(i, i - 1))) {
343 // Compute rotation and update matrix T
344 G.makeGivens(m_T.coeff(i, i), m_T.coeff(i, i - 1), &m_T.coeffRef(i, i));
345 m_T.topRows(i).applyOnTheRight(i - 1, i, G.adjoint());
346 m_T.coeffRef(i, i - 1) = Scalar(0);
347 // Update matrix S
348 m_S.applyOnTheRight(i - 1, i, G.adjoint());
349 // update Z
350 if (m_computeQZ) m_Z.applyOnTheLeft(i - 1, i, G);
351 }
352 }
353 }
354}
355
356template <typename MatrixType>
357template <typename SparseMatrixType_>
358void ComplexQZ<MatrixType>::hessenbergTriangularSparse(const SparseMatrixType_& A, const SparseMatrixType_& B) {
359 m_S = A.toDense();
360
361 SparseQR<SparseMatrix<Scalar, ColMajor>, NaturalOrdering<Index>> sparseQR;
362
363 eigen_assert(B.isCompressed() &&
364 "SparseQR requires a sparse matrix in compressed mode."
365 "Call .makeCompressed() before passing it to SparseQR");
366
367 // Computing QR decomposition of T...
368 sparseQR.setPivotThreshold(RealScalar(0)); // This prevents the algorithm from doing pivoting
369 sparseQR.compute(B);
370 // perform QR decomposition of T, overwrite T with R, save Q
371 m_T = sparseQR.matrixR();
372 m_T.template triangularView<StrictlyLower>().setZero();
373
374 if (m_computeQZ) m_Q = sparseQR.matrixQ();
375
376 // overwrite S with Q* S
377 m_S = sparseQR.matrixQ().adjoint() * m_S;
378
379 if (m_computeQZ) m_Z = MatrixType::Identity(m_n, m_n);
380
381 // reduce S to upper Hessenberg with Givens rotations
382 for (Index j = 0; j <= m_n - 3; j++) {
383 for (Index i = m_n - 1; i >= j + 2; i--) {
384 JacobiRotation<Scalar> G;
385 // kill S(i,j)
386 if (m_S.coeff(i, j) != Scalar(0)) {
387 // This is the adapted code
388 G.makeGivens(m_S.coeff(i - 1, j), m_S.coeff(i, j), &m_S.coeffRef(i - 1, j));
389 m_S.coeffRef(i, j) = Scalar(0);
390 m_T.rightCols(m_n - i + 1).applyOnTheLeft(i - 1, i, G.adjoint());
391 m_S.rightCols(m_n - j - 1).applyOnTheLeft(i - 1, i, G.adjoint());
392 // This is what we want to achieve
393 if (!is_negligible(m_S(i, j))) {
395 }
396 m_S(i, j) = Scalar(0);
397 // update Q
398 if (m_computeQZ) m_Q.applyOnTheRight(i - 1, i, G);
399 }
400
401 if (!numext::is_exactly_zero(m_T.coeff(i, i - 1))) {
402 // Compute rotation and update matrix T
403 G.makeGivens(m_T.coeff(i, i), m_T.coeff(i, i - 1), &m_T.coeffRef(i, i));
404 m_T.topRows(i).applyOnTheRight(i - 1, i, G.adjoint());
405 m_T.coeffRef(i, i - 1) = Scalar(0);
406 // Update matrix S
407 m_S.applyOnTheRight(i - 1, i, G.adjoint());
408 // update Z
409 if (m_computeQZ) m_Z.applyOnTheLeft(i - 1, i, G);
410 }
411 }
412 }
413}
414
415template <typename MatrixType>
416template <typename SparseMatrixType_>
417void ComplexQZ<MatrixType>::computeSparse(const SparseMatrixType_& A, const SparseMatrixType_& B, bool computeQZ) {
418 m_computeQZ = computeQZ;
419 m_n = A.rows();
420 eigen_assert(m_n == A.cols() && "A is not a square matrix");
421 eigen_assert(m_n == B.rows() && m_n == B.cols() && "B is not a square matrix or B is not of the same size as A");
422 m_isInitialized = true;
423 m_global_iter = 0;
424 m_info = Success;
425 hessenbergTriangularSparse(A, B);
426
427 // We assume that we already have that A is upper-Hessenberg and B is
428 // upper-triangular. This is what the hessenbergTriangular(...) method does
429 reduceHessenbergTriangular();
430}
431
432template <typename MatrixType_>
434 m_ws.resize(2 * m_n);
435 Index l = m_n - 1, f;
436 unsigned int local_iter = 0;
437 computeNorms();
438
439 while (l > 0 && local_iter < m_maxIters) {
440 f = findSmallSubdiagEntry(l);
441
442 // Subdiag entry is small -> can be safely set to 0
443 if (f > 0) {
444 m_S.coeffRef(f, f - 1) = Scalar(0);
445 }
446 if (f == l) { // One root found
447 l--;
448 local_iter = 0;
449 } else if (f == l - 1) { // Two roots found
450 // We found an undesired non-zero at (f+1,f) in S and eliminate it immediately
451 reduceDiagonal2x2block(f);
452 l -= 2;
453 local_iter = 0;
454 } else {
455 Index z = findSmallDiagEntry(f, l);
456 if (z >= f) {
457 push_down_zero_ST(z, l);
458 } else {
459 do_QZ_step(f, m_n - l - 1, local_iter);
460 local_iter++;
461 m_global_iter++;
462 }
463 }
464 }
465
466 // Preserve NumericalIssue if set
467 if (m_info != NumericalIssue) {
468 m_info = (local_iter < m_maxIters) ? Success : NoConvergence;
469 }
470}
471
472template <typename MatrixType_>
473JacobiRotation<typename ComplexQZ<MatrixType_>::Scalar> ComplexQZ<MatrixType_>::computeZk2(const Row2& b) {
474 JacobiRotation<Scalar> J;
475 J.makeGivens(numext::conj(b(1)), numext::conj(b(0)));
476 // S J S = J.transpose() for the exchange matrix S and a real Givens cosine.
477 return J.transpose();
478}
479
480template <typename MatrixType_>
481void ComplexQZ<MatrixType_>::do_QZ_step(Index p, Index q, unsigned int iter) {
482 // This is certainly not the most efficient way of doing this,
483 // but a readable one.
484 const auto a = [p, this](Index i, Index j) { return m_S(p + i - 1, p + j - 1); };
485 const auto b = [p, this](Index i, Index j) { return m_T(p + i - 1, p + j - 1); };
486 const Index m = m_n - p - q; // Size of the inner block
487 Scalar x, y, z;
488 if (iter > 0 && iter % 10 == 0) {
489 // Break stalled double-shift iterations with a single shift displaced by the trailing subdiagonals.
490 const RealScalar displacement =
491 numext::abs(a(m, m - 1) / b(m - 1, m - 1)) + numext::abs(a(m - 1, m - 2) / b(m - 2, m - 2));
492 const Scalar shift = a(m, m) / b(m, m) + displacement;
493 // (S*T^-1 - shift*I)*e1 has only two nonzero entries.
494 x = a(1, 1) / b(1, 1) - shift;
495 y = a(2, 1) / b(1, 1);
496 z = Scalar(0);
497 } else {
498 Scalar W1 = a(m - 1, m - 1) / b(m - 1, m - 1) - a(1, 1) / b(1, 1), W2 = a(m, m) / b(m, m) - a(1, 1) / b(1, 1),
499 W3 = a(m, m - 1) / b(m - 1, m - 1);
500
501 x = (W1 * W2 - a(m - 1, m) / b(m, m) * W3 + W3 * b(m - 1, m) / b(m, m) * a(1, 1) / b(1, 1)) * b(1, 1) / a(2, 1) +
502 a(1, 2) / b(2, 2) - a(1, 1) / b(1, 1) * b(1, 2) / b(2, 2);
503 y = (a(2, 2) / b(2, 2) - a(1, 1) / b(1, 1)) - a(2, 1) / b(1, 1) * b(1, 2) / b(2, 2) - W1 - W2 +
504 W3 * (b(m - 1, m) / b(m, m));
505 z = a(3, 2) / b(2, 2);
506 }
507 Vec3 X;
508 const PermutationMatrix<3, 3, int> S3(Vector3i(2, 0, 1));
509 for (Index k = p; k < p + m - 2; k++) {
510 X << x, y, z;
511 Vec2 ess;
512 Scalar tau;
513 RealScalar beta;
514 X.makeHouseholder(ess, tau, beta);
515 // The permutations are needed because the makeHouseHolder-method computes
516 // the householder transformation in a way that the vector is reflected to
517 // (1 0 ... 0) instead of (0 ... 0 1)
518 m_S.template middleRows<3>(k)
519 .rightCols((std::min)(m_n, m_n - k + 1))
520 .applyHouseholderOnTheLeft(ess, tau, m_ws.data());
521 m_T.template middleRows<3>(k).rightCols(m_n - k).applyHouseholderOnTheLeft(ess, tau, m_ws.data());
522 if (m_computeQZ) m_Q.template middleCols<3>(k).applyHouseholderOnTheRight(ess, numext::conj(tau), m_ws.data());
523
524 // Compute Matrix Zk1 s.t. (b(k+2,k) ... b(k+2, k+2)) Zk1 = (0,0,*)
525 Vec3 bprime = (m_T.template block<1, 3>(k + 2, k) * S3).adjoint();
526 bprime.makeHouseholder(ess, tau, beta);
527 auto Sk = m_S.template middleCols<3>(k).topRows((std::min)(k + 4, m_n));
528 auto Tk = m_T.template middleCols<3>(k).topRows((std::min)(k + 3, m_n));
529 // Right multiplication by S3 permutes columns as (2, 0, 1); reverse the swaps for S3^T.
530 Sk.col(0).swap(Sk.col(2));
531 Sk.col(1).swap(Sk.col(2));
532 Sk.applyHouseholderOnTheRight(ess, numext::conj(tau), m_ws.data());
533 Sk.col(1).swap(Sk.col(2));
534 Sk.col(0).swap(Sk.col(2));
535 Tk.col(0).swap(Tk.col(2));
536 Tk.col(1).swap(Tk.col(2));
537 Tk.applyHouseholderOnTheRight(ess, numext::conj(tau), m_ws.data());
538 Tk.col(1).swap(Tk.col(2));
539 Tk.col(0).swap(Tk.col(2));
540 if (m_computeQZ) {
541 auto Zk = m_Z.template middleRows<3>(k);
542 Zk.row(0).swap(Zk.row(2));
543 Zk.row(1).swap(Zk.row(2));
544 Zk.applyHouseholderOnTheLeft(ess, tau, m_ws.data());
545 Zk.row(1).swap(Zk.row(2));
546 Zk.row(0).swap(Zk.row(2));
547 }
548 const JacobiRotation<Scalar> Zk2 = computeZk2(m_T.template block<1, 2>(k + 1, k));
549 m_S.template middleCols<2>(k).topRows((std::min)(k + 4, m_n)).applyOnTheRight(0, 1, Zk2);
550 m_T.template middleCols<2>(k).topRows((std::min)(k + 3, m_n)).applyOnTheRight(0, 1, Zk2);
551
552 if (m_computeQZ) m_Z.template middleRows<2>(k).applyOnTheLeft(0, 1, Zk2.adjoint());
553
554 x = m_S(k + 1, k);
555 y = m_S(k + 2, k);
556 if (k < p + m - 3) {
557 z = m_S(k + 3, k);
558 }
559 }
560
561 // Find a Householder matrix Qn1 s.t. Qn1 (x y)^T = (* 0)
562 JacobiRotation<Scalar> J;
563 J.makeGivens(x, y);
564 m_S.template middleRows<2>(p + m - 2).applyOnTheLeft(0, 1, J.adjoint());
565 m_T.template middleRows<2>(p + m - 2).applyOnTheLeft(0, 1, J.adjoint());
566
567 if (m_computeQZ) m_Q.template middleCols<2>(p + m - 2).applyOnTheRight(0, 1, J);
568
569 // Find a rotation Zn1 s.t. (b(n,n-1) b(n,n)) * Zn1 = (0 *)
570 const JacobiRotation<Scalar> Zn1 = computeZk2(m_T.template block<1, 2>(p + m - 1, p + m - 2));
571 m_S.template middleCols<2>(p + m - 2).applyOnTheRight(0, 1, Zn1);
572 m_T.template middleCols<2>(p + m - 2).applyOnTheRight(0, 1, Zn1);
573
574 if (m_computeQZ) m_Z.template middleRows<2>(p + m - 2).applyOnTheLeft(0, 1, Zn1.adjoint());
575}
576
578template <typename MatrixType_>
580 // We have found a non-zero on the subdiagonal and want to eliminate it
581 Mat2 Si = m_S.template block<2, 2>(i, i), Ti = m_T.template block<2, 2>(i, i);
582 const RealScalar tolT = m_normOfT * NumTraits<RealScalar>::epsilon();
583 if (is_negligible(Ti(0, 0), tolT)) {
585 G.makeGivens(m_S(i, i), m_S(i + 1, i));
586 m_S.applyOnTheLeft(i, i + 1, G.adjoint());
587 m_T.applyOnTheLeft(i, i + 1, G.adjoint());
588
589 if (m_computeQZ) m_Q.applyOnTheRight(i, i + 1, G);
590
591 } else if (is_negligible(Ti(1, 1), tolT)) {
593 G.makeGivens(m_S(i + 1, i + 1), m_S(i + 1, i));
594 m_S.applyOnTheRight(i, i + 1, G.adjoint());
595 m_T.applyOnTheRight(i, i + 1, G.adjoint());
596 if (m_computeQZ) m_Z.applyOnTheLeft(i, i + 1, G);
597 } else {
598 Scalar mu = Si(0, 0) / Ti(0, 0);
599 Scalar a12_bar = Si(0, 1) - mu * Ti(0, 1);
600 Scalar a22_bar = Si(1, 1) - mu * Ti(1, 1);
601 Scalar p = Scalar(0.5) * (a22_bar / Ti(1, 1) - Ti(0, 1) * Si(1, 0) / (Ti(0, 0) * Ti(1, 1)));
602 RealScalar sgn_p = p.real() >= RealScalar(0) ? RealScalar(1) : RealScalar(-1);
603 Scalar q = Si(1, 0) * a12_bar / (Ti(0, 0) * Ti(1, 1));
604 Scalar r = p * p + q;
605 Scalar lambda = mu + p + sgn_p * numext::sqrt(r);
606 Mat2 E = Si - lambda * Ti;
607 Index l;
608 E.rowwise().norm().maxCoeff(&l);
609 JacobiRotation<Scalar> G;
610 G.makeGivens(E(l, 1), E(l, 0));
611 m_S.applyOnTheRight(i, i + 1, G.adjoint());
612 m_T.applyOnTheRight(i, i + 1, G.adjoint());
613
614 if (m_computeQZ) m_Z.applyOnTheLeft(i, i + 1, G);
615
616 Mat2 tildeSi = m_S.template block<2, 2>(i, i), tildeTi = m_T.template block<2, 2>(i, i);
617 Mat2 C = tildeSi.norm() < (lambda * tildeTi).norm() ? tildeSi : lambda * tildeTi;
618 G.makeGivens(C(0, 0), C(1, 0));
619 m_S.applyOnTheLeft(i, i + 1, G.adjoint());
620 m_T.applyOnTheLeft(i, i + 1, G.adjoint());
621
622 if (m_computeQZ) m_Q.applyOnTheRight(i, i + 1, G);
623 }
624
625 if (!is_negligible(m_S(i + 1, i), m_normOfS * NumTraits<RealScalar>::epsilon())) {
627 } else {
628 m_S(i + 1, i) = Scalar(0);
629 }
630}
631
633template <typename MatrixType_>
634void ComplexQZ<MatrixType_>::push_down_zero_ST(Index k, Index l) {
635 JacobiRotation<Scalar> J;
636 for (Index j = k + 1; j <= l; j++) {
637 // Create a 0 at _T(j, j)
638 J.makeGivens(m_T(j - 1, j), m_T(j, j), &m_T.coeffRef(j - 1, j));
639 if (m_n - j - 1 > 0) {
640 m_T.rightCols(m_n - j - 1).applyOnTheLeft(j - 1, j, J.adjoint());
641 }
642 m_T.coeffRef(j, j) = Scalar(0);
643
644 m_S.applyOnTheLeft(j - 1, j, J.adjoint());
645
646 if (m_computeQZ) m_Q.applyOnTheRight(j - 1, j, J);
647
648 // Delete the non-desired non-zero at _S(j, j-2)
649 if (j > 1) {
650 J.makeGivens(numext::conj(m_S(j, j - 1)), numext::conj(m_S(j, j - 2)));
651 m_S.applyOnTheRight(j - 1, j - 2, J);
652 m_S(j, j - 2) = Scalar(0);
653 m_T.applyOnTheRight(j - 1, j - 2, J);
654 if (m_computeQZ) m_Z.applyOnTheLeft(j - 1, j - 2, J.adjoint());
655 }
656 }
657
658 // Assume we have the desired structure now, up to the non-zero entry at
659 // _S(l, l-1) which we will delete through a last right-jacobi-rotation
660 J.makeGivens(numext::conj(m_S(l, l)), numext::conj(m_S(l, l - 1)));
661 m_S.topRows(l + 1).applyOnTheRight(l, l - 1, J);
662
663 if (!is_negligible(m_S(l, l - 1), m_normOfS * NumTraits<Scalar>::epsilon())) {
665 } else {
666 m_S(l, l - 1) = Scalar(0);
667 }
668 m_T.topRows(l + 1).applyOnTheRight(l, l - 1, J);
669
670 if (m_computeQZ) m_Z.applyOnTheLeft(l, l - 1, J.adjoint());
671
672 // Ensure postconditions
673 if (!is_negligible(m_T(l, l)) || !is_negligible(m_S(l, l - 1))) {
675 } else {
676 m_T(l, l) = Scalar(0);
677 m_S(l, l - 1) = Scalar(0);
678 }
679}
680
682template <typename MatrixType_>
684 m_normOfS = internal::hessenberg_abs_sum<Upper>(m_S);
685 m_normOfT = internal::triangular_abs_sum<Upper>(m_T);
686}
687
690template <typename MatrixType_>
692 Index res = iu;
693 while (res > 0) {
694 RealScalar s = numext::abs(m_S.coeff(res - 1, res - 1)) + numext::abs(m_S.coeff(res, res));
695 if (s == Scalar(0)) s = m_normOfS;
696 if (numext::abs(m_S.coeff(res, res - 1)) < NumTraits<RealScalar>::epsilon() * s) break;
697 res--;
698 }
699 return res;
700}
701
704template <typename MatrixType_>
705inline Index ComplexQZ<MatrixType_>::findSmallDiagEntry(Index f, Index l) {
706 Index res = l;
707 while (res >= f) {
708 if (numext::abs(m_T.coeff(res, res)) <= NumTraits<RealScalar>::epsilon() * m_normOfT) break;
709 res--;
710 }
711 return res;
712}
713
714} // namespace Eigen
715
716#endif // EIGEN_COMPLEX_QZ_H_
Performs a QZ decomposition of a pair of matrices A, B.
Rotation given by a cosine-sine pair.
Definition Jacobi.h:39
JacobiRotation adjoint() const
Definition Jacobi.h:68
void makeGivens(const Scalar &p, const Scalar &q, Scalar *r=0)
Definition Jacobi.h:165
HouseholderSequence< VectorsType, CoeffsType > householderSequence(const VectorsType &v, const CoeffsType &h)
Convenience function for constructing a Householder sequence.
Definition HouseholderSequence.h:673
@ NumericalIssue
Definition Constants.h:459
@ InvalidInput
Definition Constants.h:464
@ Success
Definition Constants.h:457
@ NoConvergence
Definition Constants.h:461
Matrix< int, 3, 1 > Vector3i
3×1 vector of type int.
Definition Matrix.h:487