|
Packit |
67cb25 |
/* gmres.c
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* Copyright (C) 2014 Patrick Alken
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
67cb25 |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
67cb25 |
* the Free Software Foundation; either version 3 of the License, or (at
|
|
Packit |
67cb25 |
* your option) any later version.
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* This program is distributed in the hope that it will be useful, but
|
|
Packit |
67cb25 |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
67cb25 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
67cb25 |
* General Public License for more details.
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
67cb25 |
* along with this program; if not, write to the Free Software
|
|
Packit |
67cb25 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
#include <config.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_math.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_vector.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_matrix.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_linalg.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_errno.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_blas.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_spmatrix.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_spblas.h>
|
|
Packit |
67cb25 |
#include <gsl/gsl_splinalg.h>
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
* The code in this module is based on the Householder GMRES
|
|
Packit |
67cb25 |
* algorithm described in
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* [1] H. F. Walker, Implementation of the GMRES method using
|
|
Packit |
67cb25 |
* Householder transformations, SIAM J. Sci. Stat. Comput.
|
|
Packit |
67cb25 |
* 9(1), 1988.
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* [2] Y. Saad, Iterative methods for sparse linear systems,
|
|
Packit |
67cb25 |
* 2nd edition, SIAM, 2003.
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
typedef struct
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
size_t n; /* size of linear system */
|
|
Packit |
67cb25 |
size_t m; /* dimension of Krylov subspace K_m */
|
|
Packit |
67cb25 |
gsl_vector *r; /* residual vector r = b - A*x */
|
|
Packit |
67cb25 |
gsl_matrix *H; /* Hessenberg matrix n-by-(m+1) */
|
|
Packit |
67cb25 |
gsl_vector *tau; /* householder scalars */
|
|
Packit |
67cb25 |
gsl_vector *y; /* least squares rhs and solution vector */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
double *c; /* Givens rotations */
|
|
Packit |
67cb25 |
double *s;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
double normr; /* residual norm ||r|| */
|
|
Packit |
67cb25 |
} gmres_state_t;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static void gmres_free(void *vstate);
|
|
Packit |
67cb25 |
static int gmres_iterate(const gsl_spmatrix *A, const gsl_vector *b,
|
|
Packit |
67cb25 |
const double tol, gsl_vector *x, void *vstate);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
gmres_alloc()
|
|
Packit |
67cb25 |
Allocate a GMRES workspace for solving an n-by-n system A x = b
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Inputs: n - size of system
|
|
Packit |
67cb25 |
krylov_m - size of Krylov subspace (ie: number of inner iterations)
|
|
Packit |
67cb25 |
if this parameter is 0, the value GSL_MIN(n,10) is
|
|
Packit |
67cb25 |
used
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Return: pointer to workspace
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static void *
|
|
Packit |
67cb25 |
gmres_alloc(const size_t n, const size_t m)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gmres_state_t *state;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (n == 0)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("matrix dimension n must be a positive integer",
|
|
Packit |
67cb25 |
GSL_EINVAL);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state = calloc(1, sizeof(gmres_state_t));
|
|
Packit |
67cb25 |
if (!state)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("failed to allocate gmres state", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->n = n;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute size of Krylov subspace */
|
|
Packit |
67cb25 |
if (m == 0)
|
|
Packit |
67cb25 |
state->m = GSL_MIN(n, 10);
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
state->m = GSL_MIN(n, m);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->r = gsl_vector_alloc(n);
|
|
Packit |
67cb25 |
if (!state->r)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gmres_free(state);
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("failed to allocate r vector", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->H = gsl_matrix_alloc(n, state->m + 1);
|
|
Packit |
67cb25 |
if (!state->H)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gmres_free(state);
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("failed to allocate H matrix", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->tau = gsl_vector_alloc(state->m + 1);
|
|
Packit |
67cb25 |
if (!state->tau)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gmres_free(state);
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("failed to allocate tau vector", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->y = gsl_vector_alloc(state->m + 1);
|
|
Packit |
67cb25 |
if (!state->y)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gmres_free(state);
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("failed to allocate y vector", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->c = malloc(state->m * sizeof(double));
|
|
Packit |
67cb25 |
state->s = malloc(state->m * sizeof(double));
|
|
Packit |
67cb25 |
if (!state->c || !state->s)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gmres_free(state);
|
|
Packit |
67cb25 |
GSL_ERROR_NULL("failed to allocate Givens vectors", GSL_ENOMEM);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
state->normr = 0.0;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return state;
|
|
Packit |
67cb25 |
} /* gmres_alloc() */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static void
|
|
Packit |
67cb25 |
gmres_free(void *vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gmres_state_t *state = (gmres_state_t *) vstate;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->r)
|
|
Packit |
67cb25 |
gsl_vector_free(state->r);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->H)
|
|
Packit |
67cb25 |
gsl_matrix_free(state->H);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->tau)
|
|
Packit |
67cb25 |
gsl_vector_free(state->tau);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->y)
|
|
Packit |
67cb25 |
gsl_vector_free(state->y);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->c)
|
|
Packit |
67cb25 |
free(state->c);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (state->s)
|
|
Packit |
67cb25 |
free(state->s);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
free(state);
|
|
Packit |
67cb25 |
} /* gmres_free() */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
gmres_iterate()
|
|
Packit |
67cb25 |
Solve A*x = b using GMRES algorithm
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Inputs: A - sparse square matrix
|
|
Packit |
67cb25 |
b - right hand side vector
|
|
Packit |
67cb25 |
tol - stopping tolerance (see below)
|
|
Packit |
67cb25 |
x - (input/output) on input, initial estimate x_0;
|
|
Packit |
67cb25 |
on output, solution vector
|
|
Packit |
67cb25 |
work - workspace
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Return:
|
|
Packit |
67cb25 |
GSL_SUCCESS if converged to solution (solution stored in x). In
|
|
Packit |
67cb25 |
this case the following will be true:
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
||b - A*x|| <= tol * ||b||
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
GSL_CONTINUE if not yet converged; in this case x contains the
|
|
Packit |
67cb25 |
most recent solution vector and calling this function more times
|
|
Packit |
67cb25 |
with the input x could result in convergence (ie: restarted GMRES)
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
Notes:
|
|
Packit |
67cb25 |
1) Based on algorithm 2.2 of (Walker, 1998 [1]) and algorithm 6.10 of
|
|
Packit |
67cb25 |
(Saad, 2003 [2])
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
2) On output, work->normr contains ||b - A*x||
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static int
|
|
Packit |
67cb25 |
gmres_iterate(const gsl_spmatrix *A, const gsl_vector *b,
|
|
Packit |
67cb25 |
const double tol, gsl_vector *x,
|
|
Packit |
67cb25 |
void *vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
const size_t N = A->size1;
|
|
Packit |
67cb25 |
gmres_state_t *state = (gmres_state_t *) vstate;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (N != A->size2)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR("matrix must be square", GSL_ENOTSQR);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else if (N != b->size)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR("matrix does not match right hand side", GSL_EBADLEN);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else if (N != x->size)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR("matrix does not match solution vector", GSL_EBADLEN);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else if (N != state->n)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
GSL_ERROR("matrix does not match workspace", GSL_EBADLEN);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
int status = GSL_SUCCESS;
|
|
Packit |
67cb25 |
const size_t maxit = state->m;
|
|
Packit |
67cb25 |
const double normb = gsl_blas_dnrm2(b); /* ||b|| */
|
|
Packit |
67cb25 |
const double reltol = tol * normb; /* tol*||b|| */
|
|
Packit |
67cb25 |
double normr; /* ||r|| */
|
|
Packit |
67cb25 |
size_t m, k;
|
|
Packit |
67cb25 |
double tau; /* householder scalar */
|
|
Packit |
67cb25 |
gsl_matrix *H = state->H; /* Hessenberg matrix */
|
|
Packit |
67cb25 |
gsl_vector *r = state->r; /* residual vector */
|
|
Packit |
67cb25 |
gsl_vector *w = state->y; /* least squares RHS */
|
|
Packit |
67cb25 |
gsl_matrix_view Rm; /* R_m = H(1:m,2:m+1) */
|
|
Packit |
67cb25 |
gsl_vector_view ym; /* y(1:m) */
|
|
Packit |
67cb25 |
gsl_vector_view h0 = gsl_matrix_column(H, 0);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
* The Hessenberg matrix will have the following structure:
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* H = [ ||r_0|| | v_1 v_2 ... v_m ]
|
|
Packit |
67cb25 |
* [ u_1 | u_2 u_3 ... u_{m+1} ]
|
|
Packit |
67cb25 |
*
|
|
Packit |
67cb25 |
* where v_j are the orthonormal vectors spanning the Krylov
|
|
Packit |
67cb25 |
* subpsace of length j + 1 and u_{j+1} are the householder
|
|
Packit |
67cb25 |
* vectors of length n - j - 1.
|
|
Packit |
67cb25 |
* In fact, u_{j+1} has length n - j since u_{j+1}[0] = 1,
|
|
Packit |
67cb25 |
* but this 1 is not stored.
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
gsl_matrix_set_zero(H);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* Step 1a: compute r = b - A*x_0 */
|
|
Packit |
67cb25 |
gsl_vector_memcpy(r, b);
|
|
Packit |
67cb25 |
gsl_spblas_dgemv(CblasNoTrans, -1.0, A, x, 1.0, r);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* Step 1b */
|
|
Packit |
67cb25 |
gsl_vector_memcpy(&h0.vector, r);
|
|
Packit |
67cb25 |
tau = gsl_linalg_householder_transform(&h0.vector);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* store tau_1 */
|
|
Packit |
67cb25 |
gsl_vector_set(state->tau, 0, tau);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* initialize w (stored in state->y) */
|
|
Packit |
67cb25 |
gsl_vector_set_zero(w);
|
|
Packit |
67cb25 |
gsl_vector_set(w, 0, gsl_vector_get(&h0.vector, 0));
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
for (m = 1; m <= maxit; ++m)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
size_t j = m - 1; /* C indexing */
|
|
Packit |
67cb25 |
double c, s; /* Givens rotation */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* v_m */
|
|
Packit |
67cb25 |
gsl_vector_view vm = gsl_matrix_column(H, m);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* v_m(m:end) */
|
|
Packit |
67cb25 |
gsl_vector_view vv = gsl_vector_subvector(&vm.vector, j, N - j);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* householder vector u_m for projection P_m */
|
|
Packit |
67cb25 |
gsl_vector_view um = gsl_matrix_subcolumn(H, j, j, N - j);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* Step 2a: form v_m = P_m e_m = e_m - tau_m w_m */
|
|
Packit |
67cb25 |
gsl_vector_set_zero(&vm.vector);
|
|
Packit |
67cb25 |
gsl_vector_memcpy(&vv.vector, &um.vector);
|
|
Packit |
67cb25 |
tau = gsl_vector_get(state->tau, j); /* tau_m */
|
|
Packit |
67cb25 |
gsl_vector_scale(&vv.vector, -tau);
|
|
Packit |
67cb25 |
gsl_vector_set(&vv.vector, 0, 1.0 - tau);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* Step 2a: v_m <- P_1 P_2 ... P_{m-1} v_m */
|
|
Packit |
67cb25 |
for (k = j; k > 0 && k--; )
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gsl_vector_view uk =
|
|
Packit |
67cb25 |
gsl_matrix_subcolumn(H, k, k, N - k);
|
|
Packit |
67cb25 |
gsl_vector_view vk =
|
|
Packit |
67cb25 |
gsl_vector_subvector(&vm.vector, k, N - k);
|
|
Packit |
67cb25 |
tau = gsl_vector_get(state->tau, k);
|
|
Packit |
67cb25 |
gsl_linalg_householder_hv(tau, &uk.vector, &vk.vector);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* Step 2a: v_m <- A*v_m */
|
|
Packit |
67cb25 |
gsl_spblas_dgemv(CblasNoTrans, 1.0, A, &vm.vector, 0.0, r);
|
|
Packit |
67cb25 |
gsl_vector_memcpy(&vm.vector, r);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* Step 2a: v_m <- P_m ... P_1 v_m */
|
|
Packit |
67cb25 |
for (k = 0; k <= j; ++k)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gsl_vector_view uk = gsl_matrix_subcolumn(H, k, k, N - k);
|
|
Packit |
67cb25 |
gsl_vector_view vk = gsl_vector_subvector(&vm.vector, k, N - k);
|
|
Packit |
67cb25 |
tau = gsl_vector_get(state->tau, k);
|
|
Packit |
67cb25 |
gsl_linalg_householder_hv(tau, &uk.vector, &vk.vector);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* Steps 2c,2d: find P_{m+1} and set v_m <- P_{m+1} v_m */
|
|
Packit |
67cb25 |
if (m < N)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
/* householder vector u_{m+1} for projection P_{m+1} */
|
|
Packit |
67cb25 |
gsl_vector_view ump1 = gsl_matrix_subcolumn(H, m, m, N - m);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
tau = gsl_linalg_householder_transform(&ump1.vector);
|
|
Packit |
67cb25 |
gsl_vector_set(state->tau, j + 1, tau);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* Step 2e: v_m <- J_{m-1} ... J_1 v_m */
|
|
Packit |
67cb25 |
for (k = 0; k < j; ++k)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
gsl_linalg_givens_gv(&vm.vector, k, k + 1,
|
|
Packit |
67cb25 |
state->c[k], state->s[k]);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (m < N)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
/* Step 2g: find givens rotation J_m for v_m(m:m+1) */
|
|
Packit |
67cb25 |
gsl_linalg_givens(gsl_vector_get(&vm.vector, j),
|
|
Packit |
67cb25 |
gsl_vector_get(&vm.vector, j + 1),
|
|
Packit |
67cb25 |
&c, &s);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* store givens rotation for later use */
|
|
Packit |
67cb25 |
state->c[j] = c;
|
|
Packit |
67cb25 |
state->s[j] = s;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* Step 2h: v_m <- J_m v_m */
|
|
Packit |
67cb25 |
gsl_linalg_givens_gv(&vm.vector, j, j + 1, c, s);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* Step 2h: w <- J_m w */
|
|
Packit |
67cb25 |
gsl_linalg_givens_gv(w, j, j + 1, c, s);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
* Step 2i: R_m = [ R_{m-1}, v_m ] - already taken care
|
|
Packit |
67cb25 |
* of due to our memory storage scheme
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* Step 2j: check residual w_{m+1} for convergence */
|
|
Packit |
67cb25 |
normr = fabs(gsl_vector_get(w, j + 1));
|
|
Packit |
67cb25 |
if (normr <= reltol)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
* method has converged, break out of loop to compute
|
|
Packit |
67cb25 |
* update to solution vector x
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
break;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
* At this point, we have either converged to a solution or
|
|
Packit |
67cb25 |
* completed all maxit iterations. In either case, compute
|
|
Packit |
67cb25 |
* an update to the solution vector x and test again for
|
|
Packit |
67cb25 |
* convergence.
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* rewind m if we exceeded maxit iterations */
|
|
Packit |
67cb25 |
if (m > maxit)
|
|
Packit |
67cb25 |
m--;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* Step 3a: solve triangular system R_m y_m = w, in place */
|
|
Packit |
67cb25 |
Rm = gsl_matrix_submatrix(H, 0, 1, m, m);
|
|
Packit |
67cb25 |
ym = gsl_vector_subvector(w, 0, m);
|
|
Packit |
67cb25 |
gsl_blas_dtrsv(CblasUpper, CblasNoTrans, CblasNonUnit,
|
|
Packit |
67cb25 |
&Rm.matrix, &ym.vector);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/*
|
|
Packit |
67cb25 |
* Step 3b: update solution vector x; the loop below
|
|
Packit |
67cb25 |
* uses a different but equivalent formulation from
|
|
Packit |
67cb25 |
* Saad, algorithm 6.10, step 14; store Krylov projection
|
|
Packit |
67cb25 |
* V_m y_m in 'r'
|
|
Packit |
67cb25 |
*/
|
|
Packit |
67cb25 |
gsl_vector_set_zero(r);
|
|
Packit |
67cb25 |
for (k = m; k > 0 && k--; )
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
double ymk = gsl_vector_get(&ym.vector, k);
|
|
Packit |
67cb25 |
gsl_vector_view uk = gsl_matrix_subcolumn(H, k, k, N - k);
|
|
Packit |
67cb25 |
gsl_vector_view rk = gsl_vector_subvector(r, k, N - k);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* r <- n_k e_k + r */
|
|
Packit |
67cb25 |
gsl_vector_set(r, k, gsl_vector_get(r, k) + ymk);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* r <- P_k r */
|
|
Packit |
67cb25 |
tau = gsl_vector_get(state->tau, k);
|
|
Packit |
67cb25 |
gsl_linalg_householder_hv(tau, &uk.vector, &rk.vector);
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* x <- x + V_m y_m */
|
|
Packit |
67cb25 |
gsl_vector_add(x, r);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* compute new residual r = b - A*x */
|
|
Packit |
67cb25 |
gsl_vector_memcpy(r, b);
|
|
Packit |
67cb25 |
gsl_spblas_dgemv(CblasNoTrans, -1.0, A, x, 1.0, r);
|
|
Packit |
67cb25 |
normr = gsl_blas_dnrm2(r);
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
if (normr <= reltol)
|
|
Packit |
67cb25 |
status = GSL_SUCCESS; /* converged */
|
|
Packit |
67cb25 |
else
|
|
Packit |
67cb25 |
status = GSL_CONTINUE; /* not yet converged */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
/* store residual norm */
|
|
Packit |
67cb25 |
state->normr = normr;
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
return status;
|
|
Packit |
67cb25 |
}
|
|
Packit |
67cb25 |
} /* gmres_iterate() */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static double
|
|
Packit |
67cb25 |
gmres_normr(const void *vstate)
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
const gmres_state_t *state = (const gmres_state_t *) vstate;
|
|
Packit |
67cb25 |
return state->normr;
|
|
Packit |
67cb25 |
} /* gmres_normr() */
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
static const gsl_splinalg_itersolve_type gmres_type =
|
|
Packit |
67cb25 |
{
|
|
Packit |
67cb25 |
"gmres",
|
|
Packit |
67cb25 |
&gmres_alloc,
|
|
Packit |
67cb25 |
&gmres_iterate,
|
|
Packit |
67cb25 |
&gmres_normr,
|
|
Packit |
67cb25 |
&gmres_free
|
|
Packit |
67cb25 |
};
|
|
Packit |
67cb25 |
|
|
Packit |
67cb25 |
const gsl_splinalg_itersolve_type * gsl_splinalg_itersolve_gmres =
|
|
Packit |
67cb25 |
&gmres_type;
|