Idrs refactoring

This commit is contained in:
Jens Wehner 2021-12-02 23:32:07 +00:00 committed by Rasmus Munk Larsen
parent f63c6dd1f9
commit 4ee2e9b340

View File

@ -9,18 +9,15 @@
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_IDRS_H
#define EIGEN_IDRS_H
#include "./InternalHeaderCheck.h"
namespace Eigen
{
namespace Eigen {
namespace internal
{
/** \internal Low-level Induced Dimension Reduction algorithm
namespace internal {
/** \internal Low-level Induced Dimension Reduction algorithm
\param A The matrix A
\param b The right hand side vector b
\param x On input and initial solution, on output the computed solution.
@ -31,16 +28,15 @@ namespace Eigen
\param S On input Number of the dimension of the shadow space.
\param smoothing switches residual smoothing on.
\param angle small omega lead to faster convergence at the expense of numerical stability
\param replacement switches on a residual replacement strategy to increase accuracy of residual at the expense of more Mat*vec products
\return false in the case of numerical issue, for example a break down of IDRS.
*/
template<typename Vector, typename RealScalar>
typename Vector::Scalar omega(const Vector& t, const Vector& s, RealScalar angle)
{
\param replacement switches on a residual replacement strategy to increase accuracy of residual at the
expense of more Mat*vec products \return false in the case of numerical issue, for example a break down of IDRS.
*/
template <typename Vector, typename RealScalar>
typename Vector::Scalar omega(const Vector& t, const Vector& s, RealScalar angle) {
using numext::abs;
typedef typename Vector::Scalar Scalar;
const RealScalar ns = s.norm();
const RealScalar nt = t.norm();
const RealScalar ns = s.StableNorm();
const RealScalar nt = t.StableNorm();
const Scalar ts = t.dot(s);
const RealScalar rho = abs(ts / (nt * ns));
@ -55,13 +51,12 @@ namespace Eigen
return angle * (ns / nt) * (ts / abs(ts));
}
return ts / (nt * nt);
}
}
template <typename MatrixType, typename Rhs, typename Dest, typename Preconditioner>
bool idrs(const MatrixType& A, const Rhs& b, Dest& x, const Preconditioner& precond,
Index& iter,
typename Dest::RealScalar& relres, Index S, bool smoothing, typename Dest::RealScalar angle, bool replacement)
{
template <typename MatrixType, typename Rhs, typename Dest, typename Preconditioner>
bool idrs(const MatrixType& A, const Rhs& b, Dest& x, const Preconditioner& precond, Index& iter,
typename Dest::RealScalar& relres, Index S, bool smoothing, typename Dest::RealScalar angle,
bool replacement) {
typedef typename Dest::RealScalar RealScalar;
typedef typename Dest::Scalar Scalar;
typedef Matrix<Scalar, Dynamic, 1> VectorType;
@ -82,11 +77,10 @@ namespace Eigen
P = (qr.householderQ() * DenseMatrixType::Identity(N, S));
}
const RealScalar normb = b.norm();
const RealScalar normb = b.StableNorm();
if (internal::isApprox(normb, RealScalar(0)))
{
//Solution is the zero vector
if (internal::isApprox(normb, RealScalar(0))) {
// Solution is the zero vector
x.setZero();
iter = 0;
relres = 0;
@ -105,25 +99,21 @@ namespace Eigen
// It only happens if things go very wrong. Too many restarts may ruin the convergence.
const RealScalar mp = RealScalar(1e3) * NumTraits<Scalar>::epsilon();
//Compute initial residual
const RealScalar tolb = tol * normb; //Relative tolerance
// Compute initial residual
const RealScalar tolb = tol * normb; // Relative tolerance
VectorType r = b - A * x;
VectorType x_s, r_s;
if (smoothing)
{
if (smoothing) {
x_s = x;
r_s = r;
}
RealScalar normr = r.norm();
RealScalar normr = r.StableNorm();
if (normr <= tolb)
{
//Initial guess is a good enough solution
if (normr <= tolb) {
// Initial guess is a good enough solution
iter = 0;
relres = normr / normb;
return true;
@ -135,162 +125,146 @@ namespace Eigen
VectorType t(N), v(N);
Scalar om = 1.;
//Main iteration loop, guild G-spaces:
// Main iteration loop, guild G-spaces:
iter = 0;
while (normr > tolb && iter < maxit)
{
//New right hand size for small system:
while (normr > tolb && iter < maxit) {
// New right hand size for small system:
VectorType f = (r.adjoint() * P).adjoint();
for (Index k = 0; k < S; ++k)
{
//Solve small system and make v orthogonal to P:
//c = M(k:s,k:s)\f(k:s);
lu_solver.compute(M.block(k , k , S -k, S - k ));
VectorType c = lu_solver.solve(f.segment(k , S - k ));
//v = r - G(:,k:s)*c;
v = r - G.rightCols(S - k ) * c;
//Preconditioning
for (Index k = 0; k < S; ++k) {
// Solve small system and make v orthogonal to P:
// c = M(k:s,k:s)\f(k:s);
lu_solver.compute(M.block(k, k, S - k, S - k));
VectorType c = lu_solver.solve(f.segment(k, S - k));
// v = r - G(:,k:s)*c;
v = r - G.rightCols(S - k) * c;
// Preconditioning
v = precond.solve(v);
//Compute new U(:,k) and G(:,k), G(:,k) is in space G_j
U.col(k) = U.rightCols(S - k ) * c + om * v;
G.col(k) = A * U.col(k );
// Compute new U(:,k) and G(:,k), G(:,k) is in space G_j
U.col(k) = U.rightCols(S - k) * c + om * v;
G.col(k) = A * U.col(k);
//Bi-Orthogonalise the new basis vectors:
for (Index i = 0; i < k-1 ; ++i)
{
//alpha = ( P(:,i)'*G(:,k) )/M(i,i);
Scalar alpha = P.col(i ).dot(G.col(k )) / M(i, i );
G.col(k ) = G.col(k ) - alpha * G.col(i );
U.col(k ) = U.col(k ) - alpha * U.col(i );
// Bi-Orthogonalise the new basis vectors:
for (Index i = 0; i < k - 1; ++i) {
// alpha = ( P(:,i)'*G(:,k) )/M(i,i);
Scalar alpha = P.col(i).dot(G.col(k)) / M(i, i);
G.col(k) = G.col(k) - alpha * G.col(i);
U.col(k) = U.col(k) - alpha * U.col(i);
}
//New column of M = P'*G (first k-1 entries are zero)
//M(k:s,k) = (G(:,k)'*P(:,k:s))';
M.block(k , k , S - k , 1) = (G.col(k ).adjoint() * P.rightCols(S - k )).adjoint();
// New column of M = P'*G (first k-1 entries are zero)
// M(k:s,k) = (G(:,k)'*P(:,k:s))';
M.block(k, k, S - k, 1) = (G.col(k).adjoint() * P.rightCols(S - k)).adjoint();
if (internal::isApprox(M(k,k), Scalar(0)))
{
if (internal::isApprox(M(k, k), Scalar(0))) {
return false;
}
//Make r orthogonal to q_i, i = 0..k-1
Scalar beta = f(k ) / M(k , k );
r = r - beta * G.col(k );
x = x + beta * U.col(k );
normr = r.norm();
// Make r orthogonal to q_i, i = 0..k-1
Scalar beta = f(k) / M(k, k);
r = r - beta * G.col(k);
x = x + beta * U.col(k);
normr = r.StableNorm();
if (replacement && normr > tolb / mp)
{
if (replacement && normr > tolb / mp) {
trueres = true;
}
//Smoothing:
if (smoothing)
{
// Smoothing:
if (smoothing) {
t = r_s - r;
//gamma is a Scalar, but the conversion is not allowed
Scalar gamma = t.dot(r_s) / t.norm();
// gamma is a Scalar, but the conversion is not allowed
Scalar gamma = t.dot(r_s) / t.StableNorm();
r_s = r_s - gamma * t;
x_s = x_s - gamma * (x_s - x);
normr = r_s.norm();
normr = r_s.StableNorm();
}
if (normr < tolb || iter == maxit)
{
if (normr < tolb || iter == maxit) {
break;
}
//New f = P'*r (first k components are zero)
if (k < S-1)
{
f.segment(k + 1, S - (k + 1) ) = f.segment(k + 1 , S - (k + 1)) - beta * M.block(k + 1 , k , S - (k + 1), 1);
// New f = P'*r (first k components are zero)
if (k < S - 1) {
f.segment(k + 1, S - (k + 1)) = f.segment(k + 1, S - (k + 1)) - beta * M.block(k + 1, k, S - (k + 1), 1);
}
}//end for
} // end for
if (normr < tolb || iter == maxit)
{
if (normr < tolb || iter == maxit) {
break;
}
//Now we have sufficient vectors in G_j to compute residual in G_j+1
//Note: r is already perpendicular to P so v = r
//Preconditioning
// Now we have sufficient vectors in G_j to compute residual in G_j+1
// Note: r is already perpendicular to P so v = r
// Preconditioning
v = r;
v = precond.solve(v);
//Matrix-vector multiplication:
// Matrix-vector multiplication:
t = A * v;
//Computation of a new omega
// Computation of a new omega
om = internal::omega(t, r, angle);
if (om == RealScalar(0.0))
{
if (om == RealScalar(0.0)) {
return false;
}
r = r - om * t;
x = x + om * v;
normr = r.norm();
normr = r.StableNorm();
if (replacement && normr > tolb / mp)
{
if (replacement && normr > tolb / mp) {
trueres = true;
}
//Residual replacement?
if (trueres && normr < normb)
{
// Residual replacement?
if (trueres && normr < normb) {
r = b - A * x;
trueres = false;
replacements++;
}
//Smoothing:
if (smoothing)
{
// Smoothing:
if (smoothing) {
t = r_s - r;
Scalar gamma = t.dot(r_s) /t.norm();
Scalar gamma = t.dot(r_s) / t.StableNorm();
r_s = r_s - gamma * t;
x_s = x_s - gamma * (x_s - x);
normr = r_s.norm();
normr = r_s.StableNorm();
}
iter++;
}//end while
} // end while
if (smoothing)
{
if (smoothing) {
x = x_s;
}
relres=normr/normb;
relres = normr / normb;
return true;
}
}
} // namespace internal
} // namespace internal
template <typename MatrixType_, typename Preconditioner_ = DiagonalPreconditioner<typename MatrixType_::Scalar> >
class IDRS;
template <typename MatrixType_, typename Preconditioner_ = DiagonalPreconditioner<typename MatrixType_::Scalar> >
class IDRS;
namespace internal
{
namespace internal {
template <typename MatrixType_, typename Preconditioner_>
struct traits<Eigen::IDRS<MatrixType_, Preconditioner_> >
{
template <typename MatrixType_, typename Preconditioner_>
struct traits<Eigen::IDRS<MatrixType_, Preconditioner_> > {
typedef MatrixType_ MatrixType;
typedef Preconditioner_ Preconditioner;
};
} // namespace internal
};
} // namespace internal
/** \ingroup IterativeLinearSolvers_Module
* \brief The Induced Dimension Reduction method (IDR(s)) is a short-recurrences Krylov method for sparse square problems.
* \brief The Induced Dimension Reduction method (IDR(s)) is a short-recurrences Krylov method for sparse square
* problems.
*
* This class allows to solve for A.x = b sparse linear problems. The vectors x and b can be either dense or sparse.
* he Induced Dimension Reduction method, IDR(), is a robust and efficient short-recurrence Krylov subspace method for
@ -329,10 +303,8 @@ namespace Eigen
*
* \sa class SimplicialCholesky, DiagonalPreconditioner, IdentityPreconditioner
*/
template <typename MatrixType_, typename Preconditioner_>
class IDRS : public IterativeSolverBase<IDRS<MatrixType_, Preconditioner_> >
{
template <typename MatrixType_, typename Preconditioner_>
class IDRS : public IterativeSolverBase<IDRS<MatrixType_, Preconditioner_> > {
public:
typedef MatrixType_ MatrixType;
typedef typename MatrixType::Scalar Scalar;
@ -353,7 +325,7 @@ namespace Eigen
public:
/** Default constructor. */
IDRS(): m_S(4), m_smoothing(false), m_angle(RealScalar(0.7)), m_residual(false) {}
IDRS() : m_S(4), m_smoothing(false), m_angle(RealScalar(0.7)), m_residual(false) {}
/** Initialize the solver with matrix \a A for further \c Ax=b solving.
@ -366,9 +338,8 @@ namespace Eigen
matrix A, or modify a copy of A.
*/
template <typename MatrixDerived>
explicit IDRS(const EigenBase<MatrixDerived>& A) : Base(A.derived()), m_S(4), m_smoothing(false),
m_angle(RealScalar(0.7)), m_residual(false) {}
explicit IDRS(const EigenBase<MatrixDerived>& A)
: Base(A.derived()), m_S(4), m_smoothing(false), m_angle(RealScalar(0.7)), m_residual(false) {}
/** \internal */
/** Loops over the number of columns of b and does the following:
@ -376,21 +347,19 @@ namespace Eigen
2. Calls the function that has the core solver routine
*/
template <typename Rhs, typename Dest>
void _solve_vector_with_guess_impl(const Rhs& b, Dest& x) const
{
void _solve_vector_with_guess_impl(const Rhs& b, Dest& x) const {
m_iterations = Base::maxIterations();
m_error = Base::m_tolerance;
bool ret = internal::idrs(matrix(), b, x, Base::m_preconditioner, m_iterations, m_error, m_S,m_smoothing,m_angle,m_residual);
bool ret = internal::idrs(matrix(), b, x, Base::m_preconditioner, m_iterations, m_error, m_S, m_smoothing, m_angle,
m_residual);
m_info = (!ret) ? NumericalIssue : m_error <= Base::m_tolerance ? Success : NoConvergence;
}
/** Sets the parameter S, indicating the dimension of the shadow space. Default is 4*/
void setS(Index S)
{
if (S < 1)
{
void setS(Index S) {
if (S < 1) {
S = 4;
}
@ -403,10 +372,7 @@ namespace Eigen
operations. Although monotonic decrease of the residual norms is a
desirable property, the rate of convergence of the unsmoothed process and
the smoothed process is basically the same. Default is off */
void setSmoothing(bool smoothing)
{
m_smoothing=smoothing;
}
void setSmoothing(bool smoothing) { m_smoothing = smoothing; }
/** The angle must be a real scalar. In IDR(s), a value for the
iteration parameter omega must be chosen in every s+1th step. The most
@ -418,20 +384,13 @@ namespace Eigen
between accurate computations and reduction of the residual norm. The
parameter angle =0.7 (maintaining the convergence strategy)
results in such a compromise. */
void setAngle(RealScalar angle)
{
m_angle=angle;
}
void setAngle(RealScalar angle) { m_angle = angle; }
/** The parameter replace is a logical that determines whether a
residual replacement strategy is employed to increase the accuracy of the
solution. */
void setResidualUpdate(bool update)
{
m_residual=update;
}
};
void setResidualUpdate(bool update) { m_residual = update; }
};
} // namespace Eigen