13408 lines
510 KiB
Plaintext
13408 lines
510 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| interpolation.mqh |
|
|
//| Copyright 2003-2012 Sergey Bochkanov (ALGLIB project) |
|
|
//| Copyright 2012-2017, MetaQuotes Software Corp. |
|
|
//| 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 "alglibmisc.mqh"
|
|
#include "optimization.mqh"
|
|
#include "solvers.mqh"
|
|
#include "integration.mqh"
|
|
//+------------------------------------------------------------------+
|
|
//| IDW interpolant. |
|
|
//+------------------------------------------------------------------+
|
|
class CIDWInterpolant
|
|
{
|
|
public:
|
|
int m_n;
|
|
int m_nx;
|
|
int m_d;
|
|
double m_r;
|
|
int m_nw;
|
|
CKDTree m_tree;
|
|
int m_modeltype;
|
|
int m_debugsolverfailures;
|
|
double m_debugworstrcond;
|
|
double m_debugbestrcond;
|
|
//--- arrays
|
|
double m_xbuf[];
|
|
int m_tbuf[];
|
|
double m_rbuf[];
|
|
//--- matrices
|
|
CMatrixDouble m_q;
|
|
CMatrixDouble m_xybuf;
|
|
|
|
public:
|
|
CIDWInterpolant(void);
|
|
~CIDWInterpolant(void);
|
|
|
|
void Copy(CIDWInterpolant &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CIDWInterpolant::CIDWInterpolant(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CIDWInterpolant::~CIDWInterpolant(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CIDWInterpolant::Copy(CIDWInterpolant &obj)
|
|
{
|
|
//--- copy variables
|
|
m_n=obj.m_n;
|
|
m_nx=obj.m_nx;
|
|
m_d=obj.m_d;
|
|
m_r=obj.m_r;
|
|
m_nw=obj.m_nw;
|
|
m_modeltype=obj.m_modeltype;
|
|
m_debugsolverfailures=obj.m_debugsolverfailures;
|
|
m_debugworstrcond=obj.m_debugworstrcond;
|
|
m_debugbestrcond=obj.m_debugbestrcond;
|
|
m_tree.Copy(obj.m_tree);
|
|
//--- copy arrays
|
|
ArrayCopy(m_xbuf,obj.m_xbuf);
|
|
ArrayCopy(m_tbuf,obj.m_tbuf);
|
|
ArrayCopy(m_rbuf,obj.m_rbuf);
|
|
//--- copy matrices
|
|
m_q=obj.m_q;
|
|
m_xybuf=obj.m_xybuf;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| IDW interpolant. |
|
|
//+------------------------------------------------------------------+
|
|
class CIDWInterpolantShell
|
|
{
|
|
private:
|
|
CIDWInterpolant m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CIDWInterpolantShell(void);
|
|
CIDWInterpolantShell(CIDWInterpolant &obj);
|
|
~CIDWInterpolantShell(void);
|
|
//--- method
|
|
CIDWInterpolant *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CIDWInterpolantShell::CIDWInterpolantShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CIDWInterpolantShell::CIDWInterpolantShell(CIDWInterpolant &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CIDWInterpolantShell::~CIDWInterpolantShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CIDWInterpolant *CIDWInterpolantShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Inverse distance weighting interpolation |
|
|
//+------------------------------------------------------------------+
|
|
class CIDWInt
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static double IDWCalcQ(CIDWInterpolant &z,double &x[],const int k);
|
|
static void IDWInit1(const int n,const int nx,const int d,int nq,int nw,CIDWInterpolant &z);
|
|
static void IDWInternalSolver(double &y[],double &w[],CMatrixDouble &fmatrix,double &temp[],const int n,const int m,int &info,double &x[],double &taskrcond);
|
|
public:
|
|
//--- class constants
|
|
static const double m_idwqfactor;
|
|
static const int m_idwkmin;
|
|
//--- constructor, destructor
|
|
CIDWInt(void);
|
|
~CIDWInt(void);
|
|
//--- public methods
|
|
static double IDWCalc(CIDWInterpolant &z,double &x[]);
|
|
static void IDWBuildModifiedShepard(CMatrixDouble &xy,const int n,const int nx,const int d,int nq,int nw,CIDWInterpolant &z);
|
|
static void IDWBuildModifiedShepardR(CMatrixDouble &xy,const int n,const int nx,const double r,CIDWInterpolant &z);
|
|
static void IDWBuildNoisy(CMatrixDouble &xy,const int n,const int nx,const int d,int nq,int nw,CIDWInterpolant &z);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize constants |
|
|
//+------------------------------------------------------------------+
|
|
const double CIDWInt::m_idwqfactor=1.5;
|
|
const int CIDWInt::m_idwkmin=5;
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CIDWInt::CIDWInt(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CIDWInt::~CIDWInt(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| IDW interpolation |
|
|
//| INPUT PARAMETERS: |
|
|
//| Z - IDW interpolant built with one of model building |
|
|
//| subroutines. |
|
|
//| X - array[0..NX-1], interpolation point |
|
|
//| Result: |
|
|
//| IDW interpolant Z(X) |
|
|
//+------------------------------------------------------------------+
|
|
static double CIDWInt::IDWCalc(CIDWInterpolant &z,double &x[])
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int nx=0;
|
|
int i=0;
|
|
int k=0;
|
|
double r=0;
|
|
double s=0;
|
|
double w=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double d0=0;
|
|
double di=0;
|
|
//--- these initializers are not really necessary,
|
|
//--- but without them compiler complains about uninitialized locals
|
|
k=0;
|
|
//--- Query
|
|
if(z.m_modeltype==0)
|
|
{
|
|
//--- NQ/NW-based model
|
|
nx=z.m_nx;
|
|
k=CNearestNeighbor::KDTreeQueryKNN(z.m_tree,x,z.m_nw,true);
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsDistances(z.m_tree,z.m_rbuf);
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsTags(z.m_tree,z.m_tbuf);
|
|
}
|
|
if(z.m_modeltype==1)
|
|
{
|
|
//--- R-based model
|
|
nx=z.m_nx;
|
|
k=CNearestNeighbor::KDTreeQueryRNN(z.m_tree,x,z.m_r,true);
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsDistances(z.m_tree,z.m_rbuf);
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsTags(z.m_tree,z.m_tbuf);
|
|
if(k<m_idwkmin)
|
|
{
|
|
//--- we need at least IDWKMin points
|
|
k=CNearestNeighbor::KDTreeQueryKNN(z.m_tree,x,m_idwkmin,true);
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsDistances(z.m_tree,z.m_rbuf);
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsTags(z.m_tree,z.m_tbuf);
|
|
}
|
|
}
|
|
//--- initialize weights for linear/quadratic members calculation.
|
|
//--- NOTE 1: weights are calculated using NORMALIZED modified
|
|
//--- Shepard's formula. Original formula gives w(i)=sqr((R-di)/(R*di)),
|
|
//--- where di is i-th distance,R is max(di). Modified formula have
|
|
//--- following form:
|
|
//--- w_mod(i)=1,if di=d0
|
|
//--- w_mod(i)=w(i)/w(0),if di<>d0
|
|
//--- NOTE 2: self-match is USED for this query
|
|
//--- NOTE 3: last point almost always gain zero weight,but it MUST
|
|
//--- be used for fitting because sometimes it will gain NON-ZERO
|
|
//--- weight - for example,when all distances are equal.
|
|
r=z.m_rbuf[k-1];
|
|
d0=z.m_rbuf[0];
|
|
result=0;
|
|
s=0;
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
di=z.m_rbuf[i];
|
|
//--- check
|
|
if(di==d0)
|
|
{
|
|
//--- distance is equal to shortest,set it 1.0
|
|
//--- without explicitly calculating (which would give
|
|
//--- us same result,but 'll expose us to the risk of
|
|
//--- division by zero).
|
|
w=1;
|
|
}
|
|
else
|
|
{
|
|
//--- use normalized formula
|
|
v1=(r-di)/(r-d0);
|
|
v2=d0/di;
|
|
w=CMath::Sqr(v1*v2);
|
|
}
|
|
//--- change result
|
|
result=result+w*IDWCalcQ(z,x,z.m_tbuf[i]);
|
|
s=s+w;
|
|
}
|
|
//--- return result
|
|
return(result/s);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| IDW interpolant using modified Shepard method for uniform point |
|
|
//| distributions. |
|
|
//| INPUT PARAMETERS: |
|
|
//| XY - X and Y values, array[0..N-1,0..NX]. |
|
|
//| First NX columns contain X-values, last column |
|
|
//| contain Y-values. |
|
|
//| N - number of nodes, N>0. |
|
|
//| NX - space dimension, NX>=1. |
|
|
//| D - nodal function type, either: |
|
|
//| * 0 constant model. Just for demonstration only,|
|
|
//| worst model ever. |
|
|
//| * 1 linear model, least squares fitting. Simpe |
|
|
//| model for datasets too small for quadratic |
|
|
//| models |
|
|
//| * 2 quadratic model, least squares fitting. |
|
|
//| Best model available (if your dataset is |
|
|
//| large enough). |
|
|
//| * -1 "fast" linear model, use with caution!!! It |
|
|
//| is significantly faster than linear/quadratic|
|
|
//| and better than constant model. But it is |
|
|
//| less robust (especially in the presence of |
|
|
//| noise). |
|
|
//| NQ - number of points used to calculate nodal functions |
|
|
//| (ignored for constant models). NQ should be LARGER |
|
|
//| than: |
|
|
//| * max(1.5*(1+NX),2^NX+1) for linear model, |
|
|
//| * max(3/4*(NX+2)*(NX+1),2^NX+1) for quadratic model. |
|
|
//| Values less than this threshold will be silently |
|
|
//| increased. |
|
|
//| NW - number of points used to calculate weights and to |
|
|
//| interpolate. Required: >=2^NX+1, values less than |
|
|
//| this threshold will be silently increased. |
|
|
//| Recommended value: about 2*NQ |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Z - IDW interpolant. |
|
|
//| NOTES: |
|
|
//| * best results are obtained with quadratic models, worst - with|
|
|
//| constant models |
|
|
//| * when N is large, NQ and NW must be significantly smaller than|
|
|
//| N both to obtain optimal performance and to obtain optimal |
|
|
//| accuracy. In 2 or 3-dimensional tasks NQ=15 and NW=25 are |
|
|
//| good values to start with. |
|
|
//| * NQ and NW may be greater than N. In such cases they will be |
|
|
//| automatically decreased. |
|
|
//| * this subroutine is always succeeds (as long as correct |
|
|
//| parameters are passed). |
|
|
//| * see 'Multivariate Interpolation of Large Sets of Scattered |
|
|
//| Data' by Robert J. Renka for more information on this |
|
|
//| algorithm. |
|
|
//| * this subroutine assumes that point distribution is uniform at|
|
|
//| the small scales. If it isn't - for example, points are |
|
|
//| concentrated along "lines", but "lines" distribution is |
|
|
//| uniform at the larger scale - then you should use |
|
|
//| IDWBuildModifiedShepardR() |
|
|
//+------------------------------------------------------------------+
|
|
static void CIDWInt::IDWBuildModifiedShepard(CMatrixDouble &xy,const int n,
|
|
const int nx,const int d,
|
|
int nq,int nw,CIDWInterpolant &z)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int j2=0;
|
|
int j3=0;
|
|
double v=0;
|
|
double r=0;
|
|
double s=0;
|
|
double d0=0;
|
|
double di=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
int nc=0;
|
|
int offs=0;
|
|
int info=0;
|
|
double taskrcond=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double qrbuf[];
|
|
double y[];
|
|
double w[];
|
|
double qsol[];
|
|
double temp[];
|
|
int tags[];
|
|
//--- create matrix
|
|
CMatrixDouble qxybuf;
|
|
CMatrixDouble fmatrix;
|
|
//--- these initializers are not really necessary,
|
|
//--- but without them compiler complains about uninitialized locals
|
|
nc=0;
|
|
//--- assertions
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(nx>=1,__FUNCTION__+": NX<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(d>=-1 && d<=2,__FUNCTION__+": D<>-1 and D<>0 and D<>1 and D<>2!"))
|
|
return;
|
|
//--- Correct parameters if needed
|
|
if(d==1)
|
|
{
|
|
nq=MathMax(nq,(int)MathCeil(m_idwqfactor*(1+nx))+1);
|
|
nq=MathMax(nq,(int)MathRound(MathPow(2,nx))+1);
|
|
}
|
|
//--- check
|
|
if(d==2)
|
|
{
|
|
nq=MathMax(nq,(int)MathCeil(m_idwqfactor*(nx+2)*(nx+1)/2)+1);
|
|
nq=MathMax(nq,(int)MathRound(MathPow(2,nx))+1);
|
|
}
|
|
//--- change values
|
|
nw=MathMax(nw,(int)MathRound(MathPow(2,nx))+1);
|
|
nq=MathMin(nq,n);
|
|
nw=MathMin(nw,n);
|
|
//--- primary initialization of Z
|
|
IDWInit1(n,nx,d,nq,nw,z);
|
|
z.m_modeltype=0;
|
|
//--- Create KD-tree
|
|
ArrayResizeAL(tags,n);
|
|
for(i=0;i<=n-1;i++)
|
|
tags[i]=i;
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeBuildTagged(xy,tags,n,nx,1,2,z.m_tree);
|
|
//--- build nodal functions
|
|
ArrayResizeAL(temp,nq+1);
|
|
ArrayResizeAL(x,nx);
|
|
ArrayResizeAL(qrbuf,nq);
|
|
qxybuf.Resize(nq,nx+1);
|
|
//--- check
|
|
if(d==-1)
|
|
ArrayResizeAL(w,nq);
|
|
//--- check
|
|
if(d==1)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(y,nq);
|
|
ArrayResizeAL(w,nq);
|
|
ArrayResizeAL(qsol,nx);
|
|
//--- NX for linear members,
|
|
//--- 1 for temporary storage
|
|
fmatrix.Resize(nq,nx+1);
|
|
}
|
|
//--- check
|
|
if(d==2)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(y,nq);
|
|
ArrayResizeAL(w,nq);
|
|
ArrayResizeAL(qsol,nx+(int)MathRound(nx*(nx+1)*0.5));
|
|
//--- NX for linear members,
|
|
//--- Round(NX*(NX+1)*0.5) for quadratic model,
|
|
//--- 1 for temporary storage
|
|
fmatrix.Resize(nq,nx+(int)MathRound(nx*(nx+1)*0.5)+1);
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- Initialize center and function value.
|
|
//--- If D=0 it is all what we need
|
|
for(i_=0;i_<=nx;i_++)
|
|
z.m_q[i].Set(i_,xy[i][i_]);
|
|
//--- check
|
|
if(d==0)
|
|
continue;
|
|
//--- calculate weights for linear/quadratic members calculation.
|
|
//--- NOTE 1: weights are calculated using NORMALIZED modified
|
|
//--- Shepard's formula. Original formula is w(i)=sqr((R-di)/(R*di)),
|
|
//--- where di is i-th distance,R is max(di). Modified formula have
|
|
//--- following form:
|
|
//--- w_mod(i)=1,if di=d0
|
|
//--- w_mod(i)=w(i)/w(0),if di<>d0
|
|
//--- NOTE 2: self-match is NOT used for this query
|
|
//--- NOTE 3: last point almost always gain zero weight,but it MUST
|
|
//--- be used for fitting because sometimes it will gain NON-ZERO
|
|
//--- weight - for example,when all distances are equal.
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=xy[i][i_];
|
|
k=CNearestNeighbor::KDTreeQueryKNN(z.m_tree,x,nq,false);
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsXY(z.m_tree,qxybuf);
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsDistances(z.m_tree,qrbuf);
|
|
r=qrbuf[k-1];
|
|
d0=qrbuf[0];
|
|
//--- calculation
|
|
for(j=0;j<=k-1;j++)
|
|
{
|
|
di=qrbuf[j];
|
|
//--- check
|
|
if(di==d0)
|
|
{
|
|
//--- distance is equal to shortest,set it 1.0
|
|
//--- without explicitly calculating (which would give
|
|
//--- us same result,but 'll expose us to the risk of
|
|
//--- division by zero).
|
|
w[j]=1;
|
|
}
|
|
else
|
|
{
|
|
//--- use normalized formula
|
|
v1=(r-di)/(r-d0);
|
|
v2=d0/di;
|
|
w[j]=CMath::Sqr(v1*v2);
|
|
}
|
|
}
|
|
//--- calculate linear/quadratic members
|
|
if(d==-1)
|
|
{
|
|
//--- "Fast" linear nodal function calculated using
|
|
//--- inverse distance weighting
|
|
for(j=0;j<=nx-1;j++)
|
|
x[j]=0;
|
|
s=0;
|
|
//--- calculation
|
|
for(j=0;j<=k-1;j++)
|
|
{
|
|
//--- calculate J-th inverse distance weighted gradient:
|
|
//--- grad_k=(y_j-y_k)*(x_j-x_k)/sqr(norm(x_j-x_k))
|
|
//--- grad=sum(wk*grad_k)/sum(w_k)
|
|
v=0;
|
|
for(j2=0;j2<=nx-1;j2++)
|
|
v=v+CMath::Sqr(qxybuf[j][j2]-xy[i][j2]);
|
|
//--- Although x_j<>x_k,sqr(norm(x_j-x_k)) may be zero due to
|
|
//--- underflow. If it is,we assume than J-th gradient is zero
|
|
//--- (i.m_e. don't add anything)
|
|
if(v!=0.0)
|
|
{
|
|
for(j2=0;j2<=nx-1;j2++)
|
|
x[j2]=x[j2]+w[j]*(qxybuf[j][nx]-xy[i][nx])*(qxybuf[j][j2]-xy[i][j2])/v;
|
|
}
|
|
s=s+w[j];
|
|
}
|
|
for(j=0;j<=nx-1;j++)
|
|
z.m_q[i].Set(nx+1+j,x[j]/s);
|
|
}
|
|
else
|
|
{
|
|
//--- Least squares models: build
|
|
if(d==1)
|
|
{
|
|
//--- Linear nodal function calculated using
|
|
//--- least squares fitting to its neighbors
|
|
for(j=0;j<=k-1;j++)
|
|
{
|
|
for(j2=0;j2<=nx-1;j2++)
|
|
fmatrix[j].Set(j2,qxybuf[j][j2]-xy[i][j2]);
|
|
y[j]=qxybuf[j][nx]-xy[i][nx];
|
|
}
|
|
nc=nx;
|
|
}
|
|
//--- check
|
|
if(d==2)
|
|
{
|
|
//--- Quadratic nodal function calculated using
|
|
//--- least squares fitting to its neighbors
|
|
for(j=0;j<=k-1;j++)
|
|
{
|
|
offs=0;
|
|
for(j2=0;j2<=nx-1;j2++)
|
|
{
|
|
fmatrix[j].Set(offs,qxybuf[j][j2]-xy[i][j2]);
|
|
offs=offs+1;
|
|
}
|
|
//--- calculation
|
|
for(j2=0;j2<=nx-1;j2++)
|
|
{
|
|
for(j3=j2;j3<=nx-1;j3++)
|
|
{
|
|
fmatrix[j].Set(offs,(qxybuf[j][j2]-xy[i][j2])*(qxybuf[j][j3]-xy[i][j3]));
|
|
offs=offs+1;
|
|
}
|
|
}
|
|
y[j]=qxybuf[j][nx]-xy[i][nx];
|
|
}
|
|
nc=nx+(int)MathRound(nx*(nx+1)*0.5);
|
|
}
|
|
//--- function call
|
|
IDWInternalSolver(y,w,fmatrix,temp,k,nc,info,qsol,taskrcond);
|
|
//--- Least squares models: copy results
|
|
if(info>0)
|
|
{
|
|
//--- LLS task is solved,copy results
|
|
z.m_debugworstrcond=MathMin(z.m_debugworstrcond,taskrcond);
|
|
z.m_debugbestrcond=MathMax(z.m_debugbestrcond,taskrcond);
|
|
for(j=0;j<=nc-1;j++)
|
|
z.m_q[i].Set(nx+1+j,qsol[j]);
|
|
}
|
|
else
|
|
{
|
|
//--- Solver failure,very strange,but we will use
|
|
//--- zero values to handle it.
|
|
z.m_debugsolverfailures=z.m_debugsolverfailures+1;
|
|
for(j=0;j<=nc-1;j++)
|
|
z.m_q[i].Set(nx+1+j,0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| IDW interpolant using modified Shepard method for non-uniform |
|
|
//| datasets. |
|
|
//| This type of model uses constant nodal functions and interpolates|
|
|
//| using all nodes which are closer than user-specified radius R. It|
|
|
//| may be used when points distribution is non-uniform at the small |
|
|
//| scale, but it is at the distances as large as R. |
|
|
//| INPUT PARAMETERS: |
|
|
//| XY - X and Y values, array[0..N-1,0..NX]. |
|
|
//| First NX columns contain X-values, last column |
|
|
//| contain Y-values. |
|
|
//| N - number of nodes, N>0. |
|
|
//| NX - space dimension, NX>=1. |
|
|
//| R - radius, R>0 |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Z - IDW interpolant. |
|
|
//| NOTES: |
|
|
//| * if there is less than IDWKMin points within R-ball, algorithm |
|
|
//| selects IDWKMin closest ones, so that continuity properties of |
|
|
//| interpolant are preserved even far from points. |
|
|
//+------------------------------------------------------------------+
|
|
static void CIDWInt::IDWBuildModifiedShepardR(CMatrixDouble &xy,const int n,
|
|
const int nx,const double r,
|
|
CIDWInterpolant &z)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
//--- create array
|
|
int tags[];
|
|
//--- assertions
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(nx>=1,__FUNCTION__+": NX<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(r>0.0,__FUNCTION__+": R<=0!"))
|
|
return;
|
|
//--- primary initialization of Z
|
|
IDWInit1(n,nx,0,0,n,z);
|
|
z.m_modeltype=1;
|
|
z.m_r=r;
|
|
//--- Create KD-tree
|
|
ArrayResizeAL(tags,n);
|
|
for(i=0;i<=n-1;i++)
|
|
tags[i]=i;
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeBuildTagged(xy,tags,n,nx,1,2,z.m_tree);
|
|
//--- build nodal functions
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=nx;i_++)
|
|
z.m_q[i].Set(i_,xy[i][i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| IDW model for noisy data. |
|
|
//| This subroutine may be used to handle noisy data, i.e. data with |
|
|
//| noise in OUTPUT values. It differs from IDWBuildModifiedShepard()|
|
|
//| in the following aspects: |
|
|
//| * nodal functions are not constrained to pass through nodes: |
|
|
//| Qi(xi)<>yi, i.e. we have fitting instead of interpolation. |
|
|
//| * weights which are used during least squares fitting stage are |
|
|
//| all equal to 1.0 (independently of distance) |
|
|
//| * "fast"-linear or constant nodal functions are not supported |
|
|
//| (either not robust enough or too rigid) |
|
|
//| This problem require far more complex tuning than interpolation |
|
|
//| problems. |
|
|
//| Below you can find some recommendations regarding this problem: |
|
|
//| * focus on tuning NQ; it controls noise reduction. As for NW, you|
|
|
//| can just make it equal to 2*NQ. |
|
|
//| * you can use cross-validation to determine optimal NQ. |
|
|
//| * optimal NQ is a result of complex tradeoff between noise level |
|
|
//| (more noise = larger NQ required) and underlying function |
|
|
//| complexity (given fixed N, larger NQ means smoothing of compex |
|
|
//| features in the data). For example, NQ=N will reduce noise to |
|
|
//| the minimum level possible, but you will end up with just |
|
|
//| constant/linear/quadratic (depending on D) least squares |
|
|
//| model for the whole dataset. |
|
|
//| INPUT PARAMETERS: |
|
|
//| XY - X and Y values, array[0..N-1,0..NX]. |
|
|
//| First NX columns contain X-values, last column |
|
|
//| contain Y-values. |
|
|
//| N - number of nodes, N>0. |
|
|
//| NX - space dimension, NX>=1. |
|
|
//| D - nodal function degree, either: |
|
|
//| * 1 linear model, least squares fitting. Simpe |
|
|
//| model for datasets too small for quadratic |
|
|
//| models (or for very noisy problems). |
|
|
//| * 2 quadratic model, least squares fitting. Best |
|
|
//| model available (if your dataset is large |
|
|
//| enough). |
|
|
//| NQ - number of points used to calculate nodal functions. |
|
|
//| NQ should be significantly larger than 1.5 times the |
|
|
//| number of coefficients in a nodal function to |
|
|
//| overcome effects of noise: |
|
|
//| * larger than 1.5*(1+NX) for linear model, |
|
|
//| * larger than 3/4*(NX+2)*(NX+1) for quadratic model. |
|
|
//| Values less than this threshold will be silently |
|
|
//| increased. |
|
|
//| NW - number of points used to calculate weights and to |
|
|
//| interpolate. Required: >=2^NX+1, values less than |
|
|
//| this threshold will be silently increased. |
|
|
//| Recommended value: about 2*NQ or larger |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Z - IDW inte rpolant. |
|
|
//| NOTES: |
|
|
//| * best results are obtained with quadratic models, linear |
|
|
//| models are not recommended to use unless you are pretty sure |
|
|
//| that it is what you want |
|
|
//| * this subroutine is always succeeds (as long as correct |
|
|
//| parameters are passed). |
|
|
//| * see 'Multivariate Interpolation of Large Sets of Scattered |
|
|
//| Data' by Robert J. Renka for more information on this |
|
|
//| algorithm. |
|
|
//+------------------------------------------------------------------+
|
|
static void CIDWInt::IDWBuildNoisy(CMatrixDouble &xy,const int n,const int nx,
|
|
const int d,int nq,int nw,CIDWInterpolant &z)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int j2=0;
|
|
int j3=0;
|
|
double v=0;
|
|
int nc=0;
|
|
int offs=0;
|
|
double taskrcond=0;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double qrbuf[];
|
|
double y[];
|
|
double w[];
|
|
double qsol[];
|
|
int tags[];
|
|
double temp[];
|
|
//--- create matrix
|
|
CMatrixDouble qxybuf;
|
|
CMatrixDouble fmatrix;
|
|
//--- these initializers are not really necessary,
|
|
//--- but without them compiler complains about uninitialized locals
|
|
nc=0;
|
|
//--- assertions
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(nx>=1,__FUNCTION__+": NX<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(d>=1 && d<=2,__FUNCTION__+": D<>1 and D<>2!"))
|
|
return;
|
|
//--- Correct parameters if needed
|
|
if(d==1)
|
|
nq=MathMax(nq,(int)MathCeil(m_idwqfactor*(1+nx))+1);
|
|
//--- check
|
|
if(d==2)
|
|
nq=MathMax(nq,(int)MathCeil(m_idwqfactor*(nx+2)*(nx+1)/2)+1);
|
|
//--- change values
|
|
nw=MathMax(nw,(int)MathRound(MathPow(2,nx))+1);
|
|
nq=MathMin(nq,n);
|
|
nw=MathMin(nw,n);
|
|
//--- primary initialization of Z
|
|
IDWInit1(n,nx,d,nq,nw,z);
|
|
z.m_modeltype=0;
|
|
//--- Create KD-tree
|
|
ArrayResizeAL(tags,n);
|
|
for(i=0;i<=n-1;i++)
|
|
tags[i]=i;
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeBuildTagged(xy,tags,n,nx,1,2,z.m_tree);
|
|
//--- build nodal functions
|
|
//--- (special algorithm for noisy data is used)
|
|
ArrayResizeAL(temp,nq+1);
|
|
ArrayResizeAL(x,nx);
|
|
ArrayResizeAL(qrbuf,nq);
|
|
qxybuf.Resize(nq,nx+1);
|
|
//--- check
|
|
if(d==1)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(y,nq);
|
|
ArrayResizeAL(w,nq);
|
|
ArrayResizeAL(qsol,1+nx);
|
|
//--- 1 for constant member,
|
|
//--- NX for linear members,
|
|
//--- 1 for temporary storage
|
|
fmatrix.Resize(nq,1+nx+1);
|
|
}
|
|
//--- check
|
|
if(d==2)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(y,nq);
|
|
ArrayResizeAL(w,nq);
|
|
ArrayResizeAL(qsol,1+nx+(int)MathRound(nx*(nx+1)*0.5));
|
|
//--- 1 for constant member,
|
|
//--- NX for linear members,
|
|
//--- Round(NX*(NX+1)*0.5) for quadratic model,
|
|
//--- 1 for temporary storage
|
|
fmatrix.Resize(nq,1+nx+(int)MathRound(nx*(nx+1)*0.5)+1);
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- Initialize center.
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
z.m_q[i].Set(i_,xy[i][i_]);
|
|
//--- Calculate linear/quadratic members
|
|
//--- using least squares fit
|
|
//--- NOTE 1: all weight are equal to 1.0
|
|
//--- NOTE 2: self-match is USED for this query
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=xy[i][i_];
|
|
k=CNearestNeighbor::KDTreeQueryKNN(z.m_tree,x,nq,true);
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsXY(z.m_tree,qxybuf);
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsDistances(z.m_tree,qrbuf);
|
|
//--- check
|
|
if(d==1)
|
|
{
|
|
//--- Linear nodal function calculated using
|
|
//--- least squares fitting to its neighbors
|
|
for(j=0;j<=k-1;j++)
|
|
{
|
|
fmatrix[j].Set(0,1.0);
|
|
for(j2=0;j2<=nx-1;j2++)
|
|
fmatrix[j].Set(1+j2,qxybuf[j][j2]-xy[i][j2]);
|
|
//--- change values
|
|
y[j]=qxybuf[j][nx];
|
|
w[j]=1;
|
|
}
|
|
nc=1+nx;
|
|
}
|
|
//--- check
|
|
if(d==2)
|
|
{
|
|
//--- Quadratic nodal function calculated using
|
|
//--- least squares fitting to its neighbors
|
|
for(j=0;j<=k-1;j++)
|
|
{
|
|
fmatrix[j].Set(0,1);
|
|
offs=1;
|
|
for(j2=0;j2<=nx-1;j2++)
|
|
{
|
|
fmatrix[j].Set(offs,qxybuf[j][j2]-xy[i][j2]);
|
|
offs=offs+1;
|
|
}
|
|
//--- calculation
|
|
for(j2=0;j2<=nx-1;j2++)
|
|
{
|
|
for(j3=j2;j3<=nx-1;j3++)
|
|
{
|
|
fmatrix[j].Set(offs,(qxybuf[j][j2]-xy[i][j2])*(qxybuf[j][j3]-xy[i][j3]));
|
|
offs=offs+1;
|
|
}
|
|
}
|
|
//--- change values
|
|
y[j]=qxybuf[j][nx];
|
|
w[j]=1;
|
|
}
|
|
nc=1+nx+(int)MathRound(nx*(nx+1)*0.5);
|
|
}
|
|
//--- function call
|
|
IDWInternalSolver(y,w,fmatrix,temp,k,nc,info,qsol,taskrcond);
|
|
//--- Least squares models: copy results
|
|
if(info>0)
|
|
{
|
|
//--- LLS task is solved,copy results
|
|
z.m_debugworstrcond=MathMin(z.m_debugworstrcond,taskrcond);
|
|
z.m_debugbestrcond=MathMax(z.m_debugbestrcond,taskrcond);
|
|
for(j=0;j<=nc-1;j++)
|
|
z.m_q[i].Set(nx+j,qsol[j]);
|
|
}
|
|
else
|
|
{
|
|
//--- Solver failure,very strange,but we will use
|
|
//--- zero values to handle it.
|
|
z.m_debugsolverfailures=z.m_debugsolverfailures+1;
|
|
v=0;
|
|
for(j=0;j<=k-1;j++)
|
|
v=v+qxybuf[j][nx];
|
|
z.m_q[i].Set(nx,v/k);
|
|
for(j=0;j<=nc-2;j++)
|
|
z.m_q[i].Set(nx+1+j,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine: K-th nodal function calculation |
|
|
//+------------------------------------------------------------------+
|
|
static double CIDWInt::IDWCalcQ(CIDWInterpolant &z,double &x[],const int k)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int nx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int offs=0;
|
|
//--- initialization
|
|
nx=z.m_nx;
|
|
//--- constant member
|
|
result=z.m_q[k][nx];
|
|
//--- linear members
|
|
if(z.m_d>=1)
|
|
{
|
|
for(i=0;i<=nx-1;i++)
|
|
result=result+z.m_q[k][nx+1+i]*(x[i]-z.m_q[k][i]);
|
|
}
|
|
//--- quadratic members
|
|
if(z.m_d>=2)
|
|
{
|
|
offs=nx+1+nx;
|
|
for(i=0;i<=nx-1;i++)
|
|
{
|
|
for(j=i;j<=nx-1;j++)
|
|
{
|
|
result=result+z.m_q[k][offs]*(x[i]-z.m_q[k][i])*(x[j]-z.m_q[k][j]);
|
|
offs=offs+1;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Initialization of internal structures. |
|
|
//| It assumes correctness of all parameters. |
|
|
//+------------------------------------------------------------------+
|
|
static void CIDWInt::IDWInit1(const int n,const int nx,const int d,
|
|
int nq,int nw,CIDWInterpolant &z)
|
|
{
|
|
//--- initialization
|
|
z.m_debugsolverfailures=0;
|
|
z.m_debugworstrcond=1.0;
|
|
z.m_debugbestrcond=0;
|
|
z.m_n=n;
|
|
z.m_nx=nx;
|
|
z.m_d=0;
|
|
//--- check
|
|
if(d==1)
|
|
z.m_d=1;
|
|
//--- check
|
|
if(d==2)
|
|
z.m_d=2;
|
|
//--- check
|
|
if(d==-1)
|
|
z.m_d=1;
|
|
z.m_nw=nw;
|
|
//--- check
|
|
if(d==-1)
|
|
z.m_q.Resize(n,2*nx+1);
|
|
//--- check
|
|
if(d==0)
|
|
z.m_q.Resize(n,nx+1);
|
|
//--- check
|
|
if(d==1)
|
|
z.m_q.Resize(n,2*nx+1);
|
|
//--- check
|
|
if(d==2)
|
|
z.m_q.Resize(n,nx+1+nx+(int)MathRound(nx*(nx+1)*0.5));
|
|
//--- allocation
|
|
ArrayResizeAL(z.m_tbuf,nw);
|
|
ArrayResizeAL(z.m_rbuf,nw);
|
|
z.m_xybuf.Resize(nw,nx+1);
|
|
ArrayResizeAL(z.m_xbuf,nx);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Linear least squares solver for small tasks. |
|
|
//| Works faster than standard ALGLIB solver in non-degenerate |
|
|
//| cases (due to absense of internal allocations and optimized |
|
|
//| row/colums). In degenerate cases it calls standard solver, which|
|
|
//| results in small performance penalty associated with preliminary |
|
|
//| steps. |
|
|
//| INPUT PARAMETERS: |
|
|
//| Y array[0..N-1] |
|
|
//| W array[0..N-1] |
|
|
//| FMatrix array[0..N-1,0..M], have additional column for |
|
|
//| temporary values |
|
|
//| Temp array[0..N] |
|
|
//+------------------------------------------------------------------+
|
|
static void CIDWInt::IDWInternalSolver(double &y[],double &w[],CMatrixDouble &fmatrix,
|
|
double &temp[],const int n,const int m,
|
|
int &info,double &x[],double &taskrcond)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double tau=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
double b[];
|
|
//--- object of class
|
|
CDenseSolverLSReport srep;
|
|
//--- set up info
|
|
info=1;
|
|
//--- prepare matrix
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
fmatrix[i].Set(m,y[i]);
|
|
v=w[i];
|
|
for(i_=0;i_<=m;i_++)
|
|
fmatrix[i].Set(i_,v*fmatrix[i][i_]);
|
|
}
|
|
//--- use either fast algorithm or general algorithm
|
|
if(m<=n)
|
|
{
|
|
//--- QR decomposition
|
|
//--- We assume that M<=N (we would have called LSFit() otherwise)
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
//--- check
|
|
if(i<n-1)
|
|
{
|
|
i1_=i-1;
|
|
for(i_=1;i_<=n-i;i_++)
|
|
temp[i_]=fmatrix[i_+i1_][i];
|
|
//--- function call
|
|
CReflections::GenerateReflection(temp,n-i,tau);
|
|
//--- change values
|
|
fmatrix[i].Set(i,temp[1]);
|
|
temp[1]=1;
|
|
//--- calculation
|
|
for(j=i+1;j<=m;j++)
|
|
{
|
|
i1_=1-i;
|
|
v=0.0;
|
|
for(i_=i;i_<=n-1;i_++)
|
|
v+=fmatrix[i_][j]*temp[i_+i1_];
|
|
//--- change values
|
|
v=tau*v;
|
|
i1_=1-i;
|
|
for(i_=i;i_<=n-1;i_++)
|
|
fmatrix[i_].Set(j,fmatrix[i_][j]-v*temp[i_+i1_]);
|
|
}
|
|
}
|
|
}
|
|
//--- Check condition number
|
|
taskrcond=CRCond::RMatrixTrRCondInf(fmatrix,m,true,false);
|
|
//--- use either fast algorithm for non-degenerate cases
|
|
//--- or slow algorithm for degenerate cases
|
|
if(taskrcond>10000*n*CMath::m_machineepsilon)
|
|
{
|
|
//--- solve triangular system R*x=FMatrix[0:M-1,M]
|
|
//--- using fast algorithm,then exit
|
|
x[m-1]=fmatrix[m-1][m]/fmatrix[m-1][m-1];
|
|
for(i=m-2;i>=0;i--)
|
|
{
|
|
v=0.0;
|
|
for(i_=i+1;i_<=m-1;i_++)
|
|
v+=fmatrix[i][i_]*x[i_];
|
|
x[i]=(fmatrix[i][m]-v)/fmatrix[i][i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- use more general algorithm
|
|
ArrayResizeAL(b,m);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=i-1;j++)
|
|
fmatrix[i].Set(j,0.0);
|
|
b[i]=fmatrix[i][m];
|
|
}
|
|
//--- function call
|
|
CDenseSolver::RMatrixSolveLS(fmatrix,m,m,b,10000*CMath::m_machineepsilon,info,srep,x);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- use more general algorithm
|
|
ArrayResizeAL(b,n);
|
|
for(i=0;i<=n-1;i++)
|
|
b[i]=fmatrix[i][m];
|
|
//--- function call
|
|
CDenseSolver::RMatrixSolveLS(fmatrix,n,m,b,10000*CMath::m_machineepsilon,info,srep,x);
|
|
taskrcond=srep.m_r2;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Barycentric interpolant. |
|
|
//+------------------------------------------------------------------+
|
|
class CBarycentricInterpolant
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_n;
|
|
double m_sy;
|
|
//--- arrays
|
|
double m_x[];
|
|
double m_y[];
|
|
double m_w[];
|
|
//--- constructor, destructor
|
|
CBarycentricInterpolant(void);
|
|
~CBarycentricInterpolant(void);
|
|
//--- copy
|
|
void Copy(CBarycentricInterpolant &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CBarycentricInterpolant::CBarycentricInterpolant(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CBarycentricInterpolant::~CBarycentricInterpolant(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CBarycentricInterpolant::Copy(CBarycentricInterpolant &obj)
|
|
{
|
|
//--- copy variables
|
|
m_n=obj.m_n;
|
|
m_sy=obj.m_sy;
|
|
//--- copy arrays
|
|
ArrayCopy(m_x,obj.m_x);
|
|
ArrayCopy(m_y,obj.m_y);
|
|
ArrayCopy(m_w,obj.m_w);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Barycentric interpolant. |
|
|
//+------------------------------------------------------------------+
|
|
class CBarycentricInterpolantShell
|
|
{
|
|
private:
|
|
CBarycentricInterpolant m_innerobj;
|
|
public:
|
|
//--- constructor, destructor
|
|
CBarycentricInterpolantShell(void);
|
|
CBarycentricInterpolantShell(CBarycentricInterpolant &obj);
|
|
~CBarycentricInterpolantShell(void);
|
|
//--- method
|
|
CBarycentricInterpolant *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CBarycentricInterpolantShell::CBarycentricInterpolantShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CBarycentricInterpolantShell::CBarycentricInterpolantShell(CBarycentricInterpolant &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CBarycentricInterpolantShell::~CBarycentricInterpolantShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CBarycentricInterpolant *CBarycentricInterpolantShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Rational interpolation |
|
|
//+------------------------------------------------------------------+
|
|
class CRatInt
|
|
{
|
|
private:
|
|
//--- private method
|
|
static void BarycentricNormalize(CBarycentricInterpolant &b);
|
|
public:
|
|
//--- constructor, destructor
|
|
CRatInt(void);
|
|
~CRatInt(void);
|
|
//--- public methods
|
|
static double BarycentricCalc(CBarycentricInterpolant &b,const double t);
|
|
static void BarycentricDiff1(CBarycentricInterpolant &b,double t,double &f,double &df);
|
|
static void BarycentricDiff2(CBarycentricInterpolant &b,const double t,double &f,double &df,double &d2f);
|
|
static void BarycentricLinTransX(CBarycentricInterpolant &b,const double ca,const double cb);
|
|
static void BarycentricLinTransY(CBarycentricInterpolant &b,const double ca,const double cb);
|
|
static void BarycentricUnpack(CBarycentricInterpolant &b,int &n,double &x[],double &y[],double &w[]);
|
|
static void BarycentricBuildXYW(double &x[],double &y[],double &w[],const int n,CBarycentricInterpolant &b);
|
|
static void BarycentricBuildFloaterHormann(double &x[],double &y[],const int n,int d,CBarycentricInterpolant &b);
|
|
static void BarycentricCopy(CBarycentricInterpolant &b,CBarycentricInterpolant &b2);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CRatInt::CRatInt(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CRatInt::~CRatInt(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Rational interpolation using barycentric formula |
|
|
//| F(t)=SUM(i=0,n-1,w[i]*f[i]/(t-x[i])) / SUM(i=0,n-1,w[i]/(t-x[i]))|
|
|
//| Input parameters: |
|
|
//| B - barycentric interpolant built with one of model |
|
|
//| building subroutines. |
|
|
//| T - interpolation point |
|
|
//| Result: |
|
|
//| barycentric interpolant F(t) |
|
|
//+------------------------------------------------------------------+
|
|
static double CRatInt::BarycentricCalc(CBarycentricInterpolant &b,const double t)
|
|
{
|
|
//--- create variables
|
|
double s1=0;
|
|
double s2=0;
|
|
double s=0;
|
|
double v=0;
|
|
int i=0;
|
|
//--- check
|
|
if(!CAp::Assert(!CInfOrNaN::IsInfinity(t),__FUNCTION__+": infinite T!"))
|
|
return(EMPTY_VALUE);
|
|
//--- special case: NaN
|
|
if(CInfOrNaN::IsNaN(t))
|
|
return(CInfOrNaN::NaN());
|
|
//--- special case: N=1
|
|
if(b.m_n==1)
|
|
return(b.m_sy*b.m_y[0]);
|
|
//--- Here we assume that task is normalized,i.m_e.:
|
|
//--- 1. abs(Y[i])<=1
|
|
//--- 2. abs(W[i])<=1
|
|
//--- 3. X[] is ordered
|
|
s=MathAbs(t-b.m_x[0]);
|
|
for(i=0;i<=b.m_n-1;i++)
|
|
{
|
|
v=b.m_x[i];
|
|
//--- check
|
|
if(v==(double)(t))
|
|
return(b.m_sy*b.m_y[i]);
|
|
v=MathAbs(t-v);
|
|
//--- check
|
|
if(v<s)
|
|
s=v;
|
|
}
|
|
//--- change values
|
|
s1=0;
|
|
s2=0;
|
|
//--- calculation
|
|
for(i=0;i<=b.m_n-1;i++)
|
|
{
|
|
v=s/(t-b.m_x[i]);
|
|
v=v*b.m_w[i];
|
|
s1=s1+v*b.m_y[i];
|
|
s2=s2+v;
|
|
}
|
|
//--- return result
|
|
return(b.m_sy*s1/s2);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Differentiation of barycentric interpolant: first derivative. |
|
|
//| Algorithm used in this subroutine is very robust and should not |
|
|
//| fail until provided with values too close to MaxRealNumber |
|
|
//| (usually MaxRealNumber/N or greater will overflow). |
|
|
//| INPUT PARAMETERS: |
|
|
//| B - barycentric interpolant built with one of model |
|
|
//| building subroutines. |
|
|
//| T - interpolation point |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| F - barycentric interpolant at T |
|
|
//| DF - first derivative |
|
|
//+------------------------------------------------------------------+
|
|
static void CRatInt::BarycentricDiff1(CBarycentricInterpolant &b,double t,
|
|
double &f,double &df)
|
|
{
|
|
//--- create variables
|
|
double v=0;
|
|
double vv=0;
|
|
int i=0;
|
|
int k=0;
|
|
double n0=0;
|
|
double n1=0;
|
|
double d0=0;
|
|
double d1=0;
|
|
double s0=0;
|
|
double s1=0;
|
|
double xk=0;
|
|
double xi=0;
|
|
double xmin=0;
|
|
double xmax=0;
|
|
double xscale1=0;
|
|
double xoffs1=0;
|
|
double xscale2=0;
|
|
double xoffs2=0;
|
|
double xprev=0;
|
|
//--- initialization
|
|
f=0;
|
|
df=0;
|
|
//--- check
|
|
if(!CAp::Assert(!CInfOrNaN::IsInfinity(t),__FUNCTION__+": infinite T!"))
|
|
return;
|
|
//--- special case: NaN
|
|
if(CInfOrNaN::IsNaN(t))
|
|
{
|
|
//--- change values
|
|
f=CInfOrNaN::NaN();
|
|
df=CInfOrNaN::NaN();
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- special case: N=1
|
|
if(b.m_n==1)
|
|
{
|
|
//--- change values
|
|
f=b.m_sy*b.m_y[0];
|
|
df=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(b.m_sy==0.0)
|
|
{
|
|
//--- change values
|
|
f=0;
|
|
df=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(b.m_sy>0.0,__FUNCTION__+": internal error"))
|
|
return;
|
|
//--- We assume than N>1 and B.SY>0. Find:
|
|
//--- 1. pivot point (X[i] closest to T)
|
|
//--- 2. width of interval containing X[i]
|
|
v=MathAbs(b.m_x[0]-t);
|
|
k=0;
|
|
xmin=b.m_x[0];
|
|
xmax=b.m_x[0];
|
|
//--- calculation
|
|
for(i=1;i<=b.m_n-1;i++)
|
|
{
|
|
vv=b.m_x[i];
|
|
//--- check
|
|
if(MathAbs(vv-t)<v)
|
|
{
|
|
v=MathAbs(vv-t);
|
|
k=i;
|
|
}
|
|
//--- change values
|
|
xmin=MathMin(xmin,vv);
|
|
xmax=MathMax(xmax,vv);
|
|
}
|
|
//--- pivot point found,calculate dNumerator and dDenominator
|
|
xscale1=1/(xmax-xmin);
|
|
xoffs1=-(xmin/(xmax-xmin))+1;
|
|
xscale2=2;
|
|
xoffs2=-3;
|
|
t=t*xscale1+xoffs1;
|
|
t=t*xscale2+xoffs2;
|
|
xk=b.m_x[k];
|
|
xk=xk*xscale1+xoffs1;
|
|
xk=xk*xscale2+xoffs2;
|
|
v=t-xk;
|
|
n0=0;
|
|
n1=0;
|
|
d0=0;
|
|
d1=0;
|
|
xprev=-2;
|
|
//--- calculation
|
|
for(i=0;i<=b.m_n-1;i++)
|
|
{
|
|
//--- change values
|
|
xi=b.m_x[i];
|
|
xi=xi*xscale1+xoffs1;
|
|
xi=xi*xscale2+xoffs2;
|
|
//--- check
|
|
if(!CAp::Assert(xi>xprev,__FUNCTION__+": points are too close!"))
|
|
return;
|
|
xprev=xi;
|
|
//--- check
|
|
if(i!=k)
|
|
{
|
|
vv=CMath::Sqr(t-xi);
|
|
s0=(t-xk)/(t-xi);
|
|
s1=(xk-xi)/vv;
|
|
}
|
|
else
|
|
{
|
|
s0=1;
|
|
s1=0;
|
|
}
|
|
//--- change values
|
|
vv=b.m_w[i]*b.m_y[i];
|
|
n0=n0+s0*vv;
|
|
n1=n1+s1*vv;
|
|
vv=b.m_w[i];
|
|
d0=d0+s0*vv;
|
|
d1=d1+s1*vv;
|
|
}
|
|
//--- change values
|
|
f=b.m_sy*n0/d0;
|
|
df=(n1*d0-n0*d1)/CMath::Sqr(d0);
|
|
//--- check
|
|
if(df!=0.0)
|
|
df=MathSign(df)*MathExp(MathLog(MathAbs(df))+MathLog(b.m_sy)+MathLog(xscale1)+MathLog(xscale2));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Differentiation of barycentric interpolant: first/second |
|
|
//| derivatives. |
|
|
//| INPUT PARAMETERS: |
|
|
//| B - barycentric interpolant built with one of model |
|
|
//| building subroutines. |
|
|
//| T - interpolation point |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| F - barycentric interpolant at T |
|
|
//| DF - first derivative |
|
|
//| D2F - second derivative |
|
|
//| NOTE: this algorithm may fail due to overflow/underflor if used |
|
|
//| on data whose values are close to MaxRealNumber or MinRealNumber.|
|
|
//| Use more robust BarycentricDiff1() subroutine in such cases. |
|
|
//+------------------------------------------------------------------+
|
|
static void CRatInt::BarycentricDiff2(CBarycentricInterpolant &b,const double t,
|
|
double &f,double &df,double &d2f)
|
|
{
|
|
//--- create variables
|
|
double v=0;
|
|
double vv=0;
|
|
int i=0;
|
|
int k=0;
|
|
double n0=0;
|
|
double n1=0;
|
|
double n2=0;
|
|
double d0=0;
|
|
double d1=0;
|
|
double d2=0;
|
|
double s0=0;
|
|
double s1=0;
|
|
double s2=0;
|
|
double xk=0;
|
|
double xi=0;
|
|
//--- initialization
|
|
f=0;
|
|
df=0;
|
|
d2f=0;
|
|
//--- check
|
|
if(!CAp::Assert(!CInfOrNaN::IsInfinity(t),__FUNCTION__+": infinite T!"))
|
|
return;
|
|
//--- special case: NaN
|
|
if(CInfOrNaN::IsNaN(t))
|
|
{
|
|
//--- change values
|
|
f=CInfOrNaN::NaN();
|
|
df=CInfOrNaN::NaN();
|
|
d2f=CInfOrNaN::NaN();
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- special case: N=1
|
|
if(b.m_n==1)
|
|
{
|
|
//--- change values
|
|
f=b.m_sy*b.m_y[0];
|
|
df=0;
|
|
d2f=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(b.m_sy==0.0)
|
|
{
|
|
//--- change values
|
|
f=0;
|
|
df=0;
|
|
d2f=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- We assume than N>1 and B.SY>0. Find:
|
|
//--- 1. pivot point (X[i] closest to T)
|
|
//--- 2. width of interval containing X[i]
|
|
if(!CAp::Assert(b.m_sy>0.0,__FUNCTION__+": internal error"))
|
|
return;
|
|
//--- change values
|
|
f=0;
|
|
df=0;
|
|
d2f=0;
|
|
v=MathAbs(b.m_x[0]-t);
|
|
k=0;
|
|
for(i=1;i<=b.m_n-1;i++)
|
|
{
|
|
vv=b.m_x[i];
|
|
//--- check
|
|
if(MathAbs(vv-t)<v)
|
|
{
|
|
v=MathAbs(vv-t);
|
|
k=i;
|
|
}
|
|
}
|
|
//--- pivot point found, calculate dNumerator and dDenominator
|
|
xk=b.m_x[k];
|
|
v=t-xk;
|
|
n0=0;
|
|
n1=0;
|
|
n2=0;
|
|
d0=0;
|
|
d1=0;
|
|
d2=0;
|
|
//--- calculation
|
|
for(i=0;i<=b.m_n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i!=k)
|
|
{
|
|
xi=b.m_x[i];
|
|
vv=CMath::Sqr(t-xi);
|
|
s0=(t-xk)/(t-xi);
|
|
s1=(xk-xi)/vv;
|
|
s2=-(2*(xk-xi)/(vv*(t-xi)));
|
|
}
|
|
else
|
|
{
|
|
s0=1;
|
|
s1=0;
|
|
s2=0;
|
|
}
|
|
//--- change values
|
|
vv=b.m_w[i]*b.m_y[i];
|
|
n0=n0+s0*vv;
|
|
n1=n1+s1*vv;
|
|
n2=n2+s2*vv;
|
|
vv=b.m_w[i];
|
|
d0=d0+s0*vv;
|
|
d1=d1+s1*vv;
|
|
d2=d2+s2*vv;
|
|
}
|
|
//--- change values
|
|
f=b.m_sy*n0/d0;
|
|
df=b.m_sy*(n1*d0-n0*d1)/CMath::Sqr(d0);
|
|
d2f=b.m_sy*((n2*d0-n0*d2)*CMath::Sqr(d0)-(n1*d0-n0*d1)*2*d0*d1)/CMath::Sqr(CMath::Sqr(d0));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine performs linear transformation of the argument. |
|
|
//| INPUT PARAMETERS: |
|
|
//| B - rational interpolant in barycentric form |
|
|
//| CA, CB - transformation coefficients: x = CA*t + CB |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| B - transformed interpolant with X replaced by T |
|
|
//+------------------------------------------------------------------+
|
|
static void CRatInt::BarycentricLinTransX(CBarycentricInterpolant &b,
|
|
const double ca,const double cb)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
//--- special case,replace by constant F(CB)
|
|
if(ca==0.0)
|
|
{
|
|
b.m_sy=BarycentricCalc(b,cb);
|
|
v=1;
|
|
for(i=0;i<=b.m_n-1;i++)
|
|
{
|
|
b.m_y[i]=1;
|
|
b.m_w[i]=v;
|
|
v=-v;
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- general case: CA<>0
|
|
for(i=0;i<=b.m_n-1;i++)
|
|
b.m_x[i]=(b.m_x[i]-cb)/ca;
|
|
//--- check
|
|
if(ca<0.0)
|
|
{
|
|
for(i=0;i<=b.m_n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i<b.m_n-1-i)
|
|
{
|
|
//--- change values
|
|
j=b.m_n-1-i;
|
|
v=b.m_x[i];
|
|
b.m_x[i]=b.m_x[j];
|
|
b.m_x[j]=v;
|
|
v=b.m_y[i];
|
|
b.m_y[i]=b.m_y[j];
|
|
b.m_y[j]=v;
|
|
v=b.m_w[i];
|
|
b.m_w[i]=b.m_w[j];
|
|
b.m_w[j]=v;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine performs linear transformation of the barycentric|
|
|
//| interpolant. |
|
|
//| INPUT PARAMETERS: |
|
|
//| B - rational interpolant in barycentric form |
|
|
//| CA, CB - transformation coefficients: B2(x) = CA*B(x) + CB|
|
|
//| OUTPUT PARAMETERS: |
|
|
//| B - transformed interpolant |
|
|
//+------------------------------------------------------------------+
|
|
static void CRatInt::BarycentricLinTransY(CBarycentricInterpolant &b,
|
|
const double ca,const double cb)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- calculation
|
|
for(i=0;i<=b.m_n-1;i++)
|
|
b.m_y[i]=ca*b.m_sy*b.m_y[i]+cb;
|
|
//--- change value
|
|
b.m_sy=0;
|
|
for(i=0;i<=b.m_n-1;i++)
|
|
b.m_sy=MathMax(b.m_sy,MathAbs(b.m_y[i]));
|
|
//--- check
|
|
if(b.m_sy>0.0)
|
|
{
|
|
v=1/b.m_sy;
|
|
//--- calculation
|
|
for(i_=0;i_<=b.m_n-1;i_++)
|
|
b.m_y[i_]=v*b.m_y[i_];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Extracts X/Y/W arrays from rational interpolant |
|
|
//| INPUT PARAMETERS: |
|
|
//| B - barycentric interpolant |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| N - nodes count, N>0 |
|
|
//| X - interpolation nodes, array[0..N-1] |
|
|
//| F - function values, array[0..N-1] |
|
|
//| W - barycentric weights, array[0..N-1] |
|
|
//+------------------------------------------------------------------+
|
|
static void CRatInt::BarycentricUnpack(CBarycentricInterpolant &b,int &n,
|
|
double &x[],double &y[],double &w[])
|
|
{
|
|
//--- create variables
|
|
double v=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
n=b.m_n;
|
|
//--- allocation
|
|
ArrayResizeAL(x,n);
|
|
ArrayResizeAL(y,n);
|
|
ArrayResizeAL(w,n);
|
|
//--- initialization
|
|
v=b.m_sy;
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
x[i_]=b.m_x[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
y[i_]=v*b.m_y[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
w[i_]=b.m_w[i_];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Rational interpolant from X/Y/W arrays |
|
|
//| F(t)=SUM(i=0,n-1,w[i]*f[i]/(t-x[i])) / SUM(i=0,n-1,w[i]/(t-x[i]))|
|
|
//| INPUT PARAMETERS: |
|
|
//| X - interpolation nodes, array[0..N-1] |
|
|
//| F - function values, array[0..N-1] |
|
|
//| W - barycentric weights, array[0..N-1] |
|
|
//| N - nodes count, N>0 |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| B - barycentric interpolant built from (X, Y, W) |
|
|
//+------------------------------------------------------------------+
|
|
static void CRatInt::BarycentricBuildXYW(double &x[],double &y[],double &w[],
|
|
const int n,CBarycentricInterpolant &b)
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": incorrect N!"))
|
|
return;
|
|
//--- fill X/Y/W
|
|
ArrayResizeAL(b.m_x,n);
|
|
ArrayResizeAL(b.m_y,n);
|
|
ArrayResizeAL(b.m_w,n);
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
b.m_x[i_]=x[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
b.m_y[i_]=y[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
b.m_w[i_]=w[i_];
|
|
b.m_n=n;
|
|
//--- Normalize
|
|
BarycentricNormalize(b);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Rational interpolant without poles |
|
|
//| The subroutine constructs the rational interpolating function |
|
|
//| without real poles (see 'Barycentric rational interpolation with |
|
|
//| no poles and high rates of approximation', Michael S. Floater. |
|
|
//| and Kai Hormann, for more information on this subject). |
|
|
//| Input parameters: |
|
|
//| X - interpolation nodes, array[0..N-1]. |
|
|
//| Y - function values, array[0..N-1]. |
|
|
//| N - number of nodes, N>0. |
|
|
//| D - order of the interpolation scheme, 0 <= D <= N-1. |
|
|
//| D<0 will cause an error. |
|
|
//| D>=N it will be replaced with D=N-1. |
|
|
//| if you don't know what D to choose, use small value |
|
|
//| about 3-5. |
|
|
//| Output parameters: |
|
|
//| B - barycentric interpolant. |
|
|
//| Note: |
|
|
//| this algorithm always succeeds and calculates the weights |
|
|
//| with close to machine precision. |
|
|
//+------------------------------------------------------------------+
|
|
static void CRatInt::BarycentricBuildFloaterHormann(double &x[],double &y[],
|
|
const int n,int d,
|
|
CBarycentricInterpolant &b)
|
|
{
|
|
//--- create variables
|
|
double s0=0;
|
|
double s=0;
|
|
double v=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
int perm[];
|
|
double wtemp[];
|
|
double sortrbuf[];
|
|
double sortrbuf2[];
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(d>=0,__FUNCTION__+": incorrect D!"))
|
|
return;
|
|
//--- Prepare
|
|
if(d>n-1)
|
|
d=n-1;
|
|
b.m_n=n;
|
|
//--- special case: N=1
|
|
if(n==1)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(b.m_x,n);
|
|
ArrayResizeAL(b.m_y,n);
|
|
ArrayResizeAL(b.m_w,n);
|
|
//--- change values
|
|
b.m_x[0]=x[0];
|
|
b.m_y[0]=y[0];
|
|
b.m_w[0]=1;
|
|
//--- function call
|
|
BarycentricNormalize(b);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Fill X/Y
|
|
ArrayResizeAL(b.m_x,n);
|
|
ArrayResizeAL(b.m_y,n);
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
b.m_x[i_]=x[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
b.m_y[i_]=y[i_];
|
|
//--- function call
|
|
CTSort::TagSortFastR(b.m_x,b.m_y,sortrbuf,sortrbuf2,n);
|
|
//--- Calculate Wk
|
|
ArrayResizeAL(b.m_w,n);
|
|
s0=1;
|
|
for(k=1;k<=d;k++)
|
|
s0=-s0;
|
|
//--- calculation
|
|
for(k=0;k<=n-1;k++)
|
|
{
|
|
//--- Wk
|
|
s=0;
|
|
for(i=(int)(MathMax(k-d,0));i<=MathMin(k,n-1-d);i++)
|
|
{
|
|
v=1;
|
|
for(j=i;j<=i+d;j++)
|
|
{
|
|
//--- check
|
|
if(j!=k)
|
|
v=v/MathAbs(b.m_x[k]-b.m_x[j]);
|
|
}
|
|
s=s+v;
|
|
}
|
|
b.m_w[k]=s0*s;
|
|
//--- Next S0
|
|
s0=-s0;
|
|
}
|
|
//--- Normalize
|
|
BarycentricNormalize(b);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copying of the barycentric interpolant (for internal use only) |
|
|
//| INPUT PARAMETERS: |
|
|
//| B - barycentric interpolant |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| B2 - copy(B1) |
|
|
//+------------------------------------------------------------------+
|
|
static void CRatInt::BarycentricCopy(CBarycentricInterpolant &b,
|
|
CBarycentricInterpolant &b2)
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- change values
|
|
b2.m_n=b.m_n;
|
|
b2.m_sy=b.m_sy;
|
|
//--- allocation
|
|
ArrayResizeAL(b2.m_x,b2.m_n);
|
|
ArrayResizeAL(b2.m_y,b2.m_n);
|
|
ArrayResizeAL(b2.m_w,b2.m_n);
|
|
//--- copy
|
|
for(i_=0;i_<=b2.m_n-1;i_++)
|
|
b2.m_x[i_]=b.m_x[i_];
|
|
for(i_=0;i_<=b2.m_n-1;i_++)
|
|
b2.m_y[i_]=b.m_y[i_];
|
|
for(i_=0;i_<=b2.m_n-1;i_++)
|
|
b2.m_w[i_]=b.m_w[i_];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Normalization of barycentric interpolant: |
|
|
//| * B.N, B.X, B.Y and B.W are initialized |
|
|
//| * B.SY is NOT initialized |
|
|
//| * Y[] is normalized, scaling coefficient is stored in B.SY |
|
|
//| * W[] is normalized, no scaling coefficient is stored |
|
|
//| * X[] is sorted |
|
|
//| Internal subroutine. |
|
|
//+------------------------------------------------------------------+
|
|
static void CRatInt::BarycentricNormalize(CBarycentricInterpolant &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int j2=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
int p1[];
|
|
int p2[];
|
|
//--- Normalize task: |Y|<=1,|W|<=1,sort X[]
|
|
b.m_sy=0;
|
|
for(i=0;i<=b.m_n-1;i++)
|
|
b.m_sy=MathMax(b.m_sy,MathAbs(b.m_y[i]));
|
|
//--- check
|
|
if(b.m_sy>0.0 && MathAbs(b.m_sy-1)>10*CMath::m_machineepsilon)
|
|
{
|
|
v=1/b.m_sy;
|
|
for(i_=0;i_<=b.m_n-1;i_++)
|
|
b.m_y[i_]=v*b.m_y[i_];
|
|
}
|
|
//--- change value
|
|
v=0;
|
|
for(i=0;i<=b.m_n-1;i++)
|
|
v=MathMax(v,MathAbs(b.m_w[i]));
|
|
//--- check
|
|
if(v>0.0 && MathAbs(v-1)>10*CMath::m_machineepsilon)
|
|
{
|
|
v=1/v;
|
|
for(i_=0;i_<=b.m_n-1;i_++)
|
|
b.m_w[i_]=v*b.m_w[i_];
|
|
}
|
|
for(i=0;i<=b.m_n-2;i++)
|
|
{
|
|
//--- check
|
|
if(b.m_x[i+1]<b.m_x[i])
|
|
{
|
|
//--- function call
|
|
CTSort::TagSort(b.m_x,b.m_n,p1,p2);
|
|
//--- calculation
|
|
for(j=0;j<=b.m_n-1;j++)
|
|
{
|
|
j2=p2[j];
|
|
v=b.m_y[j];
|
|
b.m_y[j]=b.m_y[j2];
|
|
b.m_y[j2]=v;
|
|
v=b.m_w[j];
|
|
b.m_w[j]=b.m_w[j2];
|
|
b.m_w[j2]=v;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Polynomial interpolant |
|
|
//+------------------------------------------------------------------+
|
|
class CPolInt
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CPolInt(void);
|
|
~CPolInt(void);
|
|
//--- methods
|
|
static void PolynomialBar2Cheb(CBarycentricInterpolant &p,const double a,const double b,double &t[]);
|
|
static void PolynomialCheb2Bar(double &t[],const int n,const double a,const double b,CBarycentricInterpolant &p);
|
|
static void PolynomialBar2Pow(CBarycentricInterpolant &p,const double c,const double s,double &a[]);
|
|
static void PolynomialPow2Bar(double &a[],const int n,const double c,const double s,CBarycentricInterpolant &p);
|
|
static void PolynomialBuild(double &cx[],double &cy[],const int n,CBarycentricInterpolant &p);
|
|
static void PolynomialBuildEqDist(const double a,const double b,double &y[],const int n,CBarycentricInterpolant &p);
|
|
static void PolynomialBuildCheb1(const double a,const double b,double &y[],const int n,CBarycentricInterpolant &p);
|
|
static void PolynomialBuildCheb2(const double a,const double b,double &y[],const int n,CBarycentricInterpolant &p);
|
|
static double PolynomialCalcEqDist(const double a,const double b,double &f[],const int n,const double t);
|
|
static double PolynomialCalcCheb1(const double a,const double b,double &f[],const int n,double t);
|
|
static double PolynomialCalcCheb2(const double a,const double b,double &f[],const int n,double t);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CPolInt::CPolInt(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CPolInt::~CPolInt(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Conversion from barycentric representation to Chebyshev basis. |
|
|
//| This function has O(N^2) complexity. |
|
|
//| INPUT PARAMETERS: |
|
|
//| P - polynomial in barycentric form |
|
|
//| A,B - base interval for Chebyshev polynomials (see below) |
|
|
//| A<>B |
|
|
//| OUTPUT PARAMETERS |
|
|
//| T - coefficients of Chebyshev representation; |
|
|
//| P(x) = sum { T[i]*Ti(2*(x-A)/(B-A)-1), i=0..N-1 }, |
|
|
//| where Ti - I-th Chebyshev polynomial. |
|
|
//| NOTES: |
|
|
//| barycentric interpolant passed as P may be either polynomial |
|
|
//| obtained from polynomial interpolation/ fitting or rational |
|
|
//| function which is NOT polynomial. We can't distinguish |
|
|
//| between these two cases, and this algorithm just tries to |
|
|
//| work assuming that P IS a polynomial. If not, algorithm will |
|
|
//| return results, but they won't have any meaning. |
|
|
//+------------------------------------------------------------------+
|
|
static void CPolInt::PolynomialBar2Cheb(CBarycentricInterpolant &p,
|
|
const double a,const double b,
|
|
double &t[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int k=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double vp[];
|
|
double vx[];
|
|
double tk[];
|
|
double tk1[];
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(a),__FUNCTION__+": A is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(b),__FUNCTION__+": B is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(a!=b,__FUNCTION__+": A=B!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(p.m_n>0,__FUNCTION__+": P is not correctly initialized barycentric interpolant!"))
|
|
return;
|
|
//--- Calculate function values on a Chebyshev grid
|
|
ArrayResizeAL(vp,p.m_n);
|
|
ArrayResizeAL(vx,p.m_n);
|
|
for(i=0;i<=p.m_n-1;i++)
|
|
{
|
|
vx[i]=MathCos(M_PI*(i+0.5)/p.m_n);
|
|
vp[i]=CRatInt::BarycentricCalc(p,0.5*(vx[i]+1)*(b-a)+a);
|
|
}
|
|
//--- T[0]
|
|
ArrayResizeAL(t,p.m_n);
|
|
v=0;
|
|
for(i=0;i<=p.m_n-1;i++)
|
|
v=v+vp[i];
|
|
t[0]=v/p.m_n;
|
|
//--- other T's.
|
|
//--- NOTES:
|
|
//--- 1. TK stores T{k} on VX,TK1 stores T{k-1} on VX
|
|
//--- 2. we can do same calculations with fast DCT,but it
|
|
//--- * adds dependencies
|
|
//--- * still leaves us with O(N^2) algorithm because
|
|
//--- preparation of function values is O(N^2) process
|
|
if(p.m_n>1)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(tk,p.m_n);
|
|
ArrayResizeAL(tk1,p.m_n);
|
|
for(i=0;i<=p.m_n-1;i++)
|
|
{
|
|
tk[i]=vx[i];
|
|
tk1[i]=1;
|
|
}
|
|
//--- calculation
|
|
for(k=1;k<=p.m_n-1;k++)
|
|
{
|
|
//--- calculate discrete product of function vector and TK
|
|
v=0.0;
|
|
for(i_=0;i_<=p.m_n-1;i_++)
|
|
v+=tk[i_]*vp[i_];
|
|
t[k]=v/(0.5*p.m_n);
|
|
//--- Update TK and TK1
|
|
for(i=0;i<=p.m_n-1;i++)
|
|
{
|
|
v=2*vx[i]*tk[i]-tk1[i];
|
|
tk1[i]=tk[i];
|
|
tk[i]=v;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Conversion from Chebyshev basis to barycentric representation. |
|
|
//| This function has O(N^2) complexity. |
|
|
//| INPUT PARAMETERS: |
|
|
//| T - coefficients of Chebyshev representation; |
|
|
//| P(x) = sum { T[i]*Ti(2*(x-A)/(B-A)-1), i=0..N }, |
|
|
//| where Ti - I-th Chebyshev polynomial. |
|
|
//| N - number of coefficients: |
|
|
//| * if given, only leading N elements of T are used |
|
|
//| * if not given, automatically determined from size |
|
|
//| of T |
|
|
//| A,B - base interval for Chebyshev polynomials (see above) |
|
|
//| A<B |
|
|
//| OUTPUT PARAMETERS |
|
|
//| P - polynomial in barycentric form |
|
|
//+------------------------------------------------------------------+
|
|
static void CPolInt::PolynomialCheb2Bar(double &t[],const int n,const double a,
|
|
const double b,CBarycentricInterpolant &p)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int k=0;
|
|
double tk=0;
|
|
double tk1=0;
|
|
double vx=0;
|
|
double vy=0;
|
|
double v=0;
|
|
//--- create array
|
|
double y[];
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(a),__FUNCTION__+": A is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(b),__FUNCTION__+": B is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(a!=b,__FUNCTION__+": A=B!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(t)>=n,__FUNCTION__+": Length(T)<N"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(t,n),__FUNCTION__+": T[] contains INF or NAN"))
|
|
return;
|
|
//--- Calculate function values on a Chebyshev grid spanning [-1,+1]
|
|
ArrayResizeAL(y,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- Calculate value on a grid spanning [-1,+1]
|
|
vx=MathCos(M_PI*(i+0.5)/n);
|
|
vy=t[0];
|
|
tk1=1;
|
|
tk=vx;
|
|
//--- change values
|
|
for(k=1;k<=n-1;k++)
|
|
{
|
|
vy=vy+t[k]*tk;
|
|
v=2*vx*tk-tk1;
|
|
tk1=tk;
|
|
tk=v;
|
|
}
|
|
y[i]=vy;
|
|
}
|
|
//--- Build barycentric interpolant,map grid from [-1,+1] to [A,B]
|
|
PolynomialBuildCheb1(a,b,y,n,p);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Conversion from barycentric representation to power basis. |
|
|
//| This function has O(N^2) complexity. |
|
|
//| INPUT PARAMETERS: |
|
|
//| P - polynomial in barycentric form |
|
|
//| C - offset (see below); 0.0 is used as default value. |
|
|
//| S - scale (see below); 1.0 is used as default value. |
|
|
//| S<>0. |
|
|
//| OUTPUT PARAMETERS |
|
|
//| A - coefficients, |
|
|
//| P(x) = sum { A[i]*((X-C)/S)^i, i=0..N-1 } |
|
|
//| N - number of coefficients (polynomial degree plus 1) |
|
|
//| NOTES: |
|
|
//| 1. this function accepts offset and scale, which can be set to |
|
|
//| improve numerical properties of polynomial. For example, if |
|
|
//| P was obtained as result of interpolation on [-1,+1], you can|
|
|
//| set C=0 and S=1 and represent P as sum of 1, x, x^2, x^3 and |
|
|
//| so on. In most cases you it is exactly what you need. |
|
|
//| However, if your interpolation model was built on [999,1001],|
|
|
//| you will see significant growth of numerical errors when |
|
|
//| using {1, x, x^2, x^3} as basis. Representing P as sum of 1, |
|
|
//| (x-1000), (x-1000)^2, (x-1000)^3 will be better option. Such |
|
|
//| representation can be obtained by using 1000.0 as offset |
|
|
//| C and 1.0 as scale S. |
|
|
//| 2. power basis is ill-conditioned and tricks described above |
|
|
//| can't solve this problem completely. This function will |
|
|
//| return coefficients in any case, but for N>8 they will become|
|
|
//| unreliable. However, N's less than 5 are pretty safe. |
|
|
//| 3. barycentric interpolant passed as P may be either polynomial |
|
|
//| obtained from polynomial interpolation/ fitting or rational |
|
|
//| function which is NOT polynomial. We can't distinguish |
|
|
//| between these two cases, and this algorithm just tries to |
|
|
//| work assuming that P IS a polynomial. If not, algorithm will |
|
|
//| return results, but they won't have any meaning. |
|
|
//+------------------------------------------------------------------+
|
|
static void CPolInt::PolynomialBar2Pow(CBarycentricInterpolant &p,
|
|
const double c,const double s,
|
|
double &a[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int k=0;
|
|
double e=0;
|
|
double d=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double vp[];
|
|
double vx[];
|
|
double tk[];
|
|
double tk1[];
|
|
double t[];
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(c),__FUNCTION__+": C is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(s),__FUNCTION__+": S is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(s!=0.0,__FUNCTION__+": S=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(p.m_n>0,__FUNCTION__+": P is not correctly initialized barycentric interpolant!"))
|
|
return;
|
|
//--- Calculate function values on a Chebyshev grid
|
|
ArrayResizeAL(vp,p.m_n);
|
|
ArrayResizeAL(vx,p.m_n);
|
|
for(i=0;i<=p.m_n-1;i++)
|
|
{
|
|
vx[i]=MathCos(M_PI*(i+0.5)/p.m_n);
|
|
vp[i]=CRatInt::BarycentricCalc(p,s*vx[i]+c);
|
|
}
|
|
//--- T[0]
|
|
ArrayResizeAL(t,p.m_n);
|
|
v=0;
|
|
for(i=0;i<=p.m_n-1;i++)
|
|
v=v+vp[i];
|
|
t[0]=v/p.m_n;
|
|
//--- other T's.
|
|
//--- NOTES:
|
|
//--- 1. TK stores T{k} on VX,TK1 stores T{k-1} on VX
|
|
//--- 2. we can do same calculations with fast DCT,but it
|
|
//--- * adds dependencies
|
|
//--- * still leaves us with O(N^2) algorithm because
|
|
//--- preparation of function values is O(N^2) process
|
|
if(p.m_n>1)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(tk,p.m_n);
|
|
ArrayResizeAL(tk1,p.m_n);
|
|
for(i=0;i<=p.m_n-1;i++)
|
|
{
|
|
tk[i]=vx[i];
|
|
tk1[i]=1;
|
|
}
|
|
//--- calculation
|
|
for(k=1;k<=p.m_n-1;k++)
|
|
{
|
|
//--- calculate discrete product of function vector and TK
|
|
v=0.0;
|
|
for(i_=0;i_<=p.m_n-1;i_++)
|
|
v+=tk[i_]*vp[i_];
|
|
t[k]=v/(0.5*p.m_n);
|
|
//--- Update TK and TK1
|
|
for(i=0;i<=p.m_n-1;i++)
|
|
{
|
|
v=2*vx[i]*tk[i]-tk1[i];
|
|
tk1[i]=tk[i];
|
|
tk[i]=v;
|
|
}
|
|
}
|
|
}
|
|
//--- Convert from Chebyshev basis to power basis
|
|
ArrayResizeAL(a,p.m_n);
|
|
for(i=0;i<=p.m_n-1;i++)
|
|
a[i]=0;
|
|
d=0;
|
|
//--- calculation
|
|
for(i=0;i<=p.m_n-1;i++)
|
|
{
|
|
for(k=i;k<=p.m_n-1;k++)
|
|
{
|
|
e=a[k];
|
|
a[k]=0;
|
|
//--- check
|
|
if(i<=1 && k==i)
|
|
a[k]=1;
|
|
else
|
|
{
|
|
//--- check
|
|
if(i!=0)
|
|
a[k]=2*d;
|
|
//--- check
|
|
if(k>i+1)
|
|
a[k]=a[k]-a[k-2];
|
|
}
|
|
d=e;
|
|
}
|
|
//--- change values
|
|
d=a[i];
|
|
e=0;
|
|
k=i;
|
|
//--- cycle
|
|
while(k<=p.m_n-1)
|
|
{
|
|
e=e+a[k]*t[k];
|
|
k=k+2;
|
|
}
|
|
a[i]=e;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Conversion from power basis to barycentric representation. |
|
|
//| This function has O(N^2) complexity. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - coefficients, P(x)=sum { A[i]*((X-C)/S)^i, i=0..N-1 }|
|
|
//| N - number of coefficients (polynomial degree plus 1) |
|
|
//| * if given, only leading N elements of A are used |
|
|
//| * if not given, automatically determined from size |
|
|
//| of A |
|
|
//| C - offset (see below); 0.0 is used as default value. |
|
|
//| S - scale (see below); 1.0 is used as default value. |
|
|
//| S<>0. |
|
|
//| OUTPUT PARAMETERS |
|
|
//| P - polynomial in barycentric form |
|
|
//| NOTES: |
|
|
//| 1. this function accepts offset and scale, which can be set to |
|
|
//| improve numerical properties of polynomial. For example, if |
|
|
//| you interpolate on [-1,+1], you can set C=0 and S=1 and |
|
|
//| convert from sum of 1, x, x^2, x^3 and so on. In most cases |
|
|
//| you it is exactly what you need. |
|
|
//| However, if your interpolation model was built on [999,1001],|
|
|
//| you will see significant growth of numerical errors when |
|
|
//| using {1, x, x^2, x^3} as input basis. Converting from sum |
|
|
//| of 1, (x-1000), (x-1000)^2, (x-1000)^3 will be better option |
|
|
//| (you have to specify 1000.0 as offset C and 1.0 as scale S). |
|
|
//| 2. power basis is ill-conditioned and tricks described above |
|
|
//| can't solve this problem completely. This function will |
|
|
//| return barycentric model in any case, but for N>8 accuracy |
|
|
//| well degrade. However, N's less than 5 are pretty safe. |
|
|
//+------------------------------------------------------------------+
|
|
static void CPolInt::PolynomialPow2Bar(double &a[],const int n,const double c,
|
|
const double s,CBarycentricInterpolant &p)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int k=0;
|
|
double vx=0;
|
|
double vy=0;
|
|
double px=0;
|
|
//--- create array
|
|
double y[];
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(c),__FUNCTION__+": C is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(s),__FUNCTION__+": S is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(s!=0.0,__FUNCTION__+": S is zero!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(a)>=n,__FUNCTION__+": Length(A)<N"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(a,n),__FUNCTION__+": A[] contains INF or NAN"))
|
|
return;
|
|
//--- Calculate function values on a Chebyshev grid spanning [-1,+1]
|
|
ArrayResizeAL(y,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- Calculate value on a grid spanning [-1,+1]
|
|
vx=MathCos(M_PI*(i+0.5)/n);
|
|
vy=a[0];
|
|
px=vx;
|
|
//--- calculation
|
|
for(k=1;k<=n-1;k++)
|
|
{
|
|
vy=vy+px*a[k];
|
|
px=px*vx;
|
|
}
|
|
y[i]=vy;
|
|
}
|
|
//--- Build barycentric interpolant,map grid from [-1,+1] to [A,B]
|
|
PolynomialBuildCheb1(c-s,c+s,y,n,p);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Lagrange intepolant: generation of the model on the general grid.|
|
|
//| This function has O(N^2) complexity. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - abscissas, array[0..N-1] |
|
|
//| Y - function values, array[0..N-1] |
|
|
//| N - number of points, N>=1 |
|
|
//| OUTPUT PARAMETERS |
|
|
//| P - barycentric model which represents Lagrange |
|
|
//| interpolant (see ratint unit info and |
|
|
//| BarycentricCalc() description for more information). |
|
|
//+------------------------------------------------------------------+
|
|
static void CPolInt::PolynomialBuild(double &cx[],double &cy[],const int n,
|
|
CBarycentricInterpolant &p)
|
|
{
|
|
//--- create variables
|
|
int j=0;
|
|
int k=0;
|
|
double b=0;
|
|
double a=0;
|
|
double v=0;
|
|
double mx=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double w[];
|
|
double sortrbuf[];
|
|
double sortrbuf2[];
|
|
double x[];
|
|
double y[];
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- function call
|
|
CTSort::TagSortFastR(x,y,sortrbuf,sortrbuf2,n);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
|
|
return;
|
|
//--- calculate W[j]
|
|
//--- multi-pass algorithm is used to avoid overflow
|
|
ArrayResizeAL(w,n);
|
|
a=x[0];
|
|
b=x[0];
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
w[j]=1;
|
|
a=MathMin(a,x[j]);
|
|
b=MathMax(b,x[j]);
|
|
}
|
|
//--- calculation
|
|
for(k=0;k<=n-1;k++)
|
|
{
|
|
//--- W[K] is used instead of 0.0 because
|
|
//--- cycle on J does not touch K-th element
|
|
//--- and we MUST get maximum from ALL elements
|
|
mx=MathAbs(w[k]);
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(j!=k)
|
|
{
|
|
v=(b-a)/(x[j]-x[k]);
|
|
w[j]=w[j]*v;
|
|
mx=MathMax(mx,MathAbs(w[j]));
|
|
}
|
|
}
|
|
//--- check
|
|
if(k%5==0)
|
|
{
|
|
//--- every 5-th run we renormalize W[]
|
|
v=1/mx;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
w[i_]=v*w[i_];
|
|
}
|
|
}
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x,y,w,n,p);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Lagrange intepolant: generation of the model on equidistant grid.|
|
|
//| This function has O(N) complexity. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - left boundary of [A,B] |
|
|
//| B - right boundary of [A,B] |
|
|
//| Y - function values at the nodes, array[0..N-1] |
|
|
//| N - number of points, N>=1 |
|
|
//| for N=1 a constant model is constructed. |
|
|
//| OUTPUT PARAMETERS |
|
|
//| P - barycentric model which represents Lagrange |
|
|
//| interpolant (see ratint unit info and |
|
|
//| BarycentricCalc() description for more information). |
|
|
//+------------------------------------------------------------------+
|
|
static void CPolInt::PolynomialBuildEqDist(const double a,const double b,
|
|
double &y[],const int n,
|
|
CBarycentricInterpolant &p)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v=0;
|
|
//--- create arrays
|
|
double w[];
|
|
double x[];
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(a),__FUNCTION__+": A is infinite or NaN!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(b),__FUNCTION__+": B is infinite or NaN!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(b!=a,__FUNCTION__+": B=A!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((double)(a+(b-a)/n)!=a,__FUNCTION__+": B is too close to A!"))
|
|
return;
|
|
//--- Special case: N=1
|
|
if(n==1)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(x,1);
|
|
ArrayResizeAL(w,1);
|
|
x[0]=0.5*(b+a);
|
|
w[0]=1;
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x,y,w,1,p);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- general case
|
|
ArrayResizeAL(x,n);
|
|
ArrayResizeAL(w,n);
|
|
v=1;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
w[i]=v;
|
|
x[i]=a+(b-a)*i/(n-1);
|
|
v=-(v*(n-1-i));
|
|
v=v/(i+1);
|
|
}
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x,y,w,n,p);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Lagrange intepolant on Chebyshev grid (first kind). |
|
|
//| This function has O(N) complexity. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - left boundary of [A,B] |
|
|
//| B - right boundary of [A,B] |
|
|
//| Y - function values at the nodes, array[0..N-1], |
|
|
//| Y[I] = Y(0.5*(B+A) + 0.5*(B-A)*Cos(PI*(2*i+1)/(2*n)))|
|
|
//| N - number of points, N>=1 |
|
|
//| for N=1 a constant model is constructed. |
|
|
//| OUTPUT PARAMETERS |
|
|
//| P - barycentric model which represents Lagrange |
|
|
//| interpolant (see ratint unit info and |
|
|
//| BarycentricCalc() description for more information). |
|
|
//+------------------------------------------------------------------+
|
|
static void CPolInt::PolynomialBuildCheb1(const double a,const double b,
|
|
double &y[],const int n,
|
|
CBarycentricInterpolant &p)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v=0;
|
|
double t=0;
|
|
//--- create arrays
|
|
double w[];
|
|
double x[];
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(a),__FUNCTION__+": A is infinite or NaN!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(b),__FUNCTION__+": B is infinite or NaN!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(b!=a,__FUNCTION__+": B=A!"))
|
|
return;
|
|
//--- Special case: N=1
|
|
if(n==1)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(x,1);
|
|
ArrayResizeAL(w,1);
|
|
x[0]=0.5*(b+a);
|
|
w[0]=1;
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x,y,w,1,p);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- general case
|
|
ArrayResizeAL(x,n);
|
|
ArrayResizeAL(w,n);
|
|
v=1;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
t=MathTan(0.5*M_PI*(2*i+1)/(2*n));
|
|
w[i]=2*v*t/(1+CMath::Sqr(t));
|
|
x[i]=0.5*(b+a)+0.5*(b-a)*(1-CMath::Sqr(t))/(1+CMath::Sqr(t));
|
|
v=-v;
|
|
}
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x,y,w,n,p);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Lagrange intepolant on Chebyshev grid (second kind). |
|
|
//| This function has O(N) complexity. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - left boundary of [A,B] |
|
|
//| B - right boundary of [A,B] |
|
|
//| Y - function values at the nodes, array[0..N-1], |
|
|
//| Y[I] = Y(0.5*(B+A) + 0.5*(B-A)*Cos(PI*i/(n-1))) |
|
|
//| N - number of points, N>=1 |
|
|
//| for N=1 a constant model is constructed. |
|
|
//| OUTPUT PARAMETERS |
|
|
//| P - barycentric model which represents Lagrange |
|
|
//| interpolant (see ratint unit info and |
|
|
//| BarycentricCalc() description for more information). |
|
|
//+------------------------------------------------------------------+
|
|
static void CPolInt::PolynomialBuildCheb2(const double a,const double b,
|
|
double &y[],const int n,
|
|
CBarycentricInterpolant &p)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v=0;
|
|
//--- create arrays
|
|
double w[];
|
|
double x[];
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(a),__FUNCTION__+": A is infinite or NaN!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(b),__FUNCTION__+": B is infinite or NaN!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(b!=a,__FUNCTION__+": B=A!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- Special case: N=1
|
|
if(n==1)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(x,1);
|
|
ArrayResizeAL(w,1);
|
|
x[0]=0.5*(b+a);
|
|
w[0]=1;
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x,y,w,1,p);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- general case
|
|
ArrayResizeAL(x,n);
|
|
ArrayResizeAL(w,n);
|
|
v=1;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i==0 || i==n-1)
|
|
w[i]=v*0.5;
|
|
else
|
|
w[i]=v;
|
|
x[i]=0.5*(b+a)+0.5*(b-a)*MathCos(M_PI*i/(n-1));
|
|
v=-v;
|
|
}
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x,y,w,n,p);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Fast equidistant polynomial interpolation function with O(N) |
|
|
//| complexity |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - left boundary of [A,B] |
|
|
//| B - right boundary of [A,B] |
|
|
//| F - function values, array[0..N-1] |
|
|
//| N - number of points on equidistant grid, N>=1 |
|
|
//| for N=1 a constant model is constructed. |
|
|
//| T - position where P(x) is calculated |
|
|
//| RESULT |
|
|
//| value of the Lagrange interpolant at T |
|
|
//| IMPORTANT |
|
|
//| this function provides fast interface which is not |
|
|
//| overflow-safe nor it is very precise. |
|
|
//| the best option is to use PolynomialBuildEqDist() or |
|
|
//| BarycentricCalc() subroutines unless you are pretty sure that|
|
|
//| your data will not result in overflow. |
|
|
//+------------------------------------------------------------------+
|
|
static double CPolInt::PolynomialCalcEqDist(const double a,const double b,
|
|
double &f[],const int n,const double t)
|
|
{
|
|
//--- create variables
|
|
double s1=0;
|
|
double s2=0;
|
|
double v=0;
|
|
double threshold=0;
|
|
double s=0;
|
|
double h=0;
|
|
int i=0;
|
|
int j=0;
|
|
double w=0;
|
|
double x=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(f)>=n,__FUNCTION__+": Length(F)<N!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(a),__FUNCTION__+": A is infinite or NaN!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(b),__FUNCTION__+": B is infinite or NaN!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(f,n),__FUNCTION__+": F contains infinite or NaN values!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(b!=a,__FUNCTION__+": B=A!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(!CInfOrNaN::IsInfinity(t),__FUNCTION__+": T is infinite!"))
|
|
return(EMPTY_VALUE);
|
|
//--- Special case: T is NAN
|
|
if(CInfOrNaN::IsNaN(t))
|
|
return(CInfOrNaN::NaN());
|
|
//--- Special case: N=1
|
|
if(n==1)
|
|
return(f[0]);
|
|
//--- First,decide: should we use "safe" formula (guarded
|
|
//--- against overflow) or fast one?
|
|
threshold=MathSqrt(CMath::m_minrealnumber);
|
|
j=0;
|
|
s=t-a;
|
|
//--- calculation
|
|
for(i=1;i<=n-1;i++)
|
|
{
|
|
x=a+(double)i/(double)(n-1)*(b-a);
|
|
//--- check
|
|
if(MathAbs(t-x)<MathAbs(s))
|
|
{
|
|
s=t-x;
|
|
j=i;
|
|
}
|
|
}
|
|
//--- check
|
|
if(s==0.0)
|
|
return(f[j]);
|
|
//--- check
|
|
if(MathAbs(s)>threshold)
|
|
{
|
|
//--- use fast formula
|
|
j=-1;
|
|
s=1.0;
|
|
}
|
|
//--- Calculate using safe or fast barycentric formula
|
|
s1=0;
|
|
s2=0;
|
|
w=1.0;
|
|
h=(b-a)/(n-1);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i!=j)
|
|
{
|
|
v=s*w/(t-(a+i*h));
|
|
s1=s1+v*f[i];
|
|
s2=s2+v;
|
|
}
|
|
else
|
|
{
|
|
v=w;
|
|
s1=s1+v*f[i];
|
|
s2=s2+v;
|
|
}
|
|
//--- change values
|
|
w=-(w*(n-1-i));
|
|
w=w/(i+1);
|
|
}
|
|
//--- return result
|
|
return(s1/s2);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Fast polynomial interpolation function on Chebyshev points (first|
|
|
//| kind) with O(N) complexity. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - left boundary of [A,B] |
|
|
//| B - right boundary of [A,B] |
|
|
//| F - function values, array[0..N-1] |
|
|
//| N - number of points on Chebyshev grid (first kind), |
|
|
//| X[i] = 0.5*(B+A) + 0.5*(B-A)*Cos(PI*(2*i+1)/(2*n)) |
|
|
//| for N=1 a constant model is constructed. |
|
|
//| T - position where P(x) is calculated |
|
|
//| RESULT |
|
|
//| value of the Lagrange interpolant at T |
|
|
//| IMPORTANT |
|
|
//| this function provides fast interface which is not |
|
|
//| overflow-safe nor it is very precise |
|
|
//| the best option is to use PolIntBuildCheb1() or |
|
|
//| BarycentricCalc() subroutines unless you are pretty sure that|
|
|
//| your data will not result in overflow. |
|
|
//+------------------------------------------------------------------+
|
|
static double CPolInt::PolynomialCalcCheb1(const double a,const double b,
|
|
double &f[],const int n,double t)
|
|
{
|
|
//--- create variables
|
|
double s1=0;
|
|
double s2=0;
|
|
double v=0;
|
|
double threshold=0;
|
|
double s=0;
|
|
int i=0;
|
|
int j=0;
|
|
double a0=0;
|
|
double delta=0;
|
|
double alpha=0;
|
|
double beta=0;
|
|
double ca=0;
|
|
double sa=0;
|
|
double tempc=0;
|
|
double temps=0;
|
|
double x=0;
|
|
double w=0;
|
|
double p1=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(f)>=n,__FUNCTION__+": Length(F)<N!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(a),__FUNCTION__+": A is infinite or NaN!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(b),__FUNCTION__+": B is infinite or NaN!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(f,n),__FUNCTION__+": F contains infinite or NaN values!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(b!=a,__FUNCTION__+": B=A!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(!CInfOrNaN::IsInfinity(t),__FUNCTION__+": T is infinite!"))
|
|
return(EMPTY_VALUE);
|
|
//--- Special case: T is NAN
|
|
if(CInfOrNaN::IsNaN(t))
|
|
return(CInfOrNaN::NaN());
|
|
//--- Special case: N=1
|
|
if(n==1)
|
|
return(f[0]);
|
|
//--- Prepare information for the recurrence formula
|
|
//--- used to calculate sin(pi*(2j+1)/(2n+2)) and
|
|
//--- cos(pi*(2j+1)/(2n+2)):
|
|
//--- A0=pi/(2n+2)
|
|
//--- Delta=pi/(n+1)
|
|
//--- Alpha=2 sin^2 (Delta/2)
|
|
//--- Beta=sin(Delta)
|
|
//--- so that sin(..)=sin(A0+j*delta) and cos(..)=cos(A0+j*delta).
|
|
//--- Then we use
|
|
//--- sin(x+delta)=sin(x) - (alpha*sin(x) - beta*cos(x))
|
|
//--- cos(x+delta)=cos(x) - (alpha*cos(x) - beta*sin(x))
|
|
//--- to repeatedly calculate sin(..) and cos(..).
|
|
threshold=MathSqrt(CMath::m_minrealnumber);
|
|
t=(t-0.5*(a+b))/(0.5*(b-a));
|
|
a0=M_PI/(2*(n-1)+2);
|
|
delta=2*M_PI/(2*(n-1)+2);
|
|
alpha=2*CMath::Sqr(MathSin(delta/2));
|
|
beta=MathSin(delta);
|
|
//--- First, decide: should we use "safe" formula (guarded
|
|
//--- against overflow) or fast one?
|
|
ca=MathCos(a0);
|
|
sa=MathSin(a0);
|
|
j=0;
|
|
x=ca;
|
|
s=t-x;
|
|
//--- calculation
|
|
for(i=1;i<=n-1;i++)
|
|
{
|
|
//--- Next X[i]
|
|
temps=sa-(alpha*sa-beta*ca);
|
|
tempc=ca-(alpha*ca+beta*sa);
|
|
sa=temps;
|
|
ca=tempc;
|
|
x=ca;
|
|
//--- Use X[i]
|
|
if(MathAbs(t-x)<MathAbs(s))
|
|
{
|
|
s=t-x;
|
|
j=i;
|
|
}
|
|
}
|
|
//--- check
|
|
if(s==0.0)
|
|
return(f[j]);
|
|
//--- check
|
|
if(MathAbs(s)>threshold)
|
|
{
|
|
//--- use fast formula
|
|
j=-1;
|
|
s=1.0;
|
|
}
|
|
//--- Calculate using safe or fast barycentric formula
|
|
s1=0;
|
|
s2=0;
|
|
ca=MathCos(a0);
|
|
sa=MathSin(a0);
|
|
p1=1.0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- Calculate X[i],W[i]
|
|
x=ca;
|
|
w=p1*sa;
|
|
//--- Proceed
|
|
if(i!=j)
|
|
{
|
|
v=s*w/(t-x);
|
|
s1=s1+v*f[i];
|
|
s2=s2+v;
|
|
}
|
|
else
|
|
{
|
|
v=w;
|
|
s1=s1+v*f[i];
|
|
s2=s2+v;
|
|
}
|
|
//--- Next CA,SA,P1
|
|
temps=sa-(alpha*sa-beta*ca);
|
|
tempc=ca-(alpha*ca+beta*sa);
|
|
sa=temps;
|
|
ca=tempc;
|
|
p1=-p1;
|
|
}
|
|
//--- return result
|
|
return(s1/s2);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Fast polynomial interpolation function on Chebyshev points |
|
|
//| (second kind) with O(N) complexity. |
|
|
//| INPUT PARAMETERS: |
|
|
//| A - left boundary of [A,B] |
|
|
//| B - right boundary of [A,B] |
|
|
//| F - function values, array[0..N-1] |
|
|
//| N - number of points on Chebyshev grid (second kind), |
|
|
//| X[i] = 0.5*(B+A) + 0.5*(B-A)*Cos(PI*i/(n-1)) |
|
|
//| for N=1 a constant model is constructed. |
|
|
//| T - position where P(x) is calculated |
|
|
//| RESULT |
|
|
//| value of the Lagrange interpolant at T |
|
|
//| IMPORTANT |
|
|
//| this function provides fast interface which is not |
|
|
//| overflow-safe nor it is very precise. |
|
|
//| the best option is to use PolIntBuildCheb2() or |
|
|
//| BarycentricCalc() subroutines unless you are pretty sure that|
|
|
//| your data will not result in overflow. |
|
|
//+------------------------------------------------------------------+
|
|
static double CPolInt::PolynomialCalcCheb2(const double a,const double b,
|
|
double &f[],const int n,double t)
|
|
{
|
|
//--- create variables
|
|
double s1=0;
|
|
double s2=0;
|
|
double v=0;
|
|
double threshold=0;
|
|
double s=0;
|
|
int i=0;
|
|
int j=0;
|
|
double a0=0;
|
|
double delta=0;
|
|
double alpha=0;
|
|
double beta=0;
|
|
double ca=0;
|
|
double sa=0;
|
|
double tempc=0;
|
|
double temps=0;
|
|
double x=0;
|
|
double w=0;
|
|
double p1=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(f)>=n,__FUNCTION__+": Length(F)<N!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(a),__FUNCTION__+": A is infinite or NaN!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(b),__FUNCTION__+": B is infinite or NaN!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(b!=a,__FUNCTION__+": B=A!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(f,n),__FUNCTION__+": F contains infinite or NaN values!"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(!CInfOrNaN::IsInfinity(t),__FUNCTION__+": T is infinite!"))
|
|
return(EMPTY_VALUE);
|
|
//--- Special case: T is NAN
|
|
if(CInfOrNaN::IsNaN(t))
|
|
return(CInfOrNaN::NaN());
|
|
//--- Special case: N=1
|
|
if(n==1)
|
|
return(f[0]);
|
|
//--- Prepare information for the recurrence formula
|
|
//--- used to calculate sin(pi*i/n) and
|
|
//--- cos(pi*i/n):
|
|
//--- A0=0
|
|
//--- Delta=pi/n
|
|
//--- Alpha=2 sin^2 (Delta/2)
|
|
//--- Beta=sin(Delta)
|
|
//--- so that sin(..)=sin(A0+j*delta) and cos(..)=cos(A0+j*delta).
|
|
//--- Then we use
|
|
//--- sin(x+delta)=sin(x) - (alpha*sin(x) - beta*cos(x))
|
|
//--- cos(x+delta)=cos(x) - (alpha*cos(x) - beta*sin(x))
|
|
//--- to repeatedly calculate sin(..) and cos(..).
|
|
threshold=MathSqrt(CMath::m_minrealnumber);
|
|
t=(t-0.5*(a+b))/(0.5*(b-a));
|
|
a0=0.0;
|
|
delta=M_PI/(n-1);
|
|
alpha=2*CMath::Sqr(MathSin(delta/2));
|
|
beta=MathSin(delta);
|
|
//--- First,decide: should we use "safe" formula (guarded
|
|
//--- against overflow) or fast one?
|
|
ca=MathCos(a0);
|
|
sa=MathSin(a0);
|
|
j=0;
|
|
x=ca;
|
|
s=t-x;
|
|
//--- calculation
|
|
for(i=1;i<=n-1;i++)
|
|
{
|
|
//--- Next X[i]
|
|
temps=sa-(alpha*sa-beta*ca);
|
|
tempc=ca-(alpha*ca+beta*sa);
|
|
sa=temps;
|
|
ca=tempc;
|
|
x=ca;
|
|
//--- Use X[i]
|
|
if(MathAbs(t-x)<MathAbs(s))
|
|
{
|
|
s=t-x;
|
|
j=i;
|
|
}
|
|
}
|
|
//--- check
|
|
if(s==0.0)
|
|
return(f[j]);
|
|
//--- check
|
|
if(MathAbs(s)>threshold)
|
|
{
|
|
//--- use fast formula
|
|
j=-1;
|
|
s=1.0;
|
|
}
|
|
//--- Calculate using safe or fast barycentric formula
|
|
s1=0;
|
|
s2=0;
|
|
ca=MathCos(a0);
|
|
sa=MathSin(a0);
|
|
p1=1.0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- Calculate X[i],W[i]
|
|
x=ca;
|
|
//--- check
|
|
if(i==0 || i==n-1)
|
|
w=0.5*p1;
|
|
else
|
|
w=1.0*p1;
|
|
//--- Proceed
|
|
if(i!=j)
|
|
{
|
|
v=s*w/(t-x);
|
|
s1=s1+v*f[i];
|
|
s2=s2+v;
|
|
}
|
|
else
|
|
{
|
|
v=w;
|
|
s1=s1+v*f[i];
|
|
s2=s2+v;
|
|
}
|
|
//--- Next CA,SA,P1
|
|
temps=sa-(alpha*sa-beta*ca);
|
|
tempc=ca-(alpha*ca+beta*sa);
|
|
sa=temps;
|
|
ca=tempc;
|
|
p1=-p1;
|
|
}
|
|
//--- return result
|
|
return(s1/s2);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| 1-dimensional spline inteprolant |
|
|
//+------------------------------------------------------------------+
|
|
class CSpline1DInterpolant
|
|
{
|
|
public:
|
|
//--- variables
|
|
bool m_periodic;
|
|
int m_n;
|
|
int m_k;
|
|
//--- arrays
|
|
double m_x[];
|
|
double m_c[];
|
|
//--- constructor, destructor
|
|
CSpline1DInterpolant(void);
|
|
~CSpline1DInterpolant(void);
|
|
//--- copy
|
|
void Copy(CSpline1DInterpolant &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CSpline1DInterpolant::CSpline1DInterpolant(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CSpline1DInterpolant::~CSpline1DInterpolant(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CSpline1DInterpolant::Copy(CSpline1DInterpolant &obj)
|
|
{
|
|
//--- copy variables
|
|
m_periodic=obj.m_periodic;
|
|
m_n=obj.m_n;
|
|
m_k=obj.m_k;
|
|
//--- copy arrays
|
|
ArrayCopy(m_x,obj.m_x);
|
|
ArrayCopy(m_c,obj.m_c);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| 1-dimensional spline inteprolant |
|
|
//+------------------------------------------------------------------+
|
|
class CSpline1DInterpolantShell
|
|
{
|
|
private:
|
|
CSpline1DInterpolant m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CSpline1DInterpolantShell(void);
|
|
CSpline1DInterpolantShell(CSpline1DInterpolant &obj);
|
|
~CSpline1DInterpolantShell(void);
|
|
//--- method
|
|
CSpline1DInterpolant *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CSpline1DInterpolantShell::CSpline1DInterpolantShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CSpline1DInterpolantShell::CSpline1DInterpolantShell(CSpline1DInterpolant &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CSpline1DInterpolantShell::~CSpline1DInterpolantShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CSpline1DInterpolant *CSpline1DInterpolantShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| 1-dimensional spline interpolation |
|
|
//+------------------------------------------------------------------+
|
|
class CSpline1D
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void Spline1DGridDiffCubicInternal(double &x[],double &y[],const int n,const int boundltype,const double boundl,const int boundrtype,const double boundr,double &d[],double &a1[],double &a2[],double &a3[],double &b[],double &dt[]);
|
|
static void HeapSortPoints(double &x[],double &y[],const int n);
|
|
static void HeapSortPPoints(double &x[],double &y[],int &p[],const int n);
|
|
static void SolveTridiagonal(double &a[],double &cb[],double &c[],double &cd[],const int n,double &x[]);
|
|
static void SolveCyclicTridiagonal(double &a[],double &cb[],double &c[],double &d[],const int n,double &x[]);
|
|
static double DiffThreePoint(double t,const double x0,const double f0,double x1,const double f1,double x2,const double f2);
|
|
public:
|
|
//--- constructor, destructor
|
|
CSpline1D(void);
|
|
~CSpline1D(void);
|
|
//--- public methods
|
|
static void Spline1DBuildLinear(double &cx[],double &cy[],const int n,CSpline1DInterpolant &c);
|
|
static void Spline1DBuildCubic(double &cx[],double &cy[],const int n,const int boundltype,const double boundl,const int boundrtype,const double boundr,CSpline1DInterpolant &c);
|
|
static void Spline1DGridDiffCubic(double &cx[],double &cy[],const int n,const int boundltype,const double boundl,const int boundrtype,const double boundr,double &d[]);
|
|
static void Spline1DGridDiff2Cubic(double &cx[],double &cy[],const int n,const int boundltype,const double boundl,const int boundrtype,const double boundr,double &d1[],double &d2[]);
|
|
static void Spline1DConvCubic(double &cx[],double &cy[],const int n,const int boundltype,const double boundl,const int boundrtype,const double boundr,double &cx2[],const int n2,double &y2[]);
|
|
static void Spline1DConvDiffCubic(double &cx[],double &cy[],const int n,const int boundltype,const double boundl,const int boundrtype,const double boundr,double &cx2[],const int n2,double &y2[],double &d2[]);
|
|
static void Spline1DConvDiff2Cubic(double &cx[],double &cy[],const int n,const int boundltype,const double boundl,const int boundrtype,const double boundr,double &cx2[],const int n2,double &y2[],double &d2[],double &dd2[]);
|
|
static void Spline1DBuildCatmullRom(double &cx[],double &cy[],const int n,const int boundtype,const double tension,CSpline1DInterpolant &c);
|
|
static void Spline1DBuildHermite(double &cx[],double &cy[],double &cd[],const int n,CSpline1DInterpolant &c);
|
|
static void Spline1DBuildAkima(double &cx[],double &cy[],const int n,CSpline1DInterpolant &c);
|
|
static double Spline1DCalc(CSpline1DInterpolant &c,double x);
|
|
static void Spline1DDiff(CSpline1DInterpolant &c,double x,double &s,double &ds,double &d2s);
|
|
static void Spline1DCopy(CSpline1DInterpolant &c,CSpline1DInterpolant &cc);
|
|
static void Spline1DUnpack(CSpline1DInterpolant &c,int &n,CMatrixDouble &tbl);
|
|
static void Spline1DLinTransX(CSpline1DInterpolant &c,const double a,const double b);
|
|
static void Spline1DLinTransY(CSpline1DInterpolant &c,const double a,const double b);
|
|
static double Spline1DIntegrate(CSpline1DInterpolant &c,double x);
|
|
static void Spline1DConvDiffInternal(double &xold[],double &yold[],double &dold[],const int n,double &x2[],const int n2,double &y[],const bool needy,double &d1[],const bool needd1,double &d2[],const bool needd2);
|
|
static void HeapSortDPoints(double &x[],double &y[],double &d[],const int n);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CSpline1D::CSpline1D(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CSpline1D::~CSpline1D(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine builds linear spline interpolant |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - spline nodes, array[0..N-1] |
|
|
//| Y - function values, array[0..N-1] |
|
|
//| N - points count (optional): |
|
|
//| * N>=2 |
|
|
//| * if given, only first N points are used to build |
|
|
//| spline |
|
|
//| * if not given, automatically detected from X/Y |
|
|
//| sizes (len(X) must be equal to len(Y)) |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| C - spline interpolant |
|
|
//| ORDER OF POINTS |
|
|
//| Subroutine automatically sorts points, so caller may pass |
|
|
//| unsorted array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DBuildLinear(double &cx[],double &cy[],
|
|
const int n,CSpline1DInterpolant &c)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
//--- check
|
|
if(!CAp::Assert(n>1,__FUNCTION__+": N<2!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check and sort points
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- function call
|
|
HeapSortPoints(x,y,n);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
|
|
return;
|
|
//--- Build
|
|
c.m_periodic=false;
|
|
c.m_n=n;
|
|
c.m_k=3;
|
|
//--- allocation
|
|
ArrayResizeAL(c.m_x,n);
|
|
ArrayResizeAL(c.m_c,4*(n-1));
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
c.m_x[i]=x[i];
|
|
//--- calculation
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
c.m_c[4*i+0]=y[i];
|
|
c.m_c[4*i+1]=(y[i+1]-y[i])/(x[i+1]-x[i]);
|
|
c.m_c[4*i+2]=0;
|
|
c.m_c[4*i+3]=0;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine builds cubic spline interpolant. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - spline nodes, array[0..N-1]. |
|
|
//| Y - function values, array[0..N-1]. |
|
|
//| OPTIONAL PARAMETERS: |
|
|
//| N - points count: |
|
|
//| * N>=2 |
|
|
//| * if given, only first N points are used to |
|
|
//| build spline |
|
|
//| * if not given, automatically detected from |
|
|
//| X/Y sizes (len(X) must be equal to len(Y)) |
|
|
//| BoundLType - boundary condition type for the left boundary|
|
|
//| BoundL - left boundary condition (first or second |
|
|
//| derivative, depending on the BoundLType) |
|
|
//| BoundRType - boundary condition type for the right |
|
|
//| boundary |
|
|
//| BoundR - right boundary condition (first or second |
|
|
//| derivative, depending on the BoundRType) |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| C - spline interpolant |
|
|
//| ORDER OF POINTS |
|
|
//| Subroutine automatically sorts points, so caller may pass |
|
|
//| unsorted array. |
|
|
//| SETTING BOUNDARY VALUES: |
|
|
//| The BoundLType/BoundRType parameters can have the following |
|
|
//| values: |
|
|
//| * -1, which corresonds to the periodic (cyclic) boundary |
|
|
//| conditions. In this case: |
|
|
//| * both BoundLType and BoundRType must be equal to -1. |
|
|
//| * BoundL/BoundR are ignored |
|
|
//| * Y[last] is ignored (it is assumed to be equal to |
|
|
//| Y[first]). |
|
|
//| * 0, which corresponds to the parabolically terminated |
|
|
//| spline (BoundL and/or BoundR are ignored). |
|
|
//| * 1, which corresponds to the first derivative boundary |
|
|
//| condition |
|
|
//| * 2, which corresponds to the second derivative boundary |
|
|
//| condition |
|
|
//| * by default, BoundType=0 is used |
|
|
//| PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS: |
|
|
//| Problems with periodic boundary conditions have |
|
|
//| Y[first_point]=Y[last_point]. However, this subroutine doesn't |
|
|
//| require you to specify equal values for the first and last |
|
|
//| points - it automatically forces them to be equal by copying |
|
|
//| Y[first_point] (corresponds to the leftmost, minimal X[]) to |
|
|
//| Y[last_point]. However it is recommended to pass consistent |
|
|
//| values of Y[], i.e. to make Y[first_point]=Y[last_point]. |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DBuildCubic(double &cx[],double &cy[],
|
|
const int n,const int boundltype,
|
|
const double boundl,const int boundrtype,
|
|
const double boundr,CSpline1DInterpolant &c)
|
|
{
|
|
//--- create a variable
|
|
int ylen=0;
|
|
//--- create arrays
|
|
double a1[];
|
|
double a2[];
|
|
double a3[];
|
|
double b[];
|
|
double dt[];
|
|
double d[];
|
|
int p[];
|
|
double x[];
|
|
double y[];
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
//--- check correctness of boundary conditions
|
|
if(!CAp::Assert(((boundltype==-1 || boundltype==0) || boundltype==1) || boundltype==2,__FUNCTION__+": incorrect BoundLType!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(((boundrtype==-1 || boundrtype==0) || boundrtype==1) || boundrtype==2,__FUNCTION__+": incorrect BoundRType!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((boundrtype==-1 && boundltype==-1) || (boundrtype!=-1 && boundltype!=-1),__FUNCTION__+": incorrect BoundLType/BoundRType!"))
|
|
return;
|
|
//--- check
|
|
if(boundltype==1 || boundltype==2)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(boundl),__FUNCTION__+": BoundL is infinite or NAN!"))
|
|
return;
|
|
}
|
|
//--- check
|
|
if(boundrtype==1 || boundrtype==2)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(boundr),__FUNCTION__+": BoundR is infinite or NAN!"))
|
|
return;
|
|
}
|
|
//--- check lengths of arguments
|
|
if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check and sort points
|
|
ylen=n;
|
|
//--- check
|
|
if(boundltype==-1)
|
|
ylen=n-1;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,ylen),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- function call
|
|
HeapSortPPoints(x,y,p,n);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
|
|
return;
|
|
//--- Now we've checked and preordered everything,
|
|
//--- so we can call internal function to calculate derivatives,
|
|
//--- and then build Hermite spline using these derivatives
|
|
Spline1DGridDiffCubicInternal(x,y,n,boundltype,boundl,boundrtype,boundr,d,a1,a2,a3,b,dt);
|
|
Spline1DBuildHermite(x,y,d,n,c);
|
|
//--- check
|
|
if(boundltype==-1 || boundrtype==-1)
|
|
c.m_periodic=1;
|
|
else
|
|
c.m_periodic=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function solves following problem: given table y[] of |
|
|
//| function values at nodes x[], it calculates and returns table of |
|
|
//| function derivatives d[] (calculated at the same nodes x[]). |
|
|
//| This function yields same result as Spline1DBuildCubic() call |
|
|
//| followed by sequence of Spline1DDiff() calls, but it can be |
|
|
//| several times faster when called for ordered X[] and X2[]. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - spline nodes |
|
|
//| Y - function values |
|
|
//| OPTIONAL PARAMETERS: |
|
|
//| N - points count: |
|
|
//| * N>=2 |
|
|
//| * if given, only first N points are used |
|
|
//| * if not given, automatically detected from |
|
|
//| X/Y sizes (len(X) must be equal to len(Y)) |
|
|
//| BoundLType - boundary condition type for the left boundary|
|
|
//| BoundL - left boundary condition (first or second |
|
|
//| derivative, depending on the BoundLType) |
|
|
//| BoundRType - boundary condition type for the right |
|
|
//| boundary |
|
|
//| BoundR - right boundary condition (first or second |
|
|
//| derivative, depending on the BoundRType) |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| D - derivative values at X[] |
|
|
//| ORDER OF POINTS |
|
|
//| Subroutine automatically sorts points, so caller may pass |
|
|
//| unsorted array. Derivative values are correctly reordered on |
|
|
//| return, so D[I] is always equal to S'(X[I]) independently of |
|
|
//| points order. |
|
|
//| SETTING BOUNDARY VALUES: |
|
|
//| The BoundLType/BoundRType parameters can have the following |
|
|
//| values: |
|
|
//| * -1, which corresonds to the periodic (cyclic) boundary |
|
|
//| conditions. In this case: |
|
|
//| * both BoundLType and BoundRType must be equal to -1. |
|
|
//| * BoundL/BoundR are ignored |
|
|
//| * Y[last] is ignored (it is assumed to be equal to |
|
|
//| Y[first]). |
|
|
//| * 0, which corresponds to the parabolically terminated |
|
|
//| spline (BoundL and/or BoundR are ignored). |
|
|
//| * 1, which corresponds to the first derivative boundary |
|
|
//| condition |
|
|
//| * 2, which corresponds to the second derivative boundary |
|
|
//| condition |
|
|
//| * by default, BoundType=0 is used |
|
|
//| PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS: |
|
|
//| Problems with periodic boundary conditions have |
|
|
//| Y[first_point]=Y[last_point]. However, this subroutine doesn't |
|
|
//| require you to specify equal values for the first and last |
|
|
//| points - it automatically forces them to be equal by copying |
|
|
//| Y[first_point] (corresponds to the leftmost, minimal X[]) to |
|
|
//| Y[last_point]. However it is recommended to pass consistent |
|
|
//| values of Y[], i.e. to make Y[first_point]=Y[last_point]. |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DGridDiffCubic(double &cx[],double &cy[],
|
|
const int n,const int boundltype,
|
|
const double boundl,const int boundrtype,
|
|
const double boundr,double &d[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int ylen=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double a1[];
|
|
double a2[];
|
|
double a3[];
|
|
double b[];
|
|
double dt[];
|
|
int p[];
|
|
double x[];
|
|
double y[];
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
//--- check correctness of boundary conditions
|
|
if(!CAp::Assert(((boundltype==-1 || boundltype==0) || boundltype==1) || boundltype==2,__FUNCTION__+": incorrect BoundLType!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(((boundrtype==-1 || boundrtype==0) || boundrtype==1) || boundrtype==2,__FUNCTION__+": incorrect BoundRType!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((boundrtype==-1 && boundltype==-1) || (boundrtype!=-1 && boundltype!=-1),__FUNCTION__+": incorrect BoundLType/BoundRType!"))
|
|
return;
|
|
//--- check
|
|
if(boundltype==1 || boundltype==2)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(boundl),__FUNCTION__+": BoundL is infinite or NAN!"))
|
|
return;
|
|
}
|
|
//--- check
|
|
if(boundrtype==1 || boundrtype==2)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(boundr),__FUNCTION__+": BoundR is infinite or NAN!"))
|
|
return;
|
|
}
|
|
//--- check lengths of arguments
|
|
if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check and sort points
|
|
ylen=n;
|
|
//--- check
|
|
if(boundltype==-1)
|
|
ylen=n-1;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,ylen),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- function call
|
|
HeapSortPPoints(x,y,p,n);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
|
|
return;
|
|
//--- Now we've checked and preordered everything,
|
|
//--- so we can call internal function.
|
|
Spline1DGridDiffCubicInternal(x,y,n,boundltype,boundl,boundrtype,boundr,d,a1,a2,a3,b,dt);
|
|
//--- Remember that HeapSortPPoints() call?
|
|
//--- Now we have to reorder them back.
|
|
if(CAp::Len(dt)<n)
|
|
ArrayResizeAL(dt,n);
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
dt[p[i]]=d[i];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
d[i_]=dt[i_];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function solves following problem: given table y[] of |
|
|
//| function values at nodes x[], it calculates and returns tables of|
|
|
//| first and second function derivatives d1[] and d2[] (calculated |
|
|
//| at the same nodes x[]). |
|
|
//| This function yields same result as Spline1DBuildCubic() call |
|
|
//| followed by sequence of Spline1DDiff() calls, but it can be |
|
|
//| several times faster when called for ordered X[] and X2[]. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - spline nodes |
|
|
//| Y - function values |
|
|
//| OPTIONAL PARAMETERS: |
|
|
//| N - points count: |
|
|
//| * N>=2 |
|
|
//| * if given, only first N points are used |
|
|
//| * if not given, automatically detected from |
|
|
//| X/Y sizes (len(X) must be equal to len(Y)) |
|
|
//| BoundLType - boundary condition type for the left boundary|
|
|
//| BoundL - left boundary condition (first or second |
|
|
//| derivative, depending on the BoundLType) |
|
|
//| BoundRType - boundary condition type for the right |
|
|
//| boundary |
|
|
//| BoundR - right boundary condition (first or second |
|
|
//| derivative, depending on the BoundRType) |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| D1 - S' values at X[] |
|
|
//| D2 - S'' values at X[] |
|
|
//| ORDER OF POINTS |
|
|
//| Subroutine automatically sorts points, so caller may pass |
|
|
//| unsorted array. Derivative values are correctly reordered on |
|
|
//| return, so D[I] is always equal to S'(X[I]) independently of |
|
|
//| points order. |
|
|
//| SETTING BOUNDARY VALUES: |
|
|
//| The BoundLType/BoundRType parameters can have the following |
|
|
//| values: |
|
|
//| * -1, which corresonds to the periodic (cyclic) boundary |
|
|
//| conditions. In this case: |
|
|
//| * both BoundLType and BoundRType must be equal to -1. |
|
|
//| * BoundL/BoundR are ignored |
|
|
//| * Y[last] is ignored (it is assumed to be equal to |
|
|
//| Y[first]). |
|
|
//| * 0, which corresponds to the parabolically terminated |
|
|
//| spline (BoundL and/or BoundR are ignored). |
|
|
//| * 1, which corresponds to the first derivative boundary |
|
|
//| condition |
|
|
//| * 2, which corresponds to the second derivative boundary |
|
|
//| condition |
|
|
//| * by default, BoundType=0 is used |
|
|
//| PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS: |
|
|
//| Problems with periodic boundary conditions have |
|
|
//| Y[first_point]=Y[last_point]. |
|
|
//| However, this subroutine doesn't require you to specify equal |
|
|
//| values for the first and last points - it automatically forces |
|
|
//| them to be equal by copying Y[first_point] (corresponds to the |
|
|
//| leftmost, minimal X[]) to Y[last_point]. However it is |
|
|
//| recommended to pass consistent values of Y[], i.e. to make |
|
|
//| Y[first_point]=Y[last_point]. |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DGridDiff2Cubic(double &cx[],double &cy[],
|
|
const int n,const int boundltype,
|
|
const double boundl,const int boundrtype,
|
|
const double boundr,double &d1[],double &d2[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int ylen=0;
|
|
double delta=0;
|
|
double delta2=0;
|
|
double delta3=0;
|
|
double s0=0;
|
|
double s1=0;
|
|
double s2=0;
|
|
double s3=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double a1[];
|
|
double a2[];
|
|
double a3[];
|
|
double b[];
|
|
double dt[];
|
|
int p[];
|
|
double x[];
|
|
double y[];
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
//--- check correctness of boundary conditions
|
|
if(!CAp::Assert(((boundltype==-1 || boundltype==0) || boundltype==1) || boundltype==2,__FUNCTION__+": incorrect BoundLType!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(((boundrtype==-1 || boundrtype==0) || boundrtype==1) || boundrtype==2,__FUNCTION__+": incorrect BoundRType!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((boundrtype==-1 && boundltype==-1) || (boundrtype!=-1 && boundltype!=-1),__FUNCTION__+": incorrect BoundLType/BoundRType!"))
|
|
return;
|
|
//--- check
|
|
if(boundltype==1 || boundltype==2)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(boundl),__FUNCTION__+": BoundL is infinite or NAN!"))
|
|
return;
|
|
}
|
|
//--- check
|
|
if(boundrtype==1 || boundrtype==2)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(boundr),__FUNCTION__+": BoundR is infinite or NAN!"))
|
|
return;
|
|
}
|
|
//--- check lengths of arguments
|
|
if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check and sort points
|
|
ylen=n;
|
|
//--- check
|
|
if(boundltype==-1)
|
|
ylen=n-1;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,ylen),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- function call
|
|
HeapSortPPoints(x,y,p,n);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
|
|
return;
|
|
//--- Now we've checked and preordered everything,
|
|
//--- so we can call internal function.
|
|
//--- After this call we will calculate second derivatives
|
|
//--- (manually,by converting to the power basis)
|
|
Spline1DGridDiffCubicInternal(x,y,n,boundltype,boundl,boundrtype,boundr,d1,a1,a2,a3,b,dt);
|
|
//--- allocation
|
|
ArrayResizeAL(d2,n);
|
|
delta=0;
|
|
s2=0;
|
|
s3=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
//--- We convert from Hermite basis to the power basis.
|
|
//--- Si is coefficient before x^i.
|
|
//--- Inside this cycle we need just S2,
|
|
//--- because we calculate S'' exactly at spline node,
|
|
//--- (only x^2 matters at x=0),but after iterations
|
|
//--- will be over,we will need other coefficients
|
|
//--- to calculate spline value at the last node.
|
|
delta=x[i+1]-x[i];
|
|
delta2=CMath::Sqr(delta);
|
|
delta3=delta*delta2;
|
|
s0=y[i];
|
|
s1=d1[i];
|
|
s2=(3*(y[i+1]-y[i])-2*d1[i]*delta-d1[i+1]*delta)/delta2;
|
|
s3=(2*(y[i]-y[i+1])+d1[i]*delta+d1[i+1]*delta)/delta3;
|
|
d2[i]=2*s2;
|
|
}
|
|
d2[n-1]=2*s2+6*s3*delta;
|
|
//--- Remember that HeapSortPPoints() call?
|
|
//--- Now we have to reorder them back.
|
|
if(CAp::Len(dt)<n)
|
|
ArrayResizeAL(dt,n);
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
dt[p[i]]=d1[i];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
d1[i_]=dt[i_];
|
|
for(i=0;i<=n-1;i++)
|
|
dt[p[i]]=d2[i];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
d2[i_]=dt[i_];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function solves following problem: given table y[] of |
|
|
//| function values at old nodes x[] and new nodes x2[], it |
|
|
//| calculates and returns table of function values y2[] (calculated |
|
|
//| at x2[]). |
|
|
//| This function yields same result as Spline1DBuildCubic() call |
|
|
//| followed by sequence of Spline1DDiff() calls, but it can be |
|
|
//| several times faster when called for ordered X[] and X2[]. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - old spline nodes |
|
|
//| Y - function values |
|
|
//| X2 - new spline nodes |
|
|
//| OPTIONAL PARAMETERS: |
|
|
//| N - points count: |
|
|
//| * N>=2 |
|
|
//| * if given, only first N points from X/Y are |
|
|
//| used |
|
|
//| * if not given, automatically detected from |
|
|
//| X/Y sizes (len(X) must be equal to len(Y)) |
|
|
//| BoundLType - boundary condition type for the left boundary|
|
|
//| BoundL - left boundary condition (first or second |
|
|
//| derivative, depending on the BoundLType) |
|
|
//| BoundRType - boundary condition type for the right |
|
|
//| boundary |
|
|
//| BoundR - right boundary condition (first or second |
|
|
//| derivative, depending on the BoundRType) |
|
|
//| N2 - new points count: |
|
|
//| * N2>=2 |
|
|
//| * if given, only first N2 points from X2 are |
|
|
//| used |
|
|
//| * if not given, automatically detected from |
|
|
//| X2 size |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| F2 - function values at X2[] |
|
|
//| ORDER OF POINTS |
|
|
//| Subroutine automatically sorts points, so caller may pass |
|
|
//| unsorted array. Function values are correctly reordered on |
|
|
//| return, so F2[I] is always equal to S(X2[I]) independently of |
|
|
//| points order. |
|
|
//| SETTING BOUNDARY VALUES: |
|
|
//| The BoundLType/BoundRType parameters can have the following |
|
|
//| values: |
|
|
//| * -1, which corresonds to the periodic (cyclic) boundary |
|
|
//| conditions. In this case: |
|
|
//| * both BoundLType and BoundRType must be equal to -1. |
|
|
//| * BoundL/BoundR are ignored |
|
|
//| * Y[last] is ignored (it is assumed to be equal to |
|
|
//| Y[first]). |
|
|
//| * 0, which corresponds to the parabolically terminated |
|
|
//| spline (BoundL and/or BoundR are ignored). |
|
|
//| * 1, which corresponds to the first derivative boundary |
|
|
//| condition |
|
|
//| * 2, which corresponds to the second derivative boundary |
|
|
//| condition |
|
|
//| * by default, BoundType=0 is used |
|
|
//| PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS: |
|
|
//| Problems with periodic boundary conditions have |
|
|
//| Y[first_point]=Y[last_point]. However, this subroutine doesn't |
|
|
//| require you to specify equal values for the first and last |
|
|
//| points - it automatically forces them to be equal by copying |
|
|
//| Y[first_point] (corresponds to the leftmost, minimal X[]) to |
|
|
//| Y[last_point]. However it is recommended to pass consistent |
|
|
//| values of Y[], i.e. to make Y[first_point]=Y[last_point]. |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DConvCubic(double &cx[],double &cy[],
|
|
const int n,const int boundltype,
|
|
const double boundl,const int boundrtype,
|
|
const double boundr,double &cx2[],
|
|
const int n2,double &y2[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int ylen=0;
|
|
double t=0;
|
|
double t2=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double a1[];
|
|
double a2[];
|
|
double a3[];
|
|
double b[];
|
|
double d[];
|
|
double dt[];
|
|
double d1[];
|
|
double d2[];
|
|
int p[];
|
|
int p2[];
|
|
double x[];
|
|
double y[];
|
|
double x2[];
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
ArrayCopy(x2,cx2);
|
|
//--- check correctness of boundary conditions
|
|
if(!CAp::Assert(((boundltype==-1 || boundltype==0) || boundltype==1) || boundltype==2,__FUNCTION__+": incorrect BoundLType!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(((boundrtype==-1 || boundrtype==0) || boundrtype==1) || boundrtype==2,__FUNCTION__+": incorrect BoundRType!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((boundrtype==-1 && boundltype==-1) || (boundrtype!=-1 && boundltype!=-1),__FUNCTION__+": incorrect BoundLType/BoundRType!"))
|
|
return;
|
|
//--- check
|
|
if(boundltype==1 || boundltype==2)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(boundl),__FUNCTION__+": BoundL is infinite or NAN!"))
|
|
return;
|
|
}
|
|
//--- check
|
|
if(boundrtype==1 || boundrtype==2)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(boundr),__FUNCTION__+": BoundR is infinite or NAN!"))
|
|
return;
|
|
}
|
|
//--- check lengths of arguments
|
|
if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(n2>=2,__FUNCTION__+": N2<2!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x2)>=n2,__FUNCTION__+": Length(X2)<N2!"))
|
|
return;
|
|
//--- check and sort X/Y
|
|
ylen=n;
|
|
//--- check
|
|
if(boundltype==-1)
|
|
ylen=n-1;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,ylen),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x2,n2),__FUNCTION__+": X2 contains infinite or NAN values!"))
|
|
return;
|
|
//--- function call
|
|
HeapSortPPoints(x,y,p,n);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
|
|
return;
|
|
//--- set up DT (we will need it below)
|
|
ArrayResizeAL(dt,MathMax(n,n2));
|
|
//--- sort X2:
|
|
//--- * use fake array DT because HeapSortPPoints() needs both integer AND real arrays
|
|
//--- * if we have periodic problem,wrap points
|
|
//--- * sort them,store permutation at P2
|
|
if(boundrtype==-1 && boundltype==-1)
|
|
{
|
|
for(i=0;i<=n2-1;i++)
|
|
{
|
|
t=x2[i];
|
|
CApServ::ApPeriodicMap(t,x[0],x[n-1],t2);
|
|
x2[i]=t;
|
|
}
|
|
}
|
|
//--- function call
|
|
HeapSortPPoints(x2,dt,p2,n2);
|
|
//--- Now we've checked and preordered everything,so we:
|
|
//--- * call internal GridDiff() function to get Hermite form of spline
|
|
//--- * convert using internal Conv() function
|
|
//--- * convert Y2 back to original order
|
|
Spline1DGridDiffCubicInternal(x,y,n,boundltype,boundl,boundrtype,boundr,d,a1,a2,a3,b,dt);
|
|
Spline1DConvDiffInternal(x,y,d,n,x2,n2,y2,true,d1,false,d2,false);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(dt)>=n2,__FUNCTION__+": internal error!"))
|
|
return;
|
|
//--- copy
|
|
for(i=0;i<=n2-1;i++)
|
|
dt[p2[i]]=y2[i];
|
|
for(i_=0;i_<=n2-1;i_++)
|
|
y2[i_]=dt[i_];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function solves following problem: given table y[] of |
|
|
//| function values at old nodes x[] and new nodes x2[], it |
|
|
//| calculates and returns table of function values y2[] and |
|
|
//| derivatives d2[] (calculated at x2[]). |
|
|
//| This function yields same result as Spline1DBuildCubic() call |
|
|
//| followed by sequence of Spline1DDiff() calls, but it can be |
|
|
//| several times faster when called for ordered X[] and X2[]. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - old spline nodes |
|
|
//| Y - function values |
|
|
//| X2 - new spline nodes |
|
|
//| OPTIONAL PARAMETERS: |
|
|
//| N - points count: |
|
|
//| * N>=2 |
|
|
//| * if given, only first N points from X/Y are |
|
|
//| used |
|
|
//| * if not given, automatically detected from |
|
|
//| X/Y sizes (len(X) must be equal to len(Y)) |
|
|
//| BoundLType - boundary condition type for the left boundary|
|
|
//| BoundL - left boundary condition (first or second |
|
|
//| derivative, depending on the BoundLType) |
|
|
//| BoundRType - boundary condition type for the right |
|
|
//| boundary |
|
|
//| BoundR - right boundary condition (first or second |
|
|
//| derivative, depending on the BoundRType) |
|
|
//| N2 - new points count: |
|
|
//| * N2>=2 |
|
|
//| * if given, only first N2 points from X2 are |
|
|
//| used |
|
|
//| * if not given, automatically detected from |
|
|
//| X2 size |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| F2 - function values at X2[] |
|
|
//| D2 - first derivatives at X2[] |
|
|
//| ORDER OF POINTS |
|
|
//| Subroutine automatically sorts points, so caller may pass |
|
|
//| unsorted array. Function values are correctly reordered on |
|
|
//| return, so F2[I] is always equal to S(X2[I]) independently of |
|
|
//| points order. |
|
|
//| SETTING BOUNDARY VALUES: |
|
|
//| The BoundLType/BoundRType parameters can have the following |
|
|
//| values: |
|
|
//| * -1, which corresonds to the periodic (cyclic) boundary |
|
|
//| conditions. In this case: |
|
|
//| * both BoundLType and BoundRType must be equal to -1. |
|
|
//| * BoundL/BoundR are ignored |
|
|
//| * Y[last] is ignored (it is assumed to be equal to |
|
|
//| Y[first]). |
|
|
//| * 0, which corresponds to the parabolically terminated |
|
|
//| spline (BoundL and/or BoundR are ignored). |
|
|
//| * 1, which corresponds to the first derivative boundary |
|
|
//| condition |
|
|
//| * 2, which corresponds to the second derivative boundary |
|
|
//| condition |
|
|
//| * by default, BoundType=0 is used |
|
|
//| PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS: |
|
|
//| Problems with periodic boundary conditions have |
|
|
//| Y[first_point]=Y[last_point]. However, this subroutine doesn't |
|
|
//| require you to specify equal values for the first and last |
|
|
//| points - it automatically forces them to be equal by copying |
|
|
//| Y[first_point] (corresponds to the leftmost, minimal X[]) to |
|
|
//| Y[last_point]. However it is recommended to pass consistent |
|
|
//| values of Y[], i.e. to make Y[first_point]=Y[last_point]. |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DConvDiffCubic(double &cx[],double &cy[],
|
|
const int n,const int boundltype,
|
|
const double boundl,const int boundrtype,
|
|
const double boundr,double &cx2[],
|
|
const int n2,double &y2[],double &d2[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int ylen=0;
|
|
double t=0;
|
|
double t2=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double a1[];
|
|
double a2[];
|
|
double a3[];
|
|
double b[];
|
|
double d[];
|
|
double dt[];
|
|
double rt1[];
|
|
int p[];
|
|
int p2[];
|
|
double x[];
|
|
double y[];
|
|
double x2[];
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
ArrayCopy(x2,cx2);
|
|
//--- check correctness of boundary conditions
|
|
if(!CAp::Assert(((boundltype==-1 || boundltype==0) || boundltype==1) || boundltype==2,__FUNCTION__+": incorrect BoundLType!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(((boundrtype==-1 || boundrtype==0) || boundrtype==1) || boundrtype==2,__FUNCTION__+": incorrect BoundRType!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((boundrtype==-1 && boundltype==-1) || (boundrtype!=-1 && boundltype!=-1),__FUNCTION__+": incorrect BoundLType/BoundRType!"))
|
|
return;
|
|
//--- check
|
|
if(boundltype==1 || boundltype==2)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(boundl),__FUNCTION__+": BoundL is infinite or NAN!"))
|
|
return;
|
|
}
|
|
//--- check
|
|
if(boundrtype==1 || boundrtype==2)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(boundr),__FUNCTION__+": BoundR is infinite or NAN!"))
|
|
return;
|
|
}
|
|
//--- check lengths of arguments
|
|
if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(n2>=2,__FUNCTION__+"Spline1DConvDiffCubic: N2<2!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x2)>=n2,__FUNCTION__+": Length(X2)<N2!"))
|
|
return;
|
|
//--- check and sort X/Y
|
|
ylen=n;
|
|
//--- check
|
|
if(boundltype==-1)
|
|
ylen=n-1;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,ylen),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x2,n2),__FUNCTION__+": X2 contains infinite or NAN values!"))
|
|
return;
|
|
//--- function call
|
|
HeapSortPPoints(x,y,p,n);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
|
|
return;
|
|
//--- set up DT (we will need it below)
|
|
ArrayResizeAL(dt,MathMax(n,n2));
|
|
//--- sort X2:
|
|
//--- * use fake array DT because HeapSortPPoints() needs both integer AND real arrays
|
|
//--- * if we have periodic problem,wrap points
|
|
//--- * sort them,store permutation at P2
|
|
if(boundrtype==-1 && boundltype==-1)
|
|
{
|
|
for(i=0;i<=n2-1;i++)
|
|
{
|
|
t=x2[i];
|
|
CApServ::ApPeriodicMap(t,x[0],x[n-1],t2);
|
|
x2[i]=t;
|
|
}
|
|
}
|
|
//--- function call
|
|
HeapSortPPoints(x2,dt,p2,n2);
|
|
//--- Now we've checked and preordered everything,so we:
|
|
//--- * call internal GridDiff() function to get Hermite form of spline
|
|
//--- * convert using internal Conv() function
|
|
//--- * convert Y2 back to original order
|
|
Spline1DGridDiffCubicInternal(x,y,n,boundltype,boundl,boundrtype,boundr,d,a1,a2,a3,b,dt);
|
|
Spline1DConvDiffInternal(x,y,d,n,x2,n2,y2,true,d2,true,rt1,false);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(dt)>=n2,__FUNCTION__+": internal error!"))
|
|
return;
|
|
//--- copy
|
|
for(i=0;i<=n2-1;i++)
|
|
dt[p2[i]]=y2[i];
|
|
for(i_=0;i_<=n2-1;i_++)
|
|
y2[i_]=dt[i_];
|
|
for(i=0;i<=n2-1;i++)
|
|
dt[p2[i]]=d2[i];
|
|
for(i_=0;i_<=n2-1;i_++)
|
|
d2[i_]=dt[i_];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function solves following problem: given table y[] of |
|
|
//| function values at old nodes x[] and new nodes x2[], it |
|
|
//| calculates and returns table of function values y2[], first and |
|
|
//| second derivatives d2[] and dd2[] (calculated at x2[]). |
|
|
//| This function yields same result as Spline1DBuildCubic() call |
|
|
//| followed by sequence of Spline1DDiff() calls, but it can be |
|
|
//| several times faster when called for ordered X[] and X2[]. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - old spline nodes |
|
|
//| Y - function values |
|
|
//| X2 - new spline nodes |
|
|
//| OPTIONAL PARAMETERS: |
|
|
//| N - points count: |
|
|
//| * N>=2 |
|
|
//| * if given, only first N points from X/Y are |
|
|
//| used |
|
|
//| * if not given, automatically detected from |
|
|
//| X/Y sizes (len(X) must be equal to len(Y)) |
|
|
//| BoundLType - boundary condition type for the left boundary|
|
|
//| BoundL - left boundary condition (first or second |
|
|
//| derivative, depending on the BoundLType) |
|
|
//| BoundRType - boundary condition type for the right |
|
|
//| boundary |
|
|
//| BoundR - right boundary condition (first or second |
|
|
//| derivative, depending on the BoundRType) |
|
|
//| N2 - new points count: |
|
|
//| * N2>=2 |
|
|
//| * if given, only first N2 points from X2 are |
|
|
//| used |
|
|
//| * if not given, automatically detected from |
|
|
//| X2 size |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| F2 - function values at X2[] |
|
|
//| D2 - first derivatives at X2[] |
|
|
//| DD2 - second derivatives at X2[] |
|
|
//| ORDER OF POINTS |
|
|
//| Subroutine automatically sorts points, so caller may pass |
|
|
//| unsorted array. Function values are correctly reordered on |
|
|
//| return, so F2[I] is always equal to S(X2[I]) independently of |
|
|
//| points order. |
|
|
//| SETTING BOUNDARY VALUES: |
|
|
//| The BoundLType/BoundRType parameters can have the following |
|
|
//| values: |
|
|
//| * -1, which corresonds to the periodic (cyclic) boundary |
|
|
//| conditions. In this case: |
|
|
//| * both BoundLType and BoundRType must be equal to -1. |
|
|
//| * BoundL/BoundR are ignored |
|
|
//| * Y[last] is ignored (it is assumed to be equal to |
|
|
//| Y[first]). |
|
|
//| * 0, which corresponds to the parabolically terminated |
|
|
//| spline (BoundL and/or BoundR are ignored). |
|
|
//| * 1, which corresponds to the first derivative boundary |
|
|
//| condition |
|
|
//| * 2, which corresponds to the second derivative boundary |
|
|
//| condition |
|
|
//| * by default, BoundType=0 is used |
|
|
//| PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS: |
|
|
//| Problems with periodic boundary conditions have |
|
|
//| Y[first_point]=Y[last_point]. However, this subroutine doesn't |
|
|
//| require you to specify equal values for the first and last |
|
|
//| points - it automatically forces them to be equal by copying |
|
|
//| Y[first_point] (corresponds to the leftmost, minimal X[]) to |
|
|
//| Y[last_point]. However it is recommended to pass consistent |
|
|
//| values of Y[], i.e. to make Y[first_point]=Y[last_point]. |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DConvDiff2Cubic(double &cx[],double &cy[],
|
|
const int n,const int boundltype,
|
|
const double boundl,
|
|
const int boundrtype,
|
|
const double boundr,double &cx2[],
|
|
const int n2,double &y2[],
|
|
double &d2[],double &dd2[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int ylen=0;
|
|
double t=0;
|
|
double t2=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double a1[];
|
|
double a2[];
|
|
double a3[];
|
|
double b[];
|
|
double d[];
|
|
double dt[];
|
|
int p[];
|
|
int p2[];
|
|
double x[];
|
|
double y[];
|
|
double x2[];
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
ArrayCopy(x2,cx2);
|
|
//--- check correctness of boundary conditions
|
|
if(!CAp::Assert(((boundltype==-1 || boundltype==0) || boundltype==1) || boundltype==2,__FUNCTION__+": incorrect BoundLType!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(((boundrtype==-1 || boundrtype==0) || boundrtype==1) || boundrtype==2,__FUNCTION__+": incorrect BoundRType!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((boundrtype==-1 && boundltype==-1) || (boundrtype!=-1 && boundltype!=-1),__FUNCTION__+": incorrect BoundLType/BoundRType!"))
|
|
return;
|
|
//--- check
|
|
if(boundltype==1 || boundltype==2)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(boundl),__FUNCTION__+": BoundL is infinite or NAN!"))
|
|
return;
|
|
}
|
|
//--- check
|
|
if(boundrtype==1 || boundrtype==2)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(boundr),__FUNCTION__+": BoundR is infinite or NAN!"))
|
|
return;
|
|
}
|
|
//--- check lengths of arguments
|
|
if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(n2>=2,__FUNCTION__+": N2<2!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x2)>=n2,__FUNCTION__+": Length(X2)<N2!"))
|
|
return;
|
|
//--- check and sort X/Y
|
|
ylen=n;
|
|
//--- check
|
|
if(boundltype==-1)
|
|
ylen=n-1;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,ylen),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x2,n2),__FUNCTION__+": X2 contains infinite or NAN values!"))
|
|
return;
|
|
//--- function call
|
|
HeapSortPPoints(x,y,p,n);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
|
|
return;
|
|
//--- set up DT (we will need it below)
|
|
ArrayResizeAL(dt,MathMax(n,n2));
|
|
//--- sort X2:
|
|
//--- * use fake array DT because HeapSortPPoints() needs both integer AND real arrays
|
|
//--- * if we have periodic problem,wrap points
|
|
//--- * sort them,store permutation at P2
|
|
if(boundrtype==-1 && boundltype==-1)
|
|
{
|
|
for(i=0;i<=n2-1;i++)
|
|
{
|
|
t=x2[i];
|
|
CApServ::ApPeriodicMap(t,x[0],x[n-1],t2);
|
|
x2[i]=t;
|
|
}
|
|
}
|
|
//--- function call
|
|
HeapSortPPoints(x2,dt,p2,n2);
|
|
//--- Now we've checked and preordered everything,so we:
|
|
//--- * call internal GridDiff() function to get Hermite form of spline
|
|
//--- * convert using internal Conv() function
|
|
//--- * convert Y2 back to original order
|
|
Spline1DGridDiffCubicInternal(x,y,n,boundltype,boundl,boundrtype,boundr,d,a1,a2,a3,b,dt);
|
|
Spline1DConvDiffInternal(x,y,d,n,x2,n2,y2,true,d2,true,dd2,true);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(dt)>=n2,__FUNCTION__+": internal error!"))
|
|
return;
|
|
//--- copy
|
|
for(i=0;i<=n2-1;i++)
|
|
dt[p2[i]]=y2[i];
|
|
for(i_=0;i_<=n2-1;i_++)
|
|
y2[i_]=dt[i_];
|
|
for(i=0;i<=n2-1;i++)
|
|
dt[p2[i]]=d2[i];
|
|
for(i_=0;i_<=n2-1;i_++)
|
|
d2[i_]=dt[i_];
|
|
for(i=0;i<=n2-1;i++)
|
|
dt[p2[i]]=dd2[i];
|
|
for(i_=0;i_<=n2-1;i_++)
|
|
dd2[i_]=dt[i_];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine builds Catmull-Rom spline interpolant. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - spline nodes, array[0..N-1]. |
|
|
//| Y - function values, array[0..N-1]. |
|
|
//| OPTIONAL PARAMETERS: |
|
|
//| N - points count: |
|
|
//| * N>=2 |
|
|
//| * if given, only first N points are used to |
|
|
//| build spline |
|
|
//| * if not given, automatically detected from |
|
|
//| X/Y sizes (len(X) must be equal to len(Y)) |
|
|
//| BoundType - boundary condition type: |
|
|
//| * -1 for periodic boundary condition |
|
|
//| * 0 for parabolically terminated spline |
|
|
//| (default) |
|
|
//| Tension - tension parameter: |
|
|
//| * tension=0 corresponds to classic |
|
|
//| Catmull-Rom spline (default) |
|
|
//| * 0<tension<1 corresponds to more general |
|
|
//| form - cardinal spline |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| C - spline interpolant |
|
|
//| ORDER OF POINTS |
|
|
//| Subroutine automatically sorts points, so caller may pass |
|
|
//| unsorted array. |
|
|
//| PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS: |
|
|
//| Problems with periodic boundary conditions have |
|
|
//| Y[first_point]=Y[last_point]. However, this subroutine doesn't |
|
|
//| require you to specify equal values for the first and last |
|
|
//| points - it automatically forces them to be equal by copying |
|
|
//| Y[first_point] (corresponds to the leftmost, minimal X[]) to |
|
|
//| Y[last_point]. However it is recommended to pass consistent |
|
|
//| values of Y[], i.e. to make Y[first_point]=Y[last_point]. |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DBuildCatmullRom(double &cx[],double &cy[],
|
|
const int n,const int boundtype,
|
|
const double tension,
|
|
CSpline1DInterpolant &c)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create arrays
|
|
double d[];
|
|
double x[];
|
|
double y[];
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
//--- check
|
|
if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(boundtype==-1 || boundtype==0,__FUNCTION__+": incorrect BoundType!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((double)(tension)>=0.0,__FUNCTION__+": Tension<0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((double)(tension)<=(double)(1),__FUNCTION__+": Tension>1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check and sort points
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- function call
|
|
HeapSortPoints(x,y,n);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
|
|
return;
|
|
//--- Special cases:
|
|
//--- * N=2,parabolic terminated boundary condition on both ends
|
|
//--- * N=2,periodic boundary condition
|
|
if(n==2 && boundtype==0)
|
|
{
|
|
//--- Just linear spline
|
|
Spline1DBuildLinear(x,y,n,c);
|
|
return;
|
|
}
|
|
if(n==2 && boundtype==-1)
|
|
{
|
|
//--- Same as cubic spline with periodic conditions
|
|
Spline1DBuildCubic(x,y,n,-1,0.0,-1,0.0,c);
|
|
return;
|
|
}
|
|
//--- Periodic or non-periodic boundary conditions
|
|
if(boundtype==-1)
|
|
{
|
|
//--- Periodic boundary conditions
|
|
y[n-1]=y[0];
|
|
//--- allocation
|
|
ArrayResizeAL(d,n);
|
|
d[0]=(y[1]-y[n-2])/(2*(x[1]-x[0]+x[n-1]-x[n-2]));
|
|
for(i=1;i<=n-2;i++)
|
|
d[i]=(1-tension)*(y[i+1]-y[i-1])/(x[i+1]-x[i-1]);
|
|
d[n-1]=d[0];
|
|
//--- Now problem is reduced to the cubic Hermite spline
|
|
Spline1DBuildHermite(x,y,d,n,c);
|
|
c.m_periodic=true;
|
|
}
|
|
else
|
|
{
|
|
//--- Non-periodic boundary conditions
|
|
ArrayResizeAL(d,n);
|
|
for(i=1;i<=n-2;i++)
|
|
d[i]=(1-tension)*(y[i+1]-y[i-1])/(x[i+1]-x[i-1]);
|
|
d[0]=2*(y[1]-y[0])/(x[1]-x[0])-d[1];
|
|
d[n-1]=2*(y[n-1]-y[n-2])/(x[n-1]-x[n-2])-d[n-2];
|
|
//--- Now problem is reduced to the cubic Hermite spline
|
|
Spline1DBuildHermite(x,y,d,n,c);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine builds Hermite spline interpolant. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - spline nodes, array[0..N-1] |
|
|
//| Y - function values, array[0..N-1] |
|
|
//| D - derivatives, array[0..N-1] |
|
|
//| N - points count (optional): |
|
|
//| * N>=2 |
|
|
//| * if given, only first N points are used to |
|
|
//| build spline |
|
|
//| * if not given, automatically detected from |
|
|
//| X/Y sizes (len(X) must be equal to len(Y)) |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| C - spline interpolant. |
|
|
//| ORDER OF POINTS |
|
|
//| Subroutine automatically sorts points, so caller may pass |
|
|
//| unsorted array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DBuildHermite(double &cx[],double &cy[],
|
|
double &cd[],const int n,
|
|
CSpline1DInterpolant &c)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double delta=0;
|
|
double delta2=0;
|
|
double delta3=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double d[];
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
ArrayCopy(d,cd);
|
|
//--- check
|
|
if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(d)>=n,__FUNCTION__+": Length(D)<N!"))
|
|
return;
|
|
//--- check and sort points
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(d,n),__FUNCTION__+": D contains infinite or NAN values!"))
|
|
return;
|
|
HeapSortDPoints(x,y,d,n);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
|
|
return;
|
|
//--- Build
|
|
ArrayResizeAL(c.m_x,n);
|
|
ArrayResizeAL(c.m_c,4*(n-1));
|
|
//--- change values
|
|
c.m_periodic=false;
|
|
c.m_k=3;
|
|
c.m_n=n;
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
c.m_x[i]=x[i];
|
|
//--- calculation
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
delta=x[i+1]-x[i];
|
|
delta2=CMath::Sqr(delta);
|
|
delta3=delta*delta2;
|
|
c.m_c[4*i+0]=y[i];
|
|
c.m_c[4*i+1]=d[i];
|
|
c.m_c[4*i+2]=(3*(y[i+1]-y[i])-2*d[i]*delta-d[i+1]*delta)/delta2;
|
|
c.m_c[4*i+3]=(2*(y[i]-y[i+1])+d[i]*delta+d[i+1]*delta)/delta3;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine builds Akima spline interpolant |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - spline nodes, array[0..N-1] |
|
|
//| Y - function values, array[0..N-1] |
|
|
//| N - points count (optional): |
|
|
//| * N>=5 |
|
|
//| * if given, only first N points are used to |
|
|
//| build spline |
|
|
//| * if not given, automatically detected from |
|
|
//| X/Y sizes (len(X) must be equal to len(Y)) |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| C - spline interpolant |
|
|
//| ORDER OF POINTS |
|
|
//| Subroutine automatically sorts points, so caller may pass |
|
|
//| unsorted array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DBuildAkima(double &cx[],double &cy[],
|
|
const int n,CSpline1DInterpolant &c)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create arrays
|
|
double d[];
|
|
double w[];
|
|
double diff[];
|
|
double x[];
|
|
double y[];
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
//--- check
|
|
if(!CAp::Assert(n>=5,__FUNCTION__+": N<5!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check and sort points
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- function call
|
|
HeapSortPoints(x,y,n);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::AreDistinct(x,n),__FUNCTION__+": at least two consequent points are too close!"))
|
|
return;
|
|
//--- Prepare W (weights),Diff (divided differences)
|
|
ArrayResizeAL(w,n-1);
|
|
ArrayResizeAL(diff,n-1);
|
|
for(i=0;i<=n-2;i++)
|
|
diff[i]=(y[i+1]-y[i])/(x[i+1]-x[i]);
|
|
for(i=1;i<=n-2;i++)
|
|
w[i]=MathAbs(diff[i]-diff[i-1]);
|
|
//--- Prepare Hermite interpolation scheme
|
|
ArrayResizeAL(d,n);
|
|
for(i=2;i<=n-3;i++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(w[i-1])+MathAbs(w[i+1])!=0.0)
|
|
d[i]=(w[i+1]*diff[i-1]+w[i-1]*diff[i])/(w[i+1]+w[i-1]);
|
|
else
|
|
d[i]=((x[i+1]-x[i])*diff[i-1]+(x[i]-x[i-1])*diff[i])/(x[i+1]-x[i-1]);
|
|
}
|
|
//--- change values
|
|
d[0]=DiffThreePoint(x[0],x[0],y[0],x[1],y[1],x[2],y[2]);
|
|
d[1]=DiffThreePoint(x[1],x[0],y[0],x[1],y[1],x[2],y[2]);
|
|
d[n-2]=DiffThreePoint(x[n-2],x[n-3],y[n-3],x[n-2],y[n-2],x[n-1],y[n-1]);
|
|
d[n-1]=DiffThreePoint(x[n-1],x[n-3],y[n-3],x[n-2],y[n-2],x[n-1],y[n-1]);
|
|
//--- Build Akima spline using Hermite interpolation scheme
|
|
Spline1DBuildHermite(x,y,d,n,c);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine calculates the value of the spline at the given |
|
|
//| point X. |
|
|
//| INPUT PARAMETERS: |
|
|
//| C - spline interpolant |
|
|
//| X - point |
|
|
//| Result: |
|
|
//| S(x) |
|
|
//+------------------------------------------------------------------+
|
|
static double CSpline1D::Spline1DCalc(CSpline1DInterpolant &c,double x)
|
|
{
|
|
//--- create variables
|
|
int l=0;
|
|
int r=0;
|
|
int m=0;
|
|
double t=0;
|
|
//--- check
|
|
if(!CAp::Assert(c.m_k==3,__FUNCTION__+": internal error"))
|
|
return(EMPTY_VALUE);
|
|
//--- check
|
|
if(!CAp::Assert(!CInfOrNaN::IsInfinity(x),__FUNCTION__+": infinite X!"))
|
|
return(EMPTY_VALUE);
|
|
//--- special case: NaN
|
|
if(CInfOrNaN::IsNaN(x))
|
|
return(CInfOrNaN::NaN());
|
|
//--- correct if periodic
|
|
if(c.m_periodic)
|
|
CApServ::ApPeriodicMap(x,c.m_x[0],c.m_x[c.m_n-1],t);
|
|
//--- Binary search in the [ x[0],...,x[n-2] ] (x[n-1] is not included)
|
|
l=0;
|
|
r=c.m_n-2+1;
|
|
while(l!=r-1)
|
|
{
|
|
m=(l+r)/2;
|
|
//--- check
|
|
if(c.m_x[m]>=x)
|
|
r=m;
|
|
else
|
|
l=m;
|
|
}
|
|
//--- Interpolation
|
|
x=x-c.m_x[l];
|
|
m=4*l;
|
|
//--- return result
|
|
return(c.m_c[m]+x*(c.m_c[m+1]+x*(c.m_c[m+2]+x*c.m_c[m+3])));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine differentiates the spline. |
|
|
//| INPUT PARAMETERS: |
|
|
//| C - spline interpolant. |
|
|
//| X - point |
|
|
//| Result: |
|
|
//| S - S(x) |
|
|
//| DS - S'(x) |
|
|
//| D2S - S''(x) |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DDiff(CSpline1DInterpolant &c,double x,
|
|
double &s,double &ds,double &d2s)
|
|
{
|
|
//--- create variables
|
|
int l=0;
|
|
int r=0;
|
|
int m=0;
|
|
double t=0;
|
|
//--- initialization
|
|
s=0;
|
|
ds=0;
|
|
d2s=0;
|
|
//--- check
|
|
if(!CAp::Assert(c.m_k==3,__FUNCTION__+": internal error"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(!CInfOrNaN::IsInfinity(x),__FUNCTION__+": infinite X!"))
|
|
return;
|
|
//--- special case: NaN
|
|
if(CInfOrNaN::IsNaN(x))
|
|
{
|
|
//--- change values
|
|
s=CInfOrNaN::NaN();
|
|
ds=CInfOrNaN::NaN();
|
|
d2s=CInfOrNaN::NaN();
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- correct if periodic
|
|
if(c.m_periodic)
|
|
CApServ::ApPeriodicMap(x,c.m_x[0],c.m_x[c.m_n-1],t);
|
|
//--- Binary search
|
|
l=0;
|
|
r=c.m_n-2+1;
|
|
while(l!=r-1)
|
|
{
|
|
m=(l+r)/2;
|
|
//--- check
|
|
if(c.m_x[m]>=x)
|
|
r=m;
|
|
else
|
|
l=m;
|
|
}
|
|
//--- Differentiation
|
|
x=x-c.m_x[l];
|
|
m=4*l;
|
|
s=c.m_c[m]+x*(c.m_c[m+1]+x*(c.m_c[m+2]+x*c.m_c[m+3]));
|
|
ds=c.m_c[m+1]+2*x*c.m_c[m+2]+3*CMath::Sqr(x)*c.m_c[m+3];
|
|
d2s=2*c.m_c[m+2]+6*x*c.m_c[m+3];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine makes the copy of the spline. |
|
|
//| INPUT PARAMETERS: |
|
|
//| C - spline interpolant. |
|
|
//| Result: |
|
|
//| CC - spline copy |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DCopy(CSpline1DInterpolant &c,CSpline1DInterpolant &cc)
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- change values
|
|
cc.m_periodic=c.m_periodic;
|
|
cc.m_n=c.m_n;
|
|
cc.m_k=c.m_k;
|
|
//--- allocation
|
|
ArrayResizeAL(cc.m_x,cc.m_n);
|
|
//--- copy
|
|
for(i_=0;i_<=cc.m_n-1;i_++)
|
|
cc.m_x[i_]=c.m_x[i_];
|
|
//--- allocation
|
|
ArrayResizeAL(cc.m_c,(cc.m_k+1)*(cc.m_n-1));
|
|
//--- copy
|
|
for(i_=0;i_<=(cc.m_k+1)*(cc.m_n-1)-1;i_++)
|
|
cc.m_c[i_]=c.m_c[i_];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine unpacks the spline into the coefficients table. |
|
|
//| INPUT PARAMETERS: |
|
|
//| C - spline interpolant. |
|
|
//| X - point |
|
|
//| Result: |
|
|
//| Tbl - coefficients table, unpacked format, array[0..N-2, |
|
|
//| 0..5]. |
|
|
//| For I = 0...N-2: |
|
|
//| Tbl[I,0] = X[i] |
|
|
//| Tbl[I,1] = X[i+1] |
|
|
//| Tbl[I,2] = C0 |
|
|
//| Tbl[I,3] = C1 |
|
|
//| Tbl[I,4] = C2 |
|
|
//| Tbl[I,5] = C3 |
|
|
//| On [x[i], x[i+1]] spline is equals to: |
|
|
//| S(x) = C0 + C1*t + C2*t^2 + C3*t^3 |
|
|
//| t = x-x[i] |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DUnpack(CSpline1DInterpolant &c,int &n,
|
|
CMatrixDouble &tbl)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
tbl.Resize(c.m_n-2+1,2+c.m_k+1);
|
|
//--- initialization
|
|
n=c.m_n;
|
|
//--- Fill
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
tbl[i].Set(0,c.m_x[i]);
|
|
tbl[i].Set(1,c.m_x[i+1]);
|
|
for(j=0;j<=c.m_k;j++)
|
|
tbl[i].Set(2+j,c.m_c[(c.m_k+1)*i+j]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine performs linear transformation of the spline |
|
|
//| argument. |
|
|
//| INPUT PARAMETERS: |
|
|
//| C - spline interpolant. |
|
|
//| A, B- transformation coefficients: x = A*t + B |
|
|
//| Result: |
|
|
//| C - transformed spline |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DLinTransX(CSpline1DInterpolant &c,const double a,
|
|
const double b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
double v=0;
|
|
double dv=0;
|
|
double d2v=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double d[];
|
|
//--- initialization
|
|
n=c.m_n;
|
|
//--- Special case: A=0
|
|
if(a==0.0)
|
|
{
|
|
v=Spline1DCalc(c,b);
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
c.m_c[(c.m_k+1)*i]=v;
|
|
for(j=1;j<=c.m_k;j++)
|
|
c.m_c[(c.m_k+1)*i+j]=0;
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- General case: A<>0.
|
|
//--- Unpack,X,Y,dY/dX.
|
|
//--- Scale and pack again.
|
|
if(!CAp::Assert(c.m_k==3,__FUNCTION__+": internal error"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResizeAL(x,n);
|
|
ArrayResizeAL(y,n);
|
|
ArrayResizeAL(d,n);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=c.m_x[i];
|
|
Spline1DDiff(c,x[i],v,dv,d2v);
|
|
x[i]=(x[i]-b)/a;
|
|
y[i]=v;
|
|
d[i]=a*dv;
|
|
}
|
|
//--- function call
|
|
Spline1DBuildHermite(x,y,d,n,c);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine performs linear transformation of the spline. |
|
|
//| INPUT PARAMETERS: |
|
|
//| C - spline interpolant. |
|
|
//| A,B- transformation coefficients: S2(x)=A*S(x) + B |
|
|
//| Result: |
|
|
//| C - transformed spline |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DLinTransY(CSpline1DInterpolant &c,const double a,
|
|
const double b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
//--- initialization
|
|
n=c.m_n;
|
|
//--- calculation
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
c.m_c[(c.m_k+1)*i]=a*c.m_c[(c.m_k+1)*i]+b;
|
|
for(j=1;j<=c.m_k;j++)
|
|
c.m_c[(c.m_k+1)*i+j]=a*c.m_c[(c.m_k+1)*i+j];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine integrates the spline. |
|
|
//| INPUT PARAMETERS: |
|
|
//| C - spline interpolant. |
|
|
//| X - right bound of the integration interval [a, x], |
|
|
//| here 'a' denotes min(x[]) |
|
|
//| Result: |
|
|
//| integral(S(t)dt,a,x) |
|
|
//+------------------------------------------------------------------+
|
|
static double CSpline1D::Spline1DIntegrate(CSpline1DInterpolant &c,double x)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int l=0;
|
|
int r=0;
|
|
int m=0;
|
|
double w=0;
|
|
double v=0;
|
|
double t=0;
|
|
double intab=0;
|
|
double additionalterm=0;
|
|
//--- initialization
|
|
n=c.m_n;
|
|
//--- Periodic splines require special treatment. We make
|
|
//--- following transformation:
|
|
//--- integral(S(t)dt,A,X)=integral(S(t)dt,A,Z)+AdditionalTerm
|
|
//--- here X may lie outside of [A,B],Z lies strictly in [A,B],
|
|
//--- AdditionalTerm is equals to integral(S(t)dt,A,B) times some
|
|
//--- integer number (may be zero).
|
|
if(c.m_periodic && (x<c.m_x[0] || x>c.m_x[c.m_n-1]))
|
|
{
|
|
//--- compute integral(S(x)dx,A,B)
|
|
intab=0;
|
|
for(i=0;i<=c.m_n-2;i++)
|
|
{
|
|
w=c.m_x[i+1]-c.m_x[i];
|
|
m=(c.m_k+1)*i;
|
|
intab=intab+c.m_c[m]*w;
|
|
v=w;
|
|
for(j=1;j<=c.m_k;j++)
|
|
{
|
|
v=v*w;
|
|
intab=intab+c.m_c[m+j]*v/(j+1);
|
|
}
|
|
}
|
|
//--- map X into [A,B]
|
|
CApServ::ApPeriodicMap(x,c.m_x[0],c.m_x[c.m_n-1],t);
|
|
additionalterm=t*intab;
|
|
}
|
|
else
|
|
additionalterm=0;
|
|
//--- Binary search in the [ x[0],...,x[n-2] ] (x[n-1] is not included)
|
|
l=0;
|
|
r=n-2+1;
|
|
while(l!=r-1)
|
|
{
|
|
m=(l+r)/2;
|
|
//--- check
|
|
if(c.m_x[m]>=x)
|
|
r=m;
|
|
else
|
|
l=m;
|
|
}
|
|
//--- Integration
|
|
result=0;
|
|
for(i=0;i<=l-1;i++)
|
|
{
|
|
w=c.m_x[i+1]-c.m_x[i];
|
|
m=(c.m_k+1)*i;
|
|
result=result+c.m_c[m]*w;
|
|
v=w;
|
|
//--- calculation
|
|
for(j=1;j<=c.m_k;j++)
|
|
{
|
|
v=v*w;
|
|
result=result+c.m_c[m+j]*v/(j+1);
|
|
}
|
|
}
|
|
//--- change values
|
|
w=x-c.m_x[l];
|
|
m=(c.m_k+1)*l;
|
|
v=w;
|
|
result=result+c.m_c[m]*w;
|
|
//--- calculation
|
|
for(j=1;j<=c.m_k;j++)
|
|
{
|
|
v=v*w;
|
|
result=result+c.m_c[m+j]*v/(j+1);
|
|
}
|
|
//--- return result
|
|
return(result+additionalterm);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal version of Spline1DConvDiff |
|
|
//| Converts from Hermite spline given by grid XOld to new grid X2 |
|
|
//| INPUT PARAMETERS: |
|
|
//| XOld - old grid |
|
|
//| YOld - values at old grid |
|
|
//| DOld - first derivative at old grid |
|
|
//| N - grid size |
|
|
//| X2 - new grid |
|
|
//| N2 - new grid size |
|
|
//| Y - possibly preallocated output array |
|
|
//| (reallocate if too small) |
|
|
//| NeedY - do we need Y? |
|
|
//| D1 - possibly preallocated output array |
|
|
//| (reallocate if too small) |
|
|
//| NeedD1 - do we need D1? |
|
|
//| D2 - possibly preallocated output array |
|
|
//| (reallocate if too small) |
|
|
//| NeedD2 - do we need D1? |
|
|
//| OUTPUT ARRAYS: |
|
|
//| Y - values, if needed |
|
|
//| D1 - first derivative, if needed |
|
|
//| D2 - second derivative, if needed |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DConvDiffInternal(double &xold[],double &yold[],
|
|
double &dold[],const int n,
|
|
double &x2[],const int n2,
|
|
double &y[],const bool needy,
|
|
double &d1[],const bool needd1,
|
|
double &d2[],const bool needd2)
|
|
{
|
|
//--- create variables
|
|
int intervalindex=0;
|
|
int pointindex=0;
|
|
bool havetoadvance;
|
|
double c0=0;
|
|
double c1=0;
|
|
double c2=0;
|
|
double c3=0;
|
|
double a=0;
|
|
double b=0;
|
|
double w=0;
|
|
double w2=0;
|
|
double w3=0;
|
|
double fa=0;
|
|
double fb=0;
|
|
double da=0;
|
|
double db=0;
|
|
double t=0;
|
|
//--- Prepare space
|
|
if(needy && CAp::Len(y)<n2)
|
|
ArrayResizeAL(y,n2);
|
|
//--- check
|
|
if(needd1 && CAp::Len(d1)<n2)
|
|
ArrayResizeAL(d1,n2);
|
|
//--- check
|
|
if(needd2 && CAp::Len(d2)<n2)
|
|
ArrayResizeAL(d2,n2);
|
|
//--- These assignments aren't actually needed
|
|
//--- (variables are initialized in the loop below),
|
|
//--- but without them compiler will complain about uninitialized locals
|
|
c0=0;
|
|
c1=0;
|
|
c2=0;
|
|
c3=0;
|
|
a=0;
|
|
b=0;
|
|
//--- Cycle
|
|
intervalindex=-1;
|
|
pointindex=0;
|
|
//--- calculation
|
|
while(true)
|
|
{
|
|
//--- are we ready to exit?
|
|
if(pointindex>=n2)
|
|
break;
|
|
t=x2[pointindex];
|
|
//--- do we need to advance interval?
|
|
havetoadvance=false;
|
|
//--- check
|
|
if(intervalindex==-1)
|
|
havetoadvance=true;
|
|
else
|
|
{
|
|
//--- check
|
|
if(intervalindex<n-2)
|
|
havetoadvance=t>=b;
|
|
}
|
|
//--- check
|
|
if(havetoadvance)
|
|
{
|
|
//--- change values
|
|
intervalindex=intervalindex+1;
|
|
a=xold[intervalindex];
|
|
b=xold[intervalindex+1];
|
|
w=b-a;
|
|
w2=w*w;
|
|
w3=w*w2;
|
|
fa=yold[intervalindex];
|
|
fb=yold[intervalindex+1];
|
|
da=dold[intervalindex];
|
|
db=dold[intervalindex+1];
|
|
c0=fa;
|
|
c1=da;
|
|
c2=(3*(fb-fa)-2*da*w-db*w)/w2;
|
|
c3=(2*(fa-fb)+da*w+db*w)/w3;
|
|
continue;
|
|
}
|
|
//--- Calculate spline and its derivatives using power basis
|
|
t=t-a;
|
|
if(needy)
|
|
y[pointindex]=c0+t*(c1+t*(c2+t*c3));
|
|
//--- check
|
|
if(needd1)
|
|
d1[pointindex]=c1+2*t*c2+3*t*t*c3;
|
|
//--- check
|
|
if(needd2)
|
|
d2[pointindex]=2*c2+6*t*c3;
|
|
//--- change value
|
|
pointindex=pointindex+1;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine. Heap sort. |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::HeapSortDPoints(double &x[],double &y[],double &d[],
|
|
const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double rbuf[];
|
|
int ibuf[];
|
|
double rbuf2[];
|
|
int ibuf2[];
|
|
//--- allocation
|
|
ArrayResizeAL(ibuf,n);
|
|
ArrayResizeAL(rbuf,n);
|
|
for(i=0;i<=n-1;i++)
|
|
ibuf[i]=i;
|
|
//--- function call
|
|
CTSort::TagSortFastI(x,ibuf,rbuf2,ibuf2,n);
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
rbuf[i]=y[ibuf[i]];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
y[i_]=rbuf[i_];
|
|
for(i=0;i<=n-1;i++)
|
|
rbuf[i]=d[ibuf[i]];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
d[i_]=rbuf[i_];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal version of Spline1DGridDiffCubic. |
|
|
//| Accepts pre-ordered X/Y, temporary arrays (which may be |
|
|
//| preallocated, if you want to save time, or not) and output array |
|
|
//| (which may be preallocated too). |
|
|
//| Y is passed as var-parameter because we may need to force last |
|
|
//| element to be equal to the first one (if periodic boundary |
|
|
//| conditions are specified). |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::Spline1DGridDiffCubicInternal(double &x[],double &y[],
|
|
const int n,const int boundltype,
|
|
const double boundl,
|
|
const int boundrtype,
|
|
const double boundr,
|
|
double &d[],double &a1[],
|
|
double &a2[],double &a3[],
|
|
double &b[],double &dt[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
//--- allocate arrays
|
|
if(CAp::Len(d)<n)
|
|
ArrayResizeAL(d,n);
|
|
//--- check
|
|
if(CAp::Len(a1)<n)
|
|
ArrayResizeAL(a1,n);
|
|
//--- check
|
|
if(CAp::Len(a2)<n)
|
|
ArrayResizeAL(a2,n);
|
|
//--- check
|
|
if(CAp::Len(a3)<n)
|
|
ArrayResizeAL(a3,n);
|
|
//--- check
|
|
if(CAp::Len(b)<n)
|
|
ArrayResizeAL(b,n);
|
|
//--- check
|
|
if(CAp::Len(dt)<n)
|
|
ArrayResizeAL(dt,n);
|
|
//--- Special cases:
|
|
//--- * N=2,parabolic terminated boundary condition on both ends
|
|
//--- * N=2,periodic boundary condition
|
|
if((n==2 && boundltype==0) && boundrtype==0)
|
|
{
|
|
//--- change values
|
|
d[0]=(y[1]-y[0])/(x[1]-x[0]);
|
|
d[1]=d[0];
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if((n==2 && boundltype==-1) && boundrtype==-1)
|
|
{
|
|
//--- change values
|
|
d[0]=0;
|
|
d[1]=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Periodic and non-periodic boundary conditions are
|
|
//--- two separate classes
|
|
if(boundrtype==-1 && boundltype==-1)
|
|
{
|
|
//--- Periodic boundary conditions
|
|
y[n-1]=y[0];
|
|
//--- Boundary conditions at N-1 points
|
|
//--- (one point less because last point is the same as first point).
|
|
a1[0]=x[1]-x[0];
|
|
a2[0]=2*(x[1]-x[0]+x[n-1]-x[n-2]);
|
|
a3[0]=x[n-1]-x[n-2];
|
|
b[0]=3*(y[n-1]-y[n-2])/(x[n-1]-x[n-2])*(x[1]-x[0])+3*(y[1]-y[0])/(x[1]-x[0])*(x[n-1]-x[n-2]);
|
|
//--- calculation
|
|
for(i=1;i<=n-2;i++)
|
|
{
|
|
//--- Altough last point is [N-2],we use X[N-1] and Y[N-1]
|
|
//--- (because of periodicity)
|
|
a1[i]=x[i+1]-x[i];
|
|
a2[i]=2*(x[i+1]-x[i-1]);
|
|
a3[i]=x[i]-x[i-1];
|
|
b[i]=3*(y[i]-y[i-1])/(x[i]-x[i-1])*(x[i+1]-x[i])+3*(y[i+1]-y[i])/(x[i+1]-x[i])*(x[i]-x[i-1]);
|
|
}
|
|
//--- Solve,add last point (with index N-1)
|
|
SolveCyclicTridiagonal(a1,a2,a3,b,n-1,dt);
|
|
for(i_=0;i_<=n-2;i_++)
|
|
d[i_]=dt[i_];
|
|
d[n-1]=d[0];
|
|
}
|
|
else
|
|
{
|
|
//--- Non-periodic boundary condition.
|
|
//--- Left boundary conditions.
|
|
if(boundltype==0)
|
|
{
|
|
//--- change values
|
|
a1[0]=0;
|
|
a2[0]=1;
|
|
a3[0]=1;
|
|
b[0]=2*(y[1]-y[0])/(x[1]-x[0]);
|
|
}
|
|
//--- check
|
|
if(boundltype==1)
|
|
{
|
|
//--- change values
|
|
a1[0]=0;
|
|
a2[0]=1;
|
|
a3[0]=0;
|
|
b[0]=boundl;
|
|
}
|
|
//--- check
|
|
if(boundltype==2)
|
|
{
|
|
//--- change values
|
|
a1[0]=0;
|
|
a2[0]=2;
|
|
a3[0]=1;
|
|
b[0]=3*(y[1]-y[0])/(x[1]-x[0])-0.5*boundl*(x[1]-x[0]);
|
|
}
|
|
//--- Central conditions
|
|
for(i=1;i<=n-2;i++)
|
|
{
|
|
a1[i]=x[i+1]-x[i];
|
|
a2[i]=2*(x[i+1]-x[i-1]);
|
|
a3[i]=x[i]-x[i-1];
|
|
b[i]=3*(y[i]-y[i-1])/(x[i]-x[i-1])*(x[i+1]-x[i])+3*(y[i+1]-y[i])/(x[i+1]-x[i])*(x[i]-x[i-1]);
|
|
}
|
|
//--- Right boundary conditions
|
|
if(boundrtype==0)
|
|
{
|
|
//--- change values
|
|
a1[n-1]=1;
|
|
a2[n-1]=1;
|
|
a3[n-1]=0;
|
|
b[n-1]=2*(y[n-1]-y[n-2])/(x[n-1]-x[n-2]);
|
|
}
|
|
//--- check
|
|
if(boundrtype==1)
|
|
{
|
|
//--- change values
|
|
a1[n-1]=0;
|
|
a2[n-1]=1;
|
|
a3[n-1]=0;
|
|
b[n-1]=boundr;
|
|
}
|
|
//--- check
|
|
if(boundrtype==2)
|
|
{
|
|
//--- change values
|
|
a1[n-1]=1;
|
|
a2[n-1]=2;
|
|
a3[n-1]=0;
|
|
b[n-1]=3*(y[n-1]-y[n-2])/(x[n-1]-x[n-2])+0.5*boundr*(x[n-1]-x[n-2]);
|
|
}
|
|
//--- Solve
|
|
SolveTridiagonal(a1,a2,a3,b,n,d);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine. Heap sort. |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::HeapSortPoints(double &x[],double &y[],const int n)
|
|
{
|
|
//--- create arrays
|
|
double bufx[];
|
|
double bufy[];
|
|
//--- function call
|
|
CTSort::TagSortFastR(x,y,bufx,bufy,n);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine. Heap sort. |
|
|
//| Accepts: |
|
|
//| X, Y - points |
|
|
//| P - empty or preallocated array |
|
|
//| Returns: |
|
|
//| X, Y - sorted by X |
|
|
//| P - array of permutations; I-th position of output |
|
|
//| arrays X/Y contains(X[P[I]],Y[P[I]]) |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::HeapSortPPoints(double &x[],double &y[],int &p[],
|
|
const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double rbuf[];
|
|
int ibuf[];
|
|
//--- check
|
|
if(CAp::Len(p)<n)
|
|
ArrayResizeAL(p,n);
|
|
//--- allocation
|
|
ArrayResizeAL(rbuf,n);
|
|
//--- initialization
|
|
for(i=0;i<=n-1;i++)
|
|
p[i]=i;
|
|
//--- function call
|
|
CTSort::TagSortFastI(x,p,rbuf,ibuf,n);
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
rbuf[i]=y[p[i]];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
y[i_]=rbuf[i_];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine. Tridiagonal solver. Solves |
|
|
//| ( B[0] C[0] ) |
|
|
//| ( A[1] B[1] C[1] ) |
|
|
//| ( A[2] B[2] C[2] ) |
|
|
//| ( .......... ) * X=D |
|
|
//| ( .......... ) |
|
|
//| ( A[N-2] B[N-2] C[N-2] ) |
|
|
//| ( A[N-1] B[N-1] ) |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::SolveTridiagonal(double &a[],double &cb[],double &c[],
|
|
double &cd[],const int n,double &x[])
|
|
{
|
|
//--- create variables
|
|
int k=0;
|
|
double t=0;
|
|
//--- create arrays
|
|
double d[];
|
|
double b[];
|
|
//--- copy arrays
|
|
ArrayCopy(d,cd);
|
|
ArrayCopy(b,cb);
|
|
//--- check
|
|
if(CAp::Len(x)<n)
|
|
ArrayResizeAL(x,n);
|
|
//--- calculation
|
|
for(k=1;k<=n-1;k++)
|
|
{
|
|
t=a[k]/b[k-1];
|
|
b[k]=b[k]-t*c[k-1];
|
|
d[k]=d[k]-t*d[k-1];
|
|
}
|
|
x[n-1]=d[n-1]/b[n-1];
|
|
for(k=n-2;k>=0;k--)
|
|
x[k]=(d[k]-c[k]*x[k+1])/b[k];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine. Cyclic tridiagonal solver. Solves |
|
|
//| ( B[0] C[0] A[0] ) |
|
|
//| ( A[1] B[1] C[1] ) |
|
|
//| ( A[2] B[2] C[2] ) |
|
|
//| ( .......... ) * X=D |
|
|
//| ( .......... ) |
|
|
//| ( A[N-2] B[N-2] C[N-2] ) |
|
|
//| ( C[N-1] A[N-1] B[N-1] ) |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline1D::SolveCyclicTridiagonal(double &a[],double &cb[],
|
|
double &c[],double &d[],
|
|
const int n,double &x[])
|
|
{
|
|
//--- create variables
|
|
int k=0;
|
|
double alpha=0;
|
|
double beta=0;
|
|
double gamma=0;
|
|
//--- create arrays
|
|
double y[];
|
|
double z[];
|
|
double u[];
|
|
double b[];
|
|
//--- copy array
|
|
ArrayCopy(b,cb);
|
|
//--- check
|
|
if(CAp::Len(x)<n)
|
|
ArrayResizeAL(x,n);
|
|
//--- change values
|
|
beta=a[0];
|
|
alpha=c[n-1];
|
|
gamma=-b[0];
|
|
b[0]=2*b[0];
|
|
b[n-1]=b[n-1]-alpha*beta/gamma;
|
|
//--- allocation
|
|
ArrayResizeAL(u,n);
|
|
//--- initialization
|
|
for(k=0;k<=n-1;k++)
|
|
u[k]=0;
|
|
u[0]=gamma;
|
|
u[n-1]=alpha;
|
|
//--- function call
|
|
SolveTridiagonal(a,b,c,d,n,y);
|
|
//--- function call
|
|
SolveTridiagonal(a,b,c,u,n,z);
|
|
//--- calculation
|
|
for(k=0;k<=n-1;k++)
|
|
x[k]=y[k]-(y[0]+beta/gamma*y[n-1])/(1+z[0]+beta/gamma*z[n-1])*z[k];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine. Three-point differentiation |
|
|
//+------------------------------------------------------------------+
|
|
static double CSpline1D::DiffThreePoint(double t,const double x0,const double f0,
|
|
double x1,const double f1,double x2,
|
|
const double f2)
|
|
{
|
|
//--- create variables
|
|
double a=0;
|
|
double b=0;
|
|
//--- change values
|
|
t=t-x0;
|
|
x1=x1-x0;
|
|
x2=x2-x0;
|
|
a=(f2-f0-x2/x1*(f1-f0))/(CMath::Sqr(x2)-x1*x2);
|
|
b=(f1-f0-a*CMath::Sqr(x1))/x1;
|
|
//--- return result
|
|
return(2*a*t+b);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Polynomial fitting report: |
|
|
//| TaskRCond reciprocal of task's condition number |
|
|
//| RMSError RMS error |
|
|
//| AvgError average error |
|
|
//| AvgRelError average relative error (for non-zero Y[I]) |
|
|
//| MaxError maximum error |
|
|
//+------------------------------------------------------------------+
|
|
class CPolynomialFitReport
|
|
{
|
|
public:
|
|
//--- variables
|
|
double m_taskrcond;
|
|
double m_rmserror;
|
|
double m_avgerror;
|
|
double m_avgrelerror;
|
|
double m_maxerror;
|
|
//--- constructor, destructor
|
|
CPolynomialFitReport(void);
|
|
~CPolynomialFitReport(void);
|
|
//--- copy
|
|
void Copy(CPolynomialFitReport &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CPolynomialFitReport::CPolynomialFitReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CPolynomialFitReport::~CPolynomialFitReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CPolynomialFitReport::Copy(CPolynomialFitReport &obj)
|
|
{
|
|
//--- copy variables
|
|
m_taskrcond=obj.m_taskrcond;
|
|
m_rmserror=obj.m_rmserror;
|
|
m_avgerror=obj.m_avgerror;
|
|
m_avgrelerror=obj.m_avgrelerror;
|
|
m_maxerror=obj.m_maxerror;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Polynomial fitting report: |
|
|
//| TaskRCond reciprocal of task's condition number |
|
|
//| RMSError RMS error |
|
|
//| AvgError average error |
|
|
//| AvgRelError average relative error (for non-zero Y[I]) |
|
|
//| MaxError maximum error |
|
|
//+------------------------------------------------------------------+
|
|
class CPolynomialFitReportShell
|
|
{
|
|
private:
|
|
CPolynomialFitReport m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CPolynomialFitReportShell(void);
|
|
CPolynomialFitReportShell(CPolynomialFitReport &obj);
|
|
~CPolynomialFitReportShell(void);
|
|
//--- methods
|
|
double GetTaskRCond(void);
|
|
void SetTaskRCond(const double d);
|
|
double GetRMSError(void);
|
|
void SetRMSError(const double d);
|
|
double GetAvgError(void);
|
|
void SetAvgError(const double d);
|
|
double GetAvgRelError(void);
|
|
void SetAvgRelError(const double d);
|
|
double GetMaxError(void);
|
|
void SetMaxError(const double d);
|
|
CPolynomialFitReport *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CPolynomialFitReportShell::CPolynomialFitReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CPolynomialFitReportShell::CPolynomialFitReportShell(CPolynomialFitReport &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CPolynomialFitReportShell::~CPolynomialFitReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable taskrcond |
|
|
//+------------------------------------------------------------------+
|
|
double CPolynomialFitReportShell::GetTaskRCond(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_taskrcond);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable taskrcond |
|
|
//+------------------------------------------------------------------+
|
|
void CPolynomialFitReportShell::SetTaskRCond(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_taskrcond=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable rmserror |
|
|
//+------------------------------------------------------------------+
|
|
double CPolynomialFitReportShell::GetRMSError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_rmserror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable rmserror |
|
|
//+------------------------------------------------------------------+
|
|
void CPolynomialFitReportShell::SetRMSError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_rmserror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable avgerror |
|
|
//+------------------------------------------------------------------+
|
|
double CPolynomialFitReportShell::GetAvgError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_avgerror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable avgerror |
|
|
//+------------------------------------------------------------------+
|
|
void CPolynomialFitReportShell::SetAvgError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_avgerror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable avgrelerror |
|
|
//+------------------------------------------------------------------+
|
|
double CPolynomialFitReportShell::GetAvgRelError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_avgrelerror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable avgrelerror |
|
|
//+------------------------------------------------------------------+
|
|
void CPolynomialFitReportShell::SetAvgRelError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_avgrelerror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable maxerror |
|
|
//+------------------------------------------------------------------+
|
|
double CPolynomialFitReportShell::GetMaxError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_maxerror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable maxerror |
|
|
//+------------------------------------------------------------------+
|
|
void CPolynomialFitReportShell::SetMaxError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_maxerror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CPolynomialFitReport *CPolynomialFitReportShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Barycentric fitting report: |
|
|
//| RMSError RMS error |
|
|
//| AvgError average error |
|
|
//| AvgRelError average relative error (for non-zero Y[I]) |
|
|
//| MaxError maximum error |
|
|
//| TaskRCond reciprocal of task's condition number |
|
|
//+------------------------------------------------------------------+
|
|
class CBarycentricFitReport
|
|
{
|
|
public:
|
|
//--- variables
|
|
double m_taskrcond;
|
|
int m_dbest;
|
|
double m_rmserror;
|
|
double m_avgerror;
|
|
double m_avgrelerror;
|
|
double m_maxerror;
|
|
//--- constructor, destructor
|
|
CBarycentricFitReport(void);
|
|
~CBarycentricFitReport(void);
|
|
//--- copy
|
|
void Copy(CBarycentricFitReport &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CBarycentricFitReport::CBarycentricFitReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CBarycentricFitReport::~CBarycentricFitReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CBarycentricFitReport::Copy(CBarycentricFitReport &obj)
|
|
{
|
|
//--- copy variables
|
|
m_taskrcond=obj.m_taskrcond;
|
|
m_dbest=obj.m_dbest;
|
|
m_rmserror=obj.m_rmserror;
|
|
m_avgerror=obj.m_avgerror;
|
|
m_avgrelerror=obj.m_avgrelerror;
|
|
m_maxerror=obj.m_maxerror;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Barycentric fitting report: |
|
|
//| RMSError RMS error |
|
|
//| AvgError average error |
|
|
//| AvgRelError average relative error (for non-zero Y[I]) |
|
|
//| MaxError maximum error |
|
|
//| TaskRCond reciprocal of task's condition number |
|
|
//+------------------------------------------------------------------+
|
|
class CBarycentricFitReportShell
|
|
{
|
|
private:
|
|
CBarycentricFitReport m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CBarycentricFitReportShell(void);
|
|
CBarycentricFitReportShell(CBarycentricFitReport &obj);
|
|
~CBarycentricFitReportShell(void);
|
|
//--- methods
|
|
double GetTaskRCond(void);
|
|
void SetTaskRCond(const double d);
|
|
int GetDBest(void);
|
|
void SetDBest(const int i);
|
|
double GetRMSError(void);
|
|
void SetRMSError(const double d);
|
|
double GetAvgError(void);
|
|
void SetAvgError(const double d);
|
|
double GetAvgRelError(void);
|
|
void SetAvgRelError(const double d);
|
|
double GetMaxError(void);
|
|
void SetMaxError(const double d);
|
|
CBarycentricFitReport *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CBarycentricFitReportShell::CBarycentricFitReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CBarycentricFitReportShell::CBarycentricFitReportShell(CBarycentricFitReport &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CBarycentricFitReportShell::~CBarycentricFitReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable taskrcond |
|
|
//+------------------------------------------------------------------+
|
|
double CBarycentricFitReportShell::GetTaskRCond(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_taskrcond);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable taskrcond |
|
|
//+------------------------------------------------------------------+
|
|
void CBarycentricFitReportShell::SetTaskRCond(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_taskrcond=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable dbest |
|
|
//+------------------------------------------------------------------+
|
|
int CBarycentricFitReportShell::GetDBest(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_dbest);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable dbest |
|
|
//+------------------------------------------------------------------+
|
|
void CBarycentricFitReportShell::SetDBest(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_dbest=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable rmserror |
|
|
//+------------------------------------------------------------------+
|
|
double CBarycentricFitReportShell::GetRMSError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_rmserror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable rmserror |
|
|
//+------------------------------------------------------------------+
|
|
void CBarycentricFitReportShell::SetRMSError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_rmserror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable avgerror |
|
|
//+------------------------------------------------------------------+
|
|
double CBarycentricFitReportShell::GetAvgError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_avgerror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable avgerror |
|
|
//+------------------------------------------------------------------+
|
|
void CBarycentricFitReportShell::SetAvgError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_avgerror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable avgrelerror |
|
|
//+------------------------------------------------------------------+
|
|
double CBarycentricFitReportShell::GetAvgRelError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_avgrelerror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable avgrelerror |
|
|
//+------------------------------------------------------------------+
|
|
void CBarycentricFitReportShell::SetAvgRelError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_avgrelerror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable maxerror |
|
|
//+------------------------------------------------------------------+
|
|
double CBarycentricFitReportShell::GetMaxError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_maxerror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable maxerror |
|
|
//+------------------------------------------------------------------+
|
|
void CBarycentricFitReportShell::SetMaxError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_maxerror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CBarycentricFitReport *CBarycentricFitReportShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Spline fitting report: |
|
|
//| RMSError RMS error |
|
|
//| AvgError average error |
|
|
//| AvgRelError average relative error (for non-zero Y[I]) |
|
|
//| MaxError maximum error |
|
|
//| Fields below are filled by obsolete functions (Spline1DFitCubic, |
|
|
//| Spline1DFitHermite). Modern fitting functions do NOT fill these |
|
|
//| fields: |
|
|
//| TaskRCond reciprocal of task's condition number |
|
|
//+------------------------------------------------------------------+
|
|
class CSpline1DFitReport
|
|
{
|
|
public:
|
|
//--- variables
|
|
double m_taskrcond;
|
|
double m_rmserror;
|
|
double m_avgerror;
|
|
double m_avgrelerror;
|
|
double m_maxerror;
|
|
//--- constructor, destructor
|
|
CSpline1DFitReport(void);
|
|
~CSpline1DFitReport(void);
|
|
//--- copy
|
|
void Copy(CSpline1DFitReport &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CSpline1DFitReport::CSpline1DFitReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CSpline1DFitReport::~CSpline1DFitReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CSpline1DFitReport::Copy(CSpline1DFitReport &obj)
|
|
{
|
|
//--- copy variables
|
|
m_taskrcond=obj.m_taskrcond;
|
|
m_rmserror=obj.m_rmserror;
|
|
m_avgerror=obj.m_avgerror;
|
|
m_avgrelerror=obj.m_avgrelerror;
|
|
m_maxerror=obj.m_maxerror;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Spline fitting report: |
|
|
//| RMSError RMS error |
|
|
//| AvgError average error |
|
|
//| AvgRelError average relative error (for non-zero Y[I]) |
|
|
//| MaxError maximum error |
|
|
//| Fields below are filled by obsolete functions (Spline1DFitCubic, |
|
|
//| Spline1DFitHermite). Modern fitting functions do NOT fill these |
|
|
//| fields: |
|
|
//| TaskRCond reciprocal of task's condition number |
|
|
//+------------------------------------------------------------------+
|
|
class CSpline1DFitReportShell
|
|
{
|
|
private:
|
|
CSpline1DFitReport m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CSpline1DFitReportShell(void);
|
|
CSpline1DFitReportShell(CSpline1DFitReport &obj);
|
|
~CSpline1DFitReportShell(void);
|
|
//--- methods
|
|
double GetTaskRCond(void);
|
|
void SetTaskRCond(const double d);
|
|
double GetRMSError(void);
|
|
void SetRMSError(const double d);
|
|
double GetAvgError(void);
|
|
void SetAvgError(const double d);
|
|
double GetAvgRelError(void);
|
|
void SetAvgRelError(const double d);
|
|
double GetMaxError(void);
|
|
void SetMaxError(const double d);
|
|
CSpline1DFitReport *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CSpline1DFitReportShell::CSpline1DFitReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CSpline1DFitReportShell::CSpline1DFitReportShell(CSpline1DFitReport &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CSpline1DFitReportShell::~CSpline1DFitReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable taskrcond |
|
|
//+------------------------------------------------------------------+
|
|
double CSpline1DFitReportShell::GetTaskRCond(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_taskrcond);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable taskrcond |
|
|
//+------------------------------------------------------------------+
|
|
void CSpline1DFitReportShell::SetTaskRCond(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_taskrcond=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable rmserror |
|
|
//+------------------------------------------------------------------+
|
|
double CSpline1DFitReportShell::GetRMSError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_rmserror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable rmserror |
|
|
//+------------------------------------------------------------------+
|
|
void CSpline1DFitReportShell::SetRMSError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_rmserror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable avgerror |
|
|
//+------------------------------------------------------------------+
|
|
double CSpline1DFitReportShell::GetAvgError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_avgerror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable avgerror |
|
|
//+------------------------------------------------------------------+
|
|
void CSpline1DFitReportShell::SetAvgError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_avgerror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable avgrelerror |
|
|
//+------------------------------------------------------------------+
|
|
double CSpline1DFitReportShell::GetAvgRelError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_avgrelerror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable avgrelerror |
|
|
//+------------------------------------------------------------------+
|
|
void CSpline1DFitReportShell::SetAvgRelError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_avgrelerror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable maxerror |
|
|
//+------------------------------------------------------------------+
|
|
double CSpline1DFitReportShell::GetMaxError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_maxerror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable maxerror |
|
|
//+------------------------------------------------------------------+
|
|
void CSpline1DFitReportShell::SetMaxError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_maxerror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CSpline1DFitReport *CSpline1DFitReportShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Least squares fitting report: |
|
|
//| TaskRCond reciprocal of task's condition number |
|
|
//| IterationsCount number of internal iterations |
|
|
//| RMSError RMS error |
|
|
//| AvgError average error |
|
|
//| AvgRelError average relative error (for non-zero Y[I]) |
|
|
//| MaxError maximum error |
|
|
//| WRMSError weighted RMS error |
|
|
//+------------------------------------------------------------------+
|
|
class CLSFitReport
|
|
{
|
|
public:
|
|
//--- variables
|
|
double m_taskrcond;
|
|
int m_iterationscount;
|
|
double m_rmserror;
|
|
double m_avgerror;
|
|
double m_avgrelerror;
|
|
double m_maxerror;
|
|
double m_wrmserror;
|
|
//--- constructor, destructor
|
|
CLSFitReport(void);
|
|
~CLSFitReport(void);
|
|
//--- copy
|
|
void Copy(CLSFitReport &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CLSFitReport::CLSFitReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CLSFitReport::~CLSFitReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CLSFitReport::Copy(CLSFitReport &obj)
|
|
{
|
|
//--- copy variables
|
|
m_taskrcond=obj.m_taskrcond;
|
|
m_iterationscount=obj.m_iterationscount;
|
|
m_rmserror=obj.m_rmserror;
|
|
m_avgerror=obj.m_avgerror;
|
|
m_avgrelerror=obj.m_avgrelerror;
|
|
m_maxerror=obj.m_maxerror;
|
|
m_wrmserror=obj.m_wrmserror;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Least squares fitting report: |
|
|
//| TaskRCond reciprocal of task's condition number |
|
|
//| IterationsCount number of internal iterations |
|
|
//| RMSError RMS error |
|
|
//| AvgError average error |
|
|
//| AvgRelError average relative error (for non-zero Y[I]) |
|
|
//| MaxError maximum error |
|
|
//| WRMSError weighted RMS error |
|
|
//+------------------------------------------------------------------+
|
|
class CLSFitReportShell
|
|
{
|
|
private:
|
|
CLSFitReport m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CLSFitReportShell(void);
|
|
CLSFitReportShell(CLSFitReport &obj);
|
|
~CLSFitReportShell(void);
|
|
//--- methods
|
|
double GetTaskRCond(void);
|
|
void SetTaskRCond(const double d);
|
|
int GetIterationsCount(void);
|
|
void SetIterationsCount(const int i);
|
|
double GetRMSError(void);
|
|
void SetRMSError(const double d);
|
|
double GetAvgError(void);
|
|
void SetAvgError(const double d);
|
|
double GetAvgRelError(void);
|
|
void SetAvgRelError(const double d);
|
|
double GetMaxError(void);
|
|
void SetMaxError(const double d);
|
|
double GetWRMSError(void);
|
|
void SetWRMSError(const double d);
|
|
CLSFitReport *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CLSFitReportShell::CLSFitReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CLSFitReportShell::CLSFitReportShell(CLSFitReport &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CLSFitReportShell::~CLSFitReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable taskrcond |
|
|
//+------------------------------------------------------------------+
|
|
double CLSFitReportShell::GetTaskRCond(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_taskrcond);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable taskrcond |
|
|
//+------------------------------------------------------------------+
|
|
void CLSFitReportShell::SetTaskRCond(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_taskrcond=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable iterationscount |
|
|
//+------------------------------------------------------------------+
|
|
int CLSFitReportShell::GetIterationsCount(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_iterationscount);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable iterationscount |
|
|
//+------------------------------------------------------------------+
|
|
void CLSFitReportShell::SetIterationsCount(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_iterationscount=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable rmserror |
|
|
//+------------------------------------------------------------------+
|
|
double CLSFitReportShell::GetRMSError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_rmserror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable rmserror |
|
|
//+------------------------------------------------------------------+
|
|
void CLSFitReportShell::SetRMSError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_rmserror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable avgerror |
|
|
//+------------------------------------------------------------------+
|
|
double CLSFitReportShell::GetAvgError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_avgerror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable avgerror |
|
|
//+------------------------------------------------------------------+
|
|
void CLSFitReportShell::SetAvgError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_avgerror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable avgrelerror |
|
|
//+------------------------------------------------------------------+
|
|
double CLSFitReportShell::GetAvgRelError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_avgrelerror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable avgrelerror |
|
|
//+------------------------------------------------------------------+
|
|
void CLSFitReportShell::SetAvgRelError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_avgrelerror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable maxerror |
|
|
//+------------------------------------------------------------------+
|
|
double CLSFitReportShell::GetMaxError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_maxerror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable maxerror |
|
|
//+------------------------------------------------------------------+
|
|
void CLSFitReportShell::SetMaxError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_maxerror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable wrmserror |
|
|
//+------------------------------------------------------------------+
|
|
double CLSFitReportShell::GetWRMSError(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_wrmserror);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable wrmserror |
|
|
//+------------------------------------------------------------------+
|
|
void CLSFitReportShell::SetWRMSError(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_wrmserror=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CLSFitReport *CLSFitReportShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Nonlinear fitter. |
|
|
//| You should use ALGLIB functions to work with fitter. |
|
|
//| Never try to access its fields directly! |
|
|
//+------------------------------------------------------------------+
|
|
class CLSFitState
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_optalgo;
|
|
int m_m;
|
|
int m_k;
|
|
double m_f;
|
|
double m_epsf;
|
|
double m_epsx;
|
|
int m_maxits;
|
|
double m_stpmax;
|
|
bool m_xrep;
|
|
int m_npoints;
|
|
int m_nweights;
|
|
int m_wkind;
|
|
int m_wits;
|
|
bool m_xupdated;
|
|
bool m_needf;
|
|
bool m_needfg;
|
|
bool m_needfgh;
|
|
int m_pointindex;
|
|
int m_repiterationscount;
|
|
int m_repterminationtype;
|
|
double m_reprmserror;
|
|
double m_repavgerror;
|
|
double m_repavgrelerror;
|
|
double m_repmaxerror;
|
|
double m_repwrmserror;
|
|
CMinLMState m_optstate;
|
|
CMinLMReport m_optrep;
|
|
int m_prevnpt;
|
|
int m_prevalgo;
|
|
RCommState m_rstate;
|
|
//--- arrays
|
|
double m_s[];
|
|
double m_bndl[];
|
|
double m_bndu[];
|
|
double m_tasky[];
|
|
double m_w[];
|
|
double m_x[];
|
|
double m_c[];
|
|
double m_g[];
|
|
//--- matrices
|
|
CMatrixDouble m_taskx;
|
|
CMatrixDouble m_h;
|
|
//--- constructor, destructor
|
|
CLSFitState(void);
|
|
~CLSFitState(void);
|
|
//--- copy
|
|
void Copy(CLSFitState &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CLSFitState::CLSFitState(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CLSFitState::~CLSFitState(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CLSFitState::Copy(CLSFitState &obj)
|
|
{
|
|
//--- copy variables
|
|
m_optalgo=obj.m_optalgo;
|
|
m_m=obj.m_m;
|
|
m_k=obj.m_k;
|
|
m_f=obj.m_f;
|
|
m_epsf=obj.m_epsf;
|
|
m_epsx=obj.m_epsx;
|
|
m_maxits=obj.m_maxits;
|
|
m_stpmax=obj.m_stpmax;
|
|
m_xrep=obj.m_xrep;
|
|
m_npoints=obj.m_npoints;
|
|
m_nweights=obj.m_nweights;
|
|
m_wkind=obj.m_wkind;
|
|
m_wits=obj.m_wits;
|
|
m_xupdated=obj.m_xupdated;
|
|
m_needf=obj.m_needf;
|
|
m_needfg=obj.m_needfg;
|
|
m_needfgh=obj.m_needfgh;
|
|
m_pointindex=obj.m_pointindex;
|
|
m_repiterationscount=obj.m_repiterationscount;
|
|
m_repterminationtype=obj.m_repterminationtype;
|
|
m_reprmserror=obj.m_reprmserror;
|
|
m_repavgerror=obj.m_repavgerror;
|
|
m_repavgrelerror=obj.m_repavgrelerror;
|
|
m_repmaxerror=obj.m_repmaxerror;
|
|
m_repwrmserror=obj.m_repwrmserror;
|
|
m_prevnpt=obj.m_prevnpt;
|
|
m_prevalgo=obj.m_prevalgo;
|
|
m_optstate.Copy(obj.m_optstate);
|
|
m_optrep.Copy(obj.m_optrep);
|
|
m_rstate.Copy(obj.m_rstate);
|
|
//--- copy arrays
|
|
ArrayCopy(m_s,obj.m_s);
|
|
ArrayCopy(m_bndl,obj.m_bndl);
|
|
ArrayCopy(m_bndu,obj.m_bndu);
|
|
ArrayCopy(m_tasky,obj.m_tasky);
|
|
ArrayCopy(m_w,obj.m_w);
|
|
ArrayCopy(m_x,obj.m_x);
|
|
ArrayCopy(m_c,obj.m_c);
|
|
ArrayCopy(m_g,obj.m_g);
|
|
//--- copy matrices
|
|
m_taskx=obj.m_taskx;
|
|
m_h=obj.m_h;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Nonlinear fitter. |
|
|
//| You should use ALGLIB functions to work with fitter. |
|
|
//| Never try to access its fields directly! |
|
|
//+------------------------------------------------------------------+
|
|
class CLSFitStateShell
|
|
{
|
|
private:
|
|
CLSFitState m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CLSFitStateShell(void);
|
|
CLSFitStateShell(CLSFitState &obj);
|
|
~CLSFitStateShell(void);
|
|
//--- methods
|
|
bool GetNeedF(void);
|
|
void SetNeedF(const bool b);
|
|
bool GetNeedFG(void);
|
|
void SetNeedFG(const bool b);
|
|
bool GetNeedFGH(void);
|
|
void SetNeedFGH(const bool b);
|
|
bool GetXUpdated(void);
|
|
void SetXUpdated(const bool b);
|
|
double GetF(void);
|
|
void SetF(const double d);
|
|
CLSFitState *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CLSFitStateShell::CLSFitStateShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CLSFitStateShell::CLSFitStateShell(CLSFitState &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CLSFitStateShell::~CLSFitStateShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needf |
|
|
//+------------------------------------------------------------------+
|
|
bool CLSFitStateShell::GetNeedF(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_needf);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needf |
|
|
//+------------------------------------------------------------------+
|
|
void CLSFitStateShell::SetNeedF(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_needf=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needfg |
|
|
//+------------------------------------------------------------------+
|
|
bool CLSFitStateShell::GetNeedFG(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_needfg);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needfg |
|
|
//+------------------------------------------------------------------+
|
|
void CLSFitStateShell::SetNeedFG(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_needfg=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needfgh |
|
|
//+------------------------------------------------------------------+
|
|
bool CLSFitStateShell::GetNeedFGH(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_needfgh);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needfgh |
|
|
//+------------------------------------------------------------------+
|
|
void CLSFitStateShell::SetNeedFGH(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_needfgh=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable xupdated |
|
|
//+------------------------------------------------------------------+
|
|
bool CLSFitStateShell::GetXUpdated(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_xupdated);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable xupdated |
|
|
//+------------------------------------------------------------------+
|
|
void CLSFitStateShell::SetXUpdated(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_xupdated=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable f |
|
|
//+------------------------------------------------------------------+
|
|
double CLSFitStateShell::GetF(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_f);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable f |
|
|
//+------------------------------------------------------------------+
|
|
void CLSFitStateShell::SetF(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_f=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CLSFitState *CLSFitStateShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Least squares fitting |
|
|
//+------------------------------------------------------------------+
|
|
class CLSFit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void Spline1DFitInternal(const int st,double &cx[],double &cy[],double &cw[],const int n,double &cxc[],double &cyc[],int &dc[],const int k,const int m,int &info,CSpline1DInterpolant &s,CSpline1DFitReport &rep);
|
|
static void LSFitLinearInternal(double &y[],double &w[],CMatrixDouble &fmatrix,const int n,const int m,int &info,double &c[],CLSFitReport &rep);
|
|
static void LSFitClearRequestFields(CLSFitState &state);
|
|
static void BarycentricCalcBasis(CBarycentricInterpolant &b,const double t,double &y[]);
|
|
static void InternalChebyshevFit(double &x[],double &y[],double &w[],const int n,double &cxc[],double &cyc[],int &dc[],const int k,const int m,int &info,double &c[],CLSFitReport &rep);
|
|
static void BarycentricFitWCFixedD(double &cx[],double &cy[],double &cw[],const int n,double &cxc[],double &cyc[],int &dc[],const int k,const int m,const int d,int &info,CBarycentricInterpolant &b,CBarycentricFitReport &rep);
|
|
//--- auxiliary functions for LSFitIteration
|
|
static void Func_lbl_rcomm(CLSFitState &state,int n,int m,int k,int i,int j,double v,double vv,double relcnt);
|
|
static bool Func_lbl_7(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
|
|
static bool Func_lbl_11(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
|
|
static bool Func_lbl_14(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
|
|
static bool Func_lbl_16(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
|
|
static bool Func_lbl_21(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
|
|
static bool Func_lbl_24(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
|
|
static bool Func_lbl_26(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
|
|
static bool Func_lbl_29(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
|
|
static bool Func_lbl_31(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
|
|
static bool Func_lbl_38(CLSFitState &state,int &n,int &m,int &k,int &i,int &j,double &v,double &vv,double &relcnt);
|
|
public:
|
|
//--- class constant
|
|
static const int m_rfsmax;
|
|
//--- constructor, destructor
|
|
CLSFit(void);
|
|
~CLSFit(void);
|
|
//--- public methods
|
|
static void PolynomialFit(double &x[],double &y[],const int n,const int m,int &info,CBarycentricInterpolant &p,CPolynomialFitReport &rep);
|
|
static void PolynomialFitWC(double &cx[],double &cy[],double &cw[],const int n,double &cxc[],double &cyc[],int &dc[],const int k,const int m,int &info,CBarycentricInterpolant &p,CPolynomialFitReport &rep);
|
|
static void BarycentricFitFloaterHormannWC(double &x[],double &y[],double &w[],const int n,double &xc[],double &yc[],int &dc[],const int k,const int m,int &info,CBarycentricInterpolant &b,CBarycentricFitReport &rep);
|
|
static void BarycentricFitFloaterHormann(double &x[],double &y[],const int n,const int m,int &info,CBarycentricInterpolant &b,CBarycentricFitReport &rep);
|
|
static void Spline1DFitPenalized(double &cx[],double &cy[],const int n,const int m,const double rho,int &info,CSpline1DInterpolant &s,CSpline1DFitReport &rep);
|
|
static void Spline1DFitPenalizedW(double &cx[],double &cy[],double &cw[],const int n,const int m,double rho,int &info,CSpline1DInterpolant &s,CSpline1DFitReport &rep);
|
|
static void Spline1DFitCubicWC(double &x[],double &y[],double &w[],const int n,double &xc[],double &yc[],int &dc[],const int k,const int m,int &info,CSpline1DInterpolant &s,CSpline1DFitReport &rep);
|
|
static void Spline1DFitHermiteWC(double &x[],double &y[],double &w[],const int n,double &xc[],double &yc[],int &dc[],const int k,const int m,int &info,CSpline1DInterpolant &s,CSpline1DFitReport &rep);
|
|
static void Spline1DFitCubic(double &x[],double &y[],const int n,const int m,int &info,CSpline1DInterpolant &s,CSpline1DFitReport &rep);
|
|
static void Spline1DFitHermite(double &x[],double &y[],const int n,const int m,int &info,CSpline1DInterpolant &s,CSpline1DFitReport &rep);
|
|
static void LSFitLinearW(double &y[],double &w[],CMatrixDouble &fmatrix,const int n,const int m,int &info,double &c[],CLSFitReport &rep);
|
|
static void LSFitLinearWC(double &cy[],double &w[],CMatrixDouble &fmatrix,CMatrixDouble &ccmatrix,const int n,const int m,const int k,int &info,double &c[],CLSFitReport &rep);
|
|
static void LSFitLinear(double &y[],CMatrixDouble &fmatrix,const int n,const int m,int &info,double &c[],CLSFitReport &rep);
|
|
static void LSFitLinearC(double &cy[],CMatrixDouble &fmatrix,CMatrixDouble &cmatrix,const int n,const int m,const int k,int &info,double &c[],CLSFitReport &rep);
|
|
static void LSFitCreateWF(CMatrixDouble &x,double &y[],double &w[],double &c[],const int n,const int m,const int k,const double diffstep,CLSFitState &state);
|
|
static void LSFitCreateF(CMatrixDouble &x,double &y[],double &c[],const int n,const int m,const int k,const double diffstep,CLSFitState &state);
|
|
static void LSFitCreateWFG(CMatrixDouble &x,double &y[],double &w[],double &c[],const int n,const int m,const int k,bool cheapfg,CLSFitState &state);
|
|
static void LSFitCreateFG(CMatrixDouble &x,double &y[],double &c[],const int n,const int m,const int k,const bool cheapfg,CLSFitState &state);
|
|
static void LSFitCreateWFGH(CMatrixDouble &x,double &y[],double &w[],double &c[],const int n,const int m,const int k,CLSFitState &state);
|
|
static void LSFitCreateFGH(CMatrixDouble &x,double &y[],double &c[],const int n,const int m,const int k,CLSFitState &state);
|
|
static void LSFitSetCond(CLSFitState &state,const double epsf,const double epsx,const int maxits);
|
|
static void LSFitSetStpMax(CLSFitState &state,const double stpmax);
|
|
static void LSFitSetXRep(CLSFitState &state,const bool needxrep);
|
|
static void LSFitSetScale(CLSFitState &state,double &s[]);
|
|
static void LSFitSetBC(CLSFitState &state,double &bndl[],double &bndu[]);
|
|
static void LSFitResults(CLSFitState &state,int &info,double &c[],CLSFitReport &rep);
|
|
static void LSFitScaleXY(double &x[],double &y[],double &w[],const int n,double &xc[],double &yc[],int &dc[],const int k,double &xa,double &xb,double &sa,double &sb,double &xoriginal[],double &yoriginal[]);
|
|
static bool LSFitIteration(CLSFitState &state);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize constant |
|
|
//+------------------------------------------------------------------+
|
|
const int CLSFit::m_rfsmax=10;
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CLSFit::CLSFit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CLSFit::~CLSFit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Fitting by polynomials in barycentric form. This function |
|
|
//| provides simple unterface for unconstrained unweighted fitting. |
|
|
//| See PolynomialFitWC() if you need constrained fitting. |
|
|
//| Task is linear, so linear least squares solver is used. |
|
|
//| Complexity of this computational scheme is O(N*M^2), mostly |
|
|
//| dominated by least squares solver |
|
|
//| SEE ALSO: |
|
|
//| PolynomialFitWC() |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - points, array[0..N-1]. |
|
|
//| Y - function values, array[0..N-1]. |
|
|
//| N - number of points, N>0 |
|
|
//| * if given, only leading N elements of X/Y are used |
|
|
//| * if not given, automatically determined from sizes |
|
|
//| of X/Y |
|
|
//| M - number of basis functions (= polynomial_degree + 1), |
|
|
//| M>=1 |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Info- same format as in LSFitLinearW() subroutine: |
|
|
//| * Info>0 task is solved |
|
|
//| * Info<=0 an error occured: |
|
|
//| -4 means inconvergence of internal SVD |
|
|
//| P - interpolant in barycentric form. |
|
|
//| Rep - report, same format as in LSFitLinearW() subroutine. |
|
|
//| Following fields are set: |
|
|
//| * RMSError rms error on the (X,Y). |
|
|
//| * AvgError average error on the (X,Y). |
|
|
//| * AvgRelError average relative error on the |
|
|
//| non-zero Y |
|
|
//| * MaxError maximum error |
|
|
//| NON-WEIGHTED ERRORS ARE CALCULATED |
|
|
//| NOTES: |
|
|
//| you can convert P from barycentric form to the power or |
|
|
//| Chebyshev basis with PolynomialBar2Pow() or |
|
|
//| PolynomialBar2Cheb() functions from POLINT subpackage. |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::PolynomialFit(double &x[],double &y[],const int n,
|
|
const int m,int &info,
|
|
CBarycentricInterpolant &p,
|
|
CPolynomialFitReport &rep)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create arrays
|
|
double w[];
|
|
double xc[];
|
|
double yc[];
|
|
int dc[];
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>0,__FUNCTION__+": M<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResizeAL(w,n);
|
|
//--- initialization
|
|
for(i=0;i<=n-1;i++)
|
|
w[i]=1;
|
|
//--- function call
|
|
PolynomialFitWC(x,y,w,n,xc,yc,dc,0,m,info,p,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Weighted fitting by polynomials in barycentric form, with |
|
|
//| constraints on function values or first derivatives. |
|
|
//| Small regularizing term is used when solving constrained tasks |
|
|
//| (to improve stability). |
|
|
//| Task is linear, so linear least squares solver is used. |
|
|
//| Complexity of this computational scheme is O(N*M^2), mostly |
|
|
//| dominated by least squares solver |
|
|
//| SEE ALSO: |
|
|
//| PolynomialFit() |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - points, array[0..N-1]. |
|
|
//| Y - function values, array[0..N-1]. |
|
|
//| W - weights, array[0..N-1] |
|
|
//| Each summand in square sum of approximation |
|
|
//| deviations from given values is multiplied by the |
|
|
//| square of corresponding weight. Fill it by 1's if you|
|
|
//| don't want to solve weighted task. |
|
|
//| N - number of points, N>0. |
|
|
//| * if given, only leading N elements of X/Y/W are used|
|
|
//| * if not given, automatically determined from sizes |
|
|
//| of X/Y/W |
|
|
//| XC - points where polynomial values/derivatives are |
|
|
//| constrained, array[0..K-1]. |
|
|
//| YC - values of constraints, array[0..K-1] |
|
|
//| DC - array[0..K-1], types of constraints: |
|
|
//| * DC[i]=0 means that P(XC[i])=YC[i] |
|
|
//| * DC[i]=1 means that P'(XC[i])=YC[i] |
|
|
//| SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS |
|
|
//| K - number of constraints, 0<=K<M. |
|
|
//| K=0 means no constraints (XC/YC/DC are not used in |
|
|
//| such cases) |
|
|
//| M - number of basis functions (= polynomial_degree + 1), |
|
|
//| M>=1 |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Info- same format as in LSFitLinearW() subroutine: |
|
|
//| * Info>0 task is solved |
|
|
//| * Info<=0 an error occured: |
|
|
//| -4 means inconvergence of internal SVD |
|
|
//| -3 means inconsistent constraints |
|
|
//| P - interpolant in barycentric form. |
|
|
//| Rep - report, same format as in LSFitLinearW() subroutine. |
|
|
//| Following fields are set: |
|
|
//| * RMSError rms error on the (X,Y). |
|
|
//| * AvgError average error on the (X,Y). |
|
|
//| * AvgRelError average relative error on the |
|
|
//| non-zero Y |
|
|
//| * MaxError maximum error |
|
|
//| NON-WEIGHTED ERRORS ARE CALCULATED |
|
|
//| IMPORTANT: |
|
|
//| this subroitine doesn't calculate task's condition number |
|
|
//| for K<>0. |
|
|
//| NOTES: |
|
|
//| you can convert P from barycentric form to the power or |
|
|
//| Chebyshev basis with PolynomialBar2Pow() or |
|
|
//| PolynomialBar2Cheb() functions from POLINT subpackage. |
|
|
//| SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES: |
|
|
//| Setting constraints can lead to undesired results, like |
|
|
//| ill-conditioned behavior, or inconsistency being detected. |
|
|
//| From the other side, it allows us to improve quality of the fit. |
|
|
//| Here we summarize our experience with constrained regression |
|
|
//| splines: |
|
|
//| * even simple constraints can be inconsistent, see Wikipedia |
|
|
//| article on this subject: |
|
|
//| http://en.wikipedia.org/wiki/Birkhoff_interpolation |
|
|
//| * the greater is M (given fixed constraints), the more chances |
|
|
//| that constraints will be consistent |
|
|
//| * in the general case, consistency of constraints is NOT |
|
|
//| GUARANTEED. |
|
|
//| * in the one special cases, however, we can guarantee |
|
|
//| consistency. This case is: M>1 and constraints on the |
|
|
//| function values (NOT DERIVATIVES) |
|
|
//| Our final recommendation is to use constraints WHEN AND ONLY when|
|
|
//| you can't solve your task without them. Anything beyond special |
|
|
//| cases given above is not guaranteed and may result in |
|
|
//| inconsistency. |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::PolynomialFitWC(double &cx[],double &cy[],double &cw[],
|
|
const int n,double &cxc[],double &cyc[],
|
|
int &dc[],const int k,const int m,
|
|
int &info,CBarycentricInterpolant &p,
|
|
CPolynomialFitReport &rep)
|
|
{
|
|
//--- create variables
|
|
double xa=0;
|
|
double xb=0;
|
|
double sa=0;
|
|
double sb=0;
|
|
int i=0;
|
|
int j=0;
|
|
double u=0;
|
|
double v=0;
|
|
double s=0;
|
|
int relcnt=0;
|
|
//--- create arrays
|
|
double xoriginal[];
|
|
double yoriginal[];
|
|
double y2[];
|
|
double w2[];
|
|
double tmp[];
|
|
double tmp2[];
|
|
double bx[];
|
|
double by[];
|
|
double bw[];
|
|
double x[];
|
|
double y[];
|
|
double w[];
|
|
double xc[];
|
|
double yc[];
|
|
//--- object of class
|
|
CLSFitReport lrep;
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
ArrayCopy(w,cw);
|
|
ArrayCopy(xc,cxc);
|
|
ArrayCopy(yc,cyc);
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>0,__FUNCTION__+": M<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k>=0,__FUNCTION__+": K<0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k<m,__FUNCTION__+": K>=M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": Length(W)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(xc)>=k,__FUNCTION__+": Length(XC)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(yc)>=k,__FUNCTION__+": Length(YC)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(dc)>=k,__FUNCTION__+": Length(DC)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(xc,k),__FUNCTION__+": XC contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(yc,k),__FUNCTION__+": YC contains infinite or NaN values!"))
|
|
return;
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(dc[i]==0 || dc[i]==1,__FUNCTION__+": one of DC[] is not 0 or 1!"))
|
|
return;
|
|
}
|
|
//--- Scale X,Y,XC,YC.
|
|
//--- Solve scaled problem using internal Chebyshev fitting function.
|
|
LSFitScaleXY(x,y,w,n,xc,yc,dc,k,xa,xb,sa,sb,xoriginal,yoriginal);
|
|
InternalChebyshevFit(x,y,w,n,xc,yc,dc,k,m,info,tmp,lrep);
|
|
//--- check
|
|
if(info<0)
|
|
return;
|
|
//--- Generate barycentric model and scale it
|
|
//--- * BX,BY store barycentric model nodes
|
|
//--- * FMatrix is reused (remember - it is at least MxM,what we need)
|
|
//--- Model intialization is done in O(M^2). In principle,it can be
|
|
//--- done in O(M*log(M)),but before it we solved task with O(N*M^2)
|
|
//--- complexity,so it is only a small amount of total time spent.
|
|
ArrayResizeAL(bx,m);
|
|
ArrayResizeAL(by,m);
|
|
ArrayResizeAL(bw,m);
|
|
ArrayResizeAL(tmp2,m);
|
|
s=1;
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
//--- check
|
|
if(m!=1)
|
|
u=MathCos(M_PI*i/(m-1));
|
|
else
|
|
u=0;
|
|
v=0;
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- check
|
|
if(j==0)
|
|
tmp2[j]=1;
|
|
else
|
|
{
|
|
//--- check
|
|
if(j==1)
|
|
tmp2[j]=u;
|
|
else
|
|
tmp2[j]=2*u*tmp2[j-1]-tmp2[j-2];
|
|
}
|
|
v=v+tmp[j]*tmp2[j];
|
|
}
|
|
//--- change values
|
|
bx[i]=u;
|
|
by[i]=v;
|
|
bw[i]=s;
|
|
//--- check
|
|
if(i==0 || i==m-1)
|
|
bw[i]=0.5*bw[i];
|
|
s=-s;
|
|
}
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(bx,by,bw,m,p);
|
|
//--- function call
|
|
CRatInt::BarycentricLinTransX(p,2/(xb-xa),-((xa+xb)/(xb-xa)));
|
|
//--- function call
|
|
CRatInt::BarycentricLinTransY(p,sb-sa,sa);
|
|
//--- Scale absolute errors obtained from LSFitLinearW.
|
|
//--- Relative error should be calculated separately
|
|
//--- (because of shifting/scaling of the task)
|
|
rep.m_taskrcond=lrep.m_taskrcond;
|
|
rep.m_rmserror=lrep.m_rmserror*(sb-sa);
|
|
rep.m_avgerror=lrep.m_avgerror*(sb-sa);
|
|
rep.m_maxerror=lrep.m_maxerror*(sb-sa);
|
|
rep.m_avgrelerror=0;
|
|
relcnt=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(yoriginal[i]!=0.0)
|
|
{
|
|
rep.m_avgrelerror=rep.m_avgrelerror+MathAbs(CRatInt::BarycentricCalc(p,xoriginal[i])-yoriginal[i])/MathAbs(yoriginal[i]);
|
|
relcnt=relcnt+1;
|
|
}
|
|
}
|
|
//--- check
|
|
if(relcnt!=0)
|
|
rep.m_avgrelerror=rep.m_avgrelerror/relcnt;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Weghted rational least squares fitting using Floater-Hormann |
|
|
//| rational functions with optimal D chosen from [0,9], with |
|
|
//| constraints and individual weights. |
|
|
//| Equidistant grid with M node on [min(x),max(x)] is used to build |
|
|
//| basis functions. Different values of D are tried, optimal D |
|
|
//| (least WEIGHTED root mean square error) is chosen. Task is |
|
|
//| linear, so linear least squares solver is used. Complexity of |
|
|
//| this computational scheme is O(N*M^2) (mostly dominated by the |
|
|
//| least squares solver). |
|
|
//| SEE ALSO |
|
|
//| * BarycentricFitFloaterHormann(), "lightweight" fitting without |
|
|
//| invididual weights and constraints. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - points, array[0..N-1]. |
|
|
//| Y - function values, array[0..N-1]. |
|
|
//| W - weights, array[0..N-1] |
|
|
//| Each summand in square sum of approximation |
|
|
//| deviations from given values is multiplied by the |
|
|
//| square of corresponding weight. Fill it by 1's if |
|
|
//| you don't want to solve weighted task. |
|
|
//| N - number of points, N>0. |
|
|
//| XC - points where function values/derivatives are |
|
|
//| constrained, array[0..K-1]. |
|
|
//| YC - values of constraints, array[0..K-1] |
|
|
//| DC - array[0..K-1], types of constraints: |
|
|
//| * DC[i]=0 means that S(XC[i])=YC[i] |
|
|
//| * DC[i]=1 means that S'(XC[i])=YC[i] |
|
|
//| SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS |
|
|
//| K - number of constraints, 0<=K<M. |
|
|
//| K=0 means no constraints (XC/YC/DC are not used in |
|
|
//| such cases) |
|
|
//| M - number of basis functions ( = number_of_nodes), |
|
|
//| M>=2. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Info- same format as in LSFitLinearWC() subroutine. |
|
|
//| * Info>0 task is solved |
|
|
//| * Info<=0 an error occured: |
|
|
//| -4 means inconvergence of internal SVD |
|
|
//| -3 means inconsistent constraints |
|
|
//| -1 means another errors in parameters |
|
|
//| passed (N<=0, for example) |
|
|
//| B - barycentric interpolant. |
|
|
//| Rep - report, same format as in LSFitLinearWC() subroutine.|
|
|
//| Following fields are set: |
|
|
//| * DBest best value of the D parameter |
|
|
//| * RMSError rms error on the (X,Y). |
|
|
//| * AvgError average error on the (X,Y). |
|
|
//| * AvgRelError average relative error on the |
|
|
//| non-zero Y |
|
|
//| * MaxError maximum error |
|
|
//| NON-WEIGHTED ERRORS ARE CALCULATED |
|
|
//| IMPORTANT: |
|
|
//| this subroutine doesn't calculate task's condition number |
|
|
//| for K<>0. |
|
|
//| SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES: |
|
|
//| Setting constraints can lead to undesired results, like |
|
|
//| ill-conditioned behavior, or inconsistency being detected. From |
|
|
//| the other side, it allows us to improve quality of the fit. Here |
|
|
//| we summarize our experience with constrained barycentric |
|
|
//| interpolants: |
|
|
//| * excessive constraints can be inconsistent. Floater-Hormann |
|
|
//| basis functions aren't as flexible as splines (although they |
|
|
//| are very smooth). |
|
|
//| * the more evenly constraints are spread across [min(x),max(x)], |
|
|
//| the more chances that they will be consistent |
|
|
//| * the greater is M (given fixed constraints), the more chances |
|
|
//| that constraints will be consistent |
|
|
//| * in the general case, consistency of constraints IS NOT |
|
|
//| GUARANTEED. |
|
|
//| * in the several special cases, however, we CAN guarantee |
|
|
//| consistency. |
|
|
//| * one of this cases is constraints on the function VALUES at the |
|
|
//| interval boundaries. Note that consustency of the constraints |
|
|
//| on the function DERIVATIVES is NOT guaranteed (you can use in |
|
|
//| such cases cubic splines which are more flexible). |
|
|
//| * another special case is ONE constraint on the function value |
|
|
//| (OR, but not AND, derivative) anywhere in the interval |
|
|
//| Our final recommendation is to use constraints WHEN AND ONLY |
|
|
//| WHEN you can't solve your task without them. Anything beyond |
|
|
//| special cases given above is not guaranteed and may result in |
|
|
//| inconsistency. |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::BarycentricFitFloaterHormannWC(double &x[],double &y[],
|
|
double &w[],const int n,
|
|
double &xc[],double &yc[],
|
|
int &dc[],const int k,
|
|
const int m,int &info,
|
|
CBarycentricInterpolant &b,
|
|
CBarycentricFitReport &rep)
|
|
{
|
|
//--- create variables
|
|
int d=0;
|
|
int i=0;
|
|
double wrmscur=0;
|
|
double wrmsbest=0;
|
|
int locinfo=0;
|
|
//--- objects of classes
|
|
CBarycentricInterpolant locb;
|
|
CBarycentricFitReport locrep;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>0,__FUNCTION__+": M<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k>=0,__FUNCTION__+": K<0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k<m,__FUNCTION__+": K>=M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": Length(W)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(xc)>=k,__FUNCTION__+": Length(XC)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(yc)>=k,__FUNCTION__+": Length(YC)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(dc)>=k,__FUNCTION__+": Length(DC)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(xc,k),__FUNCTION__+": XC contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(yc,k),__FUNCTION__+": YC contains infinite or NaN values!"))
|
|
return;
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(dc[i]==0 || dc[i]==1,__FUNCTION__+": one of DC[] is not 0 or 1!"))
|
|
return;
|
|
}
|
|
//--- Find optimal D
|
|
//--- Info is -3 by default (degenerate constraints).
|
|
//--- If LocInfo will always be equal to -3,Info will remain equal to -3.
|
|
//--- If at least once LocInfo will be -4,Info will be -4.
|
|
wrmsbest=CMath::m_maxrealnumber;
|
|
rep.m_dbest=-1;
|
|
info=-3;
|
|
//--- calculation
|
|
for(d=0;d<=MathMin(9,n-1);d++)
|
|
{
|
|
//--- function call
|
|
BarycentricFitWCFixedD(x,y,w,n,xc,yc,dc,k,m,d,locinfo,locb,locrep);
|
|
//--- check
|
|
if(!CAp::Assert((locinfo==-4 || locinfo==-3) || locinfo>0,__FUNCTION__+": unexpected result from BarycentricFitWCFixedD!"))
|
|
return;
|
|
//--- check
|
|
if(locinfo>0)
|
|
{
|
|
//--- Calculate weghted RMS
|
|
wrmscur=0;
|
|
for(i=0;i<=n-1;i++)
|
|
wrmscur=wrmscur+CMath::Sqr(w[i]*(y[i]-CRatInt::BarycentricCalc(locb,x[i])));
|
|
wrmscur=MathSqrt(wrmscur/n);
|
|
//--- check
|
|
if(wrmscur<wrmsbest || rep.m_dbest<0)
|
|
{
|
|
//--- function call
|
|
CRatInt::BarycentricCopy(locb,b);
|
|
//--- change values
|
|
rep.m_dbest=d;
|
|
info=1;
|
|
rep.m_rmserror=locrep.m_rmserror;
|
|
rep.m_avgerror=locrep.m_avgerror;
|
|
rep.m_avgrelerror=locrep.m_avgrelerror;
|
|
rep.m_maxerror=locrep.m_maxerror;
|
|
rep.m_taskrcond=locrep.m_taskrcond;
|
|
wrmsbest=wrmscur;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(locinfo!=-3 && info<0)
|
|
info=locinfo;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Rational least squares fitting using Floater-Hormann rational |
|
|
//| functions with optimal D chosen from [0,9]. |
|
|
//| Equidistant grid with M node on [min(x),max(x)] is used to build |
|
|
//| basis functions. Different values of D are tried, optimal D |
|
|
//| (least root mean square error) is chosen. Task is linear, so |
|
|
//| linear least squares solver is used. Complexity of this |
|
|
//| computational scheme is O(N*M^2) (mostly dominated by the least |
|
|
//| squares solver). |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - points, array[0..N-1]. |
|
|
//| Y - function values, array[0..N-1]. |
|
|
//| N - number of points, N>0. |
|
|
//| M - number of basis functions ( = number_of_nodes), M>=2.|
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Info- same format as in LSFitLinearWC() subroutine. |
|
|
//| * Info>0 task is solved |
|
|
//| * Info<=0 an error occured: |
|
|
//| -4 means inconvergence of internal SVD |
|
|
//| -3 means inconsistent constraints |
|
|
//| B - barycentric interpolant. |
|
|
//| Rep - report, same format as in LSFitLinearWC() subroutine.|
|
|
//| Following fields are set: |
|
|
//| * DBest best value of the D parameter |
|
|
//| * RMSError rms error on the (X,Y). |
|
|
//| * AvgError average error on the (X,Y). |
|
|
//| * AvgRelError average relative error on the |
|
|
//| non-zero Y |
|
|
//| * MaxError maximum error |
|
|
//| NON-WEIGHTED ERRORS ARE CALCULATED |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::BarycentricFitFloaterHormann(double &x[],double &y[],
|
|
const int n,const int m,
|
|
int &info,CBarycentricInterpolant &b,
|
|
CBarycentricFitReport &rep)
|
|
{
|
|
//--- create arrays
|
|
double w[];
|
|
double xc[];
|
|
double yc[];
|
|
int dc[];
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,__FUNCTION__+": N<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>0,__FUNCTION__+": M<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResizeAL(w,n);
|
|
//--- initialization
|
|
for(i=0;i<=n-1;i++)
|
|
w[i]=1;
|
|
//--- function call
|
|
BarycentricFitFloaterHormannWC(x,y,w,n,xc,yc,dc,0,m,info,b,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Rational least squares fitting using Floater-Hormann rational |
|
|
//| functions with optimal D chosen from [0,9]. |
|
|
//| Equidistant grid with M node on [min(x),max(x)] is used to build |
|
|
//| basis functions. Different values of D are tried, optimal D |
|
|
//| (least root mean square error) is chosen. Task is linear, so |
|
|
//| linear least squares solver is used. Complexity of this |
|
|
//| computational scheme is O(N*M^2) (mostly dominated by the least |
|
|
//| squares solver). |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - points, array[0..N-1]. |
|
|
//| Y - function values, array[0..N-1]. |
|
|
//| N - number of points, N>0. |
|
|
//| M - number of basis functions ( = number_of_nodes), M>=2.|
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Info- same format as in LSFitLinearWC() subroutine. |
|
|
//| * Info>0 task is solved |
|
|
//| * Info<=0 an error occured: |
|
|
//| -4 means inconvergence of internal SVD |
|
|
//| -3 means inconsistent constraints |
|
|
//| B - barycentric interpolant. |
|
|
//| Rep - report, same format as in LSFitLinearWC() subroutine.|
|
|
//| Following fields are set: |
|
|
//| * DBest best value of the D parameter |
|
|
//| * RMSError rms error on the (X,Y). |
|
|
//| * AvgError average error on the (X,Y). |
|
|
//| * AvgRelError average relative error on the |
|
|
//| non-zero Y |
|
|
//| * MaxError maximum error |
|
|
//| NON-WEIGHTED ERRORS ARE CALCULATED |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::Spline1DFitPenalized(double &cx[],double &cy[],
|
|
const int n,const int m,
|
|
const double rho,int &info,
|
|
CSpline1DInterpolant &s,
|
|
CSpline1DFitReport &rep)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create arrays
|
|
double w[];
|
|
double x[];
|
|
double y[];
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=4,__FUNCTION__+": M<4!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(rho),__FUNCTION__+": Rho is infinite!"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResizeAL(w,n);
|
|
//--- initialization
|
|
for(i=0;i<=n-1;i++)
|
|
w[i]=1;
|
|
//--- function call
|
|
Spline1DFitPenalizedW(x,y,w,n,m,rho,info,s,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Weighted fitting by penalized cubic spline. |
|
|
//| Equidistant grid with M nodes on [min(x,xc),max(x,xc)] is used to|
|
|
//| build basis functions. Basis functions are cubic splines with |
|
|
//| natural boundary conditions. Problem is regularized by adding |
|
|
//| non-linearity penalty to the usual least squares penalty |
|
|
//| function: |
|
|
//| S(x) = arg min { LS + P }, where |
|
|
//| LS = SUM { w[i]^2*(y[i] - S(x[i]))^2 } - least squares |
|
|
//| penalty |
|
|
//| P = C*10^rho*integral{ S''(x)^2*dx } - non-linearity |
|
|
//| penalty |
|
|
//| rho - tunable constant given by user |
|
|
//| C - automatically determined scale parameter, |
|
|
//| makes penalty invariant with respect to scaling of X, |
|
|
//| Y, W. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - points, array[0..N-1]. |
|
|
//| Y - function values, array[0..N-1]. |
|
|
//| W - weights, array[0..N-1] |
|
|
//| Each summand in square sum of approximation |
|
|
//| deviations from given values is multiplied by the |
|
|
//| square of corresponding weight. Fill it by 1's if |
|
|
//| you don't want to solve weighted problem. |
|
|
//| N - number of points (optional): |
|
|
//| * N>0 |
|
|
//| * if given, only first N elements of X/Y/W are |
|
|
//| processed |
|
|
//| * if not given, automatically determined from X/Y/W |
|
|
//| sizes |
|
|
//| M - number of basis functions ( = number_of_nodes), M>=4.|
|
|
//| Rho - regularization constant passed by user. It penalizes |
|
|
//| nonlinearity in the regression spline. It is |
|
|
//| logarithmically scaled, i.e. actual value of |
|
|
//| regularization constant is calculated as 10^Rho. It |
|
|
//| is automatically scaled so that: |
|
|
//| * Rho=2.0 corresponds to moderate amount of |
|
|
//| nonlinearity |
|
|
//| * generally, it should be somewhere in the |
|
|
//| [-8.0,+8.0] |
|
|
//| If you do not want to penalize nonlineary, |
|
|
//| pass small Rho. Values as low as -15 should work. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Info- same format as in LSFitLinearWC() subroutine. |
|
|
//| * Info>0 task is solved |
|
|
//| * Info<=0 an error occured: |
|
|
//| -4 means inconvergence of internal SVD |
|
|
//| or Cholesky decomposition; problem |
|
|
//| may be too ill-conditioned (very |
|
|
//| rare) |
|
|
//| S - spline interpolant. |
|
|
//| Rep - Following fields are set: |
|
|
//| * RMSError rms error on the (X,Y). |
|
|
//| * AvgError average error on the (X,Y). |
|
|
//| * AvgRelError average relative error on the |
|
|
//| non-zero Y |
|
|
//| * MaxError maximum error |
|
|
//| NON-WEIGHTED ERRORS ARE CALCULATED |
|
|
//| IMPORTANT: |
|
|
//| this subroitine doesn't calculate task's condition number |
|
|
//| for K<>0. |
|
|
//| NOTE 1: additional nodes are added to the spline outside of the |
|
|
//| fitting interval to force linearity when x<min(x,xc) or |
|
|
//| x>max(x,xc). It is done for consistency - we penalize |
|
|
//| non-linearity at [min(x,xc),max(x,xc)], so it is natural to |
|
|
//| force linearity outside of this interval. |
|
|
//| NOTE 2: function automatically sorts points, so caller may pass |
|
|
//| unsorted array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::Spline1DFitPenalizedW(double &cx[],double &cy[],
|
|
double &cw[],const int n,
|
|
const int m,double rho,
|
|
int &info,CSpline1DInterpolant &s,
|
|
CSpline1DFitReport &rep)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int b=0;
|
|
double v=0;
|
|
double relcnt=0;
|
|
double xa=0;
|
|
double xb=0;
|
|
double sa=0;
|
|
double sb=0;
|
|
double pdecay=0;
|
|
double tdecay=0;
|
|
double fdmax=0;
|
|
double admax=0;
|
|
double fa=0;
|
|
double ga=0;
|
|
double fb=0;
|
|
double gb=0;
|
|
double lambdav=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double xoriginal[];
|
|
double yoriginal[];
|
|
double fcolumn[];
|
|
double y2[];
|
|
double w2[];
|
|
double xc[];
|
|
double yc[];
|
|
int dc[];
|
|
double bx[];
|
|
double by[];
|
|
double bd1[];
|
|
double bd2[];
|
|
double tx[];
|
|
double ty[];
|
|
double td[];
|
|
double rightpart[];
|
|
double c[];
|
|
double tmp0[];
|
|
double x[];
|
|
double y[];
|
|
double w[];
|
|
//--- create matrix
|
|
CMatrixDouble fmatrix;
|
|
CMatrixDouble amatrix;
|
|
CMatrixDouble d2matrix;
|
|
CMatrixDouble nmatrix;
|
|
//--- objects of classes
|
|
CSpline1DInterpolant bs;
|
|
CFblsLinCgState cgstate;
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
ArrayCopy(w,cw);
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=4,__FUNCTION__+": M<4!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": Length(W)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(rho),__FUNCTION__+": Rho is infinite!"))
|
|
return;
|
|
//--- Prepare LambdaV
|
|
v=-(MathLog(CMath::m_machineepsilon)/MathLog(10));
|
|
//--- check
|
|
if(rho<-v)
|
|
rho=-v;
|
|
//--- check
|
|
if(rho>v)
|
|
rho=v;
|
|
lambdav=MathPow(10,rho);
|
|
//--- Sort X,Y,W
|
|
CSpline1D::HeapSortDPoints(x,y,w,n);
|
|
//--- Scale X,Y,XC,YC
|
|
LSFitScaleXY(x,y,w,n,xc,yc,dc,0,xa,xb,sa,sb,xoriginal,yoriginal);
|
|
//--- Allocate space
|
|
fmatrix.Resize(n,m);
|
|
amatrix.Resize(m,m);
|
|
d2matrix.Resize(m,m);
|
|
ArrayResizeAL(bx,m);
|
|
ArrayResizeAL(by,m);
|
|
ArrayResizeAL(fcolumn,n);
|
|
nmatrix.Resize(m,m);
|
|
ArrayResizeAL(rightpart,m);
|
|
ArrayResizeAL(tmp0,MathMax(m,n));
|
|
ArrayResizeAL(c,m);
|
|
//--- Fill:
|
|
//--- * FMatrix by values of basis functions
|
|
//--- * TmpAMatrix by second derivatives of I-th function at J-th point
|
|
//--- * CMatrix by constraints
|
|
fdmax=0;
|
|
for(b=0;b<=m-1;b++)
|
|
{
|
|
//--- Prepare I-th basis function
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
bx[j]=(double)(2*j)/(double)(m-1)-1;
|
|
by[j]=0;
|
|
}
|
|
by[b]=1;
|
|
//--- function call
|
|
CSpline1D::Spline1DGridDiff2Cubic(bx,by,m,2,0.0,2,0.0,bd1,bd2);
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(bx,by,m,2,0.0,2,0.0,bs);
|
|
//--- Calculate B-th column of FMatrix
|
|
//--- Update FDMax (maximum column norm)
|
|
CSpline1D::Spline1DConvCubic(bx,by,m,2,0.0,2,0.0,x,n,fcolumn);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
fmatrix[i_].Set(b,fcolumn[i_]);
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+CMath::Sqr(w[i]*fcolumn[i]);
|
|
fdmax=MathMax(fdmax,v);
|
|
//--- Fill temporary with second derivatives of basis function
|
|
for(i_=0;i_<=m-1;i_++)
|
|
d2matrix[b].Set(i_,bd2[i_]);
|
|
}
|
|
//--- * calculate penalty matrix A
|
|
//--- * calculate max of diagonal elements of A
|
|
//--- * calculate PDecay - coefficient before penalty matrix
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=i;j<=m-1;j++)
|
|
{
|
|
//--- calculate integral(B_i''*B_j'') where B_i and B_j are
|
|
//--- i-th and j-th basis splines.
|
|
//--- B_i and B_j are piecewise linear functions.
|
|
v=0;
|
|
for(b=0;b<=m-2;b++)
|
|
{
|
|
//--- change values
|
|
fa=d2matrix[i][b];
|
|
fb=d2matrix[i][b+1];
|
|
ga=d2matrix[j][b];
|
|
gb=d2matrix[j][b+1];
|
|
v=v+(bx[b+1]-bx[b])*(fa*ga+(fa*(gb-ga)+ga*(fb-fa))/2+(fb-fa)*(gb-ga)/3);
|
|
}
|
|
amatrix[i].Set(j,v);
|
|
amatrix[j].Set(i,v);
|
|
}
|
|
}
|
|
//--- change values
|
|
admax=0;
|
|
for(i=0;i<=m-1;i++)
|
|
admax=MathMax(admax,MathAbs(amatrix[i][i]));
|
|
pdecay=lambdav*fdmax/admax;
|
|
//--- Calculate TDecay for Tikhonov regularization
|
|
tdecay=fdmax*(1+pdecay)*10*CMath::m_machineepsilon;
|
|
//--- Prepare system
|
|
//--- NOTE: FMatrix is spoiled during this process
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v=w[i];
|
|
for(i_=0;i_<=m-1;i_++)
|
|
fmatrix[i].Set(i_,v*fmatrix[i][i_]);
|
|
}
|
|
//--- function call
|
|
CAblas::RMatrixGemm(m,m,n,1.0,fmatrix,0,0,1,fmatrix,0,0,0,0.0,nmatrix,0,0);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
nmatrix[i].Set(j,nmatrix[i][j]+pdecay*amatrix[i][j]);
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
nmatrix[i].Set(i,nmatrix[i][i]+tdecay);
|
|
for(i=0;i<=m-1;i++)
|
|
rightpart[i]=0;
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v=y[i]*w[i];
|
|
for(i_=0;i_<=m-1;i_++)
|
|
rightpart[i_]=rightpart[i_]+v*fmatrix[i][i_];
|
|
}
|
|
//--- Solve system
|
|
if(!CTrFac::SPDMatrixCholesky(nmatrix,m,true))
|
|
{
|
|
info=-4;
|
|
return;
|
|
}
|
|
//--- function call
|
|
CFbls::FblsCholeskySolve(nmatrix,1.0,m,true,rightpart,tmp0);
|
|
//--- copy
|
|
for(i_=0;i_<=m-1;i_++)
|
|
c[i_]=rightpart[i_];
|
|
//--- add nodes to force linearity outside of the fitting interval
|
|
CSpline1D::Spline1DGridDiffCubic(bx,c,m,2,0.0,2,0.0,bd1);
|
|
//--- allocation
|
|
ArrayResizeAL(tx,m+2);
|
|
ArrayResizeAL(ty,m+2);
|
|
ArrayResizeAL(td,m+2);
|
|
//--- copy
|
|
i1_=-1;
|
|
for(i_=1;i_<=m;i_++)
|
|
tx[i_]=bx[i_+i1_];
|
|
i1_=-1;
|
|
for(i_=1;i_<=m;i_++)
|
|
ty[i_]=rightpart[i_+i1_];
|
|
i1_=-1;
|
|
for(i_=1;i_<=m;i_++)
|
|
td[i_]=bd1[i_+i1_];
|
|
//--- change values
|
|
tx[0]=tx[1]-(tx[2]-tx[1]);
|
|
ty[0]=ty[1]-td[1]*(tx[2]-tx[1]);
|
|
td[0]=td[1];
|
|
tx[m+1]=tx[m]+(tx[m]-tx[m-1]);
|
|
ty[m+1]=ty[m]+td[m]*(tx[m]-tx[m-1]);
|
|
td[m+1]=td[m];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildHermite(tx,ty,td,m+2,s);
|
|
//--- function call
|
|
CSpline1D::Spline1DLinTransX(s,2/(xb-xa),-((xa+xb)/(xb-xa)));
|
|
//--- function call
|
|
CSpline1D::Spline1DLinTransY(s,sb-sa,sa);
|
|
//--- change value
|
|
info=1;
|
|
//--- Fill report
|
|
rep.m_rmserror=0;
|
|
rep.m_avgerror=0;
|
|
rep.m_avgrelerror=0;
|
|
rep.m_maxerror=0;
|
|
relcnt=0;
|
|
//--- function call
|
|
CSpline1D::Spline1DConvCubic(bx,rightpart,m,2,0.0,2,0.0,x,n,fcolumn);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change values
|
|
v=(sb-sa)*fcolumn[i]+sa;
|
|
rep.m_rmserror=rep.m_rmserror+CMath::Sqr(v-yoriginal[i]);
|
|
rep.m_avgerror=rep.m_avgerror+MathAbs(v-yoriginal[i]);
|
|
//--- check
|
|
if(yoriginal[i]!=0.0)
|
|
{
|
|
rep.m_avgrelerror=rep.m_avgrelerror+MathAbs(v-yoriginal[i])/MathAbs(yoriginal[i]);
|
|
relcnt=relcnt+1;
|
|
}
|
|
rep.m_maxerror=MathMax(rep.m_maxerror,MathAbs(v-yoriginal[i]));
|
|
}
|
|
//--- change values
|
|
rep.m_rmserror=MathSqrt(rep.m_rmserror/n);
|
|
rep.m_avgerror=rep.m_avgerror/n;
|
|
//--- check
|
|
if(relcnt!=0.0)
|
|
rep.m_avgrelerror=rep.m_avgrelerror/relcnt;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Weighted fitting by cubic spline, with constraints on function |
|
|
//| values or derivatives. |
|
|
//| Equidistant grid with M-2 nodes on [min(x,xc),max(x,xc)] is used |
|
|
//| to build basis functions. Basis functions are cubic splines with |
|
|
//| continuous second derivatives and non-fixed first derivatives at |
|
|
//| interval ends. Small regularizing term is used when solving |
|
|
//| constrained tasks (to improve stability). |
|
|
//| Task is linear, so linear least squares solver is used. |
|
|
//| Complexity of this computational scheme is O(N*M^2), mostly |
|
|
//| dominated by least squares solver |
|
|
//| SEE ALSO |
|
|
//| Spline1DFitHermiteWC() - fitting by Hermite splines (more |
|
|
//| flexible, less smooth) |
|
|
//| Spline1DFitCubic() - "lightweight" fitting by cubic |
|
|
//| splines, without invididual |
|
|
//| weights and constraints |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - points, array[0..N-1]. |
|
|
//| Y - function values, array[0..N-1]. |
|
|
//| W - weights, array[0..N-1] |
|
|
//| Each summand in square sum of approximation |
|
|
//| deviations from given values is multiplied by the |
|
|
//| square of corresponding weight. Fill it by 1's if you|
|
|
//| don't want to solve weighted task. |
|
|
//| N - number of points (optional): |
|
|
//| * N>0 |
|
|
//| * if given, only first N elements of X/Y/W are |
|
|
//| processed |
|
|
//| * if not given, automatically determined from X/Y/W |
|
|
//| sizes |
|
|
//| XC - points where spline values/derivatives are |
|
|
//| constrained, array[0..K-1]. |
|
|
//| YC - values of constraints, array[0..K-1] |
|
|
//| DC - array[0..K-1], types of constraints: |
|
|
//| * DC[i]=0 means that S(XC[i])=YC[i] |
|
|
//| * DC[i]=1 means that S'(XC[i])=YC[i] |
|
|
//| SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS |
|
|
//| K - number of constraints (optional): |
|
|
//| * 0<=K<M. |
|
|
//| * K=0 means no constraints (XC/YC/DC are not used) |
|
|
//| * if given, only first K elements of XC/YC/DC are |
|
|
//| used |
|
|
//| * if not given, automatically determined from |
|
|
//| XC/YC/DC |
|
|
//| M - number of basis functions ( = number_of_nodes+2), |
|
|
//| M>=4. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Info- same format as in LSFitLinearWC() subroutine. |
|
|
//| * Info>0 task is solved |
|
|
//| * Info<=0 an error occured: |
|
|
//| -4 means inconvergence of internal SVD |
|
|
//| -3 means inconsistent constraints |
|
|
//| S - spline interpolant. |
|
|
//| Rep - report, same format as in LSFitLinearWC() subroutine.|
|
|
//| Following fields are set: |
|
|
//| * RMSError rms error on the (X,Y). |
|
|
//| * AvgError average error on the (X,Y). |
|
|
//| * AvgRelError average relative error on the |
|
|
//| non-zero Y |
|
|
//| * MaxError maximum error |
|
|
//| NON-WEIGHTED ERRORS ARE CALCULATED |
|
|
//| IMPORTANT: |
|
|
//| this subroitine doesn't calculate task's condition number |
|
|
//| for K<>0. |
|
|
//| ORDER OF POINTS |
|
|
//| Subroutine automatically sorts points, so caller may pass |
|
|
//| unsorted array. |
|
|
//| SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES: |
|
|
//| Setting constraints can lead to undesired results, like |
|
|
//| ill-conditioned behavior, or inconsistency being detected. From |
|
|
//| the other side, it allows us to improve quality of the fit. |
|
|
//| Here we summarize our experience with constrained regression |
|
|
//| splines: |
|
|
//| * excessive constraints can be inconsistent. Splines are |
|
|
//| piecewise cubic functions, and it is easy to create an |
|
|
//| example, where large number of constraints concentrated in |
|
|
//| small area will result in inconsistency. Just because spline |
|
|
//| is not flexible enough to satisfy all of them. And same |
|
|
//| constraints spread across the [min(x),max(x)] will be |
|
|
//| perfectly consistent. |
|
|
//| * the more evenly constraints are spread across [min(x),max(x)], |
|
|
//| the more chances that they will be consistent |
|
|
//| * the greater is M (given fixed constraints), the more chances |
|
|
//| that constraints will be consistent |
|
|
//| * in the general case, consistency of constraints IS NOT |
|
|
//| GUARANTEED. |
|
|
//| * in the several special cases, however, we CAN guarantee |
|
|
//| consistency. |
|
|
//| * one of this cases is constraints on the function values |
|
|
//| AND/OR its derivatives at the interval boundaries. |
|
|
//| * another special case is ONE constraint on the function value |
|
|
//| (OR, but not AND, derivative) anywhere in the interval |
|
|
//| Our final recommendation is to use constraints WHEN AND ONLY WHEN|
|
|
//| you can't solve your task without them. Anything beyond special |
|
|
//| cases given above is not guaranteed and may result in |
|
|
//| inconsistency. |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::Spline1DFitCubicWC(double &x[],double &y[],double &w[],
|
|
const int n,double &xc[],double &yc[],
|
|
int &dc[],const int k,const int m,
|
|
int &info,CSpline1DInterpolant &s,
|
|
CSpline1DFitReport &rep)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=4,__FUNCTION__+": M<4!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k>=0,__FUNCTION__+": K<0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k<m,__FUNCTION__+": K>=M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": Length(W)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(xc)>=k,__FUNCTION__+": Length(XC)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(yc)>=k,__FUNCTION__+": Length(YC)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(dc)>=k,__FUNCTION__+": Length(DC)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(xc,k),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(yc,k),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(dc[i]==0 || dc[i]==1,__FUNCTION__+": DC[i] is neither 0 or 1!"))
|
|
return;
|
|
}
|
|
//--- function call
|
|
Spline1DFitInternal(0,x,y,w,n,xc,yc,dc,k,m,info,s,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Weighted fitting by Hermite spline, with constraints on function |
|
|
//| values or first derivatives. |
|
|
//| Equidistant grid with M nodes on [min(x,xc),max(x,xc)] is used to|
|
|
//| build basis functions. Basis functions are Hermite splines. Small|
|
|
//| regularizing term is used when solving constrained tasks (to |
|
|
//| improve stability). |
|
|
//| Task is linear, so linear least squares solver is used. |
|
|
//| Complexity of this computational scheme is O(N*M^2), mostly |
|
|
//| dominated by least squares solver |
|
|
//| SEE ALSO |
|
|
//| Spline1DFitCubicWC() - fitting by Cubic splines (less |
|
|
//| flexible, more smooth) |
|
|
//| Spline1DFitHermite() - "lightweight" Hermite fitting, |
|
|
//| without invididual weights and |
|
|
//| constraints |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - points, array[0..N-1]. |
|
|
//| Y - function values, array[0..N-1]. |
|
|
//| W - weights, array[0..N-1] |
|
|
//| Each summand in square sum of approximation |
|
|
//| deviations from given values is multiplied by the |
|
|
//| square of corresponding weight. Fill it by 1's if |
|
|
//| you don't want to solve weighted task. |
|
|
//| N - number of points (optional): |
|
|
//| * N>0 |
|
|
//| * if given, only first N elements of X/Y/W are |
|
|
//| processed |
|
|
//| * if not given, automatically determined from X/Y/W |
|
|
//| sizes |
|
|
//| XC - points where spline values/derivatives are |
|
|
//| constrained, array[0..K-1]. |
|
|
//| YC - values of constraints, array[0..K-1] |
|
|
//| DC - array[0..K-1], types of constraints: |
|
|
//| * DC[i]=0 means that S(XC[i])=YC[i] |
|
|
//| * DC[i]=1 means that S'(XC[i])=YC[i] |
|
|
//| SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS |
|
|
//| K - number of constraints (optional): |
|
|
//| * 0<=K<M. |
|
|
//| * K=0 means no constraints (XC/YC/DC are not used) |
|
|
//| * if given, only first K elements of XC/YC/DC are |
|
|
//| used |
|
|
//| * if not given, automatically determined from |
|
|
//| XC/YC/DC |
|
|
//| M - number of basis functions (= 2 * number of nodes), |
|
|
//| M>=4, |
|
|
//| M IS EVEN! |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Info- same format as in LSFitLinearW() subroutine: |
|
|
//| * Info>0 task is solved |
|
|
//| * Info<=0 an error occured: |
|
|
//| -4 means inconvergence of internal SVD |
|
|
//| -3 means inconsistent constraints |
|
|
//| -2 means odd M was passed (which is not |
|
|
//| supported) |
|
|
//| -1 means another errors in parameters |
|
|
//| passed (N<=0, for example) |
|
|
//| S - spline interpolant. |
|
|
//| Rep - report, same format as in LSFitLinearW() subroutine. |
|
|
//| Following fields are set: |
|
|
//| * RMSError rms error on the (X,Y). |
|
|
//| * AvgError average error on the (X,Y). |
|
|
//| * AvgRelError average relative error on the |
|
|
//| non-zero Y |
|
|
//| * MaxError maximum error |
|
|
//| NON-WEIGHTED ERRORS ARE CALCULATED |
|
|
//| IMPORTANT: |
|
|
//| this subroitine doesn't calculate task's condition number |
|
|
//| for K<>0. |
|
|
//| IMPORTANT: |
|
|
//| this subroitine supports only even M's |
|
|
//| ORDER OF POINTS |
|
|
//| ubroutine automatically sorts points, so caller may pass |
|
|
//| unsorted array. |
|
|
//| SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES: |
|
|
//| Setting constraints can lead to undesired results, like |
|
|
//| ill-conditioned behavior, or inconsistency being detected. From |
|
|
//| the other side, it allows us to improve quality of the fit. Here |
|
|
//| we summarize our experience with constrained regression splines:|
|
|
//| * excessive constraints can be inconsistent. Splines are |
|
|
//| piecewise cubic functions, and it is easy to create an example,|
|
|
//| where large number of constraints concentrated in small area |
|
|
//| will result in inconsistency. Just because spline is not |
|
|
//| flexible enough to satisfy all of them. And same constraints |
|
|
//| spread across the [min(x),max(x)] will be perfectly consistent.|
|
|
//| * the more evenly constraints are spread across [min(x),max(x)], |
|
|
//| the more chances that they will be consistent |
|
|
//| * the greater is M (given fixed constraints), the more chances |
|
|
//| that constraints will be consistent |
|
|
//| * in the general case, consistency of constraints is NOT |
|
|
//| GUARANTEED. |
|
|
//| * in the several special cases, however, we can guarantee |
|
|
//| consistency. |
|
|
//| * one of this cases is M>=4 and constraints on the function |
|
|
//| value (AND/OR its derivative) at the interval boundaries. |
|
|
//| * another special case is M>=4 and ONE constraint on the |
|
|
//| function value (OR, BUT NOT AND, derivative) anywhere in |
|
|
//| [min(x),max(x)] |
|
|
//| Our final recommendation is to use constraints WHEN AND ONLY when|
|
|
//| you can't solve your task without them. Anything beyond special |
|
|
//| cases given above is not guaranteed and may result in |
|
|
//| inconsistency. |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::Spline1DFitHermiteWC(double &x[],double &y[],double &w[],
|
|
const int n,double &xc[],double &yc[],
|
|
int &dc[],const int k,const int m,
|
|
int &info,CSpline1DInterpolant &s,
|
|
CSpline1DFitReport &rep)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=4,__FUNCTION__+": M<4!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m%2==0,__FUNCTION__+": M is odd!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k>=0,__FUNCTION__+": K<0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k<m,__FUNCTION__+": K>=M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": Length(W)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(xc)>=k,__FUNCTION__+": Length(XC)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(yc)>=k,__FUNCTION__+": Length(YC)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(dc)>=k,__FUNCTION__+": Length(DC)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(xc,k),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(yc,k),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(dc[i]==0 || dc[i]==1,__FUNCTION__+": DC[i] is neither 0 or 1!"))
|
|
return;
|
|
}
|
|
//--- function call
|
|
Spline1DFitInternal(1,x,y,w,n,xc,yc,dc,k,m,info,s,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Least squares fitting by cubic spline. |
|
|
//| This subroutine is "lightweight" alternative for more complex |
|
|
//| and feature - rich Spline1DFitCubicWC(). See Spline1DFitCubicWC()|
|
|
//| for more information about subroutine parameters (we don't |
|
|
//| duplicate it here because of length) |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::Spline1DFitCubic(double &x[],double &y[],const int n,
|
|
const int m,int &info,
|
|
CSpline1DInterpolant &s,
|
|
CSpline1DFitReport &rep)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create arrays
|
|
double w[];
|
|
double xc[];
|
|
double yc[];
|
|
int dc[];
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=4,__FUNCTION__+": M<4!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResizeAL(w,n);
|
|
//--- initialization
|
|
for(i=0;i<=n-1;i++)
|
|
w[i]=1;
|
|
//--- function call
|
|
Spline1DFitCubicWC(x,y,w,n,xc,yc,dc,0,m,info,s,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Least squares fitting by Hermite spline. |
|
|
//| This subroutine is "lightweight" alternative for more complex |
|
|
//| and feature - rich Spline1DFitHermiteWC(). See |
|
|
//| Spline1DFitHermiteWC() description for more information about |
|
|
//| subroutine parameters (we don't duplicate it here because of |
|
|
//| length). |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::Spline1DFitHermite(double &x[],double &y[],const int n,
|
|
const int m,int &info,
|
|
CSpline1DInterpolant &s,
|
|
CSpline1DFitReport &rep)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create arrays
|
|
double w[];
|
|
double xc[];
|
|
double yc[];
|
|
int dc[];
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=4,__FUNCTION__+": M<4!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m%2==0,__FUNCTION__+": M is odd!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": Length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NAN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NAN values!"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResizeAL(w,n);
|
|
//--- initialization
|
|
for(i=0;i<=n-1;i++)
|
|
w[i]=1;
|
|
//--- function call
|
|
Spline1DFitHermiteWC(x,y,w,n,xc,yc,dc,0,m,info,s,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Weighted linear least squares fitting. |
|
|
//| QR decomposition is used to reduce task to MxM, then triangular |
|
|
//| solver or SVD-based solver is used depending on condition number |
|
|
//| of the system. It allows to maximize speed and retain decent |
|
|
//| accuracy. |
|
|
//| INPUT PARAMETERS: |
|
|
//| Y - array[0..N-1] Function values in N points. |
|
|
//| W - array[0..N-1] Weights corresponding to function |
|
|
//| values. Each summand in square sum of |
|
|
//| approximation deviations from given values is |
|
|
//| multiplied by the square of corresponding weight.|
|
|
//| FMatrix - a table of basis functions values, |
|
|
//| array[0..N-1, 0..M-1]. FMatrix[I, J] - value of |
|
|
//| J-th basis function in I-th point. |
|
|
//| N - number of points used. N>=1. |
|
|
//| M - number of basis functions, M>=1. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Info - error code: |
|
|
//| * -4 internal SVD decomposition subroutine |
|
|
//| failed (very rare and for degenerate |
|
|
//| systems only) |
|
|
//| * -1 incorrect N/M were specified |
|
|
//| * 1 task is solved |
|
|
//| C - decomposition coefficients, array[0..M-1] |
|
|
//| Rep - fitting report. Following fields are set: |
|
|
//| * Rep.TaskRCond reciprocal of condition |
|
|
//| number |
|
|
//| * RMSError rms error on the (X,Y). |
|
|
//| * AvgError average error on the (X,Y). |
|
|
//| * AvgRelError average relative error on the|
|
|
//| non-zero Y |
|
|
//| * MaxError maximum error |
|
|
//| NON-WEIGHTED ERRORS ARE |
|
|
//| CALCULATED |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitLinearW(double &y[],double &w[],CMatrixDouble &fmatrix,
|
|
const int n,const int m,int &info,
|
|
double &c[],CLSFitReport &rep)
|
|
{
|
|
//--- initialization
|
|
info=0;
|
|
//--- 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(y)>=n,__FUNCTION__+": length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": length(W)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": W contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(fmatrix)>=n,__FUNCTION__+": rows(FMatrix)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(fmatrix)>=m,__FUNCTION__+": cols(FMatrix)<M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(fmatrix,n,m),__FUNCTION__+": FMatrix contains infinite or NaN values!"))
|
|
return;
|
|
//--- function call
|
|
LSFitLinearInternal(y,w,fmatrix,n,m,info,c,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Weighted constained linear least squares fitting. |
|
|
//| This is variation of LSFitLinearW(), which searchs for |
|
|
//| min|A*x=b| given that K additional constaints C*x=bc are |
|
|
//| satisfied. It reduces original task to modified one: min|B*y-d| |
|
|
//| WITHOUT constraints, then LSFitLinearW() is called. |
|
|
//| INPUT PARAMETERS: |
|
|
//| Y - array[0..N-1] Function values in N points. |
|
|
//| W - array[0..N-1] Weights corresponding to function |
|
|
//| values. Each summand in square sum of |
|
|
//| approximation deviations from given values is |
|
|
//| multiplied by the square of corresponding |
|
|
//| weight. |
|
|
//| FMatrix - a table of basis functions values, |
|
|
//| array[0..N-1, 0..M-1]. FMatrix[I,J] - value of |
|
|
//| J-th basis function in I-th point. |
|
|
//| CMatrix - a table of constaints, array[0..K-1,0..M]. |
|
|
//| I-th row of CMatrix corresponds to I-th linear |
|
|
//| constraint: CMatrix[I,0]*C[0] + ... + |
|
|
//| + CMatrix[I,M-1]*C[M-1] = CMatrix[I,M] |
|
|
//| N - number of points used. N>=1. |
|
|
//| M - number of basis functions, M>=1. |
|
|
//| K - number of constraints, 0 <= K < M |
|
|
//| K=0 corresponds to absence of constraints. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Info - error code: |
|
|
//| * -4 internal SVD decomposition subroutine |
|
|
//| failed (very rare and for degenerate |
|
|
//| systems only) |
|
|
//| * -3 either too many constraints (M or more), |
|
|
//| degenerate constraints (some constraints |
|
|
//| are repetead twice) or inconsistent |
|
|
//| constraints were specified. |
|
|
//| * 1 task is solved |
|
|
//| C - decomposition coefficients, array[0..M-1] |
|
|
//| Rep - fitting report. Following fields are set: |
|
|
//| * RMSError rms error on the (X,Y). |
|
|
//| * AvgError average error on the (X,Y). |
|
|
//| * AvgRelError average relative error on the|
|
|
//| non-zero Y |
|
|
//| * MaxError maximum error |
|
|
//| NON-WEIGHTED ERRORS ARE |
|
|
//| CALCULATED |
|
|
//| IMPORTANT: |
|
|
//| this subroitine doesn't calculate task's condition number |
|
|
//| for K<>0. |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitLinearWC(double &cy[],double &w[],CMatrixDouble &fmatrix,
|
|
CMatrixDouble &ccmatrix,const int n,
|
|
const int m,const int k,int &info,
|
|
double &c[],CLSFitReport &rep)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double tau[];
|
|
double tmp[];
|
|
double c0[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble q;
|
|
CMatrixDouble f2;
|
|
CMatrixDouble cmatrix;
|
|
//--- copy array
|
|
ArrayCopy(y,cy);
|
|
//--- copy matrix
|
|
cmatrix=ccmatrix;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k>=0,__FUNCTION__+": K<0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": length(W)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": W contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(fmatrix)>=n,__FUNCTION__+": rows(FMatrix)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(fmatrix)>=m,__FUNCTION__+": cols(FMatrix)<M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(fmatrix,n,m),__FUNCTION__+": FMatrix contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(cmatrix)>=k,__FUNCTION__+": rows(CMatrix)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(cmatrix)>=m+1 || k==0,__FUNCTION__+": cols(CMatrix)<M+1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(cmatrix,k,m+1),__FUNCTION__+": CMatrix contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(k>=m)
|
|
{
|
|
info=-3;
|
|
return;
|
|
}
|
|
//--- Solve
|
|
if(k==0)
|
|
{
|
|
//--- no constraints
|
|
LSFitLinearInternal(y,w,fmatrix,n,m,info,c,rep);
|
|
}
|
|
else
|
|
{
|
|
//--- First,find general form solution of constraints system:
|
|
//--- * factorize C=L*Q
|
|
//--- * unpack Q
|
|
//--- * fill upper part of C with zeros (for RCond)
|
|
//--- We got C=C0+Q2'*y where Q2 is lower M-K rows of Q.
|
|
COrtFac::RMatrixLQ(cmatrix,k,m,tau);
|
|
COrtFac::RMatrixLQUnpackQ(cmatrix,k,m,tau,m,q);
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
for(j=i+1;j<=m-1;j++)
|
|
cmatrix[i].Set(j,0.0);
|
|
}
|
|
//--- check
|
|
if(CRCond::RMatrixLURCondInf(cmatrix,k)<1000*CMath::m_machineepsilon)
|
|
{
|
|
info=-3;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
ArrayResizeAL(tmp,k);
|
|
//--- calculation
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
//--- check
|
|
if(i>0)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=i-1;i_++)
|
|
v+=cmatrix[i][i_]*tmp[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- change values
|
|
tmp[i]=(cmatrix[i][m]-v)/cmatrix[i][i];
|
|
}
|
|
//--- allocation
|
|
ArrayResizeAL(c0,m);
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
c0[i]=0;
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
v=tmp[i];
|
|
for(i_=0;i_<=m-1;i_++)
|
|
c0[i_]=c0[i_]+v*q[i][i_];
|
|
}
|
|
//--- Second,prepare modified matrix F2=F*Q2' and solve modified task
|
|
ArrayResizeAL(tmp,MathMax(n,m)+1);
|
|
f2.Resize(n,m-k);
|
|
//--- function call
|
|
CBlas::MatrixVectorMultiply(fmatrix,0,n-1,0,m-1,false,c0,0,m-1,-1.0,y,0,n-1,1.0);
|
|
//--- function call
|
|
CBlas::MatrixMatrixMultiply(fmatrix,0,n-1,0,m-1,false,q,k,m-1,0,m-1,true,1.0,f2,0,n-1,0,m-k-1,0.0,tmp);
|
|
//--- function call
|
|
LSFitLinearInternal(y,w,f2,n,m-k,info,tmp,rep);
|
|
rep.m_taskrcond=-1;
|
|
//--- check
|
|
if(info<=0)
|
|
return;
|
|
//--- then,convert back to original answer: C=C0 + Q2'*Y0
|
|
ArrayResizeAL(c,m);
|
|
for(i_=0;i_<=m-1;i_++)
|
|
c[i_]=c0[i_];
|
|
//--- function call
|
|
CBlas::MatrixVectorMultiply(q,k,m-1,0,m-1,true,tmp,0,m-k-1,1.0,c,0,m-1,1.0);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Linear least squares fitting. |
|
|
//| QR decomposition is used to reduce task to MxM, then triangular |
|
|
//| solver or SVD-based solver is used depending on condition number |
|
|
//| of the system. It allows to maximize speed and retain decent |
|
|
//| accuracy. |
|
|
//| INPUT PARAMETERS: |
|
|
//| Y - array[0..N-1] Function values in N points. |
|
|
//| FMatrix - a table of basis functions values, |
|
|
//| array[0..N-1, 0..M-1]. |
|
|
//| FMatrix[I, J] - value of J-th basis function in |
|
|
//| I-th point. |
|
|
//| N - number of points used. N>=1. |
|
|
//| M - number of basis functions, M>=1. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Info - error code: |
|
|
//| * -4 internal SVD decomposition subroutine |
|
|
//| failed (very rare and for degenerate |
|
|
//| systems only) |
|
|
//| * 1 task is solved |
|
|
//| C - decomposition coefficients, array[0..M-1] |
|
|
//| Rep - fitting report. Following fields are set: |
|
|
//| * Rep.TaskRCond reciprocal of condition |
|
|
//| number |
|
|
//| * RMSError rms error on the (X,Y). |
|
|
//| * AvgError average error on the (X,Y). |
|
|
//| * AvgRelError average relative error on the|
|
|
//| non-zero Y |
|
|
//| * MaxError maximum error |
|
|
//| NON-WEIGHTED ERRORS ARE |
|
|
//| CALCULATED |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitLinear(double &y[],CMatrixDouble &fmatrix,
|
|
const int n,const int m,int &info,
|
|
double &c[],CLSFitReport &rep)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create array
|
|
double w[];
|
|
//--- initialization
|
|
info=0;
|
|
//--- 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(y)>=n,__FUNCTION__+": length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(fmatrix)>=n,__FUNCTION__+": rows(FMatrix)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(fmatrix)>=m,__FUNCTION__+": cols(FMatrix)<M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(fmatrix,n,m),__FUNCTION__+": FMatrix contains infinite or NaN values!"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResizeAL(w,n);
|
|
//--- initialization
|
|
for(i=0;i<=n-1;i++)
|
|
w[i]=1;
|
|
//--- function call
|
|
LSFitLinearInternal(y,w,fmatrix,n,m,info,c,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Constained linear least squares fitting. |
|
|
//| This is variation of LSFitLinear(), which searchs for min|A*x=b| |
|
|
//| given that K additional constaints C*x=bc are satisfied. It |
|
|
//| reduces original task to modified one: min|B*y-d| WITHOUT |
|
|
//| constraints, then LSFitLinear() is called. |
|
|
//| INPUT PARAMETERS: |
|
|
//| Y - array[0..N-1] Function values in N points. |
|
|
//| FMatrix - a table of basis functions values, |
|
|
//| array[0..N-1, 0..M-1]. FMatrix[I,J] - value of |
|
|
//| J-th basis function in I-th point. |
|
|
//| CMatrix - a table of constaints, array[0..K-1,0..M]. |
|
|
//| I-th row of CMatrix corresponds to I-th linear |
|
|
//| constraint: CMatrix[I,0]*C[0] + ... + |
|
|
//| + CMatrix[I,M-1]*C[M-1] = CMatrix[I,M] |
|
|
//| N - number of points used. N>=1. |
|
|
//| M - number of basis functions, M>=1. |
|
|
//| K - number of constraints, 0 <= K < M |
|
|
//| K=0 corresponds to absence of constraints. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Info - error code: |
|
|
//| * -4 internal SVD decomposition subroutine |
|
|
//| failed (very rare and for degenerate |
|
|
//| systems only) |
|
|
//| * -3 either too many constraints (M or more), |
|
|
//| degenerate constraints (some constraints |
|
|
//| are repetead twice) or inconsistent |
|
|
//| constraints were specified. |
|
|
//| * 1 task is solved |
|
|
//| C - decomposition coefficients, array[0..M-1] |
|
|
//| Rep - fitting report. Following fields are set: |
|
|
//| * RMSError rms error on the (X,Y). |
|
|
//| * AvgError average error on the (X,Y). |
|
|
//| * AvgRelError average relative error on the|
|
|
//| non-zero Y |
|
|
//| * MaxError maximum error |
|
|
//| NON-WEIGHTED ERRORS ARE |
|
|
//| CALCULATED |
|
|
//| IMPORTANT: |
|
|
//| this subroitine doesn't calculate task's condition number |
|
|
//| for K<>0. |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitLinearC(double &cy[],CMatrixDouble &fmatrix,
|
|
CMatrixDouble &cmatrix,const int n,
|
|
const int m,const int k,int &info,
|
|
double &c[],CLSFitReport &rep)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create arrays
|
|
double w[];
|
|
double y[];
|
|
//--- copy array
|
|
ArrayCopy(y,cy);
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k>=0,__FUNCTION__+": K<0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(fmatrix)>=n,__FUNCTION__+": rows(FMatrix)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(fmatrix)>=m,__FUNCTION__+": cols(FMatrix)<M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(fmatrix,n,m),__FUNCTION__+": FMatrix contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(cmatrix)>=k,__FUNCTION__+": rows(CMatrix)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(cmatrix)>=m+1 || k==0,__FUNCTION__+": cols(CMatrix)<M+1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(cmatrix,k,m+1),__FUNCTION__+": CMatrix contains infinite or NaN values!"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResizeAL(w,n);
|
|
//--- initialization
|
|
for(i=0;i<=n-1;i++)
|
|
w[i]=1;
|
|
//--- function call
|
|
LSFitLinearWC(y,w,fmatrix,cmatrix,n,m,k,info,c,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Weighted nonlinear least squares fitting using function values |
|
|
//| only. |
|
|
//| Combination of numerical differentiation and secant updates is |
|
|
//| used to obtain function Jacobian. |
|
|
//| Nonlinear task min(F(c)) is solved, where |
|
|
//| F(c) = (w[0]*(f(c,x[0])-y[0]))^2 + ... + |
|
|
//| + (w[n-1]*(f(c,x[n-1])-y[n-1]))^2, |
|
|
//| * N is a number of points, |
|
|
//| * M is a dimension of a space points belong to, |
|
|
//| * K is a dimension of a space of parameters being fitted, |
|
|
//| * w is an N-dimensional vector of weight coefficients, |
|
|
//| * x is a set of N points, each of them is an M-dimensional |
|
|
//| vector, |
|
|
//| * c is a K-dimensional vector of parameters being fitted |
|
|
//| This subroutine uses only f(c,x[i]). |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - array[0..N-1,0..M-1], points (one row = one |
|
|
//| point) |
|
|
//| Y - array[0..N-1], function values. |
|
|
//| W - weights, array[0..N-1] |
|
|
//| C - array[0..K-1], initial approximation to the |
|
|
//| solution, |
|
|
//| N - number of points, N>1 |
|
|
//| M - dimension of space |
|
|
//| K - number of parameters being fitted |
|
|
//| DiffStep- numerical differentiation step; |
|
|
//| should not be very small or large; |
|
|
//| large = loss of accuracy |
|
|
//| small = growth of round-off errors |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitCreateWF(CMatrixDouble &x,double &y[],double &w[],
|
|
double &c[],const int n,const int m,
|
|
const int k,const double diffstep,
|
|
CLSFitState &state)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k>=1,__FUNCTION__+": K<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(c)>=k,__FUNCTION__+": length(C)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(c,k),__FUNCTION__+": C contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": length(W)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": W contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(x)>=n,__FUNCTION__+": rows(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(x)>=m,__FUNCTION__+": cols(X)<M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(x,n,m),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(diffstep),__FUNCTION__+": DiffStep is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((double)(diffstep)>0.0,__FUNCTION__+": DiffStep<=0!"))
|
|
return;
|
|
//--- initialization
|
|
state.m_npoints=n;
|
|
state.m_nweights=n;
|
|
state.m_wkind=1;
|
|
state.m_m=m;
|
|
state.m_k=k;
|
|
//--- function call
|
|
LSFitSetCond(state,0.0,0.0,0);
|
|
//--- function call
|
|
LSFitSetStpMax(state,0.0);
|
|
//--- function call
|
|
LSFitSetXRep(state,false);
|
|
//--- allocation
|
|
state.m_taskx.Resize(n,m);
|
|
ArrayResizeAL(state.m_tasky,n);
|
|
ArrayResizeAL(state.m_w,n);
|
|
ArrayResizeAL(state.m_c,k);
|
|
ArrayResizeAL(state.m_x,m);
|
|
//--- copy
|
|
for(i_=0;i_<=k-1;i_++)
|
|
state.m_c[i_]=c[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_w[i_]=w[i_];
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=m-1;i_++)
|
|
state.m_taskx[i].Set(i_,x[i][i_]);
|
|
state.m_tasky[i]=y[i];
|
|
}
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_s,k);
|
|
ArrayResizeAL(state.m_bndl,k);
|
|
ArrayResizeAL(state.m_bndu,k);
|
|
//--- change values
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
state.m_s[i]=1.0;
|
|
state.m_bndl[i]=CInfOrNaN::NegativeInfinity();
|
|
state.m_bndu[i]=CInfOrNaN::PositiveInfinity();
|
|
}
|
|
//--- change values
|
|
state.m_optalgo=0;
|
|
state.m_prevnpt=-1;
|
|
state.m_prevalgo=-1;
|
|
//--- function call
|
|
CMinLM::MinLMCreateV(k,n,state.m_c,diffstep,state.m_optstate);
|
|
//--- function call
|
|
LSFitClearRequestFields(state);
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_rstate.ia,5);
|
|
ArrayResizeAL(state.m_rstate.ra,3);
|
|
state.m_rstate.stage=-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Nonlinear least squares fitting using function values only. |
|
|
//| Combination of numerical differentiation and secant updates is |
|
|
//| used to obtain function Jacobian. |
|
|
//| Nonlinear task min(F(c)) is solved, where |
|
|
//| F(c) = (f(c,x[0])-y[0])^2 + ... + (f(c,x[n-1])-y[n-1])^2, |
|
|
//| * N is a number of points, |
|
|
//| * M is a dimension of a space points belong to, |
|
|
//| * K is a dimension of a space of parameters being fitted, |
|
|
//| * w is an N-dimensional vector of weight coefficients, |
|
|
//| * x is a set of N points, each of them is an M-dimensional |
|
|
//| vector, |
|
|
//| * c is a K-dimensional vector of parameters being fitted |
|
|
//| This subroutine uses only f(c,x[i]). |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - array[0..N-1,0..M-1], points (one row = one |
|
|
//| point) |
|
|
//| Y - array[0..N-1], function values. |
|
|
//| C - array[0..K-1], initial approximation to the |
|
|
//| solution, |
|
|
//| N - number of points, N>1 |
|
|
//| M - dimension of space |
|
|
//| K - number of parameters being fitted |
|
|
//| DiffStep- numerical differentiation step; |
|
|
//| should not be very small or large; |
|
|
//| large = loss of accuracy |
|
|
//| small = growth of round-off errors |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitCreateF(CMatrixDouble &x,double &y[],double &c[],
|
|
const int n,const int m,const int k,
|
|
const double diffstep,CLSFitState &state)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k>=1,__FUNCTION__+": K<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(c)>=k,__FUNCTION__+": length(C)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(c,k),__FUNCTION__+": C contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(x)>=n,__FUNCTION__+": rows(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(x)>=m,__FUNCTION__+": cols(X)<M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(x,n,m),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(x)>=n,__FUNCTION__+": rows(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(x)>=m,__FUNCTION__+": cols(X)<M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(x,n,m),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(diffstep),__FUNCTION__+": DiffStep is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((double)(diffstep)>0.0,__FUNCTION__+": DiffStep<=0!"))
|
|
return;
|
|
//--- initialization
|
|
state.m_npoints=n;
|
|
state.m_wkind=0;
|
|
state.m_m=m;
|
|
state.m_k=k;
|
|
//--- function call
|
|
LSFitSetCond(state,0.0,0.0,0);
|
|
//--- function call
|
|
LSFitSetStpMax(state,0.0);
|
|
//--- function call
|
|
LSFitSetXRep(state,false);
|
|
//--- allocation
|
|
state.m_taskx.Resize(n,m);
|
|
ArrayResizeAL(state.m_tasky,n);
|
|
ArrayResizeAL(state.m_c,k);
|
|
ArrayResizeAL(state.m_x,m);
|
|
//--- copy
|
|
for(i_=0;i_<=k-1;i_++)
|
|
state.m_c[i_]=c[i_];
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=m-1;i_++)
|
|
state.m_taskx[i].Set(i_,x[i][i_]);
|
|
state.m_tasky[i]=y[i];
|
|
}
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_s,k);
|
|
ArrayResizeAL(state.m_bndl,k);
|
|
ArrayResizeAL(state.m_bndu,k);
|
|
//--- change values
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
state.m_s[i]=1.0;
|
|
state.m_bndl[i]=CInfOrNaN::NegativeInfinity();
|
|
state.m_bndu[i]=CInfOrNaN::PositiveInfinity();
|
|
}
|
|
//--- change values
|
|
state.m_optalgo=0;
|
|
state.m_prevnpt=-1;
|
|
state.m_prevalgo=-1;
|
|
//--- function call
|
|
CMinLM::MinLMCreateV(k,n,state.m_c,diffstep,state.m_optstate);
|
|
//--- function call
|
|
LSFitClearRequestFields(state);
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_rstate.ia,5);
|
|
ArrayResizeAL(state.m_rstate.ra,3);
|
|
state.m_rstate.stage=-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Weighted nonlinear least squares fitting using gradient only. |
|
|
//| Nonlinear task min(F(c)) is solved, where |
|
|
//| F(c) = (w[0]*(f(c,x[0])-y[0]))^2 + ... + |
|
|
//| + (w[n-1]*(f(c,x[n-1])-y[n-1]))^2, |
|
|
//| * N is a number of points, |
|
|
//| * M is a dimension of a space points belong to, |
|
|
//| * K is a dimension of a space of parameters being fitted, |
|
|
//| * w is an N-dimensional vector of weight coefficients, |
|
|
//| * x is a set of N points, each of them is an M-dimensional |
|
|
//| vector, |
|
|
//| * c is a K-dimensional vector of parameters being fitted |
|
|
//| This subroutine uses only f(c,x[i]) and its gradient. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - array[0..N-1,0..M-1], points (one row = one |
|
|
//| point) |
|
|
//| Y - array[0..N-1], function values. |
|
|
//| W - weights, array[0..N-1] |
|
|
//| C - array[0..K-1], initial approximation to the |
|
|
//| solution, |
|
|
//| N - number of points, N>1 |
|
|
//| M - dimension of space |
|
|
//| K - number of parameters being fitted |
|
|
//| CheapFG - boolean flag, which is: |
|
|
//| * True if both function and gradient calculation |
|
|
//| complexity are less than O(M^2). An |
|
|
//| improved algorithm can be used which |
|
|
//| corresponds to FGJ scheme from MINLM unit.|
|
|
//| * False otherwise. |
|
|
//| Standard Jacibian-bases |
|
|
//| Levenberg-Marquardt algo will be used (FJ |
|
|
//| scheme). |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| See also: |
|
|
//| LSFitResults |
|
|
//| LSFitCreateFG (fitting without weights) |
|
|
//| LSFitCreateWFGH (fitting using Hessian) |
|
|
//| LSFitCreateFGH (fitting using Hessian, without weights) |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitCreateWFG(CMatrixDouble &x,double &y[],double &w[],
|
|
double &c[],const int n,const int m,
|
|
const int k,bool cheapfg,CLSFitState &state)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k>=1,__FUNCTION__+": K<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(c)>=k,__FUNCTION__+": length(C)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(c,k),__FUNCTION__+": C contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": length(W)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": W contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(x)>=n,__FUNCTION__+": rows(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(x)>=m,__FUNCTION__+": cols(X)<M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(x,n,m),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- initialization
|
|
state.m_npoints=n;
|
|
state.m_nweights=n;
|
|
state.m_wkind=1;
|
|
state.m_m=m;
|
|
state.m_k=k;
|
|
//--- function call
|
|
LSFitSetCond(state,0.0,0.0,0);
|
|
//--- function call
|
|
LSFitSetStpMax(state,0.0);
|
|
//--- function call
|
|
LSFitSetXRep(state,false);
|
|
//--- allocation
|
|
state.m_taskx.Resize(n,m);
|
|
ArrayResizeAL(state.m_tasky,n);
|
|
ArrayResizeAL(state.m_w,n);
|
|
ArrayResizeAL(state.m_c,k);
|
|
ArrayResizeAL(state.m_x,m);
|
|
ArrayResizeAL(state.m_g,k);
|
|
//--- copy
|
|
for(i_=0;i_<=k-1;i_++)
|
|
state.m_c[i_]=c[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_w[i_]=w[i_];
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=m-1;i_++)
|
|
state.m_taskx[i].Set(i_,x[i][i_]);
|
|
state.m_tasky[i]=y[i];
|
|
}
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_s,k);
|
|
ArrayResizeAL(state.m_bndl,k);
|
|
ArrayResizeAL(state.m_bndu,k);
|
|
//--- change values
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
state.m_s[i]=1.0;
|
|
state.m_bndl[i]=CInfOrNaN::NegativeInfinity();
|
|
state.m_bndu[i]=CInfOrNaN::PositiveInfinity();
|
|
}
|
|
//--- change values
|
|
state.m_optalgo=1;
|
|
state.m_prevnpt=-1;
|
|
state.m_prevalgo=-1;
|
|
//--- check
|
|
if(cheapfg)
|
|
CMinLM::MinLMCreateVGJ(k,n,state.m_c,state.m_optstate);
|
|
else
|
|
CMinLM::MinLMCreateVJ(k,n,state.m_c,state.m_optstate);
|
|
//--- function call
|
|
LSFitClearRequestFields(state);
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_rstate.ia,5);
|
|
ArrayResizeAL(state.m_rstate.ra,3);
|
|
state.m_rstate.stage=-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Nonlinear least squares fitting using gradient only, without |
|
|
//| individual weights. |
|
|
//| Nonlinear task min(F(c)) is solved, where |
|
|
//| F(c) = ((f(c,x[0])-y[0]))^2 + ... + ((f(c,x[n-1])-y[n-1]))^2,|
|
|
//| * N is a number of points, |
|
|
//| * M is a dimension of a space points belong to, |
|
|
//| * K is a dimension of a space of parameters being fitted, |
|
|
//| * x is a set of N points, each of them is an M-dimensional |
|
|
//| vector, |
|
|
//| * c is a K-dimensional vector of parameters being fitted |
|
|
//| This subroutine uses only f(c,x[i]) and its gradient. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - array[0..N-1,0..M-1], points (one row = one |
|
|
//| point) |
|
|
//| Y - array[0..N-1], function values. |
|
|
//| C - array[0..K-1], initial approximation to the |
|
|
//| solution, |
|
|
//| N - number of points, N>1 |
|
|
//| M - dimension of space |
|
|
//| K - number of parameters being fitted |
|
|
//| CheapFG - boolean flag, which is: |
|
|
//| * True if both function and gradient calculation|
|
|
//| complexity are less than O(M^2). An |
|
|
//| improved algorithm can be used which |
|
|
//| corresponds to FGJ scheme from MINLM |
|
|
//| unit. |
|
|
//| * False otherwise. |
|
|
//| Standard Jacibian-bases |
|
|
//| Levenberg-Marquardt algo will be used |
|
|
//| (FJ scheme). |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitCreateFG(CMatrixDouble &x,double &y[],double &c[],
|
|
const int n,const int m,const int k,
|
|
const bool cheapfg,CLSFitState &state)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k>=1,__FUNCTION__+": K<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(c)>=k,__FUNCTION__+": length(C)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(c,k),__FUNCTION__+": C contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(x)>=n,__FUNCTION__+": rows(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(x)>=m,__FUNCTION__+": cols(X)<M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(x,n,m),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(x)>=n,__FUNCTION__+": rows(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(x)>=m,__FUNCTION__+": cols(X)<M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(x,n,m),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- initialization
|
|
state.m_npoints=n;
|
|
state.m_wkind=0;
|
|
state.m_m=m;
|
|
state.m_k=k;
|
|
LSFitSetCond(state,0.0,0.0,0);
|
|
LSFitSetStpMax(state,0.0);
|
|
LSFitSetXRep(state,false);
|
|
//--- allocation
|
|
state.m_taskx.Resize(n,m);
|
|
ArrayResizeAL(state.m_tasky,n);
|
|
ArrayResizeAL(state.m_c,k);
|
|
ArrayResizeAL(state.m_x,m);
|
|
ArrayResizeAL(state.m_g,k);
|
|
//--- copy
|
|
for(i_=0;i_<=k-1;i_++)
|
|
state.m_c[i_]=c[i_];
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=m-1;i_++)
|
|
state.m_taskx[i].Set(i_,x[i][i_]);
|
|
state.m_tasky[i]=y[i];
|
|
}
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_s,k);
|
|
ArrayResizeAL(state.m_bndl,k);
|
|
ArrayResizeAL(state.m_bndu,k);
|
|
//--- change values
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
state.m_s[i]=1.0;
|
|
state.m_bndl[i]=CInfOrNaN::NegativeInfinity();
|
|
state.m_bndu[i]=CInfOrNaN::PositiveInfinity();
|
|
}
|
|
//--- change values
|
|
state.m_optalgo=1;
|
|
state.m_prevnpt=-1;
|
|
state.m_prevalgo=-1;
|
|
//--- check
|
|
if(cheapfg)
|
|
CMinLM::MinLMCreateVGJ(k,n,state.m_c,state.m_optstate);
|
|
else
|
|
CMinLM::MinLMCreateVJ(k,n,state.m_c,state.m_optstate);
|
|
//--- function call
|
|
LSFitClearRequestFields(state);
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_rstate.ia,5);
|
|
ArrayResizeAL(state.m_rstate.ra,3);
|
|
state.m_rstate.stage=-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Weighted nonlinear least squares fitting using gradient/Hessian. |
|
|
//| Nonlinear task min(F(c)) is solved, where |
|
|
//| F(c) = (w[0]*(f(c,x[0])-y[0]))^2 + ... + |
|
|
//| (w[n-1]*(f(c,x[n-1])-y[n-1]))^2, |
|
|
//| * N is a number of points, |
|
|
//| * M is a dimension of a space points belong to, |
|
|
//| * K is a dimension of a space of parameters being fitted, |
|
|
//| * w is an N-dimensional vector of weight coefficients, |
|
|
//| * x is a set of N points, each of them is an M-dimensional |
|
|
//| vector, |
|
|
//| * c is a K-dimensional vector of parameters being fitted |
|
|
//| This subroutine uses f(c,x[i]), its gradient and its Hessian. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - array[0..N-1,0..M-1], points (one row = one |
|
|
//| point) |
|
|
//| Y - array[0..N-1], function values. |
|
|
//| W - weights, array[0..N-1] |
|
|
//| C - array[0..K-1], initial approximation to the |
|
|
//| solution, |
|
|
//| N - number of points, N>1 |
|
|
//| M - dimension of space |
|
|
//| K - number of parameters being fitted |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitCreateWFGH(CMatrixDouble &x,double &y[],double &w[],
|
|
double &c[],const int n,const int m,
|
|
const int k,CLSFitState &state)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k>=1,__FUNCTION__+": K<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(c)>=k,__FUNCTION__+": length(C)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(c,k),__FUNCTION__+": C contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(w)>=n,__FUNCTION__+": length(W)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(w,n),__FUNCTION__+": W contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(x)>=n,__FUNCTION__+": rows(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(x)>=m,__FUNCTION__+": cols(X)<M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(x,n,m),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- initialization
|
|
state.m_npoints=n;
|
|
state.m_nweights=n;
|
|
state.m_wkind=1;
|
|
state.m_m=m;
|
|
state.m_k=k;
|
|
//--- function call
|
|
LSFitSetCond(state,0.0,0.0,0);
|
|
//--- function call
|
|
LSFitSetStpMax(state,0.0);
|
|
//--- function call
|
|
LSFitSetXRep(state,false);
|
|
//--- allocation
|
|
state.m_taskx.Resize(n,m);
|
|
ArrayResizeAL(state.m_tasky,n);
|
|
ArrayResizeAL(state.m_w,n);
|
|
ArrayResizeAL(state.m_c,k);
|
|
state.m_h.Resize(k,k);
|
|
ArrayResizeAL(state.m_x,m);
|
|
ArrayResizeAL(state.m_g,k);
|
|
//--- copy
|
|
for(i_=0;i_<=k-1;i_++)
|
|
state.m_c[i_]=c[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_w[i_]=w[i_];
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=m-1;i_++)
|
|
state.m_taskx[i].Set(i_,x[i][i_]);
|
|
state.m_tasky[i]=y[i];
|
|
}
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_s,k);
|
|
ArrayResizeAL(state.m_bndl,k);
|
|
ArrayResizeAL(state.m_bndu,k);
|
|
//--- change values
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
state.m_s[i]=1.0;
|
|
state.m_bndl[i]=CInfOrNaN::NegativeInfinity();
|
|
state.m_bndu[i]=CInfOrNaN::PositiveInfinity();
|
|
}
|
|
//--- change values
|
|
state.m_optalgo=2;
|
|
state.m_prevnpt=-1;
|
|
state.m_prevalgo=-1;
|
|
//--- function call
|
|
CMinLM::MinLMCreateFGH(k,state.m_c,state.m_optstate);
|
|
//--- function call
|
|
LSFitClearRequestFields(state);
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_rstate.ia,5);
|
|
ArrayResizeAL(state.m_rstate.ra,3);
|
|
state.m_rstate.stage=-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Nonlinear least squares fitting using gradient/Hessian, without |
|
|
//| individial weights. |
|
|
//| Nonlinear task min(F(c)) is solved, where |
|
|
//| F(c) = ((f(c,x[0])-y[0]))^2 + ... + |
|
|
//| ((f(c,x[n-1])-y[n-1]))^2, |
|
|
//| * N is a number of points, |
|
|
//| * M is a dimension of a space points belong to, |
|
|
//| * K is a dimension of a space of parameters being fitted, |
|
|
//| * x is a set of N points, each of them is an M-dimensional |
|
|
//| vector, |
|
|
//| * c is a K-dimensional vector of parameters being fitted |
|
|
//| This subroutine uses f(c,x[i]), its gradient and its Hessian. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - array[0..N-1,0..M-1], points (one row = one |
|
|
//| point) |
|
|
//| Y - array[0..N-1], function values. |
|
|
//| C - array[0..K-1], initial approximation to the |
|
|
//| solution, |
|
|
//| N - number of points, N>1 |
|
|
//| M - dimension of space |
|
|
//| K - number of parameters being fitted |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitCreateFGH(CMatrixDouble &x,double &y[],double &c[],
|
|
const int n,const int m,const int k,
|
|
CLSFitState &state)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k>=1,__FUNCTION__+": K<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(c)>=k,__FUNCTION__+": length(C)<K!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(c,k),__FUNCTION__+": C contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(y)>=n,__FUNCTION__+": length(Y)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(y,n),__FUNCTION__+": Y contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(x)>=n,__FUNCTION__+": rows(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(x)>=m,__FUNCTION__+": cols(X)<M!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(x,n,m),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- initialization
|
|
state.m_npoints=n;
|
|
state.m_wkind=0;
|
|
state.m_m=m;
|
|
state.m_k=k;
|
|
//--- function call
|
|
LSFitSetCond(state,0.0,0.0,0);
|
|
//--- function call
|
|
LSFitSetStpMax(state,0.0);
|
|
//--- function call
|
|
LSFitSetXRep(state,false);
|
|
//--- allocation
|
|
state.m_taskx.Resize(n,m);
|
|
ArrayResizeAL(state.m_tasky,n);
|
|
ArrayResizeAL(state.m_c,k);
|
|
state.m_h.Resize(k,k);
|
|
ArrayResizeAL(state.m_x,m);
|
|
ArrayResizeAL(state.m_g,k);
|
|
//--- copy
|
|
for(i_=0;i_<=k-1;i_++)
|
|
state.m_c[i_]=c[i_];
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=m-1;i_++)
|
|
state.m_taskx[i].Set(i_,x[i][i_]);
|
|
state.m_tasky[i]=y[i];
|
|
}
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_s,k);
|
|
ArrayResizeAL(state.m_bndl,k);
|
|
ArrayResizeAL(state.m_bndu,k);
|
|
//--- change values
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
state.m_s[i]=1.0;
|
|
state.m_bndl[i]=CInfOrNaN::NegativeInfinity();
|
|
state.m_bndu[i]=CInfOrNaN::PositiveInfinity();
|
|
}
|
|
//--- change values
|
|
state.m_optalgo=2;
|
|
state.m_prevnpt=-1;
|
|
state.m_prevalgo=-1;
|
|
//--- function call
|
|
CMinLM::MinLMCreateFGH(k,state.m_c,state.m_optstate);
|
|
//--- function call
|
|
LSFitClearRequestFields(state);
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_rstate.ia,5);
|
|
ArrayResizeAL(state.m_rstate.ra,3);
|
|
state.m_rstate.stage=-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Stopping conditions for nonlinear least squares fitting. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| EpsF - stopping criterion. Algorithm stops if |
|
|
//| |F(k+1)-F(k)| <= EpsF*max{|F(k)|, |F(k+1)|, 1} |
|
|
//| EpsX - >=0 |
|
|
//| The subroutine finishes its work if on k+1-th |
|
|
//| iteration the condition |v|<=EpsX is fulfilled, |
|
|
//| where: |
|
|
//| * |.| means Euclidian norm |
|
|
//| * v - scaled step vector, v[i]=dx[i]/s[i] |
|
|
//| * dx - ste pvector, dx=X(k+1)-X(k) |
|
|
//| * s - scaling coefficients set by LSFitSetScale()|
|
|
//| MaxIts - maximum number of iterations. If MaxIts=0, the |
|
|
//| number of iterations is unlimited. Only |
|
|
//| Levenberg-Marquardt iterations are counted |
|
|
//| (L-BFGS/CG iterations are NOT counted because |
|
|
//| their cost is very low compared to that of LM). |
|
|
//| NOTE |
|
|
//| Passing EpsF=0, EpsX=0 and MaxIts=0 (simultaneously) will lead to|
|
|
//| automatic stopping criterion selection (according to the scheme |
|
|
//| used by MINLM unit). |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitSetCond(CLSFitState &state,const double epsf,
|
|
const double epsx,const int maxits)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsf),__FUNCTION__+": EpsF is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((double)(epsf)>=0.0,__FUNCTION__+": negative EpsF!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsx),__FUNCTION__+": EpsX is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((double)(epsx)>=0.0,__FUNCTION__+": negative EpsX!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(maxits>=0,__FUNCTION__+": negative MaxIts!"))
|
|
return;
|
|
//--- change values
|
|
state.m_epsf=epsf;
|
|
state.m_epsx=epsx;
|
|
state.m_maxits=maxits;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| 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 you optimize target function which |
|
|
//| contains exp() or other fast growing functions, and optimization |
|
|
//| algorithm makes too large steps which leads 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. |
|
|
//| NOTE: non-zero StpMax leads to moderate performance degradation |
|
|
//| because intermediate step of preconditioned L-BFGS optimization |
|
|
//| is incompatible with limits on step size. |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitSetStpMax(CLSFitState &state,const double stpmax)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(stpmax>=0.0,__FUNCTION__+": StpMax<0!"))
|
|
return;
|
|
//--- change value
|
|
state.m_stpmax=stpmax;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function turns on/off reporting. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NeedXRep- whether iteration reports are needed or not |
|
|
//| When reports are needed, State.C (current parameters) and State. |
|
|
//| F (current value of fitting function) are reported. |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitSetXRep(CLSFitState &state,const bool needxrep)
|
|
{
|
|
//--- change value
|
|
state.m_xrep=needxrep;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets scaling coefficients for underlying optimizer.|
|
|
//| ALGLIB optimizers use scaling matrices to test stopping |
|
|
//| conditions (step size and gradient are scaled before comparison |
|
|
//| with tolerances). Scale of the I-th variable is a translation |
|
|
//| invariant measure of: |
|
|
//| a) "how large" the variable is |
|
|
//| b) how large the step should be to make significant changes in |
|
|
//| the function |
|
|
//| Generally, scale is NOT considered to be a form of |
|
|
//| preconditioner. But LM optimizer is unique in that it uses |
|
|
//| scaling matrix both in the stopping condition tests and as |
|
|
//| Marquardt damping factor. |
|
|
//| Proper scaling is very important for the algorithm performance. |
|
|
//| It is less important for the quality of results, but still has |
|
|
//| some influence (it is easier to converge when variables are |
|
|
//| properly scaled, so premature stopping is possible when very |
|
|
//| badly scalled variables are combined with relaxed stopping |
|
|
//| conditions). |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure stores algorithm state |
|
|
//| S - array[N], non-zero scaling coefficients |
|
|
//| S[i] may be negative, sign doesn't matter. |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitSetScale(CLSFitState &state,double &s[])
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(s)>=state.m_k,__FUNCTION__+": Length(S)<K"))
|
|
return;
|
|
for(i=0;i<=state.m_k-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(s[i]),__FUNCTION__+": S contains infinite or NAN elements"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((double)(s[i])!=0.0,__FUNCTION__+": S contains infinite or NAN elements"))
|
|
return;
|
|
//--- change values
|
|
state.m_s[i]=s[i];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets boundary constraints for underlying optimizer |
|
|
//| Boundary constraints are inactive by default (after initial |
|
|
//| creation). They are preserved until explicitly turned off with |
|
|
//| another SetBC() call. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure stores algorithm state |
|
|
//| BndL - lower bounds, array[K]. |
|
|
//| If some (all) variables are unbounded, you may |
|
|
//| specify very small number or -INF (latter is |
|
|
//| recommended because it will allow solver to use |
|
|
//| better algorithm). |
|
|
//| BndU - upper bounds, array[K]. |
|
|
//| If some (all) variables are unbounded, you may |
|
|
//| specify very large number or +INF (latter is |
|
|
//| recommended because it will allow solver to use |
|
|
//| better algorithm). |
|
|
//| NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case |
|
|
//| I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. |
|
|
//| NOTE 2: unlike other constrained optimization algorithms, this |
|
|
//| solver has following useful properties: |
|
|
//| * bound constraints are always satisfied exactly |
|
|
//| * function is evaluated only INSIDE area specified by bound |
|
|
//| constraints |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitSetBC(CLSFitState &state,double &bndl[],
|
|
double &bndu[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int k=0;
|
|
//--- initialization
|
|
k=state.m_k;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(bndl)>=k,__FUNCTION__+": Length(BndL)<K"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(bndu)>=k,__FUNCTION__+": Length(BndU)<K"))
|
|
return;
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(bndl[i]) || CInfOrNaN::IsNegativeInfinity(bndl[i]),__FUNCTION__+": BndL contains NAN or +INF"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(bndu[i]) || CInfOrNaN::IsPositiveInfinity(bndu[i]),__FUNCTION__+": BndU contains NAN or -INF"))
|
|
return;
|
|
//--- check
|
|
if(CMath::IsFinite(bndl[i]) && CMath::IsFinite(bndu[i]))
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(bndl[i]<=bndu[i],__FUNCTION__+": BndL[i]>BndU[i]"))
|
|
return;
|
|
}
|
|
//--- change values
|
|
state.m_bndl[i]=bndl[i];
|
|
state.m_bndu[i]=bndu[i];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Nonlinear least squares fitting results. |
|
|
//| Called after return from LSFitFit(). |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - algorithm state |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Info - completetion code: |
|
|
//| * 1 relative function improvement is no |
|
|
//| more than EpsF. |
|
|
//| * 2 relative step is no more than EpsX. |
|
|
//| * 4 gradient norm is no more than EpsG |
|
|
//| * 5 MaxIts steps was taken |
|
|
//| * 7 stopping conditions are too |
|
|
//| stringent, further improvement is |
|
|
//| impossible |
|
|
//| C - array[0..K-1], solution |
|
|
//| Rep - optimization report. Following fields are set: |
|
|
//| * Rep.TerminationType completetion code: |
|
|
//| * RMSError rms error on the (X,Y). |
|
|
//| * AvgError average error on the (X,Y). |
|
|
//| * AvgRelError average relative error on the|
|
|
//| non-zero Y |
|
|
//| * MaxError maximum error |
|
|
//| NON-WEIGHTED ERRORS ARE |
|
|
//| CALCULATED |
|
|
//| * WRMSError weighted rms error on the |
|
|
//| (X,Y). |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitResults(CLSFitState &state,int &info,double &c[],
|
|
CLSFitReport &rep)
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- initialization
|
|
info=state.m_repterminationtype;
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(c,state.m_k);
|
|
for(i_=0;i_<=state.m_k-1;i_++)
|
|
c[i_]=state.m_c[i_];
|
|
//--- change values
|
|
rep.m_rmserror=state.m_reprmserror;
|
|
rep.m_wrmserror=state.m_repwrmserror;
|
|
rep.m_avgerror=state.m_repavgerror;
|
|
rep.m_avgrelerror=state.m_repavgrelerror;
|
|
rep.m_maxerror=state.m_repmaxerror;
|
|
rep.m_iterationscount=state.m_repiterationscount;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine: automatic scaling for LLS tasks. |
|
|
//| NEVER CALL IT DIRECTLY! |
|
|
//| Maps abscissas to [-1,1], standartizes ordinates and |
|
|
//| correspondingly scales constraints. It also scales weights so |
|
|
//| that max(W[i])=1 |
|
|
//| Transformations performed: |
|
|
//| * X, XC [XA,XB] => [-1,+1] |
|
|
//| transformation makes min(X)=-1, max(X)=+1 |
|
|
//| * Y [SA,SB] => [0,1] |
|
|
//| transformation makes mean(Y)=0, stddev(Y)=1 |
|
|
//| * YC transformed accordingly to SA, SB, DC[I] |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitScaleXY(double &x[],double &y[],double &w[],
|
|
const int n,double &xc[],double &yc[],
|
|
int &dc[],const int k,double &xa,
|
|
double &xb,double &sa,double &sb,
|
|
double &xoriginal[],double &yoriginal[])
|
|
{
|
|
//--- create variables
|
|
double xmin=0;
|
|
double xmax=0;
|
|
int i=0;
|
|
double mx=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
xa=0;
|
|
xb=0;
|
|
sa=0;
|
|
sb=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": incorrect N"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(k>=0,__FUNCTION__+": incorrect K"))
|
|
return;
|
|
//--- Calculate xmin/xmax.
|
|
//--- Force xmin<>xmax.
|
|
xmin=x[0];
|
|
xmax=x[0];
|
|
for(i=1;i<=n-1;i++)
|
|
{
|
|
xmin=MathMin(xmin,x[i]);
|
|
xmax=MathMax(xmax,x[i]);
|
|
}
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
xmin=MathMin(xmin,xc[i]);
|
|
xmax=MathMax(xmax,xc[i]);
|
|
}
|
|
//--- check
|
|
if(xmin==xmax)
|
|
{
|
|
//--- check
|
|
if(xmin==0.0)
|
|
{
|
|
xmin=-1;
|
|
xmax=1;
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(xmin>0.0)
|
|
xmin=0.5*xmin;
|
|
else
|
|
xmax=0.5*xmax;
|
|
}
|
|
}
|
|
//--- Transform abscissas: map [XA,XB] to [0,1]
|
|
//--- Store old X[] in XOriginal[] (it will be used
|
|
//--- to calculate relative error).
|
|
ArrayResizeAL(xoriginal,n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xoriginal[i_]=x[i_];
|
|
//--- change values
|
|
xa=xmin;
|
|
xb=xmax;
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*(x[i]-0.5*(xa+xb))/(xb-xa);
|
|
//--- calculation
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(dc[i]>=0,__FUNCTION__+": internal error!"))
|
|
return;
|
|
xc[i]=2*(xc[i]-0.5*(xa+xb))/(xb-xa);
|
|
yc[i]=yc[i]*MathPow(0.5*(xb-xa),dc[i]);
|
|
}
|
|
//--- Transform function values: map [SA,SB] to [0,1]
|
|
//--- SA=mean(Y),
|
|
//--- SB=SA+stddev(Y).
|
|
//--- Store old Y[] in YOriginal[] (it will be used
|
|
//--- to calculate relative error).
|
|
ArrayResizeAL(yoriginal,n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
yoriginal[i_]=y[i_];
|
|
sa=0;
|
|
for(i=0;i<=n-1;i++)
|
|
sa=sa+y[i];
|
|
sa=sa/n;
|
|
//--- change value
|
|
sb=0;
|
|
for(i=0;i<=n-1;i++)
|
|
sb=sb+CMath::Sqr(y[i]-sa);
|
|
sb=MathSqrt(sb/n)+sa;
|
|
//--- check
|
|
if(sb==sa)
|
|
sb=2*sa;
|
|
//--- check
|
|
if(sb==sa)
|
|
sb=sa+1;
|
|
for(i=0;i<=n-1;i++)
|
|
y[i]=(y[i]-sa)/(sb-sa);
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
//--- check
|
|
if(dc[i]==0)
|
|
yc[i]=(yc[i]-sa)/(sb-sa);
|
|
else
|
|
yc[i]=yc[i]/(sb-sa);
|
|
}
|
|
//--- Scale weights
|
|
mx=0;
|
|
for(i=0;i<=n-1;i++)
|
|
mx=MathMax(mx,MathAbs(w[i]));
|
|
//--- check
|
|
if(mx!=0.0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
w[i]=w[i]/mx;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal spline fitting subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::Spline1DFitInternal(const int st,double &cx[],double &cy[],
|
|
double &cw[],const int n,double &cxc[],
|
|
double &cyc[],int &dc[],const int k,
|
|
const int m,int &info,
|
|
CSpline1DInterpolant &s,
|
|
CSpline1DFitReport &rep)
|
|
{
|
|
//--- create variables
|
|
double v0=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int relcnt=0;
|
|
double xa=0;
|
|
double xb=0;
|
|
double sa=0;
|
|
double sb=0;
|
|
double bl=0;
|
|
double br=0;
|
|
double decay=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double y2[];
|
|
double w2[];
|
|
double sx[];
|
|
double sy[];
|
|
double sd[];
|
|
double tmp[];
|
|
double xoriginal[];
|
|
double yoriginal[];
|
|
double x[];
|
|
double y[];
|
|
double w[];
|
|
double xc[];
|
|
double yc[];
|
|
//--- create matrix
|
|
CMatrixDouble fmatrix;
|
|
CMatrixDouble cmatrix;
|
|
//--- objects of classes
|
|
CLSFitReport lrep;
|
|
CSpline1DInterpolant s2;
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
ArrayCopy(w,cw);
|
|
ArrayCopy(xc,cxc);
|
|
ArrayCopy(yc,cyc);
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(!CAp::Assert(st==0 || st==1,__FUNCTION__+": internal error!"))
|
|
return;
|
|
//--- check
|
|
if(st==0 && m<4)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(st==1 && m<4)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if((n<1 || k<0) || k>=m)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
info=0;
|
|
//--- check
|
|
if(dc[i]<0)
|
|
info=-1;
|
|
//--- check
|
|
if(dc[i]>1)
|
|
info=-1;
|
|
//--- check
|
|
if(info<0)
|
|
return;
|
|
}
|
|
//--- check
|
|
if(st==1 && m%2!=0)
|
|
{
|
|
//--- Hermite fitter must have even number of basis functions
|
|
info=-2;
|
|
return;
|
|
}
|
|
//--- weight decay for correct handling of task which becomes
|
|
//--- degenerate after constraints are applied
|
|
decay=10000*CMath::m_machineepsilon;
|
|
//--- Scale X,Y,XC,YC
|
|
LSFitScaleXY(x,y,w,n,xc,yc,dc,k,xa,xb,sa,sb,xoriginal,yoriginal);
|
|
//--- allocate space,initialize:
|
|
//--- * SX - grid for basis functions
|
|
//--- * SY - values of basis functions at grid points
|
|
//--- * FMatrix- values of basis functions at X[]
|
|
//--- * CMatrix- values (derivatives) of basis functions at XC[]
|
|
ArrayResizeAL(y2,n+m);
|
|
ArrayResizeAL(w2,n+m);
|
|
fmatrix.Resize(n+m,m);
|
|
//--- check
|
|
if(k>0)
|
|
cmatrix.Resize(k,m+1);
|
|
//--- check
|
|
if(st==0)
|
|
{
|
|
//--- allocate space for cubic spline
|
|
ArrayResizeAL(sx,m-2);
|
|
ArrayResizeAL(sy,m-2);
|
|
for(j=0;j<=m-2-1;j++)
|
|
sx[j]=(double)(2*j)/(double)(m-2-1)-1;
|
|
}
|
|
//--- check
|
|
if(st==1)
|
|
{
|
|
//--- allocate space for Hermite spline
|
|
ArrayResizeAL(sx,m/2);
|
|
ArrayResizeAL(sy,m/2);
|
|
ArrayResizeAL(sd,m/2);
|
|
for(j=0;j<=m/2-1;j++)
|
|
sx[j]=(double)(2*j)/(double)(m/2-1)-1;
|
|
}
|
|
//--- Prepare design and constraints matrices:
|
|
//--- * fill constraints matrix
|
|
//--- * fill first N rows of design matrix with values
|
|
//--- * fill next M rows of design matrix with regularizing term
|
|
//--- * append M zeros to Y
|
|
//--- * append M elements,mean(abs(W)) each,to W
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- prepare Jth basis function
|
|
if(st==0)
|
|
{
|
|
//--- cubic spline basis
|
|
for(i=0;i<=m-2-1;i++)
|
|
sy[i]=0;
|
|
bl=0;
|
|
br=0;
|
|
//--- check
|
|
if(j<m-2)
|
|
sy[j]=1;
|
|
//--- check
|
|
if(j==m-2)
|
|
bl=1;
|
|
//--- check
|
|
if(j==m-1)
|
|
br=1;
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(sx,sy,m-2,1,bl,1,br,s2);
|
|
}
|
|
//--- check
|
|
if(st==1)
|
|
{
|
|
//--- Hermite basis
|
|
for(i=0;i<=m/2-1;i++)
|
|
{
|
|
sy[i]=0;
|
|
sd[i]=0;
|
|
}
|
|
//--- check
|
|
if(j%2==0)
|
|
sy[j/2]=1;
|
|
else
|
|
sd[j/2]=1;
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildHermite(sx,sy,sd,m/2,s2);
|
|
}
|
|
//--- values at X[],XC[]
|
|
for(i=0;i<=n-1;i++)
|
|
fmatrix[i].Set(j,CSpline1D::Spline1DCalc(s2,x[i]));
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(dc[i]>=0 && dc[i]<=2,__FUNCTION__+": internal error!"))
|
|
return;
|
|
//--- function call
|
|
CSpline1D::Spline1DDiff(s2,xc[i],v0,v1,v2);
|
|
//--- check
|
|
if(dc[i]==0)
|
|
cmatrix[i].Set(j,v0);
|
|
//--- check
|
|
if(dc[i]==1)
|
|
cmatrix[i].Set(j,v1);
|
|
//--- check
|
|
if(dc[i]==2)
|
|
cmatrix[i].Set(j,v2);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=k-1;i++)
|
|
cmatrix[i].Set(m,yc[i]);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
fmatrix[n+i].Set(j,decay);
|
|
else
|
|
fmatrix[n+i].Set(j,0);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResizeAL(y2,n+m);
|
|
ArrayResizeAL(w2,n+m);
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
y2[i_]=y[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
w2[i_]=w[i_];
|
|
//--- change value
|
|
mx=0;
|
|
for(i=0;i<=n-1;i++)
|
|
mx=mx+MathAbs(w[i]);
|
|
mx=mx/n;
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
y2[n+i]=0;
|
|
w2[n+i]=mx;
|
|
}
|
|
//--- Solve constrained task
|
|
if(k>0)
|
|
{
|
|
//--- solve using regularization
|
|
LSFitLinearWC(y2,w2,fmatrix,cmatrix,n+m,m,k,info,tmp,lrep);
|
|
}
|
|
else
|
|
{
|
|
//--- no constraints,no regularization needed
|
|
LSFitLinearWC(y,w,fmatrix,cmatrix,n,m,k,info,tmp,lrep);
|
|
}
|
|
//--- check
|
|
if(info<0)
|
|
return;
|
|
//--- Generate spline and scale it
|
|
if(st==0)
|
|
{
|
|
//--- cubic spline basis
|
|
for(i_=0;i_<=m-2-1;i_++)
|
|
sy[i_]=tmp[i_];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(sx,sy,m-2,1,tmp[m-2],1,tmp[m-1],s);
|
|
}
|
|
//--- check
|
|
if(st==1)
|
|
{
|
|
//--- Hermite basis
|
|
for(i=0;i<=m/2-1;i++)
|
|
{
|
|
sy[i]=tmp[2*i];
|
|
sd[i]=tmp[2*i+1];
|
|
}
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildHermite(sx,sy,sd,m/2,s);
|
|
}
|
|
//--- function call
|
|
CSpline1D::Spline1DLinTransX(s,2/(xb-xa),-((xa+xb)/(xb-xa)));
|
|
//--- function call
|
|
CSpline1D::Spline1DLinTransY(s,sb-sa,sa);
|
|
//--- Scale absolute errors obtained from LSFitLinearW.
|
|
//--- Relative error should be calculated separately
|
|
//--- (because of shifting/scaling of the task)
|
|
rep.m_taskrcond=lrep.m_taskrcond;
|
|
rep.m_rmserror=lrep.m_rmserror*(sb-sa);
|
|
rep.m_avgerror=lrep.m_avgerror*(sb-sa);
|
|
rep.m_maxerror=lrep.m_maxerror*(sb-sa);
|
|
rep.m_avgrelerror=0;
|
|
relcnt=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(yoriginal[i]!=0.0)
|
|
{
|
|
rep.m_avgrelerror=rep.m_avgrelerror+MathAbs(CSpline1D::Spline1DCalc(s,xoriginal[i])-yoriginal[i])/MathAbs(yoriginal[i]);
|
|
relcnt=relcnt+1;
|
|
}
|
|
}
|
|
//--- check
|
|
if(relcnt!=0)
|
|
rep.m_avgrelerror=rep.m_avgrelerror/relcnt;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal fitting subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitLinearInternal(double &y[],double &w[],
|
|
CMatrixDouble &fmatrix,const int n,
|
|
const int m,int &info,double &c[],
|
|
CLSFitReport &rep)
|
|
{
|
|
//--- create variables
|
|
double threshold=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int relcnt=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double b[];
|
|
double wmod[];
|
|
double tau[];
|
|
double sv[];
|
|
double tmp[];
|
|
double utb[];
|
|
double sutb[];
|
|
//--- create matrix
|
|
CMatrixDouble ft;
|
|
CMatrixDouble q;
|
|
CMatrixDouble l;
|
|
CMatrixDouble r;
|
|
CMatrixDouble u;
|
|
CMatrixDouble vt;
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(n<1 || m<1)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
//--- initialization
|
|
info=1;
|
|
threshold=MathSqrt(CMath::m_machineepsilon);
|
|
//--- Degenerate case,needs special handling
|
|
if(n<m)
|
|
{
|
|
//--- Create design matrix.
|
|
ft.Resize(n,m);
|
|
ArrayResizeAL(b,n);
|
|
ArrayResizeAL(wmod,n);
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=w[j];
|
|
for(i_=0;i_<=m-1;i_++)
|
|
ft[j].Set(i_,v*fmatrix[j][i_]);
|
|
//--- change values
|
|
b[j]=w[j]*y[j];
|
|
wmod[j]=1;
|
|
}
|
|
//--- LQ decomposition and reduction to M=N
|
|
ArrayResizeAL(c,m);
|
|
for(i=0;i<=m-1;i++)
|
|
c[i]=0;
|
|
rep.m_taskrcond=0;
|
|
//--- function call
|
|
COrtFac::RMatrixLQ(ft,n,m,tau);
|
|
//--- function call
|
|
COrtFac::RMatrixLQUnpackQ(ft,n,m,tau,n,q);
|
|
//--- function call
|
|
COrtFac::RMatrixLQUnpackL(ft,n,m,l);
|
|
//--- function call
|
|
LSFitLinearInternal(b,wmod,l,n,n,info,tmp,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
return;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v=tmp[i];
|
|
for(i_=0;i_<=m-1;i_++)
|
|
c[i_]=c[i_]+v*q[i][i_];
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- N>=M. Generate design matrix and reduce to N=M using
|
|
//--- QR decomposition.
|
|
ft.Resize(n,m);
|
|
ArrayResizeAL(b,n);
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=w[j];
|
|
for(i_=0;i_<=m-1;i_++)
|
|
ft[j].Set(i_,v*fmatrix[j][i_]);
|
|
b[j]=w[j]*y[j];
|
|
}
|
|
//--- function call
|
|
COrtFac::RMatrixQR(ft,n,m,tau);
|
|
//--- function call
|
|
COrtFac::RMatrixQRUnpackQ(ft,n,m,tau,m,q);
|
|
//--- function call
|
|
COrtFac::RMatrixQRUnpackR(ft,n,m,r);
|
|
//--- allocation
|
|
ArrayResizeAL(tmp,m);
|
|
for(i=0;i<=m-1;i++)
|
|
tmp[i]=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v=b[i];
|
|
for(i_=0;i_<=m-1;i_++)
|
|
tmp[i_]=tmp[i_]+v*q[i][i_];
|
|
}
|
|
//--- allocation
|
|
ArrayResizeAL(b,m);
|
|
//--- copy
|
|
for(i_=0;i_<=m-1;i_++)
|
|
b[i_]=tmp[i_];
|
|
//--- R contains reduced MxM design upper triangular matrix,
|
|
//--- B contains reduced Mx1 right part.
|
|
//--- Determine system condition number and decide
|
|
//--- should we use triangular solver (faster) or
|
|
//--- SVD-based solver (more stable).
|
|
//--- We can use LU-based RCond estimator for this task.
|
|
rep.m_taskrcond=CRCond::RMatrixLURCondInf(r,m);
|
|
//--- check
|
|
if(rep.m_taskrcond>threshold)
|
|
{
|
|
//--- use QR-based solver
|
|
ArrayResizeAL(c,m);
|
|
c[m-1]=b[m-1]/r[m-1][m-1];
|
|
//--- calculation
|
|
for(i=m-2;i>=0;i--)
|
|
{
|
|
v=0.0;
|
|
for(i_=i+1;i_<=m-1;i_++)
|
|
v+=r[i][i_]*c[i_];
|
|
c[i]=(b[i]-v)/r[i][i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- use SVD-based solver
|
|
if(!CSingValueDecompose::RMatrixSVD(r,m,m,1,1,2,sv,u,vt))
|
|
{
|
|
info=-4;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
ArrayResizeAL(utb,m);
|
|
ArrayResizeAL(sutb,m);
|
|
for(i=0;i<=m-1;i++)
|
|
utb[i]=0;
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
v=b[i];
|
|
for(i_=0;i_<=m-1;i_++)
|
|
utb[i_]=utb[i_]+v*u[i][i_];
|
|
}
|
|
//--- check
|
|
if(sv[0]>0.0)
|
|
{
|
|
rep.m_taskrcond=sv[m-1]/sv[0];
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
//--- check
|
|
if(sv[i]>threshold*sv[0])
|
|
sutb[i]=utb[i]/sv[i];
|
|
else
|
|
sutb[i]=0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
rep.m_taskrcond=0;
|
|
for(i=0;i<=m-1;i++)
|
|
sutb[i]=0;
|
|
}
|
|
//--- allocation
|
|
ArrayResizeAL(c,m);
|
|
for(i=0;i<=m-1;i++)
|
|
c[i]=0;
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
v=sutb[i];
|
|
for(i_=0;i_<=m-1;i_++)
|
|
c[i_]=c[i_]+v*vt[i][i_];
|
|
}
|
|
}
|
|
//--- calculate errors
|
|
rep.m_rmserror=0;
|
|
rep.m_avgerror=0;
|
|
rep.m_avgrelerror=0;
|
|
rep.m_maxerror=0;
|
|
relcnt=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=fmatrix[i][i_]*c[i_];
|
|
//--- change values
|
|
rep.m_rmserror=rep.m_rmserror+CMath::Sqr(v-y[i]);
|
|
rep.m_avgerror=rep.m_avgerror+MathAbs(v-y[i]);
|
|
//--- check
|
|
if(y[i]!=0.0)
|
|
{
|
|
rep.m_avgrelerror=rep.m_avgrelerror+MathAbs(v-y[i])/MathAbs(y[i]);
|
|
relcnt=relcnt+1;
|
|
}
|
|
rep.m_maxerror=MathMax(rep.m_maxerror,MathAbs(v-y[i]));
|
|
}
|
|
//--- change values
|
|
rep.m_rmserror=MathSqrt(rep.m_rmserror/n);
|
|
rep.m_avgerror=rep.m_avgerror/n;
|
|
//--- check
|
|
if(relcnt!=0)
|
|
rep.m_avgrelerror=rep.m_avgrelerror/relcnt;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::LSFitClearRequestFields(CLSFitState &state)
|
|
{
|
|
//--- change values
|
|
state.m_needf=false;
|
|
state.m_needfg=false;
|
|
state.m_needfgh=false;
|
|
state.m_xupdated=false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine, calculates barycentric basis functions. |
|
|
//| Used for efficient simultaneous calculation of N basis functions.|
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::BarycentricCalcBasis(CBarycentricInterpolant &b,
|
|
const double t,double &y[])
|
|
{
|
|
//--- create variables
|
|
double s2=0;
|
|
double s=0;
|
|
double v=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i_=0;
|
|
//--- special case: N=1
|
|
if(b.m_n==1)
|
|
{
|
|
y[0]=1;
|
|
return;
|
|
}
|
|
//--- Here we assume that task is normalized,i.m_e.:
|
|
//--- 1. abs(Y[i])<=1
|
|
//--- 2. abs(W[i])<=1
|
|
//--- 3. X[] is ordered
|
|
//--- First,we decide: should we use "safe" formula (guarded
|
|
//--- against overflow) or fast one?
|
|
s=MathAbs(t-b.m_x[0]);
|
|
for(i=0;i<=b.m_n-1;i++)
|
|
{
|
|
v=b.m_x[i];
|
|
//--- check
|
|
if(v==t)
|
|
{
|
|
for(j=0;j<=b.m_n-1;j++)
|
|
y[j]=0;
|
|
y[i]=1;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- change value
|
|
v=MathAbs(t-v);
|
|
//--- check
|
|
if(v<s)
|
|
s=v;
|
|
}
|
|
s2=0;
|
|
//--- calculation
|
|
for(i=0;i<=b.m_n-1;i++)
|
|
{
|
|
v=s/(t-b.m_x[i]);
|
|
v=v*b.m_w[i];
|
|
y[i]=v;
|
|
s2=s2+v;
|
|
}
|
|
//--- change values
|
|
v=1/s2;
|
|
for(i_=0;i_<=b.m_n-1;i_++)
|
|
y[i_]=v*y[i_];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This is internal function for Chebyshev fitting. |
|
|
//| It assumes that input data are normalized: |
|
|
//| * X/XC belong to [-1,+1], |
|
|
//| * mean(Y)=0, stddev(Y)=1. |
|
|
//| It does not checks inputs for errors. |
|
|
//| This function is used to fit general (shifted) Chebyshev models, |
|
|
//| power basis models or barycentric models. |
|
|
//| INPUT PARAMETERS: |
|
|
//| X - points, array[0..N-1]. |
|
|
//| Y - function values, array[0..N-1]. |
|
|
//| W - weights, array[0..N-1] |
|
|
//| N - number of points, N>0. |
|
|
//| XC - points where polynomial values/derivatives are |
|
|
//| constrained, array[0..K-1]. |
|
|
//| YC - values of constraints, array[0..K-1] |
|
|
//| DC - array[0..K-1], types of constraints: |
|
|
//| * DC[i]=0 means that P(XC[i])=YC[i] |
|
|
//| * DC[i]=1 means that P'(XC[i])=YC[i] |
|
|
//| K - number of constraints, 0<=K<M. |
|
|
//| K=0 means no constraints (XC/YC/DC are not used in |
|
|
//| such cases) |
|
|
//| M - number of basis functions (= polynomial_degree + 1), |
|
|
//| M>=1 |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| Info- same format as in LSFitLinearW() subroutine: |
|
|
//| * Info>0 task is solved |
|
|
//| * Info<=0 an error occured: |
|
|
//| -4 means inconvergence of internal SVD |
|
|
//| -3 means inconsistent constraints |
|
|
//| C - interpolant in Chebyshev form; [-1,+1] is used as |
|
|
//| base interval |
|
|
//| Rep - report, same format as in LSFitLinearW() subroutine. |
|
|
//| Following fields are set: |
|
|
//| * RMSError rms error on the (X,Y). |
|
|
//| * AvgError average error on the (X,Y). |
|
|
//| * AvgRelError average relative error on the |
|
|
//| non-zero Y |
|
|
//| * MaxError maximum error |
|
|
//| NON-WEIGHTED ERRORS ARE CALCULATED |
|
|
//| IMPORTANT: |
|
|
//| this subroitine doesn't calculate task's condition number for|
|
|
//| K<>0. |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::InternalChebyshevFit(double &x[],double &y[],double &w[],
|
|
const int n,double &cxc[],double &cyc[],
|
|
int &dc[],const int k,const int m,
|
|
int &info,double &c[],CLSFitReport &rep)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double mx=0;
|
|
double decay=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double y2[];
|
|
double w2[];
|
|
double tmp[];
|
|
double tmp2[];
|
|
double tmpdiff[];
|
|
double bx[];
|
|
double by[];
|
|
double bw[];
|
|
double xc[];
|
|
double yc[];
|
|
//--- create matrix
|
|
CMatrixDouble fmatrix;
|
|
CMatrixDouble cmatrix;
|
|
//--- copy arrays
|
|
ArrayCopy(xc,cxc);
|
|
ArrayCopy(yc,cyc);
|
|
//--- initialization
|
|
info=0;
|
|
//--- weight decay for correct handling of task which becomes
|
|
//--- degenerate after constraints are applied
|
|
decay=10000*CMath::m_machineepsilon;
|
|
//--- allocate space,initialize/fill:
|
|
//--- * FMatrix- values of basis functions at X[]
|
|
//--- * CMatrix- values (derivatives) of basis functions at XC[]
|
|
//--- * fill constraints matrix
|
|
//--- * fill first N rows of design matrix with values
|
|
//--- * fill next M rows of design matrix with regularizing term
|
|
//--- * append M zeros to Y
|
|
//--- * append M elements,mean(abs(W)) each,to W
|
|
ArrayResizeAL(y2,n+m);
|
|
ArrayResizeAL(w2,n+m);
|
|
ArrayResizeAL(tmp,m);
|
|
ArrayResizeAL(tmpdiff,m);
|
|
fmatrix.Resize(n+m,m);
|
|
//--- check
|
|
if(k>0)
|
|
cmatrix.Resize(k,m+1);
|
|
//--- Fill design matrix,Y2,W2:
|
|
//--- * first N rows with basis functions for original points
|
|
//--- * next M rows with decay terms
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- prepare Ith row
|
|
//--- use Tmp for calculations to avoid multidimensional arrays overhead
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- check
|
|
if(j==0)
|
|
tmp[j]=1;
|
|
else
|
|
{
|
|
//--- check
|
|
if(j==1)
|
|
tmp[j]=x[i];
|
|
else
|
|
tmp[j]=2*x[i]*tmp[j-1]-tmp[j-2];
|
|
}
|
|
}
|
|
//--- copy
|
|
for(i_=0;i_<=m-1;i_++)
|
|
fmatrix[i].Set(i_,tmp[i_]);
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
fmatrix[n+i].Set(j,decay);
|
|
else
|
|
fmatrix[n+i].Set(j,0);
|
|
}
|
|
}
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
y2[i_]=y[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
w2[i_]=w[i_];
|
|
//--- change value
|
|
mx=0;
|
|
for(i=0;i<=n-1;i++)
|
|
mx=mx+MathAbs(w[i]);
|
|
mx=mx/n;
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
y2[n+i]=0;
|
|
w2[n+i]=mx;
|
|
}
|
|
//--- fill constraints matrix
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
//--- prepare Ith row
|
|
//--- use Tmp for basis function values,
|
|
//--- TmpDiff for basos function derivatives
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- check
|
|
if(j==0)
|
|
{
|
|
tmp[j]=1;
|
|
tmpdiff[j]=0;
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(j==1)
|
|
{
|
|
tmp[j]=xc[i];
|
|
tmpdiff[j]=1;
|
|
}
|
|
else
|
|
{
|
|
tmp[j]=2*xc[i]*tmp[j-1]-tmp[j-2];
|
|
tmpdiff[j]=2*(tmp[j-1]+xc[i]*tmpdiff[j-1])-tmpdiff[j-2];
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(dc[i]==0)
|
|
{
|
|
for(i_=0;i_<=m-1;i_++)
|
|
cmatrix[i].Set(i_,tmp[i_]);
|
|
}
|
|
//--- check
|
|
if(dc[i]==1)
|
|
{
|
|
for(i_=0;i_<=m-1;i_++)
|
|
cmatrix[i].Set(i_,tmpdiff[i_]);
|
|
}
|
|
cmatrix[i].Set(m,yc[i]);
|
|
}
|
|
//--- Solve constrained task
|
|
if(k>0)
|
|
{
|
|
//--- solve using regularization
|
|
LSFitLinearWC(y2,w2,fmatrix,cmatrix,n+m,m,k,info,c,rep);
|
|
}
|
|
else
|
|
{
|
|
//--- no constraints,no regularization needed
|
|
LSFitLinearWC(y,w,fmatrix,cmatrix,n,m,0,info,c,rep);
|
|
}
|
|
//--- check
|
|
if(info<0)
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal Floater-Hormann fitting subroutine for fixed D |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::BarycentricFitWCFixedD(double &cx[],double &cy[],
|
|
double &cw[],const int n,
|
|
double &cxc[],double &cyc[],
|
|
int &dc[],const int k,const int m,
|
|
const int d,int &info,
|
|
CBarycentricInterpolant &b,
|
|
CBarycentricFitReport &rep)
|
|
{
|
|
//--- create variables
|
|
double v0=0;
|
|
double v1=0;
|
|
double mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int relcnt=0;
|
|
double xa=0;
|
|
double xb=0;
|
|
double sa=0;
|
|
double sb=0;
|
|
double decay=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double y2[];
|
|
double w2[];
|
|
double sx[];
|
|
double sy[];
|
|
double sbf[];
|
|
double xoriginal[];
|
|
double yoriginal[];
|
|
double tmp[];
|
|
double x[];
|
|
double y[];
|
|
double w[];
|
|
double xc[];
|
|
double yc[];
|
|
//--- create matrix
|
|
CMatrixDouble fmatrix;
|
|
CMatrixDouble cmatrix;
|
|
//--- objects of classes
|
|
CLSFitReport lrep;
|
|
CBarycentricInterpolant b2;
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
ArrayCopy(w,cw);
|
|
ArrayCopy(xc,cxc);
|
|
ArrayCopy(yc,cyc);
|
|
//--- initialization
|
|
info=0;
|
|
//--- check
|
|
if(((n<1 || m<2) || k<0) || k>=m)
|
|
{
|
|
info=-1;
|
|
return;
|
|
}
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
info=0;
|
|
//--- check
|
|
if(dc[i]<0)
|
|
info=-1;
|
|
//--- check
|
|
if(dc[i]>1)
|
|
info=-1;
|
|
//--- check
|
|
if(info<0)
|
|
return;
|
|
}
|
|
//--- weight decay for correct handling of task which becomes
|
|
//--- degenerate after constraints are applied
|
|
decay=10000*CMath::m_machineepsilon;
|
|
//--- Scale X,Y,XC,YC
|
|
LSFitScaleXY(x,y,w,n,xc,yc,dc,k,xa,xb,sa,sb,xoriginal,yoriginal);
|
|
//--- allocate space,initialize:
|
|
//--- * FMatrix- values of basis functions at X[]
|
|
//--- * CMatrix- values (derivatives) of basis functions at XC[]
|
|
ArrayResizeAL(y2,n+m);
|
|
ArrayResizeAL(w2,n+m);
|
|
fmatrix.Resize(n+m,m);
|
|
//--- check
|
|
if(k>0)
|
|
cmatrix.Resize(k,m+1);
|
|
//--- allocation
|
|
ArrayResizeAL(y2,n+m);
|
|
ArrayResizeAL(w2,n+m);
|
|
//--- Prepare design and constraints matrices:
|
|
//--- * fill constraints matrix
|
|
//--- * fill first N rows of design matrix with values
|
|
//--- * fill next M rows of design matrix with regularizing term
|
|
//--- * append M zeros to Y
|
|
//--- * append M elements,mean(abs(W)) each,to W
|
|
ArrayResizeAL(sx,m);
|
|
ArrayResizeAL(sy,m);
|
|
ArrayResizeAL(sbf,m);
|
|
for(j=0;j<=m-1;j++)
|
|
sx[j]=(double)(2*j)/(double)(m-1)-1;
|
|
for(i=0;i<=m-1;i++)
|
|
sy[i]=1;
|
|
//--- function call
|
|
CRatInt::BarycentricBuildFloaterHormann(sx,sy,m,d,b2);
|
|
//--- change value
|
|
mx=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- function call
|
|
BarycentricCalcBasis(b2,x[i],sbf);
|
|
for(i_=0;i_<=m-1;i_++)
|
|
fmatrix[i].Set(i_,sbf[i_]);
|
|
//--- change values
|
|
y2[i]=y[i];
|
|
w2[i]=w[i];
|
|
mx=mx+MathAbs(w[i])/n;
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
fmatrix[n+i].Set(j,decay);
|
|
else
|
|
fmatrix[n+i].Set(j,0);
|
|
}
|
|
//--- change values
|
|
y2[n+i]=0;
|
|
w2[n+i]=mx;
|
|
}
|
|
//--- check
|
|
if(k>0)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
sy[i]=0;
|
|
sy[j]=1;
|
|
//--- function call
|
|
CRatInt::BarycentricBuildFloaterHormann(sx,sy,m,d,b2);
|
|
//--- calculation
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(dc[i]>=0 && dc[i]<=1,__FUNCTION__+": internal error!"))
|
|
return;
|
|
//--- function call
|
|
CRatInt::BarycentricDiff1(b2,xc[i],v0,v1);
|
|
//--- check
|
|
if(dc[i]==0)
|
|
cmatrix[i].Set(j,v0);
|
|
//--- check
|
|
if(dc[i]==1)
|
|
cmatrix[i].Set(j,v1);
|
|
}
|
|
}
|
|
for(i=0;i<=k-1;i++)
|
|
cmatrix[i].Set(m,yc[i]);
|
|
}
|
|
//--- Solve constrained task
|
|
if(k>0)
|
|
{
|
|
//--- solve using regularization
|
|
LSFitLinearWC(y2,w2,fmatrix,cmatrix,n+m,m,k,info,tmp,lrep);
|
|
}
|
|
else
|
|
{
|
|
//--- no constraints,no regularization needed
|
|
LSFitLinearWC(y,w,fmatrix,cmatrix,n,m,k,info,tmp,lrep);
|
|
}
|
|
//--- check
|
|
if(info<0)
|
|
return;
|
|
//--- Generate interpolant and scale it
|
|
for(i_=0;i_<=m-1;i_++)
|
|
sy[i_]=tmp[i_];
|
|
//--- function call
|
|
CRatInt::BarycentricBuildFloaterHormann(sx,sy,m,d,b);
|
|
//--- function call
|
|
CRatInt::BarycentricLinTransX(b,2/(xb-xa),-((xa+xb)/(xb-xa)));
|
|
//--- function call
|
|
CRatInt::BarycentricLinTransY(b,sb-sa,sa);
|
|
//--- Scale absolute errors obtained from LSFitLinearW.
|
|
//--- Relative error should be calculated separately
|
|
//--- (because of shifting/scaling of the task)
|
|
rep.m_taskrcond=lrep.m_taskrcond;
|
|
rep.m_rmserror=lrep.m_rmserror*(sb-sa);
|
|
rep.m_avgerror=lrep.m_avgerror*(sb-sa);
|
|
rep.m_maxerror=lrep.m_maxerror*(sb-sa);
|
|
rep.m_avgrelerror=0;
|
|
relcnt=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(yoriginal[i]!=0.0)
|
|
{
|
|
rep.m_avgrelerror=rep.m_avgrelerror+MathAbs(CRatInt::BarycentricCalc(b,xoriginal[i])-yoriginal[i])/MathAbs(yoriginal[i]);
|
|
relcnt=relcnt+1;
|
|
}
|
|
}
|
|
//--- check
|
|
if(relcnt!=0)
|
|
rep.m_avgrelerror=rep.m_avgrelerror/relcnt;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| NOTES: |
|
|
//| 1. this algorithm is somewhat unusual because it works with |
|
|
//| parameterized function f(C,X), where X is a function argument |
|
|
//| (we have many points which are characterized by different |
|
|
//| argument values), and C is a parameter to fit. |
|
|
//| For example, if we want to do linear fit by f(c0,c1,x) = |
|
|
//| = c0*x+c1, then x will be argument, and {c0,c1} will be |
|
|
//| parameters. |
|
|
//| It is important to understand that this algorithm finds |
|
|
//| minimum in the space of function PARAMETERS (not arguments), |
|
|
//| so it needs derivatives of f() with respect to C, not X. |
|
|
//| In the example above it will need f=c0*x+c1 and |
|
|
//| {df/dc0,df/dc1} = {x,1} instead of {df/dx} = {c0}. |
|
|
//| 2. Callback functions accept C as the first parameter, and X as |
|
|
//| the second |
|
|
//| 3. If state was created with LSFitCreateFG(), algorithm needs |
|
|
//| just function and its gradient, but if state was created with |
|
|
//| LSFitCreateFGH(), algorithm will need function, gradient and |
|
|
//| Hessian. |
|
|
//| According to the said above, there ase several versions of |
|
|
//| this function, which accept different sets of callbacks. |
|
|
//| This flexibility opens way to subtle errors - you may create |
|
|
//| state with LSFitCreateFGH() (optimization using Hessian), but |
|
|
//| call function which does not accept Hessian. So when algorithm|
|
|
//| will request Hessian, there will be no callback to call. In |
|
|
//| this case exception will be thrown. |
|
|
//| Be careful to avoid such errors because there is no way to |
|
|
//| find them at compile time - you can see them at runtime only. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CLSFit::LSFitIteration(CLSFitState &state)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int m=0;
|
|
int k=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double vv=0;
|
|
double relcnt=0;
|
|
int i_=0;
|
|
//--- Reverse communication preparations
|
|
//--- I know it looks ugly,but it works the same way
|
|
//--- anywhere from C++ to Python.
|
|
//
|
|
//--- 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];
|
|
k=state.m_rstate.ia[2];
|
|
i=state.m_rstate.ia[3];
|
|
j=state.m_rstate.ia[4];
|
|
v=state.m_rstate.ra[0];
|
|
vv=state.m_rstate.ra[1];
|
|
relcnt=state.m_rstate.ra[2];
|
|
}
|
|
else
|
|
{
|
|
//--- initialization
|
|
n=-983;
|
|
m=-989;
|
|
k=-834;
|
|
i=900;
|
|
j=-287;
|
|
v=364;
|
|
vv=214;
|
|
relcnt=-338;
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==0)
|
|
{
|
|
//--- change value
|
|
state.m_needf=false;
|
|
//--- check
|
|
if(state.m_wkind==1)
|
|
vv=state.m_w[i];
|
|
else
|
|
vv=1.0;
|
|
//--- change values
|
|
state.m_optstate.m_fi[i]=vv*(state.m_f-state.m_tasky[i]);
|
|
i=i+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_11(state,n,m,k,i,j,v,vv,relcnt));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==1)
|
|
{
|
|
//--- change value
|
|
state.m_needf=false;
|
|
//--- check
|
|
if(state.m_wkind==1)
|
|
vv=state.m_w[i];
|
|
else
|
|
vv=1.0;
|
|
//--- change values
|
|
state.m_optstate.m_f=state.m_optstate.m_f+CMath::Sqr(vv*(state.m_f-state.m_tasky[i]));
|
|
i=i+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_16(state,n,m,k,i,j,v,vv,relcnt));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==2)
|
|
{
|
|
//--- change value
|
|
state.m_needfg=false;
|
|
//--- check
|
|
if(state.m_wkind==1)
|
|
vv=state.m_w[i];
|
|
else
|
|
vv=1.0;
|
|
//--- change values
|
|
state.m_optstate.m_f=state.m_optstate.m_f+CMath::Sqr(vv*(state.m_f-state.m_tasky[i]));
|
|
v=CMath::Sqr(vv)*2*(state.m_f-state.m_tasky[i]);
|
|
for(i_=0;i_<=k-1;i_++)
|
|
state.m_optstate.m_g[i_]=state.m_optstate.m_g[i_]+v*state.m_g[i_];
|
|
i=i+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_21(state,n,m,k,i,j,v,vv,relcnt));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==3)
|
|
{
|
|
//--- change value
|
|
state.m_needfg=false;
|
|
//--- check
|
|
if(state.m_wkind==1)
|
|
vv=state.m_w[i];
|
|
else
|
|
vv=1.0;
|
|
//--- change values
|
|
state.m_optstate.m_fi[i]=vv*(state.m_f-state.m_tasky[i]);
|
|
for(i_=0;i_<=k-1;i_++)
|
|
state.m_optstate.m_j[i].Set(i_,vv*state.m_g[i_]);
|
|
i=i+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_26(state,n,m,k,i,j,v,vv,relcnt));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==4)
|
|
{
|
|
//--- change value
|
|
state.m_needfgh=false;
|
|
//--- check
|
|
if(state.m_wkind==1)
|
|
vv=state.m_w[i];
|
|
else
|
|
vv=1.0;
|
|
//--- change values
|
|
state.m_optstate.m_f=state.m_optstate.m_f+CMath::Sqr(vv*(state.m_f-state.m_tasky[i]));
|
|
v=CMath::Sqr(vv)*2*(state.m_f-state.m_tasky[i]);
|
|
for(i_=0;i_<=k-1;i_++)
|
|
state.m_optstate.m_g[i_]=state.m_optstate.m_g[i_]+v*state.m_g[i_];
|
|
//--- calculation
|
|
for(j=0;j<=k-1;j++)
|
|
{
|
|
v=2*CMath::Sqr(vv)*state.m_g[j];
|
|
for(i_=0;i_<=k-1;i_++)
|
|
state.m_optstate.m_h[j].Set(i_,state.m_optstate.m_h[j][i_]+v*state.m_g[i_]);
|
|
v=2*CMath::Sqr(vv)*(state.m_f-state.m_tasky[i]);
|
|
for(i_=0;i_<=k-1;i_++)
|
|
state.m_optstate.m_h[j].Set(i_,state.m_optstate.m_h[j][i_]+v*state.m_h[j][i_]);
|
|
}
|
|
i=i+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_31(state,n,m,k,i,j,v,vv,relcnt));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==5)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_7(state,n,m,k,i,j,v,vv,relcnt));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==6)
|
|
{
|
|
//--- change values
|
|
state.m_needf=false;
|
|
v=state.m_f;
|
|
//--- check
|
|
if(state.m_wkind==1)
|
|
vv=state.m_w[i];
|
|
else
|
|
vv=1.0;
|
|
//--- change values
|
|
state.m_reprmserror=state.m_reprmserror+CMath::Sqr(v-state.m_tasky[i]);
|
|
state.m_repwrmserror=state.m_repwrmserror+CMath::Sqr(vv*(v-state.m_tasky[i]));
|
|
state.m_repavgerror=state.m_repavgerror+MathAbs(v-state.m_tasky[i]);
|
|
//--- check
|
|
if(state.m_tasky[i]!=0.0)
|
|
{
|
|
state.m_repavgrelerror=state.m_repavgrelerror+MathAbs(v-state.m_tasky[i])/MathAbs(state.m_tasky[i]);
|
|
relcnt=relcnt+1;
|
|
}
|
|
//--- change values
|
|
state.m_repmaxerror=MathMax(state.m_repmaxerror,MathAbs(v-state.m_tasky[i]));
|
|
i=i+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_38(state,n,m,k,i,j,v,vv,relcnt));
|
|
}
|
|
//--- Routine body
|
|
//--- init
|
|
if(state.m_wkind==1)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(state.m_npoints==state.m_nweights,__FUNCTION__+": number of points is not equal to the number of weights"))
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
n=state.m_npoints;
|
|
m=state.m_m;
|
|
k=state.m_k;
|
|
//--- function call
|
|
CMinLM::MinLMSetCond(state.m_optstate,0.0,state.m_epsf,state.m_epsx,state.m_maxits);
|
|
//--- function call
|
|
CMinLM::MinLMSetStpMax(state.m_optstate,state.m_stpmax);
|
|
//--- function call
|
|
CMinLM::MinLMSetXRep(state.m_optstate,state.m_xrep);
|
|
//--- function call
|
|
CMinLM::MinLMSetScale(state.m_optstate,state.m_s);
|
|
//--- function call
|
|
CMinLM::MinLMSetBC(state.m_optstate,state.m_bndl,state.m_bndu);
|
|
//--- Optimize
|
|
return(Func_lbl_7(state,n,m,k,i,j,v,vv,relcnt));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static void CLSFit::Func_lbl_rcomm(CLSFitState &state,int n,int m,int k,int i,
|
|
int j,double v,double vv,double relcnt)
|
|
{
|
|
//--- Saving state
|
|
state.m_rstate.ia[0]=n;
|
|
state.m_rstate.ia[1]=m;
|
|
state.m_rstate.ia[2]=k;
|
|
state.m_rstate.ia[3]=i;
|
|
state.m_rstate.ia[4]=j;
|
|
state.m_rstate.ra[0]=v;
|
|
state.m_rstate.ra[1]=vv;
|
|
state.m_rstate.ra[2]=relcnt;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CLSFit::Func_lbl_7(CLSFitState &state,int &n,int &m,int &k,int &i,
|
|
int &j,double &v,double &vv,double &relcnt)
|
|
{
|
|
//--- check
|
|
if(!CMinLM::MinLMIteration(state.m_optstate))
|
|
{
|
|
//--- function call
|
|
CMinLM::MinLMResults(state.m_optstate,state.m_c,state.m_optrep);
|
|
//--- change values
|
|
state.m_repterminationtype=state.m_optrep.m_terminationtype;
|
|
state.m_repiterationscount=state.m_optrep.m_iterationscount;
|
|
//--- calculate errors
|
|
if(state.m_repterminationtype<=0)
|
|
return(false);
|
|
//--- change values
|
|
state.m_reprmserror=0;
|
|
state.m_repwrmserror=0;
|
|
state.m_repavgerror=0;
|
|
state.m_repavgrelerror=0;
|
|
state.m_repmaxerror=0;
|
|
relcnt=0;
|
|
i=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_38(state,n,m,k,i,j,v,vv,relcnt));
|
|
}
|
|
//--- check
|
|
if(!state.m_optstate.m_needfi)
|
|
{
|
|
//--- check
|
|
if(!state.m_optstate.m_needf)
|
|
return(Func_lbl_14(state,n,m,k,i,j,v,vv,relcnt));
|
|
//--- calculate F=sum (wi*(f(xi,c)-yi))^2
|
|
state.m_optstate.m_f=0;
|
|
i=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_16(state,n,m,k,i,j,v,vv,relcnt));
|
|
}
|
|
//--- calculate f[]=wi*(f(xi,c)-yi)
|
|
i=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_11(state,n,m,k,i,j,v,vv,relcnt));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CLSFit::Func_lbl_11(CLSFitState &state,int &n,int &m,int &k,int &i,
|
|
int &j,double &v,double &vv,double &relcnt)
|
|
{
|
|
//--- check
|
|
if(i>n-1)
|
|
return(Func_lbl_7(state,n,m,k,i,j,v,vv,relcnt));
|
|
//--- copy
|
|
for(int i_=0;i_<=k-1;i_++)
|
|
state.m_c[i_]=state.m_optstate.m_x[i_];
|
|
for(int i_=0;i_<=m-1;i_++)
|
|
state.m_x[i_]=state.m_taskx[i][i_];
|
|
state.m_pointindex=i;
|
|
//--- function call
|
|
LSFitClearRequestFields(state);
|
|
//--- change values
|
|
state.m_needf=true;
|
|
state.m_rstate.stage=0;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,k,i,j,v,vv,relcnt);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CLSFit::Func_lbl_14(CLSFitState &state,int &n,int &m,int &k,int &i,
|
|
int &j,double &v,double &vv,double &relcnt)
|
|
{
|
|
//--- check
|
|
if(!state.m_optstate.m_needfg)
|
|
{
|
|
//--- check
|
|
if(!state.m_optstate.m_needfij)
|
|
return(Func_lbl_24(state,n,m,k,i,j,v,vv,relcnt));
|
|
//--- calculate Fi/jac(Fi)
|
|
i=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_26(state,n,m,k,i,j,v,vv,relcnt));
|
|
}
|
|
//--- calculate F/gradF
|
|
state.m_optstate.m_f=0;
|
|
for(i=0;i<=k-1;i++)
|
|
state.m_optstate.m_g[i]=0;
|
|
i=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_21(state,n,m,k,i,j,v,vv,relcnt));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CLSFit::Func_lbl_16(CLSFitState &state,int &n,int &m,int &k,int &i,
|
|
int &j,double &v,double &vv,double &relcnt)
|
|
{
|
|
//--- check
|
|
if(i>n-1)
|
|
return(Func_lbl_7(state,n,m,k,i,j,v,vv,relcnt));
|
|
//--- copy
|
|
for(int i_=0;i_<=k-1;i_++)
|
|
state.m_c[i_]=state.m_optstate.m_x[i_];
|
|
for(int i_=0;i_<=m-1;i_++)
|
|
state.m_x[i_]=state.m_taskx[i][i_];
|
|
state.m_pointindex=i;
|
|
//--- function call
|
|
LSFitClearRequestFields(state);
|
|
//--- change values
|
|
state.m_needf=true;
|
|
state.m_rstate.stage=1;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,k,i,j,v,vv,relcnt);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CLSFit::Func_lbl_21(CLSFitState &state,int &n,int &m,int &k,int &i,
|
|
int &j,double &v,double &vv,double &relcnt)
|
|
{
|
|
//--- check
|
|
if(i>n-1)
|
|
return(Func_lbl_7(state,n,m,k,i,j,v,vv,relcnt));
|
|
//--- copy
|
|
for(int i_=0;i_<=k-1;i_++)
|
|
state.m_c[i_]=state.m_optstate.m_x[i_];
|
|
for(int i_=0;i_<=m-1;i_++)
|
|
state.m_x[i_]=state.m_taskx[i][i_];
|
|
state.m_pointindex=i;
|
|
//--- function call
|
|
LSFitClearRequestFields(state);
|
|
//--- change values
|
|
state.m_needfg=true;
|
|
state.m_rstate.stage=2;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,k,i,j,v,vv,relcnt);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CLSFit::Func_lbl_24(CLSFitState &state,int &n,int &m,int &k,int &i,
|
|
int &j,double &v,double &vv,double &relcnt)
|
|
{
|
|
//--- check
|
|
if(!state.m_optstate.m_needfgh)
|
|
return(Func_lbl_29(state,n,m,k,i,j,v,vv,relcnt));
|
|
//--- calculate F/grad(F)/hess(F)
|
|
state.m_optstate.m_f=0;
|
|
for(i=0;i<=k-1;i++)
|
|
state.m_optstate.m_g[i]=0;
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
for(j=0;j<=k-1;j++)
|
|
state.m_optstate.m_h[i].Set(j,0);
|
|
}
|
|
//--- change value
|
|
i=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_31(state,n,m,k,i,j,v,vv,relcnt));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CLSFit::Func_lbl_26(CLSFitState &state,int &n,int &m,int &k,int &i,
|
|
int &j,double &v,double &vv,double &relcnt)
|
|
{
|
|
//--- check
|
|
if(i>n-1)
|
|
return(Func_lbl_7(state,n,m,k,i,j,v,vv,relcnt));
|
|
//--- copy
|
|
for(int i_=0;i_<=k-1;i_++)
|
|
state.m_c[i_]=state.m_optstate.m_x[i_];
|
|
for(int i_=0;i_<=m-1;i_++)
|
|
state.m_x[i_]=state.m_taskx[i][i_];
|
|
state.m_pointindex=i;
|
|
//--- function call
|
|
LSFitClearRequestFields(state);
|
|
//--- change values
|
|
state.m_needfg=true;
|
|
state.m_rstate.stage=3;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,k,i,j,v,vv,relcnt);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CLSFit::Func_lbl_29(CLSFitState &state,int &n,int &m,int &k,int &i,
|
|
int &j,double &v,double &vv,double &relcnt)
|
|
{
|
|
//--- check
|
|
if(!state.m_optstate.m_xupdated)
|
|
return(Func_lbl_7(state,n,m,k,i,j,v,vv,relcnt));
|
|
//--- Report new iteration
|
|
for(int i_=0;i_<=k-1;i_++)
|
|
state.m_c[i_]=state.m_optstate.m_x[i_];
|
|
state.m_f=state.m_optstate.m_f;
|
|
//--- function call
|
|
LSFitClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=5;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,k,i,j,v,vv,relcnt);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CLSFit::Func_lbl_31(CLSFitState &state,int &n,int &m,int &k,int &i,
|
|
int &j,double &v,double &vv,double &relcnt)
|
|
{
|
|
//--- check
|
|
if(i>n-1)
|
|
return(Func_lbl_7(state,n,m,k,i,j,v,vv,relcnt));
|
|
//--- copy
|
|
for(int i_=0;i_<=k-1;i_++)
|
|
state.m_c[i_]=state.m_optstate.m_x[i_];
|
|
for(int i_=0;i_<=m-1;i_++)
|
|
state.m_x[i_]=state.m_taskx[i][i_];
|
|
state.m_pointindex=i;
|
|
//--- function call
|
|
LSFitClearRequestFields(state);
|
|
//--- change values
|
|
state.m_needfgh=true;
|
|
state.m_rstate.stage=4;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,k,i,j,v,vv,relcnt);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for LSFitIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CLSFit::Func_lbl_38(CLSFitState &state,int &n,int &m,int &k,int &i,
|
|
int &j,double &v,double &vv,double &relcnt)
|
|
{
|
|
//--- check
|
|
if(i>n-1)
|
|
{
|
|
//--- change values
|
|
state.m_reprmserror=MathSqrt(state.m_reprmserror/n);
|
|
state.m_repwrmserror=MathSqrt(state.m_repwrmserror/n);
|
|
state.m_repavgerror=state.m_repavgerror/n;
|
|
//--- check
|
|
if(relcnt!=0.0)
|
|
state.m_repavgrelerror=state.m_repavgrelerror/relcnt;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- copy
|
|
for(int i_=0;i_<=k-1;i_++)
|
|
state.m_c[i_]=state.m_c[i_];
|
|
for(int i_=0;i_<=m-1;i_++)
|
|
state.m_x[i_]=state.m_taskx[i][i_];
|
|
state.m_pointindex=i;
|
|
//--- function call
|
|
LSFitClearRequestFields(state);
|
|
//--- change values
|
|
state.m_needf=true;
|
|
state.m_rstate.stage=6;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,k,i,j,v,vv,relcnt);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Parametric spline inteprolant: 2-dimensional curve. |
|
|
//| You should not try to access its members directly - use |
|
|
//| PSpline2XXXXXXXX() functions instead. |
|
|
//+------------------------------------------------------------------+
|
|
class CPSpline2Interpolant
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_n;
|
|
bool m_periodic;
|
|
CSpline1DInterpolant m_x;
|
|
CSpline1DInterpolant m_y;
|
|
//--- array
|
|
double m_p[];
|
|
//--- constructor, destructor
|
|
CPSpline2Interpolant(void);
|
|
~CPSpline2Interpolant(void);
|
|
//--- copy
|
|
void Copy(CPSpline2Interpolant &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CPSpline2Interpolant::CPSpline2Interpolant(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CPSpline2Interpolant::~CPSpline2Interpolant(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CPSpline2Interpolant::Copy(CPSpline2Interpolant &obj)
|
|
{
|
|
//--- copy variables
|
|
m_n=obj.m_n;
|
|
m_periodic=obj.m_periodic;
|
|
m_x.Copy(obj.m_x);
|
|
m_y.Copy(obj.m_y);
|
|
//--- copy array
|
|
ArrayCopy(m_p,obj.m_p);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Parametric spline inteprolant: 2-dimensional curve. |
|
|
//| You should not try to access its members directly - use |
|
|
//| PSpline2XXXXXXXX() functions instead. |
|
|
//+------------------------------------------------------------------+
|
|
class CPSpline2InterpolantShell
|
|
{
|
|
private:
|
|
CPSpline2Interpolant m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CPSpline2InterpolantShell(void);
|
|
CPSpline2InterpolantShell(CPSpline2Interpolant &obj);
|
|
~CPSpline2InterpolantShell(void);
|
|
//--- method
|
|
CPSpline2Interpolant *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CPSpline2InterpolantShell::CPSpline2InterpolantShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CPSpline2InterpolantShell::CPSpline2InterpolantShell(CPSpline2Interpolant &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CPSpline2InterpolantShell::~CPSpline2InterpolantShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CPSpline2Interpolant *CPSpline2InterpolantShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Parametric spline inteprolant: 3-dimensional curve. |
|
|
//| You should not try to access its members directly - use |
|
|
//| PSpline3XXXXXXXX() functions instead. |
|
|
//+------------------------------------------------------------------+
|
|
class CPSpline3Interpolant
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_n;
|
|
bool m_periodic;
|
|
CSpline1DInterpolant m_x;
|
|
CSpline1DInterpolant m_y;
|
|
CSpline1DInterpolant m_z;
|
|
//--- array
|
|
double m_p[];
|
|
//--- constructor, destructor
|
|
CPSpline3Interpolant(void);
|
|
~CPSpline3Interpolant(void);
|
|
//--- copy
|
|
void Copy(CPSpline3Interpolant &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CPSpline3Interpolant::CPSpline3Interpolant(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CPSpline3Interpolant::~CPSpline3Interpolant(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CPSpline3Interpolant::Copy(CPSpline3Interpolant &obj)
|
|
{
|
|
//--- copy variables
|
|
m_n=obj.m_n;
|
|
m_periodic=obj.m_periodic;
|
|
m_x.Copy(obj.m_x);
|
|
m_y.Copy(obj.m_y);
|
|
m_z.Copy(obj.m_z);
|
|
//--- copy array
|
|
ArrayCopy(m_p,obj.m_p);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Parametric spline inteprolant: 3-dimensional curve. |
|
|
//| You should not try to access its members directly - use |
|
|
//| PSpline3XXXXXXXX() functions instead. |
|
|
//+------------------------------------------------------------------+
|
|
class CPSpline3InterpolantShell
|
|
{
|
|
private:
|
|
CPSpline3Interpolant m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CPSpline3InterpolantShell(void);
|
|
CPSpline3InterpolantShell(CPSpline3Interpolant &obj);
|
|
~CPSpline3InterpolantShell(void);
|
|
//--- method
|
|
CPSpline3Interpolant *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CPSpline3InterpolantShell::CPSpline3InterpolantShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CPSpline3InterpolantShell::CPSpline3InterpolantShell(CPSpline3Interpolant &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CPSpline3InterpolantShell::~CPSpline3InterpolantShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CPSpline3Interpolant *CPSpline3InterpolantShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Parametric spline |
|
|
//+------------------------------------------------------------------+
|
|
class CPSpline
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void PSpline2Par(CMatrixDouble &xy,const int n,const int pt,double &p[]);
|
|
static void PSpline3Par(CMatrixDouble &xy,const int n,const int pt,double &p[]);
|
|
public:
|
|
//--- constructor, destructor
|
|
CPSpline(void);
|
|
~CPSpline(void);
|
|
//--- public methods
|
|
static void PSpline2Build(CMatrixDouble &cxy,const int n,const int st,const int pt,CPSpline2Interpolant &p);
|
|
static void PSpline3Build(CMatrixDouble &cxy,const int n,const int st,const int pt,CPSpline3Interpolant &p);
|
|
static void PSpline2BuildPeriodic(CMatrixDouble &cxy,const int n,const int st,const int pt,CPSpline2Interpolant &p);
|
|
static void PSpline3BuildPeriodic(CMatrixDouble &cxy,const int n,const int st,const int pt,CPSpline3Interpolant &p);
|
|
static void PSpline2ParameterValues(CPSpline2Interpolant &p,int &n,double &t[]);
|
|
static void PSpline3ParameterValues(CPSpline3Interpolant &p,int &n,double &t[]);
|
|
static void PSpline2Calc(CPSpline2Interpolant &p,double t,double &x,double &y);
|
|
static void PSpline3Calc(CPSpline3Interpolant &p,double t,double &x,double &y,double &z);
|
|
static void PSpline2Tangent(CPSpline2Interpolant &p,double t,double &x,double &y);
|
|
static void PSpline3Tangent(CPSpline3Interpolant &p,double t,double &x,double &y,double &z);
|
|
static void PSpline2Diff(CPSpline2Interpolant &p,double t,double &x,double &dx,double &y,double &dy);
|
|
static void PSpline3Diff(CPSpline3Interpolant &p,double t,double &x,double &dx,double &y,double &dy,double &z,double &dz);
|
|
static void PSpline2Diff2(CPSpline2Interpolant &p,double t,double &x,double &dx,double &d2x,double &y,double &dy,double &d2y);
|
|
static void PSpline3Diff2(CPSpline3Interpolant &p,double t,double &x,double &dx,double &d2x,double &y,double &dy,double &d2y,double &z,double &dz,double &d2z);
|
|
static double PSpline2ArcLength(CPSpline2Interpolant &p,const double a,const double b);
|
|
static double PSpline3ArcLength(CPSpline3Interpolant &p,const double a,const double b);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CPSpline::CPSpline(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CPSpline::~CPSpline(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function builds non-periodic 2-dimensional parametric |
|
|
//| spline which starts at (X[0],Y[0]) and ends at (X[N-1],Y[N-1]). |
|
|
//| INPUT PARAMETERS: |
|
|
//| XY - points, array[0..N-1,0..1]. |
|
|
//| XY[I,0:1] corresponds to the Ith point. |
|
|
//| Order of points is important! |
|
|
//| N - points count, N>=5 for Akima splines, N>=2 for other |
|
|
//| types of splines. |
|
|
//| ST - spline type: |
|
|
//| * 0 Akima spline |
|
|
//| * 1 parabolically terminated Catmull-Rom spline |
|
|
//| (Tension=0) |
|
|
//| * 2 parabolically terminated cubic spline |
|
|
//| PT - parameterization type: |
|
|
//| * 0 uniform |
|
|
//| * 1 chord length |
|
|
//| * 2 centripetal |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| P - parametric spline interpolant |
|
|
//| NOTES: |
|
|
//| * this function assumes that there all consequent points are |
|
|
//| distinct. I.e. (x0,y0)<>(x1,y1), (x1,y1)<>(x2,y2), |
|
|
//| (x2,y2)<>(x3,y3) and so on. However, non-consequent points may |
|
|
//| coincide, i.e. we can have (x0,y0) = (x2,y2). |
|
|
//+------------------------------------------------------------------+
|
|
static void CPSpline::PSpline2Build(CMatrixDouble &cxy,const int n,
|
|
const int st,const int pt,
|
|
CPSpline2Interpolant &p)
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- create array
|
|
double tmp[];
|
|
//--- copy matrix
|
|
CMatrixDouble xy;
|
|
xy=cxy;
|
|
//--- check
|
|
if(!CAp::Assert(st>=0 && st<=2,__FUNCTION__+": incorrect spline type!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(pt>=0 && pt<=2,__FUNCTION__+": incorrect parameterization type!"))
|
|
return;
|
|
//--- check
|
|
if(st==0)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n>=5,__FUNCTION__+": N<5 (minimum value for Akima splines)!"))
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n>=2,__FUNCTION__+": N<2!"))
|
|
return;
|
|
}
|
|
//--- Prepare
|
|
p.m_n=n;
|
|
p.m_periodic=false;
|
|
//--- allocation
|
|
ArrayResizeAL(tmp,n);
|
|
//--- Build parameterization,check that all parameters are distinct
|
|
PSpline2Par(xy,n,pt,p.m_p);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::AreDistinct(p.m_p,n),__FUNCTION__+": consequent points are too close!"))
|
|
return;
|
|
//--- Build splines
|
|
if(st==0)
|
|
{
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp[i_]=xy[i_][0];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildAkima(p.m_p,tmp,n,p.m_x);
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp[i_]=xy[i_][1];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildAkima(p.m_p,tmp,n,p.m_y);
|
|
}
|
|
//--- check
|
|
if(st==1)
|
|
{
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp[i_]=xy[i_][0];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n,0,0.0,p.m_x);
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp[i_]=xy[i_][1];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n,0,0.0,p.m_y);
|
|
}
|
|
//--- check
|
|
if(st==2)
|
|
{
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp[i_]=xy[i_][0];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n,0,0.0,0,0.0,p.m_x);
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp[i_]=xy[i_][1];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n,0,0.0,0,0.0,p.m_y);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function builds non-periodic 3-dimensional parametric spline|
|
|
//| which starts at (X[0],Y[0],Z[0]) and ends at |
|
|
//| (X[N-1],Y[N-1],Z[N-1]). |
|
|
//| Same as PSpline2Build() function, but for 3D, so we won't |
|
|
//| duplicate its description here. |
|
|
//+------------------------------------------------------------------+
|
|
static void CPSpline::PSpline3Build(CMatrixDouble &cxy,const int n,
|
|
const int st,const int pt,
|
|
CPSpline3Interpolant &p)
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- create array
|
|
double tmp[];
|
|
//--- copy matrix
|
|
CMatrixDouble xy;
|
|
xy=cxy;
|
|
//--- check
|
|
if(!CAp::Assert(st>=0 && st<=2,__FUNCTION__+": incorrect spline type!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(pt>=0 && pt<=2,__FUNCTION__+": incorrect parameterization type!"))
|
|
return;
|
|
//--- check
|
|
if(st==0)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n>=5,__FUNCTION__+": N<5 (minimum value for Akima splines)!"))
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n>=2,__FUNCTION__+"PSpline3Build: N<2!"))
|
|
return;
|
|
}
|
|
//--- Prepare
|
|
p.m_n=n;
|
|
p.m_periodic=false;
|
|
//--- allocation
|
|
ArrayResizeAL(tmp,n);
|
|
//--- Build parameterization,check that all parameters are distinct
|
|
PSpline3Par(xy,n,pt,p.m_p);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::AreDistinct(p.m_p,n),__FUNCTION__+": consequent points are too close!"))
|
|
return;
|
|
//--- Build splines
|
|
if(st==0)
|
|
{
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp[i_]=xy[i_][0];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildAkima(p.m_p,tmp,n,p.m_x);
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp[i_]=xy[i_][1];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildAkima(p.m_p,tmp,n,p.m_y);
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp[i_]=xy[i_][2];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildAkima(p.m_p,tmp,n,p.m_z);
|
|
}
|
|
//--- check
|
|
if(st==1)
|
|
{
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp[i_]=xy[i_][0];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n,0,0.0,p.m_x);
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp[i_]=xy[i_][1];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n,0,0.0,p.m_y);
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp[i_]=xy[i_][2];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n,0,0.0,p.m_z);
|
|
}
|
|
//--- check
|
|
if(st==2)
|
|
{
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp[i_]=xy[i_][0];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n,0,0.0,0,0.0,p.m_x);
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp[i_]=xy[i_][1];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n,0,0.0,0,0.0,p.m_y);
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp[i_]=xy[i_][2];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n,0,0.0,0,0.0,p.m_z);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function builds periodic 2-dimensional parametric spline |
|
|
//| which starts at (X[0],Y[0]), goes through all points to |
|
|
//| (X[N-1],Y[N-1]) and then back to (X[0],Y[0]). |
|
|
//| INPUT PARAMETERS: |
|
|
//| XY - points, array[0..N-1,0..1]. |
|
|
//| XY[I,0:1] corresponds to the Ith point. |
|
|
//| XY[N-1,0:1] must be different from XY[0,0:1]. |
|
|
//| Order of points is important! |
|
|
//| N - points count, N>=3 for other types of splines. |
|
|
//| ST - spline type: |
|
|
//| * 1 Catmull-Rom spline (Tension=0) with cyclic |
|
|
//| boundary conditions |
|
|
//| * 2 cubic spline with cyclic boundary conditions |
|
|
//| PT - parameterization type: |
|
|
//| * 0 uniform |
|
|
//| * 1 chord length |
|
|
//| * 2 centripetal |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| P - parametric spline interpolant |
|
|
//| NOTES: |
|
|
//| * this function assumes that there all consequent points are |
|
|
//| distinct. I.e. (x0,y0)<>(x1,y1), (x1,y1)<>(x2,y2), |
|
|
//| (x2,y2)<>(x3,y3) and so on. However, non-consequent points may |
|
|
//| coincide, i.e. we can have (x0,y0) = (x2,y2). |
|
|
//| * last point of sequence is NOT equal to the first point. You |
|
|
//| shouldn't make curve "explicitly periodic" by making them |
|
|
//| equal. |
|
|
//+------------------------------------------------------------------+
|
|
static void CPSpline::PSpline2BuildPeriodic(CMatrixDouble &cxy,const int n,
|
|
const int st,const int pt,
|
|
CPSpline2Interpolant &p)
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- create array
|
|
double tmp[];
|
|
//--- create matrix
|
|
CMatrixDouble xyp;
|
|
CMatrixDouble xy;
|
|
//--- copy matrix
|
|
xy=cxy;
|
|
//--- check
|
|
if(!CAp::Assert(st>=1 && st<=2,__FUNCTION__+": incorrect spline type!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(pt>=0 && pt<=2,__FUNCTION__+": incorrect parameterization type!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(n>=3,__FUNCTION__+": N<3!"))
|
|
return;
|
|
//--- Prepare
|
|
p.m_n=n;
|
|
p.m_periodic=true;
|
|
//--- allocation
|
|
ArrayResizeAL(tmp,n+1);
|
|
xyp.Resize(n+1,2);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xyp[i_].Set(0,xy[i_][0]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xyp[i_].Set(1,xy[i_][1]);
|
|
for(i_=0;i_<=1;i_++)
|
|
xyp[n].Set(i_,xy[0][i_]);
|
|
//--- Build parameterization,check that all parameters are distinct
|
|
PSpline2Par(xyp,n+1,pt,p.m_p);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::AreDistinct(p.m_p,n+1),__FUNCTION__+": consequent (or first and last) points are too close!"))
|
|
return;
|
|
//--- Build splines
|
|
if(st==1)
|
|
{
|
|
//--- copy
|
|
for(i_=0;i_<=n;i_++)
|
|
tmp[i_]=xyp[i_][0];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n+1,-1,0.0,p.m_x);
|
|
//--- copy
|
|
for(i_=0;i_<=n;i_++)
|
|
tmp[i_]=xyp[i_][1];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n+1,-1,0.0,p.m_y);
|
|
}
|
|
//--- check
|
|
if(st==2)
|
|
{
|
|
//--- copy
|
|
for(i_=0;i_<=n;i_++)
|
|
tmp[i_]=xyp[i_][0];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n+1,-1,0.0,-1,0.0,p.m_x);
|
|
//--- copy
|
|
for(i_=0;i_<=n;i_++)
|
|
tmp[i_]=xyp[i_][1];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n+1,-1,0.0,-1,0.0,p.m_y);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function builds periodic 3-dimensional parametric spline |
|
|
//| which starts at (X[0],Y[0],Z[0]), goes through all points to |
|
|
//| (X[N-1],Y[N-1],Z[N-1]) and then back to (X[0],Y[0],Z[0]). |
|
|
//| Same as PSpline2Build() function, but for 3D, so we won't |
|
|
//| duplicate its description here. |
|
|
//+------------------------------------------------------------------+
|
|
static void CPSpline::PSpline3BuildPeriodic(CMatrixDouble &cxy,const int n,
|
|
const int st,const int pt,
|
|
CPSpline3Interpolant &p)
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- create array
|
|
double tmp[];
|
|
//--- create matrix
|
|
CMatrixDouble xyp;
|
|
CMatrixDouble xy;
|
|
//--- copy matrix
|
|
xy=cxy;
|
|
//--- check
|
|
if(!CAp::Assert(st>=1 && st<=2,__FUNCTION__+": incorrect spline type!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(pt>=0 && pt<=2,__FUNCTION__+": incorrect parameterization type!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(n>=3,__FUNCTION__+": N<3!"))
|
|
return;
|
|
//--- Prepare
|
|
p.m_n=n;
|
|
p.m_periodic=true;
|
|
//--- allocation
|
|
ArrayResizeAL(tmp,n+1);
|
|
xyp.Resize(n+1,3);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xyp[i_].Set(0,xy[i_][0]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xyp[i_].Set(1,xy[i_][1]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xyp[i_].Set(2,xy[i_][2]);
|
|
for(i_=0;i_<=2;i_++)
|
|
xyp[n].Set(i_,xy[0][i_]);
|
|
//--- Build parameterization,check that all parameters are distinct
|
|
PSpline3Par(xyp,n+1,pt,p.m_p);
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::AreDistinct(p.m_p,n+1),__FUNCTION__+": consequent (or first and last) points are too close!"))
|
|
return;
|
|
//--- Build splines
|
|
if(st==1)
|
|
{
|
|
//--- copy
|
|
for(i_=0;i_<=n;i_++)
|
|
tmp[i_]=xyp[i_][0];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n+1,-1,0.0,p.m_x);
|
|
//--- copy
|
|
for(i_=0;i_<=n;i_++)
|
|
tmp[i_]=xyp[i_][1];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n+1,-1,0.0,p.m_y);
|
|
//--- copy
|
|
for(i_=0;i_<=n;i_++)
|
|
tmp[i_]=xyp[i_][2];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCatmullRom(p.m_p,tmp,n+1,-1,0.0,p.m_z);
|
|
}
|
|
//--- check
|
|
if(st==2)
|
|
{
|
|
//--- copy
|
|
for(i_=0;i_<=n;i_++)
|
|
tmp[i_]=xyp[i_][0];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n+1,-1,0.0,-1,0.0,p.m_x);
|
|
//--- copy
|
|
for(i_=0;i_<=n;i_++)
|
|
tmp[i_]=xyp[i_][1];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n+1,-1,0.0,-1,0.0,p.m_y);
|
|
//--- copy
|
|
for(i_=0;i_<=n;i_++)
|
|
tmp[i_]=xyp[i_][2];
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(p.m_p,tmp,n+1,-1,0.0,-1,0.0,p.m_z);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function returns vector of parameter values correspoding to |
|
|
//| points. |
|
|
//| I.e. for P created from (X[0],Y[0])...(X[N-1],Y[N-1]) and |
|
|
//| U=TValues(P) we have |
|
|
//| (X[0],Y[0]) = PSpline2Calc(P,U[0]), |
|
|
//| (X[1],Y[1]) = PSpline2Calc(P,U[1]), |
|
|
//| (X[2],Y[2]) = PSpline2Calc(P,U[2]), |
|
|
//| ... |
|
|
//| INPUT PARAMETERS: |
|
|
//| P - parametric spline interpolant |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| N - array size |
|
|
//| T - array[0..N-1] |
|
|
//| NOTES: |
|
|
//| * for non-periodic splines U[0]=0, U[0]<U[1]<...<U[N-1], U[N-1]=1|
|
|
//| * for periodic splines U[0]=0, U[0]<U[1]<...<U[N-1], U[N-1]<1|
|
|
//+------------------------------------------------------------------+
|
|
static void CPSpline::PSpline2ParameterValues(CPSpline2Interpolant &p,
|
|
int &n,double &t[])
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- initialization
|
|
n=0;
|
|
//--- check
|
|
if(!CAp::Assert(p.m_n>=2,__FUNCTION__+": internal error!"))
|
|
return;
|
|
//--- initialization
|
|
n=p.m_n;
|
|
//--- allocation
|
|
ArrayResizeAL(t,n);
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
t[i_]=p.m_p[i_];
|
|
t[0]=0;
|
|
//--- check
|
|
if(!p.m_periodic)
|
|
t[n-1]=1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function returns vector of parameter values correspoding to |
|
|
//| points. |
|
|
//| Same as PSpline2ParameterValues(), but for 3D. |
|
|
//+------------------------------------------------------------------+
|
|
static void CPSpline::PSpline3ParameterValues(CPSpline3Interpolant &p,
|
|
int &n,double &t[])
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- initialization
|
|
n=0;
|
|
//--- check
|
|
if(!CAp::Assert(p.m_n>=2,__FUNCTION__+": internal error!"))
|
|
return;
|
|
//--- initialization
|
|
n=p.m_n;
|
|
//--- allocation
|
|
ArrayResizeAL(t,n);
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
t[i_]=p.m_p[i_];
|
|
t[0]=0;
|
|
//--- check
|
|
if(!p.m_periodic)
|
|
t[n-1]=1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function calculates the value of the parametric spline for a|
|
|
//| given value of parameter T |
|
|
//| INPUT PARAMETERS: |
|
|
//| P - parametric spline interpolant |
|
|
//| T - point: |
|
|
//| * T in [0,1] corresponds to interval spanned by |
|
|
//| points |
|
|
//| * for non-periodic splines T<0 (or T>1) correspond to|
|
|
//| parts of the curve before the first (after the |
|
|
//| last) point |
|
|
//| * for periodic splines T<0 (or T>1) are projected |
|
|
//| into [0,1] by making T=T-floor(T). |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - X-position |
|
|
//| Y - Y-position |
|
|
//+------------------------------------------------------------------+
|
|
static void CPSpline::PSpline2Calc(CPSpline2Interpolant &p,double t,
|
|
double &x,double &y)
|
|
{
|
|
//--- initialization
|
|
x=0;
|
|
y=0;
|
|
//--- check
|
|
if(p.m_periodic)
|
|
t=t-(int)MathFloor(t);
|
|
//--- function call
|
|
x=CSpline1D::Spline1DCalc(p.m_x,t);
|
|
//--- function call
|
|
y=CSpline1D::Spline1DCalc(p.m_y,t);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function calculates the value of the parametric spline for a|
|
|
//| given value of parameter T. |
|
|
//| INPUT PARAMETERS: |
|
|
//| P - parametric spline interpolant |
|
|
//| T - point: |
|
|
//| * T in [0,1] corresponds to interval spanned by |
|
|
//| points |
|
|
//| * for non-periodic splines T<0 (or T>1) correspond |
|
|
//| to parts of the curve before the first (after the |
|
|
//| last) point |
|
|
//| * for periodic splines T<0 (or T>1) are projected |
|
|
//| into [0,1] by making T=T-floor(T). |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - X-position |
|
|
//| Y - Y-position |
|
|
//| Z - Z-position |
|
|
//+------------------------------------------------------------------+
|
|
static void CPSpline::PSpline3Calc(CPSpline3Interpolant &p,double t,
|
|
double &x,double &y,double &z)
|
|
{
|
|
//--- initialization
|
|
x=0;
|
|
y=0;
|
|
z=0;
|
|
//--- check
|
|
if(p.m_periodic)
|
|
t=t-(int)MathFloor(t);
|
|
//--- function call
|
|
x=CSpline1D::Spline1DCalc(p.m_x,t);
|
|
//--- function call
|
|
y=CSpline1D::Spline1DCalc(p.m_y,t);
|
|
//--- function call
|
|
z=CSpline1D::Spline1DCalc(p.m_z,t);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function calculates tangent vector for a given value of |
|
|
//| parameter T |
|
|
//| INPUT PARAMETERS: |
|
|
//| P - parametric spline interpolant |
|
|
//| T - point: |
|
|
//| * T in [0,1] corresponds to interval spanned by |
|
|
//| points |
|
|
//| * for non-periodic splines T<0 (or T>1) correspond to|
|
|
//| parts of the curve before the first (after the |
|
|
//| last) point |
|
|
//| * for periodic splines T<0 (or T>1) are projected |
|
|
//| into [0,1] by making T=T-floor(T). |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - X-component of tangent vector (normalized) |
|
|
//| Y - Y-component of tangent vector (normalized) |
|
|
//| NOTE: |
|
|
//| X^2+Y^2 is either 1 (for non-zero tangent vector) or 0. |
|
|
//+------------------------------------------------------------------+
|
|
static void CPSpline::PSpline2Tangent(CPSpline2Interpolant &p,double t,
|
|
double &x,double &y)
|
|
{
|
|
//--- create variables
|
|
double v=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
//--- initialization
|
|
x=0;
|
|
y=0;
|
|
//--- check
|
|
if(p.m_periodic)
|
|
t=t-(int)MathFloor(t);
|
|
//--- function call
|
|
PSpline2Diff(p,t,v0,x,v1,y);
|
|
//--- check
|
|
if(x!=0.0 || y!=0.0)
|
|
{
|
|
//--- this code is a bit more complex than X^2+Y^2 to avoid
|
|
//--- overflow for large values of X and Y.
|
|
v=CApServ::SafePythag2(x,y);
|
|
x=x/v;
|
|
y=y/v;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function calculates tangent vector for a given value of |
|
|
//| parameter T |
|
|
//| INPUT PARAMETERS: |
|
|
//| P - parametric spline interpolant |
|
|
//| T - point: |
|
|
//| * T in [0,1] corresponds to interval spanned by |
|
|
//| points |
|
|
//| * for non-periodic splines T<0 (or T>1) correspond to|
|
|
//| parts of the curve before the first (after the |
|
|
//| last) point |
|
|
//| * for periodic splines T<0 (or T>1) are projected |
|
|
//| into [0,1] by making T=T-floor(T). |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - X-component of tangent vector (normalized) |
|
|
//| Y - Y-component of tangent vector (normalized) |
|
|
//| Z - Z-component of tangent vector (normalized) |
|
|
//| NOTE: |
|
|
//| X^2+Y^2+Z^2 is either 1 (for non-zero tangent vector) or 0. |
|
|
//+------------------------------------------------------------------+
|
|
static void CPSpline::PSpline3Tangent(CPSpline3Interpolant &p,double t,
|
|
double &x,double &y,double &z)
|
|
{
|
|
//--- create variables
|
|
double v=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
//--- initialization
|
|
x=0;
|
|
y=0;
|
|
z=0;
|
|
//--- check
|
|
if(p.m_periodic)
|
|
t=t-(int)MathFloor(t);
|
|
//--- function call
|
|
PSpline3Diff(p,t,v0,x,v1,y,v2,z);
|
|
//--- check
|
|
if((x!=0.0 || y!=0.0) || z!=0.0)
|
|
{
|
|
//--- function call
|
|
v=CApServ::SafePythag3(x,y,z);
|
|
//--- change values
|
|
x=x/v;
|
|
y=y/v;
|
|
z=z/v;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function calculates derivative, i.e. it returns |
|
|
//| (dX/dT,dY/dT). |
|
|
//| INPUT PARAMETERS: |
|
|
//| P - parametric spline interpolant |
|
|
//| T - point: |
|
|
//| * T in [0,1] corresponds to interval spanned by |
|
|
//| points |
|
|
//| * for non-periodic splines T<0 (or T>1) correspond to|
|
|
//| parts of the curve before the first (after the |
|
|
//| last) point |
|
|
//| * for periodic splines T<0 (or T>1) are projected |
|
|
//| into [0,1] by making T=T-floor(T). |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - X-value |
|
|
//| DX - X-derivative |
|
|
//| Y - Y-value |
|
|
//| DY - Y-derivative |
|
|
//+------------------------------------------------------------------+
|
|
static void CPSpline::PSpline2Diff(CPSpline2Interpolant &p,double t,
|
|
double &x,double &dx,double &y,
|
|
double &dy)
|
|
{
|
|
//--- create a variable
|
|
double d2s=0;
|
|
//--- change values
|
|
x=0;
|
|
dx=0;
|
|
y=0;
|
|
dy=0;
|
|
//--- check
|
|
if(p.m_periodic)
|
|
t=t-(int)MathFloor(t);
|
|
//--- function call
|
|
CSpline1D::Spline1DDiff(p.m_x,t,x,dx,d2s);
|
|
//--- function call
|
|
CSpline1D::Spline1DDiff(p.m_y,t,y,dy,d2s);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function calculates derivative, i.e. it returns |
|
|
//| (dX/dT,dY/dT,dZ/dT). |
|
|
//| INPUT PARAMETERS: |
|
|
//| P - parametric spline interpolant |
|
|
//| T - point: |
|
|
//| * T in [0,1] corresponds to interval spanned by |
|
|
//| points |
|
|
//| * for non-periodic splines T<0 (or T>1) correspond to|
|
|
//| parts of the curve before the first (after the |
|
|
//| last) point |
|
|
//| * for periodic splines T<0 (or T>1) are projected |
|
|
//| into [0,1] by making T=T-floor(T). |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - X-value |
|
|
//| DX - X-derivative |
|
|
//| Y - Y-value |
|
|
//| DY - Y-derivative |
|
|
//| Z - Z-value |
|
|
//| DZ - Z-derivative |
|
|
//+------------------------------------------------------------------+
|
|
static void CPSpline::PSpline3Diff(CPSpline3Interpolant &p,double t,
|
|
double &x,double &dx,double &y,
|
|
double &dy,double &z,double &dz)
|
|
{
|
|
//--- create a variable
|
|
double d2s=0;
|
|
//--- initialization
|
|
x=0;
|
|
dx=0;
|
|
y=0;
|
|
dy=0;
|
|
z=0;
|
|
dz=0;
|
|
//--- check
|
|
if(p.m_periodic)
|
|
t=t-(int)MathFloor(t);
|
|
//--- function call
|
|
CSpline1D::Spline1DDiff(p.m_x,t,x,dx,d2s);
|
|
//--- function call
|
|
CSpline1D::Spline1DDiff(p.m_y,t,y,dy,d2s);
|
|
//--- function call
|
|
CSpline1D::Spline1DDiff(p.m_z,t,z,dz,d2s);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function calculates first and second derivative with respect|
|
|
//| to T. |
|
|
//| INPUT PARAMETERS: |
|
|
//| P - parametric spline interpolant |
|
|
//| T - point: |
|
|
//| * T in [0,1] corresponds to interval spanned by |
|
|
//| points |
|
|
//| * for non-periodic splines T<0 (or T>1) correspond to|
|
|
//| parts of the curve before the first (after the |
|
|
//| last) point |
|
|
//| * for periodic splines T<0 (or T>1) are projected |
|
|
//| into [0,1] by making T=T-floor(T). |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - X-value |
|
|
//| DX - derivative |
|
|
//| D2X - second derivative |
|
|
//| Y - Y-value |
|
|
//| DY - derivative |
|
|
//| D2Y - second derivative |
|
|
//+------------------------------------------------------------------+
|
|
static void CPSpline::PSpline2Diff2(CPSpline2Interpolant &p,double t,
|
|
double &x,double &dx,double &d2x,
|
|
double &y,double &dy,double &d2y)
|
|
{
|
|
//--- initialization
|
|
x=0;
|
|
dx=0;
|
|
d2x=0;
|
|
y=0;
|
|
dy=0;
|
|
d2y=0;
|
|
//--- check
|
|
if(p.m_periodic)
|
|
t=t-(int)MathFloor(t);
|
|
//--- function call
|
|
CSpline1D::Spline1DDiff(p.m_x,t,x,dx,d2x);
|
|
//--- function call
|
|
CSpline1D::Spline1DDiff(p.m_y,t,y,dy,d2y);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function calculates first and second derivative with respect|
|
|
//| to T. |
|
|
//| INPUT PARAMETERS: |
|
|
//| P - parametric spline interpolant |
|
|
//| T - point: |
|
|
//| * T in [0,1] corresponds to interval spanned by |
|
|
//| points |
|
|
//| * for non-periodic splines T<0 (or T>1) correspond to|
|
|
//| parts of the curve before the first (after the |
|
|
//| last) point |
|
|
//| * for periodic splines T<0 (or T>1) are projected |
|
|
//| into [0,1] by making T=T-floor(T). |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - X-value |
|
|
//| DX - derivative |
|
|
//| D2X - second derivative |
|
|
//| Y - Y-value |
|
|
//| DY - derivative |
|
|
//| D2Y - second derivative |
|
|
//| Z - Z-value |
|
|
//| DZ - derivative |
|
|
//| D2Z - second derivative |
|
|
//+------------------------------------------------------------------+
|
|
static void CPSpline::PSpline3Diff2(CPSpline3Interpolant &p,double t,
|
|
double &x,double &dx,double &d2x,
|
|
double &y,double &dy,double &d2y,
|
|
double &z,double &dz,double &d2z)
|
|
{
|
|
//--- initialization
|
|
x=0;
|
|
dx=0;
|
|
d2x=0;
|
|
y=0;
|
|
dy=0;
|
|
d2y=0;
|
|
z=0;
|
|
dz=0;
|
|
d2z=0;
|
|
//--- check
|
|
if(p.m_periodic)
|
|
t=t-(int)MathFloor(t);
|
|
//--- function call
|
|
CSpline1D::Spline1DDiff(p.m_x,t,x,dx,d2x);
|
|
//--- function call
|
|
CSpline1D::Spline1DDiff(p.m_y,t,y,dy,d2y);
|
|
//--- function call
|
|
CSpline1D::Spline1DDiff(p.m_z,t,z,dz,d2z);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function calculates arc length, i.e. length of curve between|
|
|
//| t=a and t=b. |
|
|
//| INPUT PARAMETERS: |
|
|
//| P - parametric spline interpolant |
|
|
//| A,B - parameter values corresponding to arc ends: |
|
|
//| * B>A will result in positive length returned |
|
|
//| * B<A will result in negative length returned |
|
|
//| RESULT: |
|
|
//| length of arc starting at T=A and ending at T=B. |
|
|
//+------------------------------------------------------------------+
|
|
static double CPSpline::PSpline2ArcLength(CPSpline2Interpolant &p,const double a,
|
|
const double b)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double sx=0;
|
|
double dsx=0;
|
|
double d2sx=0;
|
|
double sy=0;
|
|
double dsy=0;
|
|
double d2sy=0;
|
|
//--- objects of classes
|
|
CAutoGKState state;
|
|
CAutoGKReport rep;
|
|
//--- function call
|
|
CAutoGK::AutoGKSmooth(a,b,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
{
|
|
CSpline1D::Spline1DDiff(p.m_x,state.m_x,sx,dsx,d2sx);
|
|
CSpline1D::Spline1DDiff(p.m_y,state.m_x,sy,dsy,d2sy);
|
|
state.m_f=CApServ::SafePythag2(dsx,dsy);
|
|
}
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,result,rep);
|
|
//--- check
|
|
if(!CAp::Assert(rep.m_terminationtype>0,__FUNCTION__+": internal error!"))
|
|
return(EMPTY_VALUE);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function calculates arc length, i.e. length of curve between|
|
|
//| t=a and t=b. |
|
|
//| INPUT PARAMETERS: |
|
|
//| P - parametric spline interpolant |
|
|
//| A,B - parameter values corresponding to arc ends: |
|
|
//| * B>A will result in positive length returned |
|
|
//| * B<A will result in negative length returned |
|
|
//| RESULT: |
|
|
//| length of arc starting at T=A and ending at T=B. |
|
|
//+------------------------------------------------------------------+
|
|
static double CPSpline::PSpline3ArcLength(CPSpline3Interpolant &p,const double a,
|
|
const double b)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double sx=0;
|
|
double dsx=0;
|
|
double d2sx=0;
|
|
double sy=0;
|
|
double dsy=0;
|
|
double d2sy=0;
|
|
double sz=0;
|
|
double dsz=0;
|
|
double d2sz=0;
|
|
//--- objects of classes
|
|
CAutoGKState state;
|
|
CAutoGKReport rep;
|
|
//--- function call
|
|
CAutoGK::AutoGKSmooth(a,b,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
{
|
|
CSpline1D::Spline1DDiff(p.m_x,state.m_x,sx,dsx,d2sx);
|
|
CSpline1D::Spline1DDiff(p.m_y,state.m_x,sy,dsy,d2sy);
|
|
CSpline1D::Spline1DDiff(p.m_z,state.m_x,sz,dsz,d2sz);
|
|
state.m_f=CApServ::SafePythag3(dsx,dsy,dsz);
|
|
}
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,result,rep);
|
|
//--- check
|
|
if(!CAp::Assert(rep.m_terminationtype>0,__FUNCTION__+": internal error!"))
|
|
return(EMPTY_VALUE);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Builds non-periodic parameterization for 2-dimensional spline |
|
|
//+------------------------------------------------------------------+
|
|
static void CPSpline::PSpline2Par(CMatrixDouble &xy,const int n,const int pt,
|
|
double &p[])
|
|
{
|
|
//--- create variables
|
|
double v=0;
|
|
int i=0;
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(pt>=0 && pt<=2,__FUNCTION__+": internal error!"))
|
|
return;
|
|
//--- Build parameterization:
|
|
//--- * fill by non-normalized values
|
|
//--- * normalize them so we have P[0]=0,P[N-1]=1.
|
|
ArrayResizeAL(p,n);
|
|
//--- check
|
|
if(pt==0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
p[i]=i;
|
|
}
|
|
//--- check
|
|
if(pt==1)
|
|
{
|
|
p[0]=0;
|
|
//--- calculation
|
|
for(i=1;i<=n-1;i++)
|
|
p[i]=p[i-1]+CApServ::SafePythag2(xy[i][0]-xy[i-1][0],xy[i][1]-xy[i-1][1]);
|
|
}
|
|
//--- check
|
|
if(pt==2)
|
|
{
|
|
p[0]=0;
|
|
//--- calculation
|
|
for(i=1;i<=n-1;i++)
|
|
p[i]=p[i-1]+MathSqrt(CApServ::SafePythag2(xy[i][0]-xy[i-1][0],xy[i][1]-xy[i-1][1]));
|
|
}
|
|
//--- change value
|
|
v=1/p[n-1];
|
|
//--- calculation
|
|
for(i_=0;i_<=n-1;i_++)
|
|
p[i_]=v*p[i_];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Builds non-periodic parameterization for 3-dimensional spline |
|
|
//+------------------------------------------------------------------+
|
|
static void CPSpline::PSpline3Par(CMatrixDouble &xy,const int n,const int pt,
|
|
double &p[])
|
|
{
|
|
//--- create variables
|
|
double v=0;
|
|
int i=0;
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(pt>=0 && pt<=2,__FUNCTION__+": internal error!"))
|
|
return;
|
|
//--- Build parameterization:
|
|
//--- * fill by non-normalized values
|
|
//--- * normalize them so we have P[0]=0,P[N-1]=1.
|
|
ArrayResizeAL(p,n);
|
|
//--- check
|
|
if(pt==0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
p[i]=i;
|
|
}
|
|
//--- check
|
|
if(pt==1)
|
|
{
|
|
p[0]=0;
|
|
//--- calculation
|
|
for(i=1;i<=n-1;i++)
|
|
p[i]=p[i-1]+CApServ::SafePythag3(xy[i][0]-xy[i-1][0],xy[i][1]-xy[i-1][1],xy[i][2]-xy[i-1][2]);
|
|
}
|
|
//--- check
|
|
if(pt==2)
|
|
{
|
|
p[0]=0;
|
|
//--- calculation
|
|
for(i=1;i<=n-1;i++)
|
|
p[i]=p[i-1]+MathSqrt(CApServ::SafePythag3(xy[i][0]-xy[i-1][0],xy[i][1]-xy[i-1][1],xy[i][2]-xy[i-1][2]));
|
|
}
|
|
//--- change value
|
|
v=1/p[n-1];
|
|
//--- calculation
|
|
for(i_=0;i_<=n-1;i_++)
|
|
p[i_]=v*p[i_];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| 2-dimensional spline inteprolant |
|
|
//+------------------------------------------------------------------+
|
|
class CSpline2DInterpolant
|
|
{
|
|
public:
|
|
//--- variable
|
|
int m_k;
|
|
//--- array
|
|
double m_c[];
|
|
//--- constructor, destructor
|
|
CSpline2DInterpolant(void);
|
|
~CSpline2DInterpolant(void);
|
|
//--- copy
|
|
void Copy(CSpline2DInterpolant &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CSpline2DInterpolant::CSpline2DInterpolant(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CSpline2DInterpolant::~CSpline2DInterpolant(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CSpline2DInterpolant::Copy(CSpline2DInterpolant &obj)
|
|
{
|
|
//--- copy variable
|
|
m_k=obj.m_k;
|
|
//--- copy array
|
|
ArrayCopy(m_c,obj.m_c);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| 2-dimensional spline inteprolant |
|
|
//+------------------------------------------------------------------+
|
|
class CSpline2DInterpolantShell
|
|
{
|
|
private:
|
|
CSpline2DInterpolant m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CSpline2DInterpolantShell(void);
|
|
CSpline2DInterpolantShell(CSpline2DInterpolant &obj);
|
|
~CSpline2DInterpolantShell(void);
|
|
//--- method
|
|
CSpline2DInterpolant *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CSpline2DInterpolantShell::CSpline2DInterpolantShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CSpline2DInterpolantShell::CSpline2DInterpolantShell(CSpline2DInterpolant &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CSpline2DInterpolantShell::~CSpline2DInterpolantShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CSpline2DInterpolant *CSpline2DInterpolantShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| 2-dimensional spline interpolation |
|
|
//+------------------------------------------------------------------+
|
|
class CSpline2D
|
|
{
|
|
private:
|
|
//--- private method
|
|
static void BicubicCalcDerivatives(CMatrixDouble &a,double &x[],double &y[],const int m,const int n,CMatrixDouble &dx,CMatrixDouble &dy,CMatrixDouble &dxy);
|
|
public:
|
|
//--- constructor, destructor
|
|
CSpline2D(void);
|
|
~CSpline2D(void);
|
|
//--- public methods
|
|
static void Spline2DBuildBilinear(double &cx[],double &cy[],CMatrixDouble &cf,const int m,const int n,CSpline2DInterpolant &c);
|
|
static void Spline2DBuildBicubic(double &cx[],double &cy[],CMatrixDouble &cf,const int m,const int n,CSpline2DInterpolant &c);
|
|
static double Spline2DCalc(CSpline2DInterpolant &c,const double x,const double y);
|
|
static void Spline2DDiff(CSpline2DInterpolant &c,const double x,const double y,double &f,double &fx,double &fy,double &fxy);
|
|
static void Spline2DUnpack(CSpline2DInterpolant &c,int &m,int &n,CMatrixDouble &tbl);
|
|
static void Spline2DLinTransXY(CSpline2DInterpolant &c,double ax,double bx,double ay,double by);
|
|
static void Spline2DLinTransF(CSpline2DInterpolant &c,const double a,const double b);
|
|
static void Spline2DCopy(CSpline2DInterpolant &c,CSpline2DInterpolant &cc);
|
|
static void Spline2DResampleBicubic(CMatrixDouble &a,const int oldheight,const int oldwidth,CMatrixDouble &b,const int newheight,const int newwidth);
|
|
static void Spline2DResampleBilinear(CMatrixDouble &a,const int oldheight,const int oldwidth,CMatrixDouble &b,const int newheight,const int newwidth);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CSpline2D::CSpline2D(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CSpline2D::~CSpline2D(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine builds bilinear spline coefficients table. |
|
|
//| Input parameters: |
|
|
//| X - spline abscissas, array[0..N-1] |
|
|
//| Y - spline ordinates, array[0..M-1] |
|
|
//| F - function values, array[0..M-1,0..N-1] |
|
|
//| M,N - grid size, M>=2, N>=2 |
|
|
//| Output parameters: |
|
|
//| C - spline interpolant |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline2D::Spline2DBuildBilinear(double &cx[],double &cy[],
|
|
CMatrixDouble &cf,const int m,
|
|
const int n,CSpline2DInterpolant &c)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int tblsize=0;
|
|
int shift=0;
|
|
double t=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble dx;
|
|
CMatrixDouble dy;
|
|
CMatrixDouble dxy;
|
|
CMatrixDouble f;
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
//--- copy matrix
|
|
f=cf;
|
|
//--- check
|
|
if(!CAp::Assert(n>=2 && m>=2,__FUNCTION__+": N<2 or M<2!"))
|
|
return;
|
|
//--- Sort points
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
k=j;
|
|
for(i=j+1;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(x[i]<x[k])
|
|
k=i;
|
|
}
|
|
//--- check
|
|
if(k!=j)
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
//--- swap
|
|
t=f[i][j];
|
|
f[i].Set(j,f[i][k]);
|
|
f[i].Set(k,t);
|
|
}
|
|
//--- swap
|
|
t=x[j];
|
|
x[j]=x[k];
|
|
x[k]=t;
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
k=i;
|
|
for(j=i+1;j<=m-1;j++)
|
|
{
|
|
//--- check
|
|
if(y[j]<y[k])
|
|
k=j;
|
|
}
|
|
//--- check
|
|
if(k!=i)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- swap
|
|
t=f[i][j];
|
|
f[i].Set(j,f[k][j]);
|
|
f[k].Set(j,t);
|
|
}
|
|
//--- swap
|
|
t=y[i];
|
|
y[i]=y[k];
|
|
y[k]=t;
|
|
}
|
|
}
|
|
//--- Fill C:
|
|
//--- C[0] - length(C)
|
|
//--- C[1] - type(C):
|
|
//--- -1=bilinear interpolant
|
|
//--- -3=general cubic spline
|
|
//--- (see BuildBicubicSpline)
|
|
//--- C[2]:
|
|
//--- N (x count)
|
|
//--- C[3]:
|
|
//--- M (y count)
|
|
//--- C[4]...C[4+N-1]:
|
|
//--- x[i],i=0...N-1
|
|
//--- C[4+N]...C[4+N+M-1]:
|
|
//--- y[i],i=0...M-1
|
|
//--- C[4+N+M]...C[4+N+M+(N*M-1)]:
|
|
//--- f(i,j) table. f(0,0),f(0,1),f(0,2) and so on...
|
|
c.m_k=1;
|
|
tblsize=4+n+m+n*m;
|
|
//--- allocation
|
|
ArrayResizeAL(c.m_c,tblsize);
|
|
//--- change values
|
|
c.m_c[0]=tblsize;
|
|
c.m_c[1]=-1;
|
|
c.m_c[2]=n;
|
|
c.m_c[3]=m;
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
c.m_c[4+i]=x[i];
|
|
for(i=0;i<=m-1;i++)
|
|
c.m_c[4+n+i]=y[i];
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
shift=i*n+j;
|
|
c.m_c[4+n+m+shift]=f[i][j];
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine builds bicubic spline coefficients table. |
|
|
//| Input parameters: |
|
|
//| X - spline abscissas, array[0..N-1] |
|
|
//| Y - spline ordinates, array[0..M-1] |
|
|
//| F - function values, array[0..M-1,0..N-1] |
|
|
//| M,N - grid size, M>=2, N>=2 |
|
|
//| Output parameters: |
|
|
//| C - spline interpolant |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline2D::Spline2DBuildBicubic(double &cx[],double &cy[],
|
|
CMatrixDouble &cf,const int m,
|
|
const int n,CSpline2DInterpolant &c)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int tblsize=0;
|
|
int shift=0;
|
|
double t=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble dx;
|
|
CMatrixDouble dy;
|
|
CMatrixDouble dxy;
|
|
CMatrixDouble f;
|
|
//--- copy arrays
|
|
ArrayCopy(x,cx);
|
|
ArrayCopy(y,cy);
|
|
//--- copy matrix
|
|
f=cf;
|
|
//--- check
|
|
if(!CAp::Assert(n>=2 && m>=2,__FUNCTION__+": N<2 or M<2!"))
|
|
return;
|
|
//--- Sort points
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
k=j;
|
|
for(i=j+1;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(x[i]<x[k])
|
|
k=i;
|
|
}
|
|
//--- check
|
|
if(k!=j)
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
//--- swap
|
|
t=f[i][j];
|
|
f[i].Set(j,f[i][k]);
|
|
f[i].Set(k,t);
|
|
}
|
|
//--- swap
|
|
t=x[j];
|
|
x[j]=x[k];
|
|
x[k]=t;
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
k=i;
|
|
for(j=i+1;j<=m-1;j++)
|
|
{
|
|
//--- check
|
|
if(y[j]<y[k])
|
|
k=j;
|
|
}
|
|
//--- check
|
|
if(k!=i)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- swap
|
|
t=f[i][j];
|
|
f[i].Set(j,f[k][j]);
|
|
f[k].Set(j,t);
|
|
}
|
|
//--- swap
|
|
t=y[i];
|
|
y[i]=y[k];
|
|
y[k]=t;
|
|
}
|
|
}
|
|
//--- Fill C:
|
|
//--- C[0] - length(C)
|
|
//--- C[1] - type(C):
|
|
//--- -1=bilinear interpolant
|
|
//--- (see BuildBilinearInterpolant)
|
|
//--- -3=general cubic spline
|
|
//--- C[2]:
|
|
//--- N (x count)
|
|
//--- C[3]:
|
|
//--- M (y count)
|
|
//--- C[4]...C[4+N-1]:
|
|
//--- x[i],i=0...N-1
|
|
//--- C[4+N]...C[4+N+M-1]:
|
|
//--- y[i],i=0...M-1
|
|
//--- C[4+N+M]...C[4+N+M+(N*M-1)]:
|
|
//--- f(i,j) table. f(0,0),f(0,1),f(0,2) and so on...
|
|
//--- C[4+N+M+N*M]...C[4+N+M+(2*N*M-1)]:
|
|
//--- df(i,j)/dx table.
|
|
//--- C[4+N+M+2*N*M]...C[4+N+M+(3*N*M-1)]:
|
|
//--- df(i,j)/dy table.
|
|
//--- C[4+N+M+3*N*M]...C[4+N+M+(4*N*M-1)]:
|
|
//--- d2f(i,j)/dxdy table.
|
|
c.m_k=3;
|
|
tblsize=4+n+m+4*n*m;
|
|
//--- allocation
|
|
ArrayResizeAL(c.m_c,tblsize);
|
|
//--- change values
|
|
c.m_c[0]=tblsize;
|
|
c.m_c[1]=-3;
|
|
c.m_c[2]=n;
|
|
c.m_c[3]=m;
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
c.m_c[4+i]=x[i];
|
|
for(i=0;i<=m-1;i++)
|
|
c.m_c[4+n+i]=y[i];
|
|
//--- function call
|
|
BicubicCalcDerivatives(f,x,y,m,n,dx,dy,dxy);
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
shift=i*n+j;
|
|
c.m_c[4+n+m+shift]=f[i][j];
|
|
c.m_c[4+n+m+n*m+shift]=dx[i][j];
|
|
c.m_c[4+n+m+2*n*m+shift]=dy[i][j];
|
|
c.m_c[4+n+m+3*n*m+shift]=dxy[i][j];
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine calculates the value of the bilinear or bicubic |
|
|
//| spline at the given point X. |
|
|
//| Input parameters: |
|
|
//| C - coefficients table. |
|
|
//| Built by BuildBilinearSpline or BuildBicubicSpline. |
|
|
//| X, Y- point |
|
|
//| Result: |
|
|
//| S(x,y) |
|
|
//+------------------------------------------------------------------+
|
|
static double CSpline2D::Spline2DCalc(CSpline2DInterpolant &c,const double x,
|
|
const double y)
|
|
{
|
|
//--- create variables
|
|
double v=0;
|
|
double vx=0;
|
|
double vy=0;
|
|
double vxy=0;
|
|
//--- function call
|
|
Spline2DDiff(c,x,y,v,vx,vy,vxy);
|
|
//--- return result
|
|
return(v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine calculates the value of the bilinear or bicubic |
|
|
//| spline at the given point X and its derivatives. |
|
|
//| Input parameters: |
|
|
//| C - spline interpolant. |
|
|
//| X, Y- point |
|
|
//| Output parameters: |
|
|
//| F - S(x,y) |
|
|
//| FX - dS(x,y)/dX |
|
|
//| FY - dS(x,y)/dY |
|
|
//| FXY - d2S(x,y)/dXdY |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline2D::Spline2DDiff(CSpline2DInterpolant &c,const double x,
|
|
const double y,double &f,double &fx,
|
|
double &fy,double &fxy)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int m=0;
|
|
double t=0;
|
|
double dt=0;
|
|
double u=0;
|
|
double du=0;
|
|
int ix=0;
|
|
int iy=0;
|
|
int l=0;
|
|
int r=0;
|
|
int h=0;
|
|
int shift1=0;
|
|
int s1=0;
|
|
int s2=0;
|
|
int s3=0;
|
|
int s4=0;
|
|
int sf=0;
|
|
int sfx=0;
|
|
int sfy=0;
|
|
int sfxy=0;
|
|
double y1=0;
|
|
double y2=0;
|
|
double y3=0;
|
|
double y4=0;
|
|
double v=0;
|
|
double t0=0;
|
|
double t1=0;
|
|
double t2=0;
|
|
double t3=0;
|
|
double u0=0;
|
|
double u1=0;
|
|
double u2=0;
|
|
double u3=0;
|
|
//--- initialization
|
|
f=0;
|
|
fx=0;
|
|
fy=0;
|
|
fxy=0;
|
|
//--- check
|
|
if(!CAp::Assert((int)MathRound(c.m_c[1])==-1 || (int)MathRound(c.m_c[1])==-3,__FUNCTION__+": incorrect C!"))
|
|
return;
|
|
//--- initialization
|
|
n=(int)MathRound(c.m_c[2]);
|
|
m=(int)MathRound(c.m_c[3]);
|
|
//--- Binary search in the [ x[0],...,x[n-2] ] (x[n-1] is not included)
|
|
l=4;
|
|
r=4+n-2+1;
|
|
while(l!=r-1)
|
|
{
|
|
h=(l+r)/2;
|
|
//--- check
|
|
if(c.m_c[h]>=x)
|
|
r=h;
|
|
else
|
|
l=h;
|
|
}
|
|
//--- change values
|
|
t=(x-c.m_c[l])/(c.m_c[l+1]-c.m_c[l]);
|
|
dt=1.0/(c.m_c[l+1]-c.m_c[l]);
|
|
ix=l-4;
|
|
//--- Binary search in the [ y[0],...,y[m-2] ] (y[m-1] is not included)
|
|
l=4+n;
|
|
r=4+n+(m-2)+1;
|
|
while(l!=r-1)
|
|
{
|
|
h=(l+r)/2;
|
|
//--- check
|
|
if(c.m_c[h]>=y)
|
|
r=h;
|
|
else
|
|
l=h;
|
|
}
|
|
//--- change values
|
|
u=(y-c.m_c[l])/(c.m_c[l+1]-c.m_c[l]);
|
|
du=1.0/(c.m_c[l+1]-c.m_c[l]);
|
|
iy=l-(4+n);
|
|
//--- Prepare F,dF/dX,dF/dY,d2F/dXdY
|
|
f=0;
|
|
fx=0;
|
|
fy=0;
|
|
fxy=0;
|
|
//--- Bilinear interpolation
|
|
if((int)MathRound(c.m_c[1])==-1)
|
|
{
|
|
//--- calculation
|
|
shift1=4+n+m;
|
|
y1=c.m_c[shift1+n*iy+ix];
|
|
y2=c.m_c[shift1+n*iy+(ix+1)];
|
|
y3=c.m_c[shift1+n*(iy+1)+(ix+1)];
|
|
y4=c.m_c[shift1+n*(iy+1)+ix];
|
|
f=(1-t)*(1-u)*y1+t*(1-u)*y2+t*u*y3+(1-t)*u*y4;
|
|
fx=(-((1-u)*y1)+(1-u)*y2+u*y3-u*y4)*dt;
|
|
fy=(-((1-t)*y1)-t*y2+t*y3+(1-t)*y4)*du;
|
|
fxy=(y1-y2+y3-y4)*du*dt;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Bicubic interpolation
|
|
if((int)MathRound(c.m_c[1])==-3)
|
|
{
|
|
//--- Prepare info
|
|
t0=1;
|
|
t1=t;
|
|
t2=CMath::Sqr(t);
|
|
t3=t*t2;
|
|
u0=1;
|
|
u1=u;
|
|
u2=CMath::Sqr(u);
|
|
u3=u*u2;
|
|
sf=4+n+m;
|
|
sfx=4+n+m+n*m;
|
|
sfy=4+n+m+2*n*m;
|
|
sfxy=4+n+m+3*n*m;
|
|
s1=n*iy+ix;
|
|
s2=n*iy+(ix+1);
|
|
s3=n*(iy+1)+(ix+1);
|
|
s4=n*(iy+1)+ix;
|
|
//--- Calculate
|
|
v=1*c.m_c[sf+s1];
|
|
f=f+v*t0*u0;
|
|
v=1*c.m_c[sfy+s1]/du;
|
|
f=f+v*t0*u1;
|
|
fy=fy+1*v*t0*u0*du;
|
|
v=-(3*c.m_c[sf+s1])+3*c.m_c[sf+s4]-2*c.m_c[sfy+s1]/du-1*c.m_c[sfy+s4]/du;
|
|
f=f+v*t0*u2;
|
|
fy=fy+2*v*t0*u1*du;
|
|
v=2*c.m_c[sf+s1]-2*c.m_c[sf+s4]+1*c.m_c[sfy+s1]/du+1*c.m_c[sfy+s4]/du;
|
|
f=f+v*t0*u3;
|
|
fy=fy+3*v*t0*u2*du;
|
|
v=1*c.m_c[sfx+s1]/dt;
|
|
f=f+v*t1*u0;
|
|
fx=fx+1*v*t0*u0*dt;
|
|
v=1*c.m_c[sfxy+s1]/(dt*du);
|
|
f=f+v*t1*u1;
|
|
fx=fx+1*v*t0*u1*dt;
|
|
fy=fy+1*v*t1*u0*du;
|
|
fxy=fxy+1*v*t0*u0*dt*du;
|
|
v=-(3*c.m_c[sfx+s1]/dt)+3*c.m_c[sfx+s4]/dt-2*c.m_c[sfxy+s1]/(dt*du)-1*c.m_c[sfxy+s4]/(dt*du);
|
|
f=f+v*t1*u2;
|
|
fx=fx+1*v*t0*u2*dt;
|
|
fy=fy+2*v*t1*u1*du;
|
|
fxy=fxy+2*v*t0*u1*dt*du;
|
|
v=2*c.m_c[sfx+s1]/dt-2*c.m_c[sfx+s4]/dt+1*c.m_c[sfxy+s1]/(dt*du)+1*c.m_c[sfxy+s4]/(dt*du);
|
|
f=f+v*t1*u3;
|
|
fx=fx+1*v*t0*u3*dt;
|
|
fy=fy+3*v*t1*u2*du;
|
|
fxy=fxy+3*v*t0*u2*dt*du;
|
|
v=-(3*c.m_c[sf+s1])+3*c.m_c[sf+s2]-2*c.m_c[sfx+s1]/dt-1*c.m_c[sfx+s2]/dt;
|
|
f=f+v*t2*u0;
|
|
fx=fx+2*v*t1*u0*dt;
|
|
v=-(3*c.m_c[sfy+s1]/du)+3*c.m_c[sfy+s2]/du-2*c.m_c[sfxy+s1]/(dt*du)-1*c.m_c[sfxy+s2]/(dt*du);
|
|
f=f+v*t2*u1;
|
|
fx=fx+2*v*t1*u1*dt;
|
|
fy=fy+1*v*t2*u0*du;
|
|
fxy=fxy+2*v*t1*u0*dt*du;
|
|
v=9*c.m_c[sf+s1]-9*c.m_c[sf+s2]+9*c.m_c[sf+s3]-9*c.m_c[sf+s4]+6*c.m_c[sfx+s1]/dt+3*c.m_c[sfx+s2]/dt-3*c.m_c[sfx+s3]/dt-6*c.m_c[sfx+s4]/dt+6*c.m_c[sfy+s1]/du-6*c.m_c[sfy+s2]/du-3*c.m_c[sfy+s3]/du+3*c.m_c[sfy+s4]/du+4*c.m_c[sfxy+s1]/(dt*du)+2*c.m_c[sfxy+s2]/(dt*du)+1*c.m_c[sfxy+s3]/(dt*du)+2*c.m_c[sfxy+s4]/(dt*du);
|
|
f=f+v*t2*u2;
|
|
fx=fx+2*v*t1*u2*dt;
|
|
fy=fy+2*v*t2*u1*du;
|
|
fxy=fxy+4*v*t1*u1*dt*du;
|
|
v=-(6*c.m_c[sf+s1])+6*c.m_c[sf+s2]-6*c.m_c[sf+s3]+6*c.m_c[sf+s4]-4*c.m_c[sfx+s1]/dt-2*c.m_c[sfx+s2]/dt+2*c.m_c[sfx+s3]/dt+4*c.m_c[sfx+s4]/dt-3*c.m_c[sfy+s1]/du+3*c.m_c[sfy+s2]/du+3*c.m_c[sfy+s3]/du-3*c.m_c[sfy+s4]/du-2*c.m_c[sfxy+s1]/(dt*du)-1*c.m_c[sfxy+s2]/(dt*du)-1*c.m_c[sfxy+s3]/(dt*du)-2*c.m_c[sfxy+s4]/(dt*du);
|
|
f=f+v*t2*u3;
|
|
fx=fx+2*v*t1*u3*dt;
|
|
fy=fy+3*v*t2*u2*du;
|
|
fxy=fxy+6*v*t1*u2*dt*du;
|
|
v=2*c.m_c[sf+s1]-2*c.m_c[sf+s2]+1*c.m_c[sfx+s1]/dt+1*c.m_c[sfx+s2]/dt;
|
|
f=f+v*t3*u0;
|
|
fx=fx+3*v*t2*u0*dt;
|
|
v=2*c.m_c[sfy+s1]/du-2*c.m_c[sfy+s2]/du+1*c.m_c[sfxy+s1]/(dt*du)+1*c.m_c[sfxy+s2]/(dt*du);
|
|
f=f+v*t3*u1;
|
|
fx=fx+3*v*t2*u1*dt;
|
|
fy=fy+1*v*t3*u0*du;
|
|
fxy=fxy+3*v*t2*u0*dt*du;
|
|
v=-(6*c.m_c[sf+s1])+6*c.m_c[sf+s2]-6*c.m_c[sf+s3]+6*c.m_c[sf+s4]-3*c.m_c[sfx+s1]/dt-3*c.m_c[sfx+s2]/dt+3*c.m_c[sfx+s3]/dt+3*c.m_c[sfx+s4]/dt-4*c.m_c[sfy+s1]/du+4*c.m_c[sfy+s2]/du+2*c.m_c[sfy+s3]/du-2*c.m_c[sfy+s4]/du-2*c.m_c[sfxy+s1]/(dt*du)-2*c.m_c[sfxy+s2]/(dt*du)-1*c.m_c[sfxy+s3]/(dt*du)-1*c.m_c[sfxy+s4]/(dt*du);
|
|
f=f+v*t3*u2;
|
|
fx=fx+3*v*t2*u2*dt;
|
|
fy=fy+2*v*t3*u1*du;
|
|
fxy=fxy+6*v*t2*u1*dt*du;
|
|
v=4*c.m_c[sf+s1]-4*c.m_c[sf+s2]+4*c.m_c[sf+s3]-4*c.m_c[sf+s4]+2*c.m_c[sfx+s1]/dt+2*c.m_c[sfx+s2]/dt-2*c.m_c[sfx+s3]/dt-2*c.m_c[sfx+s4]/dt+2*c.m_c[sfy+s1]/du-2*c.m_c[sfy+s2]/du-2*c.m_c[sfy+s3]/du+2*c.m_c[sfy+s4]/du+1*c.m_c[sfxy+s1]/(dt*du)+1*c.m_c[sfxy+s2]/(dt*du)+1*c.m_c[sfxy+s3]/(dt*du)+1*c.m_c[sfxy+s4]/(dt*du);
|
|
f=f+v*t3*u3;
|
|
fx=fx+3*v*t2*u3*dt;
|
|
fy=fy+3*v*t3*u2*du;
|
|
fxy=fxy+9*v*t2*u2*dt*du;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine unpacks two-dimensional spline into the |
|
|
//| coefficients table |
|
|
//| Input parameters: |
|
|
//| C - spline interpolant. |
|
|
//| Result: |
|
|
//| M, N- grid size (x-axis and y-axis) |
|
|
//| Tbl - coefficients table, unpacked format, |
|
|
//| [0..(N-1)*(M-1)-1, 0..19]. |
|
|
//| For I = 0...M-2, J=0..N-2: |
|
|
//| K = I*(N-1)+J |
|
|
//| Tbl[K,0] = X[j] |
|
|
//| Tbl[K,1] = X[j+1] |
|
|
//| Tbl[K,2] = Y[i] |
|
|
//| Tbl[K,3] = Y[i+1] |
|
|
//| Tbl[K,4] = C00 |
|
|
//| Tbl[K,5] = C01 |
|
|
//| Tbl[K,6] = C02 |
|
|
//| Tbl[K,7] = C03 |
|
|
//| Tbl[K,8] = C10 |
|
|
//| Tbl[K,9] = C11 |
|
|
//| ... |
|
|
//| Tbl[K,19] = C33 |
|
|
//| On each grid square spline is equals to: |
|
|
//| S(x) = SUM(c[i,j]*(x^i)*(y^j), i=0..3, j=0..3) |
|
|
//| t = x-x[j] |
|
|
//| u = y-y[i] |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline2D::Spline2DUnpack(CSpline2DInterpolant &c,int &m,
|
|
int &n,CMatrixDouble &tbl)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int ci=0;
|
|
int cj=0;
|
|
int k=0;
|
|
int p=0;
|
|
int shift=0;
|
|
int s1=0;
|
|
int s2=0;
|
|
int s3=0;
|
|
int s4=0;
|
|
int sf=0;
|
|
int sfx=0;
|
|
int sfy=0;
|
|
int sfxy=0;
|
|
double y1=0;
|
|
double y2=0;
|
|
double y3=0;
|
|
double y4=0;
|
|
double dt=0;
|
|
double du=0;
|
|
//--- initialization
|
|
m=0;
|
|
n=0;
|
|
//--- check
|
|
if(!CAp::Assert((int)MathRound(c.m_c[1])==-3 || (int)MathRound(c.m_c[1])==-1,__FUNCTION__+": incorrect C!"))
|
|
return;
|
|
//--- initialization
|
|
n=(int)MathRound(c.m_c[2]);
|
|
m=(int)MathRound(c.m_c[3]);
|
|
//--- allocation
|
|
tbl.Resize((n-1)*(m-1),20);
|
|
//--- Fill
|
|
for(i=0;i<=m-2;i++)
|
|
{
|
|
for(j=0;j<=n-2;j++)
|
|
{
|
|
//--- calculation
|
|
p=i*(n-1)+j;
|
|
tbl[p].Set(0,c.m_c[4+j]);
|
|
tbl[p].Set(1,c.m_c[4+j+1]);
|
|
tbl[p].Set(2,c.m_c[4+n+i]);
|
|
tbl[p].Set(3,c.m_c[4+n+i+1]);
|
|
dt=1/(tbl[p][1]-tbl[p][0]);
|
|
du=1/(tbl[p][3]-tbl[p][2]);
|
|
//--- Bilinear interpolation
|
|
if((int)MathRound(c.m_c[1])==-1)
|
|
{
|
|
for(k=4;k<=19;k++)
|
|
tbl[p].Set(k,0);
|
|
//--- calculation
|
|
shift=4+n+m;
|
|
y1=c.m_c[shift+n*i+j];
|
|
y2=c.m_c[shift+n*i+(j+1)];
|
|
y3=c.m_c[shift+n*(i+1)+(j+1)];
|
|
y4=c.m_c[shift+n*(i+1)+j];
|
|
tbl[p].Set(4,y1);
|
|
tbl[p].Set(4+1*4+0,y2-y1);
|
|
tbl[p].Set(4+0*4+1,y4-y1);
|
|
tbl[p].Set(4+1*4+1,y3-y2-y4+y1);
|
|
}
|
|
//--- Bicubic interpolation
|
|
if((int)MathRound(c.m_c[1])==-3)
|
|
{
|
|
//--- change values
|
|
sf=4+n+m;
|
|
sfx=4+n+m+n*m;
|
|
sfy=4+n+m+2*n*m;
|
|
sfxy=4+n+m+3*n*m;
|
|
s1=n*i+j;
|
|
s2=n*i+(j+1);
|
|
s3=n*(i+1)+(j+1);
|
|
s4=n*(i+1)+j;
|
|
//--- change values
|
|
tbl[p].Set(4+0*4+0,1*c.m_c[sf+s1]);
|
|
tbl[p].Set(4+0*4+1,1*c.m_c[sfy+s1]/du);
|
|
tbl[p].Set(4+0*4+2,-(3*c.m_c[sf+s1])+3*c.m_c[sf+s4]-2*c.m_c[sfy+s1]/du-1*c.m_c[sfy+s4]/du);
|
|
tbl[p].Set(4+0*4+3,2*c.m_c[sf+s1]-2*c.m_c[sf+s4]+1*c.m_c[sfy+s1]/du+1*c.m_c[sfy+s4]/du);
|
|
tbl[p].Set(4+1*4+0,1*c.m_c[sfx+s1]/dt);
|
|
tbl[p].Set(4+1*4+1,1*c.m_c[sfxy+s1]/(dt*du));
|
|
tbl[p].Set(4+1*4+2,-(3*c.m_c[sfx+s1]/dt)+3*c.m_c[sfx+s4]/dt-2*c.m_c[sfxy+s1]/(dt*du)-1*c.m_c[sfxy+s4]/(dt*du));
|
|
tbl[p].Set(4+1*4+3,2*c.m_c[sfx+s1]/dt-2*c.m_c[sfx+s4]/dt+1*c.m_c[sfxy+s1]/(dt*du)+1*c.m_c[sfxy+s4]/(dt*du));
|
|
tbl[p].Set(4+2*4+0,-(3*c.m_c[sf+s1])+3*c.m_c[sf+s2]-2*c.m_c[sfx+s1]/dt-1*c.m_c[sfx+s2]/dt);
|
|
tbl[p].Set(4+2*4+1,-(3*c.m_c[sfy+s1]/du)+3*c.m_c[sfy+s2]/du-2*c.m_c[sfxy+s1]/(dt*du)-1*c.m_c[sfxy+s2]/(dt*du));
|
|
tbl[p].Set(4+2*4+2,9*c.m_c[sf+s1]-9*c.m_c[sf+s2]+9*c.m_c[sf+s3]-9*c.m_c[sf+s4]+6*c.m_c[sfx+s1]/dt+3*c.m_c[sfx+s2]/dt-3*c.m_c[sfx+s3]/dt-6*c.m_c[sfx+s4]/dt+6*c.m_c[sfy+s1]/du-6*c.m_c[sfy+s2]/du-3*c.m_c[sfy+s3]/du+3*c.m_c[sfy+s4]/du+4*c.m_c[sfxy+s1]/(dt*du)+2*c.m_c[sfxy+s2]/(dt*du)+1*c.m_c[sfxy+s3]/(dt*du)+2*c.m_c[sfxy+s4]/(dt*du));
|
|
tbl[p].Set(4+2*4+3,-(6*c.m_c[sf+s1])+6*c.m_c[sf+s2]-6*c.m_c[sf+s3]+6*c.m_c[sf+s4]-4*c.m_c[sfx+s1]/dt-2*c.m_c[sfx+s2]/dt+2*c.m_c[sfx+s3]/dt+4*c.m_c[sfx+s4]/dt-3*c.m_c[sfy+s1]/du+3*c.m_c[sfy+s2]/du+3*c.m_c[sfy+s3]/du-3*c.m_c[sfy+s4]/du-2*c.m_c[sfxy+s1]/(dt*du)-1*c.m_c[sfxy+s2]/(dt*du)-1*c.m_c[sfxy+s3]/(dt*du)-2*c.m_c[sfxy+s4]/(dt*du));
|
|
tbl[p].Set(4+3*4+0,2*c.m_c[sf+s1]-2*c.m_c[sf+s2]+1*c.m_c[sfx+s1]/dt+1*c.m_c[sfx+s2]/dt);
|
|
tbl[p].Set(4+3*4+1,2*c.m_c[sfy+s1]/du-2*c.m_c[sfy+s2]/du+1*c.m_c[sfxy+s1]/(dt*du)+1*c.m_c[sfxy+s2]/(dt*du));
|
|
tbl[p].Set(4+3*4+2,-(6*c.m_c[sf+s1])+6*c.m_c[sf+s2]-6*c.m_c[sf+s3]+6*c.m_c[sf+s4]-3*c.m_c[sfx+s1]/dt-3*c.m_c[sfx+s2]/dt+3*c.m_c[sfx+s3]/dt+3*c.m_c[sfx+s4]/dt-4*c.m_c[sfy+s1]/du+4*c.m_c[sfy+s2]/du+2*c.m_c[sfy+s3]/du-2*c.m_c[sfy+s4]/du-2*c.m_c[sfxy+s1]/(dt*du)-2*c.m_c[sfxy+s2]/(dt*du)-1*c.m_c[sfxy+s3]/(dt*du)-1*c.m_c[sfxy+s4]/(dt*du));
|
|
tbl[p].Set(4+3*4+3,4*c.m_c[sf+s1]-4*c.m_c[sf+s2]+4*c.m_c[sf+s3]-4*c.m_c[sf+s4]+2*c.m_c[sfx+s1]/dt+2*c.m_c[sfx+s2]/dt-2*c.m_c[sfx+s3]/dt-2*c.m_c[sfx+s4]/dt+2*c.m_c[sfy+s1]/du-2*c.m_c[sfy+s2]/du-2*c.m_c[sfy+s3]/du+2*c.m_c[sfy+s4]/du+1*c.m_c[sfxy+s1]/(dt*du)+1*c.m_c[sfxy+s2]/(dt*du)+1*c.m_c[sfxy+s3]/(dt*du)+1*c.m_c[sfxy+s4]/(dt*du));
|
|
}
|
|
//--- Rescale Cij
|
|
for(ci=0;ci<=3;ci++)
|
|
{
|
|
for(cj=0;cj<=3;cj++)
|
|
tbl[p].Set(4+ci*4+cj,tbl[p][4+ci*4+cj]*MathPow(dt,ci)*MathPow(du,cj));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine performs linear transformation of the spline |
|
|
//| argument. |
|
|
//| Input parameters: |
|
|
//| C - spline interpolant |
|
|
//| AX, BX - transformation coefficients: x = A*t + B |
|
|
//| AY, BY - transformation coefficients: y = A*u + B |
|
|
//| Result: |
|
|
//| C - transformed spline |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline2D::Spline2DLinTransXY(CSpline2DInterpolant &c,double ax,
|
|
double bx,double ay,double by)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
int m=0;
|
|
double v=0;
|
|
int typec=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble f;
|
|
//--- initialization
|
|
typec=(int)MathRound(c.m_c[1]);
|
|
//--- check
|
|
if(!CAp::Assert(typec==-3 || typec==-1,__FUNCTION__+": incorrect C!"))
|
|
return;
|
|
//--- initialization
|
|
n=(int)MathRound(c.m_c[2]);
|
|
m=(int)MathRound(c.m_c[3]);
|
|
//--- allocation
|
|
ArrayResizeAL(x,n);
|
|
ArrayResizeAL(y,m);
|
|
f.Resize(m,n);
|
|
//--- copy
|
|
for(j=0;j<=n-1;j++)
|
|
x[j]=c.m_c[4+j];
|
|
for(i=0;i<=m-1;i++)
|
|
y[i]=c.m_c[4+n+i];
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
f[i].Set(j,c.m_c[4+n+m+i*n+j]);
|
|
}
|
|
//--- Special case: AX=0 or AY=0
|
|
if(ax==0.0)
|
|
{
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
v=Spline2DCalc(c,bx,y[i]);
|
|
for(j=0;j<=n-1;j++)
|
|
f[i].Set(j,v);
|
|
}
|
|
//--- check
|
|
if(typec==-3)
|
|
Spline2DBuildBicubic(x,y,f,m,n,c);
|
|
//--- check
|
|
if(typec==-1)
|
|
Spline2DBuildBilinear(x,y,f,m,n,c);
|
|
//--- change values
|
|
ax=1;
|
|
bx=0;
|
|
}
|
|
//--- check
|
|
if(ay==0.0)
|
|
{
|
|
//--- change values
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=Spline2DCalc(c,x[j],by);
|
|
for(i=0;i<=m-1;i++)
|
|
f[i].Set(j,v);
|
|
}
|
|
//--- check
|
|
if(typec==-3)
|
|
Spline2DBuildBicubic(x,y,f,m,n,c);
|
|
//--- check
|
|
if(typec==-1)
|
|
Spline2DBuildBilinear(x,y,f,m,n,c);
|
|
//--- change values
|
|
ay=1;
|
|
by=0;
|
|
}
|
|
//--- General case: AX<>0,AY<>0
|
|
//--- Unpack,scale and pack again.
|
|
for(j=0;j<=n-1;j++)
|
|
x[j]=(x[j]-bx)/ax;
|
|
for(i=0;i<=m-1;i++)
|
|
y[i]=(y[i]-by)/ay;
|
|
//--- check
|
|
if(typec==-3)
|
|
Spline2DBuildBicubic(x,y,f,m,n,c);
|
|
//--- check
|
|
if(typec==-1)
|
|
Spline2DBuildBilinear(x,y,f,m,n,c);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine performs linear transformation of the spline. |
|
|
//| Input parameters: |
|
|
//| C - spline interpolant. |
|
|
//| A, B- transformation coefficients: S2(x,y) = A*S(x,y) + B |
|
|
//| Output parameters: |
|
|
//| C - transformed spline |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline2D::Spline2DLinTransF(CSpline2DInterpolant &c,const double a,
|
|
const double b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
int m=0;
|
|
int typec=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble f;
|
|
//--- initialization
|
|
typec=(int)MathRound(c.m_c[1]);
|
|
//--- check
|
|
if(!CAp::Assert(typec==-3 || typec==-1,__FUNCTION__+": incorrect C!"))
|
|
return;
|
|
//--- initialization
|
|
n=(int)MathRound(c.m_c[2]);
|
|
m=(int)MathRound(c.m_c[3]);
|
|
//--- allocation
|
|
ArrayResizeAL(x,n);
|
|
ArrayResizeAL(y,m);
|
|
f.Resize(m,n);
|
|
//--- copy
|
|
for(j=0;j<=n-1;j++)
|
|
x[j]=c.m_c[4+j];
|
|
for(i=0;i<=m-1;i++)
|
|
y[i]=c.m_c[4+n+i];
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
f[i].Set(j,a*c.m_c[4+n+m+i*n+j]+b);
|
|
}
|
|
//--- check
|
|
if(typec==-3)
|
|
Spline2DBuildBicubic(x,y,f,m,n,c);
|
|
//--- check
|
|
if(typec==-1)
|
|
Spline2DBuildBilinear(x,y,f,m,n,c);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine makes the copy of the spline model. |
|
|
//| Input parameters: |
|
|
//| C - spline interpolant |
|
|
//| Output parameters: |
|
|
//| CC - spline copy |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline2D::Spline2DCopy(CSpline2DInterpolant &c,CSpline2DInterpolant &cc)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(c.m_k==1 || c.m_k==3,__FUNCTION__+": incorrect C!"))
|
|
return;
|
|
//--- change values
|
|
cc.m_k=c.m_k;
|
|
n=(int)MathRound(c.m_c[0]);
|
|
//--- allocation
|
|
ArrayResizeAL(cc.m_c,n);
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
cc.m_c[i_]=c.m_c[i_];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Bicubic spline resampling |
|
|
//| Input parameters: |
|
|
//| A - function values at the old grid, |
|
|
//| array[0..OldHeight-1, 0..OldWidth-1] |
|
|
//| OldHeight - old grid height, OldHeight>1 |
|
|
//| OldWidth - old grid width, OldWidth>1 |
|
|
//| NewHeight - new grid height, NewHeight>1 |
|
|
//| NewWidth - new grid width, NewWidth>1 |
|
|
//| Output parameters: |
|
|
//| B - function values at the new grid, |
|
|
//| array[0..NewHeight-1, 0..NewWidth-1] |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline2D::Spline2DResampleBicubic(CMatrixDouble &a,const int oldheight,
|
|
const int oldwidth,CMatrixDouble &b,
|
|
const int newheight,const int newwidth)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int mw=0;
|
|
int mh=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble buf;
|
|
//--- object of class
|
|
CSpline1DInterpolant c;
|
|
//--- check
|
|
if(!CAp::Assert(oldwidth>1 && oldheight>1,__FUNCTION__+": width/height less than 1"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(newwidth>1 && newheight>1,__FUNCTION__+": width/height less than 1"))
|
|
return;
|
|
//--- Prepare
|
|
mw=MathMax(oldwidth,newwidth);
|
|
mh=MathMax(oldheight,newheight);
|
|
//--- allocation
|
|
b.Resize(newheight,newwidth);
|
|
buf.Resize(oldheight,newwidth);
|
|
ArrayResizeAL(x,MathMax(mw,mh));
|
|
ArrayResizeAL(y,MathMax(mw,mh));
|
|
//--- Horizontal interpolation
|
|
for(i=0;i<=oldheight-1;i++)
|
|
{
|
|
//--- Fill X,Y
|
|
for(j=0;j<=oldwidth-1;j++)
|
|
{
|
|
x[j]=(double)j/(double)(oldwidth-1);
|
|
y[j]=a[i][j];
|
|
}
|
|
//--- Interpolate and place result into temporary matrix
|
|
CSpline1D::Spline1DBuildCubic(x,y,oldwidth,0,0.0,0,0.0,c);
|
|
for(j=0;j<=newwidth-1;j++)
|
|
buf[i].Set(j,CSpline1D::Spline1DCalc(c,(double)j/(double)(newwidth-1)));
|
|
}
|
|
//--- Vertical interpolation
|
|
for(j=0;j<=newwidth-1;j++)
|
|
{
|
|
//--- Fill X,Y
|
|
for(i=0;i<=oldheight-1;i++)
|
|
{
|
|
x[i]=(double)i/(double)(oldheight-1);
|
|
y[i]=buf[i][j];
|
|
}
|
|
//--- Interpolate and place result into B
|
|
CSpline1D::Spline1DBuildCubic(x,y,oldheight,0,0.0,0,0.0,c);
|
|
for(i=0;i<=newheight-1;i++)
|
|
b[i].Set(j,CSpline1D::Spline1DCalc(c,(double)i/(double)(newheight-1)));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Bilinear spline resampling |
|
|
//| Input parameters: |
|
|
//| A - function values at the old grid, |
|
|
//| array[0..OldHeight-1, 0..OldWidth-1] |
|
|
//| OldHeight - old grid height, OldHeight>1 |
|
|
//| OldWidth - old grid width, OldWidth>1 |
|
|
//| NewHeight - new grid height, NewHeight>1 |
|
|
//| NewWidth - new grid width, NewWidth>1 |
|
|
//| Output parameters: |
|
|
//| B - function values at the new grid, |
|
|
//| array[0..NewHeight-1, 0..NewWidth-1] |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline2D::Spline2DResampleBilinear(CMatrixDouble &a,const int oldheight,
|
|
const int oldwidth,CMatrixDouble &b,
|
|
const int newheight,const int newwidth)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int l=0;
|
|
int c=0;
|
|
double t=0;
|
|
double u=0;
|
|
//--- allocation
|
|
b.Resize(newheight,newwidth);
|
|
for(i=0;i<=newheight-1;i++)
|
|
{
|
|
for(j=0;j<=newwidth-1;j++)
|
|
{
|
|
//--- calculation
|
|
l=i*(oldheight-1)/(newheight-1);
|
|
//--- check
|
|
if(l==oldheight-1)
|
|
l=oldheight-2;
|
|
//--- calculation
|
|
u=(double)i/(double)(newheight-1)*(oldheight-1)-l;
|
|
c=j*(oldwidth-1)/(newwidth-1);
|
|
//--- check
|
|
if(c==oldwidth-1)
|
|
c=oldwidth-2;
|
|
//--- calculation
|
|
t=(double)(j*(oldwidth-1))/(double)(newwidth-1)-c;
|
|
b[i].Set(j,(1-t)*(1-u)*a[l][c]+t*(1-u)*a[l][c+1]+t*u*a[l+1][c+1]+(1-t)*u*a[l+1][c]);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine. |
|
|
//| Calculation of the first derivatives and the cross-derivative. |
|
|
//+------------------------------------------------------------------+
|
|
static void CSpline2D::BicubicCalcDerivatives(CMatrixDouble &a,double &x[],
|
|
double &y[],const int m,
|
|
const int n,CMatrixDouble &dx,
|
|
CMatrixDouble &dy,CMatrixDouble &dxy)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double s=0;
|
|
double ds=0;
|
|
double d2s=0;
|
|
//--- create arrays
|
|
double xt[];
|
|
double ft[];
|
|
//--- object of class
|
|
CSpline1DInterpolant c;
|
|
//--- allocation
|
|
dx.Resize(m,n);
|
|
dy.Resize(m,n);
|
|
dxy.Resize(m,n);
|
|
//--- dF/dX
|
|
ArrayResizeAL(xt,n);
|
|
ArrayResizeAL(ft,n);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
xt[j]=x[j];
|
|
ft[j]=a[i][j];
|
|
}
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(xt,ft,n,0,0.0,0,0.0,c);
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- function call
|
|
CSpline1D::Spline1DDiff(c,x[j],s,ds,d2s);
|
|
dx[i].Set(j,ds);
|
|
}
|
|
}
|
|
//--- dF/dY
|
|
ArrayResizeAL(xt,m);
|
|
ArrayResizeAL(ft,m);
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
xt[i]=y[i];
|
|
ft[i]=a[i][j];
|
|
}
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(xt,ft,m,0,0.0,0,0.0,c);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
//--- function call
|
|
CSpline1D::Spline1DDiff(c,y[i],s,ds,d2s);
|
|
dy[i].Set(j,ds);
|
|
}
|
|
}
|
|
//--- d2F/dXdY
|
|
ArrayResizeAL(xt,n);
|
|
ArrayResizeAL(ft,n);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
xt[j]=x[j];
|
|
ft[j]=dy[i][j];
|
|
}
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(xt,ft,n,0,0.0,0,0.0,c);
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- function call
|
|
CSpline1D::Spline1DDiff(c,x[j],s,ds,d2s);
|
|
dxy[i].Set(j,ds);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|