mirror of https://github.com/CGAL/cgal
191 lines
6.2 KiB
C++
191 lines
6.2 KiB
C++
/*
|
|
* OpenNL<T>: Generic Numerical Library
|
|
* Copyright (C) 2004 Bruno Levy
|
|
*
|
|
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
* In addition, as a special exception, the INRIA gives permission to link the
|
|
* code of this program with the CGAL library, and distribute linked combinations
|
|
* including the two. You must obey the GNU General Public License in all respects
|
|
* for all of the code used other than CGAL.
|
|
*
|
|
* If you modify this software, you should include a notice giving the
|
|
* name of the person performing the modification, the date of modification,
|
|
* and the reason for such modification.
|
|
*
|
|
* Contact: Bruno Levy
|
|
*
|
|
* levy@loria.fr
|
|
*
|
|
* ISA-ALICE Project
|
|
* LORIA, INRIA Lorraine,
|
|
* Campus Scientifique, BP 239
|
|
* 54506 VANDOEUVRE LES NANCY CEDEX
|
|
* FRANCE
|
|
*
|
|
* Note that the GNU General Public License does not permit incorporating
|
|
* the Software into proprietary programs.
|
|
*
|
|
* Laurent Saboret 01/2005-04/2005: Changes for CGAL:
|
|
* - Added OpenNL namespace
|
|
* - solve() returns true on success
|
|
* - test divisions by zero with IsZero() method
|
|
* - added comments and traces
|
|
*/
|
|
|
|
#ifndef __OPENNL_CONJUGATE_GRADIENT__
|
|
#define __OPENNL_CONJUGATE_GRADIENT__
|
|
|
|
#include <OpenNL/blas.h>
|
|
|
|
#include <cmath>
|
|
#include <cfloat>
|
|
#include <climits>
|
|
#include <cassert>
|
|
|
|
namespace OpenNL {
|
|
|
|
|
|
// Utility macro to display a variable's value
|
|
// Usage: x=3.7; cerr << OPENNL_STREAM_TRACE(x) << endl;
|
|
// prints
|
|
// x=3.7
|
|
#define OPENNL_STREAM_TRACE(var) #var << "=" << var << " "
|
|
|
|
|
|
/**
|
|
* The Conjugate Gradient algorithm without preconditioner:
|
|
* Ashby, Manteuffel, Saylor
|
|
* A taxononmy for conjugate gradient methods
|
|
* SIAM J Numer Anal 27, 1542-1568 (1990)
|
|
*
|
|
* This implementation is inspired by the lsolver library,
|
|
* by Christian Badura, available from:
|
|
* http://www.mathematik.uni-freiburg.de/IAM/Research/projectskr/lin_solver/
|
|
*
|
|
* @param A generic matrix, a function
|
|
* mult(const MATRIX& M, const double* x, double* y)
|
|
* needs to be defined.
|
|
* @param b right hand side of the system.
|
|
* @param x initial value.
|
|
* @param eps threshold for the residual.
|
|
* @param max_iter maximum number of iterations.
|
|
*/
|
|
|
|
template< class MATRIX, class VECTOR > class Solver_CG {
|
|
public:
|
|
typedef MATRIX Matrix ;
|
|
typedef VECTOR Vector ;
|
|
typedef typename Vector::CoeffType CoeffType ;
|
|
|
|
Solver_CG() {
|
|
epsilon_ = 1e-6 ;
|
|
max_iter_ = 0 ;
|
|
}
|
|
|
|
void set_epsilon(CoeffType eps) { epsilon_ = eps ; }
|
|
void set_max_iter(unsigned int max_iter) { max_iter_ = max_iter ; }
|
|
|
|
// Solve the sparse linear system "A*x = b" for A SYMMETRIC POSITIVE
|
|
// Return true on success
|
|
//
|
|
// Preconditions:
|
|
// * A.dimension() == b.dimension()
|
|
// * A.dimension() == x.dimension()
|
|
bool solve(const MATRIX &A, const VECTOR& b, VECTOR& x) {
|
|
assert(A.dimension() == b.dimension()) ;
|
|
assert(A.dimension() == x.dimension()) ;
|
|
assert (A.dimension() > 0);
|
|
|
|
unsigned int n = A.dimension() ; // (Square) matrix dimension
|
|
|
|
unsigned int max_iter = max_iter_ ; // Max number of iterations
|
|
if(max_iter == 0) {
|
|
max_iter = 5 * n ;
|
|
}
|
|
Vector g(n) ;
|
|
Vector r(n) ;
|
|
Vector p(n) ;
|
|
unsigned int its=0; // Loop counter
|
|
CoeffType t, tau, sig, rho, gam;
|
|
CoeffType bnorm2 = BLAS<Vector>::dot(b,b) ;
|
|
CoeffType err=epsilon_*epsilon_*bnorm2 ; // Error to reach
|
|
// Current residue g=b-A*x
|
|
mult(A,x,g);
|
|
BLAS<Vector>::axpy(-1,b,g);
|
|
BLAS<Vector>::scal(-1,g);
|
|
// Initially, r=g=b-A*x
|
|
BLAS<Vector>::copy(g,r); // r = g
|
|
CoeffType gg=BLAS<Vector>::dot(g,g); // Current error gg = (g|g)
|
|
|
|
while ( gg>err && its < max_iter)
|
|
{
|
|
mult(A,r,p);
|
|
rho=BLAS<Vector>::dot(p,p);
|
|
sig=BLAS<Vector>::dot(r,p);
|
|
tau=BLAS<Vector>::dot(g,r);
|
|
if (IsZero(sig))
|
|
break; // stop if bad conditioning
|
|
t=tau/sig;
|
|
BLAS<Vector>::axpy(t,r,x);
|
|
BLAS<Vector>::axpy(-t,p,g);
|
|
if (IsZero(tau))
|
|
break; // stop if bad conditioning
|
|
gam=(t*t*rho-tau)/tau;
|
|
BLAS<Vector>::scal(gam,r);
|
|
BLAS<Vector>::axpy(1,g,r);
|
|
gg=BLAS<Vector>::dot(g,g); // Current error gg = (g|g)
|
|
its++;
|
|
}
|
|
|
|
bool success = (gg <= err);
|
|
#ifndef NDEBUG
|
|
// Trace on error
|
|
if ( ! success )
|
|
std::cerr << "Solver_CG<>::solve: failure: "
|
|
<< "(" << OPENNL_STREAM_TRACE(its) << OPENNL_STREAM_TRACE(max_iter)
|
|
<< OPENNL_STREAM_TRACE(gg) << OPENNL_STREAM_TRACE(err)
|
|
<< ")" << std::endl;
|
|
#endif
|
|
return success;
|
|
}
|
|
|
|
private:
|
|
// Test if a floating point number is (close to) 0.0
|
|
static inline bool IsZero(CoeffType a)
|
|
{
|
|
return (fabs(a) < 10.0 * std::numeric_limits<CoeffType>::min());
|
|
}
|
|
|
|
// Test if 2 floating point numbers are very close
|
|
static inline bool AreEqual(CoeffType a, CoeffType b)
|
|
{
|
|
if (IsZero(a))
|
|
return IsZero(b);
|
|
else
|
|
return IsZero(b/a - 1.0);
|
|
}
|
|
|
|
private:
|
|
CoeffType epsilon_ ;
|
|
unsigned int max_iter_ ;
|
|
} ;
|
|
|
|
|
|
}; // namespace OpenNL
|
|
|
|
#endif // __OPENNL_CONJUGATE_GRADIENT__
|
|
|