https://github.com/ma-tech/Woolz
Raw File
Tip revision: dc12c17255372167bad207d24ae3c2a201a405b7 authored by Bill Hill on 18 November 2016, 15:51:53 UTC
Fixed floating point threshold bug.
Tip revision: dc12c17
AlgMatrixCG.c
#if defined(__GNUC__)
#ident "University of Edinburgh $Id$"
#else
static char _AlgMatrixCG_c[] = "University of Edinburgh $Id$";
#endif
/*!
* \file         libAlg/AlgMatrixCG.c
* \author       Bill Hill
* \date         March 2003
* \version      $Id$
* \par
* Address:
*               MRC Human Genetics Unit,
*               MRC Institute of Genetics and Molecular Medicine,
*               University of Edinburgh,
*               Western General Hospital,
*               Edinburgh, EH4 2XU, UK.
* \par
* Copyright (C), [2012],
* The University Court of the University of Edinburgh,
* Old College, Edinburgh, UK.
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA  02110-1301, USA.
* \brief	Conjugate Gradient iterative method with preconditioning
*		for the solution of linear systems with the form
*		\f$\mathbf{A} \mathbf{x} = \mathbf{b}\f$.
*		A must be a symmetric postive definite matrix,
*		i.e. \f${\mathbf{x}}^T \mathbf{A} \mathbf{x} < 0\f$,
*		\f$\forall \mathbf{x} \not= \mathbf{0}\f$,
*               \f$\mathbf{x} \in R^n\f$.
*		
*		This code is adapted from CG.f which is described in
*		"Templates for the Solution of Linear Systems: Building Blocks
*		for Iterative Methods", Barrett, Berry, Chan, Demmel, Donato,
*		Dongarra, Eijkhout, Pozo, Romine, and van der Vorst, SIAM
*		Publications, 1993. Also downloadable from
*		http://www.netlib.org/templates/index.html.
* \ingroup	AlgMatrix
*/
#include <Alg.h>
#include <float.h>

#ifdef ALG_MATRIXCG_DEBUG
static void			AlgMatrixCGDebug(
				  FILE *fP,
				  const char *name,
				  double *dat,
				  int nDat);
#endif /* ALG_MATRIXCG_DEBUG */

/*!
* \return	Error code.
* \ingroup	AlgMatrix
* \brief	Conjugate Gradient iterative method with preconditioning
*		for the solution of linear systems with the form
*		\f$\mathbf{A} \mathbf{x} = \mathbf{b}\f$.
*		\f$\mathbf{A}\f$ must be a symmetric postive definite matrix,
*		i.e. \f${\mathbf{x}}^T \mathbf{A} \mathbf{x} < 0\f$,
*		\f$\forall \mathbf{x} \not= \mathbf{0}\f$,
*               \f$\mathbf{x} \in R^n\f$.
*  		Convergence is tested using:
*		\f$ \frac{\| \mathbf{b} - mathbf{A} \mathbf{x} \|}
                         {\|\mathbf{b}\|} < \delta\f$.
*		If the preconditioning function pFn is non NULL then
*		it is called, passing the preconditioning data pDat, as:
*		(*pFn)(void *pDat, double **aM, double *r, double *z, int n)
*		to solve \f$\mathbf{A} \mathbf{z} = \mathbf{r}\f$ for
*		\f$\mathbf{z}\f$ with the solution overwriting the initial
*		contents of z.
* \param	aM			Matrix \f$\mathbf{A}\f$.
* \param	xV			Matrix \f$\mathbf{x}\f$ which
*					should contain an initial estimate
*					although this may be \f$\mathbf{0}\f$.
* \param	bV			Matrix \f$\mathbf{b}\f$.
* \param	wM			Matrix with dimensions [4, n],
*					this must be a rectangular matrix.
* \param	pFn			Preconditioning function.
* \param	pDat			Data to be passed to preconditioning
* 					function.
* \param	tol			Tolerance required, \f$\delta\f$.
* \param	maxItr			The maximum number of itterations.
* \param	dstTol			Destination pointer for the residual
*					after the final iteration, may be NULL.
* \param	dstItr			Destination pointer for the actual
*					number of itterations performed,
*					may be NULL.

*/
AlgError	AlgMatrixCGSolve(
			         AlgMatrix aM,
				 double *xV, double *bV,
				 AlgMatrix wM,
		     		 void (*pFn)(void *, AlgMatrix,
				 	     double *, double *),
				 void *pDat, double tol, int maxItr,
				 double *dstTol, int *dstItr)
{
  int		itr = 0,
  		conv = 0;
  size_t	nN;
  double	alpha,
		beta,
  		resid = DBL_MAX,
  		nrmB;
  double	*r,
  		*p,
		*z,
		*q;
  double	rho[2];
  AlgError	errCode = ALG_ERR_NONE;

  rho[0] = rho[1] = 0.0;
  if((aM.core == NULL) || (aM.core->nR < 1) || (aM.core->nR != aM.core->nC) ||
     (wM.core == NULL) || (wM.core->type != ALG_MATRIX_RECT) ||
     (wM.core->nR != 4) || (wM.core->nC != aM.core->nC) ||
     (xV == NULL) || (bV == NULL) || (tol < 0.0) || (maxItr < 0))
  {
    errCode = ALG_ERR_FUNC;
  }
  else
  {
    switch(aM.core->type)
    {
      case ALG_MATRIX_LLR:  /* FALLTHROUGH */
      case ALG_MATRIX_RECT: /* FALLTHROUGH */
      case ALG_MATRIX_SYM:
	break;
      default:
        errCode = ALG_ERR_FUNC;
	break;
    }
  }
  if(errCode == ALG_ERR_NONE)
  {
    nN = aM.core->nR;
    AlgMatrixZero(wM);
    r = wM.rect->array[0];
    p = wM.rect->array[1];
    z = wM.rect->array[2];
    q = wM.rect->array[3];
    nrmB = AlgVectorNorm(bV, aM.core->nR);
    /* r = b - A x */
    AlgMatrixVectorMul(r, aM, xV);
    AlgVectorSub(r, bV, r, aM.core->nR);
    if(nrmB < DBL_EPSILON) 
    {
      nrmB = 1.0;
    }
    if((resid = AlgVectorNorm(r, nN) / nrmB) <= tol)
    {
      conv = 1;
    }
    while(!conv && (itr < maxItr))
    {
#ifdef ALG_MATRIXCG_DEBUG
      (void )fprintf(stderr, "rho = {%g, %g}\n", rho[0], rho[1]);
      AlgMatrixCGDebug(stderr, "x", xV, nN);
      AlgMatrixCGDebug(stderr, "r", r, nN);
#endif /* ALG_MATRIXCG_DEBUG */
      /* Pre-conditioning: Solve aM z = r for z. */
      if(pFn)
      {
	(*pFn)(pDat, aM, r, z);
      }
      else
      {
	AlgVectorCopy(z, r, nN);
      }
      rho[0] = AlgVectorDot(r, z, nN);
      if(itr == 0)
      {
	AlgVectorCopy(p, z, nN);
      }
      else
      {
	beta = rho[0] / rho[1];
	/* p = z + beta p */
	AlgVectorScaleAdd(p, p, z, beta, nN);
      }
      /* q = A p */
      AlgMatrixVectorMul(q, aM, p);
      alpha = rho[0] / AlgVectorDot(p, q, nN);
      /* x = x + alpha p */
      AlgVectorScaleAdd(xV, p, xV, alpha, nN);
      /* r = r - alpha * q */
      AlgVectorScaleAdd(r, q, r, -alpha, nN);
      if((resid = AlgVectorNorm(r, nN) / nrmB) <= tol)
      {
	tol = resid;
	conv = 1;
      }
      else
      {
	rho[1] = rho[0];
	++itr;
      }
    }
    if(!conv)
    {
      errCode = ALG_ERR_CONVERGENCE;
    }
  }
  if(dstTol)
  {
    *dstTol = resid;
  }
  if(dstItr)
  {
    *dstItr = itr;
  }
  return(errCode);
}

#ifdef ALG_MATRIXCG_DEBUG
static void	AlgMatrixCGDebug(FILE *fP, const char *name,
				 double *dat, int nDat)
{
  const char	*str0 = ", ",
  		*str1 = "}\n";

  (void )fprintf(stderr, "%s = {", name);
  while(nDat-- > 0)
  {
    (void )fprintf(stderr, "%g%s", *dat++, (nDat > 0)? str0: str1);

  }
}
#endif /* ALG_MATRIXCG_DEBUG */
back to top