1 /* -*- c++ -*- (enables emacs c++ mode) */
2 /*===========================================================================
3 
4  Copyright (C) 2003-2020 Yves Renard
5 
6  This file is a part of GetFEM
7 
8  GetFEM  is  free software;  you  can  redistribute  it  and/or modify it
9  under  the  terms  of the  GNU  Lesser General Public License as published
10  by  the  Free Software Foundation;  either version 3 of the License,  or
11  (at your option) any later version along with the GCC Runtime Library
12  Exception either version 3.1 or (at your option) any later version.
13  This program  is  distributed  in  the  hope  that it will be useful,  but
14  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15  or  FITNESS  FOR  A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16  License and GCC Runtime Library Exception for more details.
17  You  should  have received a copy of the GNU Lesser General Public License
18  along  with  this program;  if not, write to the Free Software Foundation,
19  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
20 
21  As a special exception, you  may use  this file  as it is a part of a free
22  software  library  without  restriction.  Specifically,  if   other  files
23  instantiate  templates  or  use macros or inline functions from this file,
24  or  you compile this  file  and  link  it  with other files  to produce an
25  executable, this file  does  not  by itself cause the resulting executable
26  to be covered  by the GNU Lesser General Public License.  This   exception
27  does not  however  invalidate  any  other  reasons why the executable file
28  might be covered by the GNU Lesser General Public License.
29 
30 ===========================================================================*/
31 
32 /**@file gmm_dense_qr.h
33    @author  Caroline Lecalvez, Caroline.Lecalvez@gmm.insa-tlse.fr, Yves Renard <Yves.Renard@insa-lyon.fr>
34    @date September 12, 2003.
35    @brief Dense QR factorization.
36 */
37 #ifndef GMM_DENSE_QR_H
38 #define GMM_DENSE_QR_H
39 
40 #include "gmm_dense_Householder.h"
41 
42 namespace gmm {
43 
44 
45   /**
46      QR factorization using Householder method (complex and real version).
47   */
48   template <typename MAT1>
qr_factor(const MAT1 & A_)49   void qr_factor(const MAT1 &A_) {
50     MAT1 &A = const_cast<MAT1 &>(A_);
51     typedef typename linalg_traits<MAT1>::value_type value_type;
52 
53     size_type m = mat_nrows(A), n = mat_ncols(A);
54     GMM_ASSERT2(m >= n, "dimensions mismatch");
55 
56     std::vector<value_type> W(m), V(m);
57 
58     for (size_type j = 0; j < n; ++j) {
59       sub_interval SUBI(j, m-j), SUBJ(j, n-j);
60       V.resize(m-j); W.resize(n-j);
61 
62       for (size_type i = j; i < m; ++i) V[i-j] = A(i, j);
63       house_vector(V);
64 
65       row_house_update(sub_matrix(A, SUBI, SUBJ), V, W);
66       for (size_type i = j+1; i < m; ++i) A(i, j) = V[i-j];
67     }
68   }
69 
70 
71   // QR comes from QR_factor(QR) where the upper triangular part stands for R
72   // and the lower part contains the Householder reflectors.
73   // A <- AQ
74   template <typename MAT1, typename MAT2>
apply_house_right(const MAT1 & QR,const MAT2 & A_)75   void apply_house_right(const MAT1 &QR, const MAT2 &A_) {
76     MAT2 &A = const_cast<MAT2 &>(A_);
77     typedef typename linalg_traits<MAT1>::value_type T;
78     size_type m = mat_nrows(QR), n = mat_ncols(QR);
79     GMM_ASSERT2(m == mat_ncols(A), "dimensions mismatch");
80     if (m == 0) return;
81     std::vector<T> V(m), W(mat_nrows(A));
82     V[0] = T(1);
83     for (size_type j = 0; j < n; ++j) {
84       V.resize(m-j);
85       for (size_type i = j+1; i < m; ++i) V[i-j] = QR(i, j);
86       col_house_update(sub_matrix(A, sub_interval(0, mat_nrows(A)),
87                                   sub_interval(j, m-j)), V, W);
88     }
89   }
90 
91   // QR comes from QR_factor(QR) where the upper triangular part stands for R
92   // and the lower part contains the Householder reflectors.
93   // A <- Q*A
94   template <typename MAT1, typename MAT2>
apply_house_left(const MAT1 & QR,const MAT2 & A_)95   void apply_house_left(const MAT1 &QR, const MAT2 &A_) {
96     MAT2 &A = const_cast<MAT2 &>(A_);
97     typedef typename linalg_traits<MAT1>::value_type T;
98     size_type m = mat_nrows(QR), n = mat_ncols(QR);
99     GMM_ASSERT2(m == mat_nrows(A), "dimensions mismatch");
100     if (m == 0) return;
101     std::vector<T> V(m), W(mat_ncols(A));
102     V[0] = T(1);
103     for (size_type j = 0; j < n; ++j) {
104       V.resize(m-j);
105       for (size_type i = j+1; i < m; ++i) V[i-j] = QR(i, j);
106       row_house_update(sub_matrix(A, sub_interval(j, m-j),
107                                   sub_interval(0, mat_ncols(A))), V, W);
108     }
109   }
110 
111   /** Compute the QR factorization, where Q is assembled. */
112   template <typename MAT1, typename MAT2, typename MAT3>
qr_factor(const MAT1 & A,const MAT2 & QQ,const MAT3 & RR)113     void qr_factor(const MAT1 &A, const MAT2 &QQ, const MAT3 &RR) {
114     MAT2 &Q = const_cast<MAT2 &>(QQ); MAT3 &R = const_cast<MAT3 &>(RR);
115     typedef typename linalg_traits<MAT1>::value_type value_type;
116 
117     size_type m = mat_nrows(A), n = mat_ncols(A);
118     GMM_ASSERT2(m >= n, "dimensions mismatch");
119     gmm::copy(A, Q);
120 
121     std::vector<value_type> W(m);
122     dense_matrix<value_type> VV(m, n);
123 
124     for (size_type j = 0; j < n; ++j) {
125       sub_interval SUBI(j, m-j), SUBJ(j, n-j);
126 
127       for (size_type i = j; i < m; ++i) VV(i,j) = Q(i, j);
128       house_vector(sub_vector(mat_col(VV,j), SUBI));
129 
130       row_house_update(sub_matrix(Q, SUBI, SUBJ),
131                        sub_vector(mat_col(VV,j), SUBI), sub_vector(W, SUBJ));
132     }
133 
134     gmm::copy(sub_matrix(Q, sub_interval(0, n), sub_interval(0, n)), R);
135     gmm::copy(identity_matrix(), Q);
136 
137     for (size_type j = n-1; j != size_type(-1); --j) {
138       sub_interval SUBI(j, m-j), SUBJ(j, n-j);
139       row_house_update(sub_matrix(Q, SUBI, SUBJ),
140                        sub_vector(mat_col(VV,j), SUBI), sub_vector(W, SUBJ));
141     }
142   }
143 
144   ///@cond DOXY_SHOW_ALL_FUNCTIONS
145   template <typename TA, typename TV, typename Ttol,
146             typename MAT, typename VECT>
extract_eig(const MAT & A,VECT & V,Ttol tol,TA,TV)147   void extract_eig(const MAT &A, VECT &V, Ttol tol, TA, TV) {
148     size_type n = mat_nrows(A);
149     if (n == 0) return;
150     tol *= Ttol(2);
151     Ttol tol_i = tol * gmm::abs(A(0,0)), tol_cplx = tol_i;
152     for (size_type i = 0; i < n; ++i) {
153       if (i < n-1) {
154         tol_i = (gmm::abs(A(i,i))+gmm::abs(A(i+1,i+1)))*tol;
155         tol_cplx = std::max(tol_cplx, tol_i);
156       }
157       if ((i < n-1) && gmm::abs(A(i+1,i)) >= tol_i) {
158         TA tr = A(i,i) + A(i+1, i+1);
159         TA det = A(i,i)*A(i+1, i+1) - A(i,i+1)*A(i+1, i);
160         TA delta = tr*tr - TA(4) * det;
161         if (delta < -tol_cplx) {
162           GMM_WARNING1("A complex eigenvalue has been detected : "
163                       << std::complex<TA>(tr/TA(2), gmm::sqrt(-delta)/TA(2)));
164           V[i] = V[i+1] = tr / TA(2);
165         }
166         else {
167           delta = std::max(TA(0), delta);
168           V[i  ] = TA(tr + gmm::sqrt(delta))/ TA(2);
169           V[i+1] = TA(tr -  gmm::sqrt(delta))/ TA(2);
170         }
171         ++i;
172       }
173       else
174         V[i] = TV(A(i,i));
175     }
176   }
177 
178   template <typename TA, typename TV, typename Ttol,
179             typename MAT, typename VECT>
extract_eig(const MAT & A,VECT & V,Ttol tol,TA,std::complex<TV>)180   void extract_eig(const MAT &A, VECT &V, Ttol tol, TA, std::complex<TV>) {
181     size_type n = mat_nrows(A);
182     tol *= Ttol(2);
183     for (size_type i = 0; i < n; ++i)
184       if ((i == n-1) ||
185           gmm::abs(A(i+1,i)) < (gmm::abs(A(i,i))+gmm::abs(A(i+1,i+1)))*tol)
186         V[i] = std::complex<TV>(A(i,i));
187       else {
188         TA tr = A(i,i) + A(i+1, i+1);
189         TA det = A(i,i)*A(i+1, i+1) - A(i,i+1)*A(i+1, i);
190         TA delta = tr*tr - TA(4) * det;
191         if (delta < TA(0)) {
192           V[i] = std::complex<TV>(tr / TA(2), gmm::sqrt(-delta) / TA(2));
193           V[i+1] = std::complex<TV>(tr / TA(2), -gmm::sqrt(-delta)/ TA(2));
194         }
195         else {
196           V[i  ] = TA(tr + gmm::sqrt(delta)) / TA(2);
197           V[i+1] = TA(tr -  gmm::sqrt(delta)) / TA(2);
198         }
199         ++i;
200       }
201   }
202 
203   template <typename TA, typename TV, typename Ttol,
204             typename MAT, typename VECT>
extract_eig(const MAT & A,VECT & V,Ttol tol,std::complex<TA>,TV)205   void extract_eig(const MAT &A, VECT &V, Ttol tol, std::complex<TA>, TV) {
206     typedef std::complex<TA> T;
207     size_type n = mat_nrows(A);
208     if (n == 0) return;
209     tol *= Ttol(2);
210     Ttol tol_i = tol * gmm::abs(A(0,0)), tol_cplx = tol_i;
211     for (size_type i = 0; i < n; ++i) {
212       if (i < n-1) {
213         tol_i = (gmm::abs(A(i,i))+gmm::abs(A(i+1,i+1)))*tol;
214         tol_cplx = std::max(tol_cplx, tol_i);
215       }
216       if ((i == n-1) || gmm::abs(A(i+1,i)) < tol_i) {
217         if (gmm::abs(std::imag(A(i,i))) > tol_cplx)
218           GMM_WARNING1("A complex eigenvalue has been detected : "
219                       << T(A(i,i)) << " : "  << gmm::abs(std::imag(A(i,i)))
220                       / gmm::abs(std::real(A(i,i))) << " : " << tol_cplx);
221         V[i] = std::real(A(i,i));
222       }
223       else {
224         T tr = A(i,i) + A(i+1, i+1);
225         T det = A(i,i)*A(i+1, i+1) - A(i,i+1)*A(i+1, i);
226         T delta = tr*tr - TA(4) * det;
227         T a1 = (tr + gmm::sqrt(delta)) / TA(2);
228         T a2 = (tr - gmm::sqrt(delta)) / TA(2);
229         if (gmm::abs(std::imag(a1)) > tol_cplx)
230           GMM_WARNING1("A complex eigenvalue has been detected : " << a1);
231         if (gmm::abs(std::imag(a2)) > tol_cplx)
232           GMM_WARNING1("A complex eigenvalue has been detected : " << a2);
233 
234         V[i] = std::real(a1); V[i+1] = std::real(a2);
235         ++i;
236       }
237     }
238   }
239 
240   template <typename TA, typename TV, typename Ttol,
241             typename MAT, typename VECT>
extract_eig(const MAT & A,VECT & V,Ttol tol,std::complex<TA>,std::complex<TV>)242   void extract_eig(const MAT &A, VECT &V, Ttol tol,
243                    std::complex<TA>, std::complex<TV>) {
244     size_type n = mat_nrows(A);
245     tol *= Ttol(2);
246     for (size_type i = 0; i < n; ++i)
247       if ((i == n-1) ||
248           gmm::abs(A(i+1,i)) < (gmm::abs(A(i,i))+gmm::abs(A(i+1,i+1)))*tol)
249         V[i] = std::complex<TV>(A(i,i));
250       else {
251         std::complex<TA> tr = A(i,i) + A(i+1, i+1);
252         std::complex<TA> det = A(i,i)*A(i+1, i+1) - A(i,i+1)*A(i+1, i);
253         std::complex<TA> delta = tr*tr - TA(4) * det;
254         V[i] = (tr + gmm::sqrt(delta)) / TA(2);
255         V[i+1] = (tr - gmm::sqrt(delta)) / TA(2);
256         ++i;
257       }
258   }
259 
260   ///@endcond
261   /**
262      Compute eigenvalue vector.
263   */
264   template <typename MAT, typename Ttol, typename VECT> inline
extract_eig(const MAT & A,const VECT & V,Ttol tol)265   void extract_eig(const MAT &A, const VECT &V, Ttol tol) {
266     extract_eig(A, const_cast<VECT&>(V), tol,
267                 typename linalg_traits<MAT>::value_type(),
268                 typename linalg_traits<VECT>::value_type());
269   }
270 
271   /* ********************************************************************* */
272   /*    Stop criterion for QR algorithms                                   */
273   /* ********************************************************************* */
274 
275   template <typename MAT, typename Ttol>
qr_stop_criterion(MAT & A,size_type & p,size_type & q,Ttol tol)276   void qr_stop_criterion(MAT &A, size_type &p, size_type &q, Ttol tol) {
277     typedef typename linalg_traits<MAT>::value_type T;
278     typedef typename number_traits<T>::magnitude_type R;
279     R rmin = default_min(R()) * R(2);
280     size_type n = mat_nrows(A);
281     if (n <= 2) { q = n; p = 0; }
282     else {
283       for (size_type i = 1; i < n-q; ++i)
284         if (gmm::abs(A(i,i-1)) < (gmm::abs(A(i,i))+ gmm::abs(A(i-1,i-1)))*tol
285             || gmm::abs(A(i,i-1)) < rmin)
286           A(i,i-1) = T(0);
287 
288       while ((q < n-1 && A(n-1-q, n-2-q) == T(0)) ||
289              (q < n-2 && A(n-2-q, n-3-q) == T(0))) ++q;
290       if (q >= n-2) q = n;
291       p = n-q; if (p) --p; if (p) --p;
292       while (p > 0 && A(p,p-1) != T(0)) --p;
293     }
294   }
295 
296   template <typename MAT, typename Ttol> inline
symmetric_qr_stop_criterion(const MAT & AA,size_type & p,size_type & q,Ttol tol)297   void symmetric_qr_stop_criterion(const MAT &AA, size_type &p, size_type &q,
298                                 Ttol tol) {
299     typedef typename linalg_traits<MAT>::value_type T;
300     typedef typename number_traits<T>::magnitude_type R;
301     R rmin = default_min(R()) * R(2);
302     MAT& A = const_cast<MAT&>(AA);
303     size_type n = mat_nrows(A);
304     if (n <= 1) { q = n; p = 0; }
305     else {
306       for (size_type i = 1; i < n-q; ++i)
307         if (gmm::abs(A(i,i-1)) < (gmm::abs(A(i,i))+ gmm::abs(A(i-1,i-1)))*tol
308             || gmm::abs(A(i,i-1)) < rmin)
309           A(i,i-1) = T(0);
310 
311       while (q < n-1 && A(n-1-q, n-2-q) == T(0)) ++q;
312       if (q >= n-1) q = n;
313       p = n-q; if (p) --p; if (p) --p;
314       while (p > 0 && A(p,p-1) != T(0)) --p;
315     }
316   }
317 
318   template <typename VECT1, typename VECT2, typename Ttol> inline
symmetric_qr_stop_criterion(const VECT1 & diag,const VECT2 & sdiag_,size_type & p,size_type & q,Ttol tol)319   void symmetric_qr_stop_criterion(const VECT1 &diag, const VECT2 &sdiag_,
320                                    size_type &p, size_type &q, Ttol tol) {
321     typedef typename linalg_traits<VECT2>::value_type T;
322     typedef typename number_traits<T>::magnitude_type R;
323     R rmin = default_min(R()) * R(2);
324     VECT2 &sdiag = const_cast<VECT2 &>(sdiag_);
325     size_type n = vect_size(diag);
326     if (n <= 1) { q = n; p = 0; return; }
327     for (size_type i = 1; i < n-q; ++i)
328       if (gmm::abs(sdiag[i-1]) < (gmm::abs(diag[i])+ gmm::abs(diag[i-1]))*tol
329           || gmm::abs(sdiag[i-1]) < rmin)
330         sdiag[i-1] = T(0);
331     while (q < n-1 && sdiag[n-2-q] == T(0)) ++q;
332     if (q >= n-1) q = n;
333     p = n-q; if (p) --p; if (p) --p;
334     while (p > 0 && sdiag[p-1] != T(0)) --p;
335   }
336 
337   /* ********************************************************************* */
338   /*    2x2 blocks reduction for Schur vectors                             */
339   /* ********************************************************************* */
340 
341   template <typename MATH, typename MATQ, typename Ttol>
block2x2_reduction(MATH & H,MATQ & Q,Ttol tol)342   void block2x2_reduction(MATH &H, MATQ &Q, Ttol tol) {
343     typedef typename linalg_traits<MATH>::value_type T;
344     typedef typename number_traits<T>::magnitude_type R;
345 
346     size_type n = mat_nrows(H), nq = mat_nrows(Q);
347     if (n < 2) return;
348     sub_interval SUBQ(0, nq), SUBL(0, 2);
349     std::vector<T> v(2), w(std::max(n, nq)); v[0] = T(1);
350     tol *= Ttol(2);
351     Ttol tol_i = tol * gmm::abs(H(0,0)), tol_cplx = tol_i;
352     for (size_type i = 0; i < n-1; ++i) {
353       tol_i = (gmm::abs(H(i,i))+gmm::abs(H(i+1,i+1)))*tol;
354       tol_cplx = std::max(tol_cplx, tol_i);
355 
356       if (gmm::abs(H(i+1,i)) > tol_i) { // 2x2 block detected
357         T tr = (H(i+1, i+1) - H(i,i)) / T(2);
358         T delta = tr*tr + H(i,i+1)*H(i+1, i);
359 
360         if (is_complex(T()) || gmm::real(delta) >= R(0)) {
361           sub_interval SUBI(i, 2);
362           T theta = (tr - gmm::sqrt(delta)) / H(i+1,i);
363           R a = gmm::abs(theta);
364           v[1] = (a == R(0)) ? T(-1)
365             : gmm::conj(theta) * (R(1) - gmm::sqrt(a*a + R(1)) / a);
366           row_house_update(sub_matrix(H, SUBI), v, sub_vector(w, SUBL));
367           col_house_update(sub_matrix(H, SUBI), v, sub_vector(w, SUBL));
368           col_house_update(sub_matrix(Q, SUBQ, SUBI), v, sub_vector(w, SUBQ));
369         }
370         ++i;
371       }
372     }
373   }
374 
375   /* ********************************************************************* */
376   /*    Basic qr algorithm.                                                */
377   /* ********************************************************************* */
378 
379   #define tol_type_for_qr typename number_traits<typename \
380                           linalg_traits<MAT1>::value_type>::magnitude_type
381   #define default_tol_for_qr \
382     (gmm::default_tol(tol_type_for_qr()) *  tol_type_for_qr(3))
383 
384   // QR method for real or complex square matrices based on QR factorisation.
385   // eigval has to be a complex vector if A has complex eigeinvalues.
386   // Very slow method. Use implicit_qr_method instead.
387   template <typename MAT1, typename VECT, typename MAT2>
388     void rudimentary_qr_algorithm(const MAT1 &A, const VECT &eigval_,
389                                   const MAT2 &eigvect_,
390                                   tol_type_for_qr tol = default_tol_for_qr,
391                                   bool compvect = true) {
392     VECT &eigval = const_cast<VECT &>(eigval_);
393     MAT2 &eigvect = const_cast<MAT2 &>(eigvect_);
394 
395     typedef typename linalg_traits<MAT1>::value_type value_type;
396 
397     size_type n = mat_nrows(A), p, q = 0, ite = 0;
398     dense_matrix<value_type> Q(n, n), R(n,n), A1(n,n);
399     gmm::copy(A, A1);
400 
401     Hessenberg_reduction(A1, eigvect, compvect);
402     qr_stop_criterion(A1, p, q, tol);
403 
404     while (q < n) {
405       qr_factor(A1, Q, R);
406       gmm::mult(R, Q, A1);
407       if (compvect) { gmm::mult(eigvect, Q, R); gmm::copy(R, eigvect); }
408 
409       qr_stop_criterion(A1, p, q, tol);
410       ++ite;
411       GMM_ASSERT1(ite < n*1000, "QR algorithm failed");
412     }
413     if (compvect) block2x2_reduction(A1, Q, tol);
414     extract_eig(A1, eigval, tol);
415   }
416 
417   template <typename MAT1, typename VECT>
418     void rudimentary_qr_algorithm(const MAT1 &a, VECT &eigval,
419                                   tol_type_for_qr tol = default_tol_for_qr) {
420     dense_matrix<typename linalg_traits<MAT1>::value_type> m(0,0);
421     rudimentary_qr_algorithm(a, eigval, m, tol, false);
422   }
423 
424   /* ********************************************************************* */
425   /*    Francis QR step.                                                   */
426   /* ********************************************************************* */
427 
428   template <typename MAT1, typename MAT2>
Francis_qr_step(const MAT1 & HH,const MAT2 & QQ,bool compute_Q)429     void Francis_qr_step(const MAT1& HH, const MAT2 &QQ, bool compute_Q) {
430     MAT1& H = const_cast<MAT1&>(HH); MAT2& Q = const_cast<MAT2&>(QQ);
431     typedef typename linalg_traits<MAT1>::value_type value_type;
432     size_type n = mat_nrows(H), nq = mat_nrows(Q);
433 
434     std::vector<value_type> v(3), w(std::max(n, nq));
435 
436     value_type s = H(n-2, n-2) + H(n-1, n-1);
437     value_type t = H(n-2, n-2) * H(n-1, n-1) - H(n-2, n-1) * H(n-1, n-2);
438     value_type x = H(0, 0) * H(0, 0) + H(0,1) * H(1, 0) - s * H(0,0) + t;
439     value_type y = H(1, 0) * (H(0,0) + H(1,1) - s);
440     value_type z = H(1, 0) * H(2, 1);
441 
442     sub_interval SUBQ(0, nq);
443 
444     for (size_type k = 0; k < n - 2; ++k) {
445       v[0] = x; v[1] = y; v[2] = z;
446       house_vector(v);
447       size_type r = std::min(k+4, n), q = (k==0) ? 0 : k-1;
448       sub_interval SUBI(k, 3), SUBJ(0, r), SUBK(q, n-q);
449 
450       row_house_update(sub_matrix(H, SUBI, SUBK),  v, sub_vector(w, SUBK));
451       col_house_update(sub_matrix(H, SUBJ, SUBI),  v, sub_vector(w, SUBJ));
452 
453       if (compute_Q)
454         col_house_update(sub_matrix(Q, SUBQ, SUBI),  v, sub_vector(w, SUBQ));
455 
456       x = H(k+1, k); y = H(k+2, k);
457       if (k < n-3) z = H(k+3, k);
458     }
459     sub_interval SUBI(n-2,2), SUBJ(0, n), SUBK(n-3,3), SUBL(0, 3);
460     v.resize(2);
461     v[0] = x; v[1] = y;
462     house_vector(v);
463     row_house_update(sub_matrix(H, SUBI, SUBK), v, sub_vector(w, SUBL));
464     col_house_update(sub_matrix(H, SUBJ, SUBI), v, sub_vector(w, SUBJ));
465     if (compute_Q)
466       col_house_update(sub_matrix(Q, SUBQ, SUBI), v, sub_vector(w, SUBQ));
467   }
468 
469   /* ********************************************************************* */
470   /*    Wilkinson Double shift QR step (from Lapack).                      */
471   /* ********************************************************************* */
472 
473   template <typename MAT1, typename MAT2, typename Ttol>
Wilkinson_double_shift_qr_step(const MAT1 & HH,const MAT2 & QQ,Ttol tol,bool exc,bool compute_Q)474   void Wilkinson_double_shift_qr_step(const MAT1& HH, const MAT2 &QQ,
475                                       Ttol tol, bool exc, bool compute_Q) {
476     MAT1& H = const_cast<MAT1&>(HH); MAT2& Q = const_cast<MAT2&>(QQ);
477     typedef typename linalg_traits<MAT1>::value_type T;
478     typedef typename number_traits<T>::magnitude_type R;
479 
480     size_type n = mat_nrows(H), nq = mat_nrows(Q), m;
481     std::vector<T> v(3), w(std::max(n, nq));
482     const R dat1(0.75), dat2(-0.4375);
483     T h33, h44, h43h34, v1(0), v2(0), v3(0);
484 
485     if (exc) {                    /* Exceptional shift.                    */
486       R s = gmm::abs(H(n-1, n-2)) + gmm::abs(H(n-2, n-3));
487       h33 = h44 = dat1 * s;
488       h43h34 = dat2*s*s;
489     }
490     else {                        /* Wilkinson double shift.               */
491       h44 = H(n-1,n-1); h33 = H(n-2, n-2);
492       h43h34 = H(n-1, n-2) * H(n-2, n-1);
493     }
494 
495     /* Look for two consecutive small subdiagonal elements.                */
496     /* Determine the effect of starting the double-shift QR iteration at   */
497     /* row m, and see if this would make H(m-1, m-2) negligible.           */
498     for (m = n-2; m != 0; --m) {
499       T h11  = H(m-1, m-1), h22  = H(m, m);
500       T h21  = H(m, m-1),   h12  = H(m-1, m);
501       T h44s = h44 - h11,   h33s = h33 - h11;
502       v1 = (h33s*h44s-h43h34) / h21 + h12;
503       v2 = h22 - h11 - h33s - h44s;
504       v3 = H(m+1, m);
505       R s = gmm::abs(v1) + gmm::abs(v2) + gmm::abs(v3);
506       v1 /= s; v2 /= s; v3 /= s;
507       if (m == 1) break;
508       T h00 = H(m-2, m-2);
509       T h10 = H(m-1, m-2);
510       R tst1 = gmm::abs(v1)*(gmm::abs(h00)+gmm::abs(h11)+gmm::abs(h22));
511       if (gmm::abs(h10)*(gmm::abs(v2)+gmm::abs(v3)) <= tol * tst1) break;
512     }
513 
514     /* Double shift QR step.                                               */
515     sub_interval SUBQ(0, nq);
516     for (size_type k = (m == 0) ? 0 : m-1; k < n-2; ++k) {
517       v[0] = v1; v[1] = v2; v[2] = v3;
518       house_vector(v);
519       size_type r = std::min(k+4, n), q = (k==0) ? 0 : k-1;
520       sub_interval SUBI(k, 3), SUBJ(0, r), SUBK(q, n-q);
521 
522       row_house_update(sub_matrix(H, SUBI, SUBK),  v, sub_vector(w, SUBK));
523       col_house_update(sub_matrix(H, SUBJ, SUBI),  v, sub_vector(w, SUBJ));
524       if (k > m-1) { H(k+1, k-1) = T(0); if (k < n-3) H(k+2, k-1) = T(0); }
525 
526       if (compute_Q)
527         col_house_update(sub_matrix(Q, SUBQ, SUBI),  v, sub_vector(w, SUBQ));
528 
529       v1 = H(k+1, k); v2 = H(k+2, k);
530       if (k < n-3) v3 = H(k+3, k);
531     }
532     sub_interval SUBI(n-2,2), SUBJ(0, n), SUBK(n-3,3), SUBL(0, 3);
533     v.resize(2); v[0] = v1; v[1] = v2;
534     house_vector(v);
535     row_house_update(sub_matrix(H, SUBI, SUBK), v, sub_vector(w, SUBL));
536     col_house_update(sub_matrix(H, SUBJ, SUBI), v, sub_vector(w, SUBJ));
537     if (compute_Q)
538       col_house_update(sub_matrix(Q, SUBQ, SUBI), v, sub_vector(w, SUBQ));
539   }
540 
541   /* ********************************************************************* */
542   /*    Implicit QR algorithm.                                             */
543   /* ********************************************************************* */
544 
545   // QR method for real or complex square matrices based on an
546   // implicit QR factorisation. eigval has to be a complex vector
547   // if A has complex eigenvalues. Complexity about 10n^3, 25n^3 if
548   // eigenvectors are computed
549   template <typename MAT1, typename VECT, typename MAT2>
550     void implicit_qr_algorithm(const MAT1 &A, const VECT &eigval_,
551                                const MAT2 &Q_,
552                                tol_type_for_qr tol = default_tol_for_qr,
553                                bool compvect = true) {
554     VECT &eigval = const_cast<VECT &>(eigval_);
555     MAT2 &Q = const_cast<MAT2 &>(Q_);
556     typedef typename linalg_traits<MAT1>::value_type value_type;
557 
558     size_type n(mat_nrows(A)), q(0), q_old, p(0), ite(0), its(0);
559     dense_matrix<value_type> H(n,n);
560     sub_interval SUBK(0,0);
561 
562     gmm::copy(A, H);
563     Hessenberg_reduction(H, Q, compvect);
564     qr_stop_criterion(H, p, q, tol);
565 
566     while (q < n) {
567       sub_interval SUBI(p, n-p-q), SUBJ(0, mat_ncols(Q));
568       if (compvect) SUBK = SUBI;
569 //       Francis_qr_step(sub_matrix(H, SUBI),
570 //                       sub_matrix(Q, SUBJ, SUBK), compvect);
571       Wilkinson_double_shift_qr_step(sub_matrix(H, SUBI),
572                                      sub_matrix(Q, SUBJ, SUBK),
573                                      tol, (its == 10 || its == 20), compvect);
574       q_old = q;
575       qr_stop_criterion(H, p, q, tol*2);
576       if (q != q_old) its = 0;
577       ++its; ++ite;
578       GMM_ASSERT1(ite < n*100, "QR algorithm failed");
579     }
580     if (compvect) block2x2_reduction(H, Q, tol);
581     extract_eig(H, eigval, tol);
582   }
583 
584 
585   template <typename MAT1, typename VECT>
586     void implicit_qr_algorithm(const MAT1 &a, VECT &eigval,
587                                tol_type_for_qr tol = default_tol_for_qr) {
588     dense_matrix<typename linalg_traits<MAT1>::value_type> m(1,1);
589     implicit_qr_algorithm(a, eigval, m, tol, false);
590   }
591 
592   /* ********************************************************************* */
593   /*    Implicit symmetric QR step with Wilkinson Shift.                   */
594   /* ********************************************************************* */
595 
596   template <typename MAT1, typename MAT2>
symmetric_Wilkinson_qr_step(const MAT1 & MM,const MAT2 & ZZ,bool compute_z)597     void symmetric_Wilkinson_qr_step(const MAT1& MM, const MAT2 &ZZ,
598                                      bool compute_z) {
599     MAT1& M = const_cast<MAT1&>(MM); MAT2& Z = const_cast<MAT2&>(ZZ);
600     typedef typename linalg_traits<MAT1>::value_type T;
601     typedef typename number_traits<T>::magnitude_type R;
602     size_type n = mat_nrows(M);
603 
604     for (size_type i = 0; i < n; ++i) {
605       M(i, i) = T(gmm::real(M(i, i)));
606       if (i > 0) {
607         T a = (M(i, i-1) + gmm::conj(M(i-1, i)))/R(2);
608         M(i, i-1) = a; M(i-1, i) = gmm::conj(a);
609       }
610     }
611 
612     R d = gmm::real(M(n-2, n-2) - M(n-1, n-1)) / R(2);
613     R e = gmm::abs_sqr(M(n-1, n-2));
614     R nu = d + gmm::sgn(d)*gmm::sqrt(d*d+e);
615     if (nu == R(0)) { M(n-1, n-2) = T(0); return; }
616     R mu = gmm::real(M(n-1, n-1)) - e / nu;
617     T x = M(0,0) - T(mu), z = M(1, 0), c, s;
618 
619     for (size_type k = 1; k < n; ++k) {
620       Givens_rotation(x, z, c, s);
621 
622       if (k > 1) Apply_Givens_rotation_left(M(k-1,k-2), M(k,k-2), c, s);
623       Apply_Givens_rotation_left(M(k-1,k-1), M(k,k-1), c, s);
624       Apply_Givens_rotation_left(M(k-1,k  ), M(k,k  ), c, s);
625       if (k < n-1) Apply_Givens_rotation_left(M(k-1,k+1), M(k,k+1), c, s);
626       if (k > 1) Apply_Givens_rotation_right(M(k-2,k-1), M(k-2,k), c, s);
627       Apply_Givens_rotation_right(M(k-1,k-1), M(k-1,k), c, s);
628       Apply_Givens_rotation_right(M(k  ,k-1), M(k,k)  , c, s);
629       if (k < n-1) Apply_Givens_rotation_right(M(k+1,k-1), M(k+1,k), c, s);
630 
631       if (compute_z) col_rot(Z, c, s, k-1, k);
632       if (k < n-1) { x = M(k, k-1); z = M(k+1, k-1); }
633     }
634 
635   }
636 
637   template <typename VECT1, typename VECT2, typename MAT>
symmetric_Wilkinson_qr_step(const VECT1 & diag_,const VECT2 & sdiag_,const MAT & ZZ,bool compute_z)638   void symmetric_Wilkinson_qr_step(const VECT1& diag_, const VECT2& sdiag_,
639                                    const MAT &ZZ, bool compute_z) {
640     VECT1& diag = const_cast<VECT1&>(diag_);
641     VECT2& sdiag = const_cast<VECT2&>(sdiag_);
642     MAT& Z = const_cast<MAT&>(ZZ);
643     typedef typename linalg_traits<VECT2>::value_type T;
644     typedef typename number_traits<T>::magnitude_type R;
645 
646     size_type n = vect_size(diag);
647     R d = (diag[n-2] - diag[n-1]) / R(2);
648     R e = gmm::abs_sqr(sdiag[n-2]);
649     R nu = d + gmm::sgn(d)*gmm::sqrt(d*d+e);
650     if (nu == R(0)) { sdiag[n-2] = T(0); return; }
651     R mu = diag[n-1] - e / nu;
652     T x = diag[0] - T(mu), z = sdiag[0], c, s;
653 
654     T a01(0), a02(0);
655     T a10(0), a11(diag[0]), a12(gmm::conj(sdiag[0])), a13(0);
656     T a20(0), a21(sdiag[0]), a22(diag[1]), a23(gmm::conj(sdiag[1]));
657     T a31(0), a32(sdiag[1]);
658 
659     for (size_type k = 1; k < n; ++k) {
660       Givens_rotation(x, z, c, s);
661 
662       if (k > 1) Apply_Givens_rotation_left(a10, a20, c, s);
663       Apply_Givens_rotation_left(a11, a21, c, s);
664       Apply_Givens_rotation_left(a12, a22, c, s);
665       if (k < n-1) Apply_Givens_rotation_left(a13, a23, c, s);
666 
667       if (k > 1) Apply_Givens_rotation_right(a01, a02, c, s);
668       Apply_Givens_rotation_right(a11, a12, c, s);
669       Apply_Givens_rotation_right(a21, a22, c, s);
670       if (k < n-1) Apply_Givens_rotation_right(a31, a32, c, s);
671 
672       if (compute_z) col_rot(Z, c, s, k-1, k);
673 
674       diag[k-1] = gmm::real(a11);
675       diag[k] = gmm::real(a22);
676       if (k > 1) sdiag[k-2] = (gmm::conj(a01) + a10) / R(2);
677       sdiag[k-1] = (gmm::conj(a12) + a21) / R(2);
678 
679       x = sdiag[k-1]; z = (gmm::conj(a13) + a31) / R(2);
680 
681       a01 = a12; a02 = a13;
682       a10 = a21; a11 = a22; a12 = a23; a13 = T(0);
683       a20 = a31; a21 = a32; a31 = T(0);
684 
685       if (k < n-1) {
686         sdiag[k] = (gmm::conj(a23) + a32) / R(2);
687         a22 = T(diag[k+1]); a32 = sdiag[k+1]; a23 = gmm::conj(a32);
688       }
689     }
690   }
691 
692   /* ********************************************************************* */
693   /*    Implicit QR algorithm for symmetric or hermitian matrices.         */
694   /* ********************************************************************* */
695 
696   // implicit QR method for real square symmetric matrices or complex
697   // hermitian matrices.
698   // eigval has to be a complex vector if A has complex eigeinvalues.
699   // complexity about 4n^3/3, 9n^3 if eigenvectors are computed
700   template <typename MAT1, typename VECT, typename MAT2>
701   void symmetric_qr_algorithm_old(const MAT1 &A, const VECT &eigval_,
702                               const MAT2 &eigvect_,
703                               tol_type_for_qr tol = default_tol_for_qr,
704                               bool compvect = true) {
705     VECT &eigval = const_cast<VECT &>(eigval_);
706     MAT2 &eigvect = const_cast<MAT2 &>(eigvect_);
707     typedef typename linalg_traits<MAT1>::value_type T;
708     typedef typename number_traits<T>::magnitude_type R;
709 
710     if (compvect) gmm::copy(identity_matrix(), eigvect);
711     size_type n = mat_nrows(A), q = 0, p, ite = 0;
712     dense_matrix<T> Tri(n, n);
713     gmm::copy(A, Tri);
714 
715     Householder_tridiagonalization(Tri, eigvect, compvect);
716 
717     symmetric_qr_stop_criterion(Tri, p, q, tol);
718 
719     while (q < n) {
720 
721       sub_interval SUBI(p, n-p-q), SUBJ(0, mat_ncols(eigvect)), SUBK(p, n-p-q);
722       if (!compvect) SUBK = sub_interval(0,0);
723       symmetric_Wilkinson_qr_step(sub_matrix(Tri, SUBI),
724                                   sub_matrix(eigvect, SUBJ, SUBK), compvect);
725 
726       symmetric_qr_stop_criterion(Tri, p, q, tol*R(2));
727       ++ite;
728       GMM_ASSERT1(ite < n*100, "QR algorithm failed. Probably, your matrix"
729                   " is not real symmetric or complex hermitian");
730     }
731 
732     extract_eig(Tri, eigval, tol);
733   }
734 
735   template <typename MAT1, typename VECT, typename MAT2>
736   void symmetric_qr_algorithm(const MAT1 &A, const VECT &eigval_,
737                               const MAT2 &eigvect_,
738                               tol_type_for_qr tol = default_tol_for_qr,
739                               bool compvect = true) {
740     VECT &eigval = const_cast<VECT &>(eigval_);
741     MAT2 &eigvect = const_cast<MAT2 &>(eigvect_);
742     typedef typename linalg_traits<MAT1>::value_type T;
743     typedef typename number_traits<T>::magnitude_type R;
744 
745     size_type n = mat_nrows(A), q = 0, p, ite = 0;
746     if (compvect) gmm::copy(identity_matrix(), eigvect);
747     if (n == 0) return;
748     if (n == 1) { eigval[0]=gmm::real(A(0,0)); return; }
749     dense_matrix<T> Tri(n, n);
750     gmm::copy(A, Tri);
751 
752     Householder_tridiagonalization(Tri, eigvect, compvect);
753 
754     std::vector<R> diag(n);
755     std::vector<T> sdiag(n);
756     for (size_type i = 0; i < n; ++i)
757       { diag[i] = gmm::real(Tri(i, i)); if (i+1 < n) sdiag[i] = Tri(i+1, i); }
758 
759     symmetric_qr_stop_criterion(diag, sdiag, p, q, tol);
760 
761     while (q < n) {
762       sub_interval SUBI(p, n-p-q), SUBJ(0, mat_ncols(eigvect)), SUBK(p, n-p-q);
763       if (!compvect) SUBK = sub_interval(0,0);
764 
765       symmetric_Wilkinson_qr_step(sub_vector(diag, SUBI),
766                                   sub_vector(sdiag, SUBI),
767                                   sub_matrix(eigvect, SUBJ, SUBK), compvect);
768 
769       symmetric_qr_stop_criterion(diag, sdiag, p, q, tol*R(3));
770       ++ite;
771       GMM_ASSERT1(ite < n*100, "QR algorithm failed.");
772     }
773 
774     gmm::copy(diag, eigval);
775   }
776 
777 
778   template <typename MAT1, typename VECT>
779     void symmetric_qr_algorithm(const MAT1 &a, VECT &eigval,
780                                 tol_type_for_qr tol = default_tol_for_qr) {
781     dense_matrix<typename linalg_traits<MAT1>::value_type> m(0,0);
782     symmetric_qr_algorithm(a, eigval, m, tol, false);
783   }
784 
785 
786 }
787 
788 #endif
789 
790