/* -*- c++ -*- (enables emacs c++ mode) */ /*=========================================================================== Copyright (C) 2003-2017 Yves Renard This file is a part of GetFEM++ GetFEM++ is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version along with the GCC Runtime Library Exception either version 3.1 or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License and GCC Runtime Library Exception for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, you may use this file as it is a part of a free software library without restriction. Specifically, if other files instantiate templates or use macros or inline functions from this file, or you compile this file and link it with other files to produce an executable, this file does not by itself cause the resulting executable to be covered by the GNU Lesser General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Lesser General Public License. ===========================================================================*/ /**@file gmm_dense_qr.h @author Caroline Lecalvez, Caroline.Lecalvez@gmm.insa-tlse.fr, Yves Renard @date September 12, 2003. @brief Dense QR factorization. */ #ifndef GMM_DENSE_QR_H #define GMM_DENSE_QR_H #include "gmm_dense_Householder.h" namespace gmm { /** QR factorization using Householder method (complex and real version). */ template void qr_factor(const MAT1 &A_) { MAT1 &A = const_cast(A_); typedef typename linalg_traits::value_type value_type; size_type m = mat_nrows(A), n = mat_ncols(A); GMM_ASSERT2(m >= n, "dimensions mismatch"); std::vector W(m), V(m); for (size_type j = 0; j < n; ++j) { sub_interval SUBI(j, m-j), SUBJ(j, n-j); V.resize(m-j); W.resize(n-j); for (size_type i = j; i < m; ++i) V[i-j] = A(i, j); house_vector(V); row_house_update(sub_matrix(A, SUBI, SUBJ), V, W); for (size_type i = j+1; i < m; ++i) A(i, j) = V[i-j]; } } // QR comes from QR_factor(QR) where the upper triangular part stands for R // and the lower part contains the Householder reflectors. // A <- AQ template void apply_house_right(const MAT1 &QR, const MAT2 &A_) { MAT2 &A = const_cast(A_); typedef typename linalg_traits::value_type T; size_type m = mat_nrows(QR), n = mat_ncols(QR); GMM_ASSERT2(m == mat_ncols(A), "dimensions mismatch"); if (m == 0) return; std::vector V(m), W(mat_nrows(A)); V[0] = T(1); for (size_type j = 0; j < n; ++j) { V.resize(m-j); for (size_type i = j+1; i < m; ++i) V[i-j] = QR(i, j); col_house_update(sub_matrix(A, sub_interval(0, mat_nrows(A)), sub_interval(j, m-j)), V, W); } } // QR comes from QR_factor(QR) where the upper triangular part stands for R // and the lower part contains the Householder reflectors. // A <- Q*A template void apply_house_left(const MAT1 &QR, const MAT2 &A_) { MAT2 &A = const_cast(A_); typedef typename linalg_traits::value_type T; size_type m = mat_nrows(QR), n = mat_ncols(QR); GMM_ASSERT2(m == mat_nrows(A), "dimensions mismatch"); if (m == 0) return; std::vector V(m), W(mat_ncols(A)); V[0] = T(1); for (size_type j = 0; j < n; ++j) { V.resize(m-j); for (size_type i = j+1; i < m; ++i) V[i-j] = QR(i, j); row_house_update(sub_matrix(A, sub_interval(j, m-j), sub_interval(0, mat_ncols(A))), V, W); } } /** Compute the QR factorization, where Q is assembled. */ template void qr_factor(const MAT1 &A, const MAT2 &QQ, const MAT3 &RR) { MAT2 &Q = const_cast(QQ); MAT3 &R = const_cast(RR); typedef typename linalg_traits::value_type value_type; size_type m = mat_nrows(A), n = mat_ncols(A); GMM_ASSERT2(m >= n, "dimensions mismatch"); gmm::copy(A, Q); std::vector W(m); dense_matrix VV(m, n); for (size_type j = 0; j < n; ++j) { sub_interval SUBI(j, m-j), SUBJ(j, n-j); for (size_type i = j; i < m; ++i) VV(i,j) = Q(i, j); house_vector(sub_vector(mat_col(VV,j), SUBI)); row_house_update(sub_matrix(Q, SUBI, SUBJ), sub_vector(mat_col(VV,j), SUBI), sub_vector(W, SUBJ)); } gmm::copy(sub_matrix(Q, sub_interval(0, n), sub_interval(0, n)), R); gmm::copy(identity_matrix(), Q); for (size_type j = n-1; j != size_type(-1); --j) { sub_interval SUBI(j, m-j), SUBJ(j, n-j); row_house_update(sub_matrix(Q, SUBI, SUBJ), sub_vector(mat_col(VV,j), SUBI), sub_vector(W, SUBJ)); } } ///@cond DOXY_SHOW_ALL_FUNCTIONS template void extract_eig(const MAT &A, VECT &V, Ttol tol, TA, TV) { size_type n = mat_nrows(A); if (n == 0) return; tol *= Ttol(2); Ttol tol_i = tol * gmm::abs(A(0,0)), tol_cplx = tol_i; for (size_type i = 0; i < n; ++i) { if (i < n-1) { tol_i = (gmm::abs(A(i,i))+gmm::abs(A(i+1,i+1)))*tol; tol_cplx = std::max(tol_cplx, tol_i); } if ((i < n-1) && gmm::abs(A(i+1,i)) >= tol_i) { TA tr = A(i,i) + A(i+1, i+1); TA det = A(i,i)*A(i+1, i+1) - A(i,i+1)*A(i+1, i); TA delta = tr*tr - TA(4) * det; if (delta < -tol_cplx) { GMM_WARNING1("A complex eigenvalue has been detected : " << std::complex(tr/TA(2), gmm::sqrt(-delta)/TA(2))); V[i] = V[i+1] = tr / TA(2); } else { delta = std::max(TA(0), delta); V[i ] = TA(tr + gmm::sqrt(delta))/ TA(2); V[i+1] = TA(tr - gmm::sqrt(delta))/ TA(2); } ++i; } else V[i] = TV(A(i,i)); } } template void extract_eig(const MAT &A, VECT &V, Ttol tol, TA, std::complex) { size_type n = mat_nrows(A); tol *= Ttol(2); for (size_type i = 0; i < n; ++i) if ((i == n-1) || gmm::abs(A(i+1,i)) < (gmm::abs(A(i,i))+gmm::abs(A(i+1,i+1)))*tol) V[i] = std::complex(A(i,i)); else { TA tr = A(i,i) + A(i+1, i+1); TA det = A(i,i)*A(i+1, i+1) - A(i,i+1)*A(i+1, i); TA delta = tr*tr - TA(4) * det; if (delta < TA(0)) { V[i] = std::complex(tr / TA(2), gmm::sqrt(-delta) / TA(2)); V[i+1] = std::complex(tr / TA(2), -gmm::sqrt(-delta)/ TA(2)); } else { V[i ] = TA(tr + gmm::sqrt(delta)) / TA(2); V[i+1] = TA(tr - gmm::sqrt(delta)) / TA(2); } ++i; } } template void extract_eig(const MAT &A, VECT &V, Ttol tol, std::complex, TV) { typedef std::complex T; size_type n = mat_nrows(A); if (n == 0) return; tol *= Ttol(2); Ttol tol_i = tol * gmm::abs(A(0,0)), tol_cplx = tol_i; for (size_type i = 0; i < n; ++i) { if (i < n-1) { tol_i = (gmm::abs(A(i,i))+gmm::abs(A(i+1,i+1)))*tol; tol_cplx = std::max(tol_cplx, tol_i); } if ((i == n-1) || gmm::abs(A(i+1,i)) < tol_i) { if (gmm::abs(std::imag(A(i,i))) > tol_cplx) GMM_WARNING1("A complex eigenvalue has been detected : " << T(A(i,i)) << " : " << gmm::abs(std::imag(A(i,i))) / gmm::abs(std::real(A(i,i))) << " : " << tol_cplx); V[i] = std::real(A(i,i)); } else { T tr = A(i,i) + A(i+1, i+1); T det = A(i,i)*A(i+1, i+1) - A(i,i+1)*A(i+1, i); T delta = tr*tr - TA(4) * det; T a1 = (tr + gmm::sqrt(delta)) / TA(2); T a2 = (tr - gmm::sqrt(delta)) / TA(2); if (gmm::abs(std::imag(a1)) > tol_cplx) GMM_WARNING1("A complex eigenvalue has been detected : " << a1); if (gmm::abs(std::imag(a2)) > tol_cplx) GMM_WARNING1("A complex eigenvalue has been detected : " << a2); V[i] = std::real(a1); V[i+1] = std::real(a2); ++i; } } } template void extract_eig(const MAT &A, VECT &V, Ttol tol, std::complex, std::complex) { size_type n = mat_nrows(A); tol *= Ttol(2); for (size_type i = 0; i < n; ++i) if ((i == n-1) || gmm::abs(A(i+1,i)) < (gmm::abs(A(i,i))+gmm::abs(A(i+1,i+1)))*tol) V[i] = std::complex(A(i,i)); else { std::complex tr = A(i,i) + A(i+1, i+1); std::complex det = A(i,i)*A(i+1, i+1) - A(i,i+1)*A(i+1, i); std::complex delta = tr*tr - TA(4) * det; V[i] = (tr + gmm::sqrt(delta)) / TA(2); V[i+1] = (tr - gmm::sqrt(delta)) / TA(2); ++i; } } ///@endcond /** Compute eigenvalue vector. */ template inline void extract_eig(const MAT &A, const VECT &V, Ttol tol) { extract_eig(A, const_cast(V), tol, typename linalg_traits::value_type(), typename linalg_traits::value_type()); } /* ********************************************************************* */ /* Stop criterion for QR algorithms */ /* ********************************************************************* */ template void qr_stop_criterion(MAT &A, size_type &p, size_type &q, Ttol tol) { typedef typename linalg_traits::value_type T; typedef typename number_traits::magnitude_type R; R rmin = default_min(R()) * R(2); size_type n = mat_nrows(A); if (n <= 2) { q = n; p = 0; } else { for (size_type i = 1; i < n-q; ++i) if (gmm::abs(A(i,i-1)) < (gmm::abs(A(i,i))+ gmm::abs(A(i-1,i-1)))*tol || gmm::abs(A(i,i-1)) < rmin) A(i,i-1) = T(0); while ((q < n-1 && A(n-1-q, n-2-q) == T(0)) || (q < n-2 && A(n-2-q, n-3-q) == T(0))) ++q; if (q >= n-2) q = n; p = n-q; if (p) --p; if (p) --p; while (p > 0 && A(p,p-1) != T(0)) --p; } } template inline void symmetric_qr_stop_criterion(const MAT &AA, size_type &p, size_type &q, Ttol tol) { typedef typename linalg_traits::value_type T; typedef typename number_traits::magnitude_type R; R rmin = default_min(R()) * R(2); MAT& A = const_cast(AA); size_type n = mat_nrows(A); if (n <= 1) { q = n; p = 0; } else { for (size_type i = 1; i < n-q; ++i) if (gmm::abs(A(i,i-1)) < (gmm::abs(A(i,i))+ gmm::abs(A(i-1,i-1)))*tol || gmm::abs(A(i,i-1)) < rmin) A(i,i-1) = T(0); while (q < n-1 && A(n-1-q, n-2-q) == T(0)) ++q; if (q >= n-1) q = n; p = n-q; if (p) --p; if (p) --p; while (p > 0 && A(p,p-1) != T(0)) --p; } } template inline void symmetric_qr_stop_criterion(const VECT1 &diag, const VECT2 &sdiag_, size_type &p, size_type &q, Ttol tol) { typedef typename linalg_traits::value_type T; typedef typename number_traits::magnitude_type R; R rmin = default_min(R()) * R(2); VECT2 &sdiag = const_cast(sdiag_); size_type n = vect_size(diag); if (n <= 1) { q = n; p = 0; return; } for (size_type i = 1; i < n-q; ++i) if (gmm::abs(sdiag[i-1]) < (gmm::abs(diag[i])+ gmm::abs(diag[i-1]))*tol || gmm::abs(sdiag[i-1]) < rmin) sdiag[i-1] = T(0); while (q < n-1 && sdiag[n-2-q] == T(0)) ++q; if (q >= n-1) q = n; p = n-q; if (p) --p; if (p) --p; while (p > 0 && sdiag[p-1] != T(0)) --p; } /* ********************************************************************* */ /* 2x2 blocks reduction for Schur vectors */ /* ********************************************************************* */ template void block2x2_reduction(MATH &H, MATQ &Q, Ttol tol) { typedef typename linalg_traits::value_type T; typedef typename number_traits::magnitude_type R; size_type n = mat_nrows(H), nq = mat_nrows(Q); if (n < 2) return; sub_interval SUBQ(0, nq), SUBL(0, 2); std::vector v(2), w(std::max(n, nq)); v[0] = T(1); tol *= Ttol(2); Ttol tol_i = tol * gmm::abs(H(0,0)), tol_cplx = tol_i; for (size_type i = 0; i < n-1; ++i) { tol_i = (gmm::abs(H(i,i))+gmm::abs(H(i+1,i+1)))*tol; tol_cplx = std::max(tol_cplx, tol_i); if (gmm::abs(H(i+1,i)) > tol_i) { // 2x2 block detected T tr = (H(i+1, i+1) - H(i,i)) / T(2); T delta = tr*tr + H(i,i+1)*H(i+1, i); if (is_complex(T()) || gmm::real(delta) >= R(0)) { sub_interval SUBI(i, 2); T theta = (tr - gmm::sqrt(delta)) / H(i+1,i); R a = gmm::abs(theta); v[1] = (a == R(0)) ? T(-1) : gmm::conj(theta) * (R(1) - gmm::sqrt(a*a + R(1)) / a); row_house_update(sub_matrix(H, SUBI), v, sub_vector(w, SUBL)); col_house_update(sub_matrix(H, SUBI), v, sub_vector(w, SUBL)); col_house_update(sub_matrix(Q, SUBQ, SUBI), v, sub_vector(w, SUBQ)); } ++i; } } } /* ********************************************************************* */ /* Basic qr algorithm. */ /* ********************************************************************* */ #define tol_type_for_qr typename number_traits::value_type>::magnitude_type #define default_tol_for_qr \ (gmm::default_tol(tol_type_for_qr()) * tol_type_for_qr(3)) // QR method for real or complex square matrices based on QR factorisation. // eigval has to be a complex vector if A has complex eigeinvalues. // Very slow method. Use implicit_qr_method instead. template void rudimentary_qr_algorithm(const MAT1 &A, const VECT &eigval_, const MAT2 &eigvect_, tol_type_for_qr tol = default_tol_for_qr, bool compvect = true) { VECT &eigval = const_cast(eigval_); MAT2 &eigvect = const_cast(eigvect_); typedef typename linalg_traits::value_type value_type; size_type n = mat_nrows(A), p, q = 0, ite = 0; dense_matrix Q(n, n), R(n,n), A1(n,n); gmm::copy(A, A1); Hessenberg_reduction(A1, eigvect, compvect); qr_stop_criterion(A1, p, q, tol); while (q < n) { qr_factor(A1, Q, R); gmm::mult(R, Q, A1); if (compvect) { gmm::mult(eigvect, Q, R); gmm::copy(R, eigvect); } qr_stop_criterion(A1, p, q, tol); ++ite; GMM_ASSERT1(ite < n*1000, "QR algorithm failed"); } if (compvect) block2x2_reduction(A1, Q, tol); extract_eig(A1, eigval, tol); } template void rudimentary_qr_algorithm(const MAT1 &a, VECT &eigval, tol_type_for_qr tol = default_tol_for_qr) { dense_matrix::value_type> m(0,0); rudimentary_qr_algorithm(a, eigval, m, tol, false); } /* ********************************************************************* */ /* Francis QR step. */ /* ********************************************************************* */ template void Francis_qr_step(const MAT1& HH, const MAT2 &QQ, bool compute_Q) { MAT1& H = const_cast(HH); MAT2& Q = const_cast(QQ); typedef typename linalg_traits::value_type value_type; size_type n = mat_nrows(H), nq = mat_nrows(Q); std::vector v(3), w(std::max(n, nq)); value_type s = H(n-2, n-2) + H(n-1, n-1); value_type t = H(n-2, n-2) * H(n-1, n-1) - H(n-2, n-1) * H(n-1, n-2); value_type x = H(0, 0) * H(0, 0) + H(0,1) * H(1, 0) - s * H(0,0) + t; value_type y = H(1, 0) * (H(0,0) + H(1,1) - s); value_type z = H(1, 0) * H(2, 1); sub_interval SUBQ(0, nq); for (size_type k = 0; k < n - 2; ++k) { v[0] = x; v[1] = y; v[2] = z; house_vector(v); size_type r = std::min(k+4, n), q = (k==0) ? 0 : k-1; sub_interval SUBI(k, 3), SUBJ(0, r), SUBK(q, n-q); row_house_update(sub_matrix(H, SUBI, SUBK), v, sub_vector(w, SUBK)); col_house_update(sub_matrix(H, SUBJ, SUBI), v, sub_vector(w, SUBJ)); if (compute_Q) col_house_update(sub_matrix(Q, SUBQ, SUBI), v, sub_vector(w, SUBQ)); x = H(k+1, k); y = H(k+2, k); if (k < n-3) z = H(k+3, k); } sub_interval SUBI(n-2,2), SUBJ(0, n), SUBK(n-3,3), SUBL(0, 3); v.resize(2); v[0] = x; v[1] = y; house_vector(v); row_house_update(sub_matrix(H, SUBI, SUBK), v, sub_vector(w, SUBL)); col_house_update(sub_matrix(H, SUBJ, SUBI), v, sub_vector(w, SUBJ)); if (compute_Q) col_house_update(sub_matrix(Q, SUBQ, SUBI), v, sub_vector(w, SUBQ)); } /* ********************************************************************* */ /* Wilkinson Double shift QR step (from Lapack). */ /* ********************************************************************* */ template void Wilkinson_double_shift_qr_step(const MAT1& HH, const MAT2 &QQ, Ttol tol, bool exc, bool compute_Q) { MAT1& H = const_cast(HH); MAT2& Q = const_cast(QQ); typedef typename linalg_traits::value_type T; typedef typename number_traits::magnitude_type R; size_type n = mat_nrows(H), nq = mat_nrows(Q), m; std::vector v(3), w(std::max(n, nq)); const R dat1(0.75), dat2(-0.4375); T h33, h44, h43h34, v1(0), v2(0), v3(0); if (exc) { /* Exceptional shift. */ R s = gmm::abs(H(n-1, n-2)) + gmm::abs(H(n-2, n-3)); h33 = h44 = dat1 * s; h43h34 = dat2*s*s; } else { /* Wilkinson double shift. */ h44 = H(n-1,n-1); h33 = H(n-2, n-2); h43h34 = H(n-1, n-2) * H(n-2, n-1); } /* Look for two consecutive small subdiagonal elements. */ /* Determine the effect of starting the double-shift QR iteration at */ /* row m, and see if this would make H(m-1, m-2) negligible. */ for (m = n-2; m != 0; --m) { T h11 = H(m-1, m-1), h22 = H(m, m); T h21 = H(m, m-1), h12 = H(m-1, m); T h44s = h44 - h11, h33s = h33 - h11; v1 = (h33s*h44s-h43h34) / h21 + h12; v2 = h22 - h11 - h33s - h44s; v3 = H(m+1, m); R s = gmm::abs(v1) + gmm::abs(v2) + gmm::abs(v3); v1 /= s; v2 /= s; v3 /= s; if (m == 1) break; T h00 = H(m-2, m-2); T h10 = H(m-1, m-2); R tst1 = gmm::abs(v1)*(gmm::abs(h00)+gmm::abs(h11)+gmm::abs(h22)); if (gmm::abs(h10)*(gmm::abs(v2)+gmm::abs(v3)) <= tol * tst1) break; } /* Double shift QR step. */ sub_interval SUBQ(0, nq); for (size_type k = (m == 0) ? 0 : m-1; k < n-2; ++k) { v[0] = v1; v[1] = v2; v[2] = v3; house_vector(v); size_type r = std::min(k+4, n), q = (k==0) ? 0 : k-1; sub_interval SUBI(k, 3), SUBJ(0, r), SUBK(q, n-q); row_house_update(sub_matrix(H, SUBI, SUBK), v, sub_vector(w, SUBK)); col_house_update(sub_matrix(H, SUBJ, SUBI), v, sub_vector(w, SUBJ)); if (k > m-1) { H(k+1, k-1) = T(0); if (k < n-3) H(k+2, k-1) = T(0); } if (compute_Q) col_house_update(sub_matrix(Q, SUBQ, SUBI), v, sub_vector(w, SUBQ)); v1 = H(k+1, k); v2 = H(k+2, k); if (k < n-3) v3 = H(k+3, k); } sub_interval SUBI(n-2,2), SUBJ(0, n), SUBK(n-3,3), SUBL(0, 3); v.resize(2); v[0] = v1; v[1] = v2; house_vector(v); row_house_update(sub_matrix(H, SUBI, SUBK), v, sub_vector(w, SUBL)); col_house_update(sub_matrix(H, SUBJ, SUBI), v, sub_vector(w, SUBJ)); if (compute_Q) col_house_update(sub_matrix(Q, SUBQ, SUBI), v, sub_vector(w, SUBQ)); } /* ********************************************************************* */ /* Implicit QR algorithm. */ /* ********************************************************************* */ // QR method for real or complex square matrices based on an // implicit QR factorisation. eigval has to be a complex vector // if A has complex eigenvalues. Complexity about 10n^3, 25n^3 if // eigenvectors are computed template void implicit_qr_algorithm(const MAT1 &A, const VECT &eigval_, const MAT2 &Q_, tol_type_for_qr tol = default_tol_for_qr, bool compvect = true) { VECT &eigval = const_cast(eigval_); MAT2 &Q = const_cast(Q_); typedef typename linalg_traits::value_type value_type; size_type n(mat_nrows(A)), q(0), q_old, p(0), ite(0), its(0); dense_matrix H(n,n); sub_interval SUBK(0,0); gmm::copy(A, H); Hessenberg_reduction(H, Q, compvect); qr_stop_criterion(H, p, q, tol); while (q < n) { sub_interval SUBI(p, n-p-q), SUBJ(0, mat_ncols(Q)); if (compvect) SUBK = SUBI; // Francis_qr_step(sub_matrix(H, SUBI), // sub_matrix(Q, SUBJ, SUBK), compvect); Wilkinson_double_shift_qr_step(sub_matrix(H, SUBI), sub_matrix(Q, SUBJ, SUBK), tol, (its == 10 || its == 20), compvect); q_old = q; qr_stop_criterion(H, p, q, tol*2); if (q != q_old) its = 0; ++its; ++ite; GMM_ASSERT1(ite < n*100, "QR algorithm failed"); } if (compvect) block2x2_reduction(H, Q, tol); extract_eig(H, eigval, tol); } template void implicit_qr_algorithm(const MAT1 &a, VECT &eigval, tol_type_for_qr tol = default_tol_for_qr) { dense_matrix::value_type> m(1,1); implicit_qr_algorithm(a, eigval, m, tol, false); } /* ********************************************************************* */ /* Implicit symmetric QR step with Wilkinson Shift. */ /* ********************************************************************* */ template void symmetric_Wilkinson_qr_step(const MAT1& MM, const MAT2 &ZZ, bool compute_z) { MAT1& M = const_cast(MM); MAT2& Z = const_cast(ZZ); typedef typename linalg_traits::value_type T; typedef typename number_traits::magnitude_type R; size_type n = mat_nrows(M); for (size_type i = 0; i < n; ++i) { M(i, i) = T(gmm::real(M(i, i))); if (i > 0) { T a = (M(i, i-1) + gmm::conj(M(i-1, i)))/R(2); M(i, i-1) = a; M(i-1, i) = gmm::conj(a); } } R d = gmm::real(M(n-2, n-2) - M(n-1, n-1)) / R(2); R e = gmm::abs_sqr(M(n-1, n-2)); R nu = d + gmm::sgn(d)*gmm::sqrt(d*d+e); if (nu == R(0)) { M(n-1, n-2) = T(0); return; } R mu = gmm::real(M(n-1, n-1)) - e / nu; T x = M(0,0) - T(mu), z = M(1, 0), c, s; for (size_type k = 1; k < n; ++k) { Givens_rotation(x, z, c, s); if (k > 1) Apply_Givens_rotation_left(M(k-1,k-2), M(k,k-2), c, s); Apply_Givens_rotation_left(M(k-1,k-1), M(k,k-1), c, s); Apply_Givens_rotation_left(M(k-1,k ), M(k,k ), c, s); if (k < n-1) Apply_Givens_rotation_left(M(k-1,k+1), M(k,k+1), c, s); if (k > 1) Apply_Givens_rotation_right(M(k-2,k-1), M(k-2,k), c, s); Apply_Givens_rotation_right(M(k-1,k-1), M(k-1,k), c, s); Apply_Givens_rotation_right(M(k ,k-1), M(k,k) , c, s); if (k < n-1) Apply_Givens_rotation_right(M(k+1,k-1), M(k+1,k), c, s); if (compute_z) col_rot(Z, c, s, k-1, k); if (k < n-1) { x = M(k, k-1); z = M(k+1, k-1); } } } template void symmetric_Wilkinson_qr_step(const VECT1& diag_, const VECT2& sdiag_, const MAT &ZZ, bool compute_z) { VECT1& diag = const_cast(diag_); VECT2& sdiag = const_cast(sdiag_); MAT& Z = const_cast(ZZ); typedef typename linalg_traits::value_type T; typedef typename number_traits::magnitude_type R; size_type n = vect_size(diag); R d = (diag[n-2] - diag[n-1]) / R(2); R e = gmm::abs_sqr(sdiag[n-2]); R nu = d + gmm::sgn(d)*gmm::sqrt(d*d+e); if (nu == R(0)) { sdiag[n-2] = T(0); return; } R mu = diag[n-1] - e / nu; T x = diag[0] - T(mu), z = sdiag[0], c, s; T a01(0), a02(0); T a10(0), a11(diag[0]), a12(gmm::conj(sdiag[0])), a13(0); T a20(0), a21(sdiag[0]), a22(diag[1]), a23(gmm::conj(sdiag[1])); T a31(0), a32(sdiag[1]); for (size_type k = 1; k < n; ++k) { Givens_rotation(x, z, c, s); if (k > 1) Apply_Givens_rotation_left(a10, a20, c, s); Apply_Givens_rotation_left(a11, a21, c, s); Apply_Givens_rotation_left(a12, a22, c, s); if (k < n-1) Apply_Givens_rotation_left(a13, a23, c, s); if (k > 1) Apply_Givens_rotation_right(a01, a02, c, s); Apply_Givens_rotation_right(a11, a12, c, s); Apply_Givens_rotation_right(a21, a22, c, s); if (k < n-1) Apply_Givens_rotation_right(a31, a32, c, s); if (compute_z) col_rot(Z, c, s, k-1, k); diag[k-1] = gmm::real(a11); diag[k] = gmm::real(a22); if (k > 1) sdiag[k-2] = (gmm::conj(a01) + a10) / R(2); sdiag[k-1] = (gmm::conj(a12) + a21) / R(2); x = sdiag[k-1]; z = (gmm::conj(a13) + a31) / R(2); a01 = a12; a02 = a13; a10 = a21; a11 = a22; a12 = a23; a13 = T(0); a20 = a31; a21 = a32; a31 = T(0); if (k < n-1) { sdiag[k] = (gmm::conj(a23) + a32) / R(2); a22 = T(diag[k+1]); a32 = sdiag[k+1]; a23 = gmm::conj(a32); } } } /* ********************************************************************* */ /* Implicit QR algorithm for symmetric or hermitian matrices. */ /* ********************************************************************* */ // implicit QR method for real square symmetric matrices or complex // hermitian matrices. // eigval has to be a complex vector if A has complex eigeinvalues. // complexity about 4n^3/3, 9n^3 if eigenvectors are computed template void symmetric_qr_algorithm_old(const MAT1 &A, const VECT &eigval_, const MAT2 &eigvect_, tol_type_for_qr tol = default_tol_for_qr, bool compvect = true) { VECT &eigval = const_cast(eigval_); MAT2 &eigvect = const_cast(eigvect_); typedef typename linalg_traits::value_type T; typedef typename number_traits::magnitude_type R; if (compvect) gmm::copy(identity_matrix(), eigvect); size_type n = mat_nrows(A), q = 0, p, ite = 0; dense_matrix Tri(n, n); gmm::copy(A, Tri); Householder_tridiagonalization(Tri, eigvect, compvect); symmetric_qr_stop_criterion(Tri, p, q, tol); while (q < n) { sub_interval SUBI(p, n-p-q), SUBJ(0, mat_ncols(eigvect)), SUBK(p, n-p-q); if (!compvect) SUBK = sub_interval(0,0); symmetric_Wilkinson_qr_step(sub_matrix(Tri, SUBI), sub_matrix(eigvect, SUBJ, SUBK), compvect); symmetric_qr_stop_criterion(Tri, p, q, tol*R(2)); ++ite; GMM_ASSERT1(ite < n*100, "QR algorithm failed. Probably, your matrix" " is not real symmetric or complex hermitian"); } extract_eig(Tri, eigval, tol); } template void symmetric_qr_algorithm(const MAT1 &A, const VECT &eigval_, const MAT2 &eigvect_, tol_type_for_qr tol = default_tol_for_qr, bool compvect = true) { VECT &eigval = const_cast(eigval_); MAT2 &eigvect = const_cast(eigvect_); typedef typename linalg_traits::value_type T; typedef typename number_traits::magnitude_type R; size_type n = mat_nrows(A), q = 0, p, ite = 0; if (compvect) gmm::copy(identity_matrix(), eigvect); if (n == 0) return; if (n == 1) { eigval[0]=gmm::real(A(0,0)); return; } dense_matrix Tri(n, n); gmm::copy(A, Tri); Householder_tridiagonalization(Tri, eigvect, compvect); std::vector diag(n); std::vector sdiag(n); for (size_type i = 0; i < n; ++i) { diag[i] = gmm::real(Tri(i, i)); if (i+1 < n) sdiag[i] = Tri(i+1, i); } symmetric_qr_stop_criterion(diag, sdiag, p, q, tol); while (q < n) { sub_interval SUBI(p, n-p-q), SUBJ(0, mat_ncols(eigvect)), SUBK(p, n-p-q); if (!compvect) SUBK = sub_interval(0,0); symmetric_Wilkinson_qr_step(sub_vector(diag, SUBI), sub_vector(sdiag, SUBI), sub_matrix(eigvect, SUBJ, SUBK), compvect); symmetric_qr_stop_criterion(diag, sdiag, p, q, tol*R(3)); ++ite; GMM_ASSERT1(ite < n*100, "QR algorithm failed."); } gmm::copy(diag, eigval); } template void symmetric_qr_algorithm(const MAT1 &a, VECT &eigval, tol_type_for_qr tol = default_tol_for_qr) { dense_matrix::value_type> m(0,0); symmetric_qr_algorithm(a, eigval, m, tol, false); } } #endif