1 /* -*- c++ -*- (enables emacs c++ mode) */
2 /*===========================================================================
3 
4  Copyright (C) 2002-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 // This file is a modified version of qmr.h from ITL.
33 // See http://osl.iu.edu/research/itl/
34 // Following the corresponding Copyright notice.
35 //===========================================================================
36 //
37 // Copyright (c) 1997-2020, The Trustees of Indiana University.
38 // All rights reserved.
39 // Redistribution and use in source and binary forms, with or without
40 // modification, are permitted provided that the following conditions are met:
41 //
42 //    * Redistributions of source code must retain the above copyright
43 //      notice, this list of conditions and the following disclaimer.
44 //    * Redistributions in binary form must reproduce the above copyright
45 //      notice, this list of conditions and the following disclaimer in the
46 //      documentation and/or other materials provided with the distribution.
47 //    * Neither the name of the University of Notre Dame nor the
48 //      names of its contributors may be used to endorse or promote products
49 //      derived from this software without specific prior written permission.
50 //
51 // THIS SOFTWARE  IS  PROVIDED  BY  THE TRUSTEES  OF  INDIANA UNIVERSITY  AND
52 // CONTRIBUTORS  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,  INCLUDING,
53 // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS
54 // FOR  A PARTICULAR PURPOSE ARE DISCLAIMED. IN  NO  EVENT SHALL THE TRUSTEES
55 // OF INDIANA UNIVERSITY AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
56 // INCIDENTAL, SPECIAL, EXEMPLARY,  OR CONSEQUENTIAL DAMAGES (INCLUDING,  BUT
57 // NOT  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
58 // DATA,  OR PROFITS;  OR BUSINESS  INTERRUPTION)  HOWEVER  CAUSED AND ON ANY
59 // THEORY  OF  LIABILITY,  WHETHER  IN  CONTRACT,  STRICT  LIABILITY, OR TORT
60 // (INCLUDING  NEGLIGENCE  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
61 // THIS  SOFTWARE,  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62 //
63 //===========================================================================
64 
65 /**@file gmm_solver_qmr.h
66    @author Andrew Lumsdaine <lums@osl.iu.edu>
67    @author Lie-Quan Lee     <llee@osl.iu.edu>
68    @author  Yves Renard <Yves.Renard@insa-lyon.fr>
69    @date October 13, 2002.
70    @brief Quasi-Minimal Residual iterative solver.
71 */
72 #ifndef GMM_QMR_H
73 #define GMM_QMR_H
74 
75 #include "gmm_kernel.h"
76 #include "gmm_iter.h"
77 
78 namespace gmm {
79 
80   /** Quasi-Minimal Residual.
81 
82      This routine solves the unsymmetric linear system Ax = b using
83      the Quasi-Minimal Residual method.
84 
85      See: R. W. Freund and N. M. Nachtigal, A quasi-minimal residual
86      method for non-Hermitian linear systems, Numerical Math.,
87      60(1991), pp. 315-339
88 
89      Preconditioner -  Incomplete LU, Incomplete LU with threshold,
90                        SSOR or identity_preconditioner.
91   */
92   template <typename Matrix, typename Vector, typename VectorB,
93 	    typename Precond1>
qmr(const Matrix & A,Vector & x,const VectorB & b,const Precond1 & M1,iteration & iter)94   void qmr(const Matrix &A, Vector &x, const VectorB &b, const Precond1 &M1,
95 	   iteration& iter) {
96 
97     typedef typename linalg_traits<Vector>::value_type T;
98     typedef typename number_traits<T>::magnitude_type R;
99 
100     T delta(0), ep(0), beta(0), theta_1(0), gamma_1(0);
101     T theta(0), gamma(1), eta(-1);
102     R rho_1(0), rho, xi;
103 
104     typedef typename temporary_vector<Vector>::vector_type TmpVec;
105     size_type nn = vect_size(x);
106     TmpVec r(nn), v_tld(nn), y(nn), w_tld(nn), z(nn), v(nn), w(nn);
107     TmpVec y_tld(nn), z_tld(nn), p(nn), q(nn), p_tld(nn), d(nn), s(nn);
108 
109     iter.set_rhsnorm(double(gmm::vect_norm2(b)));
110     if (iter.get_rhsnorm() == 0.0) { clear(x); return; }
111 
112     gmm::mult(A, gmm::scaled(x, T(-1)), b, r);
113     gmm::copy(r, v_tld);
114 
115     gmm::left_mult(M1, v_tld, y);
116     rho = gmm::vect_norm2(y);
117 
118     gmm::copy(r, w_tld);
119     gmm::transposed_right_mult(M1, w_tld, z);
120     xi = gmm::vect_norm2(z);
121 
122     while (! iter.finished_vect(r)) {
123 
124       if (rho == R(0) || xi == R(0)) {
125 	if (iter.get_maxiter() == size_type(-1))
126 	  { GMM_ASSERT1(false, "QMR failed to converge"); }
127 	else { GMM_WARNING1("QMR failed to converge"); return; }
128       }
129       gmm::copy(gmm::scaled(v_tld, T(R(1)/rho)), v);
130       gmm::scale(y, T(R(1)/rho));
131 
132       gmm::copy(gmm::scaled(w_tld, T(R(1)/xi)), w);
133       gmm::scale(z, T(R(1)/xi));
134 
135       delta = gmm::vect_sp(z, y);
136       if (delta == T(0)) {
137 	if (iter.get_maxiter() == size_type(-1))
138 	  { GMM_ASSERT1(false, "QMR failed to converge"); }
139 	else { GMM_WARNING1("QMR failed to converge"); return; }
140       }
141       gmm::right_mult(M1, y, y_tld);
142       gmm::transposed_left_mult(M1, z, z_tld);
143 
144       if (iter.first()) {
145 	gmm::copy(y_tld, p);
146 	gmm::copy(z_tld, q);
147       } else {
148 	gmm::add(y_tld, gmm::scaled(p, -(T(xi  * delta) / ep)), p);
149 	gmm::add(z_tld, gmm::scaled(q, -(T(rho * delta) / ep)), q);
150       }
151 
152       gmm::mult(A, p, p_tld);
153 
154       ep = gmm::vect_sp(q, p_tld);
155       if (ep == T(0)) {
156 	if (iter.get_maxiter() == size_type(-1))
157 	  { GMM_ASSERT1(false, "QMR failed to converge"); }
158 	else { GMM_WARNING1("QMR failed to converge"); return; }
159       }
160       beta = ep / delta;
161       if (beta == T(0)) {
162 	if (iter.get_maxiter() == size_type(-1))
163 	  { GMM_ASSERT1(false, "QMR failed to converge"); }
164 	else { GMM_WARNING1("QMR failed to converge"); return; }
165       }
166       gmm::add(p_tld, gmm::scaled(v, -beta), v_tld);
167       gmm::left_mult(M1, v_tld, y);
168 
169       rho_1 = rho;
170       rho = gmm::vect_norm2(y);
171 
172       gmm::mult(gmm::transposed(A), q, w_tld);
173       gmm::add(w_tld, gmm::scaled(w, -beta), w_tld);
174       gmm::transposed_right_mult(M1, w_tld, z);
175 
176       xi = gmm::vect_norm2(z);
177 
178       gamma_1 = gamma;
179       theta_1 = theta;
180 
181       theta = rho / (gamma_1 * beta);
182       gamma = T(1) / gmm::sqrt(T(1) + gmm::sqr(theta));
183 
184       if (gamma == T(0)) {
185 	if (iter.get_maxiter() == size_type(-1))
186 	  { GMM_ASSERT1(false, "QMR failed to converge"); }
187 	else { GMM_WARNING1("QMR failed to converge"); return; }
188       }
189       eta = -eta * T(rho_1) * gmm::sqr(gamma) / (beta * gmm::sqr(gamma_1));
190 
191       if (iter.first()) {
192 	gmm::copy(gmm::scaled(p, eta), d);
193 	gmm::copy(gmm::scaled(p_tld, eta), s);
194       } else {
195 	T tmp = gmm::sqr(theta_1 * gamma);
196 	gmm::add(gmm::scaled(p, eta), gmm::scaled(d, tmp), d);
197 	gmm::add(gmm::scaled(p_tld, eta), gmm::scaled(s, tmp), s);
198       }
199       gmm::add(d, x);
200       gmm::add(gmm::scaled(s, T(-1)), r);
201 
202       ++iter;
203     }
204   }
205 
206 
207 }
208 
209 #endif
210 
211