7154 lines
295 KiB
MQL5
7154 lines
295 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| solvers.mqh |
|
|
//| Copyright 2003-2022 Sergey Bochkanov (ALGLIB project) |
|
|
//| Copyright 2012-2023, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
//| Implementation of ALGLIB library in MetaQuotes Language 5 |
|
|
//| |
|
|
//| The features of the library include: |
|
|
//| - Linear algebra (direct algorithms, EVD, SVD) |
|
|
//| - Solving systems of linear and non-linear equations |
|
|
//| - Interpolation |
|
|
//| - Optimization |
|
|
//| - FFT (Fast Fourier Transform) |
|
|
//| - Numerical integration |
|
|
//| - Linear and nonlinear least-squares fitting |
|
|
//| - Ordinary differential equations |
|
|
//| - Computation of special functions |
|
|
//| - Descriptive statistics and hypothesis testing |
|
|
//| - Data analysis - classification, regression |
|
|
//| - Implementing linear algebra algorithms, interpolation, etc. |
|
|
//| in high-precision arithmetic (using MPFR) |
|
|
//| |
|
|
//| This file 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 (www.fsf.org); 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. |
|
|
//+------------------------------------------------------------------+
|
|
//#include "matrix.mqh"
|
|
#include "ap.mqh"
|
|
#include "alglibinternal.mqh"
|
|
#include "linalg.mqh"
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary class for CDenseSolver |
|
|
//+------------------------------------------------------------------+
|
|
class CDenseSolverReport
|
|
{
|
|
public:
|
|
double m_r1;
|
|
double m_rinf;
|
|
|
|
CDenseSolverReport(void) { ZeroMemory(this); }
|
|
~CDenseSolverReport(void) {}
|
|
|
|
void Copy(CDenseSolverReport &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolverReport::Copy(CDenseSolverReport &obj)
|
|
{
|
|
//--- copy variables
|
|
m_r1=obj.m_r1;
|
|
m_rinf=obj.m_rinf;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This class is a shell for class CDenseSolverReport |
|
|
//+------------------------------------------------------------------+
|
|
class CDenseSolverReportShell
|
|
{
|
|
private:
|
|
CDenseSolverReport m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CDenseSolverReportShell(void) {}
|
|
CDenseSolverReportShell(CDenseSolverReport &obj) { m_innerobj.Copy(obj); }
|
|
~CDenseSolverReportShell(void) {}
|
|
//--- methods
|
|
double GetR1(void);
|
|
void SetR1(const double d);
|
|
double GetRInf(void);
|
|
void SetRInf(const double d);
|
|
CDenseSolverReport *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable r1 |
|
|
//+------------------------------------------------------------------+
|
|
double CDenseSolverReportShell::GetR1(void)
|
|
{
|
|
return(m_innerobj.m_r1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable r1 |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolverReportShell::SetR1(const double d)
|
|
{
|
|
m_innerobj.m_r1=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable rinf |
|
|
//+------------------------------------------------------------------+
|
|
double CDenseSolverReportShell::GetRInf(void)
|
|
{
|
|
return(m_innerobj.m_rinf);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable rinf |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolverReportShell::SetRInf(const double d)
|
|
{
|
|
m_innerobj.m_rinf=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CDenseSolverReport *CDenseSolverReportShell::GetInnerObj(void)
|
|
{
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary class for CDenseSolver |
|
|
//+------------------------------------------------------------------+
|
|
class CDenseSolverLSReport
|
|
{
|
|
public:
|
|
double m_r2;
|
|
CMatrixDouble m_cx;
|
|
int m_n;
|
|
int m_k;
|
|
//--- constructor, destructor
|
|
CDenseSolverLSReport(void) { m_r2=0; m_n=0; m_k=0; }
|
|
~CDenseSolverLSReport(void) {}
|
|
//--- copy
|
|
void Copy(CDenseSolverLSReport &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolverLSReport::Copy(CDenseSolverLSReport &obj)
|
|
{
|
|
//--- copy variables
|
|
m_r2=obj.m_r2;
|
|
m_n=obj.m_n;
|
|
m_k=obj.m_k;
|
|
//--- copy matrix
|
|
m_cx=obj.m_cx;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This class is a shell for class CDenseSolverLSReport |
|
|
//+------------------------------------------------------------------+
|
|
class CDenseSolverLSReportShell
|
|
{
|
|
private:
|
|
CDenseSolverLSReport m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CDenseSolverLSReportShell(void) {}
|
|
CDenseSolverLSReportShell(CDenseSolverLSReport &obj) { m_innerobj.Copy(obj); }
|
|
~CDenseSolverLSReportShell(void) {}
|
|
//--- methods
|
|
double GetR2(void);
|
|
void SetR2(const double d);
|
|
int GetN(void);
|
|
void SetN(const int i);
|
|
int GetK(void);
|
|
void SetK(const int i);
|
|
CDenseSolverLSReport *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable r2 |
|
|
//+------------------------------------------------------------------+
|
|
double CDenseSolverLSReportShell::GetR2(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_r2);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable r2 |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolverLSReportShell::SetR2(const double d)
|
|
{
|
|
m_innerobj.m_r2=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable n |
|
|
//+------------------------------------------------------------------+
|
|
int CDenseSolverLSReportShell::GetN(void)
|
|
{
|
|
return(m_innerobj.m_n);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable n |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolverLSReportShell::SetN(const int i)
|
|
{
|
|
m_innerobj.m_n=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable k |
|
|
//+------------------------------------------------------------------+
|
|
int CDenseSolverLSReportShell::GetK(void)
|
|
{
|
|
return(m_innerobj.m_k);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable k |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolverLSReportShell::SetK(const int i)
|
|
{
|
|
m_innerobj.m_k=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CDenseSolverLSReport *CDenseSolverLSReportShell::GetInnerObj(void)
|
|
{
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver |
|
|
//+------------------------------------------------------------------+
|
|
class CDenseSolver
|
|
{
|
|
private:
|
|
static void RMatrixLUSolveInternal(CMatrixDouble &lua,int &p[],const double scalea,const int n,CMatrixDouble &a,const bool havea,CMatrixDouble &b,const int m,int &info,CDenseSolverReport &rep,CMatrixDouble &x);
|
|
static void SPDMatrixCholeskySolveInternal(CMatrixDouble &cha,const double sqrtscalea,const int n,const bool IsUpper,CMatrixDouble &a,const bool havea,CMatrixDouble &b,const int m,int &info,CDenseSolverReport &rep,CMatrixDouble &x);
|
|
static void CMatrixLUSolveInternal(CMatrixComplex &lua,int &p[],const double scalea,const int n,CMatrixComplex &a,const bool havea,CMatrixComplex &b,const int m,int &info,CDenseSolverReport &rep,CMatrixComplex &x);
|
|
static void HPDMatrixCholeskySolveInternal(CMatrixComplex &cha,const double sqrtscalea,const int n,const bool IsUpper,CMatrixComplex &a,const bool havea,CMatrixComplex &b,const int m,int &info,CDenseSolverReport &rep,CMatrixComplex &x);
|
|
static int CDenseSolverRFSMax(const int n,const double r1,const double rinf);
|
|
static int CDenseSolverRFSMaxV2(const int n,const double r2);
|
|
static void RBasicLUSolve(CMatrixDouble &lua,int &p[],const double scalea,const int n,double &xb[],double &tmp[]);
|
|
static void SPDBasicCholeskySolve(CMatrixDouble &cha,const double sqrtscalea,const int n,const bool IsUpper,double &xb[],double &tmp[]);
|
|
static void CBasicLUSolve(CMatrixComplex &lua,int &p[],const double scalea,const int n,complex &xb[],complex &tmp[]);
|
|
static void HPDBasicCholeskySolve(CMatrixComplex &cha,const double sqrtscalea,const int n,const bool IsUpper,complex &xb[],complex &tmp[]);
|
|
|
|
public:
|
|
static void RMatrixSolve(CMatrixDouble &a,const int n,double &b[],int &info,CDenseSolverReport &rep,double &x[]);
|
|
static void RMatrixSolve(CMatrixDouble &a,const int n,CRowDouble &b,int &info,CDenseSolverReport &rep,CRowDouble &x);
|
|
static void RMatrixSolveM(CMatrixDouble &a,const int n,CMatrixDouble &b,const int m,const bool rfs,int &info,CDenseSolverReport &rep,CMatrixDouble &x);
|
|
static void RMatrixLUSolve(CMatrixDouble &lua,int &p[],const int n,double &b[],int &info,CDenseSolverReport &rep,double &x[]);
|
|
static void RMatrixLUSolveM(CMatrixDouble &lua,int &p[],const int n,CMatrixDouble &b,const int m,int &info,CDenseSolverReport &rep,CMatrixDouble &x);
|
|
static void RMatrixMixedSolve(CMatrixDouble &a,CMatrixDouble &lua,int &p[],const int n,double &b[],int &info,CDenseSolverReport &rep,double &x[]);
|
|
static void RMatrixMixedSolveM(CMatrixDouble &a,CMatrixDouble &lua,int &p[],const int n,CMatrixDouble &b,const int m,int &info,CDenseSolverReport &rep,CMatrixDouble &x);
|
|
static void CMatrixSolveM(CMatrixComplex &a,const int n,CMatrixComplex &b,const int m,const bool rfs,int &info,CDenseSolverReport &rep,CMatrixComplex &x);
|
|
static void CMatrixSolve(CMatrixComplex &a,const int n,complex &b[],int &info,CDenseSolverReport &rep,complex &x[]);
|
|
static void CMatrixLUSolveM(CMatrixComplex &lua,int &p[],const int n,CMatrixComplex &b,const int m,int &info,CDenseSolverReport &rep,CMatrixComplex &x);
|
|
static void CMatrixLUSolve(CMatrixComplex &lua,int &p[],const int n,complex &b[],int &info,CDenseSolverReport &rep,complex &x[]);
|
|
static void CMatrixMixedSolveM(CMatrixComplex &a,CMatrixComplex &lua,int &p[],const int n,CMatrixComplex &b,const int m,int &info,CDenseSolverReport &rep,CMatrixComplex &x);
|
|
static void CMatrixMixedSolve(CMatrixComplex &a,CMatrixComplex &lua,int &p[],const int n,complex &b[],int &info,CDenseSolverReport &rep,complex &x[]);
|
|
static void SPDMatrixSolveM(CMatrixDouble &a,const int n,const bool IsUpper,CMatrixDouble &b,const int m,int &info,CDenseSolverReport &rep,CMatrixDouble &x);
|
|
static void SPDMatrixSolve(CMatrixDouble &a,const int n,const bool IsUpper,double &b[],int &info,CDenseSolverReport &rep,double &x[]);
|
|
static void SPDMatrixCholeskySolveM(CMatrixDouble &cha,const int n,const bool IsUpper,CMatrixDouble &b,const int m,int &info,CDenseSolverReport &rep,CMatrixDouble &x);
|
|
static void SPDMatrixCholeskySolve(CMatrixDouble &cha,const int n,const bool IsUpper,double &b[],int &info,CDenseSolverReport &rep,double &x[]);
|
|
static void SPDMatrixCholeskySolve(CMatrixDouble &cha,const int n,const bool IsUpper,CRowDouble &b,int &info,CDenseSolverReport &rep,CRowDouble &x);
|
|
static void HPDMatrixSolveM(CMatrixComplex &a,const int n,const bool IsUpper,CMatrixComplex &b,const int m,int &info,CDenseSolverReport &rep,CMatrixComplex &x);
|
|
static void HPDMatrixSolve(CMatrixComplex &a,const int n,const bool IsUpper,complex &b[],int &info,CDenseSolverReport &rep,complex &x[]);
|
|
static void HPDMatrixCholeskySolveM(CMatrixComplex &cha,const int n,const bool IsUpper,CMatrixComplex &b,const int m,int &info,CDenseSolverReport &rep,CMatrixComplex &x);
|
|
static void HPDMatrixCholeskySolve(CMatrixComplex &cha,const int n,const bool IsUpper,complex &b[],int &info,CDenseSolverReport &rep,complex &x[]);
|
|
static void RMatrixSolveLS(CMatrixDouble &a,const int nrows,const int ncols,double &b[],double threshold,int &info,CDenseSolverLSReport &rep,double &x[]);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. |
|
|
//| This subroutine solves a system A*x=b, where A is NxN |
|
|
//| non-denegerate real matrix, x and b are vectors. |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * condition number estimation |
|
|
//| * iterative refinement |
|
|
//| * O(N^3) complexity |
|
|
//| INPUT PARAMETERS |
|
|
//| A - array[0..N-1,0..N-1], system matrix |
|
|
//| N - size of A |
|
|
//| B - array[0..N-1], right part |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - return code: |
|
|
//| * -3 A is singular, or VERY close to singular.|
|
|
//| X is filled by zeros in such cases. |
|
|
//| * -1 N<=0 was passed |
|
|
//| * 1 task is solved (but matrix A may be |
|
|
//| ill-conditioned, check R1/RInf parameters|
|
|
//| for condition numbers). |
|
|
//| Rep - solver report, see below for more info |
|
|
//| X - array[0..N-1], it contains: |
|
|
//| * solution of A*x=b if A is non-singular |
|
|
//| (well-conditioned or ill-conditioned, but not |
|
|
//| very close to singular) |
|
|
//| * zeros, if A is singular or VERY close to |
|
|
//| singular (in this case Info=-3). |
|
|
//| SOLVER REPORT |
|
|
//| Subroutine sets following fields of the Rep structure: |
|
|
//| * R1 reciprocal of condition number: 1/cond(A), 1-norm. |
|
|
//| * RInf reciprocal of condition number: 1/cond(A), inf-norm. |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::RMatrixSolve(CMatrixDouble &a,const int n,double &b[],
|
|
int &info,CDenseSolverReport &rep,
|
|
double &x[])
|
|
{
|
|
//--- create matrix
|
|
CMatrixDouble bm;
|
|
CMatrixDouble xm;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(n<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
bm.Resize(n,1);
|
|
//--- filling
|
|
for(int i_=0; i_<n; i_++)
|
|
bm.Set(i_,0,b[i_]);
|
|
//--- function call
|
|
RMatrixSolveM(a,n,bm,1,true,info,rep,xm);
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
//--- copy
|
|
for(int i_=0; i_<n; i_++)
|
|
x[i_]=xm[i_][0];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::RMatrixSolve(CMatrixDouble &a,const int n,CRowDouble &b,
|
|
int &info,CDenseSolverReport &rep,
|
|
CRowDouble &x)
|
|
{
|
|
//--- create matrix
|
|
CMatrixDouble bm;
|
|
CMatrixDouble xm;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(n<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
bm.Resize(n,1);
|
|
//--- filling
|
|
bm.Col(0,b);
|
|
//--- function call
|
|
RMatrixSolveM(a,n,bm,1,true,info,rep,xm);
|
|
//--- copy
|
|
x=xm.Col(0)+0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. |
|
|
//| Similar to RMatrixSolve() but solves task with multiple right |
|
|
//| parts (where b and x are NxM matrices). |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * condition number estimation |
|
|
//| * optional iterative refinement |
|
|
//| * O(N^3+M*N^2) complexity |
|
|
//| INPUT PARAMETERS |
|
|
//| A - array[0..N-1,0..N-1], system matrix |
|
|
//| N - size of A |
|
|
//| B - array[0..N-1,0..M-1], right part |
|
|
//| M - right part size |
|
|
//| RFS - iterative refinement switch: |
|
|
//| * True - refinement is used. |
|
|
//| Less performance, more precision. |
|
|
//| * False - refinement is not used. |
|
|
//| More performance, less precision. |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolve |
|
|
//| Rep - same as in RMatrixSolve |
|
|
//| X - same as in RMatrixSolve |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::RMatrixSolveM(CMatrixDouble &a,const int n,
|
|
CMatrixDouble &b,const int m,
|
|
const bool rfs,int &info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixDouble &x)
|
|
{
|
|
double scalea=0;
|
|
//--- create matrix
|
|
CMatrixDouble da;
|
|
CMatrixDouble emptya;
|
|
//--- create array
|
|
int p[];
|
|
//--- initialization
|
|
info=0;
|
|
//--- prepare: check inputs,allocate space...
|
|
if(n<=0 || m<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
da.Resize(n,n);
|
|
//--- 1. scale matrix,max(|A[i][j]|)
|
|
//--- 2. factorize scaled matrix
|
|
//--- 3. solve
|
|
scalea=0;
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
scalea=MathMax(scalea,MathAbs(a[i][j]));
|
|
}
|
|
//--- check
|
|
if(scalea==0.0)
|
|
scalea=1;
|
|
//--- change values
|
|
scalea=1/scalea;
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int i_=0; i_<n; i_++)
|
|
da.Set(i,i_,a[i][i_]);
|
|
}
|
|
//--- function call
|
|
CTrFac::RMatrixLU(da,n,n,p);
|
|
//--- check
|
|
if(rfs)
|
|
RMatrixLUSolveInternal(da,p,scalea,n,a,true,b,m,info,rep,x);
|
|
else
|
|
RMatrixLUSolveInternal(da,p,scalea,n,emptya,false,b,m,info,rep,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. |
|
|
//| This subroutine solves a system A*X=B, where A is NxN |
|
|
//| non-denegerate real matrix given by its LU decomposition, X and |
|
|
//| B are NxM real matrices. |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * O(N^2) complexity |
|
|
//| * condition number estimation |
|
|
//| No iterative refinement is provided because exact form of |
|
|
//| original matrix is not known to subroutine. Use RMatrixSolve or |
|
|
//| RMatrixMixedSolve if you need iterative refinement. |
|
|
//| INPUT PARAMETERS |
|
|
//| LUA - array[0..N-1,0..N-1], LU decomposition, RMatrixLU|
|
|
//| result |
|
|
//| P - array[0..N-1], pivots array, RMatrixLU result |
|
|
//| N - size of A |
|
|
//| B - array[0..N-1], right part |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolve |
|
|
//| Rep - same as in RMatrixSolve |
|
|
//| X - same as in RMatrixSolve |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::RMatrixLUSolve(CMatrixDouble &lua,int &p[],
|
|
const int n,double &b[],
|
|
int &info,CDenseSolverReport &rep,
|
|
double &x[])
|
|
{
|
|
//--- create matrix
|
|
CMatrixDouble bm;
|
|
CMatrixDouble xm;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(n<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
bm.Resize(n,1);
|
|
//--- filling
|
|
for(int i_=0; i_<n; i_++)
|
|
bm.Set(i_,0,b[i_]);
|
|
//--- function call
|
|
RMatrixLUSolveM(lua,p,n,bm,1,info,rep,xm);
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
//--- copy
|
|
for(int i_=0; i_<n; i_++)
|
|
x[i_]=xm[i_][0];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. |
|
|
//| Similar to RMatrixLUSolve() but solves task with multiple right |
|
|
//| parts (where b and x are NxM matrices). |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * O(M*N^2) complexity |
|
|
//| * condition number estimation |
|
|
//| No iterative refinement is provided because exact form of |
|
|
//| original matrix is not known to subroutine. Use RMatrixSolve or |
|
|
//| RMatrixMixedSolve if you need iterative refinement. |
|
|
//| INPUT PARAMETERS |
|
|
//| LUA - array[0..N-1,0..N-1], LU decomposition, RMatrixLU|
|
|
//| result |
|
|
//| P - array[0..N-1], pivots array, RMatrixLU result |
|
|
//| N - size of A |
|
|
//| B - array[0..N-1,0..M-1], right part |
|
|
//| M - right part size |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolve |
|
|
//| Rep - same as in RMatrixSolve |
|
|
//| X - same as in RMatrixSolve |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::RMatrixLUSolveM(CMatrixDouble &lua,int &p[],
|
|
const int n,CMatrixDouble &b,
|
|
const int m,int &info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixDouble &x)
|
|
{
|
|
double scalea=0;
|
|
//--- create matrix
|
|
CMatrixDouble emptya;
|
|
//--- initialization
|
|
info=0;
|
|
//--- prepare: check inputs,allocate space...
|
|
if(n<=0 || m<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- 1. scale matrix,max(|U[i][j]|)
|
|
//--- we assume that LU is in its normal form,i.e. |L[i][j]|<=1
|
|
//--- 2. solve
|
|
scalea=0;
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=i; j<n; j++)
|
|
scalea=MathMax(scalea,MathAbs(lua[i][j]));
|
|
}
|
|
//--- check
|
|
if(scalea==0.0)
|
|
scalea=1;
|
|
//--- change values
|
|
scalea=1/scalea;
|
|
//--- function call
|
|
RMatrixLUSolveInternal(lua,p,scalea,n,emptya,false,b,m,info,rep,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. |
|
|
//| This subroutine solves a system A*x=b, where BOTH ORIGINAL A AND |
|
|
//| ITS LU DECOMPOSITION ARE KNOWN. You can use it if for some |
|
|
//| reasons you have both A and its LU decomposition. |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * condition number estimation |
|
|
//| * iterative refinement |
|
|
//| * O(N^2) complexity |
|
|
//| INPUT PARAMETERS |
|
|
//| A - array[0..N-1,0..N-1], system matrix |
|
|
//| LUA - array[0..N-1,0..N-1], LU decomposition, RMatrixLU|
|
|
//| result |
|
|
//| P - array[0..N-1], pivots array, RMatrixLU result |
|
|
//| N - size of A |
|
|
//| B - array[0..N-1], right part |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolveM |
|
|
//| Rep - same as in RMatrixSolveM |
|
|
//| X - same as in RMatrixSolveM |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::RMatrixMixedSolve(CMatrixDouble &a,CMatrixDouble &lua,
|
|
int &p[],const int n,double &b[],
|
|
int &info,CDenseSolverReport &rep,
|
|
double &x[])
|
|
{
|
|
//--- create matrix
|
|
CMatrixDouble bm;
|
|
CMatrixDouble xm;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(n<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
bm.Resize(n,1);
|
|
//--- filling
|
|
for(int i_=0; i_<n; i_++)
|
|
bm.Set(i_,0,b[i_]);
|
|
//--- function call
|
|
RMatrixMixedSolveM(a,lua,p,n,bm,1,info,rep,xm);
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
//--- copy
|
|
for(int i_=0; i_<n; i_++)
|
|
x[i_]=xm[i_][0];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. |
|
|
//| Similar to RMatrixMixedSolve() but solves task with multiple |
|
|
//| right parts (where b and x are NxM matrices). |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * condition number estimation |
|
|
//| * iterative refinement |
|
|
//| * O(M*N^2) complexity |
|
|
//| INPUT PARAMETERS |
|
|
//| A - array[0..N-1,0..N-1], system matrix |
|
|
//| LUA - array[0..N-1,0..N-1], LU decomposition, RMatrixLU|
|
|
//| result |
|
|
//| P - array[0..N-1], pivots array, RMatrixLU result |
|
|
//| N - size of A |
|
|
//| B - array[0..N-1,0..M-1], right part |
|
|
//| M - right part size |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolveM |
|
|
//| Rep - same as in RMatrixSolveM |
|
|
//| X - same as in RMatrixSolveM |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::RMatrixMixedSolveM(CMatrixDouble &a,CMatrixDouble &lua,
|
|
int &p[],const int n,CMatrixDouble &b,
|
|
const int m,int &info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixDouble &x)
|
|
{
|
|
double scalea=0;
|
|
//--- initialization
|
|
info=0;
|
|
//--- prepare: check inputs,allocate space...
|
|
if(n<=0 || m<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- 1. scale matrix,max(|A[i][j]|)
|
|
//--- 2. factorize scaled matrix
|
|
//--- 3. solve
|
|
scalea=0;
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
scalea=MathMax(scalea,MathAbs(a[i][j]));
|
|
}
|
|
//--- check
|
|
if(scalea==0.0)
|
|
scalea=1;
|
|
//--- change values
|
|
scalea=1/scalea;
|
|
//--- function call
|
|
RMatrixLUSolveInternal(lua,p,scalea,n,a,true,b,m,info,rep,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. Same as RMatrixSolveM(), but for complex matrices. |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * condition number estimation |
|
|
//| * iterative refinement |
|
|
//| * O(N^3+M*N^2) complexity |
|
|
//| INPUT PARAMETERS |
|
|
//| A - array[0..N-1,0..N-1], system matrix |
|
|
//| N - size of A |
|
|
//| B - array[0..N-1,0..M-1], right part |
|
|
//| M - right part size |
|
|
//| RFS - iterative refinement switch: |
|
|
//| * True - refinement is used. |
|
|
//| Less performance, more precision. |
|
|
//| * False - refinement is not used. |
|
|
//| More performance, less precision. |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolve |
|
|
//| Rep - same as in RMatrixSolve |
|
|
//| X - same as in RMatrixSolve |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::CMatrixSolveM(CMatrixComplex &a,const int n,
|
|
CMatrixComplex &b,const int m,
|
|
const bool rfs,int &info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixComplex &x)
|
|
{
|
|
double scalea=0;
|
|
//--- create array
|
|
int p[];
|
|
//--- create matrix
|
|
CMatrixComplex da;
|
|
CMatrixComplex emptya;
|
|
//--- initialization
|
|
info=0;
|
|
//--- prepare: check inputs,allocate space...
|
|
if(n<=0 || m<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
da.Resize(n,n);
|
|
//--- 1. scale matrix,max(|A[i][j]|)
|
|
//--- 2. factorize scaled matrix
|
|
//--- 3. solve
|
|
scalea=0;
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
scalea=MathMax(scalea,CMath::AbsComplex(a[i][j]));
|
|
}
|
|
//--- check
|
|
if(scalea==0.0)
|
|
scalea=1;
|
|
//--- change values
|
|
scalea=1/scalea;
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int i_=0; i_<n; i_++)
|
|
da.Set(i,i_,a[i][i_]);
|
|
}
|
|
//--- function call
|
|
CTrFac::CMatrixLU(da,n,n,p);
|
|
//--- check
|
|
if(rfs)
|
|
CMatrixLUSolveInternal(da,p,scalea,n,a,true,b,m,info,rep,x);
|
|
else
|
|
CMatrixLUSolveInternal(da,p,scalea,n,emptya,false,b,m,info,rep,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. Same as RMatrixSolve(), but for complex matrices. |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * condition number estimation |
|
|
//| * iterative refinement |
|
|
//| * O(N^3) complexity |
|
|
//| INPUT PARAMETERS |
|
|
//| A - array[0..N-1,0..N-1], system matrix |
|
|
//| N - size of A |
|
|
//| B - array[0..N-1], right part |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolve |
|
|
//| Rep - same as in RMatrixSolve |
|
|
//| X - same as in RMatrixSolve |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::CMatrixSolve(CMatrixComplex &a,const int n,
|
|
complex &b[],int &info,
|
|
CDenseSolverReport &rep,complex &x[])
|
|
{
|
|
//--- create matrix
|
|
CMatrixComplex bm;
|
|
CMatrixComplex xm;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(n<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
bm.Resize(n,1);
|
|
//--- filling
|
|
for(int i_=0; i_<n; i_++)
|
|
bm.Set(i_,0,b[i_]);
|
|
//--- function call
|
|
CMatrixSolveM(a,n,bm,1,true,info,rep,xm);
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
//--- copy
|
|
for(int i_=0; i_<n; i_++)
|
|
x[i_]=xm[i_][0];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. Same as RMatrixLUSolveM(), but for complex |
|
|
//| matrices. |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * O(M*N^2) complexity |
|
|
//| * condition number estimation |
|
|
//| No iterative refinement is provided because exact form of |
|
|
//| original matrix is not known to subroutine. Use CMatrixSolve or |
|
|
//| CMatrixMixedSolve if you need iterative refinement. |
|
|
//| INPUT PARAMETERS |
|
|
//| LUA - array[0..N-1,0..N-1], LU decomposition, RMatrixLU|
|
|
//| result |
|
|
//| P - array[0..N-1], pivots array, RMatrixLU result |
|
|
//| N - size of A |
|
|
//| B - array[0..N-1,0..M-1], right part |
|
|
//| M - right part size |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolve |
|
|
//| Rep - same as in RMatrixSolve |
|
|
//| X - same as in RMatrixSolve |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::CMatrixLUSolveM(CMatrixComplex &lua,int &p[],
|
|
const int n,CMatrixComplex &b,
|
|
const int m,int &info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixComplex &x)
|
|
{
|
|
double scalea=0;
|
|
//--- create matrix
|
|
CMatrixComplex emptya;
|
|
//--- initialization
|
|
info=0;
|
|
//--- prepare: check inputs,allocate space...
|
|
if(n<=0 || m<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- 1. scale matrix,max(|U[i][j]|)
|
|
//--- we assume that LU is in its normal form,i.e. |L[i][j]|<=1
|
|
//--- 2. solve
|
|
scalea=0;
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=i; j<n; j++)
|
|
scalea=MathMax(scalea,CMath::AbsComplex(lua[i][j]));
|
|
}
|
|
//--- check
|
|
if(scalea==0.0)
|
|
scalea=1;
|
|
//--- change values
|
|
scalea=1/scalea;
|
|
//--- function call
|
|
CMatrixLUSolveInternal(lua,p,scalea,n,emptya,false,b,m,info,rep,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. Same as RMatrixLUSolve(), but for complex matrices.|
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * O(N^2) complexity |
|
|
//| * condition number estimation |
|
|
//| No iterative refinement is provided because exact form of |
|
|
//| original matrix is not known to subroutine. Use CMatrixSolve or |
|
|
//| CMatrixMixedSolve if you need iterative refinement. |
|
|
//| INPUT PARAMETERS |
|
|
//| LUA - array[0..N-1,0..N-1], LU decomposition, CMatrixLU|
|
|
//| result |
|
|
//| P - array[0..N-1], pivots array, CMatrixLU result |
|
|
//| N - size of A |
|
|
//| B - array[0..N-1], right part |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolve |
|
|
//| Rep - same as in RMatrixSolve |
|
|
//| X - same as in RMatrixSolve |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::CMatrixLUSolve(CMatrixComplex &lua,int &p[],
|
|
const int n,complex &b[],int &info,
|
|
CDenseSolverReport &rep,complex &x[])
|
|
{
|
|
//--- create matrix
|
|
CMatrixComplex bm;
|
|
CMatrixComplex xm;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(n<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
bm.Resize(n,1);
|
|
//--- filling
|
|
for(int i_=0; i_<n; i_++)
|
|
bm.Set(i_,0,b[i_]);
|
|
//--- function call
|
|
CMatrixLUSolveM(lua,p,n,bm,1,info,rep,xm);
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
//--- copy
|
|
for(int i_=0; i_<n; i_++)
|
|
x[i_]=xm[i_][0];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. Same as RMatrixMixedSolveM(), but for complex |
|
|
//| matrices. |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * condition number estimation |
|
|
//| * iterative refinement |
|
|
//| * O(M*N^2) complexity |
|
|
//| INPUT PARAMETERS |
|
|
//| A - array[0..N-1,0..N-1], system matrix |
|
|
//| LUA - array[0..N-1,0..N-1], LU decomposition, CMatrixLU|
|
|
//| result |
|
|
//| P - array[0..N-1], pivots array, CMatrixLU result |
|
|
//| N - size of A |
|
|
//| B - array[0..N-1,0..M-1], right part |
|
|
//| M - right part size |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolveM |
|
|
//| Rep - same as in RMatrixSolveM |
|
|
//| X - same as in RMatrixSolveM |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::CMatrixMixedSolveM(CMatrixComplex &a,CMatrixComplex &lua,
|
|
int &p[],const int n,CMatrixComplex &b,
|
|
const int m,int &info,CDenseSolverReport &rep,
|
|
CMatrixComplex &x)
|
|
{
|
|
double scalea=0;
|
|
//--- initialization
|
|
info=0;
|
|
//--- prepare: check inputs,allocate space...
|
|
if(n<=0 || m<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- 1. scale matrix,max(|A[i][j]|)
|
|
//--- 2. factorize scaled matrix
|
|
//--- 3. solve
|
|
scalea=0;
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
for(int j=0; j<n; j++)
|
|
scalea=MathMax(scalea,CMath::AbsComplex(a[i][j]));
|
|
}
|
|
//--- check
|
|
if(scalea==0.0)
|
|
scalea=1;
|
|
//--- change values
|
|
scalea=1/scalea;
|
|
//--- function call
|
|
CMatrixLUSolveInternal(lua,p,scalea,n,a,true,b,m,info,rep,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. Same as RMatrixMixedSolve(), but for complex |
|
|
//| matrices. |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * condition number estimation |
|
|
//| * iterative refinement |
|
|
//| * O(N^2) complexity |
|
|
//| INPUT PARAMETERS |
|
|
//| A - array[0..N-1,0..N-1], system matrix |
|
|
//| LUA - array[0..N-1,0..N-1], LU decomposition, CMatrixLU|
|
|
//| result |
|
|
//| P - array[0..N-1], pivots array, CMatrixLU result |
|
|
//| N - size of A |
|
|
//| B - array[0..N-1], right part |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolveM |
|
|
//| Rep - same as in RMatrixSolveM |
|
|
//| X - same as in RMatrixSolveM |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::CMatrixMixedSolve(CMatrixComplex &a,CMatrixComplex &lua,
|
|
int &p[],const int n,complex &b[],
|
|
int &info,CDenseSolverReport &rep,
|
|
complex &x[])
|
|
{
|
|
//--- create matrix
|
|
CMatrixComplex bm;
|
|
CMatrixComplex xm;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(n<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
bm.Resize(n,1);
|
|
//--- filling
|
|
for(int i_=0; i_<n; i_++)
|
|
bm.Set(i_,0,b[i_]);
|
|
//--- function call
|
|
CMatrixMixedSolveM(a,lua,p,n,bm,1,info,rep,xm);
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
//--- copy
|
|
for(int i_=0; i_<n; i_++)
|
|
x[i_]=xm[i_][0];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. Same as RMatrixSolveM(), but for symmetric positive|
|
|
//| definite matrices. |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * condition number estimation |
|
|
//| * O(N^3+M*N^2) complexity |
|
|
//| * matrix is represented by its upper or lower triangle |
|
|
//| No iterative refinement is provided because such partial |
|
|
//| representation of matrix does not allow efficient calculation of |
|
|
//| extra-precise matrix-vector products for large matrices. Use |
|
|
//| RMatrixSolve or RMatrixMixedSolve if you need iterative |
|
|
//| refinement. |
|
|
//| INPUT PARAMETERS |
|
|
//| A - array[0..N-1,0..N-1], system matrix |
|
|
//| N - size of A |
|
|
//| IsUpper - what half of A is provided |
|
|
//| B - array[0..N-1,0..M-1], right part |
|
|
//| M - right part size |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolve. |
|
|
//| Returns -3 for non-SPD matrices. |
|
|
//| Rep - same as in RMatrixSolve |
|
|
//| X - same as in RMatrixSolve |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::SPDMatrixSolveM(CMatrixDouble &a,const int n,
|
|
const bool IsUpper,CMatrixDouble &b,
|
|
const int m,int &info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixDouble &x)
|
|
{
|
|
//--- create variables
|
|
double sqrtscalea=0;
|
|
int i=0;
|
|
int j=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble da;
|
|
//--- initialization
|
|
info=0;
|
|
//--- prepare: check inputs,allocate space...
|
|
if(n<=0 || m<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
da.Resize(n,n);
|
|
//--- 1. scale matrix,max(|A[i][j]|)
|
|
//--- 2. factorize scaled matrix
|
|
//--- 3. solve
|
|
sqrtscalea=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(IsUpper)
|
|
{
|
|
j1=i;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i;
|
|
}
|
|
//--- calculation
|
|
for(j=j1; j<=j2; j++)
|
|
sqrtscalea=MathMax(sqrtscalea,MathAbs(a[i][j]));
|
|
}
|
|
//--- check
|
|
if(sqrtscalea==0.0)
|
|
sqrtscalea=1;
|
|
//--- change values
|
|
sqrtscalea=1/sqrtscalea;
|
|
sqrtscalea=MathSqrt(sqrtscalea);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(IsUpper)
|
|
{
|
|
j1=i;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i;
|
|
}
|
|
//--- calculation
|
|
for(i_=j1; i_<=j2; i_++)
|
|
da.Set(i,i_,a[i][i_]);
|
|
}
|
|
//--- check
|
|
if(!CTrFac::SPDMatrixCholesky(da,n,IsUpper))
|
|
{
|
|
//--- allocation
|
|
x.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=m-1; j++)
|
|
x.Set(i,j,0);
|
|
}
|
|
//--- change values
|
|
rep.m_r1=0;
|
|
rep.m_rinf=0;
|
|
info=-3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
info=1;
|
|
//--- function call
|
|
SPDMatrixCholeskySolveInternal(da,sqrtscalea,n,IsUpper,a,true,b,m,info,rep,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. Same as RMatrixSolve(), but for SPD matrices. |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * condition number estimation |
|
|
//| * O(N^3) complexity |
|
|
//| * matrix is represented by its upper or lower triangle |
|
|
//| No iterative refinement is provided because such partial |
|
|
//| representation of matrix does not allow efficient calculation of |
|
|
//| extra-precise matrix-vector products for large matrices. Use |
|
|
//| RMatrixSolve or RMatrixMixedSolve if you need iterative |
|
|
//| refinement. |
|
|
//| INPUT PARAMETERS |
|
|
//| A - array[0..N-1,0..N-1], system matrix |
|
|
//| N - size of A |
|
|
//| IsUpper - what half of A is provided |
|
|
//| B - array[0..N-1], right part |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolve |
|
|
//| Returns -3 for non-SPD matrices. |
|
|
//| Rep - same as in RMatrixSolve |
|
|
//| X - same as in RMatrixSolve |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::SPDMatrixSolve(CMatrixDouble &a,const int n,
|
|
const bool IsUpper,double &b[],
|
|
int &info,CDenseSolverReport &rep,
|
|
double &x[])
|
|
{
|
|
//--- create matrix
|
|
CMatrixDouble bm;
|
|
CMatrixDouble xm;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(n<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
bm.Resize(n,1);
|
|
//--- filling
|
|
for(int i_=0; i_<n; i_++)
|
|
bm.Set(i_,0,b[i_]);
|
|
//--- function call
|
|
SPDMatrixSolveM(a,n,IsUpper,bm,1,info,rep,xm);
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
//--- copy
|
|
for(int i_=0; i_<n; i_++)
|
|
x[i_]=xm[i_][0];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. Same as RMatrixLUSolveM(), but for SPD matrices |
|
|
//| represented by their Cholesky decomposition. |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * O(M*N^2) complexity |
|
|
//| * condition number estimation |
|
|
//| * matrix is represented by its upper or lower triangle |
|
|
//| No iterative refinement is provided because such partial |
|
|
//| representation of matrix does not allow efficient calculation of |
|
|
//| extra-precise matrix-vector products for large matrices. Use |
|
|
//| RMatrixSolve or RMatrixMixedSolve if you need iterative |
|
|
//| refinement. |
|
|
//| INPUT PARAMETERS |
|
|
//| CHA - array[0..N-1,0..N-1], Cholesky decomposition, |
|
|
//| SPDMatrixCholesky result |
|
|
//| N - size of CHA |
|
|
//| IsUpper - what half of CHA is provided |
|
|
//| B - array[0..N-1,0..M-1], right part |
|
|
//| M - right part size |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolve |
|
|
//| Rep - same as in RMatrixSolve |
|
|
//| X - same as in RMatrixSolve |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::SPDMatrixCholeskySolveM(CMatrixDouble &cha,const int n,
|
|
const bool IsUpper,CMatrixDouble &b,
|
|
const int m,int &info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixDouble &x)
|
|
{
|
|
//--- create variables
|
|
double sqrtscalea=0;
|
|
int i=0;
|
|
int j=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
//--- create matrix
|
|
CMatrixDouble emptya;
|
|
//--- initialization
|
|
info=0;
|
|
//--- prepare: check inputs,allocate space...
|
|
if(n<=0 || m<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- 1. scale matrix,max(|U[i][j]|)
|
|
//--- 2. factorize scaled matrix
|
|
//--- 3. solve
|
|
sqrtscalea=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(IsUpper)
|
|
{
|
|
j1=i;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i;
|
|
}
|
|
//--- calculation
|
|
for(j=j1; j<=j2; j++)
|
|
sqrtscalea=MathMax(sqrtscalea,MathAbs(cha[i][j]));
|
|
}
|
|
//--- check
|
|
if(sqrtscalea==0.0)
|
|
sqrtscalea=1;
|
|
//--- change values
|
|
sqrtscalea=1/sqrtscalea;
|
|
//--- function call
|
|
SPDMatrixCholeskySolveInternal(cha,sqrtscalea,n,IsUpper,emptya,false,b,m,info,rep,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. Same as RMatrixLUSolve(), but for SPD matrices |
|
|
//| represented by their Cholesky decomposition. |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * O(N^2) complexity |
|
|
//| * condition number estimation |
|
|
//| * matrix is represented by its upper or lower triangle |
|
|
//| No iterative refinement is provided because such partial |
|
|
//| representation of matrix does not allow efficient calculation of |
|
|
//| extra-precise matrix-vector products for large matrices. Use |
|
|
//| RMatrixSolve or RMatrixMixedSolve if you need iterative |
|
|
//| refinement. |
|
|
//| INPUT PARAMETERS |
|
|
//| CHA - array[0..N-1,0..N-1], Cholesky decomposition, |
|
|
//| SPDMatrixCholesky result |
|
|
//| N - size of A |
|
|
//| IsUpper - what half of CHA is provided |
|
|
//| B - array[0..N-1], right part |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolve |
|
|
//| Rep - same as in RMatrixSolve |
|
|
//| X - same as in RMatrixSolve |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::SPDMatrixCholeskySolve(CMatrixDouble &cha,const int n,
|
|
const bool IsUpper,double &b[],
|
|
int &info,CDenseSolverReport &rep,
|
|
double &x[])
|
|
{
|
|
CRowDouble X;
|
|
CRowDouble B=b;
|
|
SPDMatrixCholeskySolve(cha,n,IsUpper,B,info,rep,X);
|
|
X.ToArray(x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::SPDMatrixCholeskySolve(CMatrixDouble &cha,const int n,
|
|
const bool IsUpper,CRowDouble &b,
|
|
int &info,CDenseSolverReport &rep,
|
|
CRowDouble &x)
|
|
{
|
|
//--- create matrix
|
|
CMatrixDouble bm;
|
|
CMatrixDouble xm;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(n<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
bm.Resize(n,1);
|
|
//--- filling
|
|
bm.Col(0,b);
|
|
//--- function call
|
|
SPDMatrixCholeskySolveM(cha,n,IsUpper,bm,1,info,rep,xm);
|
|
//--- copy
|
|
x=xm.Col(0)+0;
|
|
x.Resize(n);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. Same as RMatrixSolveM(), but for Hermitian positive|
|
|
//| definite matrices. |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * condition number estimation |
|
|
//| * O(N^3+M*N^2) complexity |
|
|
//| * matrix is represented by its upper or lower triangle |
|
|
//| No iterative refinement is provided because such partial |
|
|
//| representation of matrix does not allow efficient calculation of |
|
|
//| extra-precise matrix-vector products for large matrices. Use |
|
|
//| RMatrixSolve or RMatrixMixedSolve if you need iterative |
|
|
//| refinement. |
|
|
//| INPUT PARAMETERS |
|
|
//| A - array[0..N-1,0..N-1], system matrix |
|
|
//| N - size of A |
|
|
//| IsUpper - what half of A is provided |
|
|
//| B - array[0..N-1,0..M-1], right part |
|
|
//| M - right part size |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolve. |
|
|
//| Returns -3 for non-HPD matrices. |
|
|
//| Rep - same as in RMatrixSolve |
|
|
//| X - same as in RMatrixSolve |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::HPDMatrixSolveM(CMatrixComplex &a,const int n,
|
|
const bool IsUpper,CMatrixComplex &b,
|
|
const int m,int &info,
|
|
CDenseSolverReport &rep,CMatrixComplex &x)
|
|
{
|
|
//--- create variables
|
|
double sqrtscalea=0;
|
|
int i=0;
|
|
int j=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixComplex da;
|
|
//--- initialization
|
|
info=0;
|
|
//--- prepare: check inputs,allocate space...
|
|
if(n<=0 || m<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
da.Resize(n,n);
|
|
//--- 1. scale matrix,max(|A[i][j]|)
|
|
//--- 2. factorize scaled matrix
|
|
//--- 3. solve
|
|
sqrtscalea=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(IsUpper)
|
|
{
|
|
j1=i;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i;
|
|
}
|
|
//--- calculation
|
|
for(j=j1; j<=j2; j++)
|
|
sqrtscalea=MathMax(sqrtscalea,CMath::AbsComplex(a[i][j]));
|
|
}
|
|
//--- check
|
|
if(sqrtscalea==0.0)
|
|
sqrtscalea=1;
|
|
//--- change values
|
|
sqrtscalea=1/sqrtscalea;
|
|
sqrtscalea=MathSqrt(sqrtscalea);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(IsUpper)
|
|
{
|
|
j1=i;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i;
|
|
}
|
|
//--- calculation
|
|
for(i_=j1; i_<=j2; i_++)
|
|
da.Set(i,i_,a[i][i_]);
|
|
}
|
|
//--- check
|
|
if(!CTrFac::HPDMatrixCholesky(da,n,IsUpper))
|
|
{
|
|
//--- allocation
|
|
x.Resize(n,m);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=m-1; j++)
|
|
x.Set(i,j,0.0);
|
|
}
|
|
//--- change values
|
|
rep.m_r1=0;
|
|
rep.m_rinf=0;
|
|
info=-3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
info=1;
|
|
//--- function call
|
|
HPDMatrixCholeskySolveInternal(da,sqrtscalea,n,IsUpper,a,true,b,m,info,rep,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. Same as RMatrixSolve(), but for Hermitian positive |
|
|
//| definite matrices. |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * condition number estimation |
|
|
//| * O(N^3) complexity |
|
|
//| * matrix is represented by its upper or lower triangle |
|
|
//| No iterative refinement is provided because such partial |
|
|
//| representation of matrix does not allow efficient calculation of |
|
|
//| extra-precise matrix-vector products for large matrices. Use |
|
|
//| RMatrixSolve or RMatrixMixedSolve if you need iterative |
|
|
//| refinement. |
|
|
//| INPUT PARAMETERS |
|
|
//| A - array[0..N-1,0..N-1], system matrix |
|
|
//| N - size of A |
|
|
//| IsUpper - what half of A is provided |
|
|
//| B - array[0..N-1], right part |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolve |
|
|
//| Returns -3 for non-HPD matrices. |
|
|
//| Rep - same as in RMatrixSolve |
|
|
//| X - same as in RMatrixSolve |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::HPDMatrixSolve(CMatrixComplex &a,const int n,
|
|
const bool IsUpper,complex &b[],
|
|
int &info,CDenseSolverReport &rep,
|
|
complex &x[])
|
|
{
|
|
//--- create matrix
|
|
CMatrixComplex bm;
|
|
CMatrixComplex xm;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(n<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
bm.Resize(n,1);
|
|
//--- filling
|
|
for(int i_=0; i_<n; i_++)
|
|
bm.Set(i_,0,b[i_]);
|
|
//--- function call
|
|
HPDMatrixSolveM(a,n,IsUpper,bm,1,info,rep,xm);
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
//--- copy
|
|
for(int i_=0; i_<n; i_++)
|
|
x[i_]=xm[i_][0];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. Same as RMatrixLUSolveM(), but for HPD matrices |
|
|
//| represented by their Cholesky decomposition. |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * O(M*N^2) complexity |
|
|
//| * condition number estimation |
|
|
//| * matrix is represented by its upper or lower triangle |
|
|
//| No iterative refinement is provided because such partial |
|
|
//| representation of matrix does not allow efficient calculation of |
|
|
//| extra-precise matrix-vector products for large matrices. Use |
|
|
//| RMatrixSolve or RMatrixMixedSolve if you need iterative |
|
|
//| refinement. |
|
|
//| INPUT PARAMETERS |
|
|
//| CHA - array[0..N-1,0..N-1], Cholesky decomposition, |
|
|
//| HPDMatrixCholesky result |
|
|
//| N - size of CHA |
|
|
//| IsUpper - what half of CHA is provided |
|
|
//| B - array[0..N-1,0..M-1], right part |
|
|
//| M - right part size |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolve |
|
|
//| Rep - same as in RMatrixSolve |
|
|
//| X - same as in RMatrixSolve |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::HPDMatrixCholeskySolveM(CMatrixComplex &cha,
|
|
const int n,const bool IsUpper,
|
|
CMatrixComplex &b,const int m,
|
|
int &info,CDenseSolverReport &rep,
|
|
CMatrixComplex &x)
|
|
{
|
|
//--- create variables
|
|
double sqrtscalea=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
//--- create matrix
|
|
CMatrixComplex emptya;
|
|
//--- initialization
|
|
info=0;
|
|
//--- prepare: check inputs,allocate space...
|
|
if(n<=0 || m<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- 1. scale matrix,max(|U[i][j]|)
|
|
//--- 2. factorize scaled matrix
|
|
//--- 3. solve
|
|
sqrtscalea=0;
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(IsUpper)
|
|
{
|
|
j1=i;
|
|
j2=n-1;
|
|
}
|
|
else
|
|
{
|
|
j1=0;
|
|
j2=i;
|
|
}
|
|
//--- calculation
|
|
for(int j=j1; j<=j2; j++)
|
|
{
|
|
sqrtscalea=MathMax(sqrtscalea,CMath::AbsComplex(cha[i][j]));
|
|
}
|
|
}
|
|
//--- check
|
|
if(sqrtscalea==0.0)
|
|
{
|
|
sqrtscalea=1;
|
|
}
|
|
//--- change values
|
|
sqrtscalea=1/sqrtscalea;
|
|
//--- function call
|
|
HPDMatrixCholeskySolveInternal(cha,sqrtscalea,n,IsUpper,emptya,false,b,m,info,rep,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. Same as RMatrixLUSolve(), but for HPD matrices |
|
|
//| represented by their Cholesky decomposition. |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * O(N^2) complexity |
|
|
//| * condition number estimation |
|
|
//| * matrix is represented by its upper or lower triangle |
|
|
//| No iterative refinement is provided because such partial |
|
|
//| representation of matrix does not allow efficient calculation of |
|
|
//| extra-precise matrix-vector products for large matrices. Use |
|
|
//| RMatrixSolve or RMatrixMixedSolve if you need iterative |
|
|
//| refinement. |
|
|
//| INPUT PARAMETERS |
|
|
//| CHA - array[0..N-1,0..N-1], Cholesky decomposition, |
|
|
//| SPDMatrixCholesky result |
|
|
//| N - size of A |
|
|
//| IsUpper - what half of CHA is provided |
|
|
//| B - array[0..N-1], right part |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - same as in RMatrixSolve |
|
|
//| Rep - same as in RMatrixSolve |
|
|
//| X - same as in RMatrixSolve |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::HPDMatrixCholeskySolve(CMatrixComplex &cha,
|
|
const int n,const bool IsUpper,
|
|
complex &b[],int &info,
|
|
CDenseSolverReport &rep,
|
|
complex &x[])
|
|
{
|
|
//--- create matrix
|
|
CMatrixComplex bm;
|
|
CMatrixComplex xm;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(n<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
bm.Resize(n,1);
|
|
//--- filling
|
|
for(int i_=0; i_<n; i_++)
|
|
bm.Set(i_,0,b[i_]);
|
|
//--- function call
|
|
HPDMatrixCholeskySolveM(cha,n,IsUpper,bm,1,info,rep,xm);
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
//--- copy
|
|
for(int i_=0; i_<n; i_++)
|
|
x[i_]=xm[i_][0];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Dense solver. |
|
|
//| This subroutine finds solution of the linear system A*X=B with |
|
|
//| non-square, possibly degenerate A. System is solved in the least |
|
|
//| squares sense, and general least squares solution X = X0 + CX*y |
|
|
//| which minimizes |A*X-B| is returned. If A is non-degenerate, |
|
|
//| solution in the usual sense is returned |
|
|
//| Algorithm features: |
|
|
//| * automatic detection of degenerate cases |
|
|
//| * iterative refinement |
|
|
//| * O(N^3) complexity |
|
|
//| INPUT PARAMETERS |
|
|
//| A - array[0..NRows-1,0..NCols-1], system matrix |
|
|
//| NRows - vertical size of A |
|
|
//| NCols - horizontal size of A |
|
|
//| B - array[0..NCols-1], right part |
|
|
//| Threshold- a number in [0,1]. Singular values beyond |
|
|
//| Threshold are considered zero. Set it to 0.0, |
|
|
//| if you don't understand what it means, so the |
|
|
//| solver will choose good value on its own. |
|
|
//| OUTPUT PARAMETERS |
|
|
//| Info - return code: |
|
|
//| * -4 SVD subroutine failed |
|
|
//| * -1 if NRows<=0 or NCols<=0 or Threshold<0 |
|
|
//| was passed |
|
|
//| * 1 if task is solved |
|
|
//| Rep - solver report, see below for more info |
|
|
//| X - array[0..N-1,0..M-1], it contains: |
|
|
//| * solution of A*X=B if A is non-singular |
|
|
//| (well-conditioned or ill-conditioned, but not |
|
|
//| very close to singular) |
|
|
//| * zeros, if A is singular or VERY close to |
|
|
//| singular (in this case Info=-3). |
|
|
//| SOLVER REPORT |
|
|
//| Subroutine sets following fields of the Rep structure: |
|
|
//| * R2 reciprocal of condition number: 1/cond(A), 2-norm. |
|
|
//| * N = NCols |
|
|
//| * K dim(Null(A)) |
|
|
//| * CX array[0..N-1,0..K-1], kernel of A. |
|
|
//| Columns of CX store such vectors that A*CX[i]=0. |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::RMatrixSolveLS(CMatrixDouble &a,const int nrows,
|
|
const int ncols,double &b[],
|
|
double threshold,int &info,
|
|
CDenseSolverLSReport &rep,
|
|
double &x[])
|
|
{
|
|
//--- create matrix
|
|
CMatrixDouble u;
|
|
CMatrixDouble vt;
|
|
//--- create arrays
|
|
double sv[];
|
|
double rp[];
|
|
double utb[];
|
|
double sutb[];
|
|
double tmp[];
|
|
double ta[];
|
|
double tx[];
|
|
double buf[];
|
|
double w[];
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int nsv=0;
|
|
int kernelidx=0;
|
|
double v=0;
|
|
double verr=0;
|
|
bool svdfailed;
|
|
bool zeroa;
|
|
int rfs=0;
|
|
int nrfs=0;
|
|
bool terminatenexttime;
|
|
bool smallerr;
|
|
int i_=0;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if((nrows<=0 || ncols<=0) || threshold<0.0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(threshold==0.0)
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- Factorize A first
|
|
svdfailed=!CSingValueDecompose::RMatrixSVD(a,nrows,ncols,1,2,2,sv,u,vt);
|
|
//--- check
|
|
if(sv[0]==0.0)
|
|
zeroa=true;
|
|
else
|
|
zeroa=false;
|
|
//--- check
|
|
if(svdfailed || zeroa)
|
|
{
|
|
//--- check
|
|
if(svdfailed)
|
|
info=-4;
|
|
else
|
|
info=1;
|
|
//--- allocation
|
|
ArrayResize(x,ncols);
|
|
for(i=0; i<=ncols-1; i++)
|
|
x[i]=0;
|
|
//--- change values
|
|
rep.m_n=ncols;
|
|
rep.m_k=ncols;
|
|
rep.m_cx.Resize(ncols,ncols);
|
|
for(i=0; i<=ncols-1; i++)
|
|
{
|
|
for(j=0; j<=ncols-1; j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
rep.m_cx.Set(i,j,1);
|
|
else
|
|
rep.m_cx.Set(i,j,0);
|
|
}
|
|
}
|
|
rep.m_r2=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
nsv=MathMin(ncols,nrows);
|
|
//--- check
|
|
if(nsv==ncols)
|
|
rep.m_r2=sv[nsv-1]/sv[0];
|
|
else
|
|
rep.m_r2=0;
|
|
//--- change values
|
|
rep.m_n=ncols;
|
|
info=1;
|
|
//--- Iterative refinement of xc combined with solution:
|
|
//--- 1. xc=0
|
|
//--- 2. calculate r=bc-A*xc using extra-precise dot product
|
|
//--- 3. solve A*y=r
|
|
//--- 4. update x:=x+r
|
|
//--- 5. goto 2
|
|
//--- This cycle is executed until one of two things happens:
|
|
//--- 1. maximum number of iterations reached
|
|
//--- 2. last iteration decreased error to the lower limit
|
|
ArrayResize(utb,nsv);
|
|
ArrayResize(sutb,nsv);
|
|
ArrayResize(x,ncols);
|
|
ArrayResize(tmp,ncols);
|
|
ArrayResize(ta,ncols+1);
|
|
ArrayResize(tx,ncols+1);
|
|
ArrayResize(buf,ncols+1);
|
|
//--- initialization
|
|
for(i=0; i<=ncols-1; i++)
|
|
x[i]=0;
|
|
kernelidx=nsv;
|
|
for(i=0; i<=nsv-1; i++)
|
|
{
|
|
//--- check
|
|
if(sv[i]<=threshold*sv[0])
|
|
{
|
|
kernelidx=i;
|
|
break;
|
|
}
|
|
}
|
|
//--- change values
|
|
rep.m_k=ncols-kernelidx;
|
|
nrfs=CDenseSolverRFSMaxV2(ncols,rep.m_r2);
|
|
terminatenexttime=false;
|
|
//--- allocation
|
|
ArrayResize(rp,nrows);
|
|
for(rfs=0; rfs<=nrfs; rfs++)
|
|
{
|
|
//--- check
|
|
if(terminatenexttime)
|
|
break;
|
|
//--- calculate right part
|
|
if(rfs==0)
|
|
{
|
|
for(i_=0; i_<=nrows-1; i_++)
|
|
rp[i_]=b[i_];
|
|
}
|
|
else
|
|
{
|
|
smallerr=true;
|
|
for(i=0; i<=nrows-1; i++)
|
|
{
|
|
//--- copy
|
|
for(i_=0; i_<=ncols-1; i_++)
|
|
ta[i_]=a[i][i_];
|
|
ta[ncols]=-1;
|
|
//--- copy
|
|
for(i_=0; i_<=ncols-1; i_++)
|
|
tx[i_]=x[i_];
|
|
tx[ncols]=b[i];
|
|
//--- function call
|
|
CXblas::XDot(ta,tx,ncols+1,buf,v,verr);
|
|
rp[i]=-v;
|
|
smallerr=smallerr && MathAbs(v)<4*verr;
|
|
}
|
|
//--- check
|
|
if(smallerr)
|
|
terminatenexttime=true;
|
|
}
|
|
//--- solve A*dx=rp
|
|
for(i=0; i<=ncols-1; i++)
|
|
tmp[i]=0;
|
|
for(i=0; i<=nsv-1; i++)
|
|
utb[i]=0;
|
|
//--- change values
|
|
for(i=0; i<=nrows-1; i++)
|
|
{
|
|
v=rp[i];
|
|
for(i_=0; i_<=nsv-1; i_++)
|
|
utb[i_]=utb[i_]+v*u[i][i_];
|
|
}
|
|
for(i=0; i<=nsv-1; i++)
|
|
{
|
|
//--- check
|
|
if(i<kernelidx)
|
|
sutb[i]=utb[i]/sv[i];
|
|
else
|
|
sutb[i]=0;
|
|
}
|
|
//--- change values
|
|
for(i=0; i<=nsv-1; i++)
|
|
{
|
|
v=sutb[i];
|
|
for(i_=0; i_<=ncols-1; i_++)
|
|
tmp[i_]=tmp[i_]+v*vt[i][i_];
|
|
}
|
|
//--- update x: x:=x+dx
|
|
for(i_=0; i_<=ncols-1; i_++)
|
|
x[i_]=x[i_]+tmp[i_];
|
|
}
|
|
//--- fill CX
|
|
if(rep.m_k>0)
|
|
{
|
|
//--- allocation
|
|
rep.m_cx.Resize(ncols,rep.m_k);
|
|
for(i=0; i<=rep.m_k-1; i++)
|
|
{
|
|
for(i_=0; i_<=ncols-1; i_++)
|
|
rep.m_cx.Set(i_,i,vt[kernelidx+i][i_]);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal LU solver |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::RMatrixLUSolveInternal(CMatrixDouble &lua,int &p[],
|
|
const double scalea,const int n,
|
|
CMatrixDouble &a,const bool havea,
|
|
CMatrixDouble &b,const int m,
|
|
int &info,CDenseSolverReport &rep,
|
|
CMatrixDouble &x)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int rfs=0;
|
|
int nrfs=0;
|
|
double v=0;
|
|
double verr=0;
|
|
double mxb=0;
|
|
double scaleright=0;
|
|
bool smallerr;
|
|
bool terminatenexttime;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double xc[];
|
|
double y[];
|
|
double bc[];
|
|
double xa[];
|
|
double xb[];
|
|
double tx[];
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(scalea>0.0))
|
|
return;
|
|
//--- prepare: check inputs,allocate space...
|
|
if(n<=0 || m<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(p[i]>n-1 || p[i]<i)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
}
|
|
//--- allocation
|
|
x.Resize(n,m);
|
|
ArrayResize(y,n);
|
|
ArrayResize(xc,n);
|
|
ArrayResize(bc,n);
|
|
ArrayResize(tx,n+1);
|
|
ArrayResize(xa,n+1);
|
|
ArrayResize(xb,n+1);
|
|
//--- estimate condition number,test for near singularity
|
|
rep.m_r1=CRCond::RMatrixLURCond1(lua,n);
|
|
rep.m_rinf=CRCond::RMatrixLURCondInf(lua,n);
|
|
//--- check
|
|
if(rep.m_r1<CRCond::RCondThreshold() || rep.m_rinf<CRCond::RCondThreshold())
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=m-1; j++)
|
|
x.Set(i,j,0);
|
|
}
|
|
//--- change values
|
|
rep.m_r1=0;
|
|
rep.m_rinf=0;
|
|
info=-3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
info=1;
|
|
//--- solve
|
|
for(k=0; k<=m-1; k++)
|
|
{
|
|
//--- copy B to contiguous storage
|
|
for(i_=0; i_<n; i_++)
|
|
bc[i_]=b[i_][k];
|
|
//--- Scale right part:
|
|
//--- * MX stores max(|Bi|)
|
|
//--- * ScaleRight stores actual scaling applied to B when solving systems
|
|
//--- it is chosen to make |scaleRight*b| close to 1.
|
|
mxb=0;
|
|
for(i=0; i<n; i++)
|
|
mxb=MathMax(mxb,MathAbs(bc[i]));
|
|
//--- check
|
|
if(mxb==0.0)
|
|
mxb=1;
|
|
//--- change values
|
|
scaleright=1/mxb;
|
|
//--- First,non-iterative part of solution process.
|
|
//--- We use separate code for this task because
|
|
//--- XDot is quite slow and we want to save time.
|
|
for(i_=0; i_<n; i_++)
|
|
xc[i_]=bc[i_]*scaleright;
|
|
//--- function call
|
|
RBasicLUSolve(lua,p,scalea,n,xc,tx);
|
|
//--- Iterative refinement of xc:
|
|
//--- * calculate r=bc-A*xc using extra-precise dot product
|
|
//--- * solve A*y=r
|
|
//--- * update x:=x+r
|
|
//--- This cycle is executed until one of two things happens:
|
|
//--- 1. maximum number of iterations reached
|
|
//--- 2. last iteration decreased error to the lower limit
|
|
if(havea)
|
|
{
|
|
//--- calculation
|
|
nrfs=CDenseSolverRFSMax(n,rep.m_r1,rep.m_rinf);
|
|
terminatenexttime=false;
|
|
for(rfs=0; rfs<=nrfs-1; rfs++)
|
|
{
|
|
//--- check
|
|
if(terminatenexttime)
|
|
break;
|
|
//--- generate right part
|
|
smallerr=true;
|
|
for(i_=0; i_<n; i_++)
|
|
xb[i_]=xc[i_];
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
xa[i_]=a[i][i_]*scalea;
|
|
xa[n]=-1;
|
|
xb[n]=bc[i]*scaleright;
|
|
//--- function call
|
|
CXblas::XDot(xa,xb,n+1,tx,v,verr);
|
|
y[i]=-v;
|
|
smallerr=smallerr && MathAbs(v)<4*verr;
|
|
}
|
|
//--- check
|
|
if(smallerr)
|
|
terminatenexttime=true;
|
|
//--- solve and update
|
|
RBasicLUSolve(lua,p,scalea,n,y,tx);
|
|
for(i_=0; i_<n; i_++)
|
|
xc[i_]=xc[i_]+y[i_];
|
|
}
|
|
}
|
|
//--- Store xc.
|
|
//--- Post-scale result.
|
|
v=scalea*mxb;
|
|
for(i_=0; i_<n; i_++)
|
|
x.Set(i_,k,xc[i_]*v);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal Cholesky solver |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::SPDMatrixCholeskySolveInternal(CMatrixDouble &cha,
|
|
const double sqrtscalea,
|
|
const int n,const bool IsUpper,
|
|
CMatrixDouble &a,
|
|
const bool havea,
|
|
CMatrixDouble &b,
|
|
const int m,int &info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixDouble &x)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
double mxb=0;
|
|
double scaleright=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double xc[];
|
|
double y[];
|
|
double bc[];
|
|
double xa[];
|
|
double xb[];
|
|
double tx[];
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(sqrtscalea>0.0))
|
|
return;
|
|
//--- prepare: check inputs,allocate space...
|
|
if(n<=0 || m<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
x.Resize(n,m);
|
|
ArrayResize(y,n);
|
|
ArrayResize(xc,n);
|
|
ArrayResize(bc,n);
|
|
ArrayResize(tx,n+1);
|
|
ArrayResize(xa,n+1);
|
|
ArrayResize(xb,n+1);
|
|
//--- estimate condition number,test for near singularity
|
|
rep.m_r1=CRCond::SPDMatrixCholeskyRCond(cha,n,IsUpper);
|
|
rep.m_rinf=rep.m_r1;
|
|
//--- check
|
|
if(rep.m_r1<CRCond::RCondThreshold())
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=m-1; j++)
|
|
x.Set(i,j,0);
|
|
}
|
|
//--- change values
|
|
rep.m_r1=0;
|
|
rep.m_rinf=0;
|
|
info=-3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
info=1;
|
|
//--- solve
|
|
for(k=0; k<=m-1; k++)
|
|
{
|
|
//--- copy B to contiguous storage
|
|
for(i_=0; i_<n; i_++)
|
|
bc[i_]=b[i_][k];
|
|
//--- Scale right part:
|
|
//--- * MX stores max(|Bi|)
|
|
//--- * ScaleRight stores actual scaling applied to B when solving systems
|
|
//--- it is chosen to make |scaleRight*b| close to 1.
|
|
mxb=0;
|
|
for(i=0; i<n; i++)
|
|
mxb=MathMax(mxb,MathAbs(bc[i]));
|
|
//--- check
|
|
if(mxb==0.0)
|
|
mxb=1;
|
|
scaleright=1/mxb;
|
|
//--- First,non-iterative part of solution process.
|
|
//--- We use separate code for this task because
|
|
//--- XDot is quite slow and we want to save time.
|
|
for(i_=0; i_<n; i_++)
|
|
xc[i_]=bc[i_]*scaleright;
|
|
//--- function call
|
|
SPDBasicCholeskySolve(cha,sqrtscalea,n,IsUpper,xc,tx);
|
|
//--- Store xc.
|
|
//--- Post-scale result.
|
|
v=CMath::Sqr(sqrtscalea)*mxb;
|
|
for(i_=0; i_<n; i_++)
|
|
x.Set(i_,k,xc[i_]*v);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal LU solver |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::CMatrixLUSolveInternal(CMatrixComplex &lua,int &p[],
|
|
const double scalea,const int n,
|
|
CMatrixComplex &a,const bool havea,
|
|
CMatrixComplex &b,const int m,
|
|
int &info,CDenseSolverReport &rep,
|
|
CMatrixComplex &x)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int rfs=0;
|
|
int nrfs=0;
|
|
complex v=0;
|
|
double verr=0;
|
|
double mxb=0;
|
|
double scaleright=0;
|
|
bool smallerr;
|
|
bool terminatenexttime;
|
|
int i_=0;
|
|
//--- create arrays
|
|
complex xc[];
|
|
complex y[];
|
|
complex bc[];
|
|
complex xa[];
|
|
complex xb[];
|
|
complex tx[];
|
|
double tmpbuf[];
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(scalea>0.0))
|
|
return;
|
|
//--- prepare: check inputs,allocate space...
|
|
if(n<=0 || m<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(p[i]>n-1 || p[i]<i)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
}
|
|
//--- allocation
|
|
x.Resize(n,m);
|
|
ArrayResize(y,n);
|
|
ArrayResize(xc,n);
|
|
ArrayResize(bc,n);
|
|
ArrayResize(tx,n);
|
|
ArrayResize(xa,n+1);
|
|
ArrayResize(xb,n+1);
|
|
ArrayResize(tmpbuf,2*n+2);
|
|
//--- estimate condition number,test for near singularity
|
|
rep.m_r1=CRCond::CMatrixLURCond1(lua,n);
|
|
rep.m_rinf=CRCond::CMatrixLURCondInf(lua,n);
|
|
//--- check
|
|
if(rep.m_r1<CRCond::RCondThreshold() || rep.m_rinf<CRCond::RCondThreshold())
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=m-1; j++)
|
|
x.Set(i,j,0.0);
|
|
}
|
|
//--- change values
|
|
rep.m_r1=0;
|
|
rep.m_rinf=0;
|
|
info=-3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
info=1;
|
|
//--- solve
|
|
for(k=0; k<=m-1; k++)
|
|
{
|
|
//--- copy B to contiguous storage
|
|
for(i_=0; i_<n; i_++)
|
|
bc[i_]=b[i_][k];
|
|
//--- Scale right part:
|
|
//--- * MX stores max(|Bi|)
|
|
//--- * ScaleRight stores actual scaling applied to B when solving systems
|
|
//--- it is chosen to make |scaleRight*b| close to 1.
|
|
mxb=0;
|
|
for(i=0; i<n; i++)
|
|
mxb=MathMax(mxb,CMath::AbsComplex(bc[i]));
|
|
//--- check
|
|
if(mxb==0.0)
|
|
mxb=1;
|
|
scaleright=1/mxb;
|
|
//--- First,non-iterative part of solution process.
|
|
//--- We use separate code for this task because
|
|
//--- XDot is quite slow and we want to save time.
|
|
for(i_=0; i_<n; i_++)
|
|
xc[i_]=bc[i_]*scaleright;
|
|
//--- function call
|
|
CBasicLUSolve(lua,p,scalea,n,xc,tx);
|
|
//--- Iterative refinement of xc:
|
|
//--- * calculate r=bc-A*xc using extra-precise dot product
|
|
//--- * solve A*y=r
|
|
//--- * update x:=x+r
|
|
//--- This cycle is executed until one of two things happens:
|
|
//--- 1. maximum number of iterations reached
|
|
//--- 2. last iteration decreased error to the lower limit
|
|
if(havea)
|
|
{
|
|
//--- calculation
|
|
nrfs=CDenseSolverRFSMax(n,rep.m_r1,rep.m_rinf);
|
|
terminatenexttime=false;
|
|
for(rfs=0; rfs<=nrfs-1; rfs++)
|
|
{
|
|
//--- check
|
|
if(terminatenexttime)
|
|
break;
|
|
//--- generate right part
|
|
smallerr=true;
|
|
//--- copy
|
|
for(i_=0; i_<n; i_++)
|
|
xb[i_]=xc[i_];
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- change values
|
|
for(i_=0; i_<n; i_++)
|
|
xa[i_]=a[i][i_]*scalea;
|
|
xa[n]=-1;
|
|
xb[n]=bc[i]*scaleright;
|
|
//--- function call
|
|
CXblas::XCDot(xa,xb,n+1,tmpbuf,v,verr);
|
|
y[i]=-v;
|
|
smallerr=smallerr && CMath::AbsComplex(v)<4*verr;
|
|
}
|
|
//--- check
|
|
if(smallerr)
|
|
terminatenexttime=true;
|
|
//--- solve and update
|
|
CBasicLUSolve(lua,p,scalea,n,y,tx);
|
|
for(i_=0; i_<n; i_++)
|
|
xc[i_]=xc[i_]+y[i_];
|
|
}
|
|
}
|
|
//--- Store xc.
|
|
//--- Post-scale result.
|
|
v=scalea*mxb;
|
|
for(i_=0; i_<n; i_++)
|
|
x.Set(i_,k,xc[i_]*v);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal Cholesky solver |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::HPDMatrixCholeskySolveInternal(CMatrixComplex &cha,
|
|
const double sqrtscalea,
|
|
const int n,
|
|
const bool IsUpper,
|
|
CMatrixComplex &a,
|
|
const bool havea,
|
|
CMatrixComplex &b,
|
|
const int m,int &info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixComplex &x)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
double mxb=0;
|
|
double scaleright=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
complex xc[];
|
|
complex y[];
|
|
complex bc[];
|
|
complex xa[];
|
|
complex xb[];
|
|
complex tx[];
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(sqrtscalea>0.0))
|
|
return;
|
|
//--- prepare: check inputs,allocate space...
|
|
if(n<=0 || m<=0)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
x.Resize(n,m);
|
|
ArrayResize(y,n);
|
|
ArrayResize(xc,n);
|
|
ArrayResize(bc,n);
|
|
ArrayResize(tx,n+1);
|
|
ArrayResize(xa,n+1);
|
|
ArrayResize(xb,n+1);
|
|
//--- estimate condition number,test for near singularity
|
|
rep.m_r1=CRCond::HPDMatrixCholeskyRCond(cha,n,IsUpper);
|
|
rep.m_rinf=rep.m_r1;
|
|
//--- check
|
|
if(rep.m_r1<CRCond::RCondThreshold())
|
|
{
|
|
for(i=0; i<n; i++)
|
|
{
|
|
for(j=0; j<=m-1; j++)
|
|
x.Set(i,j,0.0);
|
|
}
|
|
//--- change values
|
|
rep.m_r1=0;
|
|
rep.m_rinf=0;
|
|
info=-3;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
info=1;
|
|
//--- solve
|
|
for(k=0; k<=m-1; k++)
|
|
{
|
|
//--- copy B to contiguous storage
|
|
for(i_=0; i_<n; i_++)
|
|
bc[i_]=b[i_][k];
|
|
//--- Scale right part:
|
|
//--- * MX stores max(|Bi|)
|
|
//--- * ScaleRight stores actual scaling applied to B when solving systems
|
|
//--- it is chosen to make |scaleRight*b| close to 1.
|
|
mxb=0;
|
|
for(i=0; i<n; i++)
|
|
mxb=MathMax(mxb,CMath::AbsComplex(bc[i]));
|
|
//--- check
|
|
if(mxb==0.0)
|
|
mxb=1;
|
|
scaleright=1/mxb;
|
|
//--- First,non-iterative part of solution process.
|
|
//--- We use separate code for this task because
|
|
//--- XDot is quite slow and we want to save time.
|
|
for(i_=0; i_<n; i_++)
|
|
xc[i_]=bc[i_]*scaleright;
|
|
//--- function call
|
|
HPDBasicCholeskySolve(cha,sqrtscalea,n,IsUpper,xc,tx);
|
|
//--- Store xc.
|
|
//--- Post-scale result.
|
|
v=CMath::Sqr(sqrtscalea)*mxb;
|
|
for(i_=0; i_<n; i_++)
|
|
x.Set(i_,k,xc[i_]*v);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine. |
|
|
//| Returns maximum count of RFS iterations as function of: |
|
|
//| 1. machine epsilon |
|
|
//| 2. task size. |
|
|
//| 3. condition number |
|
|
//+------------------------------------------------------------------+
|
|
int CDenseSolver::CDenseSolverRFSMax(const int n,const double r1,
|
|
const double rinf)
|
|
{
|
|
//--- return result
|
|
return(5);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine. |
|
|
//| Returns maximum count of RFS iterations as function of: |
|
|
//| 1. machine epsilon |
|
|
//| 2. task size. |
|
|
//| 3. norm-2 condition number |
|
|
//+------------------------------------------------------------------+
|
|
int CDenseSolver::CDenseSolverRFSMaxV2(const int n,const double r2)
|
|
{
|
|
//--- return result
|
|
return(CDenseSolverRFSMax(n,0,0));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic LU solver for ScaleA*PLU*x = y. |
|
|
//| This subroutine assumes that: |
|
|
//| * L is well-scaled, and it is U which needs scaling by ScaleA. |
|
|
//| * A=PLU is well-conditioned, so no zero divisions or overflow may|
|
|
//| occur |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::RBasicLUSolve(CMatrixDouble &lua,int &p[],
|
|
const double scalea,const int n,
|
|
double &xb[],double &tmp[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- swap
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(p[i]!=i)
|
|
{
|
|
v=xb[i];
|
|
xb[i]=xb[p[i]];
|
|
xb[p[i]]=v;
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=1; i<n; i++)
|
|
{
|
|
v=0.0;
|
|
//--- change value
|
|
for(i_=0; i_<=i-1; i_++)
|
|
v+=lua[i][i_]*xb[i_];
|
|
//--- shift
|
|
xb[i]=xb[i]-v;
|
|
}
|
|
//--- change value
|
|
xb[n-1]=xb[n-1]/(lua[n-1][n-1]*scalea);
|
|
for(i=n-2; i>=0; i--)
|
|
{
|
|
//--- calculation
|
|
for(i_=i+1; i_<n; i_++)
|
|
tmp[i_]=lua[i][i_]*scalea;
|
|
v=0.0;
|
|
//--- change value
|
|
for(i_=i+1; i_<n; i_++)
|
|
v+=tmp[i_]*xb[i_];
|
|
//--- get result
|
|
xb[i]=(xb[i]-v)/(lua[i][i]*scalea);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic Cholesky solver for ScaleA*Cholesky(A)'*x = y. |
|
|
//| This subroutine assumes that: |
|
|
//| * A*ScaleA is well scaled |
|
|
//| * A is well-conditioned, so no zero divisions or overflow may |
|
|
//| occur |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::SPDBasicCholeskySolve(CMatrixDouble &cha,
|
|
const double sqrtscalea,
|
|
const int n,const bool IsUpper,
|
|
double &xb[],double &tmp[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- A=L*L' or A=U'*U
|
|
if(IsUpper)
|
|
{
|
|
//--- Solve U'*y=b first.
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xb[i]=xb[i]/(sqrtscalea*cha[i][i]);
|
|
//--- check
|
|
if(i<n-1)
|
|
{
|
|
v=xb[i];
|
|
//--- calculation
|
|
for(i_=i+1; i_<n; i_++)
|
|
tmp[i_]=sqrtscalea*cha[i][i_];
|
|
for(i_=i+1; i_<n; i_++)
|
|
xb[i_]=xb[i_]-v*tmp[i_];
|
|
}
|
|
}
|
|
//--- Solve U*x=y then.
|
|
for(i=n-1; i>=0; i--)
|
|
{
|
|
//--- check
|
|
if(i<n-1)
|
|
{
|
|
for(i_=i+1; i_<n; i_++)
|
|
tmp[i_]=sqrtscalea*cha[i][i_];
|
|
v=0.0;
|
|
//--- change value
|
|
for(i_=i+1; i_<n; i_++)
|
|
v+=tmp[i_]*xb[i_];
|
|
//--- shift
|
|
xb[i]=xb[i]-v;
|
|
}
|
|
xb[i]=xb[i]/(sqrtscalea*cha[i][i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Solve L*y=b first
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(i>0)
|
|
{
|
|
for(i_=0; i_<=i-1; i_++)
|
|
tmp[i_]=sqrtscalea*cha[i][i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<=i-1; i_++)
|
|
v+=tmp[i_]*xb[i_];
|
|
//--- shift
|
|
xb[i]=xb[i]-v;
|
|
}
|
|
xb[i]=xb[i]/(sqrtscalea*cha[i][i]);
|
|
}
|
|
//--- Solve L'*x=y then.
|
|
for(i=n-1; i>=0; i--)
|
|
{
|
|
xb[i]=xb[i]/(sqrtscalea*cha[i][i]);
|
|
//--- check
|
|
if(i>0)
|
|
{
|
|
v=xb[i];
|
|
//--- calculation
|
|
for(i_=0; i_<=i-1; i_++)
|
|
tmp[i_]=sqrtscalea*cha[i][i_];
|
|
for(i_=0; i_<=i-1; i_++)
|
|
xb[i_]=xb[i_]-v*tmp[i_];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic LU solver for ScaleA*PLU*x = y. |
|
|
//| This subroutine assumes that: |
|
|
//| * L is well-scaled, and it is U which needs scaling by ScaleA. |
|
|
//| * A=PLU is well-conditioned, so no zero divisions or overflow may|
|
|
//| occur |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::CBasicLUSolve(CMatrixComplex &lua,int &p[],
|
|
const double scalea,const int n,
|
|
complex &xb[],complex &tmp[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- swap
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(p[i]!=i)
|
|
{
|
|
v=xb[i];
|
|
xb[i]=xb[p[i]];
|
|
xb[p[i]]=v;
|
|
}
|
|
}
|
|
for(i=1; i<n; i++)
|
|
{
|
|
v=0.0;
|
|
//--- calculation
|
|
for(i_=0; i_<=i-1; i_++)
|
|
v+=lua[i][i_]*xb[i_];
|
|
//--- shift
|
|
xb[i]=xb[i]-v;
|
|
}
|
|
//--- change values
|
|
xb[n-1]=xb[n-1]/(lua[n-1][n-1]*scalea);
|
|
for(i=n-2; i>=0; i--)
|
|
{
|
|
for(i_=i+1; i_<n; i_++)
|
|
tmp[i_]=lua[i][i_]*scalea;
|
|
//--- calculation
|
|
v=0.0;
|
|
for(i_=i+1; i_<n; i_++)
|
|
v+=tmp[i_]*xb[i_];
|
|
//--- get result
|
|
xb[i]=(xb[i]-v)/(lua[i][i]*scalea);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic Cholesky solver for ScaleA*Cholesky(A)'*x = y. |
|
|
//| This subroutine assumes that: |
|
|
//| * A*ScaleA is well scaled |
|
|
//| * A is well-conditioned, so no zero divisions or overflow may |
|
|
//| occur |
|
|
//+------------------------------------------------------------------+
|
|
void CDenseSolver::HPDBasicCholeskySolve(CMatrixComplex &cha,
|
|
const double sqrtscalea,
|
|
const int n,const bool IsUpper,
|
|
complex &xb[],complex &tmp[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- A=L*L' or A=U'*U
|
|
if(IsUpper)
|
|
{
|
|
//--- Solve U'*y=b first.
|
|
for(i=0; i<n; i++)
|
|
{
|
|
xb[i]=xb[i]/(CMath::Conj(cha[i][i])*sqrtscalea);
|
|
//--- check
|
|
if(i<n-1)
|
|
{
|
|
v=xb[i];
|
|
//--- calculation
|
|
for(i_=i+1; i_<n; i_++)
|
|
tmp[i_]=CMath::Conj(cha[i][i_])*sqrtscalea;
|
|
for(i_=i+1; i_<n; i_++)
|
|
xb[i_]=xb[i_]-v*tmp[i_];
|
|
}
|
|
}
|
|
//--- Solve U*x=y then.
|
|
for(i=n-1; i>=0; i--)
|
|
{
|
|
//--- check
|
|
if(i<n-1)
|
|
{
|
|
for(i_=i+1; i_<n; i_++)
|
|
tmp[i_]=cha[i][i_]*sqrtscalea;
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=i+1; i_<n; i_++)
|
|
v+=tmp[i_]*xb[i_];
|
|
//--- shift
|
|
xb[i]=xb[i]-v;
|
|
}
|
|
xb[i]=xb[i]/(cha[i][i]*sqrtscalea);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Solve L*y=b first
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(i>0)
|
|
{
|
|
for(i_=0; i_<=i-1; i_++)
|
|
tmp[i_]=cha[i][i_]*sqrtscalea;
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0; i_<=i-1; i_++)
|
|
v+=tmp[i_]*xb[i_];
|
|
//--- shift
|
|
xb[i]=xb[i]-v;
|
|
}
|
|
xb[i]=xb[i]/(cha[i][i]*sqrtscalea);
|
|
}
|
|
//--- Solve L'*x=y then.
|
|
for(i=n-1; i>=0; i--)
|
|
{
|
|
xb[i]=xb[i]/(CMath::Conj(cha[i][i])*sqrtscalea);
|
|
//--- check
|
|
if(i>0)
|
|
{
|
|
v=xb[i];
|
|
//--- calculation
|
|
for(i_=0; i_<=i-1; i_++)
|
|
tmp[i_]=CMath::Conj(cha[i][i_])*sqrtscalea;
|
|
for(i_=0; i_<=i-1; i_++)
|
|
xb[i_]=xb[i_]-v*tmp[i_];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary class for CNlEq |
|
|
//+------------------------------------------------------------------+
|
|
class CNlEqState
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_n;
|
|
int m_m;
|
|
double m_epsf;
|
|
int m_maxits;
|
|
bool m_xrep;
|
|
double m_stpmax;
|
|
double m_f;
|
|
bool m_needf;
|
|
bool m_needfij;
|
|
bool m_xupdated;
|
|
RCommState m_rstate;
|
|
int m_repiterationscount;
|
|
int m_repnfunc;
|
|
int m_repnjac;
|
|
int m_repterminationtype;
|
|
double m_fbase;
|
|
double m_fprev;
|
|
//--- arrays
|
|
double m_x[];
|
|
double m_fi[];
|
|
double m_xbase[];
|
|
double m_candstep[];
|
|
double m_rightpart[];
|
|
double m_cgbuf[];
|
|
//--- matrix
|
|
CMatrixDouble m_j;
|
|
//--- constructor, destructor
|
|
CNlEqState(void);
|
|
~CNlEqState(void) {}
|
|
//--- copy
|
|
void Copy(CNlEqState &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor |
|
|
//+------------------------------------------------------------------+
|
|
CNlEqState::CNlEqState(void)
|
|
{
|
|
m_n=0;
|
|
m_m=0;
|
|
m_epsf=0;
|
|
m_maxits=0;
|
|
m_xrep=false;
|
|
m_stpmax=0;
|
|
m_f=0;
|
|
m_needf=false;
|
|
m_needfij=false;
|
|
m_xupdated=false;
|
|
m_repiterationscount=0;
|
|
m_repnfunc=0;
|
|
m_repnjac=0;
|
|
m_repterminationtype=0;
|
|
m_fbase=0;
|
|
m_fprev=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEqState::Copy(CNlEqState &obj)
|
|
{
|
|
//--- copy variables
|
|
m_n=obj.m_n;
|
|
m_m=obj.m_m;
|
|
m_epsf=obj.m_epsf;
|
|
m_maxits=obj.m_maxits;
|
|
m_xrep=obj.m_xrep;
|
|
m_stpmax=obj.m_stpmax;
|
|
m_f=obj.m_f;
|
|
m_needf=obj.m_needf;
|
|
m_needfij=obj.m_needfij;
|
|
m_xupdated=obj.m_xupdated;
|
|
m_repiterationscount=obj.m_repiterationscount;
|
|
m_repnfunc=obj.m_repnfunc;
|
|
m_repnjac=obj.m_repnjac;
|
|
m_repterminationtype=obj.m_repterminationtype;
|
|
m_fbase=obj.m_fbase;
|
|
m_fprev=obj.m_fprev;
|
|
m_rstate.Copy(obj.m_rstate);
|
|
//--- copy arrays
|
|
ArrayCopy(m_x,obj.m_x);
|
|
ArrayCopy(m_fi,obj.m_fi);
|
|
ArrayCopy(m_xbase,obj.m_xbase);
|
|
ArrayCopy(m_candstep,obj.m_candstep);
|
|
ArrayCopy(m_rightpart,obj.m_rightpart);
|
|
ArrayCopy(m_cgbuf,obj.m_cgbuf);
|
|
//--- copy matrix
|
|
m_j=obj.m_j;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This class is a shell for class CNlEqState |
|
|
//+------------------------------------------------------------------+
|
|
class CNlEqStateShell
|
|
{
|
|
private:
|
|
CNlEqState m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CNlEqStateShell(void) {}
|
|
CNlEqStateShell(CNlEqState &obj) { m_innerobj.Copy(obj); }
|
|
~CNlEqStateShell(void) {}
|
|
//--- methods
|
|
bool GetNeedF(void);
|
|
void SetNeedF(const bool b);
|
|
bool GetNeedFIJ(void);
|
|
void SetNeedFIJ(const bool b);
|
|
bool GetXUpdated(void);
|
|
void SetXUpdated(const bool b);
|
|
double GetF(void);
|
|
void SetF(const double d);
|
|
CNlEqState *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needf |
|
|
//+------------------------------------------------------------------+
|
|
bool CNlEqStateShell::GetNeedF(void)
|
|
{
|
|
return(m_innerobj.m_needf);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needf |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEqStateShell::SetNeedF(const bool b)
|
|
{
|
|
m_innerobj.m_needf=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needfij |
|
|
//+------------------------------------------------------------------+
|
|
bool CNlEqStateShell::GetNeedFIJ(void)
|
|
{
|
|
return(m_innerobj.m_needfij);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needfij |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEqStateShell::SetNeedFIJ(const bool b)
|
|
{
|
|
m_innerobj.m_needfij=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable xupdated |
|
|
//+------------------------------------------------------------------+
|
|
bool CNlEqStateShell::GetXUpdated(void)
|
|
{
|
|
return(m_innerobj.m_xupdated);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable xupdated |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEqStateShell::SetXUpdated(const bool b)
|
|
{
|
|
m_innerobj.m_xupdated=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable f |
|
|
//+------------------------------------------------------------------+
|
|
double CNlEqStateShell::GetF(void)
|
|
{
|
|
return(m_innerobj.m_f);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable f |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEqStateShell::SetF(const double d)
|
|
{
|
|
m_innerobj.m_f=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CNlEqState *CNlEqStateShell::GetInnerObj(void)
|
|
{
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary class for CNlEq |
|
|
//+------------------------------------------------------------------+
|
|
class CNlEqReport
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_iterationscount;
|
|
int m_nfunc;
|
|
int m_njac;
|
|
int m_terminationtype;
|
|
//--- constructor, destructor
|
|
CNlEqReport(void) { ZeroMemory(this); }
|
|
~CNlEqReport(void) {}
|
|
//--- copy
|
|
void Copy(CNlEqReport &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEqReport::Copy(CNlEqReport &obj)
|
|
{
|
|
//--- copy variables
|
|
m_iterationscount=obj.m_iterationscount;
|
|
m_nfunc=obj.m_nfunc;
|
|
m_njac=obj.m_njac;
|
|
m_terminationtype=obj.m_terminationtype;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This class is a shell for class CNlEqReport |
|
|
//+------------------------------------------------------------------+
|
|
class CNlEqReportShell
|
|
{
|
|
private:
|
|
CNlEqReport m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CNlEqReportShell(void) {}
|
|
CNlEqReportShell(CNlEqReport &obj) { m_innerobj.Copy(obj); }
|
|
~CNlEqReportShell(void) {}
|
|
//--- methods
|
|
int GetIterationsCount(void);
|
|
void SetIterationsCount(const int i);
|
|
int GetNFunc(void);
|
|
void SetNFunc(const int i);
|
|
int GetNJac(void);
|
|
void SetNJac(const int i);
|
|
int GetTerminationType(void);
|
|
void SetTerminationType(const int i);
|
|
CNlEqReport *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable iterationscount |
|
|
//+------------------------------------------------------------------+
|
|
int CNlEqReportShell::GetIterationsCount(void)
|
|
{
|
|
return(m_innerobj.m_iterationscount);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable iterationscount |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEqReportShell::SetIterationsCount(const int i)
|
|
{
|
|
m_innerobj.m_iterationscount=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable nfunc |
|
|
//+------------------------------------------------------------------+
|
|
int CNlEqReportShell::GetNFunc(void)
|
|
{
|
|
return(m_innerobj.m_nfunc);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable nfunc |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEqReportShell::SetNFunc(const int i)
|
|
{
|
|
m_innerobj.m_nfunc=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable njac |
|
|
//+------------------------------------------------------------------+
|
|
int CNlEqReportShell::GetNJac(void)
|
|
{
|
|
return(m_innerobj.m_njac);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable njac |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEqReportShell::SetNJac(const int i)
|
|
{
|
|
m_innerobj.m_njac=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable terminationtype |
|
|
//+------------------------------------------------------------------+
|
|
int CNlEqReportShell::GetTerminationType(void)
|
|
{
|
|
return(m_innerobj.m_terminationtype);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable terminationtype |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEqReportShell::SetTerminationType(const int i)
|
|
{
|
|
m_innerobj.m_terminationtype=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CNlEqReport *CNlEqReportShell::GetInnerObj(void)
|
|
{
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Solving systems of nonlinear equations |
|
|
//+------------------------------------------------------------------+
|
|
class CNlEq
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void ClearRequestFields(CNlEqState &state);
|
|
static bool IncreaseLambda(double &lambdav,double &nu,const double lambdaup);
|
|
static void DecreaseLambda(double &lambdav,double &nu,const double lambdadown);
|
|
//--- auxiliary functions forNlEqiteration
|
|
static void Func_case_rcomm(CNlEqState &state,const int n,const int m,const int i,const bool b,const double lambdaup,const double lambdadown,const double lambdav,const double rho,const double mu,const double stepnorm);
|
|
static void Func_case_7(CNlEqState &state,const int n);
|
|
static bool Func_case_5(CNlEqState &state,double &lambdaup,double &lambdadown,double &lambdav,double &rho);
|
|
static bool Func_case_11(CNlEqState &state,const double stepnorm);
|
|
static int Func_case_10(CNlEqState &state,const int n,const int m,const int i,const bool b,const double lambdaup,const double lambdadown,const double lambdav,const double rho,const double mu,const double stepnorm);
|
|
static int Func_case_9(CNlEqState &state,int &n,int &m,int &i,bool &b,const double lambdaup,const double lambdadown,double &lambdav,const double rho,const double mu,double &stepnorm);
|
|
|
|
public:
|
|
//--- constant
|
|
static const int m_armijomaxfev;
|
|
//--- public methods
|
|
static void NlEqCreateLM(const int n,const int m,double &x[],CNlEqState &state);
|
|
static void NlEqSetCond(CNlEqState &state,double epsf,const int maxits);
|
|
static void NlEqSetXRep(CNlEqState &state,const bool needxrep);
|
|
static void NlEqSetStpMax(CNlEqState &state,const double stpmax);
|
|
static void NlEqResults(CNlEqState &state,double &x[],CNlEqReport &rep);
|
|
static void NlEqResultsBuf(CNlEqState &state,double &x[],CNlEqReport &rep);
|
|
static void NlEqRestartFrom(CNlEqState &state,double &x[]);
|
|
static bool NlEqIteration(CNlEqState &state);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize constant |
|
|
//+------------------------------------------------------------------+
|
|
const int CNlEq::m_armijomaxfev=20;
|
|
//+------------------------------------------------------------------+
|
|
//| LEVENBERG-MARQUARDT-LIKE NONLINEAR SOLVER |
|
|
//| DESCRIPTION: |
|
|
//| This algorithm solves system of nonlinear equations |
|
|
//| F[0](x[0], ..., x[n-1]) = 0 |
|
|
//| F[1](x[0], ..., x[n-1]) = 0 |
|
|
//| ... |
|
|
//| F[M-1](x[0], ..., x[n-1]) = 0 |
|
|
//| with M/N do not necessarily coincide. Algorithm converges |
|
|
//| quadratically under following conditions: |
|
|
//| * the solution set XS is nonempty |
|
|
//| * for some xs in XS there exist such neighbourhood N(xs) |
|
|
//| that: |
|
|
//| * vector function F(x) and its Jacobian J(x) are |
|
|
//| continuously differentiable on N |
|
|
//| * ||F(x)|| provides local error bound on N, i.e. there |
|
|
//| exists such c1, that ||F(x)||>c1*distance(x,XS) |
|
|
//| Note that these conditions are much more weaker than usual |
|
|
//| non-singularity conditions. For example, algorithm will converge |
|
|
//| for any affine function F (whether its Jacobian singular or not).|
|
|
//| REQUIREMENTS: |
|
|
//| Algorithm will request following information during its |
|
|
//| operation: |
|
|
//| * function vector F[] and Jacobian matrix at given point X |
|
|
//| * value of merit function f(x)=F[0]^2(x)+...+F[M-1]^2(x) at given|
|
|
//| point X |
|
|
//| USAGE: |
|
|
//| 1. User initializes algorithm state with NLEQCreateLM() call |
|
|
//| 2. User tunes solver parameters with NLEQSetCond(), |
|
|
//| NLEQSetStpMax() and other functions |
|
|
//| 3. User calls NLEQSolve() function which takes algorithm state |
|
|
//| and pointers (delegates, etc.) to callback functions which |
|
|
//| calculate merit function value and Jacobian. |
|
|
//| 4. User calls NLEQResults() to get solution |
|
|
//| 5. Optionally, user may call NLEQRestartFrom() to solve another |
|
|
//| problem with same parameters (N/M) but another starting point |
|
|
//| and/or another function vector. NLEQRestartFrom() allows to |
|
|
//| reuse already initialized structure. |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - space dimension, N>1: |
|
|
//| * if provided, only leading N elements of X are |
|
|
//| used |
|
|
//| * if not provided, determined automatically from |
|
|
//| size of X |
|
|
//| M - system size |
|
|
//| X - starting point |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NOTES: |
|
|
//| 1. you may tune stopping conditions with NLEQSetCond() function |
|
|
//| 2. if target function contains exp() or other fast growing |
|
|
//| functions, and optimization algorithm makes too large steps |
|
|
//| which leads to overflow, use NLEQSetStpMax() function to bound|
|
|
//| algorithm's steps. |
|
|
//| 3. this algorithm is a slightly modified implementation of the |
|
|
//| method described in 'Levenberg-Marquardt method for |
|
|
//| constrained nonlinear equations with strong local convergence |
|
|
//| properties' by Christian Kanzow Nobuo Yamashita and Masao |
|
|
//| Fukushima and further developed in 'On the convergence of a |
|
|
//| New Levenberg-Marquardt Method' by Jin-yan Fan and Ya-Xiang |
|
|
//| Yuan. |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEq::NlEqCreateLM(const int n,const int m,double &x[],
|
|
CNlEqState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- Initialize
|
|
state.m_n=n;
|
|
state.m_m=m;
|
|
NlEqSetCond(state,0,0);
|
|
NlEqSetXRep(state,false);
|
|
NlEqSetStpMax(state,0);
|
|
//--- allocation
|
|
ArrayResize(state.m_x,n);
|
|
ArrayResize(state.m_xbase,n);
|
|
state.m_j.Resize(m,n);
|
|
ArrayResize(state.m_fi,m);
|
|
ArrayResize(state.m_rightpart,n);
|
|
ArrayResize(state.m_candstep,n);
|
|
//--- function call
|
|
NlEqRestartFrom(state,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets stopping conditions for the nonlinear solver |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| EpsF - >=0 |
|
|
//| The subroutine finishes its work if on k+1-th |
|
|
//| iteration the condition ||F||<=EpsF is satisfied |
|
|
//| MaxIts - maximum number of iterations. If MaxIts=0, the |
|
|
//| number of iterations is unlimited. |
|
|
//| Passing EpsF=0 and MaxIts=0 simultaneously will lead to |
|
|
//| automatic stopping criterion selection (small EpsF). |
|
|
//| NOTES: |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEq::NlEqSetCond(CNlEqState &state,double epsf,const int maxits)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsf),__FUNCTION__+": EpsF is not finite number!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsf>=0.0,__FUNCTION__+": negative EpsF!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(maxits>=0,__FUNCTION__+": negative MaxIts!"))
|
|
return;
|
|
//--- check
|
|
if(epsf==0.0 && maxits==0)
|
|
epsf=1.0E-6;
|
|
//--- change values
|
|
state.m_epsf=epsf;
|
|
state.m_maxits=maxits;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function turns on/off reporting. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NeedXRep- whether iteration reports are needed or not |
|
|
//| If NeedXRep is True, algorithm will call rep() callback function |
|
|
//| if it is provided to NLEQSolve(). |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEq::NlEqSetXRep(CNlEqState &state,const bool needxrep)
|
|
{
|
|
//--- change value
|
|
state.m_xrep=needxrep;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets maximum step length |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| StpMax - maximum step length, >=0. Set StpMax to 0.0, if |
|
|
//| you don't want to limit step length. |
|
|
//| Use this subroutine when target function contains exp() or other |
|
|
//| fast growing functions, and algorithm makes too large steps which|
|
|
//| lead to overflow. This function allows us to reject steps that |
|
|
//| are too large (and therefore expose us to the possible overflow) |
|
|
//| without actually calculating function value at the x+stp*d. |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEq::NlEqSetStpMax(CNlEqState &state,const double stpmax)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(stpmax),__FUNCTION__+": StpMax is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(stpmax>=0.0,__FUNCTION__+": StpMax<0!"))
|
|
return;
|
|
//--- change value
|
|
state.m_stpmax=stpmax;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| NLEQ solver results |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - algorithm state. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[0..N-1], solution |
|
|
//| Rep - optimization report: |
|
|
//| * Rep.TerminationType completetion code: |
|
|
//| * -4 ERROR: algorithm has converged to the|
|
|
//| stationary point Xf which is local |
|
|
//| minimum of f=F[0]^2+...+F[m-1]^2, |
|
|
//| but is not solution of nonlinear |
|
|
//| system. |
|
|
//| * 1 sqrt(f)<=EpsF. |
|
|
//| * 5 MaxIts steps was taken |
|
|
//| * 7 stopping conditions are too |
|
|
//| stringent, further improvement is |
|
|
//| impossible |
|
|
//| * Rep.IterationsCount contains iterations count |
|
|
//| * NFEV countains number of function calculations |
|
|
//| * ActiveConstraints contains number of active |
|
|
//| constraints |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEq::NlEqResults(CNlEqState &state,double &x[],CNlEqReport &rep)
|
|
{
|
|
ArrayResize(x,0);
|
|
//--- function call
|
|
NlEqResultsBuf(state,x,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| NLEQ solver results |
|
|
//| Buffered implementation of NLEQResults(), which uses |
|
|
//| pre-allocated buffer to store X[]. If buffer size is too small, |
|
|
//| it resizes buffer. It is intended to be used in the inner cycles |
|
|
//| of performance critical algorithms where array reallocation |
|
|
//| penalty is too large to be ignored. |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEq::NlEqResultsBuf(CNlEqState &state,double &x[],CNlEqReport &rep)
|
|
{
|
|
//--- check
|
|
if(CAp::Len(x)<state.m_n)
|
|
ArrayResize(x,state.m_n);
|
|
//--- copy
|
|
for(int i_=0; i_<state.m_n; i_++)
|
|
x[i_]=state.m_xbase[i_];
|
|
//--- change values
|
|
rep.m_iterationscount=state.m_repiterationscount;
|
|
rep.m_nfunc=state.m_repnfunc;
|
|
rep.m_njac=state.m_repnjac;
|
|
rep.m_terminationtype=state.m_repterminationtype;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine restarts CG algorithm from new point. All |
|
|
//| optimization parameters are left unchanged. |
|
|
//| This function allows to solve multiple optimization problems |
|
|
//| (which must have same number of dimensions) without object |
|
|
//| reallocation penalty. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure used for reverse communication |
|
|
//| previously allocated with MinCGCreate call. |
|
|
//| X - new starting point. |
|
|
//| BndL - new lower bounds |
|
|
//| BndU - new upper bounds |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEq::NlEqRestartFrom(CNlEqState &state,double &x[])
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=state.m_n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,state.m_n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- copy
|
|
for(int i_=0; i_<state.m_n; i_++)
|
|
state.m_x[i_]=x[i_];
|
|
//--- allocation
|
|
state.m_rstate.ia.Resize(3);
|
|
ArrayResizeAL(state.m_rstate.ba,1);
|
|
state.m_rstate.ra.Resize(6);
|
|
state.m_rstate.stage=-1;
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Clears request fileds (to be sure that we don't forgot to clear |
|
|
//| something) |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEq::ClearRequestFields(CNlEqState &state)
|
|
{
|
|
//--- change values
|
|
state.m_needf=false;
|
|
state.m_needfij=false;
|
|
state.m_xupdated=false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Increases lambda,returns False when there is a danger of |
|
|
//| overflow |
|
|
//+------------------------------------------------------------------+
|
|
bool CNlEq::IncreaseLambda(double &lambdav,double &nu,const double lambdaup)
|
|
{
|
|
//--- create variables
|
|
double lnlambda=MathLog(lambdav);
|
|
double lnlambdaup=MathLog(lambdaup);
|
|
double lnnu=MathLog(nu);
|
|
double lnmax=0.5*MathLog(CMath::m_maxrealnumber);
|
|
//--- check
|
|
if(lnlambda+lnlambdaup+lnnu>lnmax)
|
|
return(false);
|
|
//--- check
|
|
if(lnnu+MathLog(2)>lnmax)
|
|
return(false);
|
|
//--- change values
|
|
lambdav=lambdav*lambdaup*nu;
|
|
nu=nu*2;
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Decreases lambda, but leaves it unchanged when there is danger of|
|
|
//| underflow. |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEq::DecreaseLambda(double &lambdav,double &nu,const double lambdadown)
|
|
{
|
|
//--- initialization
|
|
nu=1;
|
|
//--- check
|
|
if(MathLog(lambdav)+MathLog(lambdadown)<MathLog(CMath::m_minrealnumber))
|
|
lambdav=CMath::m_minrealnumber;
|
|
else
|
|
lambdav=lambdav*lambdadown;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CNlEq::NlEqIteration(CNlEqState &state)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
double lambdaup=0;
|
|
double lambdadown=0;
|
|
double lambdav=0;
|
|
double rho=0;
|
|
double mu=0;
|
|
double stepnorm=0;
|
|
bool b;
|
|
int i_=0;
|
|
int temp;
|
|
//--- This code initializes locals by:
|
|
//--- * random values determined during code
|
|
//--- generation - on first subroutine call
|
|
//--- * values from previous call - on subsequent calls
|
|
if(state.m_rstate.stage>=0)
|
|
{
|
|
//--- initialization
|
|
n=state.m_rstate.ia[0];
|
|
m=state.m_rstate.ia[1];
|
|
i=state.m_rstate.ia[2];
|
|
b=state.m_rstate.ba[0];
|
|
lambdaup=state.m_rstate.ra[0];
|
|
lambdadown=state.m_rstate.ra[1];
|
|
lambdav=state.m_rstate.ra[2];
|
|
rho=state.m_rstate.ra[3];
|
|
mu=state.m_rstate.ra[4];
|
|
stepnorm=state.m_rstate.ra[5];
|
|
}
|
|
else
|
|
{
|
|
//--- initialization
|
|
n=-983;
|
|
m=-989;
|
|
i=-834;
|
|
b=false;
|
|
lambdaup=-287;
|
|
lambdadown=364;
|
|
lambdav=214;
|
|
rho=-338;
|
|
mu=-686;
|
|
stepnorm=912;
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==0)
|
|
{
|
|
//--- change values
|
|
state.m_needf=false;
|
|
state.m_repnfunc=state.m_repnfunc+1;
|
|
//--- copy
|
|
for(i_=0; i_<n; i_++)
|
|
state.m_xbase[i_]=state.m_x[i_];
|
|
//--- change values
|
|
state.m_fbase=state.m_f;
|
|
state.m_fprev=CMath::m_maxrealnumber;
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
{
|
|
//--- check
|
|
if(!Func_case_5(state,lambdaup,lambdadown,lambdav,rho))
|
|
return(false);
|
|
//--- function call
|
|
Func_case_7(state,n);
|
|
//--- Saving state
|
|
Func_case_rcomm(state,n,m,i,b,lambdaup,lambdadown,lambdav,rho,mu,stepnorm);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- progress report
|
|
ClearRequestFields(state);
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=1;
|
|
//--- Saving state
|
|
Func_case_rcomm(state,n,m,i,b,lambdaup,lambdadown,lambdav,rho,mu,stepnorm);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==1)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- check
|
|
if(!Func_case_5(state,lambdaup,lambdadown,lambdav,rho))
|
|
return(false);
|
|
//--- function call
|
|
Func_case_7(state,n);
|
|
//--- Saving state
|
|
Func_case_rcomm(state,n,m,i,b,lambdaup,lambdadown,lambdav,rho,mu,stepnorm);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==2)
|
|
{
|
|
//--- change values
|
|
state.m_needfij=false;
|
|
state.m_repnfunc=state.m_repnfunc+1;
|
|
state.m_repnjac=state.m_repnjac+1;
|
|
//--- function call
|
|
CAblas::RMatrixMVect(n,m,state.m_j,0,0,1,state.m_fi,0,state.m_rightpart,0);
|
|
for(i_=0; i_<n; i_++)
|
|
state.m_rightpart[i_]=-1*state.m_rightpart[i_];
|
|
//--- Inner cycle: find good lambda
|
|
temp=Func_case_9(state,n,m,i,b,lambdaup,lambdadown,lambdav,rho,mu,stepnorm);
|
|
//--- check
|
|
if(temp==-1)
|
|
return(false);
|
|
//--- check
|
|
if(temp==1)
|
|
return(true);
|
|
//--- Saving state
|
|
Func_case_rcomm(state,n,m,i,b,lambdaup,lambdadown,lambdav,rho,mu,stepnorm);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==3)
|
|
{
|
|
//--- change values
|
|
state.m_needf=false;
|
|
state.m_repnfunc=state.m_repnfunc+1;
|
|
//--- check
|
|
if(state.m_f<state.m_fbase)
|
|
{
|
|
//--- function value decreased,move on
|
|
DecreaseLambda(lambdav,rho,lambdadown);
|
|
temp=Func_case_10(state,n,m,i,b,lambdaup,lambdadown,lambdav,rho,mu,stepnorm);
|
|
//--- check
|
|
if(temp==-1)
|
|
return(false);
|
|
//--- check
|
|
if(temp==1)
|
|
return(true);
|
|
//--- Saving state
|
|
Func_case_rcomm(state,n,m,i,b,lambdaup,lambdadown,lambdav,rho,mu,stepnorm);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(!IncreaseLambda(lambdav,rho,lambdaup))
|
|
{
|
|
//--- Lambda is too large (near overflow),force zero step and break
|
|
stepnorm=0;
|
|
for(i_=0; i_<n; i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
state.m_f=state.m_fbase;
|
|
temp=Func_case_10(state,n,m,i,b,lambdaup,lambdadown,lambdav,rho,mu,stepnorm);
|
|
//--- check
|
|
if(temp==-1)
|
|
return(false);
|
|
//--- check
|
|
if(temp==1)
|
|
return(true);
|
|
//--- Saving state
|
|
Func_case_rcomm(state,n,m,i,b,lambdaup,lambdadown,lambdav,rho,mu,stepnorm);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
temp=Func_case_9(state,n,m,i,b,lambdaup,lambdadown,lambdav,rho,mu,stepnorm);
|
|
//--- check
|
|
if(temp==-1)
|
|
return(false);
|
|
//--- check
|
|
if(temp==1)
|
|
return(true);
|
|
//--- Saving state
|
|
Func_case_rcomm(state,n,m,i,b,lambdaup,lambdadown,lambdav,rho,mu,stepnorm);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==4)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- check
|
|
if(!Func_case_11(state,stepnorm))
|
|
return(false);
|
|
//--- Now,iteration is finally over
|
|
Func_case_7(state,n);
|
|
//--- Saving state
|
|
Func_case_rcomm(state,n,m,i,b,lambdaup,lambdadown,lambdav,rho,mu,stepnorm);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- Routine body
|
|
//--- Prepare
|
|
n=state.m_n;
|
|
m=state.m_m;
|
|
state.m_repterminationtype=0;
|
|
state.m_repiterationscount=0;
|
|
state.m_repnfunc=0;
|
|
state.m_repnjac=0;
|
|
//--- Calculate F/G,initialize algorithm
|
|
ClearRequestFields(state);
|
|
state.m_needf=true;
|
|
state.m_rstate.stage=0;
|
|
//--- Saving state
|
|
Func_case_rcomm(state,n,m,i,b,lambdaup,lambdadown,lambdav,rho,mu,stepnorm);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for NlEqiteration. Is a product to get rid of |
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEq::Func_case_rcomm(CNlEqState &state,const int n,const int m,
|
|
const int i,const bool b,const double lambdaup,
|
|
const double lambdadown,const double lambdav,
|
|
const double rho,const double mu,const double stepnorm)
|
|
{
|
|
//--- save
|
|
state.m_rstate.ia.Set(0,n);
|
|
state.m_rstate.ia.Set(1,m);
|
|
state.m_rstate.ia.Set(2,i);
|
|
state.m_rstate.ba[0]=b;
|
|
state.m_rstate.ra.Set(0,lambdaup);
|
|
state.m_rstate.ra.Set(1,lambdadown);
|
|
state.m_rstate.ra.Set(2,lambdav);
|
|
state.m_rstate.ra.Set(3,rho);
|
|
state.m_rstate.ra.Set(4,mu);
|
|
state.m_rstate.ra.Set(5,stepnorm);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for NlEqiteration. Is a product to get rid of |
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
void CNlEq::Func_case_7(CNlEqState &state,const int n)
|
|
{
|
|
//--- Get Jacobian;
|
|
//--- before we get to this point we already have State.XBase filled
|
|
//--- with current point and State.FBase filled with function value
|
|
//--- at XBase
|
|
ClearRequestFields(state);
|
|
state.m_needfij=true;
|
|
//--- copy
|
|
for(int i_=0; i_<n; i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
state.m_rstate.stage=2;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for NlEqiteration. Is a product to get rid of |
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
bool CNlEq::Func_case_5(CNlEqState &state,double &lambdaup,
|
|
double &lambdadown,double &lambdav,
|
|
double &rho)
|
|
{
|
|
//--- check
|
|
if(state.m_f<=CMath::Sqr(state.m_epsf))
|
|
{
|
|
state.m_repterminationtype=1;
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
lambdaup=10;
|
|
lambdadown=0.3;
|
|
lambdav=0.001;
|
|
rho=1;
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for NlEqiteration. Is a product to get rid of |
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
bool CNlEq::Func_case_11(CNlEqState &state,const double stepnorm)
|
|
{
|
|
//--- Test stopping conditions on F,step (zero/non-zero) and MaxIts;
|
|
//--- If one of the conditions is met,RepTerminationType is changed.
|
|
if(MathSqrt(state.m_f)<=state.m_epsf)
|
|
state.m_repterminationtype=1;
|
|
//--- check
|
|
if(stepnorm==0.0 && state.m_repterminationtype==0)
|
|
state.m_repterminationtype=-4;
|
|
//--- check
|
|
if(state.m_repiterationscount>=state.m_maxits && state.m_maxits>0)
|
|
state.m_repterminationtype=5;
|
|
//--- check
|
|
if(state.m_repterminationtype!=0)
|
|
return(false);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for NlEqiteration. Is a product to get rid of |
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
int CNlEq::Func_case_10(CNlEqState &state,const int n,const int m,
|
|
const int i,const bool b,const double lambdaup,
|
|
const double lambdadown,const double lambdav,
|
|
const double rho,const double mu,
|
|
const double stepnorm)
|
|
{
|
|
//--- Accept step:
|
|
//--- * new position
|
|
//--- * new function value
|
|
state.m_fbase=state.m_f;
|
|
for(int i_=0; i_<n; i_++)
|
|
state.m_xbase[i_]=state.m_xbase[i_]+stepnorm*state.m_candstep[i_];
|
|
state.m_repiterationscount=state.m_repiterationscount+1;
|
|
//--- Report new iteration
|
|
if(!state.m_xrep)
|
|
{
|
|
//--- check
|
|
if(!Func_case_11(state,stepnorm))
|
|
return(-1);
|
|
//--- Now,iteration is finally over
|
|
Func_case_7(state,n);
|
|
//--- Saving state
|
|
Func_case_rcomm(state,n,m,i,b,lambdaup,lambdadown,lambdav,rho,mu,stepnorm);
|
|
//--- return result
|
|
return(1);
|
|
}
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_f=state.m_fbase;
|
|
//--- copy
|
|
for(int i_=0; i_<n; i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
state.m_rstate.stage=4;
|
|
//--- return result
|
|
return(0);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for NlEqiteration. Is a product to get rid of |
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
int CNlEq::Func_case_9(CNlEqState &state,int &n,int &m,int &i,bool &b,
|
|
const double lambdaup,const double lambdadown,
|
|
double &lambdav,const double rho,const double mu,
|
|
double &stepnorm)
|
|
{
|
|
//--- Solve (J^T*J + (Lambda+Mu)*I)*y=J^T*F
|
|
//--- to get step d=-y where:
|
|
//--- * Mu=||F|| - is damping parameter for nonlinear system
|
|
//--- * Lambda - is additional Levenberg-Marquardt parameter
|
|
//--- for better convergence when far away from minimum
|
|
for(i=0; i<n; i++)
|
|
state.m_candstep[i]=0;
|
|
//--- function call
|
|
CFbls::FblsSolveCGx(state.m_j,m,n,lambdav,state.m_rightpart,state.m_candstep,state.m_cgbuf);
|
|
//--- Normalize step (it must be no more than StpMax)
|
|
stepnorm=0;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(state.m_candstep[i]!=0.0)
|
|
{
|
|
stepnorm=1;
|
|
break;
|
|
}
|
|
}
|
|
CLinMin::LinMinNormalized(state.m_candstep,stepnorm,n);
|
|
//--- check
|
|
if(state.m_stpmax!=0.0)
|
|
stepnorm=MathMin(stepnorm,state.m_stpmax);
|
|
//--- Test new step - is it good enough?
|
|
//--- * if not,Lambda is increased and we try again.
|
|
//--- * if step is good,we decrease Lambda and move on.
|
|
//--- We can break this cycle on two occasions:
|
|
//--- * step is so small that x+step==x (in floating point arithmetics)
|
|
//--- * lambda is so large
|
|
for(int i_=0; i_<n; i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
for(int i_=0; i_<n; i_++)
|
|
state.m_x[i_]=state.m_x[i_]+stepnorm*state.m_candstep[i_];
|
|
b=true;
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(state.m_x[i]!=state.m_xbase[i])
|
|
{
|
|
b=false;
|
|
break;
|
|
}
|
|
}
|
|
//--- check
|
|
if(b)
|
|
{
|
|
//--- Step is too small,force zero step and break
|
|
stepnorm=0;
|
|
for(int i_=0; i_<n; i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
state.m_f=state.m_fbase;
|
|
//--- function call
|
|
int temp=Func_case_10(state,n,m,i,b,lambdaup,lambdadown,lambdav,rho,mu,stepnorm);
|
|
//--- check
|
|
if(temp!=0)
|
|
return(temp);
|
|
//--- Saving state
|
|
Func_case_rcomm(state,n,m,i,b,lambdaup,lambdadown,lambdav,rho,mu,stepnorm);
|
|
//--- return result
|
|
return(1);
|
|
}
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_needf=true;
|
|
state.m_rstate.stage=3;
|
|
//--- return result
|
|
return(0);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
struct CPolynomialSolverReport
|
|
{
|
|
double m_maxerr;
|
|
//--- constructor / destructor
|
|
CPolynomialSolverReport(void) { m_maxerr=0; }
|
|
~CPolynomialSolverReport(void) {}
|
|
void Copy(const CPolynomialSolverReport &obj) { m_maxerr=obj.m_maxerr; }
|
|
//--- overloading
|
|
void operator=(const CPolynomialSolverReport &obj) { Copy(obj); }
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CPolynomialSolver
|
|
{
|
|
public:
|
|
static void PolynomialSolve(CRowDouble &a,int n,CRowComplex &x,CPolynomialSolverReport &rep);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Polynomial root finding. |
|
|
//| This function returns all roots of the polynomial |
|
|
//| P(x) = a0 + a1*x + a2*x^2 + ... + an*x^n |
|
|
//| Both real and complex roots are returned (see below). |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - array[N+1], polynomial coefficients: |
|
|
//| * A[0] is constant term |
|
|
//| * A[N] is a coefficient of X^N |
|
|
//| N - polynomial degree |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array of complex roots: |
|
|
//| * for isolated real root, X[I] is strictly real: |
|
|
//| IMAGE(X[I])=0 |
|
|
//| * complex roots are always returned in pairs-roots |
|
|
//| occupy positions I and I+1, with: |
|
|
//| * X[I+1]=Conj(X[I]) |
|
|
//| * IMAGE(X[I]) > 0 |
|
|
//| * IMAGE(X[I+1]) = -IMAGE(X[I]) < 0 |
|
|
//| * multiple real roots may have non-zero imaginary |
|
|
//| part due to roundoff errors. There is no reliable|
|
|
//| way to distinguish real root of multiplicity 2 |
|
|
//| from two complex roots in the presence of |
|
|
//| roundoff errors. |
|
|
//| Rep - report, additional information, following fields |
|
|
//| are set: |
|
|
//| * Rep.MaxErr - max( |P(xi)| ) for i=0..N-1. This |
|
|
//| field allows to quickly estimate "quality" of the|
|
|
//| roots being returned. |
|
|
//| NOTE: this function uses companion matrix method to find roots. |
|
|
//| In case internal EVD solver fails do find eigenvalues, |
|
|
//| exception is generated. |
|
|
//| NOTE: roots are not "polished" and no matrix balancing is |
|
|
//| performed for them. |
|
|
//+------------------------------------------------------------------+
|
|
void CPolynomialSolver::PolynomialSolve(CRowDouble &A,int n,
|
|
CRowComplex &x,
|
|
CPolynomialSolverReport &rep)
|
|
{
|
|
//--- create variables
|
|
CMatrixDouble c;
|
|
CMatrixDouble vl;
|
|
CMatrixDouble vr;
|
|
CRowDouble wr;
|
|
CRowDouble wi;
|
|
int i=0;
|
|
int j=0;
|
|
bool status=false;
|
|
int nz=0;
|
|
int ne=0;
|
|
complex v=0;
|
|
complex vv=0;
|
|
CRowDouble a=A;
|
|
x.Resize(0);
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0"))
|
|
return;
|
|
if(!CAp::Assert(CAp::Len(a)>n,__FUNCTION__+": Length(A)<N+1"))
|
|
return;
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(a,n+1),__FUNCTION__+": A contains infitite numbers"))
|
|
return;
|
|
if(!CAp::Assert(a[n]!=0.0,__FUNCTION__+": A[N]=0"))
|
|
return;
|
|
//--- Prepare
|
|
x.Resize(n);
|
|
//--- Normalize A:
|
|
//--- * analytically determine NZ zero roots
|
|
//--- * quick exit for NZ=N
|
|
//--- * make residual NE-th degree polynomial monic
|
|
//--- (here NE=N-NZ)
|
|
nz=0;
|
|
while(nz<n && a[nz]==0.0)
|
|
nz++;
|
|
ne=n-nz;
|
|
for(i=nz; i<=n; i++)
|
|
a.Set(i-nz,a[i]/a[n]);
|
|
//--- For NZ<N, build companion matrix and find NE non-zero roots
|
|
if(ne>0)
|
|
{
|
|
c=matrix<double>::Zeros(ne,ne);
|
|
c.Set(0,ne-1,-a[0]);
|
|
for(i=1; i<ne; i++)
|
|
{
|
|
c.Set(i,i-1,1);
|
|
c.Set(i,ne-1,-a[i]);
|
|
}
|
|
status=CEigenVDetect::RMatrixEVD(c,ne,0,wr,wi,vl,vr);
|
|
//--- check
|
|
if(!CAp::Assert(status,__FUNCTION__+": inernal error - EVD solver failed"))
|
|
return;
|
|
for(i=0; i<ne; i++)
|
|
{
|
|
x.SetRe(i,wr[i]);
|
|
x.SetIm(i,wi[i]);
|
|
}
|
|
}
|
|
//--- Remaining NZ zero roots
|
|
for(i=ne; i<n; i++)
|
|
x.Set(i,0.0);
|
|
//--- Rep
|
|
rep.m_maxerr=0;
|
|
for(i=0; i<ne; i++)
|
|
{
|
|
v=0;
|
|
vv=1;
|
|
for(j=0; j<=ne; j++)
|
|
{
|
|
v=v+a[j]*vv;
|
|
vv=vv*x[i];
|
|
}
|
|
rep.m_maxerr=MathMax(rep.m_maxerr,CMath::AbsComplex(v));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This structure is a sparse solver report (both direct and |
|
|
//| iterative solvers use this structure). |
|
|
//| Following fields can be accessed by users: |
|
|
//| * TerminationType (specific error codes depend on the solver |
|
|
//| being used, with positive values ALWAYS signaling that |
|
|
//| something useful is returned in X, and negative values |
|
|
//| ALWAYS meaning critical failures. |
|
|
//| * NMV - number of matrix - vector products performed (0 for |
|
|
//| direct solvers) |
|
|
//| * IterationsCount - inner iterations count (0 for direct |
|
|
//| solvers) |
|
|
//| * R2 - squared residual |
|
|
//+------------------------------------------------------------------+
|
|
struct CSparseSolverReport
|
|
{
|
|
int m_iterationscount;
|
|
int m_nmv;
|
|
int m_terminationtype;
|
|
double m_r2;
|
|
//--- constructor / destructor
|
|
CSparseSolverReport(void) { ZeroMemory(this); }
|
|
~CSparseSolverReport(void) {}
|
|
//---
|
|
void Copy(const CSparseSolverReport &obj)
|
|
{
|
|
m_iterationscount=obj.m_iterationscount;
|
|
m_nmv=obj.m_nmv;
|
|
m_terminationtype=obj.m_terminationtype;
|
|
m_r2=obj.m_r2;
|
|
}
|
|
//--- overloading
|
|
void operator=(const CSparseSolverReport &obj) { Copy(obj); }
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CDirectSparseSolvers
|
|
{
|
|
public:
|
|
static void SparseSPDSolveSKS(CSparseMatrix &a,bool IsUpper,CRowDouble &b,CRowDouble &x,CSparseSolverReport &rep);
|
|
static void SparseSPDSolve(CSparseMatrix &a,bool IsUpper,CRowDouble &b,CRowDouble &x,CSparseSolverReport &rep);
|
|
static void SparseSPDCholeskySolve(CSparseMatrix &a,bool IsUpper,CRowDouble &b,CRowDouble &x,CSparseSolverReport &rep);
|
|
static void SparseSolve(CSparseMatrix &a,CRowDouble &b,CRowDouble &x,CSparseSolverReport &rep);
|
|
static void SparseLUSolve(CSparseMatrix &a,CRowInt &p,CRowInt &q,CRowDouble &b,CRowDouble &x,CSparseSolverReport &rep);
|
|
static void InitSparseSolverReport(CSparseSolverReport &rep);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Sparse linear solver for A*x = b with N*N sparse real symmetric |
|
|
//| positive definite matrix A, N * 1 vectors x and b. |
|
|
//| This solver converts input matrix to SKS format, performs |
|
|
//| Cholesky factorization using SKS Cholesky subroutine (works well |
|
|
//| for limited bandwidth matrices) and uses sparse triangular |
|
|
//| solvers to get solution of the original system. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - sparse matrix, must be NxN exactly |
|
|
//| IsUpper - which half of A is provided (another half is |
|
|
//| ignored) |
|
|
//| B - array[0..N - 1], right part |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[N], it contains: |
|
|
//| * rep.m_terminationtype > 0 => solution |
|
|
//| * rep.m_terminationtype = -3 => filled by zeros |
|
|
//| Rep - solver report, following fields are set: |
|
|
//| * rep.m_terminationtype - solver status; > 0 for |
|
|
//| success, set to - 3 on |
|
|
//| failure (degenerate or |
|
|
//| non-SPD system). |
|
|
//+------------------------------------------------------------------+
|
|
void CDirectSparseSolvers::SparseSPDSolveSKS(CSparseMatrix &a,
|
|
bool IsUpper,
|
|
CRowDouble &b,
|
|
CRowDouble &x,
|
|
CSparseSolverReport &rep)
|
|
{
|
|
//--- create variables
|
|
CSparseMatrix a2 ;
|
|
int n=CSparse::SparseGetNRows(a);
|
|
|
|
x.Resize(0);
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseGetNRows(a)==n,__FUNCTION__+": rows(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseGetNCols(a)==n,__FUNCTION__+": cols(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CAp::Len(b)>=n,__FUNCTION__+": length(B)<N"))
|
|
return;
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(b,n),__FUNCTION__+": B contains infinities or NANs"))
|
|
return;
|
|
|
|
InitSparseSolverReport(rep);
|
|
CSparse::SparseCopyToSKS(a,a2);
|
|
if(!CTrFac::SparseCholeskySkyLine(a2,n,IsUpper))
|
|
{
|
|
rep.m_terminationtype=-3;
|
|
x=vector<double>::Zeros(n);
|
|
return;
|
|
}
|
|
|
|
x=b;
|
|
x.Resize(n);
|
|
if(IsUpper)
|
|
{
|
|
CSparse::SparseTRSV(a2,IsUpper,false,1,x);
|
|
CSparse::SparseTRSV(a2,IsUpper,false,0,x);
|
|
}
|
|
else
|
|
{
|
|
CSparse::SparseTRSV(a2,IsUpper,false,0,x);
|
|
CSparse::SparseTRSV(a2,IsUpper,false,1,x);
|
|
}
|
|
|
|
rep.m_terminationtype=1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sparse linear solver for A*x = b with N*N sparse real symmetric |
|
|
//| positive definite matrix A, N * 1 vectors x and b. |
|
|
//| This solver converts input matrix to CRS format, performs |
|
|
//| Cholesky factorization using supernodal Cholesky decomposition |
|
|
//| with permutation-reducing ordering and uses sparse triangular |
|
|
//| solver to get solution of the original system. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - sparse matrix, must be NxN exactly |
|
|
//| IsUpper - which half of A is provided (another half is |
|
|
//| ignored) |
|
|
//| B - array[N], right part |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[N], it contains: |
|
|
//| * rep.m_terminationtype > 0 => solution |
|
|
//| * rep.m_terminationtype = -3 => filled by zeros |
|
|
//| Rep - solver report, following fields are set: |
|
|
//| * rep.m_terminationtype - solver status; > 0 for |
|
|
//| success, set to - 3 on |
|
|
//| failure (degenerate or |
|
|
//| non-SPD system). |
|
|
//+------------------------------------------------------------------+
|
|
void CDirectSparseSolvers::SparseSPDSolve(CSparseMatrix &a,
|
|
bool IsUpper,
|
|
CRowDouble &b,
|
|
CRowDouble &x,
|
|
CSparseSolverReport &rep)
|
|
{
|
|
//--- create variables
|
|
int n=CSparse::SparseGetNRows(a);
|
|
double v=0;
|
|
CSparseMatrix a2;
|
|
CRowInt p;
|
|
|
|
x.Resize(0);
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseGetNRows(a)==n,__FUNCTION__+": rows(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseGetNCols(a)==n,__FUNCTION__+": cols(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CAp::Len(b)>=n,__FUNCTION__+": length(B)<N"))
|
|
return;
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(b,n),__FUNCTION__+": B contains infinities or NANs"))
|
|
return;
|
|
|
|
InitSparseSolverReport(rep);
|
|
CSparse::SparseCopyToCRS(a,a2);
|
|
if(!CTrFac::SparseCholeskyP(a2,IsUpper,p))
|
|
{
|
|
rep.m_terminationtype=-3;
|
|
x=vector<double>::Zeros(n);
|
|
return;
|
|
}
|
|
CAblasF::RCopyAllocV(n,b,x);
|
|
|
|
for(int i=0; i<n; i++)
|
|
x.Swap(i,p[i]);
|
|
if(IsUpper)
|
|
{
|
|
CSparse::SparseTRSV(a2,IsUpper,false,1,x);
|
|
CSparse::SparseTRSV(a2,IsUpper,false,0,x);
|
|
}
|
|
else
|
|
{
|
|
CSparse::SparseTRSV(a2,IsUpper,false,0,x);
|
|
CSparse::SparseTRSV(a2,IsUpper,false,1,x);
|
|
}
|
|
for(int i=n-1; i>=0; i--)
|
|
x.Swap(i,p[i]);
|
|
|
|
rep.m_terminationtype=1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sparse linear solver for A*x = b with N*N real symmetric positive|
|
|
//| definite matrix A given by its Cholesky decomposition, and N * 1 |
|
|
//| vectors x and b. |
|
|
//| IMPORTANT: this solver requires input matrix to be in the SKS |
|
|
//| (Skyline) or CRS (compressed row storage) format. An |
|
|
//| exception will be generated if you pass matrix in some|
|
|
//| other format. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - sparse NxN matrix stored in CRs or SKS format, must|
|
|
//| be NxN exactly |
|
|
//| IsUpper - which half of A is provided (another half is |
|
|
//| ignored) |
|
|
//| B - array[N], right part |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[N], it contains: |
|
|
//| * rep.m_terminationtype > 0 => solution |
|
|
//| * rep.m_terminationtype = -3 => filled by zeros |
|
|
//| Rep - solver report, following fields are set: |
|
|
//| * rep.m_terminationtype - solver status; > 0 for |
|
|
//| success, set to - 3 on |
|
|
//| failure (degenerate or |
|
|
//| non-SPD system). |
|
|
//+------------------------------------------------------------------+
|
|
void CDirectSparseSolvers::SparseSPDCholeskySolve(CSparseMatrix &a,
|
|
bool IsUpper,
|
|
CRowDouble &b,
|
|
CRowDouble &x,
|
|
CSparseSolverReport &rep)
|
|
{
|
|
int n=CSparse::SparseGetNRows(a);
|
|
|
|
x.Resize(0);
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseGetNRows(a)==n,__FUNCTION__+": rows(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseGetNCols(a)==n,__FUNCTION__+": cols(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseIsSKS(a) || CSparse::SparseIsCRS(a),__FUNCTION__+": A is not an SKS/CRS matrix"))
|
|
return;
|
|
if(!CAp::Assert(CAp::Len(b)>=n,__FUNCTION__+": length(B)<N"))
|
|
return;
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(b,n),__FUNCTION__+": B contains infinities or NANs"))
|
|
return;
|
|
|
|
InitSparseSolverReport(rep);
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
if(CSparse::SparseGet(a,i,i)==0.0)
|
|
{
|
|
rep.m_terminationtype=-3;
|
|
x=vector<double>::Zeros(n);
|
|
return;
|
|
}
|
|
}
|
|
|
|
x=b;
|
|
x.Resize(n);
|
|
if(IsUpper)
|
|
{
|
|
CSparse::SparseTRSV(a,IsUpper,false,1,x);
|
|
CSparse::SparseTRSV(a,IsUpper,false,0,x);
|
|
}
|
|
else
|
|
{
|
|
CSparse::SparseTRSV(a,IsUpper,false,0,x);
|
|
CSparse::SparseTRSV(a,IsUpper,false,1,x);
|
|
}
|
|
|
|
rep.m_terminationtype=1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sparse linear solver for A*x = b with general (nonsymmetric) N*N |
|
|
//| sparse real matrix A, N * 1 vectors x and b. |
|
|
//| This solver converts input matrix to CRS format, performs LU |
|
|
//| factorization and uses sparse triangular solvers to get solution |
|
|
//| of the original system. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - sparse matrix, must be NxN exactly, any storage |
|
|
//| format |
|
|
//| N - size of A, N > 0 |
|
|
//| B - array[0..N - 1], right part |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[N], it contains: |
|
|
//| * rep.m_terminationtype > 0 => solution |
|
|
//| * rep.m_terminationtype = -3 => filled by zeros |
|
|
//| Rep - solver report, following fields are set: |
|
|
//| * rep.m_terminationtype - solver status; > 0 for |
|
|
//| success, set to - 3 on failure (degenerate |
|
|
//| system). |
|
|
//+------------------------------------------------------------------+
|
|
void CDirectSparseSolvers::SparseSolve(CSparseMatrix &a,
|
|
CRowDouble &b,
|
|
CRowDouble &x,
|
|
CSparseSolverReport &rep)
|
|
{
|
|
//--- create variables
|
|
int n=CSparse::SparseGetNRows(a);
|
|
double v=0;
|
|
CSparseMatrix a2;
|
|
CRowInt pivp;
|
|
CRowInt pivq;
|
|
|
|
x.Resize(0);
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseGetNRows(a)==n,__FUNCTION__+": rows(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseGetNCols(a)==n,__FUNCTION__+": cols(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CAp::Len(b)>=n,__FUNCTION__+": length(B)<N"))
|
|
return;
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(b,n),__FUNCTION__+": B contains infinities or NANs"))
|
|
return;
|
|
|
|
InitSparseSolverReport(rep);
|
|
CSparse::SparseCopyToCRS(a,a2);
|
|
if(!CTrFac::SparseLU(a2,0,pivp,pivq))
|
|
{
|
|
rep.m_terminationtype=-3;
|
|
x=vector<double>::Zeros(n);
|
|
return;
|
|
}
|
|
|
|
x=b;
|
|
x.Resize(n);
|
|
for(int i=0; i<n; i++)
|
|
x.Swap(i,pivp[i]);
|
|
CSparse::SparseTRSV(a2,false,true,0,x);
|
|
CSparse::SparseTRSV(a2,true,false,0,x);
|
|
for(int i=n-1; i>=0; i--)
|
|
x.Swap(i,pivq[i]);
|
|
|
|
rep.m_terminationtype=1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sparse linear solver for A*x = b with general (nonsymmetric) N*N |
|
|
//| sparse real matrix A given by its LU factorization, N*1 vectors x|
|
|
//| and b. |
|
|
//| IMPORTANT: this solver requires input matrix to be in the CRS |
|
|
//| sparse storage format. An exception will be generated |
|
|
//| if you pass matrix in some other format (HASH or SKS).|
|
|
//| INPUT PARAMETERS: |
|
|
//| A - LU factorization of the sparse matrix, must be NxN |
|
|
//| exactly in CRS storage format |
|
|
//| P, Q - pivot indexes from LU factorization |
|
|
//| N - size of A, N > 0 |
|
|
//| B - array[0..N - 1], right part |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[N], it contains: |
|
|
//| * rep.m_terminationtype > 0 => solution |
|
|
//| * rep.m_terminationtype = -3 => filled by zeros |
|
|
//| Rep - solver report, following fields are set: |
|
|
//| * rep.m_terminationtype - solver status; > 0 for |
|
|
//| success, set to - 3 on |
|
|
//| failure (degenerate |
|
|
//| system). |
|
|
//+------------------------------------------------------------------+
|
|
void CDirectSparseSolvers::SparseLUSolve(CSparseMatrix &a,
|
|
CRowInt &p,
|
|
CRowInt &q,
|
|
CRowDouble &b,
|
|
CRowDouble &x,
|
|
CSparseSolverReport &rep)
|
|
{
|
|
//--- create variables
|
|
int n=CSparse::SparseGetNRows(a);
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
|
|
x.Resize(0);
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseGetNRows(a)==n,__FUNCTION__+": rows(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseGetNCols(a)==n,__FUNCTION__+": cols(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseIsCRS(a),__FUNCTION__+": A is not an SKS matrix"))
|
|
return;
|
|
if(!CAp::Assert(CAp::Len(b)>=n,__FUNCTION__+": length(B)<N"))
|
|
return;
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(b,n),__FUNCTION__+": B contains infinities or NANs"))
|
|
return;
|
|
if(!CAp::Assert(CAp::Len(p)>=n,__FUNCTION__+": length(P)<N"))
|
|
return;
|
|
if(!CAp::Assert(CAp::Len(q)>=n,__FUNCTION__+": length(Q)<N"))
|
|
return;
|
|
|
|
for(i=0; i<n; i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(p[i]>=i && p[i]<n,__FUNCTION__+": P is corrupted"))
|
|
return;
|
|
if(!CAp::Assert(q[i]>=i && q[i]<n,__FUNCTION__+": Q is corrupted"))
|
|
return;
|
|
}
|
|
InitSparseSolverReport(rep);
|
|
x.Resize(n);
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(a.m_DIdx[i]==a.m_UIdx[i] || a.m_Vals[a.m_DIdx[i]]==0.0)
|
|
{
|
|
rep.m_terminationtype=-3;
|
|
x=vector<double>::Zeros(n);
|
|
return;
|
|
}
|
|
}
|
|
|
|
x=b;
|
|
x.Resize(n);
|
|
for(i=0; i<n; i++)
|
|
x.Swap(i,p[i]);
|
|
CSparse::SparseTRSV(a,false,true,0,x);
|
|
CSparse::SparseTRSV(a,true,false,0,x);
|
|
for(i=n-1; i>=0; i--)
|
|
x.Swap(i,q[i]);
|
|
rep.m_terminationtype=1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reset report fields |
|
|
//+------------------------------------------------------------------+
|
|
void CDirectSparseSolvers::InitSparseSolverReport(CSparseSolverReport &rep)
|
|
{
|
|
rep.m_terminationtype=0;
|
|
rep.m_nmv=0;
|
|
rep.m_iterationscount=0;
|
|
rep.m_r2=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This object stores state of the sparse linear solver object. |
|
|
//| You should use ALGLIB functions to work with this object. |
|
|
//| Never try to access its fields directly! |
|
|
//+------------------------------------------------------------------+
|
|
struct CSparseSolverState
|
|
{
|
|
int m_algotype;
|
|
int m_gmresk;
|
|
int m_maxits;
|
|
int m_n;
|
|
int m_repiterationscount;
|
|
int m_repnmv;
|
|
int m_repterminationtype;
|
|
int m_requesttype;
|
|
double m_epsf;
|
|
double m_reply1;
|
|
double m_repr2;
|
|
bool m_running;
|
|
bool m_userterminationneeded;
|
|
bool m_xrep;
|
|
RCommState m_rstate;
|
|
CSparseMatrix m_convbuf;
|
|
CRowDouble m_ax;
|
|
CRowDouble m_b;
|
|
CRowDouble m_wrkb;
|
|
CRowDouble m_x0;
|
|
CRowDouble m_x;
|
|
CRowDouble m_xf;
|
|
CFblsGMRESState m_gmressolver;
|
|
//--- constructor / destructor
|
|
CSparseSolverState(void);
|
|
~CSparseSolverState(void) {}
|
|
//---
|
|
void Copy(const CSparseSolverState &obj);
|
|
//--- overloading
|
|
void operator=(const CSparseSolverState &obj) { Copy(obj); }
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor |
|
|
//+------------------------------------------------------------------+
|
|
CSparseSolverState::CSparseSolverState(void)
|
|
{
|
|
m_algotype=0;
|
|
m_gmresk=0;
|
|
m_maxits=0;
|
|
m_n=0;
|
|
m_repiterationscount=0;
|
|
m_repnmv=0;
|
|
m_repterminationtype=0;
|
|
m_requesttype=0;
|
|
m_epsf=0;
|
|
m_reply1=0;
|
|
m_repr2=0;
|
|
m_running=false;
|
|
m_userterminationneeded=false;
|
|
m_xrep=false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void CSparseSolverState::Copy(const CSparseSolverState &obj)
|
|
{
|
|
m_rstate=obj.m_rstate;
|
|
m_algotype=obj.m_algotype;
|
|
m_gmresk=obj.m_gmresk;
|
|
m_maxits=obj.m_maxits;
|
|
m_n=obj.m_n;
|
|
m_repiterationscount=obj.m_repiterationscount;
|
|
m_repnmv=obj.m_repnmv;
|
|
m_repterminationtype=obj.m_repterminationtype;
|
|
m_requesttype=obj.m_requesttype;
|
|
m_epsf=obj.m_epsf;
|
|
m_reply1=obj.m_reply1;
|
|
m_repr2=obj.m_repr2;
|
|
m_running=obj.m_running;
|
|
m_userterminationneeded=obj.m_userterminationneeded;
|
|
m_xrep=obj.m_xrep;
|
|
m_convbuf=obj.m_convbuf;
|
|
m_ax=obj.m_ax;
|
|
m_b=obj.m_b;
|
|
m_wrkb=obj.m_wrkb;
|
|
m_x0=obj.m_x0;
|
|
m_x=obj.m_x;
|
|
m_xf=obj.m_xf;
|
|
m_gmressolver=obj.m_gmressolver;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CIterativeSparse
|
|
{
|
|
public:
|
|
static void SparseSolveSymmetricGMRES(CSparseMatrix &a,bool IsUpper,CRowDouble &b,int k,double epsf,int maxits,CRowDouble &x,CSparseSolverReport &rep);
|
|
static void SparseSolveGMRES(CSparseMatrix &a,CRowDouble &b,int k,double epsf,int maxits,CRowDouble &x,CSparseSolverReport &rep);
|
|
static void SparseSolverCreate(int n,CSparseSolverState &state);
|
|
static void SparseSolverSetAlgoGMRES(CSparseSolverState &state,int k);
|
|
static void SparseSolverSetStartingPoint(CSparseSolverState &state,CRowDouble &x);
|
|
static void SparseSolverSetCond(CSparseSolverState &state,double epsf,int maxits);
|
|
static void SparseSolverSolveSymmetric(CSparseSolverState &state,CSparseMatrix &a,bool IsUpper,CRowDouble &b);
|
|
static void SparseSolverSolve(CSparseSolverState &state,CSparseMatrix &a,CRowDouble &b);
|
|
static void SparseSolverResults(CSparseSolverState &state,CRowDouble &x,CSparseSolverReport &rep);
|
|
static void SparseSolverSetXRep(CSparseSolverState &state,bool needxrep);
|
|
static void SparseSolverOOCStart(CSparseSolverState &state,CRowDouble &b);
|
|
static bool SparseSolverOOCContinue(CSparseSolverState &state);
|
|
static void SparseSolverOOCGetRequestInfo(CSparseSolverState &state,int &requesttype);
|
|
static void SparseSolverOOCGetRequestData(CSparseSolverState &state,CRowDouble &x);
|
|
static void SparseSolverOOCGetRequestData1(CSparseSolverState &state,double &v);
|
|
static void SparseSolverOOCSendResult(CSparseSolverState &state,CRowDouble &ax);
|
|
static void SparseSolverOOCStop(CSparseSolverState &state,CRowDouble &x,CSparseSolverReport &rep);
|
|
static void SparseSolverRequestTermination(CSparseSolverState &state);
|
|
static bool SparseSolverIteration(CSparseSolverState &state);
|
|
|
|
private:
|
|
static void ClearRequestFields(CSparseSolverState &state);
|
|
static void ClearReportFields(CSparseSolverState &state);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Solving sparse symmetric linear system A*x = b using GMRES(k) |
|
|
//| method. Sparse symmetric A is given by its lower or upper |
|
|
//| triangle. |
|
|
//| NOTE: use SparseSolveGMRES() to solve system with nonsymmetric A.|
|
|
//| This function provides convenience API for an 'expert' interface |
|
|
//| provided by SparseSolverState class. Use SparseSolver API if you |
|
|
//| need advanced functions like providing initial point, using |
|
|
//| out-of-core API and so on. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - sparse symmetric NxN matrix in any sparse storage |
|
|
//| format. Using CRS format is recommended because it |
|
|
//| avoids internal conversion. An exception will be |
|
|
//| generated if A is not NxN matrix (where N is a size|
|
|
//| specified during solver object creation). |
|
|
//| IsUpper - whether upper or lower triangle of A is used: |
|
|
//| * IsUpper = True => only upper triangle is used and|
|
|
//| lower triangle is not referenced at all |
|
|
//| * IsUpper = False => only lower triangle is used |
|
|
//| and upper triangle is not referenced at all |
|
|
//| B - right part, array[N] |
|
|
//| K - k parameter for GMRES(k), k >= 0. Zero value means |
|
|
//| that algorithm will choose it automatically. |
|
|
//| EpsF - stopping condition, EpsF >= 0. The algorithm will |
|
|
//| stop when residual will decrease below EpsF* | B |.|
|
|
//| Having EpsF = 0 means that this stopping condition |
|
|
//| is ignored. |
|
|
//| MaxIts - stopping condition, MaxIts >= 0. The algorithm will|
|
|
//| stop after performing MaxIts iterations. Zero value|
|
|
//| means no limit. |
|
|
//| NOTE: having both EpsF = 0 and MaxIts = 0 means that stopping |
|
|
//| criteria will be chosen automatically. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[N], the solution |
|
|
//| Rep - solution report: |
|
|
//| * Rep.TerminationType completion code: |
|
|
//| * -5 CG method was used for a matrix which is |
|
|
//| not positive definite |
|
|
//| * -4 overflow / underflow during solution (ill |
|
|
//| conditioned problem) |
|
|
//| * 1 || residual || <= EpsF* || b || |
|
|
//| * 5 MaxIts steps was taken |
|
|
//| * 7 rounding errors prevent further progress, |
|
|
//| best point found is returned |
|
|
//| * 8 the algorithm was terminated early with |
|
|
//| SparseSolverRequestTermination() being |
|
|
//| called from other thread. |
|
|
//| * Rep.IterationsCount contains iterations count |
|
|
//| * Rep.NMV contains number of matrix - vector |
|
|
//| calculations |
|
|
//| * Rep.R2 contains squared residual |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolveSymmetricGMRES(CSparseMatrix &a,
|
|
bool IsUpper,
|
|
CRowDouble &b,
|
|
int k,
|
|
double epsf,
|
|
int maxits,
|
|
CRowDouble &x,
|
|
CSparseSolverReport &rep)
|
|
{
|
|
//--- create variables
|
|
int n=CSparse::SparseGetNRows(a);
|
|
CSparseMatrix convbuf;
|
|
CSparseSolverState solver;
|
|
x.Resize(0);
|
|
//--- Test inputs
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": tried to automatically detect N from sizeof(A),got nonpositive size"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseGetNRows(a)==n,__FUNCTION__+": rows(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseGetNCols(a)==n,__FUNCTION__+": cols(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CAp::Len(b)>=n,__FUNCTION__+": length(B)<N"))
|
|
return;
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(b,n),__FUNCTION__+": B contains NAN/INF"))
|
|
return;
|
|
if(!CAp::Assert(MathIsValidNumber(epsf) && epsf>=0.0,__FUNCTION__+": EpsF<0 or infinite"))
|
|
return;
|
|
if(!CAp::Assert(maxits>=0,__FUNCTION__+": MaxIts<0"))
|
|
return;
|
|
if(epsf==0.0 && maxits==0)
|
|
epsf=1.0E-6;
|
|
//--- If A is non-CRS, perform conversion
|
|
if(!CSparse::SparseIsCRS(a))
|
|
{
|
|
CSparse::SparseCopyToCRSBuf(a,convbuf);
|
|
SparseSolveSymmetricGMRES(convbuf,IsUpper,b,k,epsf,maxits,x,rep);
|
|
return;
|
|
}
|
|
//--- Solve using temporary solver object
|
|
SparseSolverCreate(n,solver);
|
|
SparseSolverSetAlgoGMRES(solver,k);
|
|
SparseSolverSetCond(solver,epsf,maxits);
|
|
SparseSolverSolveSymmetric(solver,a,IsUpper,b);
|
|
SparseSolverResults(solver,x,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Solving sparse linear system A*x = b using GMRES(k) method. |
|
|
//| This function provides convenience API for an 'expert' interface |
|
|
//| provided by SparseSolverState class. Use SparseSolver API if you |
|
|
//| need advanced functions like providing initial point, using |
|
|
//| out-of-core API and so on. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - sparse NxN matrix in any sparse storage format. |
|
|
//| Using CRS format is recommended because it avoids |
|
|
//| internal conversion. An exception will be generated|
|
|
//| if A is not NxN matrix (where N is a size specified|
|
|
//| during solver object creation). |
|
|
//| B - right part, array[N] |
|
|
//| K - k parameter for GMRES(k), k >= 0. Zero value means |
|
|
//| that algorithm will choose it automatically. |
|
|
//| EpsF - stopping condition, EpsF >= 0. The algorithm will |
|
|
//| stop when residual will decrease below EpsF*| B |. |
|
|
//| Having EpsF = 0 means that this stopping condition |
|
|
//| is ignored. |
|
|
//| MaxIts - stopping condition, MaxIts >= 0. The algorithm will|
|
|
//| stop after performing MaxIts iterations. Zero value|
|
|
//| means no limit. |
|
|
//| NOTE: having both EpsF = 0 and MaxIts = 0 means that stopping |
|
|
//| criteria will be chosen automatically. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[N], the solution |
|
|
//| Rep - solution report: |
|
|
//| * Rep.TerminationType completion code: |
|
|
//| * -5 CG method was used for a matrix which is |
|
|
//| not positive definite |
|
|
//| * -4 overflow / underflow during solution (ill |
|
|
//| conditioned problem) |
|
|
//| * 1 || residual || <= EpsF* || b || |
|
|
//| * 5 MaxIts steps was taken |
|
|
//| * 7 rounding errors prevent further progress, |
|
|
//| best point found is returned |
|
|
//| * 8 the algorithm was terminated early with |
|
|
//| SparseSolverRequestTermination() being |
|
|
//| called from other thread. |
|
|
//| * Rep.IterationsCount contains iterations count |
|
|
//| * Rep.NMV contains number of matrix - vector |
|
|
//| calculations |
|
|
//| * Rep.R2 contains squared residual |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolveGMRES(CSparseMatrix &a,
|
|
CRowDouble &b,
|
|
int k,
|
|
double epsf,
|
|
int maxits,
|
|
CRowDouble &x,
|
|
CSparseSolverReport &rep)
|
|
{
|
|
//--- create variables
|
|
int n=CSparse::SparseGetNRows(a);
|
|
CSparseMatrix convbuf;
|
|
CSparseSolverState solver;
|
|
x.Resize(0);
|
|
//--- Test inputs
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": tried to automatically detect N from sizeof(A),got nonpositive size"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseGetNRows(a)==n,__FUNCTION__+": rows(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseGetNCols(a)==n,__FUNCTION__+": cols(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CAp::Len(b)>=n,__FUNCTION__+": length(B)<N"))
|
|
return;
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(b,n),__FUNCTION__+": B contains NAN/INF"))
|
|
return;
|
|
if(!CAp::Assert(MathIsValidNumber(epsf) && epsf>=0.0,__FUNCTION__+": EpsF<0 or infinite"))
|
|
return;
|
|
if(!CAp::Assert(maxits>=0,__FUNCTION__+": MaxIts<0"))
|
|
return;
|
|
|
|
if(epsf==0.0 && maxits==0)
|
|
epsf=1.0E-6;
|
|
//--- If A is non-CRS, perform conversion
|
|
if(!CSparse::SparseIsCRS(a))
|
|
{
|
|
CSparse::SparseCopyToCRSBuf(a,convbuf);
|
|
SparseSolveGMRES(convbuf,b,k,epsf,maxits,x,rep);
|
|
return;
|
|
}
|
|
//--- Solve using temporary solver object
|
|
SparseSolverCreate(n,solver);
|
|
SparseSolverSetAlgoGMRES(solver,k);
|
|
SparseSolverSetCond(solver,epsf,maxits);
|
|
SparseSolverSolve(solver,a,b);
|
|
SparseSolverResults(solver,x,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function initializes sparse linear iterative solver object. |
|
|
//| This solver can be used to solve nonsymmetric and symmetric |
|
|
//| positive definite NxN(square) linear systems. |
|
|
//| The solver provides 'expert' API which allows advanced control |
|
|
//| over algorithms being used, including ability to get progress |
|
|
//| report, terminate long-running solver from other thread, |
|
|
//| out-of-core solution and so on. |
|
|
//| NOTE: there are also convenience functions that allows quick one-|
|
|
//| line to the solvers: |
|
|
//| * SparseSolveCG() to solve SPD linear systems |
|
|
//| * SparseSolveGMRES() to solve unsymmetric linear systems. |
|
|
//| NOTE: if you want to solve MxN(rectangular) linear problem you |
|
|
//| may use LinLSQR solver provided by ALGLIB. |
|
|
//| USAGE (A is given by the SparseMatrix structure): |
|
|
//| 1. User initializes algorithm state with SparseSolverCreate() |
|
|
//| call |
|
|
//| 2. User selects algorithm with one of the SparseSolverSetAlgo??|
|
|
//| functions. By default, GMRES(k) is used with automatically |
|
|
//| chosen k |
|
|
//| 3. Optionally, user tunes solver parameters, sets starting |
|
|
//| point, etc. |
|
|
//| 4. Depending on whether system is symmetric or not, user calls:|
|
|
//| * SparseSolverSolveSymmetric() for a symmetric system given |
|
|
//| by its lower or upper triangle |
|
|
//| * SparseSolverSolve() for a nonsymmetric system or a |
|
|
//| symmetric one given by the full matrix |
|
|
//| 5. User calls SparseSolverResults() to get the solution |
|
|
//| It is possible to call SparseSolverSolve???() again to solve |
|
|
//| another task with same dimensionality but different matrix and/or|
|
|
//| right part without reinitializing SparseSolverState structure. |
|
|
//| USAGE(out-of-core mode): |
|
|
//| 1. User initializes algorithm state with SparseSolverCreate() |
|
|
//| call |
|
|
//| 2. User selects algorithm with one of the SparseSolverSetAlgo??|
|
|
//| functions. By default, GMRES(k) is used with automatically |
|
|
//| chosen k |
|
|
//| 3. Optionally, user tunes solver parameters, sets starting |
|
|
//| point, etc. |
|
|
//| 4. After that user should work with out-of-core interface in a |
|
|
//| loop like one given below: |
|
|
//| > CAlgLib::SparseSolverOOCStart(state) |
|
|
//| > while CAlgLib::SparseSolverOOCContinue(state) do |
|
|
//| > CAlgLib::SparseSolver))CGetRequestInfo(state, |
|
|
//| RequestType) |
|
|
//| > CAlgLib::SparseSolverOOCGetRequeStdata(state, X) |
|
|
//| > if RequestType = 0 then |
|
|
//| > [calculate Y = A * X, with X = R ^ N] |
|
|
//| > CAlgLib:SparseSolverOOCSendResult(state, Y) |
|
|
//| > CAlgLib::SparseSolverOOCStop(state, X, Report) |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - problem dimensionality (fixed at start-up) |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolverCreate(int n,
|
|
CSparseSolverState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<=0"))
|
|
return;
|
|
|
|
state.m_n=n;
|
|
state.m_running=false;
|
|
state.m_userterminationneeded=false;
|
|
CAblasF::RSetAllocV(state.m_n,0.0,state.m_x0);
|
|
CAblasF::RSetAllocV(state.m_n,0.0,state.m_x);
|
|
CAblasF::RSetAllocV(state.m_n,0.0,state.m_ax);
|
|
CAblasF::RSetAllocV(state.m_n,0.0,state.m_xf);
|
|
CAblasF::RSetAllocV(state.m_n,0.0,state.m_b);
|
|
CAblasF::RSetAllocV(state.m_n,0.0,state.m_wrkb);
|
|
state.m_reply1=0.0;
|
|
SparseSolverSetXRep(state,false);
|
|
SparseSolverSetCond(state,0.0,0);
|
|
SparseSolverSetAlgoGMRES(state,0);
|
|
ClearRequestFields(state);
|
|
ClearReportFields(state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets the solver algorithm to GMRES(k). |
|
|
//| NOTE: if you do not need advanced functionality of the |
|
|
//| SparseSolver API, you may use convenience functions |
|
|
//| SparseSolveGMRES() and SparseSolveSymmetricGMRES(). |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| K - GMRES parameter, K >= 0: |
|
|
//| * recommended values are in 10..100 range |
|
|
//| * larger values up to N are possible but have |
|
|
//| little sense - the algorithm will be slower than |
|
|
//| any dense solver. |
|
|
//| * values above N are truncated down to N |
|
|
//| * zero value means that default value is chosen. |
|
|
//| This value is 50 in the current version, but it |
|
|
//| may change in future ALGLIB releases. |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolverSetAlgoGMRES(CSparseSolverState &state,
|
|
int k)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(k>=0,__FUNCTION__+": K<0"))
|
|
return;
|
|
|
|
state.m_algotype=0;
|
|
if(k==0)
|
|
k=50;
|
|
state.m_gmresk=MathMin(k,state.m_n);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets starting point. |
|
|
//| By default, zero starting point is used. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| X - starting point, array[N] |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - new starting point was set |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolverSetStartingPoint(CSparseSolverState &state,
|
|
CRowDouble &x)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(state.m_n<=CAp::Len(x),__FUNCTION__+": Length(X)<N"))
|
|
return;
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,state.m_n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
|
|
state.m_x0=x;
|
|
state.m_x0.Resize(state.m_n);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets stopping criteria. |
|
|
//| INPUT PARAMETERS: |
|
|
//| EpsF - algorithm will be stopped if norm of residual is |
|
|
//| less than EpsF* || b ||. |
|
|
//| MaxIts - algorithm will be stopped if number of iterations |
|
|
//| is more than MaxIts. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NOTES: If both EpsF and MaxIts are zero then small EpsF will be |
|
|
//| set to small value. |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolverSetCond(CSparseSolverState &state,
|
|
double epsf,
|
|
int maxits)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(MathIsValidNumber(epsf) && epsf>=0.0,__FUNCTION__+": EpsF is negative or contains infinite or NaN values"))
|
|
return;
|
|
if(!CAp::Assert(maxits>=0,__FUNCTION__+": MaxIts is negative"))
|
|
return;
|
|
|
|
if(epsf==0.0 && maxits==0)
|
|
{
|
|
state.m_epsf=1.0E-6;
|
|
state.m_maxits=0;
|
|
}
|
|
else
|
|
{
|
|
state.m_epsf=epsf;
|
|
state.m_maxits=maxits;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Procedure for the solution of A*x = b with sparse symmetric A |
|
|
//| given by its lower or upper triangle. |
|
|
//| This function will work with any solver algorithm being used, |
|
|
//| SPD one (like CG) or not(like GMRES). Using unsymmetric solvers |
|
|
//| (like GMRES) on SPD problems is suboptimal, but still possible. |
|
|
//| NOTE: the solver behavior is ill-defined for a situation when a |
|
|
//| SPD solver is used on indefinite matrix. It may solve the |
|
|
//| problem up to desired precision (sometimes, rarely) or |
|
|
//| return with error code signalling violation of underlying |
|
|
//| assumptions. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - algorithm state |
|
|
//| A - sparse symmetric NxN matrix in any sparse storage |
|
|
//| format. Using CRS format is recommended because it |
|
|
//| avoids internal conversion. An exception will be |
|
|
//| generated if A is not NxN matrix (where N is a size|
|
|
//| specified during solver object creation). |
|
|
//| IsUpper - whether upper or lower triangle of A is used: |
|
|
//| * IsUpper = True => only upper triangle is used and|
|
|
//| lower triangle is not referenced at all |
|
|
//| * IsUpper = False => only lower triangle is used |
|
|
//| and upper triangle is not referenced at all |
|
|
//| B - right part, array[N] |
|
|
//| RESULT: |
|
|
//| This function returns no result. You can get the solution by |
|
|
//| calling SparseSolverResults() |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolverSolveSymmetric(CSparseSolverState &state,
|
|
CSparseMatrix &a,
|
|
bool IsUpper,
|
|
CRowDouble &b)
|
|
{
|
|
int n=state.m_n;
|
|
//--- Test inputs
|
|
if(!CAp::Assert(CSparse::SparseGetNRows(a)==n,__FUNCTION__+": rows(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseGetNCols(a)==n,__FUNCTION__+": cols(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CAp::Len(b)>=n,__FUNCTION__+": length(B)<N"))
|
|
return;
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(b,n),__FUNCTION__+": B contains NAN/INF"))
|
|
return;
|
|
//--- If A is non-CRS, perform conversion
|
|
if(!CSparse::SparseIsCRS(a))
|
|
{
|
|
CSparse::SparseCopyToCRSBuf(a,state.m_convbuf);
|
|
SparseSolverSolveSymmetric(state,state.m_convbuf,IsUpper,b);
|
|
return;
|
|
}
|
|
//--- Solve using out-of-core API
|
|
SparseSolverOOCStart(state,b);
|
|
while(SparseSolverOOCContinue(state))
|
|
{
|
|
if(state.m_requesttype==-1)
|
|
{
|
|
//--- Skip location reports
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(state.m_requesttype==0,__FUNCTION__+": integrity check 7372 failed"))
|
|
return;
|
|
CSparse::SparseSMV(a,IsUpper,state.m_x,state.m_ax);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Procedure for the solution of A*x = b with sparse nonsymmetric A |
|
|
//| IMPORTANT: this function will work with any solver algorithm |
|
|
//| being used, symmetric solver like CG, or not. However,|
|
|
//| using symmetric solvers on nonsymmetric problems is |
|
|
//| dangerous. It may solve the problem up to desired |
|
|
//| precision (sometimes, rarely) or terminate with error |
|
|
//| code signalling violation of underlying assumptions. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - algorithm state |
|
|
//| A - sparse NxN matrix in any sparse storage format. |
|
|
//| Using CRS format is recommended because it avoids |
|
|
//| internal conversion. An exception will be generated|
|
|
//| if A is not NxN matrix (where N is a size specified|
|
|
//| during solver object creation). |
|
|
//| B - right part, array[N] |
|
|
//| RESULT: |
|
|
//| This function returns no result. |
|
|
//| You can get the solution by calling SparseSolverResults() |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolverSolve(CSparseSolverState &state,
|
|
CSparseMatrix &a,
|
|
CRowDouble &b)
|
|
{
|
|
int n=state.m_n;
|
|
//--- Test inputs
|
|
if(!CAp::Assert(CSparse::SparseGetNRows(a)==n,__FUNCTION__+": rows(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CSparse::SparseGetNCols(a)==n,__FUNCTION__+": cols(A)!=N"))
|
|
return;
|
|
if(!CAp::Assert(CAp::Len(b)>=n,__FUNCTION__+": length(B)<N"))
|
|
return;
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(b,n),__FUNCTION__+": B contains NAN/INF"))
|
|
return;
|
|
//--- If A is non-CRS, perform conversion
|
|
if(!CSparse::SparseIsCRS(a))
|
|
{
|
|
CSparse::SparseCopyToCRSBuf(a,state.m_convbuf);
|
|
SparseSolverSolve(state,state.m_convbuf,b);
|
|
return;
|
|
}
|
|
//--- Solve using out-of-core API
|
|
SparseSolverOOCStart(state,b);
|
|
while(SparseSolverOOCContinue(state))
|
|
{
|
|
if(state.m_requesttype==-1)
|
|
{
|
|
//--- Skip location reports
|
|
continue;
|
|
}
|
|
if(!CAp::Assert(state.m_requesttype==0,__FUNCTION__+": integrity check 7372 failed"))
|
|
return;
|
|
CSparse::SparseMV(a,state.m_x,state.m_ax);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sparse solver results. |
|
|
//| This function must be called after calling one of the |
|
|
//| SparseSolverSolve() functions. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - algorithm state |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[N], solution |
|
|
//| Rep - solution report: |
|
|
//| * Rep.TerminationType completion code: |
|
|
//| * -5 CG method was used for a matrix which is |
|
|
//| not positive definite |
|
|
//| * -4 overflow/underflow during solution (ill |
|
|
//| conditioned problem) |
|
|
//| * 1 || residual || <= EpsF* || b || |
|
|
//| * 5 MaxIts steps was taken |
|
|
//| * 7 rounding errors prevent further progress, |
|
|
//| best point found is returned |
|
|
//| * 8 the algorithm was terminated early with |
|
|
//| SparseSolverRequestTermination() being |
|
|
//| called from other thread. |
|
|
//| * Rep.IterationsCount contains iterations count |
|
|
//| * Rep.NMV contains number of matrix - vector |
|
|
//| calculations |
|
|
//| * Rep.R2 contains squared residual |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolverResults(CSparseSolverState &state,
|
|
CRowDouble &x,
|
|
CSparseSolverReport &rep)
|
|
{
|
|
x.Resize(0);
|
|
SparseSolverOOCStop(state,x,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//|This function turns on/off reporting during out-of-core processing|
|
|
//| When the solver works in the out-of-core mode, it can be |
|
|
//| configured to report its progress by returning current location. |
|
|
//| These location reports implemented as a special kind of the |
|
|
//| out-of-core request: |
|
|
//| * SparseSolverOOCGetRequestInfo() returns - 1 |
|
|
//| * SparseSolverOOCGetRequestData() returns current location |
|
|
//| * SparseSolverOOCGetRequestData1() returns squared norm of |
|
|
//| the residual |
|
|
//| * SparseSolverOOCSendResult() shall NOT be called |
|
|
//| This function has no effect when SparseSolverSolve() is used |
|
|
//| because this function has no method of reporting its progress. |
|
|
//| NOTE: when used with GMRES(k), this function reports progress |
|
|
//| every k-th iteration. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NeedXRep - whether iteration reports are needed or not |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolverSetXRep(CSparseSolverState &state,
|
|
bool needxrep)
|
|
{
|
|
state.m_xrep=needxrep;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function initiates out-of-core mode of the sparse solver. It|
|
|
//| should be used in conjunction with other out-of-core - related |
|
|
//| functions of this subspackage in a loop like one given below: |
|
|
//| > CAlgLib::SparseSolverOOCStart(state) |
|
|
//| > while CAlgLib::SparseSolverOOCContinue(state) do |
|
|
//| > CAlgLib::SparseSolverOOCGetRequestInfo(state, RequestType) |
|
|
//| > CAlgLib::SparseSolverOOCGetRequestData(state, out X) |
|
|
//| > if RequestType = 0 then |
|
|
//| > [calculate Y = A * X, with X = R ^ N] |
|
|
//| > CAlgLib::SparseSolverOOCSendResult(state, in Y) |
|
|
//| > CAlgLib::SparseSolverOOCStop(state, out X, out Report) |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - solver object |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolverOOCStart(CSparseSolverState &state,
|
|
CRowDouble &b)
|
|
{
|
|
state.m_rstate.ia.Resize(1);
|
|
state.m_rstate.ra.Resize(3);
|
|
state.m_rstate.stage=-1;
|
|
ClearRequestFields(state);
|
|
ClearReportFields(state);
|
|
state.m_running=true;
|
|
state.m_userterminationneeded=false;
|
|
CAblasF::RCopyV(state.m_n,b,state.m_b);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function performs iterative solution of the linear system in|
|
|
//| the out-of-core mode. It should be used in conjunction with other|
|
|
//| out-of-core - related functions of this subspackage in a loop |
|
|
//| like one given below: |
|
|
//| > CAlgLib::SparseSolverOOCStart(state) |
|
|
//| > while CAlgLib::SparseSolverOOCContinue(state) do |
|
|
//| > CAlgLib::SparseSolverOOCGetRequestInfo(state, RequestType) |
|
|
//| > CAlgLib::SparseSolverOOCGetRequestData(state, out X) |
|
|
//| > if RequestType = 0 then |
|
|
//| > [calculate Y = A * X, with X = R ^ N] |
|
|
//| > CAlgLib::SparseSolverOOCSendResult(state, in Y) |
|
|
//| > CAlgLib::SparseSolverOOCStop(state, out X, out Report) |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - solver object |
|
|
//+------------------------------------------------------------------+
|
|
bool CIterativeSparse::SparseSolverOOCContinue(CSparseSolverState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(state.m_running,__FUNCTION__+": the solver is not running"))
|
|
return(false);
|
|
|
|
bool result=SparseSolverIteration(state);
|
|
state.m_running=result;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function is used to retrieve information about out-of-core |
|
|
//| request sent by the solver: |
|
|
//| * RequestType = 0 means that matrix - vector products A * x is |
|
|
//| requested |
|
|
//| * RequestType = -1 means that solver reports its progress; this|
|
|
//| request is returned only when reports are |
|
|
//| activated wit SparseSolverSetXRep(). |
|
|
//| This function returns just request type; in order to get contents|
|
|
//| of the trial vector, use SparseSolverOOCGetRequestData(). |
|
|
//| It should be used in conjunction with other out-of-core - related|
|
|
//| functions of this subspackage in a loop like one given below: |
|
|
//| > CAlgLib::SparseSolverOOCStart(state) |
|
|
//| > while CAlgLib::SparseSolverOOCContinue(state) do |
|
|
//| > CAlgLib::SparseSolverOOCGetRequestInfo(state, RequestType) |
|
|
//| > CAlgLib::SparseSolverOOCGetRequestData(state, out X) |
|
|
//| > if RequestType = 0 then |
|
|
//| > [calculate Y = A * X, with X = R ^ N] |
|
|
//| > CAlgLib::SparseSolverOOCSendResult(state, in Y) |
|
|
//| > CAlgLib::SparseSolverOOCStop(state, out X, out Report) |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - solver running in out-of-core mode |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| RequestType - type of the request to process: |
|
|
//| * 0 for matrix - vector product A * x, with A |
|
|
//| being NxN system matrix and X being |
|
|
//| N-dimensional vector |
|
|
//| *-1 for location and residual report |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolverOOCGetRequestInfo(CSparseSolverState &state,
|
|
int &requesttype)
|
|
{
|
|
requesttype=0;
|
|
//--- check
|
|
if(!CAp::Assert(state.m_running,__FUNCTION__+": the solver is not running"))
|
|
return;
|
|
|
|
requesttype=state.m_requesttype;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function is used to retrieve vector associated with |
|
|
//| out-of-core request sent by the solver to user code. Depending on|
|
|
//| the request type(returned by the SparseSolverOOCGetRequestInfo())|
|
|
//| this vector should be multiplied by A or subjected to another |
|
|
//| processing. |
|
|
//| It should be used in conjunction with other out-of-core - related|
|
|
//| functions of this subspackage in a loop like one given below: |
|
|
//| > CAlgLib::SparseSolverOOCStart(state) |
|
|
//| > while CAlgLib::SparseSolverOOCContinue(state) do |
|
|
//| > CAlgLib::SparseSolverOOCGetRequestInfo(state, RequestType) |
|
|
//| > CAlgLib::SparseSolverOOCGetRequestData(state, out X) |
|
|
//| > if RequestType = 0 then |
|
|
//| > [calculate Y = A * X, with X = R ^ N] |
|
|
//| > CAlgLib::SparseSolverOOCSendResult(state, in Y) |
|
|
//| > CAlgLib::SparseSolverOOCStop(state, out X, out Report) |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - solver running in out-of-core mode |
|
|
//| X - possibly preallocated storage; reallocated if |
|
|
//| needed, left unchanged, if large enough to store|
|
|
//| request data. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[N] or larger, leading N elements are |
|
|
//| filled with vector X. |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolverOOCGetRequestData(CSparseSolverState &state,
|
|
CRowDouble &x)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(state.m_running,__FUNCTION__+": the solver is not running"))
|
|
return;
|
|
|
|
x=state.m_x;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function is used to retrieve scalar value associated with |
|
|
//| out-of-core request sent by the solver to user code. In the |
|
|
//| current ALGLIB version this function is used to retrieve squared |
|
|
//| residual norm during progress reports. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - solver running in out-of-core mode |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| V - scalar value associated with the current request|
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolverOOCGetRequestData1(CSparseSolverState &state,
|
|
double &v)
|
|
{
|
|
v=0;
|
|
//--- check
|
|
if(!CAp::Assert(state.m_running,__FUNCTION__+": the solver is not running"))
|
|
return;
|
|
|
|
v=state.m_reply1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function is used to send user reply to out-of-core request |
|
|
//| sent by the solver. Usually it is product A*x for vector X |
|
|
//| returned by the solver. |
|
|
//| It should be used in conjunction with other out-of-core - related|
|
|
//| functions of this subspackage in a loop like one given below: |
|
|
//| > CAlgLib::SparseSolverOOCStart(state) |
|
|
//| > while CAlgLib::SparseSolverOOCContinue(state) do |
|
|
//| > CAlgLib::SparseSolverOOCGetRequestInfo(state, RequestType) |
|
|
//| > CAlgLib::SparseSolverOOCGetRequestData(state, out X) |
|
|
//| > if RequestType = 0 then |
|
|
//| > [calculate Y = A * X, with X = R ^ N] |
|
|
//| > CAlgLib::SparseSolverOOCSendResult(state, in Y) |
|
|
//| > CAlgLib::SparseSolverOOCStop(state, out X, out Report) |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - solver running in out-of-core mode |
|
|
//| AX - array[N] or larger, leading N elements contain A*x |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolverOOCSendResult(CSparseSolverState &state,
|
|
CRowDouble &ax)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(state.m_running,__FUNCTION__+": the solver is not running"))
|
|
return;
|
|
if(!CAp::Assert(state.m_requesttype==0,__FUNCTION__+": this request type does not accept replies"))
|
|
return;
|
|
|
|
CAblasF::RCopyV(state.m_n,ax,state.m_ax);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function finalizes out-of-core mode of the linear solver. It|
|
|
//| should be used in conjunction with other out-of-core - related |
|
|
//| functions of this subspackage in a loop like one given below: |
|
|
//| > CAlgLib::SparseSolverOOCStart(state) |
|
|
//| > while CAlgLib::SparseSolverOOCContinue(state) do |
|
|
//| > CAlgLib::SparseSolverOOCGetRequestInfo(state, RequestType) |
|
|
//| > CAlgLib::SparseSolverOOCGetRequestData(state, out X) |
|
|
//| > if RequestType = 0 then |
|
|
//| > [calculate Y = A * X, with X = R ^ N] |
|
|
//| > CAlgLib::SparseSolverOOCSendResult(state, in Y) |
|
|
//| > CAlgLib::SparseSolverOOCStop(state, out X, out Report) |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - solver state |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[N], the solution. |
|
|
//| Zero - filled on the failure(Rep.TerminationType < 0). |
|
|
//| Rep - report with additional info: |
|
|
//| * Rep.TerminationType completion code: |
|
|
//| * -5 CG method was used for a matrix which is |
|
|
//| not positive definite |
|
|
//| * -4 overflow/underflow during solution (ill |
|
|
//| conditioned problem) |
|
|
//| * 1 || residual || <= EpsF* || b || |
|
|
//| * 5 MaxIts steps was taken |
|
|
//| * 7 rounding errors prevent further progress, |
|
|
//| best point found is returned |
|
|
//| * 8 the algorithm was terminated early with |
|
|
//| SparseSolverRequestTermination() being |
|
|
//| called from other thread. |
|
|
//| * Rep.IterationsCount contains iterations count |
|
|
//| * Rep.NMV contains number of matrix - vector |
|
|
//| calculations |
|
|
//| * Rep.R2 contains squared residual |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolverOOCStop(CSparseSolverState &state,
|
|
CRowDouble &x,
|
|
CSparseSolverReport &rep)
|
|
{
|
|
x.Resize(0);
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_running,__FUNCTION__+": the solver is still running"))
|
|
return;
|
|
|
|
x.Resize(state.m_n);
|
|
CAblasF::RCopyV(state.m_n,state.m_xf,x);
|
|
CDirectSparseSolvers::InitSparseSolverReport(rep);
|
|
rep.m_iterationscount=state.m_repiterationscount;
|
|
rep.m_nmv=state.m_repnmv;
|
|
rep.m_terminationtype=state.m_repterminationtype;
|
|
rep.m_r2=state.m_repr2;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine submits request for termination of the running |
|
|
//| solver. It can be called from some other thread which wants the |
|
|
//| solver to terminate or when processing an out-of-core request. |
|
|
//| As result, solver stops at point which was "current accepted" |
|
|
//| when the termination request was submitted and returns error code|
|
|
//| 8 (successful termination). Such termination is a smooth process |
|
|
//| which properly deallocates all temporaries. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - solver structure |
|
|
//| NOTE: calling this function on solver which is NOT running will |
|
|
//| have no effect. |
|
|
//| NOTE: multiple calls to this function are possible. First call is|
|
|
//| counted, subsequent calls are silently ignored. |
|
|
//| NOTE: solver clears termination flag on its start, it means that |
|
|
//| if some other thread will request termination too soon, its|
|
|
//| request will went unnoticed. |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::SparseSolverRequestTermination(CSparseSolverState &state)
|
|
{
|
|
state.m_userterminationneeded=true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reverse communication sparse iteration subroutine |
|
|
//+------------------------------------------------------------------+
|
|
bool CIterativeSparse::SparseSolverIteration(CSparseSolverState &state)
|
|
{
|
|
//--- create variables
|
|
int outeridx=0;
|
|
double res=0;
|
|
double prevres=0;
|
|
double res0=0;
|
|
int label=-1;
|
|
//--- Reverse communication preparations
|
|
//--- This code initializes locals by:
|
|
//--- * random values determined during code
|
|
//--- generation - on first subroutine call
|
|
//--- * values from previous call - on subsequent calls
|
|
if(state.m_rstate.stage>=0)
|
|
{
|
|
outeridx=state.m_rstate.ia[0];
|
|
res=state.m_rstate.ra[0];
|
|
prevres=state.m_rstate.ra[1];
|
|
res0=state.m_rstate.ra[2];
|
|
}
|
|
else
|
|
{
|
|
outeridx=359;
|
|
res=-58;
|
|
prevres=-919;
|
|
res0=-909;
|
|
}
|
|
|
|
switch(state.m_rstate.stage)
|
|
{
|
|
case 0:
|
|
label=0;
|
|
break;
|
|
case 1:
|
|
label=1;
|
|
break;
|
|
case 2:
|
|
label=2;
|
|
break;
|
|
case 3:
|
|
label=3;
|
|
break;
|
|
case 4:
|
|
label=4;
|
|
break;
|
|
default:
|
|
//--- Routine body
|
|
state.m_running=true;
|
|
ClearRequestFields(state);
|
|
ClearReportFields(state);
|
|
//--- GMRES?
|
|
//
|
|
if(state.m_algotype!=0)
|
|
{
|
|
label=5;
|
|
break;
|
|
}
|
|
if(CAblasF::RDotV2(state.m_n,state.m_x0)!=0.0)
|
|
{
|
|
label=7;
|
|
break;
|
|
}
|
|
//--- Starting point is default one (zero), quick initialization
|
|
CAblasF::RSetV(state.m_n,0.0,state.m_xf);
|
|
CAblasF::RCopyV(state.m_n,state.m_b,state.m_wrkb);
|
|
label=8;
|
|
break;
|
|
}
|
|
//--- main loop
|
|
while(label>=0)
|
|
{
|
|
switch(label)
|
|
{
|
|
case 7:
|
|
//--- Non-zero starting point is provided,
|
|
CAblasF::RCopyV(state.m_n,state.m_x0,state.m_xf);
|
|
state.m_requesttype=0;
|
|
CAblasF::RCopyV(state.m_n,state.m_x0,state.m_x);
|
|
state.m_rstate.stage=0;
|
|
label=-1;
|
|
break;
|
|
case 0:
|
|
state.m_requesttype=-999;
|
|
state.m_repnmv=state.m_repnmv+1;
|
|
CAblasF::RCopyV(state.m_n,state.m_b,state.m_wrkb);
|
|
CAblasF::RAddV(state.m_n,-1.0,state.m_ax,state.m_wrkb);
|
|
case 8:
|
|
outeridx=0;
|
|
state.m_repterminationtype=5;
|
|
state.m_repr2=CAblasF::RDotV2(state.m_n,state.m_wrkb);
|
|
res0=MathSqrt(CAblasF::RDotV2(state.m_n,state.m_b));
|
|
res=MathSqrt(state.m_repr2);
|
|
if(!state.m_xrep)
|
|
{
|
|
label=9;
|
|
break;
|
|
}
|
|
//--- Report initial point
|
|
state.m_requesttype=-1;
|
|
state.m_reply1=res*res;
|
|
CAblasF::RCopyV(state.m_n,state.m_xf,state.m_x);
|
|
state.m_rstate.stage=1;
|
|
label=-1;
|
|
break;
|
|
case 1:
|
|
state.m_requesttype=-999;
|
|
case 9:
|
|
case 11:
|
|
if(!(res>0.0 && (state.m_maxits==0 || state.m_repiterationscount<state.m_maxits)))
|
|
{
|
|
label=12;
|
|
break;
|
|
}
|
|
//--- Solve with GMRES(k) for current residual.
|
|
//--- We set EpsF-based stopping condition for GMRES(k). It allows us
|
|
//--- to quickly detect sufficient decrease in the residual. We still
|
|
//--- have to recompute residual after the GMRES round because residuals
|
|
//--- computed by GMRES are different from the true one (due to restarts).
|
|
//--- However, checking residual decrease within GMRES still gives us
|
|
//--- an opportunity to stop early without waiting for GMRES round to
|
|
//--- complete.
|
|
CFbls::FblsGMRESCreate(state.m_wrkb,state.m_n,state.m_gmresk,state.m_gmressolver);
|
|
state.m_gmressolver.m_epsres=state.m_epsf*res0/res;
|
|
case 13:
|
|
if(!CFbls::FblsGMRESIteration(state.m_gmressolver))
|
|
{
|
|
label=14;
|
|
break;
|
|
}
|
|
state.m_requesttype=0;
|
|
CAblasF::RCopyV(state.m_n,state.m_gmressolver.m_x,state.m_x);
|
|
state.m_rstate.stage=2;
|
|
label=-1;
|
|
break;
|
|
case 2:
|
|
state.m_requesttype=-999;
|
|
CAblasF::RCopyV(state.m_n,state.m_ax,state.m_gmressolver.m_ax);
|
|
state.m_repnmv++;
|
|
if(state.m_userterminationneeded)
|
|
{
|
|
//--- User requested termination
|
|
state.m_repterminationtype=8;
|
|
return(false);
|
|
}
|
|
label=13;
|
|
break;
|
|
case 14:
|
|
state.m_repiterationscount+=state.m_gmressolver.m_itsperformed;
|
|
CAblasF::RAddV(state.m_n,1.0,state.m_gmressolver.m_xs,state.m_xf);
|
|
//--- Update residual, evaluate residual decrease, terminate if needed
|
|
state.m_requesttype=0;
|
|
CAblasF::RCopyV(state.m_n,state.m_xf,state.m_x);
|
|
state.m_rstate.stage=3;
|
|
label=-1;
|
|
break;
|
|
case 3:
|
|
state.m_requesttype=-999;
|
|
state.m_repnmv++;
|
|
CAblasF::RCopyV(state.m_n,state.m_b,state.m_wrkb);
|
|
CAblasF::RAddV(state.m_n,-1.0,state.m_ax,state.m_wrkb);
|
|
state.m_repr2=CAblasF::RDotV2(state.m_n,state.m_wrkb);
|
|
prevres=res;
|
|
res=MathSqrt(state.m_repr2);
|
|
if(!state.m_xrep)
|
|
{
|
|
label=15;
|
|
break;
|
|
}
|
|
//--- Report initial point
|
|
state.m_requesttype=-1;
|
|
state.m_reply1=res*res;
|
|
CAblasF::RCopyV(state.m_n,state.m_xf,state.m_x);
|
|
state.m_rstate.stage=4;
|
|
label=-1;
|
|
break;
|
|
case 4:
|
|
state.m_requesttype=-999;
|
|
case 15:
|
|
if(res<=(state.m_epsf*res0))
|
|
{
|
|
//--- Residual decrease condition met, stopping
|
|
state.m_repterminationtype=1;
|
|
label=12;
|
|
break;
|
|
}
|
|
if(res>=(prevres*(1-MathSqrt(CMath::m_machineepsilon))))
|
|
{
|
|
//--- The algorithm stagnated
|
|
state.m_repterminationtype=7;
|
|
label=12;
|
|
break;
|
|
}
|
|
if(state.m_userterminationneeded)
|
|
{
|
|
//--- User requested termination
|
|
state.m_repterminationtype=8;
|
|
return(false);
|
|
}
|
|
outeridx++;
|
|
label=11;
|
|
break;
|
|
case 12:
|
|
return(false);
|
|
case 5:
|
|
CAp::Assert(false,__FUNCTION__+": integrity check failed (unexpected algo)");
|
|
return(false);
|
|
}
|
|
}
|
|
//--- Saving state
|
|
state.m_rstate.ia.Set(0,outeridx);
|
|
state.m_rstate.ra.Set(0,res);
|
|
state.m_rstate.ra.Set(1,prevres);
|
|
state.m_rstate.ra.Set(2,res0);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Clears request fileds (to be sure that we don't forgot to clear |
|
|
//| something) |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::ClearRequestFields(CSparseSolverState &state)
|
|
{
|
|
state.m_requesttype=-999;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Clears report fileds (to be sure that we don't forgot to clear |
|
|
//| something) |
|
|
//+------------------------------------------------------------------+
|
|
void CIterativeSparse::ClearReportFields(CSparseSolverState &state)
|
|
{
|
|
state.m_repiterationscount=0;
|
|
state.m_repnmv=0;
|
|
state.m_repterminationtype=0;
|
|
state.m_repr2=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This object stores state of the linear CG method. |
|
|
//| You should use ALGLIB functions to work with this object. |
|
|
//| Never try to access its fields directly! |
|
|
//+------------------------------------------------------------------+
|
|
struct CLinCGState
|
|
{
|
|
int m_itsbeforerestart;
|
|
int m_itsbeforerupdate;
|
|
int m_maxits;
|
|
int m_n;
|
|
int m_prectype;
|
|
int m_repiterationscount;
|
|
int m_repnmv;
|
|
int m_repterminationtype;
|
|
double m_alpha;
|
|
double m_beta;
|
|
double m_epsf;
|
|
double m_meritfunction;
|
|
double m_r2;
|
|
double m_vmv;
|
|
bool m_needmtv;
|
|
bool m_needmv2;
|
|
bool m_needmv;
|
|
bool m_needprec;
|
|
bool m_needvmv;
|
|
bool m_running;
|
|
bool m_xrep;
|
|
bool m_xupdated;
|
|
RCommState m_rstate;
|
|
CRowDouble m_b;
|
|
CRowDouble m_cr;
|
|
CRowDouble m_cx;
|
|
CRowDouble m_cz;
|
|
CRowDouble m_mv;
|
|
CRowDouble m_p;
|
|
CRowDouble m_pv;
|
|
CRowDouble m_r;
|
|
CRowDouble m_rx;
|
|
CRowDouble m_startx;
|
|
CRowDouble m_tmpd;
|
|
CRowDouble m_x;
|
|
CRowDouble m_z;
|
|
//--- constructor / destructor
|
|
CLinCGState(void);
|
|
~CLinCGState(void) {}
|
|
//---
|
|
void Copy(const CLinCGState &obj);
|
|
//--- overloading
|
|
void operator=(const CLinCGState &obj) { Copy(obj); }
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor |
|
|
//+------------------------------------------------------------------+
|
|
CLinCGState::CLinCGState(void)
|
|
{
|
|
m_itsbeforerestart=0;
|
|
m_itsbeforerupdate=0;
|
|
m_maxits=0;
|
|
m_n=0;
|
|
m_prectype=0;
|
|
m_repiterationscount=0;
|
|
m_repnmv=0;
|
|
m_repterminationtype=0;
|
|
m_alpha=0;
|
|
m_beta=0;
|
|
m_epsf=0;
|
|
m_meritfunction=0;
|
|
m_r2=0;
|
|
m_vmv=0;
|
|
m_needmtv=false;
|
|
m_needmv2=false;
|
|
m_needmv=false;
|
|
m_needprec=false;
|
|
m_needvmv=false;
|
|
m_running=false;
|
|
m_xrep=false;
|
|
m_xupdated=false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CLinCGState::Copy(const CLinCGState &obj)
|
|
{
|
|
m_itsbeforerestart=obj.m_itsbeforerestart;
|
|
m_itsbeforerupdate=obj.m_itsbeforerupdate;
|
|
m_maxits=obj.m_maxits;
|
|
m_n=obj.m_n;
|
|
m_prectype=obj.m_prectype;
|
|
m_repiterationscount=obj.m_repiterationscount;
|
|
m_repnmv=obj.m_repnmv;
|
|
m_repterminationtype=obj.m_repterminationtype;
|
|
m_alpha=obj.m_alpha;
|
|
m_beta=obj.m_beta;
|
|
m_epsf=obj.m_epsf;
|
|
m_meritfunction=obj.m_meritfunction;
|
|
m_r2=obj.m_r2;
|
|
m_vmv=obj.m_vmv;
|
|
m_needmtv=obj.m_needmtv;
|
|
m_needmv2=obj.m_needmv2;
|
|
m_needmv=obj.m_needmv;
|
|
m_needprec=obj.m_needprec;
|
|
m_needvmv=obj.m_needvmv;
|
|
m_running=obj.m_running;
|
|
m_xrep=obj.m_xrep;
|
|
m_xupdated=obj.m_xupdated;
|
|
m_rstate=obj.m_rstate;
|
|
m_b=obj.m_b;
|
|
m_cr=obj.m_cr;
|
|
m_cx=obj.m_cx;
|
|
m_cz=obj.m_cz;
|
|
m_mv=obj.m_mv;
|
|
m_p=obj.m_p;
|
|
m_pv=obj.m_pv;
|
|
m_r=obj.m_r;
|
|
m_rx=obj.m_rx;
|
|
m_startx=obj.m_startx;
|
|
m_tmpd=obj.m_tmpd;
|
|
m_x=obj.m_x;
|
|
m_z=obj.m_z;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
struct CLinCGReport
|
|
{
|
|
int m_iterationscount;
|
|
int m_nmv;
|
|
int m_terminationtype;
|
|
double m_r2;
|
|
//--- constructor / destructor
|
|
CLinCGReport(void) { ZeroMemory(this); }
|
|
~CLinCGReport(void) {}
|
|
//---
|
|
void Copy(const CLinCGReport &obj);
|
|
//--- overloading
|
|
void operator=(const CLinCGReport &obj) { Copy(obj); }
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CLinCGReport::Copy(const CLinCGReport &obj)
|
|
{
|
|
m_iterationscount=obj.m_iterationscount;
|
|
m_nmv=obj.m_nmv;
|
|
m_terminationtype=obj.m_terminationtype;
|
|
m_r2=obj.m_r2;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CLinCG
|
|
{
|
|
public:
|
|
//--- constants
|
|
static const double m_defaultprecision;
|
|
|
|
static void LinCGCreate(int n,CLinCGState &state);
|
|
static void LinCGSetStartingPoint(CLinCGState &state,CRowDouble &x);
|
|
static void LinCGSetB(CLinCGState &state,CRowDouble &b);
|
|
static void LinCGSetPrecUnit(CLinCGState &state);
|
|
static void LinCGSetPrecDiag(CLinCGState &state);
|
|
static void LinCGSetCond(CLinCGState &state,double epsf,int maxits);
|
|
static bool LinCGIteration(CLinCGState &state);
|
|
static void LinCGSolveSparse(CLinCGState &state,CSparseMatrix &a,bool IsUpper,CRowDouble &b);
|
|
static void LinCGResult(CLinCGState &state,CRowDouble &x,CLinCGReport &rep);
|
|
static void LinCGSetRestartFreq(CLinCGState &state,int srf);
|
|
static void LinCGSetRUpdateFreq(CLinCGState &state,int freq);
|
|
static void LinCGSetXRep(CLinCGState &state,bool needxrep);
|
|
static void LinCGRestart(CLinCGState &state);
|
|
|
|
private:
|
|
static void ClearrFields(CLinCGState &state);
|
|
static void UpdateItersData(CLinCGState &state);
|
|
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
const double CLinCG::m_defaultprecision=1.0E-6;
|
|
//+------------------------------------------------------------------+
|
|
//| This function initializes linear CG Solver. This solver is used |
|
|
//| to solve symmetric positive definite problems. If you want to |
|
|
//| solve nonsymmetric (or non-positive definite) problem you may use|
|
|
//| LinLSQR solver provided by ALGLIB. |
|
|
//| USAGE: |
|
|
//| 1. User initializes algorithm state with LinCGCreate() call |
|
|
//| 2. User tunes solver parameters with LinCGSetCond() and other |
|
|
//| functions |
|
|
//| 3. Optionally, user sets starting point with |
|
|
//| LinCGSetStartingPoint() |
|
|
//| 4. User calls LinCGSolveSparse() function which takes algorithm|
|
|
//| state and SparseMatrix object. |
|
|
//| 5. User calls LinCGResults() to get solution |
|
|
//| 6. Optionally, user may call LinCGSolveSparse() again to solve |
|
|
//| another problem with different matrix and/or right part |
|
|
//| without reinitializing LinCGState structure. |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - problem dimension, N > 0 |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
void CLinCG::LinCGCreate(int n,CLinCGState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0"))
|
|
return;
|
|
|
|
state.m_n=n;
|
|
state.m_prectype=0;
|
|
state.m_itsbeforerestart=n;
|
|
state.m_itsbeforerupdate=10;
|
|
state.m_epsf=m_defaultprecision;
|
|
state.m_maxits=0;
|
|
state.m_xrep=false;
|
|
state.m_running=false;
|
|
//--- * allocate arrays
|
|
//--- * set RX to NAN (just for the case user calls Results() without
|
|
//--- calling SolveSparse()
|
|
//--- * set starting point to zero
|
|
//--- * we do NOT initialize B here because we assume that user should
|
|
//--- initializate it using LinCGSetB() function. In case he forgets
|
|
//--- to do so, exception will be thrown in the LinCGIteration().
|
|
state.m_rx=vector<double>::Full(state.m_n,AL_NaN);
|
|
state.m_startx=vector<double>::Zeros(state.m_n);
|
|
state.m_b=vector<double>::Zeros(state.m_n);
|
|
state.m_cx=vector<double>::Zeros(state.m_n);
|
|
state.m_p=vector<double>::Zeros(state.m_n);
|
|
state.m_r=vector<double>::Zeros(state.m_n);
|
|
state.m_cr=vector<double>::Zeros(state.m_n);
|
|
state.m_z=vector<double>::Zeros(state.m_n);
|
|
state.m_cz=vector<double>::Zeros(state.m_n);
|
|
state.m_x=vector<double>::Zeros(state.m_n);
|
|
state.m_mv=vector<double>::Zeros(state.m_n);
|
|
state.m_pv=vector<double>::Zeros(state.m_n);
|
|
UpdateItersData(state);
|
|
state.m_rstate.ia.Resize(1);
|
|
state.m_rstate.ra=vector<double>::Zeros(3);
|
|
state.m_rstate.stage=-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets starting point. |
|
|
//| By default, zero starting point is used. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - starting point, array[N] |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
void CLinCG::LinCGSetStartingPoint(CLinCGState &state,
|
|
CRowDouble &x)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_running,__FUNCTION__+": you can not change starting point because LinCGIteration() function is running"))
|
|
return;
|
|
if(!CAp::Assert(state.m_n<=CAp::Len(x),__FUNCTION__+": Length(X)<N"))
|
|
return;
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,state.m_n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- copy
|
|
state.m_startx=x;
|
|
state.m_startx.Resize(state.m_n);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets right part. By default, right part is zero. |
|
|
//| INPUT PARAMETERS: |
|
|
//| B - right part, array[N]. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
void CLinCG::LinCGSetB(CLinCGState &state,CRowDouble &b)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_running,__FUNCTION__+": you can not set B,because function LinCGIteration is running!"))
|
|
return;
|
|
if(!CAp::Assert(CAp::Len(b)>=state.m_n,__FUNCTION__+": Length(B)<N"))
|
|
return;
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(b,state.m_n),__FUNCTION__+": B contains infinite or NaN values!"))
|
|
return;
|
|
//--- copy
|
|
state.m_b=b;
|
|
state.m_b.Resize(state.m_n);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function changes preconditioning settings of |
|
|
//| LinCGSolveSparse() function. By default, SolveSparse() uses |
|
|
//| diagonal preconditioner, but if you want to use solver without |
|
|
//| preconditioning, you can call this function which forces solver |
|
|
//| to use unit matrix for preconditioning. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
void CLinCG::LinCGSetPrecUnit(CLinCGState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_running,__FUNCTION__+": you can not change preconditioner,because function LinCGIteration is running!"))
|
|
return;
|
|
|
|
state.m_prectype=-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function changes preconditioning settings of |
|
|
//| LinCGSolveSparse() function. LinCGSolveSparse() will use diagonal|
|
|
//| of the system matrix as preconditioner. This preconditioning mode|
|
|
//| is active by default. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
void CLinCG::LinCGSetPrecDiag(CLinCGState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_running,__FUNCTION__+": you can not change preconditioner,because function LinCGIteration is running!"))
|
|
return;
|
|
|
|
state.m_prectype=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets stopping criteria. |
|
|
//| INPUT PARAMETERS: |
|
|
//| EpsF - algorithm will be stopped if norm of residual is |
|
|
//| less than EpsF* || b ||. |
|
|
//| MaxIts - algorithm will be stopped if number of iterations |
|
|
//| is more than MaxIts. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NOTES: If both EpsF and MaxIts are zero then small EpsF will be |
|
|
//| set to small value. |
|
|
//+------------------------------------------------------------------+
|
|
void CLinCG::LinCGSetCond(CLinCGState &state,double epsf,int maxits)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_running,__FUNCTION__+": you can not change stopping criteria when LinCGIteration() is running"))
|
|
return;
|
|
if(!CAp::Assert(MathIsValidNumber(epsf) && epsf>=0.0,__FUNCTION__+": EpsF is negative or contains infinite or NaN values"))
|
|
return;
|
|
if(!CAp::Assert(maxits>=0,__FUNCTION__+": MaxIts is negative"))
|
|
return;
|
|
|
|
if(epsf==0.0 && maxits==0)
|
|
{
|
|
state.m_epsf=m_defaultprecision;
|
|
state.m_maxits=maxits;
|
|
}
|
|
else
|
|
{
|
|
state.m_epsf=epsf;
|
|
state.m_maxits=maxits;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reverse communication version of linear CG. |
|
|
//+------------------------------------------------------------------+
|
|
bool CLinCG::LinCGIteration(CLinCGState &state)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double uvar=0;
|
|
double bnorm=0;
|
|
double v=0;
|
|
int label=-1;
|
|
//--- Reverse communication preparations
|
|
//--- This code initializes locals by:
|
|
//--- * random values determined during code
|
|
//--- generation - on first subroutine call
|
|
//--- * values from previous call - on subsequent calls
|
|
//
|
|
if(state.m_rstate.stage>=0)
|
|
{
|
|
i=state.m_rstate.ia[0];
|
|
uvar=state.m_rstate.ra[0];
|
|
bnorm=state.m_rstate.ra[1];
|
|
v=state.m_rstate.ra[2];
|
|
}
|
|
else
|
|
{
|
|
i=359;
|
|
uvar=-58;
|
|
bnorm=-919;
|
|
v=-909;
|
|
}
|
|
|
|
switch(state.m_rstate.stage)
|
|
{
|
|
case 0:
|
|
label=0;
|
|
break;
|
|
case 1:
|
|
label=1;
|
|
break;
|
|
case 2:
|
|
label=2;
|
|
break;
|
|
case 3:
|
|
label=3;
|
|
break;
|
|
case 4:
|
|
label=4;
|
|
break;
|
|
case 5:
|
|
label=5;
|
|
break;
|
|
case 6:
|
|
label=6;
|
|
break;
|
|
case 7:
|
|
label=7;
|
|
break;
|
|
default:
|
|
//--- Routine body
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(state.m_b)>0,__FUNCTION__+": B is not initialized (you must initialize B by LinCGSetB() call"))
|
|
return(false);
|
|
state.m_running=true;
|
|
state.m_repnmv=0;
|
|
ClearrFields(state);
|
|
UpdateItersData(state);
|
|
//--- Start 0-th iteration
|
|
state.m_rx=state.m_startx;
|
|
state.m_x=state.m_rx;
|
|
state.m_repnmv++;
|
|
ClearrFields(state);
|
|
state.m_needvmv=true;
|
|
state.m_rstate.stage=0;
|
|
label=-1;
|
|
break;
|
|
}
|
|
//--- main loop
|
|
while(label>=0)
|
|
{
|
|
switch(label)
|
|
{
|
|
case 0:
|
|
state.m_needvmv=false;
|
|
bnorm=0;
|
|
state.m_r2=0;
|
|
state.m_meritfunction=0;
|
|
state.m_r=state.m_b-state.m_mv+0;
|
|
state.m_r2=state.m_r.Dot(state.m_r);
|
|
state.m_meritfunction= state.m_mv.Dot(state.m_rx)-2*state.m_b.Dot(state.m_rx);
|
|
bnorm=state.m_b.Dot(state.m_b);
|
|
bnorm=MathSqrt(bnorm);
|
|
//--- Output first report
|
|
if(!state.m_xrep)
|
|
{
|
|
label=8;
|
|
break;
|
|
}
|
|
state.m_x=state.m_rx;
|
|
ClearrFields(state);
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=1;
|
|
label=-1;
|
|
break;
|
|
case 1:
|
|
state.m_xupdated=false;
|
|
case 8:
|
|
//--- Is x0 a solution?
|
|
if(!MathIsValidNumber(state.m_r2) || MathSqrt(state.m_r2)<=(state.m_epsf*bnorm))
|
|
{
|
|
state.m_running=false;
|
|
if(MathIsValidNumber(state.m_r2))
|
|
state.m_repterminationtype=1;
|
|
else
|
|
state.m_repterminationtype=-4;
|
|
return(false);
|
|
}
|
|
//--- Calculate Z and P
|
|
state.m_x=state.m_r;
|
|
state.m_repnmv++;
|
|
ClearrFields(state);
|
|
state.m_needprec=true;
|
|
state.m_rstate.stage=2;
|
|
label=-1;
|
|
break;
|
|
case 2:
|
|
state.m_needprec=false;
|
|
state.m_z=state.m_pv;
|
|
state.m_p=state.m_z;
|
|
//--- Other iterations(1..N)
|
|
state.m_repiterationscount=0;
|
|
case 10:
|
|
state.m_repiterationscount++;
|
|
//--- Calculate Alpha
|
|
state.m_x=state.m_p;
|
|
state.m_repnmv++;
|
|
ClearrFields(state);
|
|
state.m_needvmv=true;
|
|
state.m_rstate.stage=3;
|
|
label=-1;
|
|
break;
|
|
case 3:
|
|
state.m_needvmv=false;
|
|
if(!MathIsValidNumber(state.m_vmv) || state.m_vmv<=0.0)
|
|
{
|
|
//--- a) Overflow when calculating VMV
|
|
//--- b) non-positive VMV (non-SPD matrix)
|
|
state.m_running=false;
|
|
if(MathIsValidNumber(state.m_vmv))
|
|
state.m_repterminationtype=-5;
|
|
else
|
|
state.m_repterminationtype=-4;
|
|
return(false);
|
|
}
|
|
state.m_alpha=state.m_r.Dot(state.m_z);
|
|
state.m_alpha=state.m_alpha/state.m_vmv;
|
|
if(!MathIsValidNumber(state.m_alpha))
|
|
{
|
|
//--- Overflow when calculating Alpha
|
|
state.m_running=false;
|
|
state.m_repterminationtype=-4;
|
|
return(false);
|
|
}
|
|
//--- Next step toward solution
|
|
state.m_cx=state.m_rx.ToVector()+state.m_p*state.m_alpha;
|
|
//--- Calculate R:
|
|
//--- * use recurrent relation to update R
|
|
//--- * at every ItsBeforeRUpdate-th iteration recalculate it from scratch, using matrix-vector product
|
|
//--- in case R grows instead of decreasing, algorithm is terminated with positive completion code
|
|
if(!(state.m_itsbeforerupdate==0 || state.m_repiterationscount%state.m_itsbeforerupdate!=0))
|
|
{
|
|
label=12;
|
|
break;
|
|
}
|
|
//--- Calculate R using recurrent formula
|
|
state.m_cr=state.m_r.ToVector()-state.m_mv*state.m_alpha;
|
|
state.m_x=state.m_cr;
|
|
label=13;
|
|
break;
|
|
case 12:
|
|
//--- Calculate R using matrix-vector multiplication
|
|
state.m_x=state.m_cx;
|
|
state.m_repnmv++;
|
|
ClearrFields(state);
|
|
state.m_needmv=true;
|
|
state.m_rstate.stage=4;
|
|
label=-1;
|
|
break;
|
|
case 4:
|
|
state.m_needmv=false;
|
|
state.m_cr=state.m_b-state.m_mv+0;
|
|
state.m_x=state.m_cr;
|
|
//--- Calculating merit function
|
|
//--- Check emergency stopping criterion
|
|
v=state.m_mv.Dot(state.m_cx)-2*state.m_b.Dot(state.m_cx);
|
|
if(v<state.m_meritfunction)
|
|
{
|
|
label=14;
|
|
break;
|
|
}
|
|
if(!CApServ::IsFiniteVector(state.m_rx,state.m_n))
|
|
{
|
|
state.m_running=false;
|
|
state.m_repterminationtype=-4;
|
|
return(false);
|
|
}
|
|
//output last report
|
|
if(!state.m_xrep)
|
|
{
|
|
label=16;
|
|
break;
|
|
}
|
|
state.m_x=state.m_rx;
|
|
ClearrFields(state);
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=5;
|
|
label=-1;
|
|
break;
|
|
case 5:
|
|
state.m_xupdated=false;
|
|
case 16:
|
|
state.m_running=false;
|
|
state.m_repterminationtype=7;
|
|
return(false);
|
|
case 14:
|
|
state.m_meritfunction=v;
|
|
case 13:
|
|
state.m_rx=state.m_cx;
|
|
//--- calculating RNorm
|
|
//--- NOTE: monotonic decrease of R2 is not guaranteed by algorithm.
|
|
state.m_r2=state.m_cr.Dot(state.m_cr);
|
|
//output report
|
|
if(!state.m_xrep)
|
|
{
|
|
label=18;
|
|
break;
|
|
}
|
|
state.m_x=state.m_rx;
|
|
ClearrFields(state);
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=6;
|
|
label=-1;
|
|
break;
|
|
case 6:
|
|
state.m_xupdated=false;
|
|
case 18:
|
|
//stopping criterion
|
|
//achieved the required precision
|
|
if(!MathIsValidNumber(state.m_r2) || (MathSqrt(state.m_r2)<=(state.m_epsf*bnorm)))
|
|
{
|
|
state.m_running=false;
|
|
if(MathIsValidNumber(state.m_r2))
|
|
state.m_repterminationtype=1;
|
|
else
|
|
state.m_repterminationtype=-4;
|
|
return(false);
|
|
}
|
|
if(state.m_repiterationscount>=state.m_maxits && state.m_maxits>0)
|
|
{
|
|
if(!CApServ::IsFiniteVector(state.m_rx,state.m_n))
|
|
{
|
|
state.m_running=false;
|
|
state.m_repterminationtype=-4;
|
|
return(false);
|
|
}
|
|
//if X is finite number
|
|
state.m_running=false;
|
|
state.m_repterminationtype=5;
|
|
return(false);
|
|
}
|
|
state.m_x=state.m_cr;
|
|
//prepere of parameters for next iteration
|
|
state.m_repnmv++;
|
|
ClearrFields(state);
|
|
state.m_needprec=true;
|
|
state.m_rstate.stage=7;
|
|
label=-1;
|
|
break;
|
|
case 7:
|
|
state.m_needprec=false;
|
|
state.m_cz=state.m_pv;
|
|
if(state.m_repiterationscount%state.m_itsbeforerestart!=0)
|
|
{
|
|
state.m_beta=state.m_cz.Dot(state.m_cr);
|
|
uvar=state.m_z.Dot(state.m_r);
|
|
//check that UVar is't INF or is't zero
|
|
if(!MathIsValidNumber(uvar) || uvar==0.0)
|
|
{
|
|
state.m_running=false;
|
|
state.m_repterminationtype=-4;
|
|
return(false);
|
|
}
|
|
//calculate .BETA
|
|
state.m_beta/=uvar;
|
|
//check that .BETA neither INF nor NaN
|
|
if(!MathIsValidNumber(state.m_beta))
|
|
{
|
|
state.m_running=false;
|
|
state.m_repterminationtype=-1;
|
|
return(false);
|
|
}
|
|
state.m_p=state.m_cz.ToVector()+state.m_p*state.m_beta;
|
|
}
|
|
else
|
|
state.m_p=state.m_cz;
|
|
//prepere data for next iteration
|
|
//write (k+1)th iteration to (k )th iteration
|
|
state.m_r=state.m_cr;
|
|
state.m_z=state.m_cz;
|
|
label=10;
|
|
break;
|
|
case 11:
|
|
return(false);
|
|
}
|
|
}
|
|
//--- Saving state
|
|
state.m_rstate.ia.Set(0,i);
|
|
state.m_rstate.ra.Set(0,uvar);
|
|
state.m_rstate.ra.Set(1,bnorm);
|
|
state.m_rstate.ra.Set(2,v);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Procedure for solution of A*x = b with sparse A. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - algorithm state |
|
|
//| A - sparse matrix in the CRS format (you MUST |
|
|
//| contvert it to CRS format by calling |
|
|
//| SparseConvertToCRS() function). |
|
|
//| IsUpper - whether upper or lower triangle of A is used: |
|
|
//| * IsUpper = True => only upper triangle is used |
|
|
//| and lower triangle is not |
|
|
//| referenced at all |
|
|
//| * IsUpper = False => only lower triangle is used|
|
|
//| and upper triangle is not |
|
|
//| referenced at all |
|
|
//| B - right part, array[N] |
|
|
//| RESULT: |
|
|
//| This function returns no result. |
|
|
//| You can get solution by calling LinCGResults() |
|
|
//| NOTE: this function uses lightweight preconditioning - |
|
|
//| multiplication by inverse of diag(A). If you want, you can |
|
|
//| turn preconditioning off by calling LinCGSetPrecUnit(). |
|
|
//| However, preconditioning cost is low and preconditioner is |
|
|
//| very important for solution of badly scaled problems. |
|
|
//+------------------------------------------------------------------+
|
|
void CLinCG::LinCGSolveSparse(CLinCGState &state,CSparseMatrix &a,
|
|
bool IsUpper,CRowDouble &b)
|
|
{
|
|
//--- create variables
|
|
int n=state.m_n;
|
|
double v=0;
|
|
double vmv=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(b)>=state.m_n,__FUNCTION__+": Length(B)<N"))
|
|
return;
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(b,state.m_n),__FUNCTION__+": B contains infinite or NaN values!"))
|
|
return;
|
|
//--- Allocate temporaries
|
|
state.m_tmpd.Resize(n);
|
|
//--- Compute diagonal scaling matrix D
|
|
if(state.m_prectype==0)
|
|
{
|
|
//--- Default preconditioner - inverse of matrix diagonal
|
|
for(int i=0; i<n; i++)
|
|
{
|
|
v=CSparse::SparseGetDiagonal(a,i);
|
|
if(v>0.0)
|
|
state.m_tmpd.Set(i,1/MathSqrt(v));
|
|
else
|
|
state.m_tmpd.Set(i,1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- No diagonal scaling
|
|
state.m_tmpd.Fill(1);
|
|
}
|
|
//--- Solve
|
|
LinCGRestart(state);
|
|
LinCGSetB(state,b);
|
|
while(LinCGIteration(state))
|
|
{
|
|
//--- Process different requests from optimizer
|
|
if(state.m_needmv)
|
|
CSparse::SparseSMV(a,IsUpper,state.m_x,state.m_mv);
|
|
if(state.m_needvmv)
|
|
{
|
|
CSparse::SparseSMV(a,IsUpper,state.m_x,state.m_mv);
|
|
vmv=state.m_x.Dot(state.m_mv);
|
|
state.m_vmv=vmv;
|
|
}
|
|
if(state.m_needprec)
|
|
state.m_pv=state.m_x.ToVector()*state.m_tmpd.Pow(2);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| CG - solver: results. |
|
|
//| This function must be called after LinCGSolve |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - algorithm state |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[N], solution |
|
|
//| Rep - optimization report: |
|
|
//| * Rep.TerminationType completetion code: |
|
|
//| * -5 input matrix is either not positive |
|
|
//| definite, too large or too small |
|
|
//| * -4 overflow / underflow during solution |
|
|
//| (ill conditioned problem) |
|
|
//| * 1 || residual || <= EpsF* || b || |
|
|
//| * 5 MaxIts steps was taken |
|
|
//| * 7 rounding errors prevent further |
|
|
//| progress, best point found is returned |
|
|
//| * Rep.IterationsCount contains iterations count |
|
|
//| * NMV countains number of matrix - vector |
|
|
//| calculations |
|
|
//+------------------------------------------------------------------+
|
|
void CLinCG::LinCGResult(CLinCGState &state,CRowDouble &x,
|
|
CLinCGReport &rep)
|
|
{
|
|
x.Resize(0);
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_running,__FUNCTION__+": you can not get result,because function LinCGIteration has been launched!"))
|
|
return;
|
|
|
|
if(CAp::Len(x)<state.m_n)
|
|
x.Resize(state.m_n);
|
|
x=state.m_rx;
|
|
rep.m_iterationscount=state.m_repiterationscount;
|
|
rep.m_nmv=state.m_repnmv;
|
|
rep.m_terminationtype=state.m_repterminationtype;
|
|
rep.m_r2=state.m_r2;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets restart frequency. By default, algorithm is |
|
|
//| restarted after N subsequent iterations. |
|
|
//+------------------------------------------------------------------+
|
|
void CLinCG::LinCGSetRestartFreq(CLinCGState &state,int srf)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_running,__FUNCTION__+": you can not change restart frequency when LinCGIteration() is running"))
|
|
return;
|
|
if(!CAp::Assert(srf>0,__FUNCTION__+": non-positive SRF"))
|
|
return;
|
|
|
|
state.m_itsbeforerestart=srf;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets frequency of residual recalculations. |
|
|
//| Algorithm updates residual r_k using iterative formula, but |
|
|
//| recalculates it from scratch after each 10 iterations. It is done|
|
|
//| to avoid accumulation of numerical errors and to stop algorithm |
|
|
//| when r_k starts to grow. |
|
|
//| Such low update frequence(1 / 10) gives very little overhead, |
|
|
//| but makes algorithm a bit more robust against numerical errors. |
|
|
//| However, you may change it |
|
|
//| INPUT PARAMETERS: |
|
|
//| Freq - desired update frequency, Freq >= 0. |
|
|
//| Zero value means that no updates will be done. |
|
|
//+------------------------------------------------------------------+
|
|
void CLinCG::LinCGSetRUpdateFreq(CLinCGState &state,int freq)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_running,__FUNCTION__+": you can not change update frequency when LinCGIteration() is running"))
|
|
return;
|
|
if(!CAp::Assert(freq>=0,__FUNCTION__+": non-positive Freq"))
|
|
return;
|
|
|
|
state.m_itsbeforerupdate=freq;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function turns on / off reporting. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NeedXRep - whether iteration reports are needed or not |
|
|
//| If NeedXRep is True, algorithm will call rep() callback function |
|
|
//| if it is provided to MinCGOptimize(). |
|
|
//+------------------------------------------------------------------+
|
|
void CLinCG::LinCGSetXRep(CLinCGState &state,bool needxrep)
|
|
{
|
|
state.m_xrep=needxrep;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Procedure for restart function LinCGIteration |
|
|
//+------------------------------------------------------------------+
|
|
void CLinCG::LinCGRestart(CLinCGState &state)
|
|
{
|
|
state.m_rstate.ia.Resize(1);
|
|
state.m_rstate.ra.Resize(3);
|
|
state.m_rstate.stage=-1;
|
|
ClearrFields(state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Clears request fileds (to be sure that we don't forgot to clear |
|
|
//| something) |
|
|
//+------------------------------------------------------------------+
|
|
void CLinCG::ClearrFields(CLinCGState &state)
|
|
{
|
|
state.m_xupdated=false;
|
|
state.m_needmv=false;
|
|
state.m_needmtv=false;
|
|
state.m_needmv2=false;
|
|
state.m_needvmv=false;
|
|
state.m_needprec=false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Clears request fileds (to be sure that we don't forgot to clear |
|
|
//| something) |
|
|
//+------------------------------------------------------------------+
|
|
void CLinCG::UpdateItersData(CLinCGState &state)
|
|
{
|
|
state.m_repiterationscount=0;
|
|
state.m_repnmv=0;
|
|
state.m_repterminationtype=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This object stores state of the LinLSQR method. |
|
|
//| You should use ALGLIB functions to work with this object. |
|
|
//+------------------------------------------------------------------+
|
|
struct CLinLSQRState
|
|
{
|
|
int m_m;
|
|
int m_maxits;
|
|
int m_n;
|
|
int m_prectype;
|
|
int m_repiterationscount;
|
|
int m_repnmv;
|
|
int m_repterminationtype;
|
|
double m_alphai;
|
|
double m_alphaip1;
|
|
double m_anorm;
|
|
double m_betai;
|
|
double m_betaip1;
|
|
double m_bnorm2;
|
|
double m_ci;
|
|
double m_dnorm;
|
|
double m_epsa;
|
|
double m_epsb;
|
|
double m_epsc;
|
|
double m_lambdai;
|
|
double m_phibari;
|
|
double m_phibarip1;
|
|
double m_phii;
|
|
double m_r2;
|
|
double m_rhobari;
|
|
double m_rhobarip1;
|
|
double m_rhoi;
|
|
double m_si;
|
|
double m_theta;
|
|
bool m_needmtv;
|
|
bool m_needmv2;
|
|
bool m_needmv;
|
|
bool m_needprec;
|
|
bool m_needvmv;
|
|
bool m_running;
|
|
bool m_userterminationneeded;
|
|
bool m_xrep;
|
|
bool m_xupdated;
|
|
RCommState m_rstate;
|
|
CRowDouble m_b;
|
|
CRowDouble m_d;
|
|
CRowDouble m_mtv;
|
|
CRowDouble m_mv;
|
|
CRowDouble m_omegai;
|
|
CRowDouble m_omegaip1;
|
|
CRowDouble m_rx;
|
|
CRowDouble m_tmpd;
|
|
CRowDouble m_tmpx;
|
|
CRowDouble m_ui;
|
|
CRowDouble m_uip1;
|
|
CRowDouble m_vi;
|
|
CRowDouble m_vip1;
|
|
CRowDouble m_x;
|
|
CNormEstimatorState m_nes;
|
|
//--- constructor / destructor
|
|
CLinLSQRState(void);
|
|
~CLinLSQRState(void) {}
|
|
//---
|
|
void Copy(const CLinLSQRState &obj);
|
|
//--- overloading
|
|
void operator=(const CLinLSQRState &obj) { Copy(obj); }
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor |
|
|
//+------------------------------------------------------------------+
|
|
CLinLSQRState::CLinLSQRState(void)
|
|
{
|
|
m_m=0;
|
|
m_maxits=0;
|
|
m_n=0;
|
|
m_prectype=0;
|
|
m_repiterationscount=0;
|
|
m_repnmv=0;
|
|
m_repterminationtype=0;
|
|
m_alphai=0;
|
|
m_alphaip1=0;
|
|
m_anorm=0;
|
|
m_betai=0;
|
|
m_betaip1=0;
|
|
m_bnorm2=0;
|
|
m_ci=0;
|
|
m_dnorm=0;
|
|
m_epsa=0;
|
|
m_epsb=0;
|
|
m_epsc=0;
|
|
m_lambdai=0;
|
|
m_phibari=0;
|
|
m_phibarip1=0;
|
|
m_phii=0;
|
|
m_r2=0;
|
|
m_rhobari=0;
|
|
m_rhobarip1=0;
|
|
m_rhoi=0;
|
|
m_si=0;
|
|
m_theta=0;
|
|
m_needmtv=false;
|
|
m_needmv2=false;
|
|
m_needmv=false;
|
|
m_needprec=false;
|
|
m_needvmv=false;
|
|
m_running=false;
|
|
m_userterminationneeded=false;
|
|
m_xrep=false;
|
|
m_xupdated=false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CLinLSQRState::Copy(const CLinLSQRState &obj)
|
|
{
|
|
m_m=obj.m_m;
|
|
m_maxits=obj.m_maxits;
|
|
m_n=obj.m_n;
|
|
m_prectype=obj.m_prectype;
|
|
m_repiterationscount=obj.m_repiterationscount;
|
|
m_repnmv=obj.m_repnmv;
|
|
m_repterminationtype=obj.m_repterminationtype;
|
|
m_alphai=obj.m_alphai;
|
|
m_alphaip1=obj.m_alphaip1;
|
|
m_anorm=obj.m_anorm;
|
|
m_betai=obj.m_betai;
|
|
m_betaip1=obj.m_betaip1;
|
|
m_bnorm2=obj.m_bnorm2;
|
|
m_ci=obj.m_ci;
|
|
m_dnorm=obj.m_dnorm;
|
|
m_epsa=obj.m_epsa;
|
|
m_epsb=obj.m_epsb;
|
|
m_epsc=obj.m_epsc;
|
|
m_lambdai=obj.m_lambdai;
|
|
m_phibari=obj.m_phibari;
|
|
m_phibarip1=obj.m_phibarip1;
|
|
m_phii=obj.m_phii;
|
|
m_r2=obj.m_r2;
|
|
m_rhobari=obj.m_rhobari;
|
|
m_rhobarip1=obj.m_rhobarip1;
|
|
m_rhoi=obj.m_rhoi;
|
|
m_si=obj.m_si;
|
|
m_theta=obj.m_theta;
|
|
m_needmtv=obj.m_needmtv;
|
|
m_needmv2=obj.m_needmv2;
|
|
m_needmv=obj.m_needmv;
|
|
m_needprec=obj.m_needprec;
|
|
m_needvmv=obj.m_needvmv;
|
|
m_running=obj.m_running;
|
|
m_userterminationneeded=obj.m_userterminationneeded;
|
|
m_xrep=obj.m_xrep;
|
|
m_xupdated=obj.m_xupdated;
|
|
m_rstate=obj.m_rstate;
|
|
m_b=obj.m_b;
|
|
m_d=obj.m_d;
|
|
m_mtv=obj.m_mtv;
|
|
m_mv=obj.m_mv;
|
|
m_omegai=obj.m_omegai;
|
|
m_omegaip1=obj.m_omegaip1;
|
|
m_rx=obj.m_rx;
|
|
m_tmpd=obj.m_tmpd;
|
|
m_tmpx=obj.m_tmpx;
|
|
m_ui=obj.m_ui;
|
|
m_uip1=obj.m_uip1;
|
|
m_vi=obj.m_vi;
|
|
m_vip1=obj.m_vip1;
|
|
m_x=obj.m_x;
|
|
m_nes=obj.m_nes;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
struct CLinLSQRReport
|
|
{
|
|
int m_iterationscount;
|
|
int m_nmv;
|
|
int m_terminationtype;
|
|
//--- constructor / destructor
|
|
CLinLSQRReport(void) { ZeroMemory(this); }
|
|
~CLinLSQRReport(void) {}
|
|
//---
|
|
void Copy(const CLinLSQRReport &obj);
|
|
//--- overloading
|
|
void operator=(const CLinLSQRReport &obj) { Copy(obj); }
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CLinLSQRReport::Copy(const CLinLSQRReport &obj)
|
|
{
|
|
m_iterationscount=obj.m_iterationscount;
|
|
m_nmv=obj.m_nmv;
|
|
m_terminationtype=obj.m_terminationtype;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class CLinLSQR
|
|
{
|
|
public:
|
|
//--- constants
|
|
static const double m_atol;
|
|
static const double m_btol;
|
|
|
|
static void LinLSQRCreate(int m,int n,CLinLSQRState &state);
|
|
static void LinLSQRCreateBuf(int m,int n,CLinLSQRState &state);
|
|
static void LinLSQRSetB(CLinLSQRState &state,CRowDouble &b);
|
|
static void LinLSQRSetPrecUnit(CLinLSQRState &state);
|
|
static void LinLSQRSetPrecDiag(CLinLSQRState &state);
|
|
static void LinLSQRSetLambdaI(CLinLSQRState &state,double lambdai);
|
|
static bool LinLSQRIteration(CLinLSQRState &state);
|
|
static void LinLSQRSolveSparse(CLinLSQRState &state,CSparseMatrix &a,CRowDouble &b);
|
|
static void LinLSQRSetCond(CLinLSQRState &state,double epsa,double epsb,int maxits);
|
|
static void LinLSQRResults(CLinLSQRState &state,CRowDouble &x,CLinLSQRReport &rep);
|
|
static void LinLSQRSetXRep(CLinLSQRState &state,bool needxrep);
|
|
static void CLinLSQR::LinLSQRRestart(CLinLSQRState &state);
|
|
static int LinLSQRPeekIterationsCount(CLinLSQRState &s);
|
|
static void LinLSQRRequestTermination(CLinLSQRState &state);
|
|
|
|
private:
|
|
static void ClearrFields(CLinLSQRState &state);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constants |
|
|
//+------------------------------------------------------------------+
|
|
const double CLinLSQR::m_atol=1.0E-6;
|
|
const double CLinLSQR::m_btol=1.0E-6;
|
|
//+------------------------------------------------------------------+
|
|
//| This function initializes linear LSQR Solver. This solver is used|
|
|
//| to solve non-symmetric (and, possibly, non-square) problems. |
|
|
//| Least squares solution is returned for non - compatible systems. |
|
|
//| USAGE: |
|
|
//| 1. User initializes algorithm state with LinLSQRCreate() call |
|
|
//| 2. User tunes solver parameters with LinLSQRSetCond() and other|
|
|
//| functions |
|
|
//| 3. User calls LinLSQRSolveSparse() function which takes |
|
|
//| algorithm state and SparseMatrix object. |
|
|
//| 4. User calls LinLSQRResults() to get solution |
|
|
//| 5. Optionally, user may call LinLSQRSolveSparse() again to |
|
|
//| solve another problem with different matrix and/or right |
|
|
//| part without reinitializing LinLSQRState structure. |
|
|
//| INPUT PARAMETERS: |
|
|
//| M - number of rows in A |
|
|
//| N - number of variables, N > 0 |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NOTE: see also LinLSQRCreateBuf() for version which reuses |
|
|
//| previously allocated place as much as possible. |
|
|
//+------------------------------------------------------------------+
|
|
void CLinLSQR::LinLSQRCreate(int m,int n,CLinLSQRState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(m>0,__FUNCTION__+": M<=0"))
|
|
return;
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0"))
|
|
return;
|
|
//--- function call
|
|
LinLSQRCreateBuf(m,n,state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function initializes linear LSQR Solver. It provides exactly|
|
|
//| same functionality as LinLSQRCreate(), but reuses previously |
|
|
//| allocated space as much as possible. |
|
|
//| INPUT PARAMETERS: |
|
|
//| M - number of rows in A |
|
|
//| N - number of variables, N > 0 |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
void CLinLSQR::LinLSQRCreateBuf(int m,int n,CLinLSQRState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(m>0,__FUNCTION__+": M<=0"))
|
|
return;
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0"))
|
|
return;
|
|
|
|
state.m_m=m;
|
|
state.m_n=n;
|
|
state.m_prectype=0;
|
|
state.m_epsa=m_atol;
|
|
state.m_epsb=m_btol;
|
|
state.m_epsc=1/MathSqrt(CMath::m_machineepsilon);
|
|
state.m_maxits=0;
|
|
state.m_lambdai=0;
|
|
state.m_xrep=false;
|
|
state.m_running=false;
|
|
state.m_repiterationscount=0;
|
|
//--- * allocate arrays
|
|
//--- * set RX to NAN (just for the case user calls Results() without
|
|
//--- calling SolveSparse()
|
|
//--- * set B to zero
|
|
CNormEstimator::NormEstimatorCreate(m,n,2,2,state.m_nes);
|
|
state.m_rx=vector<double>::Full(state.m_n,AL_NaN);
|
|
state.m_ui=vector<double>::Zeros(state.m_m+state.m_n);
|
|
state.m_uip1=vector<double>::Zeros(state.m_m+state.m_n);
|
|
state.m_vip1=vector<double>::Zeros(state.m_n);
|
|
state.m_vi=vector<double>::Zeros(state.m_n);
|
|
state.m_omegai=vector<double>::Zeros(state.m_n);
|
|
state.m_omegaip1=vector<double>::Zeros(state.m_n);
|
|
state.m_d=vector<double>::Zeros(state.m_n);
|
|
state.m_x=vector<double>::Zeros(state.m_m+state.m_n);
|
|
state.m_mv=vector<double>::Zeros(state.m_m+state.m_n);
|
|
state.m_mtv=vector<double>::Zeros(state.m_n);
|
|
state.m_b=vector<double>::Zeros(state.m_m);
|
|
state.m_rstate.ia.Resize(2);
|
|
state.m_rstate.ra=vector<double>::Zeros(1);
|
|
state.m_rstate.stage=-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets right part. By default, right part is zero. |
|
|
//| INPUT PARAMETERS: |
|
|
//| B - right part, array[N]. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
void CLinLSQR::LinLSQRSetB(CLinLSQRState &state,CRowDouble &b)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_running,__FUNCTION__+": you can not change B when LinLSQRIteration is running"))
|
|
return;
|
|
if(!CAp::Assert(state.m_m<=CAp::Len(b),__FUNCTION__+": Length(B)<M"))
|
|
return;
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(b,state.m_m),__FUNCTION__+": B contains infinite or NaN values"))
|
|
return;
|
|
|
|
state.m_b=b;
|
|
state.m_b.Resize(state.m_m);
|
|
state.m_bnorm2=state.m_b.Dot(state.m_b);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function changes preconditioning settings of |
|
|
//| LinLSQQSolveSparse() function. By default, SolveSparse() uses |
|
|
//| diagonal preconditioner, but if you want to use solver without |
|
|
//| preconditioning, you can call this function which forces solver |
|
|
//| to use unit matrix for preconditioning. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
void CLinLSQR::LinLSQRSetPrecUnit(CLinLSQRState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_running,__FUNCTION__+": you can not change preconditioner,because function LinLSQRIteration is running!"))
|
|
return;
|
|
|
|
state.m_prectype=-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function changes preconditioning settings of |
|
|
//| LinCGSolveSparse() function. LinCGSolveSparse() will use diagonal|
|
|
//| of the system matrix as preconditioner. This preconditioning mode|
|
|
//| is active by default. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
void CLinLSQR::LinLSQRSetPrecDiag(CLinLSQRState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_running,__FUNCTION__+": you can not change preconditioner,because function LinCGIteration is running!"))
|
|
return;
|
|
|
|
state.m_prectype=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets optional Tikhonov regularization coefficient. |
|
|
//| It is zero by default. |
|
|
//| INPUT PARAMETERS: |
|
|
//| LambdaI - regularization factor, LambdaI >= 0 |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
void CLinLSQR::LinLSQRSetLambdaI(CLinLSQRState &state,double lambdai)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_running,"LinLSQRSetLambdaI: you can not set LambdaI,because function LinLSQRIteration is running"))
|
|
return;
|
|
if(!CAp::Assert(MathIsValidNumber(lambdai) && lambdai>=0.0,"LinLSQRSetLambdaI: LambdaI is infinite or NaN"))
|
|
return;
|
|
|
|
state.m_lambdai=lambdai;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool CLinLSQR::LinLSQRIteration(CLinLSQRState &state)
|
|
{
|
|
//--- create variables
|
|
int summn=0;
|
|
double bnorm=0;
|
|
int i=0;
|
|
int i_=0;
|
|
int label=-1;
|
|
//--- Reverse communication preparations
|
|
//--- This code initializes locals by:
|
|
//--- * random values determined during code
|
|
//--- generation - on first subroutine call
|
|
//--- * values from previous call - on subsequent calls
|
|
if(state.m_rstate.stage>=0)
|
|
{
|
|
summn=state.m_rstate.ia[0];
|
|
i=state.m_rstate.ia[1];
|
|
bnorm=state.m_rstate.ra[0];
|
|
}
|
|
else
|
|
{
|
|
summn=359;
|
|
i=-58;
|
|
bnorm=-919;
|
|
}
|
|
switch(state.m_rstate.stage)
|
|
{
|
|
case 0:
|
|
label=0;
|
|
break;
|
|
case 1:
|
|
label=1;
|
|
break;
|
|
case 2:
|
|
label=2;
|
|
break;
|
|
case 3:
|
|
label=3;
|
|
break;
|
|
case 4:
|
|
label=4;
|
|
break;
|
|
case 5:
|
|
label=5;
|
|
break;
|
|
case 6:
|
|
label=6;
|
|
break;
|
|
default:
|
|
//--- Routine body
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(state.m_b)>0,__FUNCTION__+": using non-allocated array B"))
|
|
return(false);
|
|
summn=state.m_m+state.m_n;
|
|
bnorm=MathSqrt(state.m_bnorm2);
|
|
state.m_userterminationneeded=false;
|
|
state.m_running=true;
|
|
state.m_repnmv=0;
|
|
state.m_repiterationscount=0;
|
|
state.m_r2=state.m_bnorm2;
|
|
ClearrFields(state);
|
|
//estimate for ANorm
|
|
CNormEstimator::NormEstimatorRestart(state.m_nes);
|
|
label=7;
|
|
break;
|
|
}
|
|
//--- main loop
|
|
while(label>=0)
|
|
switch(label)
|
|
{
|
|
case 7:
|
|
if(!CNormEstimator::NormEstimatorIteration(state.m_nes))
|
|
{
|
|
label=8;
|
|
break;
|
|
}
|
|
if(!state.m_nes.m_NeedMv)
|
|
{
|
|
label=9;
|
|
break;
|
|
}
|
|
for(i_=0; i_<state.m_n; i_++)
|
|
state.m_x.Set(i_,state.m_nes.m_X[i_]);
|
|
state.m_repnmv++;
|
|
ClearrFields(state);
|
|
state.m_needmv=true;
|
|
state.m_rstate.stage=0;
|
|
label=-1;
|
|
break;
|
|
case 0:
|
|
state.m_needmv=false;
|
|
state.m_nes.m_Mv=state.m_mv;
|
|
label=7;
|
|
break;
|
|
case 9:
|
|
if(!state.m_nes.m_NeedMtv)
|
|
{
|
|
label=11;
|
|
break;
|
|
}
|
|
for(i_=0; i_<state.m_n; i_++)
|
|
state.m_x.Set(i_,state.m_nes.m_X[i_]);
|
|
//matrix-vector multiplication
|
|
state.m_repnmv++;
|
|
ClearrFields(state);
|
|
state.m_needmtv=true;
|
|
state.m_rstate.stage=1;
|
|
label=-1;
|
|
break;
|
|
case 1:
|
|
state.m_needmtv=false;
|
|
state.m_nes.m_Mtv=state.m_mtv;
|
|
label=7;
|
|
break;
|
|
case 11:
|
|
label=7;
|
|
break;
|
|
case 8:
|
|
CNormEstimator::NormEstimatorResults(state.m_nes,state.m_anorm);
|
|
//--- initialize .RX by zeros
|
|
state.m_rx.Fill(0);
|
|
//output first report
|
|
if(!state.m_xrep)
|
|
{
|
|
label=13;
|
|
break;
|
|
}
|
|
for(i_=0; i_<state.m_n; i_++)
|
|
state.m_x.Set(i_,state.m_rx[i_]);
|
|
ClearrFields(state);
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=2;
|
|
label=-1;
|
|
break;
|
|
case 2:
|
|
state.m_xupdated=false;
|
|
case 13:
|
|
//--- LSQR, Step 0.
|
|
//--- Algorithm outline corresponds to one which was described at p.50 of
|
|
//--- "LSQR - an algorithm for sparse linear equations and sparse least
|
|
//--- squares" by C.Paige and M.Saunders with one small addition - we
|
|
//--- explicitly extend system matrix by additional N lines in order
|
|
//--- to handle non-zero lambda, i.e. original A is replaced by
|
|
//--- [ A ]
|
|
//--- A_mod = [ ]
|
|
//--- [ lambda*I ].
|
|
//--- Step 0:
|
|
//--- x[0] = 0
|
|
//--- beta[1]*u[1] = b
|
|
//--- alpha[1]*v[1] = A_mod'*u[1]
|
|
//--- w[1] = v[1]
|
|
//--- phiBar[1] = beta[1]
|
|
//--- rhoBar[1] = alpha[1]
|
|
//--- d[0] = 0
|
|
//--- NOTE:
|
|
//--- There are three criteria for stopping:
|
|
//--- (S0) maximum number of iterations
|
|
//--- (S1) ||Rk||<=EpsB*||B||;
|
|
//--- (S2) ||A^T*Rk||/(||A||*||Rk||)<=EpsA.
|
|
//--- It is very important that S2 always checked AFTER S1. It is necessary
|
|
//--- to avoid division by zero when Rk=0.
|
|
state.m_betai=bnorm;
|
|
if(state.m_betai==0.0)
|
|
{
|
|
//--- Zero right part
|
|
state.m_running=false;
|
|
state.m_repterminationtype=1;
|
|
return(false);
|
|
}
|
|
for(i=0; i<summn; i++)
|
|
{
|
|
if(i<state.m_m)
|
|
state.m_ui.Set(i,state.m_b[i]/state.m_betai);
|
|
else
|
|
state.m_ui.Set(i,0);
|
|
state.m_x.Set(i,state.m_ui[i]);
|
|
}
|
|
state.m_repnmv++;
|
|
ClearrFields(state);
|
|
state.m_needmtv=true;
|
|
state.m_rstate.stage=3;
|
|
label=-1;
|
|
break;
|
|
case 3:
|
|
state.m_needmtv=false;
|
|
for(i=0; i<state.m_n; i++)
|
|
state.m_mtv.Add(i,state.m_lambdai*state.m_ui[state.m_m+i]);
|
|
state.m_alphai=state.m_mtv.Dot(state.m_mtv);
|
|
state.m_alphai=MathSqrt(state.m_alphai);
|
|
if(state.m_alphai==0.0)
|
|
{
|
|
//--- Orthogonality stopping criterion is met
|
|
state.m_running=false;
|
|
state.m_repterminationtype=4;
|
|
return(false);
|
|
}
|
|
state.m_vi=state.m_mtv/state.m_alphai+0;
|
|
state.m_omegai=state.m_vi;
|
|
state.m_phibari=state.m_betai;
|
|
state.m_rhobari=state.m_alphai;
|
|
state.m_d.Fill(0);
|
|
state.m_dnorm=0;
|
|
//--- Steps I=1, 2, ...
|
|
case 15:
|
|
//--- At I-th step State.RepIterationsCount=I.
|
|
state.m_repiterationscount++;
|
|
//--- Bidiagonalization part:
|
|
//--- beta[i+1]*u[i+1] = A_mod*v[i]-alpha[i]*u[i]
|
|
//--- alpha[i+1]*v[i+1] = A_mod'*u[i+1] - beta[i+1]*v[i]
|
|
//--- NOTE: beta[i+1]=0 or alpha[i+1]=0 will lead to successful termination
|
|
//--- in the end of the current iteration. In this case u/v are zero.
|
|
//--- NOTE2: algorithm won't fail on zero alpha or beta (there will be no
|
|
//--- division by zero because it will be stopped BEFORE division
|
|
//--- occurs). However, near-zero alpha and beta won't stop algorithm
|
|
//--- and, although no division by zero will happen, orthogonality
|
|
//--- in U and V will be lost.
|
|
for(i_=0; i_<state.m_n; i_++)
|
|
state.m_x.Set(i_,state.m_vi[i_]);
|
|
state.m_repnmv=state.m_repnmv+1;
|
|
ClearrFields(state);
|
|
state.m_needmv=true;
|
|
state.m_rstate.stage=4;
|
|
label=-1;
|
|
break;
|
|
case 4:
|
|
state.m_needmv=false;
|
|
for(i=0; i<state.m_n; i++)
|
|
state.m_mv.Set(state.m_m+i,state.m_lambdai*state.m_vi[i]);
|
|
state.m_betaip1=0;
|
|
state.m_uip1=state.m_mv.ToVector()-state.m_ui*state.m_alphai;
|
|
state.m_betaip1=CAblasF::RDotV2(summn,state.m_uip1);
|
|
if(state.m_betaip1!=0.0)
|
|
{
|
|
state.m_betaip1=MathSqrt(state.m_betaip1);
|
|
state.m_uip1/=state.m_betaip1;
|
|
}
|
|
state.m_x=state.m_uip1;
|
|
state.m_repnmv++;
|
|
ClearrFields(state);
|
|
state.m_needmtv=true;
|
|
state.m_rstate.stage=5;
|
|
label=-1;
|
|
break;
|
|
case 5:
|
|
state.m_needmtv=false;
|
|
for(i=0; i<state.m_n; i++)
|
|
state.m_mtv.Add(i,state.m_lambdai*state.m_uip1[state.m_m+i]);
|
|
state.m_alphaip1=0;
|
|
state.m_vip1=state.m_mtv.ToVector()-state.m_vi*state.m_betaip1 ;
|
|
state.m_alphaip1=state.m_vip1.Dot(state.m_vip1);
|
|
if(state.m_alphaip1!=0.0)
|
|
{
|
|
state.m_alphaip1=MathSqrt(state.m_alphaip1);
|
|
state.m_vip1/=state.m_alphaip1;
|
|
}
|
|
//--- Build next orthogonal transformation
|
|
state.m_rhoi=CApServ::SafePythag2(state.m_rhobari,state.m_betaip1);
|
|
state.m_ci=state.m_rhobari/state.m_rhoi;
|
|
state.m_si=state.m_betaip1/state.m_rhoi;
|
|
state.m_theta=state.m_si*state.m_alphaip1;
|
|
state.m_rhobarip1=-(state.m_ci*state.m_alphaip1);
|
|
state.m_phii=state.m_ci*state.m_phibari;
|
|
state.m_phibarip1=state.m_si*state.m_phibari;
|
|
//--- Update .RNorm
|
|
//--- This tricky formula is necessary because simply writing
|
|
//--- State.R2:=State.PhiBarIP1*State.PhiBarIP1 does NOT guarantees
|
|
//--- monotonic decrease of R2. Roundoff error combined with 80-bit
|
|
//--- precision used internally by Intel chips allows R2 to increase
|
|
//--- slightly in some rare, but possible cases. This property is
|
|
//--- undesirable, so we prefer to guard against R increase.
|
|
state.m_r2=MathMin(state.m_r2,state.m_phibarip1*state.m_phibarip1);
|
|
//--- Update d and DNorm, check condition-related stopping criteria
|
|
state.m_d=(state.m_vi.ToVector()-state.m_d*state.m_theta)/state.m_rhoi;
|
|
state.m_dnorm=CAblasF::RDotV2(state.m_n,state.m_d);
|
|
if((MathSqrt(state.m_dnorm)*state.m_anorm)>=state.m_epsc)
|
|
{
|
|
state.m_running=false;
|
|
state.m_repterminationtype=7;
|
|
return(false);
|
|
}
|
|
//--- Update x, output report
|
|
state.m_rx+=state.m_omegai.ToVector()*state.m_phii/state.m_rhoi;
|
|
if(!state.m_xrep)
|
|
{
|
|
label=17;
|
|
break;
|
|
}
|
|
for(i_=0; i_<state.m_n; i_++)
|
|
state.m_x.Set(i_,state.m_rx[i_]);
|
|
ClearrFields(state);
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=6;
|
|
label=-1;
|
|
break;
|
|
case 6:
|
|
state.m_xupdated=false;
|
|
case 17:
|
|
//--- Check stopping criteria
|
|
//--- 1. achieved required number of iterations;
|
|
//--- 2. ||Rk||<=EpsB*||B||;
|
|
//--- 3. ||A^T*Rk||/(||A||*||Rk||)<=EpsA;
|
|
if(state.m_maxits>0 && state.m_repiterationscount>=state.m_maxits)
|
|
{
|
|
//--- Achieved required number of iterations
|
|
state.m_running=false;
|
|
state.m_repterminationtype=5;
|
|
return(false);
|
|
}
|
|
if(state.m_phibarip1<=state.m_epsb*bnorm)
|
|
{
|
|
//--- ||Rk||<=EpsB*||B||, here ||Rk||=PhiBar
|
|
state.m_running=false;
|
|
state.m_repterminationtype=1;
|
|
return(false);
|
|
}
|
|
if((state.m_alphaip1*MathAbs(state.m_ci)/state.m_anorm)<=state.m_epsa)
|
|
{
|
|
//--- ||A^T*Rk||/(||A||*||Rk||)<=EpsA, here ||A^T*Rk||=PhiBar*Alpha[i+1]*|.C|
|
|
state.m_running=false;
|
|
state.m_repterminationtype=4;
|
|
return(false);
|
|
}
|
|
if(state.m_userterminationneeded)
|
|
{
|
|
//--- User requested termination
|
|
state.m_running=false;
|
|
state.m_repterminationtype=8;
|
|
return(false);
|
|
}
|
|
//--- Update omega
|
|
state.m_omegaip1=state.m_vip1.ToVector()-state.m_omegai*state.m_theta/state.m_rhoi;
|
|
//--- Prepare for the next iteration - rename variables:
|
|
//--- u[i] := u[i+1]
|
|
//--- v[i] := v[i+1]
|
|
//--- rho[i] := rho[i+1]
|
|
//--- ...
|
|
state.m_ui=state.m_uip1;
|
|
state.m_vi=state.m_vip1;
|
|
state.m_omegai=state.m_omegaip1;
|
|
state.m_alphai=state.m_alphaip1;
|
|
state.m_betai=state.m_betaip1;
|
|
state.m_phibari=state.m_phibarip1;
|
|
state.m_rhobari=state.m_rhobarip1;
|
|
label=15;
|
|
break;
|
|
case 16:
|
|
return(false);
|
|
}
|
|
//--- Saving state
|
|
state.m_rstate.ia.Set(0,summn);
|
|
state.m_rstate.ia.Set(1,i);
|
|
state.m_rstate.ra.Set(0,bnorm);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Procedure for solution of A*x = b with sparse A. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - algorithm state |
|
|
//| A - sparse M*N matrix in the CRS format (you MUST |
|
|
//| contvert it to CRS format by calling |
|
|
//| SparseConvertToCRS() function BEFORE you pass it|
|
|
//| to this function). |
|
|
//| B - right part, array[M] |
|
|
//| RESULT: |
|
|
//| This function returns no result. |
|
|
//| You can get solution by calling LinCGResults() |
|
|
//| NOTE: this function uses lightweight preconditioning - |
|
|
//| multiplication by inverse of diag(A). If you want, you can |
|
|
//| turn preconditioning off by calling LinLSQRSetPrecUnit(). |
|
|
//| However, preconditioning cost is low and preconditioner is |
|
|
//| very important for solution of badly scaled problems. |
|
|
//+------------------------------------------------------------------+
|
|
void CLinLSQR::LinLSQRSolveSparse(CLinLSQRState &state,
|
|
CSparseMatrix &a,CRowDouble &b)
|
|
{
|
|
//--- create variables
|
|
int n=state.m_n;
|
|
int i=0;
|
|
int j=0;
|
|
int t0=0;
|
|
int t1=0;
|
|
double v=0;
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_running,"LinLSQRSolveSparse: you can not call this function when LinLSQRIteration is running"))
|
|
return;
|
|
if(!CAp::Assert(CAp::Len(b)>=state.m_m,"LinLSQRSolveSparse: Length(B)<M"))
|
|
return;
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(b,state.m_m),"LinLSQRSolveSparse: B contains infinite or NaN values"))
|
|
return;
|
|
//--- Allocate temporaries
|
|
state.m_tmpd.Resize(n);
|
|
state.m_tmpx.Resize(n);
|
|
//--- Compute diagonal scaling matrix D
|
|
if(state.m_prectype==0)
|
|
{
|
|
//--- Default preconditioner - inverse of column norms
|
|
state.m_tmpd.Fill(0);
|
|
t0=0;
|
|
t1=0;
|
|
while(CSparse::SparseEnumerate(a,t0,t1,i,j,v))
|
|
state.m_tmpd.Add(j,CMath::Sqr(v));
|
|
for(i=0; i<n; i++)
|
|
{
|
|
if(state.m_tmpd[i]>0.0)
|
|
state.m_tmpd.Set(i,1/MathSqrt(state.m_tmpd[i]));
|
|
else
|
|
state.m_tmpd.Set(i,1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- No diagonal scaling
|
|
state.m_tmpd.Fill(1);
|
|
}
|
|
//--- Solve.
|
|
//--- Instead of solving A*x=b we solve preconditioned system (A*D)*(inv(D)*x)=b.
|
|
//--- Transformed A is not calculated explicitly, we just modify multiplication
|
|
//--- by A or A'. After solution we modify State.RX so it will store untransformed
|
|
//--- variables
|
|
LinLSQRSetB(state,b);
|
|
LinLSQRRestart(state);
|
|
while(LinLSQRIteration(state))
|
|
{
|
|
if(state.m_needmv)
|
|
{
|
|
for(i=0; i<n; i++)
|
|
state.m_tmpx.Set(i,state.m_tmpd[i]*state.m_x[i]);
|
|
CSparse::SparseMV(a,state.m_tmpx,state.m_mv);
|
|
}
|
|
if(state.m_needmtv)
|
|
{
|
|
CSparse::SparseMTV(a,state.m_x,state.m_mtv);
|
|
state.m_mtv*=state.m_tmpd;
|
|
}
|
|
}
|
|
state.m_rx*=state.m_tmpd;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets stopping criteria. |
|
|
//| INPUT PARAMETERS: |
|
|
//| EpsA - algorithm will be stopped if |
|
|
//| || A^T * Rk || / ( || A || * || Rk ||) <= EpsA. |
|
|
//| EpsB - algorithm will be stopped if |
|
|
//| || Rk || <= EpsB * || B || |
|
|
//| MaxIts - algorithm will be stopped if number of |
|
|
//| iterations more than MaxIts. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NOTE: if EpsA, EpsB, EpsC and MaxIts are zero then these |
|
|
//| variables will be setted as default values. |
|
|
//+------------------------------------------------------------------+
|
|
void CLinLSQR::LinLSQRSetCond(CLinLSQRState &state,double epsa,
|
|
double epsb,int maxits)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_running,__FUNCTION__+": you can not call this function when LinLSQRIteration is running"))
|
|
return;
|
|
if(!CAp::Assert(MathIsValidNumber(epsa) && epsa>=0.0,__FUNCTION__+": EpsA is negative,INF or NAN"))
|
|
return;
|
|
if(!CAp::Assert(MathIsValidNumber(epsb) && epsb>=0.0,__FUNCTION__+": EpsB is negative,INF or NAN"))
|
|
return;
|
|
if(!CAp::Assert(maxits>=0,__FUNCTION__+": MaxIts is negative"))
|
|
return;
|
|
|
|
if((epsa==0.0 && epsb==0.0) && maxits==0)
|
|
{
|
|
state.m_epsa=m_atol;
|
|
state.m_epsb=m_btol;
|
|
state.m_maxits=state.m_n;
|
|
}
|
|
else
|
|
{
|
|
state.m_epsa=epsa;
|
|
state.m_epsb=epsb;
|
|
state.m_maxits=maxits;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LSQR solver: results. |
|
|
//| This function must be called after LinLSQRSolve |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - algorithm state |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[N], solution |
|
|
//| Rep - optimization report: |
|
|
//| * Rep.TerminationType completetion code: |
|
|
//| * 1 || Rk || <= EpsB* || B || |
|
|
//| * 4 ||A^T * Rk|| / (||A|| * ||Rk||) <= EpsA |
|
|
//| * 5 MaxIts steps was taken |
|
|
//| * 7 rounding errors prevent further progress, |
|
|
//| X contains best point found so far. |
|
|
//| (sometimes returned on singular systems) |
|
|
//| * 8 user requested termination via calling |
|
|
//| LinLSQRRequestTermination() |
|
|
//| * Rep.IterationsCount contains iterations count |
|
|
//| * NMV countains number of matrix - vector |
|
|
//| calculations |
|
|
//+------------------------------------------------------------------+
|
|
void CLinLSQR::LinLSQRResults(CLinLSQRState &state,CRowDouble &x,
|
|
CLinLSQRReport &rep)
|
|
{
|
|
x.Resize(0);
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_running,__FUNCTION__+": you can not call this function when LinLSQRIteration is running"))
|
|
return;
|
|
|
|
x=state.m_rx;
|
|
x.Resize(state.m_n);
|
|
rep.m_iterationscount=state.m_repiterationscount;
|
|
rep.m_nmv=state.m_repnmv;
|
|
rep.m_terminationtype=state.m_repterminationtype;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function turns on / off reporting. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NeedXRep - whether iteration reports are needed or not |
|
|
//| If NeedXRep is True, algorithm will call rep() |
|
|
//| callback function if it is provided to |
|
|
//| MinCGOptimize(). |
|
|
//+------------------------------------------------------------------+
|
|
void CLinLSQR::LinLSQRSetXRep(CLinLSQRState &state,bool needxrep)
|
|
{
|
|
state.m_xrep=needxrep;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function restarts LinLSQRIteration |
|
|
//+------------------------------------------------------------------+
|
|
void CLinLSQR::LinLSQRRestart(CLinLSQRState &state)
|
|
{
|
|
state.m_rstate.ia.Resize(2);
|
|
state.m_rstate.ra.Resize(1);
|
|
state.m_rstate.stage=-1;
|
|
ClearrFields(state);
|
|
state.m_repiterationscount=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function is used to peek into LSQR solver and get current |
|
|
//| iteration counter. You can safely "peek" into the solver from |
|
|
//| another thread. |
|
|
//| INPUT PARAMETERS: |
|
|
//| S - solver object |
|
|
//| RESULT: |
|
|
//| iteration counter, in [0, INF) |
|
|
//+------------------------------------------------------------------+
|
|
int CLinLSQR::LinLSQRPeekIterationsCount(CLinLSQRState &s)
|
|
{
|
|
int result=s.m_repiterationscount;
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine submits request for termination of the running |
|
|
//| solver. It can be called from some other thread which wants LSQR |
|
|
//| solver to terminate (obviously, the thread running LSQR solver |
|
|
//| can not request termination because it is already busy working on|
|
|
//| LSQR). |
|
|
//| As result, solver stops at point which was "current accepted" |
|
|
//| when termination request was submitted and returns error code 8 |
|
|
//| (successful termination). Such termination is a smooth process |
|
|
//| which properly deallocates all temporaries. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - solver structure |
|
|
//| NOTE: calling this function on solver which is NOT running will |
|
|
//| have no effect. |
|
|
//| NOTE: multiple calls to this function are possible. First call is|
|
|
//| counted, subsequent calls are silently ignored. |
|
|
//| NOTE: solver clears termination flag on its start, it means that |
|
|
//| if some other thread will request termination too soon, its|
|
|
//| request will went unnoticed. |
|
|
//+------------------------------------------------------------------+
|
|
void CLinLSQR::LinLSQRRequestTermination(CLinLSQRState &state)
|
|
{
|
|
state.m_userterminationneeded=true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Clears request fileds (to be sure that we don't forgot to clear |
|
|
//| something) |
|
|
//+------------------------------------------------------------------+
|
|
void CLinLSQR::ClearrFields(CLinLSQRState &state)
|
|
{
|
|
state.m_xupdated=false;
|
|
state.m_needmv=false;
|
|
state.m_needmtv=false;
|
|
state.m_needmv2=false;
|
|
state.m_needvmv=false;
|
|
state.m_needprec=false;
|
|
}
|
|
//+------------------------------------------------------------------+
|