Files
2018-03-09 16:43:19 +01:00

43858 lines
1.3 MiB
Plaintext

//+------------------------------------------------------------------+
//| TestClasses.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 <Math\Alglib\alglibmisc.mqh>
#include <Math\Alglib\ap.mqh>
#include <Math\Alglib\dataanalysis.mqh>
#include <Math\Alglib\diffequations.mqh>
#include <Math\Alglib\interpolation.mqh>
#include <Math\Alglib\fasttransforms.mqh>
//+------------------------------------------------------------------+
//| Testing class CHighQualityRand |
//+------------------------------------------------------------------+
class CTestHQRndUnit
{
public:
CTestHQRndUnit(void);
~CTestHQRndUnit(void);
static bool TestHQRnd(const bool silent);
private:
static void CalculateMV(double &x[],const int n,double &mean,double &means,double &stddev,double &stddevs);
static void UnsetState(CHighQualityRandState &state);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestHQRndUnit::CTestHQRndUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestHQRndUnit::~CTestHQRndUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CHighQualityRand |
//+------------------------------------------------------------------+
static bool CTestHQRndUnit::TestHQRnd(const bool silent)
{
bool waserrors;
int samplesize=0;
double sigmathreshold=0;
int passcount=0;
int n=0;
int i=0;
int pass=0;
int s1=0;
int s2=0;
int i1=0;
int i2=0;
double r1=0;
double r2=0;
double mean=0;
double means=0;
double stddev=0;
double stddevs=0;
double lambdav=0;
bool seederrors;
bool urerrors;
double ursigmaerr=0;
bool uierrors;
double uisigmaerr=0;
bool normerrors;
double normsigmaerr=0;
bool experrors;
double expsigmaerr=0;
//--- create array
double x[];
//--- object of class
CHighQualityRandState state;
//--- initialization
waserrors=false;
sigmathreshold=7;
samplesize=100000;
passcount=50;
seederrors=false;
urerrors=false;
uierrors=false;
normerrors=false;
experrors=false;
//--- allocation
ArrayResize(x,samplesize);
//--- Test seed errors
for(pass=1;pass<=passcount;pass++)
{
//--- change values
s1=1+CMath::RandomInteger(32000);
s2=1+CMath::RandomInteger(32000);
//--- function calls
UnsetState(state);
CHighQualityRand::HQRndSeed(s1,s2,state);
//--- change value
i1=CHighQualityRand::HQRndUniformI(state,100);
//--- function calls
UnsetState(state);
CHighQualityRand::HQRndSeed(s1,s2,state);
//--- change values
i2=CHighQualityRand::HQRndUniformI(state,100);
seederrors=seederrors || i1!=i2;
//--- function calls
UnsetState(state);
CHighQualityRand::HQRndSeed(s1,s2,state);
//--- change value
r1=CHighQualityRand::HQRndUniformR(state);
//--- function calls
UnsetState(state);
CHighQualityRand::HQRndSeed(s1,s2,state);
//--- change values
r2=CHighQualityRand::HQRndUniformR(state);
seederrors=seederrors || r1!=r2;
}
//--- Test HQRNDRandomize() and real uniform generator
UnsetState(state);
CHighQualityRand::HQRndRandomize(state);
//--- change values
ursigmaerr=0;
for(i=0;i<=samplesize-1;i++)
x[i]=CHighQualityRand::HQRndUniformR(state);
for(i=0;i<=samplesize-1;i++)
urerrors=(urerrors || x[i]<=0.0) || x[i]>=1.0;
//--- function call
CalculateMV(x,samplesize,mean,means,stddev,stddevs);
//--- check
if(means!=0.0)
ursigmaerr=MathMax(ursigmaerr,MathAbs((mean-0.5)/means));
else
urerrors=true;
//--- check
if(stddevs!=0.0)
ursigmaerr=MathMax(ursigmaerr,MathAbs((stddev-MathSqrt(1.0/12.0))/stddevs));
else
urerrors=true;
//--- change value
urerrors=urerrors || ursigmaerr>sigmathreshold;
//--- Test HQRNDRandomize() and integer uniform
UnsetState(state);
CHighQualityRand::HQRndRandomize(state);
//--- calculation
uisigmaerr=0;
for(n=2;n<=10;n++)
{
for(i=0;i<=samplesize-1;i++)
x[i]=CHighQualityRand::HQRndUniformI(state,n);
for(i=0;i<=samplesize-1;i++)
uierrors=(uierrors || x[i]<0.0) || x[i]>=n;
//--- function call
CalculateMV(x,samplesize,mean,means,stddev,stddevs);
//--- check
if(means!=0.0)
uisigmaerr=MathMax(uisigmaerr,MathAbs((mean-0.5*(n-1))/means));
else
uierrors=true;
//--- check
if(stddevs!=0.0)
uisigmaerr=MathMax(uisigmaerr,MathAbs((stddev-MathSqrt((CMath::Sqr(n)-1)/12))/stddevs));
else
uierrors=true;
}
//--- change values
uierrors=uierrors || uisigmaerr>sigmathreshold;
//--- Special 'close-to-limit' test on uniformity of integers
//--- (straightforward implementation like 'RND mod N' will return
//--- non-uniform numbers for N=2/3*LIMIT)
UnsetState(state);
CHighQualityRand::HQRndRandomize(state);
//--- change values
uisigmaerr=0;
n=1431655708;
//--- calculation
for(i=0;i<=samplesize-1;i++)
x[i]=CHighQualityRand::HQRndUniformI(state,n);
for(i=0;i<=samplesize-1;i++)
uierrors=(uierrors || x[i]<0.0) || x[i]>=n;
//--- function call
CalculateMV(x,samplesize,mean,means,stddev,stddevs);
//--- check
if(means!=0.0)
uisigmaerr=MathMax(uisigmaerr,MathAbs((mean-0.5*(n-1))/means));
else
uierrors=true;
//--- check
if(stddevs!=0.0)
uisigmaerr=MathMax(uisigmaerr,MathAbs((stddev-MathSqrt((CMath::Sqr(n)-1)/12))/stddevs));
else
uierrors=true;
uierrors=uierrors || uisigmaerr>sigmathreshold;
//--- Test normal
UnsetState(state);
CHighQualityRand::HQRndRandomize(state);
//--- change values
normsigmaerr=0;
i=0;
//--- cycle
while(i<samplesize)
{
//--- function call
CHighQualityRand::HQRndNormal2(state,r1,r2);
x[i]=r1;
//--- check
if(i+1<samplesize)
{
x[i+1]=r2;
}
i=i+2;
}
//--- function call
CalculateMV(x,samplesize,mean,means,stddev,stddevs);
//--- check
if(means!=0.0)
normsigmaerr=MathMax(normsigmaerr,MathAbs((mean-0)/means));
else
normerrors=true;
//--- check
if(stddevs!=0.0)
normsigmaerr=MathMax(normsigmaerr,MathAbs((stddev-1)/stddevs));
else
normerrors=true;
normerrors=normerrors || (double)(normsigmaerr)>sigmathreshold;
//--- Test exponential
UnsetState(state);
CHighQualityRand::HQRndRandomize(state);
//--- change values
expsigmaerr=0;
lambdav=2+5*CMath::RandomReal();
//--- calculation
for(i=0;i<=samplesize-1;i++)
x[i]=CHighQualityRand::HQRndExponential(state,lambdav);
for(i=0;i<=samplesize-1;i++)
uierrors=uierrors || x[i]<0.0;
//--- function call
CalculateMV(x,samplesize,mean,means,stddev,stddevs);
//--- check
if(means!=0.0)
expsigmaerr=MathMax(expsigmaerr,MathAbs((mean-1.0/lambdav)/means));
else
experrors=true;
//--- check
if(stddevs!=0.0)
expsigmaerr=MathMax(expsigmaerr,MathAbs((stddev-1.0/lambdav)/stddevs));
else
experrors=true;
experrors=experrors || expsigmaerr>sigmathreshold;
//--- Final report
waserrors=(((seederrors || urerrors) || uierrors) || normerrors) || experrors;
//--- check
if(!silent)
{
Print("RNG TEST");
//--- check
if(!seederrors)
Print("SEED TEST: OK");
else
Print("SEED TEST: FAILED");
//--- check
if(!urerrors)
Print("UNIFORM CONTINUOUS: OK");
else
Print("UNIFORM CONTINUOUS: FAILED");
//--- check
if(!uierrors)
Print("UNIFORM INTEGER: OK");
else
Print("UNIFORM INTEGER: FAILED");
//--- check
if(!normerrors)
Print("NORMAL: OK");
else
Print("NORMAL: FAILED");
//--- check
if(!experrors)
Print("EXPONENTIAL: OK");
else
Print("EXPONENTIAL: FAILED");
//--- check
if(waserrors)
Print("TEST SUMMARY: FAILED");
else
Print("TEST SUMMARY: PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestHQRndUnit::CalculateMV(double &x[],const int n,double &mean,
double &means,double &stddev,
double &stddevs)
{
//--- create variables
int i=0;
double v1=0;
double v2=0;
double variance=0;
//--- initialization
mean=0;
means=1;
stddev=0;
stddevs=1;
variance=0;
//--- check
if(n<=1)
return;
//--- Mean
for(i=0;i<=n-1;i++)
mean=mean+x[i];
mean=mean/n;
//--- Variance (using corrected two-pass algorithm)
if(n!=1)
{
//--- change value
v1=0;
for(i=0;i<=n-1;i++)
v1=v1+CMath::Sqr(x[i]-mean);
//--- change value
v2=0;
for(i=0;i<=n-1;i++)
v2=v2+(x[i]-mean);
//--- calculation
v2=CMath::Sqr(v2)/n;
variance=(v1-v2)/(n-1);
//--- check
if(variance<0.0)
variance=0;
stddev=MathSqrt(variance);
}
//--- Errors
means=stddev/MathSqrt(n);
stddevs=stddev*MathSqrt(2)/MathSqrt(n-1);
}
//+------------------------------------------------------------------+
//| Unsets HQRNDState structure |
//+------------------------------------------------------------------+
static void CTestHQRndUnit::UnsetState(CHighQualityRandState &state)
{
state.m_s1=0;
state.m_s2=0;
state.m_v=0;
state.m_magicv=0;
}
//+------------------------------------------------------------------+
//| Testing class CTSort |
//+------------------------------------------------------------------+
class CTestTSortUnit
{
public:
CTestTSortUnit(void);
~CTestTSortUnit(void);
static bool TestTSort(const bool silent);
private:
static void Unset2D(CMatrixComplex &a);
static void Unset1D(double &a[]);
static void Unset1DI(int &a[]);
static void TestSortResults(double &asorted[],int &p1[],int &p2[],double &aoriginal[],const int n,bool &waserrors);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestTSortUnit::CTestTSortUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestTSortUnit::~CTestTSortUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CTSort |
//+------------------------------------------------------------------+
static bool CTestTSortUnit::TestTSort(const bool silent)
{
//--- create variables
bool waserrors;
int n=0;
int i=0;
int pass=0;
int passcount=0;
int maxn=0;
//--- create arrays
double a[];
double a0[];
double a1[];
double a2[];
double a3[];
double ar[];
int ai[];
int p1[];
int p2[];
double bufr1[];
double bufr2[];
int bufi1[];
//--- initialization
waserrors=false;
maxn=100;
passcount=10;
//--- Test tagsort
for(n=1;n<=maxn;n++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- (probably) distinct sort:
//--- * first sort A0 using TagSort and test sort results
//--- * now we can use A0 as reference point and test other functions
Unset1DI(p1);
Unset1DI(p2);
//--- allocation
ArrayResize(a,n);
ArrayResize(a0,n);
ArrayResize(a1,n);
ArrayResize(a2,n);
ArrayResize(a3,n);
ArrayResize(ar,n);
ArrayResize(ai,n);
//--- change values
for(i=0;i<=n-1;i++)
{
a[i]=2*CMath::RandomReal()-1;
a0[i]=a[i];
a1[i]=a[i];
a2[i]=a[i];
a3[i]=a[i];
ar[i]=i;
ai[i]=i;
}
//--- function call
CTSort::TagSort(a0,n,p1,p2);
//--- function call
TestSortResults(a0,p1,p2,a,n,waserrors);
//--- function call
CTSort::TagSortFastI(a1,ai,bufr1,bufi1,n);
//--- search errors
for(i=0;i<=n-1;i++)
waserrors=(waserrors || a1[i]!=a0[i]) || ai[i]!=p1[i];
//--- function call
CTSort::TagSortFastR(a2,ar,bufr1,bufr2,n);
//--- search errors
for(i=0;i<=n-1;i++)
waserrors=(waserrors || a2[i]!=a0[i]) || ar[i]!=p1[i];
//--- function call
CTSort::TagSortFast(a3,bufr1,n);
//--- search errors
for(i=0;i<=n-1;i++)
waserrors=waserrors || a3[i]!=a0[i];
//--- non-distinct sort
Unset1DI(p1);
Unset1DI(p2);
//--- allocation
ArrayResize(a,n);
ArrayResize(a0,n);
ArrayResize(a1,n);
ArrayResize(a2,n);
ArrayResize(a3,n);
ArrayResize(ar,n);
ArrayResize(ai,n);
//--- change values
for(i=0;i<=n-1;i++)
{
a[i]=i/2;
a0[i]=a[i];
a1[i]=a[i];
a2[i]=a[i];
a3[i]=a[i];
ar[i]=i;
ai[i]=i;
}
//--- function call
CTSort::TagSort(a0,n,p1,p2);
//--- function call
TestSortResults(a0,p1,p2,a,n,waserrors);
//--- function call
CTSort::TagSortFastI(a1,ai,bufr1,bufi1,n);
//--- search errors
for(i=0;i<=n-1;i++)
waserrors=(waserrors || a1[i]!=a0[i]) || ai[i]!=p1[i];
//--- function call
CTSort::TagSortFastR(a2,ar,bufr1,bufr2,n);
//--- search errors
for(i=0;i<=n-1;i++)
waserrors=(waserrors || a2[i]!=a0[i]) || ar[i]!=p1[i];
//--- function call
CTSort::TagSortFast(a3,bufr1,n);
//--- search errors
for(i=0;i<=n-1;i++)
waserrors=waserrors || a3[i]!=a0[i];
//--- 'All same' sort
Unset1DI(p1);
Unset1DI(p2);
//--- allocation
ArrayResize(a,n);
ArrayResize(a0,n);
ArrayResize(a1,n);
ArrayResize(a2,n);
ArrayResize(a3,n);
ArrayResize(ar,n);
ArrayResize(ai,n);
//--- change values
for(i=0;i<=n-1;i++)
{
a[i]=0;
a0[i]=a[i];
a1[i]=a[i];
a2[i]=a[i];
a3[i]=a[i];
ar[i]=i;
ai[i]=i;
}
//--- function call
CTSort::TagSort(a0,n,p1,p2);
//--- function call
TestSortResults(a0,p1,p2,a,n,waserrors);
//--- function call
CTSort::TagSortFastI(a1,ai,bufr1,bufi1,n);
//--- search errors
for(i=0;i<=n-1;i++)
waserrors=(waserrors || a1[i]!=a0[i]) || ai[i]!=p1[i];
//--- function call
CTSort::TagSortFastR(a2,ar,bufr1,bufr2,n);
//--- search errors
for(i=0;i<=n-1;i++)
waserrors=(waserrors || a2[i]!=a0[i]) || ar[i]!=p1[i];
//--- function call
CTSort::TagSortFast(a3,bufr1,n);
//--- search errors
for(i=0;i<=n-1;i++)
waserrors=waserrors || a3[i]!=a0[i];
//--- 0-1 sort
Unset1DI(p1);
Unset1DI(p2);
//--- allocation
ArrayResize(a,n);
ArrayResize(a0,n);
ArrayResize(a1,n);
ArrayResize(a2,n);
ArrayResize(a3,n);
ArrayResize(ar,n);
ArrayResize(ai,n);
//--- change values
for(i=0;i<=n-1;i++)
{
a[i]=CMath::RandomInteger(2);
a0[i]=a[i];
a1[i]=a[i];
a2[i]=a[i];
a3[i]=a[i];
ar[i]=i;
ai[i]=i;
}
//--- function call
CTSort::TagSort(a0,n,p1,p2);
//--- function call
TestSortResults(a0,p1,p2,a,n,waserrors);
//--- function call
CTSort::TagSortFastI(a1,ai,bufr1,bufi1,n);
//--- search errors
for(i=0;i<=n-1;i++)
waserrors=(waserrors || a1[i]!=a0[i]) || ai[i]!=p1[i];
//--- function call
CTSort::TagSortFastR(a2,ar,bufr1,bufr2,n);
//--- search errors
for(i=0;i<=n-1;i++)
waserrors=(waserrors || a2[i]!=a0[i]) || ar[i]!=p1[i];
//--- function call
CTSort::TagSortFast(a3,bufr1,n);
//--- search errors
for(i=0;i<=n-1;i++)
waserrors=waserrors || a3[i]!=a0[i];
}
}
//--- report
if(!silent)
{
Print("TESTING TAGSORT");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Unsets 2D array. |
//+------------------------------------------------------------------+
static void CTestTSortUnit::Unset2D(CMatrixComplex &a)
{
//--- allocation
a.Resize(1,1);
//--- change value
a[0].Set(0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets 1D array. |
//+------------------------------------------------------------------+
static void CTestTSortUnit::Unset1D(double &a[])
{
//--- allocation
ArrayResize(a,1);
//--- change value
a[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Unsets 1D array. |
//+------------------------------------------------------------------+
static void CTestTSortUnit::Unset1DI(int &a[])
{
//--- allocation
ArrayResize(a,1);
//--- change value
a[0]=CMath::RandomInteger(3)-1;
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestTSortUnit::TestSortResults(double &asorted[],int &p1[],
int &p2[],double &aoriginal[],
const int n,bool &waserrors)
{
//--- create variables
int i=0;
double t=0;
//--- create arrays
double a2[];
int f[];
//--- allocation
ArrayResize(a2,n);
ArrayResize(f,n);
//--- is set ordered?
for(i=0;i<=n-2;i++)
waserrors=waserrors || asorted[i]>asorted[i+1];
//--- P1 correctness
for(i=0;i<=n-1;i++)
waserrors=waserrors || asorted[i]!=aoriginal[p1[i]];
//--- change values
for(i=0;i<=n-1;i++)
f[i]=0;
for(i=0;i<=n-1;i++)
f[p1[i]]=f[p1[i]]+1;
//--- search errors
for(i=0;i<=n-1;i++)
waserrors=waserrors || f[i]!=1;
//--- P2 correctness
for(i=0;i<=n-1;i++)
a2[i]=aoriginal[i];
for(i=0;i<=n-1;i++)
{
//--- check
if(p2[i]!=i)
{
t=a2[i];
a2[i]=a2[p2[i]];
a2[p2[i]]=t;
}
}
//--- search errors
for(i=0;i<=n-1;i++)
waserrors=waserrors || asorted[i]!=a2[i];
}
//+------------------------------------------------------------------+
//| Testing class CNearestNeighbor |
//+------------------------------------------------------------------+
class CTestNearestNeighborUnit
{
public:
CTestNearestNeighborUnit(void);
~CTestNearestNeighborUnit(void);
static bool TestNearestNeighbor(const bool silent);
static void Unset2D(CMatrixComplex &a);
static void Unset1D(double &a[]);
static bool KDTResultsDifferent(CMatrixDouble &refxy,const int ntotal,CMatrixDouble &qx,CMatrixDouble &qxy,int &qt[],const int n,const int nx,const int ny);
static double VNorm(double &x[],const int n,const int normtype);
static void TestKDTUniform(CMatrixDouble &xy,const int n,const int nx,const int ny,const int normtype,bool &kdterrors);
private:
static void TestKDTreeSerialization(bool &err);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestNearestNeighborUnit::CTestNearestNeighborUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestNearestNeighborUnit::~CTestNearestNeighborUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CNearestNeighbor |
//+------------------------------------------------------------------+
static bool CTestNearestNeighborUnit::TestNearestNeighbor(const bool silent)
{
//--- create variables
int i=0;
int j=0;
double v=0;
int normtype=0;
int nx=0;
int ny=0;
int n=0;
int smalln=0;
int largen=0;
int passcount=0;
int pass=0;
bool waserrors;
bool kdterrors;
//--- create matrix
CMatrixDouble xy;
//--- initialization
kdterrors=false;
passcount=2;
smalln=256;
largen=2048;
ny=3;
//--- function call
TestKDTreeSerialization(kdterrors);
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
for(normtype=0;normtype<=2;normtype++)
{
for(nx=1;nx<=3;nx++)
{
//--- Test in hypercube
xy.Resize(largen,nx+ny);
for(i=0;i<=largen-1;i++)
{
for(j=0;j<=nx+ny-1;j++)
xy[i].Set(j,10*CMath::RandomReal()-5);
}
//--- function calls
for(n=1;n<=10;n++)
TestKDTUniform(xy,n,nx,CMath::RandomInteger(ny+1),normtype,kdterrors);
TestKDTUniform(xy,largen,nx,CMath::RandomInteger(ny+1),normtype,kdterrors);
//--- Test clustered (2*N points,pairs of equal points)
xy.Resize(2*smalln,nx+ny);
for(i=0;i<=smalln-1;i++)
{
for(j=0;j<=nx+ny-1;j++)
{
xy[2*i].Set(j,10*CMath::RandomReal()-5);
xy[2*i+1].Set(j,xy[2*i][j]);
}
}
//--- function call
TestKDTUniform(xy,2*smalln,nx,CMath::RandomInteger(ny+1),normtype,kdterrors);
//--- Test degenerate case: all points are same except for one
xy.Resize(smalln,nx+ny);
v=CMath::RandomReal();
//--- change values
for(i=0;i<=smalln-2;i++)
{
for(j=0;j<=nx+ny-1;j++)
xy[i].Set(j,v);
}
for(j=0;j<=nx+ny-1;j++)
xy[smalln-1].Set(j,10*CMath::RandomReal()-5);
//--- function call
TestKDTUniform(xy,smalln,nx,CMath::RandomInteger(ny+1),normtype,kdterrors);
}
}
}
//--- report
waserrors=kdterrors;
//--- check
if(!silent)
{
Print("TESTING NEAREST NEIGHBOR SEARCH");
//--- check
if(!kdterrors)
Print("KD TREES: OK");
else
Print("KD TREES: FAILED");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Unsets 2D array. |
//+------------------------------------------------------------------+
static void CTestNearestNeighborUnit::Unset2D(CMatrixComplex &a)
{
//--- allocation
a.Resize(1,1);
//--- change value
a[0].Set(0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets 1D array. |
//+------------------------------------------------------------------+
static void CTestNearestNeighborUnit::Unset1D(double &a[])
{
//--- allocation
ArrayResize(a,1);
//--- change value
a[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Compare results from different queries: |
//| * X just X-values |
//| * XY X-values and Y-values |
//| * XT X-values and tag values |
//+------------------------------------------------------------------+
static bool CTestNearestNeighborUnit::KDTResultsDifferent(CMatrixDouble &refxy,
const int ntotal,
CMatrixDouble &qx,
CMatrixDouble &qxy,
int &qt[],const int n,
const int nx,const int ny)
{
//--- create variables
bool result;
int i=0;
int j=0;
//--- initialization
result=false;
for(i=0;i<=n-1;i++)
{
//--- check
if(qt[i]<0 || qt[i]>=ntotal)
{
//--- return result
return(true);
}
//--- search errors
for(j=0;j<=nx-1;j++)
{
result=result || qx[i][j]!=refxy[qt[i]][j];
result=result || qxy[i][j]!=refxy[qt[i]][j];
}
for(j=0;j<=ny-1;j++)
result=result || qxy[i][nx+j]!=refxy[qt[i]][nx+j];
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Returns norm |
//+------------------------------------------------------------------+
static double CTestNearestNeighborUnit::VNorm(double &x[],const int n,
const int normtype)
{
//--- create variables
double result=0;
int i=0;
//--- initialization
result=CMath::RandomReal();
//--- check
if(normtype==0)
{
//--- calculation
result=0;
for(i=0;i<=n-1;i++)
result=MathMax(result,MathAbs(x[i]));
//--- return result
return(result);
}
//--- check
if(normtype==1)
{
//--- calculation
result=0;
for(i=0;i<=n-1;i++)
result=result+MathAbs(x[i]);
//--- return result
return(result);
}
//--- check
if(normtype==2)
{
//--- calculation
result=0;
for(i=0;i<=n-1;i++)
result=result+CMath::Sqr(x[i]);
result=MathSqrt(result);
//--- return result
return(result);
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Testing Nearest Neighbor Search on uniformly distributed |
//| hypercube |
//| NormType: 0,1,2 |
//| D: space dimension |
//| N: points count |
//+------------------------------------------------------------------+
static void CTestNearestNeighborUnit::TestKDTUniform(CMatrixDouble &xy,
const int n,const int nx,
const int ny,const int normtype,
bool &kdterrors)
{
//--- create variables
double errtol=0;
int kx=0;
int kxy=0;
int kt=0;
double eps=0;
int i=0;
int j=0;
int k=0;
int task=0;
bool isequal;
double r=0;
int q=0;
int qcount=0;
int i_=0;
//--- create arrays
int tags[];
double ptx[];
double tmpx[];
bool tmpb[];
int qtags[];
double qr[];
//--- objects of classes
CKDTree treex;
CKDTree treexy;
CKDTree treext;
//--- create matrix
CMatrixDouble qx;
CMatrixDouble qxy;
//--- initialization
qcount=10;
//--- Tol - roundoff error tolerance (for '>=' comparisons)
errtol=100000*CMath::m_machineepsilon;
//--- fill tags
ArrayResize(tags,n);
for(i=0;i<=n-1;i++)
tags[i]=i;
//--- build trees
CNearestNeighbor::KDTreeBuild(xy,n,nx,0,normtype,treex);
CNearestNeighbor::KDTreeBuild(xy,n,nx,ny,normtype,treexy);
CNearestNeighbor::KDTreeBuildTagged(xy,tags,n,nx,0,normtype,treext);
//--- allocate arrays
ArrayResize(tmpx,nx);
ArrayResize(tmpb,n);
qx.Resize(n,nx);
qxy.Resize(n,nx+ny);
ArrayResize(qtags,n);
ArrayResize(qr,n);
ArrayResize(ptx,nx);
//--- test general K-NN queries (with self-matches):
//--- * compare results from different trees (must be equal) and
//--- check that correct (value,tag) pairs are returned
//--- * test results from XT tree - let R be radius of query result.
//--- then all points not in result must be not closer than R.
for(q=1;q<=qcount;q++)
{
//--- Select K: 1..N
if(CMath::RandomReal()>0.5)
k=1+CMath::RandomInteger(n);
else
k=1;
//--- Select point (either one of the points,or random)
if(CMath::RandomReal()>0.5)
{
i=CMath::RandomInteger(n);
for(i_=0;i_<=nx-1;i_++)
ptx[i_]=xy[i][i_];
}
else
{
for(i=0;i<=nx-1;i++)
ptx[i]=2*CMath::RandomReal()-1;
}
//--- Test:
//--- * consistency of results from different queries
//--- * points in query are IN the R-sphere (or at the boundary),
//--- and points not in query are outside of the R-sphere (or at the boundary)
//--- * distances are correct and are ordered
kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,k,true);
kxy=CNearestNeighbor::KDTreeQueryKNN(treexy,ptx,k,true);
kt=CNearestNeighbor::KDTreeQueryKNN(treext,ptx,k,true);
//--- check
if((kx!=k || kxy!=k) || kt!=k)
{
kdterrors=true;
return;
}
//--- function calls
CNearestNeighbor::KDTreeQueryResultsXI(treex,qx);
CNearestNeighbor::KDTreeQueryResultsXYI(treexy,qxy);
CNearestNeighbor::KDTreeQueryResultsTagsI(treext,qtags);
CNearestNeighbor::KDTreeQueryResultsDistancesI(treext,qr);
//--- search errors
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
//--- function calls
CNearestNeighbor::KDTreeQueryResultsX(treex,qx);
CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy);
CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags);
CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr);
//--- search errors
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
//--- change values
for(i=0;i<=n-1;i++)
tmpb[i]=true;
r=0;
//--- calculation
for(i=0;i<=k-1;i++)
{
tmpb[qtags[i]]=false;
for(i_=0;i_<=nx-1;i_++)
tmpx[i_]=ptx[i_];
for(i_=0;i_<=nx-1;i_++)
tmpx[i_]=tmpx[i_]-qx[i][i_];
r=MathMax(r,VNorm(tmpx,nx,normtype));
}
for(i=0;i<=n-1;i++)
{
//--- check
if(tmpb[i])
{
for(i_=0;i_<=nx-1;i_++)
tmpx[i_]=ptx[i_];
for(i_=0;i_<=nx-1;i_++)
tmpx[i_]=tmpx[i_]-xy[i][i_];
//--- search errors
kdterrors=kdterrors || VNorm(tmpx,nx,normtype)<r*(1-errtol);
}
}
for(i=0;i<=k-2;i++)
{
//--- search errors
kdterrors=kdterrors || qr[i]>qr[i+1];
}
for(i=0;i<=k-1;i++)
{
for(i_=0;i_<=nx-1;i_++)
tmpx[i_]=ptx[i_];
for(i_=0;i_<=nx-1;i_++)
tmpx[i_]=tmpx[i_]-xy[qtags[i]][i_];
//--- search errors
kdterrors=kdterrors || MathAbs(VNorm(tmpx,nx,normtype)-qr[i])>errtol;
}
//--- Test reallocation properties: buffered functions must automatically
//--- resize array which is too small,but leave unchanged array which is
//--- too large.
if(n>=2)
{
//--- First step: array is too small,two elements are required
k=2;
kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,k,true);
kxy=CNearestNeighbor::KDTreeQueryKNN(treexy,ptx,k,true);
kt=CNearestNeighbor::KDTreeQueryKNN(treext,ptx,k,true);
//--- check
if((kx!=k || kxy!=k) || kt!=k)
{
kdterrors=true;
return;
}
//--- allocation
qx.Resize(1,1);
qxy.Resize(1,1);
ArrayResize(qtags,1);
ArrayResize(qr,1);
//--- function calls
CNearestNeighbor::KDTreeQueryResultsX(treex,qx);
CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy);
CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags);
CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr);
//--- search errors
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
//--- Second step: array is one row larger than needed,so only first
//--- row is overwritten. Test it.
k=1;
kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,k,true);
kxy=CNearestNeighbor::KDTreeQueryKNN(treexy,ptx,k,true);
kt=CNearestNeighbor::KDTreeQueryKNN(treext,ptx,k,true);
//--- check
if((kx!=k || kxy!=k) || kt!=k)
{
kdterrors=true;
return;
}
//--- change values
for(i=0;i<=nx-1;i++)
qx[1].Set(i,CInfOrNaN::NaN());
for(i=0;i<=nx+ny-1;i++)
qxy[1].Set(i,CInfOrNaN::NaN());
qtags[1]=999;
qr[1]=CInfOrNaN::NaN();
//--- function calls
CNearestNeighbor::KDTreeQueryResultsX(treex,qx);
CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy);
CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags);
CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr);
//--- search errors
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
for(i=0;i<=nx-1;i++)
{
//--- search errors
kdterrors=kdterrors || !CInfOrNaN::IsNaN(qx[1][i]);
}
for(i=0;i<=nx+ny-1;i++)
{
//--- search errors
kdterrors=kdterrors || !CInfOrNaN::IsNaN(qxy[1][i]);
}
//--- search errors
kdterrors=kdterrors || !(qtags[1]==999);
kdterrors=kdterrors || !CInfOrNaN::IsNaN(qr[1]);
}
//--- Test reallocation properties: 'interactive' functions must allocate
//--- new array on each call.
if(n>=2)
{
//--- On input array is either too small or too large
for(k=1;k<=2;k++)
{
//--- check
if(!CAp::Assert(k==1 || k==2,"KNN: internal error (unexpected K)!"))
return;
//--- change values
kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,k,true);
kxy=CNearestNeighbor::KDTreeQueryKNN(treexy,ptx,k,true);
kt=CNearestNeighbor::KDTreeQueryKNN(treext,ptx,k,true);
//--- check
if((kx!=k || kxy!=k) || kt!=k)
{
kdterrors=true;
return;
}
//--- allocation
qx.Resize(3-k,3-k);
qxy.Resize(3-k,3-k);
ArrayResize(qtags,3-k);
ArrayResize(qr,3-k);
//--- function calls
CNearestNeighbor::KDTreeQueryResultsXI(treex,qx);
CNearestNeighbor::KDTreeQueryResultsXYI(treexy,qxy);
CNearestNeighbor::KDTreeQueryResultsTagsI(treext,qtags);
CNearestNeighbor::KDTreeQueryResultsDistancesI(treext,qr);
//--- search errors
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
kdterrors=(kdterrors || CAp::Rows(qx)!=k) || CAp::Cols(qx)!=nx;
kdterrors=(kdterrors || CAp::Rows(qxy)!=k) || CAp::Cols(qxy)!=nx+ny;
kdterrors=kdterrors || CAp::Len(qtags)!=k;
kdterrors=kdterrors || CAp::Len(qr)!=k;
}
}
}
//--- test general approximate K-NN queries (with self-matches):
//--- * compare results from different trees (must be equal) and
//--- check that correct (value,tag) pairs are returned
//--- * test results from XT tree - let R be radius of query result.
//--- then all points not in result must be not closer than R/(1+Eps).
for(q=1;q<=qcount;q++)
{
//--- Select K: 1..N
if(CMath::RandomReal()>0.5)
k=1+CMath::RandomInteger(n);
else
k=1;
//--- Select Eps
eps=0.5+CMath::RandomReal();
//--- Select point (either one of the points,or random)
if(CMath::RandomReal()>0.5)
{
i=CMath::RandomInteger(n);
for(i_=0;i_<=nx-1;i_++)
ptx[i_]=xy[i][i_];
}
else
{
for(i=0;i<=nx-1;i++)
ptx[i]=2*CMath::RandomReal()-1;
}
//--- Test:
//--- * consistency of results from different queries
//--- * points in query are IN the R-sphere (or at the boundary),
//--- and points not in query are outside of the R-sphere (or at the boundary)
//--- * distances are correct and are ordered
kx=CNearestNeighbor::KDTreeQueryAKNN(treex,ptx,k,true,eps);
kxy=CNearestNeighbor::KDTreeQueryAKNN(treexy,ptx,k,true,eps);
kt=CNearestNeighbor::KDTreeQueryAKNN(treext,ptx,k,true,eps);
//--- check
if((kx!=k || kxy!=k) || kt!=k)
{
kdterrors=true;
return;
}
//--- function calls
CNearestNeighbor::KDTreeQueryResultsXI(treex,qx);
CNearestNeighbor::KDTreeQueryResultsXYI(treexy,qxy);
CNearestNeighbor::KDTreeQueryResultsTagsI(treext,qtags);
CNearestNeighbor::KDTreeQueryResultsDistancesI(treext,qr);
//--- search errors
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
//--- function calls
CNearestNeighbor::KDTreeQueryResultsX(treex,qx);
CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy);
CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags);
CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr);
//--- search errors
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
//--- change values
for(i=0;i<=n-1;i++)
tmpb[i]=true;
r=0;
//--- calculation
for(i=0;i<=k-1;i++)
{
tmpb[qtags[i]]=false;
for(i_=0;i_<=nx-1;i_++)
tmpx[i_]=ptx[i_];
for(i_=0;i_<=nx-1;i_++)
tmpx[i_]=tmpx[i_]-qx[i][i_];
r=MathMax(r,VNorm(tmpx,nx,normtype));
}
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- check
if(tmpb[i])
{
for(i_=0;i_<=nx-1;i_++)
tmpx[i_]=ptx[i_];
for(i_=0;i_<=nx-1;i_++)
tmpx[i_]=tmpx[i_]-xy[i][i_];
//--- search errors
kdterrors=kdterrors || VNorm(tmpx,nx,normtype)<r*(1-errtol)/(1+eps);
}
}
for(i=0;i<=k-2;i++)
{
//--- search errors
kdterrors=kdterrors || qr[i]>qr[i+1];
}
//--- calculation
for(i=0;i<=k-1;i++)
{
for(i_=0;i_<=nx-1;i_++)
tmpx[i_]=ptx[i_];
for(i_=0;i_<=nx-1;i_++)
tmpx[i_]=tmpx[i_]-xy[qtags[i]][i_];
//--- search errors
kdterrors=kdterrors || MathAbs(VNorm(tmpx,nx,normtype)-qr[i])>errtol;
}
}
//--- test general R-NN queries (with self-matches):
//--- * compare results from different trees (must be equal) and
//--- check that correct (value,tag) pairs are returned
//--- * test results from XT tree - let R be radius of query result.
//--- then all points not in result must be not closer than R.
for(q=1;q<=qcount;q++)
{
//--- Select R
if(CMath::RandomReal()>0.3)
r=MathMax(CMath::RandomReal(),CMath::m_machineepsilon);
else
r=CMath::m_machineepsilon;
//--- Select point (either one of the points,or random)
if(CMath::RandomReal()>0.5)
{
i=CMath::RandomInteger(n);
for(i_=0;i_<=nx-1;i_++)
ptx[i_]=xy[i][i_];
}
else
{
for(i=0;i<=nx-1;i++)
ptx[i]=2*CMath::RandomReal()-1;
}
//--- Test:
//--- * consistency of results from different queries
//--- * points in query are IN the R-sphere (or at the boundary),
//--- and points not in query are outside of the R-sphere (or at the boundary)
//--- * distances are correct and are ordered
kx=CNearestNeighbor::KDTreeQueryRNN(treex,ptx,r,true);
kxy=CNearestNeighbor::KDTreeQueryRNN(treexy,ptx,r,true);
kt=CNearestNeighbor::KDTreeQueryRNN(treext,ptx,r,true);
//--- check
if(kxy!=kx || kt!=kx)
{
kdterrors=true;
return;
}
//--- function calls
CNearestNeighbor::KDTreeQueryResultsXI(treex,qx);
CNearestNeighbor::KDTreeQueryResultsXYI(treexy,qxy);
CNearestNeighbor::KDTreeQueryResultsTagsI(treext,qtags);
CNearestNeighbor::KDTreeQueryResultsDistancesI(treext,qr);
//--- search errors
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,kx,nx,ny);
//--- function calls
CNearestNeighbor::KDTreeQueryResultsX(treex,qx);
CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy);
CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags);
CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr);
//--- search errors
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,kx,nx,ny);
//--- change values
for(i=0;i<=n-1;i++)
tmpb[i]=true;
for(i=0;i<=kx-1;i++)
tmpb[qtags[i]]=false;
//--- calculation
for(i=0;i<=n-1;i++)
{
for(i_=0;i_<=nx-1;i_++)
tmpx[i_]=ptx[i_];
for(i_=0;i_<=nx-1;i_++)
tmpx[i_]=tmpx[i_]-xy[i][i_];
//--- check
if(tmpb[i])
{
//--- search errors
kdterrors=kdterrors || VNorm(tmpx,nx,normtype)<r*(1-errtol);
}
else
{
//--- search errors
kdterrors=kdterrors || VNorm(tmpx,nx,normtype)>r*(1+errtol);
}
}
for(i=0;i<=kx-2;i++)
{
//--- search errors
kdterrors=kdterrors || qr[i]>qr[i+1];
}
}
//--- Test self-matching:
//--- * self-match - nearest neighbor of each point in XY is the point itself
//--- * no self-match - nearest neighbor is NOT the point itself
if(n>1)
{
//--- test for N=1 have non-general form,but it is not really needed
for(task=0;task<=1;task++)
{
for(i=0;i<=n-1;i++)
{
for(i_=0;i_<=nx-1;i_++)
ptx[i_]=xy[i][i_];
//--- function calls
kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,1,task==0);
CNearestNeighbor::KDTreeQueryResultsXI(treex,qx);
//--- check
if(kx!=1)
{
kdterrors=true;
return;
}
//--- change value
isequal=true;
for(j=0;j<=nx-1;j++)
isequal=isequal && qx[0][j]==ptx[j];
//--- check
if(task==0)
kdterrors=kdterrors || !isequal;
else
kdterrors=kdterrors || isequal;
}
}
}
}
//+------------------------------------------------------------------+
//| Testing serialization of KD trees |
//| This function sets Err to True on errors, but leaves it unchanged|
//| on success |
//+------------------------------------------------------------------+
static void CTestNearestNeighborUnit::TestKDTreeSerialization(bool &err)
{
//--- create variables
int n=0;
int nx=0;
int ny=0;
int normtype=0;
int i=0;
int j=0;
int k=0;
int q=0;
double threshold=0;
int k0=0;
int k1=0;
//--- objects of classes
CKDTree tree0;
CKDTree tree1;
//--- create matrix
CMatrixDouble xy;
CMatrixDouble xy0;
CMatrixDouble xy1;
//--- create arrays
double x[];
int tags[];
int qsizes[];
int tags0[];
int tags1[];
int i_=0;
//--- initialization
threshold=100*CMath::m_machineepsilon;
//--- different N,NX,NY,NormType
n=1;
while(n<=51)
{
//--- prepare array with query sizes
ArrayResize(qsizes,4);
qsizes[0]=1;
qsizes[1]=(int)(MathMin(2,n));
qsizes[2]=(int)(MathMin(4,n));
qsizes[3]=n;
//--- different NX/NY/NormType
for(nx=1;nx<=2;nx++)
{
for(ny=0;ny<=2;ny++)
{
for(normtype=0;normtype<=2;normtype++)
{
//--- Prepare data
xy.Resize(n,nx+ny);
ArrayResize(tags,n);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=nx+ny-1;j++)
xy[i].Set(j,CMath::RandomReal());
tags[i]=CMath::RandomInteger(100);
}
//--- Build tree,pass it through serializer
CNearestNeighbor::KDTreeBuildTagged(xy,tags,n,nx,ny,normtype,tree0);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
//--- serialization
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CNearestNeighbor::KDTreeAlloc(_local_serializer,tree0);
_local_serializer.SStart_Str();
CNearestNeighbor::KDTreeSerialize(_local_serializer,tree0);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//--- unserialization
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CNearestNeighbor::KDTreeUnserialize(_local_serializer,tree1);
_local_serializer.Stop();
}
//--- For each point of XY we make queries with different sizes
ArrayResize(x,nx);
for(k=0;k<=n-1;k++)
{
for(q=0;q<=CAp::Len(qsizes)-1;q++)
{
for(i_=0;i_<=nx-1;i_++)
x[i_]=xy[k][i_];
//--- change values
k0=CNearestNeighbor::KDTreeQueryKNN(tree0,x,qsizes[q],true);
k1=CNearestNeighbor::KDTreeQueryKNN(tree1,x,qsizes[q],true);
//--- check
if(k0!=k1)
{
err=true;
return;
}
//--- function call
CNearestNeighbor::KDTreeQueryResultsXY(tree0,xy0);
//--- function call
CNearestNeighbor::KDTreeQueryResultsXY(tree1,xy1);
for(i=0;i<=k0-1;i++)
{
for(j=0;j<=nx+ny-1;j++)
{
//--- check
if(MathAbs(xy0[i][j]-xy1[i][j])>threshold)
{
err=true;
return;
}
}
}
//--- function call
CNearestNeighbor::KDTreeQueryResultsTags(tree0,tags0);
//--- function call
CNearestNeighbor::KDTreeQueryResultsTags(tree1,tags1);
for(i=0;i<=k0-1;i++)
{
//--- check
if(tags0[i]!=tags1[i])
{
err=true;
return;
}
}
}
}
}
}
}
//--- Next N
n=n+25;
}
}
//+------------------------------------------------------------------+
//| Testing class CAblas |
//+------------------------------------------------------------------+
class CTestAblasUnit
{
public:
CTestAblasUnit(void);
~CTestAblasUnit(void);
static bool TestAblas(const bool silent);
private:
static void NaiveMatrixMatrixMultiply(CMatrixDouble &a,const int ai1,const int ai2,const int aj1,const int aj2,const bool transa,CMatrixDouble &b,const int bi1,const int bi2,const int bj1,const int bj2,const bool transb,const double alpha,CMatrixDouble &c,const int ci1,const int ci2,const int cj1,const int cj2,const double beta);
static bool TestTrsM(const int minn,const int maxn);
static bool TestSyrk(const int minn,const int maxn);
static bool TestGemm(const int minn,const int maxn);
static bool TestTrans(const int minn,const int maxn);
static bool TestRank1(const int minn,const int maxn);
static bool TestMV(const int minn,const int maxn);
static bool TestCopy(const int minn,const int maxn);
static void RefCMatrixRightTrsM(const int m,const int n,CMatrixComplex &a,const int i1,const int j1,const bool isupper,const bool isunit,const int optype,CMatrixComplex &x,const int i2,const int j2);
static void RefCMatrixLeftTrsM(const int m,const int n,CMatrixComplex &a,const int i1,const int j1,const bool isupper,const bool isunit,const int optype,CMatrixComplex &x,const int i2,const int j2);
static void RefRMatrixRightTrsM(const int m,const int n,CMatrixDouble &a,const int i1,const int j1,const bool isupper,const bool isunit,const int optype,CMatrixDouble &x,const int i2,const int j2);
static void RefRMatrixLeftTrsM(const int m,const int n,CMatrixDouble &a,const int i1,const int j1,const bool isupper,const bool isunit,const int optype,CMatrixDouble &x,const int i2,const int j2);
static bool InternalCMatrixTrInverse(CMatrixComplex &a,const int n,const bool isupper,const bool isunittriangular);
static bool InternalRMatrixTrInverse(CMatrixDouble &a,const int n,const bool isupper,const bool isunittriangular);
static void RefCMatrixSyrk(const int n,const int k,const double alpha,CMatrixComplex &a,const int ia,const int ja,const int optypea,const double beta,CMatrixComplex &c,const int ic,const int jc,const bool isupper);
static void RefRMatrixSyrk(const int n,const int k,const double alpha,CMatrixDouble &a,const int ia,const int ja,const int optypea,const double beta,CMatrixDouble &c,const int ic,const int jc,const bool isupper);
static void RefCMatrixGemm(const int m,const int n,const int k,complex &alpha,CMatrixComplex &a,const int ia,const int ja,const int optypea,CMatrixComplex &b,const int ib,const int jb,const int optypeb,complex &beta,CMatrixComplex &c,const int ic,const int jc);
static void RefRMatrixGemm(const int m,const int n,const int k,const double alpha,CMatrixDouble &a,const int ia,const int ja,const int optypea,CMatrixDouble &b,const int ib,const int jb,const int optypeb,const double beta,CMatrixDouble &c,const int ic,const int jc);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestAblasUnit::CTestAblasUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestAblasUnit::~CTestAblasUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CAblas |
//+------------------------------------------------------------------+
static bool CTestAblasUnit::TestAblas(const bool silent)
{
//--- create variables
double threshold=0;
bool trsmerrors;
bool syrkerrors;
bool gemmerrors;
bool transerrors;
bool rank1errors;
bool mverrors;
bool copyerrors;
bool waserrors;
//--- create array
CMatrixDouble ra;
//--- initialization
trsmerrors=false;
syrkerrors=false;
gemmerrors=false;
transerrors=false;
rank1errors=false;
mverrors=false;
copyerrors=false;
waserrors=false;
threshold=10000*CMath::m_machineepsilon;
//--- search errors
trsmerrors=trsmerrors || TestTrsM(1,3*CAblas::AblasBlockSize()+1);
syrkerrors=syrkerrors || TestSyrk(1,3*CAblas::AblasBlockSize()+1);
gemmerrors=gemmerrors || TestGemm(1,3*CAblas::AblasBlockSize()+1);
transerrors=transerrors || TestTrans(1,3*CAblas::AblasBlockSize()+1);
rank1errors=rank1errors || TestRank1(1,3*CAblas::AblasBlockSize()+1);
mverrors=mverrors || TestMV(1,3*CAblas::AblasBlockSize()+1);
copyerrors=copyerrors || TestCopy(1,3*CAblas::AblasBlockSize()+1);
//--- report
waserrors=(((((trsmerrors || syrkerrors) || gemmerrors) || transerrors) || rank1errors) || mverrors) || copyerrors;
//--- check
if(!silent)
{
Print("TESTING ABLAS");
Print("* TRSM: ");
//--- check
if(trsmerrors)
Print("FAILED");
else
Print("OK");
Print("* SYRK: ");
//--- check
if(syrkerrors)
Print("FAILED");
else
Print("OK");
Print("* GEMM: ");
//--- check
if(gemmerrors)
Print("FAILED");
else
Print("OK");
Print("* TRANS: ");
//--- check
if(transerrors)
Print("FAILED");
else
Print("OK");
Print("* RANK1: ");
//--- check
if(rank1errors)
Print("FAILED");
else
Print("OK");
Print("* MV: ");
//--- check
if(mverrors)
Print("FAILED");
else
Print("OK");
Print("* COPY: ");
//--- check
if(copyerrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestAblasUnit::NaiveMatrixMatrixMultiply(CMatrixDouble &a,
const int ai1,
const int ai2,
const int aj1,
const int aj2,
const bool transa,
CMatrixDouble &b,
const int bi1,
const int bi2,
const int bj1,
const int bj2,
const bool transb,
const double alpha,
CMatrixDouble &c,
const int ci1,
const int ci2,
const int cj1,
const int cj2,
const double beta)
{
//--- create variables
int arows=0;
int acols=0;
int brows=0;
int bcols=0;
int i=0;
int j=0;
int k=0;
int l=0;
int r=0;
double v=0;
int i_=0;
int i1_=0;
//--- create arrays
double x1[];
double x2[];
//--- Setup
if(!transa)
{
arows=ai2-ai1+1;
acols=aj2-aj1+1;
}
else
{
arows=aj2-aj1+1;
acols=ai2-ai1+1;
}
//--- check
if(!transb)
{
brows=bi2-bi1+1;
bcols=bj2-bj1+1;
}
else
{
brows=bj2-bj1+1;
bcols=bi2-bi1+1;
}
//--- check
if(!CAp::Assert(acols==brows,"NaiveMatrixMatrixMultiply: incorrect matrix sizes!"))
return;
//--- check
if(((arows<=0 || acols<=0) || brows<=0) || bcols<=0)
return;
//--- change values
l=arows;
r=bcols;
k=acols;
//--- allocation
ArrayResize(x1,k+1);
ArrayResize(x2,k+1);
//--- calculation
for(i=1;i<=l;i++)
{
for(j=1;j<=r;j++)
{
//--- check
if(!transa)
{
//--- check
if(!transb)
{
//--- change values
i1_=aj1-bi1;
v=0.0;
for(i_=bi1;i_<=bi2;i_++)
v+=b[i_][bj1+j-1]*a[ai1+i-1][i_+i1_];
}
else
{
//--- change values
i1_=aj1-bj1;
v=0.0;
for(i_=bj1;i_<=bj2;i_++)
v+=b[bi1+j-1][i_]*a[ai1+i-1][i_+i1_];
}
}
else
{
//--- check
if(!transb)
{
//--- change values
i1_=ai1-bi1;
v=0.0;
for(i_=bi1;i_<=bi2;i_++)
v+=b[i_][bj1+j-1]*a[i_+i1_][aj1+i-1];
}
else
{
//--- change values
i1_=ai1-bj1;
v=0.0;
for(i_=bj1;i_<=bj2;i_++)
v+=b[bi1+j-1][i_]*a[i_+i1_][aj1+i-1];
}
}
//--- check
if(beta==0.0)
c[ci1+i-1].Set(cj1+j-1,alpha*v);
else
c[ci1+i-1].Set(cj1+j-1,beta*c[ci1+i-1][cj1+j-1]+alpha*v);
}
}
}
//+------------------------------------------------------------------+
//| ?Matrix????TRSM tests |
//| Returns False for passed test,True - for failed |
//+------------------------------------------------------------------+
static bool CTestAblasUnit::TestTrsM(const int minn,const int maxn)
{
//--- create variables
bool result;
int n=0;
int m=0;
int mx=0;
int i=0;
int j=0;
int optype=0;
int uppertype=0;
int unittype=0;
int xoffsi=0;
int xoffsj=0;
int aoffsitype=0;
int aoffsjtype=0;
int aoffsi=0;
int aoffsj=0;
double threshold=0;
//--- create matrix
CMatrixDouble refra;
CMatrixDouble refrxl;
CMatrixDouble refrxr;
CMatrixComplex refca;
CMatrixComplex refcxl;
CMatrixComplex refcxr;
CMatrixDouble ra;
CMatrixComplex ca;
CMatrixDouble rxr1;
CMatrixDouble rxl1;
CMatrixComplex cxr1;
CMatrixComplex cxl1;
CMatrixDouble rxr2;
CMatrixDouble rxl2;
CMatrixComplex cxr2;
CMatrixComplex cxl2;
//--- initialization
threshold=CMath::Sqr(maxn)*100*CMath::m_machineepsilon;
result=false;
//--- calculation
for(mx=minn;mx<=maxn;mx++)
{
//--- Select random M/N in [1,MX] such that max(M,N)=MX
m=1+CMath::RandomInteger(mx);
n=1+CMath::RandomInteger(mx);
//--- check
if(CMath::RandomReal()>0.5)
m=mx;
else
n=mx;
//--- Initialize RefRA/RefCA by random matrices whose upper
//--- and lower triangle submatrices are non-degenerate
//--- well-conditioned matrices.
//--- Matrix size is 2Mx2M (four copies of same MxM matrix
//--- to test different offsets)
refra.Resize(2*m,2*m);
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
refra[i].Set(j,0.2*CMath::RandomReal()-0.1);
}
for(i=0;i<=m-1;i++)
refra[i].Set(i,(2*CMath::RandomInteger(1)-1)*(2*m+CMath::RandomReal()));
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
{
refra[i+m].Set(j,refra[i][j]);
refra[i].Set(j+m,refra[i][j]);
refra[i+m].Set(j+m,refra[i][j]);
}
}
//--- allocation
refca.Resize(2*m,2*m);
//--- change values
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
{
refca[i].SetRe(j,0.2*CMath::RandomReal()-0.1);
refca[i].SetIm(j,0.2*CMath::RandomReal()-0.1);
}
}
//--- change values
for(i=0;i<=m-1;i++)
{
refca[i].SetRe(i,(2*CMath::RandomInteger(2)-1)*(2*m+CMath::RandomReal()));
refca[i].SetIm(i,(2*CMath::RandomInteger(2)-1)*(2*m+CMath::RandomReal()));
}
//--- change values
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
{
refca[i+m].Set(j,refca[i][j]);
refca[i].Set(j+m,refca[i][j]);
refca[i+m].Set(j+m,refca[i][j]);
}
}
//--- Generate random XL/XR.
//--- XR is NxM matrix (matrix for 'Right' subroutines)
//--- XL is MxN matrix (matrix for 'Left' subroutines)
refrxr.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
refrxr[i].Set(j,2*CMath::RandomReal()-1);
}
//--- allocation
refrxl.Resize(m,n);
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
refrxl[i].Set(j,2*CMath::RandomReal()-1);
}
//--- allocation
refcxr.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
{
refcxr[i].SetRe(j,2*CMath::RandomReal()-1);
refcxr[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- allocation
refcxl.Resize(m,n);
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
refcxl[i].SetRe(j,2*CMath::RandomReal()-1);
refcxl[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- test different types of operations,offsets,and so on...
//--- to avoid unnecessary slowdown we don't test ALL possible
//--- combinations of operation types. We just generate one random
//--- set of parameters and test it.
ra.Resize(2*m,2*m);
rxr1.Resize(n,m);
rxr2.Resize(n,m);
rxl1.Resize(m,n);
rxl2.Resize(m,n);
ca.Resize(2*m,2*m);
cxr1.Resize(n,m);
cxr2.Resize(n,m);
cxl1.Resize(m,n);
cxl2.Resize(m,n);
//--- initialization
optype=CMath::RandomInteger(3);
uppertype=CMath::RandomInteger(2);
unittype=CMath::RandomInteger(2);
xoffsi=CMath::RandomInteger(2);
xoffsj=CMath::RandomInteger(2);
aoffsitype=CMath::RandomInteger(2);
aoffsjtype=CMath::RandomInteger(2);
aoffsi=m*aoffsitype;
aoffsj=m*aoffsjtype;
//--- copy A,XR,XL (fill unused parts with random garbage)
for(i=0;i<=2*m-1;i++)
{
for(j=0;j<=2*m-1;j++)
{
//--- check
if(((i>=aoffsi && i<aoffsi+m) && j>=aoffsj) && j<aoffsj+m)
{
ca[i].Set(j,refca[i][j]);
ra[i].Set(j,refra[i][j]);
}
else
{
ca[i].Set(j,CMath::RandomReal());
ra[i].Set(j,CMath::RandomReal());
}
}
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- check
if(i>=xoffsi && j>=xoffsj)
{
cxr1[i].Set(j,refcxr[i][j]);
cxr2[i].Set(j,refcxr[i][j]);
rxr1[i].Set(j,refrxr[i][j]);
rxr2[i].Set(j,refrxr[i][j]);
}
else
{
cxr1[i].Set(j,CMath::RandomReal());
cxr2[i].Set(j,cxr1[i][j]);
rxr1[i].Set(j,CMath::RandomReal());
rxr2[i].Set(j,rxr1[i][j]);
}
}
}
//--- change values
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(i>=xoffsi && j>=xoffsj)
{
cxl1[i].Set(j,refcxl[i][j]);
cxl2[i].Set(j,refcxl[i][j]);
rxl1[i].Set(j,refrxl[i][j]);
rxl2[i].Set(j,refrxl[i][j]);
}
else
{
cxl1[i].Set(j,CMath::RandomReal());
cxl2[i].Set(j,cxl1[i][j]);
rxl1[i].Set(j,CMath::RandomReal());
rxl2[i].Set(j,rxl1[i][j]);
}
}
}
//--- Test CXR
CAblas::CMatrixRightTrsM(n-xoffsi,m-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxr1,xoffsi,xoffsj);
RefCMatrixRightTrsM(n-xoffsi,m-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxr2,xoffsi,xoffsj);
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
result=result || CMath::AbsComplex(cxr1[i][j]-cxr2[i][j])>threshold;
}
//--- Test CXL
CAblas::CMatrixLeftTrsM(m-xoffsi,n-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxl1,xoffsi,xoffsj);
RefCMatrixLeftTrsM(m-xoffsi,n-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxl2,xoffsi,xoffsj);
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
result=result || CMath::AbsComplex(cxl1[i][j]-cxl2[i][j])>threshold;
}
//--- check
if(optype<2)
{
//--- Test RXR
CAblas::RMatrixRightTrsM(n-xoffsi,m-xoffsj,ra,aoffsi,aoffsj,uppertype==0,unittype==0,optype,rxr1,xoffsi,xoffsj);
RefRMatrixRightTrsM(n-xoffsi,m-xoffsj,ra,aoffsi,aoffsj,uppertype==0,unittype==0,optype,rxr2,xoffsi,xoffsj);
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
result=result || MathAbs(rxr1[i][j]-rxr2[i][j])>threshold;
}
//--- Test RXL
CAblas::RMatrixLeftTrsM(m-xoffsi,n-xoffsj,ra,aoffsi,aoffsj,uppertype==0,unittype==0,optype,rxl1,xoffsi,xoffsj);
RefRMatrixLeftTrsM(m-xoffsi,n-xoffsj,ra,aoffsi,aoffsj,uppertype==0,unittype==0,optype,rxl2,xoffsi,xoffsj);
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
result=result || MathAbs(rxl1[i][j]-rxl2[i][j])>threshold;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| SYRK tests |
//| Returns False for passed test,True - for failed |
//+------------------------------------------------------------------+
static bool CTestAblasUnit::TestSyrk(const int minn,const int maxn)
{
//--- create variables
bool result;
int n=0;
int k=0;
int mx=0;
int i=0;
int j=0;
int uppertype=0;
int xoffsi=0;
int xoffsj=0;
int aoffsitype=0;
int aoffsjtype=0;
int aoffsi=0;
int aoffsj=0;
int alphatype=0;
int betatype=0;
double alpha=0;
double beta=0;
double threshold=0;
//--- create matrix
CMatrixDouble refra;
CMatrixDouble refrc;
CMatrixComplex refca;
CMatrixComplex refcc;
CMatrixDouble ra1;
CMatrixDouble ra2;
CMatrixComplex ca1;
CMatrixComplex ca2;
CMatrixDouble rc;
CMatrixDouble rct;
CMatrixComplex cc;
CMatrixComplex cct;
//--- initialization
threshold=maxn*100*CMath::m_machineepsilon;
result=false;
for(mx=minn;mx<=maxn;mx++)
{
//--- Select random M/N in [1,MX] such that max(M,N)=MX
k=1+CMath::RandomInteger(mx);
n=1+CMath::RandomInteger(mx);
//--- check
if(CMath::RandomReal()>0.5)
k=mx;
else
n=mx;
//--- Initialize RefRA/RefCA by random Hermitian matrices,
//--- RefRC/RefCC by random matrices
//--- RA/CA size is 2Nx2N (four copies of same NxN matrix
//--- to test different offsets)
refra.Resize(2*n,2*n);
refca.Resize(2*n,2*n);
for(i=0;i<=n-1;i++)
{
refra[i].Set(i,2*CMath::RandomReal()-1);
refca[i].Set(i,2*CMath::RandomReal()-1);
for(j=i+1;j<=n-1;j++)
{
refra[i].Set(j,2*CMath::RandomReal()-1);
refca[i].SetRe(j,2*CMath::RandomReal()-1);
refca[i].SetIm(j,2*CMath::RandomReal()-1);
refra[j].Set(i,refra[i][j]);
refca[j].Set(i,CMath::Conj(refca[i][j]));
}
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
refra[i+n].Set(j,refra[i][j]);
refra[i].Set(j+n,refra[i][j]);
refra[i+n].Set(j+n,refra[i][j]);
refca[i+n].Set(j,refca[i][j]);
refca[i].Set(j+n,refca[i][j]);
refca[i+n].Set(j+n,refca[i][j]);
}
}
//--- allocation
refrc.Resize(n,k);
refcc.Resize(n,k);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=k-1;j++)
{
refrc[i].Set(j,2*CMath::RandomReal()-1);
refcc[i].SetRe(j,2*CMath::RandomReal()-1);
refcc[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- test different types of operations,offsets,and so on...
//--- to avoid unnecessary slowdown we don't test ALL possible
//--- combinations of operation types. We just generate one random
//--- set of parameters and test it.
ra1.Resize(2*n,2*n);
ra2.Resize(2*n,2*n);
ca1.Resize(2*n,2*n);
ca2.Resize(2*n,2*n);
rc.Resize(n,k);
rct.Resize(k,n);
cc.Resize(n,k);
cct.Resize(k,n);
//--- initialization
uppertype=CMath::RandomInteger(2);
xoffsi=CMath::RandomInteger(2);
xoffsj=CMath::RandomInteger(2);
aoffsitype=CMath::RandomInteger(2);
aoffsjtype=CMath::RandomInteger(2);
alphatype=CMath::RandomInteger(2);
betatype=CMath::RandomInteger(2);
aoffsi=n*aoffsitype;
aoffsj=n*aoffsjtype;
alpha=alphatype*(2*CMath::RandomReal()-1);
beta=betatype*(2*CMath::RandomReal()-1);
//--- copy A,C (fill unused parts with random garbage)
for(i=0;i<=2*n-1;i++)
{
for(j=0;j<=2*n-1;j++)
{
//--- check
if(((i>=aoffsi && i<aoffsi+n) && j>=aoffsj) && j<aoffsj+n)
{
ca1[i].Set(j,refca[i][j]);
ca2[i].Set(j,refca[i][j]);
ra1[i].Set(j,refra[i][j]);
ra2[i].Set(j,refra[i][j]);
}
else
{
ca1[i].Set(j,CMath::RandomReal());
ca2[i].Set(j,ca1[i][j]);
ra1[i].Set(j,CMath::RandomReal());
ra2[i].Set(j,ra1[i][j]);
}
}
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=k-1;j++)
{
//--- check
if(i>=xoffsi && j>=xoffsj)
{
rc[i].Set(j,refrc[i][j]);
rct[j].Set(i,refrc[i][j]);
cc[i].Set(j,refcc[i][j]);
cct[j].Set(i,refcc[i][j]);
}
else
{
rc[i].Set(j,CMath::RandomReal());
rct[j].Set(i,rc[i][j]);
cc[i].Set(j,CMath::RandomReal());
cct[j].Set(i,cct[j][i]);
}
}
}
//--- Test complex
//--- Only one of transform types is selected and tested
if(CMath::RandomReal()>0.5)
{
CAblas::CMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cc,xoffsi,xoffsj,0,beta,ca1,aoffsi,aoffsj,uppertype==0);
RefCMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cc,xoffsi,xoffsj,0,beta,ca2,aoffsi,aoffsj,uppertype==0);
}
else
{
CAblas::CMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cct,xoffsj,xoffsi,2,beta,ca1,aoffsi,aoffsj,uppertype==0);
RefCMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cct,xoffsj,xoffsi,2,beta,ca2,aoffsi,aoffsj,uppertype==0);
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
result=result || CMath::AbsComplex(ca1[i][j]-ca2[i][j])>threshold;
}
//--- Test real
//--- Only one of transform types is selected and tested
if(CMath::RandomReal()>0.5)
{
CAblas::RMatrixSyrk(n-xoffsi,k-xoffsj,alpha,rc,xoffsi,xoffsj,0,beta,ra1,aoffsi,aoffsj,uppertype==0);
RefRMatrixSyrk(n-xoffsi,k-xoffsj,alpha,rc,xoffsi,xoffsj,0,beta,ra2,aoffsi,aoffsj,uppertype==0);
}
else
{
CAblas::RMatrixSyrk(n-xoffsi,k-xoffsj,alpha,rct,xoffsj,xoffsi,1,beta,ra1,aoffsi,aoffsj,uppertype==0);
RefRMatrixSyrk(n-xoffsi,k-xoffsj,alpha,rct,xoffsj,xoffsi,1,beta,ra2,aoffsi,aoffsj,uppertype==0);
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
result=result || MathAbs(ra1[i][j]-ra2[i][j])>threshold;
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| GEMM tests |
//| Returns False for passed test,True - for failed |
//+------------------------------------------------------------------+
static bool CTestAblasUnit::TestGemm(const int minn,const int maxn)
{
//--- create variables
bool result;
int m=0;
int n=0;
int k=0;
int mx=0;
int i=0;
int j=0;
int aoffsi=0;
int aoffsj=0;
int aoptype=0;
int aoptyper=0;
int boffsi=0;
int boffsj=0;
int boptype=0;
int boptyper=0;
int coffsi=0;
int coffsj=0;
double alphar=0;
double betar=0;
complex alphac=0;
complex betac=0;
double threshold=0;
//--- create matrix
CMatrixDouble refra;
CMatrixDouble refrb;
CMatrixDouble refrc;
CMatrixComplex refca;
CMatrixComplex refcb;
CMatrixComplex refcc;
CMatrixDouble rc1;
CMatrixDouble rc2;
CMatrixComplex cc1;
CMatrixComplex cc2;
//--- initialization
threshold=maxn*100*CMath::m_machineepsilon;
result=false;
//--- calculation
for(mx=minn;mx<=maxn;mx++)
{
//--- Select random M/N/K in [1,MX] such that max(M,N,K)=MX
m=1+CMath::RandomInteger(mx);
n=1+CMath::RandomInteger(mx);
k=1+CMath::RandomInteger(mx);
i=CMath::RandomInteger(3);
//--- check
if(i==0)
m=mx;
//--- check
if(i==1)
n=mx;
//--- check
if(i==2)
k=mx;
//--- Initialize A/B/C by random matrices with size (MaxN+1)*(MaxN+1)
refra.Resize(maxn+1,maxn+1);
refrb.Resize(maxn+1,maxn+1);
refrc.Resize(maxn+1,maxn+1);
refca.Resize(maxn+1,maxn+1);
refcb.Resize(maxn+1,maxn+1);
refcc.Resize(maxn+1,maxn+1);
//--- change values
for(i=0;i<=maxn;i++)
{
for(j=0;j<=maxn;j++)
{
refra[i].Set(j,2*CMath::RandomReal()-1);
refrb[i].Set(j,2*CMath::RandomReal()-1);
refrc[i].Set(j,2*CMath::RandomReal()-1);
refca[i].SetRe(j,2*CMath::RandomReal()-1);
refca[i].SetIm(j,2*CMath::RandomReal()-1);
refcb[i].SetRe(j,2*CMath::RandomReal()-1);
refcb[i].SetIm(j,2*CMath::RandomReal()-1);
refcc[i].SetRe(j,2*CMath::RandomReal()-1);
refcc[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- test different types of operations,offsets,and so on...
//--- to avoid unnecessary slowdown we don't test ALL possible
//--- combinations of operation types. We just generate one random
//--- set of parameters and test it.
rc1.Resize(maxn+1,maxn+1);
rc2.Resize(maxn+1,maxn+1);
cc1.Resize(maxn+1,maxn+1);
cc2.Resize(maxn+1,maxn+1);
//--- initialization
aoffsi=CMath::RandomInteger(2);
aoffsj=CMath::RandomInteger(2);
aoptype=CMath::RandomInteger(3);
aoptyper=CMath::RandomInteger(2);
boffsi=CMath::RandomInteger(2);
boffsj=CMath::RandomInteger(2);
boptype=CMath::RandomInteger(3);
boptyper=CMath::RandomInteger(2);
coffsi=CMath::RandomInteger(2);
coffsj=CMath::RandomInteger(2);
alphar=CMath::RandomInteger(2)*(2*CMath::RandomReal()-1);
betar=CMath::RandomInteger(2)*(2*CMath::RandomReal()-1);
//--- check
if(CMath::RandomReal()>0.5)
{
alphac.re=2*CMath::RandomReal()-1;
alphac.im=2*CMath::RandomReal()-1;
}
else
alphac=0;
//--- check
if(CMath::RandomReal()>0.5)
{
betac.re=2*CMath::RandomReal()-1;
betac.im=2*CMath::RandomReal()-1;
}
else
betac=0;
//--- copy C
for(i=0;i<=maxn;i++)
{
for(j=0;j<=maxn;j++)
{
rc1[i].Set(j,refrc[i][j]);
rc2[i].Set(j,refrc[i][j]);
cc1[i].Set(j,refcc[i][j]);
cc2[i].Set(j,refcc[i][j]);
}
}
//--- Test complex
CAblas::CMatrixGemm(m,n,k,alphac,refca,aoffsi,aoffsj,aoptype,refcb,boffsi,boffsj,boptype,betac,cc1,coffsi,coffsj);
RefCMatrixGemm(m,n,k,alphac,refca,aoffsi,aoffsj,aoptype,refcb,boffsi,boffsj,boptype,betac,cc2,coffsi,coffsj);
//--- search errors
for(i=0;i<=maxn;i++)
{
for(j=0;j<=maxn;j++)
result=result || CMath::AbsComplex(cc1[i][j]-cc2[i][j])>threshold;
}
//--- Test real
CAblas::RMatrixGemm(m,n,k,alphar,refra,aoffsi,aoffsj,aoptyper,refrb,boffsi,boffsj,boptyper,betar,rc1,coffsi,coffsj);
RefRMatrixGemm(m,n,k,alphar,refra,aoffsi,aoffsj,aoptyper,refrb,boffsi,boffsj,boptyper,betar,rc2,coffsi,coffsj);
//--- search errors
for(i=0;i<=maxn;i++)
{
for(j=0;j<=maxn;j++)
result=result || MathAbs(rc1[i][j]-rc2[i][j])>threshold;
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| transpose tests |
//| Returns False for passed test,True - for failed |
//+------------------------------------------------------------------+
static bool CTestAblasUnit::TestTrans(const int minn,const int maxn)
{
//--- create variables
bool result;
int m=0;
int n=0;
int mx=0;
int i=0;
int j=0;
int aoffsi=0;
int aoffsj=0;
int boffsi=0;
int boffsj=0;
double v1=0;
double v2=0;
double threshold=0;
//--- create matrix
CMatrixDouble refra;
CMatrixDouble refrb;
CMatrixComplex refca;
CMatrixComplex refcb;
//--- initialization
result=false;
threshold=1000*CMath::m_machineepsilon;
//--- calculation
for(mx=minn;mx<=maxn;mx++)
{
//--- Select random M/N in [1,MX] such that max(M,N)=MX
//--- Generate random V1 and V2 which are used to fill
//--- RefRB/RefCB with control values.
m=1+CMath::RandomInteger(mx);
n=1+CMath::RandomInteger(mx);
//--- check
if(CMath::RandomInteger(2)==0)
m=mx;
else
n=mx;
//--- change values
v1=CMath::RandomReal();
v2=CMath::RandomReal();
//--- Initialize A by random matrix with size (MaxN+1)*(MaxN+1)
//--- Fill B with control values
refra.Resize(maxn+1,maxn+1);
refrb.Resize(maxn+1,maxn+1);
refca.Resize(maxn+1,maxn+1);
refcb.Resize(maxn+1,maxn+1);
//--- change values
for(i=0;i<=maxn;i++)
{
for(j=0;j<=maxn;j++)
{
refra[i].Set(j,2*CMath::RandomReal()-1);
refca[i].SetRe(j,2*CMath::RandomReal()-1);
refca[i].SetIm(j,2*CMath::RandomReal()-1);
refrb[i].Set(j,i*v1+j*v2);
refcb[i].Set(j,i*v1+j*v2);
}
}
//--- test different offsets (zero or one)
//--- to avoid unnecessary slowdown we don't test ALL possible
//--- combinations of operation types. We just generate one random
//--- set of parameters and test it.
aoffsi=CMath::RandomInteger(2);
aoffsj=CMath::RandomInteger(2);
boffsi=CMath::RandomInteger(2);
boffsj=CMath::RandomInteger(2);
//--- function call
CAblas::RMatrixTranspose(m,n,refra,aoffsi,aoffsj,refrb,boffsi,boffsj);
//--- search errors
for(i=0;i<=maxn;i++)
{
for(j=0;j<=maxn;j++)
{
//--- check
if(((i<boffsi || i>=boffsi+n) || j<boffsj) || j>=boffsj+m)
result=result || MathAbs(refrb[i][j]-(v1*i+v2*j))>threshold;
else
result=result || MathAbs(refrb[i][j]-refra[aoffsi+j-boffsj][aoffsj+i-boffsi])>threshold;
}
}
//--- function call
CAblas::CMatrixTranspose(m,n,refca,aoffsi,aoffsj,refcb,boffsi,boffsj);
//--- search errors
for(i=0;i<=maxn;i++)
{
for(j=0;j<=maxn;j++)
{
//--- check
if(((i<boffsi || i>=boffsi+n) || j<boffsj) || j>=boffsj+m)
result=result || CMath::AbsComplex(refcb[i][j]-(v1*i+v2*j))>threshold;
else
result=result || CMath::AbsComplex(refcb[i][j]-refca[aoffsi+j-boffsj][aoffsj+i-boffsi])>threshold;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| rank-1tests |
//| Returns False for passed test,True - for failed |
//+------------------------------------------------------------------+
static bool CTestAblasUnit::TestRank1(const int minn,const int maxn)
{
//--- create variables
bool result;
int m=0;
int n=0;
int mx=0;
int i=0;
int j=0;
int aoffsi=0;
int aoffsj=0;
int uoffs=0;
int voffs=0;
double threshold=0;
//--- create arrays
double ru[];
double rv[];
complex cu[];
complex cv[];
//--- create matrix
CMatrixDouble refra;
CMatrixDouble refrb;
CMatrixComplex refca;
CMatrixComplex refcb;
//--- initialization
result=false;
threshold=1000*CMath::m_machineepsilon;
//--- calculation
for(mx=minn;mx<=maxn;mx++)
{
//--- Select random M/N in [1,MX] such that max(M,N)=MX
m=1+CMath::RandomInteger(mx);
n=1+CMath::RandomInteger(mx);
//--- check
if(CMath::RandomInteger(2)==0)
m=mx;
else
n=mx;
//--- Initialize A by random matrix with size (MaxN+1)*(MaxN+1)
//--- Fill B with control values
refra.Resize(maxn+maxn,maxn+maxn);
refrb.Resize(maxn+maxn,maxn+maxn);
refca.Resize(maxn+maxn,maxn+maxn);
refcb.Resize(maxn+maxn,maxn+maxn);
//--- change values
for(i=0;i<=2*maxn-1;i++)
{
for(j=0;j<=2*maxn-1;j++)
{
refra[i].Set(j,2*CMath::RandomReal()-1);
refca[i].SetRe(j,2*CMath::RandomReal()-1);
refca[i].SetIm(j,2*CMath::RandomReal()-1);
refrb[i].Set(j,refra[i][j]);
refcb[i].Set(j,refca[i][j]);
}
}
//--- allocation
ArrayResize(ru,2*m);
ArrayResize(cu,2*m);
//--- change values
for(i=0;i<=2*m-1;i++)
{
ru[i]=2*CMath::RandomReal()-1;
cu[i].re=2*CMath::RandomReal()-1;
cu[i].im=2*CMath::RandomReal()-1;
}
//--- allocation
ArrayResize(rv,2*n);
ArrayResize(cv,2*n);
//--- change values
for(i=0;i<=2*n-1;i++)
{
rv[i]=2*CMath::RandomReal()-1;
cv[i].re=2*CMath::RandomReal()-1;
cv[i].im=2*CMath::RandomReal()-1;
}
//--- test different offsets (zero or one)
//--- to avoid unnecessary slowdown we don't test ALL possible
//--- combinations of operation types. We just generate one random
//--- set of parameters and test it.
aoffsi=CMath::RandomInteger(maxn);
aoffsj=CMath::RandomInteger(maxn);
uoffs=CMath::RandomInteger(m);
voffs=CMath::RandomInteger(n);
//--- function call
CAblas::CMatrixRank1(m,n,refca,aoffsi,aoffsj,cu,uoffs,cv,voffs);
//--- search errors
for(i=0;i<=2*maxn-1;i++)
{
for(j=0;j<=2*maxn-1;j++)
{
//--- check
if(((i<aoffsi || i>=aoffsi+m) || j<aoffsj) || j>=aoffsj+n)
result=result || CMath::AbsComplex(refca[i][j]-refcb[i][j])>threshold;
else
result=result || CMath::AbsComplex(refca[i][j]-(refcb[i][j]+cu[i-aoffsi+uoffs]*cv[j-aoffsj+voffs]))>threshold;
}
}
//--- function call
CAblas::RMatrixRank1(m,n,refra,aoffsi,aoffsj,ru,uoffs,rv,voffs);
//--- search errors
for(i=0;i<=2*maxn-1;i++)
{
for(j=0;j<=2*maxn-1;j++)
{
//--- check
if(((i<aoffsi || i>=aoffsi+m) || j<aoffsj) || j>=aoffsj+n)
result=result || MathAbs(refra[i][j]-refrb[i][j])>threshold;
else
result=result || MathAbs(refra[i][j]-(refrb[i][j]+ru[i-aoffsi+uoffs]*rv[j-aoffsj+voffs]))>threshold;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| MV tests |
//| Returns False for passed test,True - for failed |
//+------------------------------------------------------------------+
static bool CTestAblasUnit::TestMV(const int minn,const int maxn)
{
//--- create variables
bool result;
int m=0;
int n=0;
int mx=0;
int i=0;
int j=0;
int aoffsi=0;
int aoffsj=0;
int xoffs=0;
int yoffs=0;
int opca=0;
int opra=0;
double threshold=0;
double rv1=0;
double rv2=0;
complex cv1=0;
complex cv2=0;
int i_=0;
int i1_=0;
//--- create arrays
double rx[];
double ry[];
complex cx[];
complex cy[];
//--- create matrix
CMatrixDouble refra;
CMatrixComplex refca;
//--- initialization
result=false;
threshold=1000*CMath::m_machineepsilon;
//--- calculation
for(mx=minn;mx<=maxn;mx++)
{
//--- Select random M/N in [1,MX] such that max(M,N)=MX
m=1+CMath::RandomInteger(mx);
n=1+CMath::RandomInteger(mx);
//--- check
if(CMath::RandomInteger(2)==0)
m=mx;
else
n=mx;
//--- Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN)
//--- Initialize X by random vector with size (MaxN+MaxN)
//--- Fill Y by control values
refra.Resize(maxn+maxn,maxn+maxn);
refca.Resize(maxn+maxn,maxn+maxn);
//--- change values
for(i=0;i<=2*maxn-1;i++)
{
for(j=0;j<=2*maxn-1;j++)
{
refra[i].Set(j,2*CMath::RandomReal()-1);
refca[i].SetRe(j,2*CMath::RandomReal()-1);
refca[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- allocation
ArrayResize(rx,2*maxn);
ArrayResize(cx,2*maxn);
ArrayResize(ry,2*maxn);
ArrayResize(cy,2*maxn);
//--- change values
for(i=0;i<=2*maxn-1;i++)
{
rx[i]=2*CMath::RandomReal()-1;
cx[i].re=2*CMath::RandomReal()-1;
cx[i].im=2*CMath::RandomReal()-1;
ry[i]=i;
cy[i]=i;
}
//--- test different offsets (zero or one)
//--- to avoid unnecessary slowdown we don't test ALL possible
//--- combinations of operation types. We just generate one random
//--- set of parameters and test it.
aoffsi=CMath::RandomInteger(maxn);
aoffsj=CMath::RandomInteger(maxn);
xoffs=CMath::RandomInteger(maxn);
yoffs=CMath::RandomInteger(maxn);
opca=CMath::RandomInteger(3);
opra=CMath::RandomInteger(2);
//--- function call
CAblas::CMatrixMVect(m,n,refca,aoffsi,aoffsj,opca,cx,xoffs,cy,yoffs);
//--- search errors
for(i=0;i<=2*maxn-1;i++)
{
//--- check
if(i<yoffs || i>=yoffs+m)
result=result || cy[i]!=i;
else
{
cv1=cy[i];
cv2=0.0;
//--- check
if(opca==0)
{
//--- change values
i1_=xoffs-aoffsj;
cv2=0.0;
for(i_=aoffsj;i_<=aoffsj+n-1;i_++)
cv2+=refca[aoffsi+i-yoffs][i_]*cx[i_+i1_];
}
//--- check
if(opca==1)
{
//--- change values
i1_=xoffs-aoffsi;
cv2=0.0;
for(i_=aoffsi;i_<=aoffsi+n-1;i_++)
cv2+=refca[i_][aoffsj+i-yoffs]*cx[i_+i1_];
}
//--- check
if(opca==2)
{
//--- change values
i1_=xoffs-aoffsi;
cv2=0.0;
for(i_=aoffsi;i_<=aoffsi+n-1;i_++)
cv2+=CMath::Conj(refca[i_][aoffsj+i-yoffs])*cx[i_+i1_];
}
result=result || CMath::AbsComplex(cv1-cv2)>threshold;
}
}
//--- function call
CAblas::RMatrixMVect(m,n,refra,aoffsi,aoffsj,opra,rx,xoffs,ry,yoffs);
//--- function call
for(i=0;i<=2*maxn-1;i++)
{
//--- check
if(i<yoffs || i>=yoffs+m)
result=result || ry[i]!=i;
else
{
rv1=ry[i];
rv2=0;
//--- check
if(opra==0)
{
//--- change values
i1_=xoffs-aoffsj;
rv2=0.0;
for(i_=aoffsj;i_<=aoffsj+n-1;i_++)
rv2+=refra[aoffsi+i-yoffs][i_]*rx[i_+i1_];
}
//--- check
if(opra==1)
{
//--- change values
i1_=xoffs-aoffsi;
rv2=0.0;
for(i_=aoffsi;i_<=aoffsi+n-1;i_++)
rv2+=refra[i_][aoffsj+i-yoffs]*rx[i_+i1_];
}
result=result || MathAbs(rv1-rv2)>threshold;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| COPY tests |
//| Returns False for passed test,True - for failed |
//+------------------------------------------------------------------+
static bool CTestAblasUnit::TestCopy(const int minn,const int maxn)
{
//--- create variables
bool result;
int m=0;
int n=0;
int mx=0;
int i=0;
int j=0;
int aoffsi=0;
int aoffsj=0;
int boffsi=0;
int boffsj=0;
double threshold=0;
//--- create matrix
CMatrixDouble ra;
CMatrixDouble rb;
CMatrixComplex ca;
CMatrixComplex cb;
//--- initialization
result=false;
threshold=1000*CMath::m_machineepsilon;
//--- calculation
for(mx=minn;mx<=maxn;mx++)
{
//--- Select random M/N in [1,MX] such that max(M,N)=MX
m=1+CMath::RandomInteger(mx);
n=1+CMath::RandomInteger(mx);
//--- check
if(CMath::RandomInteger(2)==0)
m=mx;
else
n=mx;
//--- Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN)
//--- Initialize X by random vector with size (MaxN+MaxN)
//--- Fill Y by control values
ra.Resize(maxn+maxn,maxn+maxn);
ca.Resize(maxn+maxn,maxn+maxn);
rb.Resize(maxn+maxn,maxn+maxn);
cb.Resize(maxn+maxn,maxn+maxn);
//--- change values
for(i=0;i<=2*maxn-1;i++)
{
for(j=0;j<=2*maxn-1;j++)
{
ra[i].Set(j,2*CMath::RandomReal()-1);
ca[i].SetRe(j,2*CMath::RandomReal()-1);
ca[i].SetIm(j,2*CMath::RandomReal()-1);
rb[i].Set(j,1+2*i+3*j);
cb[i].Set(j,1+2*i+3*j);
}
}
//--- test different offsets (zero or one)
//--- to avoid unnecessary slowdown we don't test ALL possible
//--- combinations of operation types. We just generate one random
//--- set of parameters and test it.
aoffsi=CMath::RandomInteger(maxn);
aoffsj=CMath::RandomInteger(maxn);
boffsi=CMath::RandomInteger(maxn);
boffsj=CMath::RandomInteger(maxn);
//--- function call
CAblas::CMatrixCopy(m,n,ca,aoffsi,aoffsj,cb,boffsi,boffsj);
//--- search errors
for(i=0;i<=2*maxn-1;i++)
{
for(j=0;j<=2*maxn-1;j++)
{
//--- check
if(((i<boffsi || i>=boffsi+m) || j<boffsj) || j>=boffsj+n)
result=result || cb[i][j]!=1+2*i+3*j;
else
result=result || CMath::AbsComplex(ca[aoffsi+i-boffsi][aoffsj+j-boffsj]-cb[i][j])>threshold;
}
}
//--- function call
CAblas::RMatrixCopy(m,n,ra,aoffsi,aoffsj,rb,boffsi,boffsj);
//--- search errors
for(i=0;i<=2*maxn-1;i++)
{
for(j=0;j<=2*maxn-1;j++)
{
//--- check
if(((i<boffsi || i>=boffsi+m) || j<boffsj) || j>=boffsj+n)
result=result || rb[i][j]!=1+2*i+3*j;
else
result=result || MathAbs(ra[aoffsi+i-boffsi][aoffsj+j-boffsj]-rb[i][j])>threshold;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
static void CTestAblasUnit::RefCMatrixRightTrsM(const int m,const int n,
CMatrixComplex &a,
const int i1,const int j1,
const bool isupper,
const bool isunit,
const int optype,
CMatrixComplex &x,
const int i2,const int j2)
{
//--- create variables
int i=0;
int j=0;
complex vc=0;
bool rupper;
int i_=0;
int i1_=0;
//--- create array
complex tx[];
//--- create matrix
CMatrixComplex a1;
CMatrixComplex a2;
//--- check
if(n*m==0)
return;
//--- allocation
a1.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a1[i].Set(j,0);
}
//--- check
if(isupper)
{
for(i=0;i<=n-1;i++)
{
for(j=i;j<=n-1;j++)
a1[i].Set(j,a[i1+i][j1+j]);
}
}
else
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=i;j++)
a1[i].Set(j,a[i1+i][j1+j]);
}
}
//--- change value
rupper=isupper;
//--- check
if(isunit)
{
for(i=0;i<=n-1;i++)
{
a1[i].Set(i,1);
}
}
//--- allocation
a2.Resize(n,n);
//--- check
if(optype==0)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a2[i].Set(j,a1[i][j]);
}
}
//--- check
if(optype==1)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a2[i].Set(j,a1[j][i]);
}
//--- change value
rupper=!rupper;
}
//--- check
if(optype==2)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a2[i].Set(j,CMath::Conj(a1[j][i]));
}
}
//--- change value
rupper=!rupper;
}
//--- function call
InternalCMatrixTrInverse(a2,n,rupper,false);
//--- allocation
ArrayResize(tx,n);
//--- calculation
for(i=0;i<=m-1;i++)
{
i1_=j2;
for(i_=0;i_<=n-1;i_++)
tx[i_]=x[i2+i][i_+i1_];
//--- change values
for(j=0;j<=n-1;j++)
{
vc=0.0;
for(i_=0;i_<=n-1;i_++)
vc+=tx[i_]*a2[i_][j];
x[i2+i].Set(j2+j,vc);
}
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
static void CTestAblasUnit::RefCMatrixLeftTrsM(const int m,const int n,
CMatrixComplex &a,
const int i1,const int j1,
const bool isupper,
const bool isunit,
const int optype,
CMatrixComplex &x,
const int i2,const int j2)
{
//--- create variables
int i=0;
int j=0;
complex vc=0;
bool rupper;
int i_=0;
int i1_=0;
//--- create array
complex tx[];
//--- create matrix
CMatrixComplex a1;
CMatrixComplex a2;
//--- check
if(n*m==0)
return;
//--- allocation
a1.Resize(m,m);
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
a1[i].Set(j,0);
}
//--- check
if(isupper)
{
for(i=0;i<=m-1;i++)
{
for(j=i;j<=m-1;j++)
a1[i].Set(j,a[i1+i][j1+j]);
}
}
else
{
for(i=0;i<=m-1;i++)
{
for(j=0;j<=i;j++)
a1[i].Set(j,a[i1+i][j1+j]);
}
}
//--- change value
rupper=isupper;
//--- check
if(isunit)
{
for(i=0;i<=m-1;i++)
a1[i].Set(i,1);
}
//--- allocation
a2.Resize(m,m);
//--- check
if(optype==0)
{
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
a2[i].Set(j,a1[i][j]);
}
}
//--- check
if(optype==1)
{
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
a2[i].Set(j,a1[j][i]);
}
//--- change value
rupper=!rupper;
}
//--- check
if(optype==2)
{
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
a2[i].Set(j,CMath::Conj(a1[j][i]));
}
//--- change value
rupper=!rupper;
}
//--- function call
InternalCMatrixTrInverse(a2,m,rupper,false);
//--- allocation
ArrayResize(tx,m);
for(j=0;j<=n-1;j++)
{
i1_=i2;
for(i_=0;i_<=m-1;i_++)
tx[i_]=x[i_+i1_][j2+j];
//--- change values
for(i=0;i<=m-1;i++)
{
vc=0.0;
for(i_=0;i_<=m-1;i_++)
vc+=a2[i][i_]*tx[i_];
x[i2+i].Set(j2+j,vc);
}
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
static void CTestAblasUnit::RefRMatrixRightTrsM(const int m,const int n,
CMatrixDouble &a,
const int i1,const int j1,
const bool isupper,
const bool isunit,
const int optype,
CMatrixDouble &x,
const int i2,const int j2)
{
//--- create variables
int i=0;
int j=0;
double vr=0;
bool rupper;
int i_=0;
int i1_=0;
//--- create array
double tx[];
//--- create matrix
CMatrixDouble a1;
CMatrixDouble a2;
//--- check
if(n*m==0)
return;
//--- allocation
a1.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a1[i].Set(j,0);
}
//--- check
if(isupper)
{
for(i=0;i<=n-1;i++)
{
for(j=i;j<=n-1;j++)
a1[i].Set(j,a[i1+i][j1+j]);
}
}
else
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=i;j++)
a1[i].Set(j,a[i1+i][j1+j]);
}
}
//--- change value
rupper=isupper;
//--- check
if(isunit)
{
for(i=0;i<=n-1;i++)
a1[i].Set(i,1);
}
//--- allocation
a2.Resize(n,n);
//--- check
if(optype==0)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a2[i].Set(j,a1[i][j]);
}
}
//--- check
if(optype==1)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a2[i].Set(j,a1[j][i]);
}
//--- change value
rupper=!rupper;
}
//--- function call
InternalRMatrixTrInverse(a2,n,rupper,false);
//--- allocation
ArrayResize(tx,n);
for(i=0;i<=m-1;i++)
{
i1_=j2;
for(i_=0;i_<=n-1;i_++)
tx[i_]=x[i2+i][i_+i1_];
//--- change values
for(j=0;j<=n-1;j++)
{
vr=0.0;
for(i_=0;i_<=n-1;i_++)
vr+=tx[i_]*a2[i_][j];
x[i2+i].Set(j2+j,vr);
}
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
static void CTestAblasUnit::RefRMatrixLeftTrsM(const int m,const int n,
CMatrixDouble &a,
const int i1,const int j1,
const bool isupper,
const bool isunit,
const int optype,
CMatrixDouble &x,
const int i2,const int j2)
{
//--- create variables
int i=0;
int j=0;
double vr=0;
bool rupper;
int i_=0;
int i1_=0;
//--- create array
double tx[];
//--- create matrix
CMatrixDouble a1;
CMatrixDouble a2;
//--- check
if(n*m==0)
return;
//--- allocation
a1.Resize(m,m);
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
a1[i].Set(j,0);
}
//--- check
if(isupper)
{
for(i=0;i<=m-1;i++)
{
for(j=i;j<=m-1;j++)
a1[i].Set(j,a[i1+i][j1+j]);
}
}
else
{
for(i=0;i<=m-1;i++)
{
for(j=0;j<=i;j++)
a1[i].Set(j,a[i1+i][j1+j]);
}
}
//--- change value
rupper=isupper;
//--- check
if(isunit)
{
for(i=0;i<=m-1;i++)
a1[i].Set(i,1);
}
//--- allocation
a2.Resize(m,m);
//--- check
if(optype==0)
{
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
a2[i].Set(j,a1[i][j]);
}
}
//--- check
if(optype==1)
{
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
a2[i].Set(j,a1[j][i]);
}
//--- change value
rupper=!rupper;
}
//--- function call
InternalRMatrixTrInverse(a2,m,rupper,false);
//--- allocation
ArrayResize(tx,m);
for(j=0;j<=n-1;j++)
{
i1_=i2;
for(i_=0;i_<=m-1;i_++)
tx[i_]=x[i_+i1_][j2+j];
//--- change values
for(i=0;i<=m-1;i++)
{
vr=0.0;
for(i_=0;i_<=m-1;i_++)
vr+=a2[i][i_]*tx[i_];
x[i2+i].Set(j2+j,vr);
}
}
}
//+------------------------------------------------------------------+
//| Internal subroutine. |
//| Triangular matrix inversion |
//| -- LAPACK routine (version 3.0) -- |
//| Univ. of Tennessee,Univ. of California Berkeley,NAG Ltd., |
//| Courant Institute,Argonne National Lab,and Rice University |
//| February 29,1992 |
//+------------------------------------------------------------------+
static bool CTestAblasUnit::InternalCMatrixTrInverse(CMatrixComplex &a,
const int n,
const bool isupper,
const bool isunittriangular)
{
//--- create variables
bool result;
bool nounit;
int i=0;
int j=0;
complex v=0;
complex ajj=0;
complex one=1;
int i_=0;
//--- create array
complex t[];
//--- initialization
result=true;
//--- allocation
ArrayResize(t,n);
//--- Test the input parameters.
nounit=!isunittriangular;
//--- check
if(isupper)
{
//--- Compute inverse of upper triangular matrix.
for(j=0;j<=n-1;j++)
{
//--- check
if(nounit)
{
//--- check
if(a[j][j]==0)
{
//--- return result
return(false);
}
//--- change values
a[j].Set(j,one/a[j][j]);
ajj=-a[j][j];
}
else
ajj=-1;
//--- Compute elements 1:j-1 of j-th column.
if(j>0)
{
for(i_=0;i_<=j-1;i_++)
t[i_]=a[i_][j];
for(i=0;i<=j-1;i++)
{
//--- check
if(i+1<j)
{
v=0.0;
for(i_=i+1;i_<=j-1;i_++)
v+=a[i][i_]*t[i_];
}
else
v=0;
//--- check
if(nounit)
a[i].Set(j,v+a[i][i]*t[i]);
else
a[i].Set(j,v+t[i]);
}
//--- change values
for(i_=0;i_<=j-1;i_++)
a[i_].Set(j,ajj*a[i_][j]);
}
}
}
else
{
//--- Compute inverse of lower triangular matrix.
for(j=n-1;j>=0;j--)
{
//--- check
if(nounit)
{
//--- check
if(a[j][j]==0)
{
//--- return result
return(false);
}
//--- change values
a[j].Set(j,one/a[j][j]);
ajj=-a[j][j];
}
else
ajj=-1;
//--- check
if(j+1<n)
{
//--- Compute elements j+1:n of j-th column.
for(i_=j+1;i_<=n-1;i_++)
t[i_]=a[i_][j];
for(i=j+1;i<=n-1;i++)
{
//--- check
if(i>j+1)
{
v=0.0;
for(i_=j+1;i_<=i-1;i_++)
v+=a[i][i_]*t[i_];
}
else
v=0;
//--- check
if(nounit)
a[i].Set(j,v+a[i][i]*t[i]);
else
a[i].Set(j,v+t[i]);
}
//--- change values
for(i_=j+1;i_<=n-1;i_++)
a[i_].Set(j,ajj*a[i_][j]);
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Internal subroutine. |
//| Triangular matrix inversion |
//| -- LAPACK routine (version 3.0) -- |
//| Univ. of Tennessee,Univ. of California Berkeley,NAG Ltd., |
//| Courant Institute,Argonne National Lab,and Rice University |
//| February 29,1992 |
//+------------------------------------------------------------------+
static bool CTestAblasUnit::InternalRMatrixTrInverse(CMatrixDouble &a,
const int n,
const bool isupper,
const bool isunittriangular)
{
//--- create variables
bool result;
bool nounit;
int i=0;
int j=0;
double v=0;
double ajj=0;
int i_=0;
//--- create array
double t[];
//--- initialization
result=true;
//--- allocation
ArrayResize(t,n);
//--- Test the input parameters.
nounit=!isunittriangular;
//--- check
if(isupper)
{
//--- Compute inverse of upper triangular matrix.
for(j=0;j<=n-1;j++)
{
//--- check
if(nounit)
{
//--- check
if(a[j][j]==0.0)
{
//--- return result
return(false);
}
//--- change values
a[j].Set(j,1/a[j][j]);
ajj=-a[j][j];
}
else
ajj=-1;
//--- Compute elements 1:j-1 of j-th column.
if(j>0)
{
for(i_=0;i_<=j-1;i_++)
t[i_]=a[i_][j];
for(i=0;i<=j-1;i++)
{
//--- check
if(i<j-1)
{
v=0.0;
for(i_=i+1;i_<=j-1;i_++)
v+=a[i][i_]*t[i_];
}
else
v=0;
//--- check
if(nounit)
a[i].Set(j,v+a[i][i]*t[i]);
else
a[i].Set(j,v+t[i]);
}
//--- change values
for(i_=0;i_<=j-1;i_++)
a[i_].Set(j,ajj*a[i_][j]);
}
}
}
else
{
//--- Compute inverse of lower triangular matrix.
for(j=n-1;j>=0;j--)
{
//--- check
if(nounit)
{
//--- check
if(a[j][j]==0.0)
{
//--- return result
return(false);
}
//--- change values
a[j].Set(j,1/a[j][j]);
ajj=-a[j][j];
}
else
ajj=-1;
//--- check
if(j<n-1)
{
//--- Compute elements j+1:n of j-th column.
for(i_=j+1;i_<=n-1;i_++)
t[i_]=a[i_][j];
for(i=j+1;i<=n-1;i++)
{
//--- check
if(i>j+1)
{
v=0.0;
for(i_=j+1;i_<=i-1;i_++)
v+=a[i][i_]*t[i_];
}
else
v=0;
//--- check
if(nounit)
a[i].Set(j,v+a[i][i]*t[i]);
else
a[i].Set(j,v+t[i]);
}
//--- change values
for(i_=j+1;i_<=n-1;i_++)
a[i_].Set(j,ajj*a[i_][j]);
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Reference SYRK subroutine. |
//+------------------------------------------------------------------+
static void CTestAblasUnit::RefCMatrixSyrk(const int n,const int k,
const double alpha,CMatrixComplex &a,
const int ia,const int ja,
const int optypea,const double beta,
CMatrixComplex &c,const int ic,
const int jc,const bool isupper)
{
//--- create variables
int i=0;
int j=0;
complex vc=0;
int i_=0;
//--- create matrix
CMatrixComplex ae;
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if((isupper && j>=i) || (!isupper && j<=i))
{
//--- check
if(beta==0.0)
c[i+ic].Set(j+jc,0);
else
c[i+ic].Set(j+jc,c[i+ic][j+jc]*beta);
}
}
}
//--- check
if(alpha==0.0)
return;
//--- check
if(n*k>0)
ae.Resize(n,k);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=k-1;j++)
{
//--- check
if(optypea==0)
ae[i].Set(j,a[ia+i][ja+j]);
//--- check
if(optypea==2)
ae[i].Set(j,CMath::Conj(a[ia+j][ja+i]));
}
}
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
vc=0;
//--- check
if(k>0)
{
vc=0.0;
for(i_=0;i_<=k-1;i_++)
vc+=ae[i][i_]*CMath::Conj(ae[j][i_]);
}
vc=vc*alpha;
//--- check
if(isupper && j>=i)
c[ic+i].Set(jc+j,vc+c[ic+i][jc+j]);
//--- check
if(!isupper && j<=i)
c[ic+i].Set(jc+j,vc+c[ic+i][jc+j]);
}
}
}
//+------------------------------------------------------------------+
//| Reference SYRK subroutine. |
//+------------------------------------------------------------------+
static void CTestAblasUnit::RefRMatrixSyrk(const int n,const int k,
const double alpha,CMatrixDouble &a,
const int ia,const int ja,
const int optypea,const double beta,
CMatrixDouble &c,const int ic,
const int jc,const bool isupper)
{
//--- create variables
int i=0;
int j=0;
double vr=0;
int i_=0;
//--- create matrix
CMatrixDouble ae;
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if((isupper && j>=i) || (!isupper && j<=i))
{
//--- check
if(beta==0.0)
c[i+ic].Set(j+jc,0);
else
c[i+ic].Set(j+jc,c[i+ic][j+jc]*beta);
}
}
}
//--- check
if(alpha==0.0)
return;
//--- check
if(n*k>0)
ae.Resize(n,k);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=k-1;j++)
{
//--- check
if(optypea==0)
ae[i].Set(j,a[ia+i][ja+j]);
//--- check
if(optypea==1)
ae[i].Set(j,a[ia+j][ja+i]);
}
}
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
vr=0;
//--- check
if(k>0)
{
vr=0.0;
for(i_=0;i_<=k-1;i_++)
vr+=ae[i][i_]*ae[j][i_];
}
vr=alpha*vr;
//--- check
if(isupper && j>=i)
c[ic+i].Set(jc+j,vr+c[ic+i][jc+j]);
//--- check
if(!isupper && j<=i)
c[ic+i].Set(jc+j,vr+c[ic+i][jc+j]);
}
}
}
//+------------------------------------------------------------------+
//| Reference GEMM, |
//| ALGLIB subroutine |
//+------------------------------------------------------------------+
static void CTestAblasUnit::RefCMatrixGemm(const int m,const int n,
const int k,complex &alpha,
CMatrixComplex &a,const int ia,
const int ja,const int optypea,
CMatrixComplex &b,const int ib,
const int jb,const int optypeb,
complex &beta,CMatrixComplex &c,
const int ic,const int jc)
{
//--- create variables
int i=0;
int j=0;
complex vc=0;
int i_=0;
//--- create matrix
CMatrixComplex ae;
CMatrixComplex be;
//--- allocation
ae.Resize(m,k);
//--- change values
for(i=0;i<=m-1;i++)
{
for(j=0;j<=k-1;j++)
{
//--- check
if(optypea==0)
ae[i].Set(j,a[ia+i][ja+j]);
//--- check
if(optypea==1)
ae[i].Set(j,a[ia+j][ja+i]);
//--- check
if(optypea==2)
ae[i].Set(j,CMath::Conj(a[ia+j][ja+i]));
}
}
//--- allocation
be.Resize(k,n);
//--- change values
for(i=0;i<=k-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(optypeb==0)
be[i].Set(j,b[ib+i][jb+j]);
//--- check
if(optypeb==1)
be[i].Set(j,b[ib+j][jb+i]);
//--- check
if(optypeb==2)
be[i].Set(j,CMath::Conj(b[ib+j][jb+i]));
}
}
//--- calculation
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
vc=0.0;
for(i_=0;i_<=k-1;i_++)
vc+=ae[i][i_]*be[i_][j];
vc=alpha*vc;
//--- check
if(beta!=0)
vc=vc+beta*c[ic+i][jc+j];
c[ic+i].Set(jc+j,vc);
}
}
}
//+------------------------------------------------------------------+
//| Reference GEMM, |
//| ALGLIB subroutine |
//+------------------------------------------------------------------+
static void CTestAblasUnit::RefRMatrixGemm(const int m,const int n,
const int k,const double alpha,
CMatrixDouble &a,const int ia,
const int ja,const int optypea,
CMatrixDouble &b,const int ib,
const int jb,const int optypeb,
const double beta,CMatrixDouble &c,
const int ic,const int jc)
{
//--- create variables
int i=0;
int j=0;
double vc=0;
int i_=0;
//--- create matrix
CMatrixDouble ae;
CMatrixDouble be;
//--- allocation
ae.Resize(m,k);
//--- change values
for(i=0;i<=m-1;i++)
{
for(j=0;j<=k-1;j++)
{
//--- check
if(optypea==0)
ae[i].Set(j,a[ia+i][ja+j]);
//--- check
if(optypea==1)
ae[i].Set(j,a[ia+j][ja+i]);
}
}
//--- allocation
be.Resize(k,n);
//--- change values
for(i=0;i<=k-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(optypeb==0)
be[i].Set(j,b[ib+i][jb+j]);
//--- check
if(optypeb==1)
be[i].Set(j,b[ib+j][jb+i]);
}
}
//--- calculation
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
vc=0.0;
for(i_=0;i_<=k-1;i_++)
vc+=ae[i][i_]*be[i_][j];
vc=alpha*vc;
//--- check
if(beta!=0.0)
vc=vc+beta*c[ic+i][jc+j];
c[ic+i].Set(jc+j,vc);
}
}
}
//+------------------------------------------------------------------+
//| Testing class CBaseStat |
//+------------------------------------------------------------------+
class CTestBaseStatUnit
{
public:
//--- constructor, destructor
CTestBaseStatUnit(void);
~CTestBaseStatUnit(void);
//--- public method
static bool TestBaseStat(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestBaseStatUnit::CTestBaseStatUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestBaseStatUnit::~CTestBaseStatUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CBaseStat |
//+------------------------------------------------------------------+
static bool CTestBaseStatUnit::TestBaseStat(const bool silent)
{
//--- create variables
bool waserrors;
bool s1errors;
bool covcorrerrors;
double threshold=0;
int i=0;
int j=0;
int n=0;
int kx=0;
int ky=0;
int ctype=0;
int cidxx=0;
int cidxy=0;
double mean=0;
double variance=0;
double skewness=0;
double kurtosis=0;
double adev=0;
double median=0;
double pv=0;
double v=0;
int i_=0;
//--- create arrays
double x[];
double y[];
//--- create matrix
CMatrixDouble mx;
CMatrixDouble my;
CMatrixDouble cc;
CMatrixDouble cp;
CMatrixDouble cs;
//--- Primary settings
waserrors=false;
s1errors=false;
covcorrerrors=false;
threshold=1000*CMath::m_machineepsilon;
//--- * prepare X and Y - two test samples
//--- * test 1-sample coefficients
n=10;
//--- allocation
ArrayResize(x,n);
for(i=0;i<=n-1;i++)
x[i]=CMath::Sqr(i);
//--- function call
CBaseStat::SampleMoments(x,n,mean,variance,skewness,kurtosis);
//--- search errors
s1errors=s1errors || MathAbs(mean-28.5)>0.001;
s1errors=s1errors || MathAbs(variance-801.1667)>0.001;
s1errors=s1errors || MathAbs(skewness-0.5751)>0.001;
s1errors=s1errors || MathAbs(kurtosis+1.2666)>0.001;
//--- function call
CBaseStat::SampleAdev(x,n,adev);
//--- search errors
s1errors=s1errors || MathAbs(adev-23.2000)>0.001;
//--- function call
CBaseStat::SampleMedian(x,n,median);
//--- search errors
s1errors=s1errors || MathAbs(median-0.5*(16+25))>0.001;
for(i=0;i<=n-1;i++)
{
//--- function call
CBaseStat::SamplePercentile(x,n,(double)i/(double)(n-1),pv);
//--- search errors
s1errors=s1errors || MathAbs(pv-x[i])>0.001;
}
//--- function call
CBaseStat::SamplePercentile(x,n,0.5,pv);
//--- search errors
s1errors=s1errors || MathAbs(pv-0.5*(16+25))>0.001;
//--- test covariance/correlation:
//--- * 2-sample coefficients
//--- We generate random matrices MX and MY
n=10;
ArrayResize(x,n);
ArrayResize(y,n);
for(i=0;i<=n-1;i++)
{
x[i]=CMath::Sqr(i);
y[i]=i;
}
//--- search errors
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::PearsonCorr2(x,y,n)-0.9627)>0.0001;
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::SpearmanCorr2(x,y,n)-1.0000)>0.0001;
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::Cov2(x,y,n)-82.5000)>0.0001;
for(i=0;i<=n-1;i++)
{
x[i]=CMath::Sqr(i-0.5*n);
y[i]=i;
}
//--- search errors
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::PearsonCorr2(x,y,n)+0.3676)>0.0001;
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::SpearmanCorr2(x,y,n)+0.2761)>0.0001;
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::Cov2(x,y,n)+9.1667)>0.0001;
//--- test covariance/correlation:
//--- * matrix covariance/correlation
//--- * matrix cross-covariance/cross-correlation
//--- We generate random matrices MX and MY which contain KX (KY)
//--- columns,all except one are random,one of them is constant.
//--- We test that function (a) do not crash on constant column,
//--- and (b) return variances/correlations that are exactly zero
//--- for this column.
//--- CType control variable controls type of constant: 0 - no constant
//--- column,1 - zero column,2 - nonzero column with value whose
//--- binary representation contains many non-zero bits. Using such
//--- type of constant column we are able to ensure than even in the
//--- presense of roundoff error functions correctly detect constant
//--- columns.
for(n=0;n<=10;n++)
{
//--- check
if(n>0)
{
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
}
//--- calculation
for(ctype=0;ctype<=2;ctype++)
{
for(kx=1;kx<=10;kx++)
{
for(ky=1;ky<=10;ky++)
{
//--- Fill matrices,add constant column (when CType=1 or=2)
cidxx=-1;
cidxy=-1;
//--- check
if(n>0)
{
//--- allocation
mx.Resize(n,kx);
my.Resize(n,ky);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=kx-1;j++)
mx[i].Set(j,2*CMath::RandomReal()-1);
for(j=0;j<=ky-1;j++)
my[i].Set(j,2*CMath::RandomReal()-1);
}
//--- check
if(ctype==1)
{
cidxx=CMath::RandomInteger(kx);
cidxy=CMath::RandomInteger(ky);
//--- change values
for(i=0;i<=n-1;i++)
{
mx[i].Set(cidxx,0.0);
my[i].Set(cidxy,0.0);
}
}
//--- check
if(ctype==2)
{
cidxx=CMath::RandomInteger(kx);
cidxy=CMath::RandomInteger(ky);
//--- change values
v=MathSqrt((CMath::RandomInteger(kx)+1)/(double)kx);
for(i=0;i<=n-1;i++)
{
mx[i].Set(cidxx,v);
my[i].Set(cidxy,v);
}
}
}
//--- test covariance/correlation matrix using
//--- 2-sample functions as reference point.
//--- We also test that coefficients for constant variables
//--- are exactly zero.
CBaseStat::CovM(mx,n,kx,cc);
CBaseStat::PearsonCorrM(mx,n,kx,cp);
CBaseStat::SpearmanCorrM(mx,n,kx,cs);
for(i=0;i<=kx-1;i++)
{
for(j=0;j<=kx-1;j++)
{
//--- check
if(n>0)
{
for(i_=0;i_<=n-1;i_++)
{
x[i_]=mx[i_][i];
}
for(i_=0;i_<=n-1;i_++)
{
y[i_]=mx[i_][j];
}
}
//--- search errors
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::Cov2(x,y,n)-cc[i][j])>threshold;
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::PearsonCorr2(x,y,n)-cp[i][j])>threshold;
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::SpearmanCorr2(x,y,n)-cs[i][j])>threshold;
}
}
//--- check
if(ctype!=0 && n>0)
{
for(i=0;i<=kx-1;i++)
{
//--- search errors
covcorrerrors=covcorrerrors || cc[i][cidxx]!=0.0;
covcorrerrors=covcorrerrors || cc[cidxx][i]!=0.0;
covcorrerrors=covcorrerrors || cp[i][cidxx]!=0.0;
covcorrerrors=covcorrerrors || cp[cidxx][i]!=0.0;
covcorrerrors=covcorrerrors || cs[i][cidxx]!=0.0;
covcorrerrors=covcorrerrors || cs[cidxx][i]!=0.0;
}
}
//--- test cross-covariance/cross-correlation matrix using
//--- 2-sample functions as reference point.
//--- We also test that coefficients for constant variables
//--- are exactly zero.
CBaseStat::CovM2(mx,my,n,kx,ky,cc);
CBaseStat::PearsonCorrM2(mx,my,n,kx,ky,cp);
CBaseStat::SpearmanCorrM2(mx,my,n,kx,ky,cs);
for(i=0;i<=kx-1;i++)
{
for(j=0;j<=ky-1;j++)
{
//--- check
if(n>0)
{
for(i_=0;i_<=n-1;i_++)
x[i_]=mx[i_][i];
for(i_=0;i_<=n-1;i_++)
y[i_]=my[i_][j];
}
//--- search errors
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::Cov2(x,y,n)-cc[i][j])>threshold;
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::PearsonCorr2(x,y,n)-cp[i][j])>threshold;
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::SpearmanCorr2(x,y,n)-cs[i][j])>threshold;
}
}
//--- check
if(ctype!=0 && n>0)
{
for(i=0;i<=kx-1;i++)
{
//--- search errors
covcorrerrors=covcorrerrors || cc[i][cidxy]!=0.0;
covcorrerrors=covcorrerrors || cp[i][cidxy]!=0.0;
covcorrerrors=covcorrerrors || cs[i][cidxy]!=0.0;
}
for(j=0;j<=ky-1;j++)
{
//--- search errors
covcorrerrors=covcorrerrors || cc[cidxx][j]!=0.0;
covcorrerrors=covcorrerrors || cp[cidxx][j]!=0.0;
covcorrerrors=covcorrerrors || cs[cidxx][j]!=0.0;
}
}
}
}
}
}
//--- Final report
waserrors=s1errors || covcorrerrors;
//--- check
if(!silent)
{
Print("DESC.STAT TEST");
Print("TOTAL RESULTS: ");
//--- check
if(!waserrors)
Print("OK");
else
Print("FAILED");
Print("* 1-SAMPLE FUNCTIONALITY: ");
//--- check
if(!s1errors)
Print("OK");
else
Print("FAILED");
Print("* CORRELATION/COVARIATION: ");
//--- check
if(!covcorrerrors)
Print("OK");
else
Print("FAILED");
//--- check
if(waserrors)
Print("TEST SUMMARY: FAILED");
else
Print("TEST SUMMARY: PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CBdSS |
//+------------------------------------------------------------------+
class CTestBdSSUnit
{
public:
CTestBdSSUnit(void);
~CTestBdSSUnit(void);
static bool TestBdSS(const bool silent);
private:
static void Unset2D(CMatrixComplex &a);
static void Unset1D(double &a[]);
static void Unset1DI(int &a[]);
static void TestSortResults(double &asorted[],int &p1[],int &p2[],double &aoriginal[],const int n,bool &waserrors);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestBdSSUnit::CTestBdSSUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestBdSSUnit::~CTestBdSSUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CBdSS |
//+------------------------------------------------------------------+
static bool CTestBdSSUnit::TestBdSS(const bool silent)
{
//--- create variables
int n=0;
int i=0;
int j=0;
int pass=0;
int passcount=0;
int maxn=0;
int maxnq=0;
int tiecount=0;
int c1=0;
int c0=0;
int ni=0;
int nc=0;
double pal=0;
double pbl=0;
double par=0;
double pbr=0;
double cve=0;
double cvr=0;
int info=0;
double threshold=0;
double rms=0;
double cvrms=0;
bool waserrors;
bool tieserrors;
bool split2errors;
bool optimalsplitkerrors;
bool splitkerrors;
//--- create arrays
double a[];
double a0[];
double at[];
double thresholds[];
int c[];
int p1[];
int p2[];
int ties[];
int pt1[];
int pt2[];
double tmp[];
double sortrbuf[];
double sortrbuf2[];
int sortibuf[];
int tiebuf[];
int cntbuf[];
//--- create matrix
CMatrixDouble p;
//--- initialization
waserrors=false;
tieserrors=false;
split2errors=false;
splitkerrors=false;
optimalsplitkerrors=false;
maxn=100;
maxnq=49;
passcount=10;
//--- Test ties
for(n=1;n<=maxn;n++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- untied data,test DSTie
Unset1DI(p1);
Unset1DI(p2);
Unset1DI(pt1);
Unset1DI(pt2);
//--- allocation
ArrayResize(a,n);
ArrayResize(a0,n);
ArrayResize(at,n);
ArrayResize(tmp,n);
//--- change values
a[0]=2*CMath::RandomReal()-1;
tmp[0]=CMath::RandomReal();
for(i=1;i<=n-1;i++)
{
//--- A is randomly permuted
a[i]=a[i-1]+0.1*CMath::RandomReal()+0.1;
tmp[i]=CMath::RandomReal();
}
//--- function call
CTSort::TagSortFastR(tmp,a,sortrbuf,sortrbuf2,n);
for(i=0;i<=n-1;i++)
{
a0[i]=a[i];
at[i]=a[i];
}
//--- function call
CBdSS::DSTie(a0,n,ties,tiecount,p1,p2);
//--- function call
CTSort::TagSort(at,n,pt1,pt2);
//--- search errors
for(i=0;i<=n-1;i++)
{
tieserrors=tieserrors || p1[i]!=pt1[i];
tieserrors=tieserrors || p2[i]!=pt2[i];
}
tieserrors=tieserrors || tiecount!=n;
//--- check
if(tiecount==n)
{
for(i=0;i<=n;i++)
tieserrors=tieserrors || ties[i]!=i;
}
//--- tied data,test DSTie
Unset1DI(p1);
Unset1DI(p2);
Unset1DI(pt1);
Unset1DI(pt2);
//--- allocation
ArrayResize(a,n);
ArrayResize(a0,n);
ArrayResize(at,n);
//--- change values
c1=0;
c0=0;
for(i=0;i<=n-1;i++)
{
a[i]=CMath::RandomInteger(2);
//--- check
if(a[i]==0.0)
c0=c0+1;
else
c1=c1+1;
a0[i]=a[i];
at[i]=a[i];
}
//--- function call
CBdSS::DSTie(a0,n,ties,tiecount,p1,p2);
//--- function call
CTSort::TagSort(at,n,pt1,pt2);
//--- search errors
for(i=0;i<=n-1;i++)
{
tieserrors=tieserrors || p1[i]!=pt1[i];
tieserrors=tieserrors || p2[i]!=pt2[i];
}
//--- check
if(c0==0 || c1==0)
{
//--- search errors
tieserrors=tieserrors || tiecount!=1;
//--- check
if(tiecount==1)
{
tieserrors=tieserrors || ties[0]!=0;
tieserrors=tieserrors || ties[1]!=n;
}
}
else
{
//--- search errors
tieserrors=tieserrors || tiecount!=2;
//--- check
if(tiecount==2)
{
tieserrors=tieserrors || ties[0]!=0;
tieserrors=tieserrors || ties[1]!=c0;
tieserrors=tieserrors || ties[2]!=n;
}
}
}
}
//--- split-2
//--- General tests for different N's
for(n=1;n<=maxn;n++)
{
//--- allocation
ArrayResize(a,n);
ArrayResize(c,n);
//--- one-tie test
if(n%2==0)
{
for(i=0;i<=n-1;i++)
{
a[i]=n;
c[i]=i%2;
}
//--- function call
CBdSS::DSOptimalSplit2(a,c,n,info,threshold,pal,pbl,par,pbr,cve);
//--- check
if(info!=-3)
{
split2errors=true;
continue;
}
}
//--- two-tie test
//--- test #1
if(n>1)
{
for(i=0;i<=n-1;i++)
{
a[i]=i/((n+1)/2);
c[i]=i/((n+1)/2);
}
//--- function call
CBdSS::DSOptimalSplit2(a,c,n,info,threshold,pal,pbl,par,pbr,cve);
//--- check
if(info!=1)
{
split2errors=true;
continue;
}
//--- search errors
split2errors=split2errors || MathAbs(threshold-0.5)>100*CMath::m_machineepsilon;
split2errors=split2errors || MathAbs(pal-1)>100*CMath::m_machineepsilon;
split2errors=split2errors || MathAbs(pbl-0)>100*CMath::m_machineepsilon;
split2errors=split2errors || MathAbs(par-0)>100*CMath::m_machineepsilon;
split2errors=split2errors || MathAbs(pbr-1)>100*CMath::m_machineepsilon;
}
}
//--- Special "CREDIT"-test (transparency coefficient)
n=110;
//--- allocation
ArrayResize(a,n);
ArrayResize(c,n);
//--- initialization
a[0]=0.000;
c[0]=0;
a[1]=0.000;
c[1]=0;
a[2]=0.000;
c[2]=0;
a[3]=0.000;
c[3]=0;
a[4]=0.000;
c[4]=0;
a[5]=0.000;
c[5]=0;
a[6]=0.000;
c[6]=0;
a[7]=0.000;
c[7]=1;
a[8]=0.000;
c[8]=0;
a[9]=0.000;
c[9]=1;
a[10]=0.000;
c[10]=0;
a[11]=0.000;
c[11]=0;
a[12]=0.000;
c[12]=0;
a[13]=0.000;
c[13]=0;
a[14]=0.000;
c[14]=0;
a[15]=0.000;
c[15]=0;
a[16]=0.000;
c[16]=0;
a[17]=0.000;
c[17]=0;
a[18]=0.000;
c[18]=0;
a[19]=0.000;
c[19]=0;
a[20]=0.000;
c[20]=0;
a[21]=0.000;
c[21]=0;
a[22]=0.000;
c[22]=1;
a[23]=0.000;
c[23]=0;
a[24]=0.000;
c[24]=0;
a[25]=0.000;
c[25]=0;
a[26]=0.000;
c[26]=0;
a[27]=0.000;
c[27]=1;
a[28]=0.000;
c[28]=0;
a[29]=0.000;
c[29]=1;
a[30]=0.000;
c[30]=0;
a[31]=0.000;
c[31]=1;
a[32]=0.000;
c[32]=0;
a[33]=0.000;
c[33]=1;
a[34]=0.000;
c[34]=0;
a[35]=0.030;
c[35]=0;
a[36]=0.030;
c[36]=0;
a[37]=0.050;
c[37]=0;
a[38]=0.070;
c[38]=1;
a[39]=0.110;
c[39]=0;
a[40]=0.110;
c[40]=1;
a[41]=0.120;
c[41]=0;
a[42]=0.130;
c[42]=0;
a[43]=0.140;
c[43]=0;
a[44]=0.140;
c[44]=0;
a[45]=0.140;
c[45]=0;
a[46]=0.150;
c[46]=0;
a[47]=0.150;
c[47]=0;
a[48]=0.170;
c[48]=0;
a[49]=0.190;
c[49]=1;
a[50]=0.200;
c[50]=0;
a[51]=0.200;
c[51]=0;
a[52]=0.250;
c[52]=0;
a[53]=0.250;
c[53]=0;
a[54]=0.260;
c[54]=0;
a[55]=0.270;
c[55]=0;
a[56]=0.280;
c[56]=0;
a[57]=0.310;
c[57]=0;
a[58]=0.310;
c[58]=0;
a[59]=0.330;
c[59]=0;
a[60]=0.330;
c[60]=0;
a[61]=0.340;
c[61]=0;
a[62]=0.340;
c[62]=0;
a[63]=0.370;
c[63]=0;
a[64]=0.380;
c[64]=1;
a[65]=0.380;
c[65]=0;
a[66]=0.410;
c[66]=0;
a[67]=0.460;
c[67]=0;
a[68]=0.520;
c[68]=0;
a[69]=0.530;
c[69]=0;
a[70]=0.540;
c[70]=0;
a[71]=0.560;
c[71]=0;
a[72]=0.560;
c[72]=0;
a[73]=0.570;
c[73]=0;
a[74]=0.600;
c[74]=0;
a[75]=0.600;
c[75]=0;
a[76]=0.620;
c[76]=0;
a[77]=0.650;
c[77]=0;
a[78]=0.660;
c[78]=0;
a[79]=0.680;
c[79]=0;
a[80]=0.700;
c[80]=0;
a[81]=0.750;
c[81]=0;
a[82]=0.770;
c[82]=0;
a[83]=0.770;
c[83]=0;
a[84]=0.770;
c[84]=0;
a[85]=0.790;
c[85]=0;
a[86]=0.810;
c[86]=0;
a[87]=0.840;
c[87]=0;
a[88]=0.860;
c[88]=0;
a[89]=0.870;
c[89]=0;
a[90]=0.890;
c[90]=0;
a[91]=0.900;
c[91]=1;
a[92]=0.900;
c[92]=0;
a[93]=0.910;
c[93]=0;
a[94]=0.940;
c[94]=0;
a[95]=0.950;
c[95]=0;
a[96]=0.952;
c[96]=0;
a[97]=0.970;
c[97]=0;
a[98]=0.970;
c[98]=0;
a[99]=0.980;
c[99]=0;
a[100]=1.000;
c[100]=0;
a[101]=1.000;
c[101]=0;
a[102]=1.000;
c[102]=0;
a[103]=1.000;
c[103]=0;
a[104]=1.000;
c[104]=0;
a[105]=1.020;
c[105]=0;
a[106]=1.090;
c[106]=0;
a[107]=1.130;
c[107]=0;
a[108]=1.840;
c[108]=0;
a[109]=2.470;
c[109]=0;
//--- function call
CBdSS::DSOptimalSplit2(a,c,n,info,threshold,pal,pbl,par,pbr,cve);
//--- check
if(info!=1)
split2errors=true;
else
{
//--- search errors
split2errors=split2errors || MathAbs(threshold-0.195)>100*CMath::m_machineepsilon;
split2errors=split2errors || MathAbs(pal-0.80)>0.02;
split2errors=split2errors || MathAbs(pbl-0.20)>0.02;
split2errors=split2errors || MathAbs(par-0.97)>0.02;
split2errors=split2errors || MathAbs(pbr-0.03)>0.02;
}
//--- split-2 fast
//--- General tests for different N's
for(n=1;n<=maxn;n++)
{
//--- allocation
ArrayResize(a,n);
ArrayResize(c,n);
ArrayResize(tiebuf,n+1);
ArrayResize(cntbuf,4);
//--- one-tie test
if(n%2==0)
{
for(i=0;i<=n-1;i++)
{
a[i]=n;
c[i]=i%2;
}
//--- function call
CBdSS::DSOptimalSplit2Fast(a,c,tiebuf,cntbuf,sortrbuf,sortibuf,n,2,0.00,info,threshold,rms,cvrms);
//--- check
if(info!=-3)
{
split2errors=true;
continue;
}
}
//--- two-tie test
//--- test #1
if(n>1)
{
for(i=0;i<=n-1;i++)
{
a[i]=i/((n+1)/2);
c[i]=i/((n+1)/2);
}
//--- function call
CBdSS::DSOptimalSplit2Fast(a,c,tiebuf,cntbuf,sortrbuf,sortibuf,n,2,0.00,info,threshold,rms,cvrms);
//--- check
if(info!=1)
{
split2errors=true;
continue;
}
//--- search errors
split2errors=split2errors || MathAbs(threshold-0.5)>100*CMath::m_machineepsilon;
split2errors=split2errors || MathAbs(rms-0)>100*CMath::m_machineepsilon;
//--- check
if(n==2)
split2errors=split2errors || MathAbs(cvrms-0.5)>100*CMath::m_machineepsilon;
else
{
//--- check
if(n==3)
split2errors=split2errors || MathAbs(cvrms-MathSqrt((2*0+2*0+2*0.25)/6))>100*CMath::m_machineepsilon;
else
split2errors=split2errors || MathAbs(cvrms)>100*CMath::m_machineepsilon;
}
}
}
//--- special tests
n=10;
//--- allocation
ArrayResize(a,n);
ArrayResize(c,n);
ArrayResize(tiebuf,n+1);
ArrayResize(cntbuf,2*3);
//--- change values
for(i=0;i<=n-1;i++)
{
a[i]=i;
//--- check
if(i<=n-3)
c[i]=0;
else
c[i]=i-(n-3);
}
//--- function call
CBdSS::DSOptimalSplit2Fast(a,c,tiebuf,cntbuf,sortrbuf,sortibuf,n,3,0.00,info,threshold,rms,cvrms);
//--- check
if(info!=1)
split2errors=true;
else
{
//--- search errors
split2errors=split2errors || MathAbs(threshold-(n-2.5))>100*CMath::m_machineepsilon;
split2errors=split2errors || MathAbs(rms-MathSqrt((0.25+0.25+0.25+0.25)/(3*n)))>100*CMath::m_machineepsilon;
split2errors=split2errors || MathAbs(cvrms-MathSqrt((double)(1+1+1+1)/(double)(3*n)))>100*CMath::m_machineepsilon;
}
//--- Optimal split-K
//--- General tests for different N's
for(n=1;n<=maxnq;n++)
{
//--- allocation
ArrayResize(a,n);
ArrayResize(c,n);
//--- one-tie test
if(n%2==0)
{
for(i=0;i<=n-1;i++)
{
a[i]=n;
c[i]=i%2;
}
//--- function call
CBdSS::DSOptimalSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
//--- check
if(info!=-3)
{
optimalsplitkerrors=true;
continue;
}
}
//--- two-tie test
//--- test #1
if(n>1)
{
c0=0;
c1=0;
for(i=0;i<=n-1;i++)
{
a[i]=i/((n+1)/2);
c[i]=i/((n+1)/2);
//--- check
if(c[i]==0)
c0=c0+1;
//--- check
if(c[i]==1)
c1=c1+1;
}
//--- function call
CBdSS::DSOptimalSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
//--- check
if(info!=1)
{
optimalsplitkerrors=true;
continue;
}
//--- search errors
optimalsplitkerrors=optimalsplitkerrors || ni!=2;
optimalsplitkerrors=optimalsplitkerrors || MathAbs(thresholds[0]-0.5)>100*CMath::m_machineepsilon;
optimalsplitkerrors=optimalsplitkerrors || MathAbs(cve-(-(c0*MathLog((double)c0/(double)(c0+1)))-c1*MathLog((double)c1/(double)(c1+1))))>100*CMath::m_machineepsilon;
}
//--- test #2
if(n>2)
{
c0=1+CMath::RandomInteger(n-1);
c1=n-c0;
for(i=0;i<=n-1;i++)
{
//--- check
if(i<c0)
{
a[i]=0;
c[i]=0;
}
else
{
a[i]=1;
c[i]=1;
}
}
//--- function call
CBdSS::DSOptimalSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
//--- check
if(info!=1)
{
optimalsplitkerrors=true;
continue;
}
//--- search errors
optimalsplitkerrors=optimalsplitkerrors || ni!=2;
optimalsplitkerrors=optimalsplitkerrors || MathAbs(thresholds[0]-0.5)>100*CMath::m_machineepsilon;
optimalsplitkerrors=optimalsplitkerrors || MathAbs(cve-(-(c0*MathLog((double)c0/(double)(c0+1)))-c1*MathLog((double)c1/(double)(c1+1))))>100*CMath::m_machineepsilon;
}
//--- multi-tie test
if(n>=16)
{
//--- Multi-tie test.
//--- First NC-1 ties have C0 entries,remaining NC-th tie
//--- have C1 entries.
nc=(int)MathRound(MathSqrt(n));
c0=n/nc;
c1=n-c0*(nc-1);
for(i=0;i<=nc-2;i++)
{
for(j=c0*i;j<=c0*(i+1)-1;j++)
{
a[j]=j;
c[j]=i;
}
}
//--- change values
for(j=c0*(nc-1);j<=n-1;j++)
{
a[j]=j;
c[j]=nc-1;
}
//--- function call
CBdSS::DSOptimalSplitK(a,c,n,nc,nc+CMath::RandomInteger(nc),info,thresholds,ni,cve);
//--- check
if(info!=1)
{
optimalsplitkerrors=true;
continue;
}
//--- search errors
optimalsplitkerrors=optimalsplitkerrors || ni!=nc;
//--- check
if(ni==nc)
{
for(i=0;i<=nc-2;i++)
optimalsplitkerrors=optimalsplitkerrors || MathAbs(thresholds[i]-(c0*(i+1)-1+0.5))>100*CMath::m_machineepsilon;
cvr=-((nc-1)*c0*MathLog((double)c0/(double)(c0+nc-1))+c1*MathLog((double)c1/(double)(c1+nc-1)));
optimalsplitkerrors=optimalsplitkerrors || MathAbs(cve-cvr)>100*CMath::m_machineepsilon;
}
}
}
//--- Non-optimal split-K
//--- General tests for different N's
for(n=1;n<=maxnq;n++)
{
//--- allocation
ArrayResize(a,n);
ArrayResize(c,n);
//--- one-tie test
if(n%2==0)
{
for(i=0;i<=n-1;i++)
{
a[i]=pass;
c[i]=i%2;
}
//--- function call
CBdSS::DSSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
//--- check
if(info!=-3)
{
splitkerrors=true;
continue;
}
}
//--- two-tie test
//--- test #1
if(n>1)
{
c0=0;
c1=0;
for(i=0;i<=n-1;i++)
{
a[i]=i/((n+1)/2);
c[i]=i/((n+1)/2);
//--- check
if(c[i]==0)
c0=c0+1;
//--- check
if(c[i]==1)
c1=c1+1;
}
//--- function call
CBdSS::DSSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
//--- check
if(info!=1)
{
splitkerrors=true;
continue;
}
//--- search errors
splitkerrors=splitkerrors || ni!=2;
//--- check
if(ni==2)
{
splitkerrors=splitkerrors || MathAbs(thresholds[0]-0.5)>100*CMath::m_machineepsilon;
splitkerrors=splitkerrors || MathAbs(cve-(-(c0*MathLog((double)c0/(double)(c0+1)))-c1*MathLog((double)c1/(double)(c1+1))))>100*CMath::m_machineepsilon;
}
}
//--- test #2
if(n>2)
{
c0=1+CMath::RandomInteger(n-1);
c1=n-c0;
for(i=0;i<=n-1;i++)
{
//--- check
if(i<c0)
{
a[i]=0;
c[i]=0;
}
else
{
a[i]=1;
c[i]=1;
}
}
//--- function call
CBdSS::DSSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
//--- check
if(info!=1)
{
splitkerrors=true;
continue;
}
splitkerrors=splitkerrors || ni!=2;
//--- check
if(ni==2)
{
splitkerrors=splitkerrors || MathAbs(thresholds[0]-0.5)>100*CMath::m_machineepsilon;
splitkerrors=splitkerrors || MathAbs(cve-(-(c0*MathLog((double)c0/(double)(c0+1)))-c1*MathLog((double)c1/(double)(c1+1))))>100*CMath::m_machineepsilon;
}
}
//--- multi-tie test
for(c0=4;c0<=n;c0++)
{
//--- check
if((n%c0==0 && n/c0<=c0) && n/c0>1)
{
nc=n/c0;
for(i=0;i<=nc-1;i++)
{
for(j=c0*i;j<=c0*(i+1)-1;j++)
{
a[j]=j;
c[j]=i;
}
}
//--- function call
CBdSS::DSSplitK(a,c,n,nc,nc+CMath::RandomInteger(nc),info,thresholds,ni,cve);
//--- check
if(info!=1)
{
splitkerrors=true;
continue;
}
splitkerrors=splitkerrors || ni!=nc;
//--- check
if(ni==nc)
{
for(i=0;i<=nc-2;i++)
{
splitkerrors=splitkerrors || MathAbs(thresholds[i]-(c0*(i+1)-1+0.5))>100*CMath::m_machineepsilon;
}
cvr=-(nc*c0*MathLog((double)c0/(double)(c0+nc-1)));
splitkerrors=splitkerrors || MathAbs(cve-cvr)>100*CMath::m_machineepsilon;
}
}
}
}
//--- report
waserrors=((tieserrors || split2errors) || optimalsplitkerrors) || splitkerrors;
//--- check
if(!silent)
{
Print("TESTING BASIC DATASET SUBROUTINES");
Print("TIES: ");
//--- check
if(!tieserrors)
Print("OK");
else
Print("FAILED");
Print("SPLIT-2: ");
//--- check
if(!split2errors)
Print("OK");
else
Print("FAILED");
Print("OPTIMAL SPLIT-K: ");
//--- check
if(!optimalsplitkerrors)
Print("OK");
else
Print("FAILED");
Print("SPLIT-K: ");
//--- check
if(!splitkerrors)
Print("OK");
else
Print("FAILED");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Unsets 2D array. |
//+------------------------------------------------------------------+
static void CTestBdSSUnit::Unset2D(CMatrixComplex &a)
{
//--- allocation
a.Resize(1,1);
//--- change value
a[0].Set(0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets 1D array. |
//+------------------------------------------------------------------+
static void CTestBdSSUnit::Unset1D(double &a[])
{
//--- allocation
ArrayResize(a,1);
//--- change value
a[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Unsets 1D array. |
//+------------------------------------------------------------------+
static void CTestBdSSUnit::Unset1DI(int &a[])
{
//--- allocation
ArrayResize(a,1);
//--- change value
a[0]=CMath::RandomInteger(3)-1;
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestBdSSUnit::TestSortResults(double &asorted[],int &p1[],
int &p2[],double &aoriginal[],
const int n,bool &waserrors)
{
//--- create variables
int i=0;
double t=0;
//--- create arrays
double a2[];
int f[];
//--- allocation
ArrayResize(a2,n);
ArrayResize(f,n);
//--- is set ordered?
for(i=0;i<=n-2;i++)
waserrors=waserrors || asorted[i]>asorted[i+1];
//--- P1 correctness
for(i=0;i<=n-1;i++)
waserrors=waserrors || asorted[i]!=aoriginal[p1[i]];
//--- change values
for(i=0;i<=n-1;i++)
f[i]=0;
for(i=0;i<=n-1;i++)
f[p1[i]]=f[p1[i]]+1;
//--- search errors
for(i=0;i<=n-1;i++)
waserrors=waserrors || f[i]!=1;
//--- P2 correctness
for(i=0;i<=n-1;i++)
a2[i]=aoriginal[i];
for(i=0;i<=n-1;i++)
{
//--- check
if(p2[i]!=i)
{
t=a2[i];
a2[i]=a2[p2[i]];
a2[p2[i]]=t;
}
}
//--- search errors
for(i=0;i<=n-1;i++)
waserrors=waserrors || asorted[i]!=a2[i];
}
//+------------------------------------------------------------------+
//| Testing class CDForest |
//+------------------------------------------------------------------+
class CTestDForestUnit
{
public:
CTestDForestUnit(void);
~CTestDForestUnit(void);
static bool TestDForest(const bool silent);
private:
static void TestProcessing(bool &err);
static void BasicTest1(const int nvars,const int nclasses,const int passcount,bool &err);
static void BasicTest2(bool &err);
static void BasicTest3(bool &err);
static void BasicTest4(bool &err);
static void BasicTest5(bool &err);
static double RNormal(void);
static void RSphere(CMatrixDouble &xy,const int n,const int i);
static void UnsetDF(CDecisionForest &df);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestDForestUnit::CTestDForestUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestDForestUnit::~CTestDForestUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CDForest |
//+------------------------------------------------------------------+
static bool CTestDForestUnit::TestDForest(const bool silent)
{
//--- create variables
int ncmax=0;
int nvmax=0;
int passcount=0;
int nvars=0;
int nclasses=0;
bool waserrors;
bool basicerrors;
bool procerrors;
//--- Primary settings
nvmax=4;
ncmax=3;
passcount=10;
basicerrors=false;
procerrors=false;
waserrors=false;
//--- Tests
TestProcessing(procerrors);
for(nvars=1;nvars<=nvmax;nvars++)
{
for(nclasses=1;nclasses<=ncmax;nclasses++)
BasicTest1(nvars,nclasses,passcount,basicerrors);
}
//--- function calls
BasicTest2(basicerrors);
BasicTest3(basicerrors);
BasicTest4(basicerrors);
BasicTest5(basicerrors);
//--- Final report
waserrors=basicerrors || procerrors;
//--- check
if(!silent)
{
Print("RANDOM FOREST TEST");
Print("");
Print("TOTAL RESULTS: ");
//--- check
if(!waserrors)
Print("OK");
else
Print("FAILED");
Print("* PROCESSING FUNCTIONS: ");
//--- check
if(!procerrors)
Print("OK");
else
Print("FAILED");
Print("* BASIC TESTS: ");
//--- check
if(!basicerrors)
Print("OK");
else
Print("FAILED");
//--- check
if(waserrors)
Print("TEST SUMMARY: FAILED");
else
Print("TEST SUMMARY: PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Processing functions test |
//+------------------------------------------------------------------+
static void CTestDForestUnit::TestProcessing(bool &err)
{
//--- create variables
int nvars=0;
int nclasses=0;
int nsample=0;
int ntrees=0;
int nfeatures=0;
int flags=0;
int npoints=0;
int pass=0;
int passcount=0;
int i=0;
int j=0;
bool allsame;
int info=0;
double v=0;
//--- create matrix
CMatrixDouble xy;
//--- create arrays
double x1[];
double x2[];
double y1[];
double y2[];
//--- objects of classes
CDecisionForest df1;
CDecisionForest df2;
CDFReport rep;
//--- initialization
passcount=100;
//--- Main cycle
for(pass=1;pass<=passcount;pass++)
{
//--- initialize parameters
nvars=1+CMath::RandomInteger(5);
nclasses=1+CMath::RandomInteger(3);
ntrees=1+CMath::RandomInteger(4);
nfeatures=1+CMath::RandomInteger(nvars);
flags=0;
//--- check
if(CMath::RandomReal()>0.5)
flags=flags+2;
//--- Initialize arrays and data
npoints=10+CMath::RandomInteger(50);
nsample=(int)(MathMax(10,CMath::RandomInteger(npoints)));
//--- allocation
ArrayResize(x1,nvars);
ArrayResize(x2,nvars);
ArrayResize(y1,nclasses);
ArrayResize(y2,nclasses);
xy.Resize(npoints,nvars+1);
//--- change values
for(i=0;i<=npoints-1;i++)
{
for(j=0;j<=nvars-1;j++)
{
//--- check
if(j%2==0)
xy[i].Set(j,2*CMath::RandomReal()-1);
else
xy[i].Set(j,CMath::RandomInteger(2));
}
//--- check
if(nclasses==1)
xy[i].Set(nvars,2*CMath::RandomReal()-1);
else
xy[i].Set(nvars,CMath::RandomInteger(nclasses));
}
//--- create forest
CDForest::DFBuildInternal(xy,npoints,nvars,nclasses,ntrees,nsample,nfeatures,flags,info,df1,rep);
//--- check
if(info<=0)
{
err=true;
return;
}
//--- Same inputs leads to same outputs
for(i=0;i<=nvars-1;i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=x1[i];
}
for(i=0;i<=nclasses-1;i++)
{
y1[i]=2*CMath::RandomReal()-1;
y2[i]=2*CMath::RandomReal()-1;
}
//--- function call
CDForest::DFProcess(df1,x1,y1);
//--- function call
CDForest::DFProcess(df1,x2,y2);
allsame=true;
//--- search errors
for(i=0;i<=nclasses-1;i++)
allsame=allsame && y1[i]==y2[i];
err=err || !allsame;
//--- Same inputs on original forest leads to same outputs
//--- on copy created using DFCopy
UnsetDF(df2);
//--- function call
CDForest::DFCopy(df1,df2);
for(i=0;i<=nvars-1;i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=x1[i];
}
for(i=0;i<=nclasses-1;i++)
{
y1[i]=2*CMath::RandomReal()-1;
y2[i]=2*CMath::RandomReal()-1;
}
//--- function call
CDForest::DFProcess(df1,x1,y1);
//--- function call
CDForest::DFProcess(df2,x2,y2);
allsame=true;
//--- search errors
for(i=0;i<=nclasses-1;i++)
allsame=allsame && y1[i]==y2[i];
err=err || !allsame;
//--- Same inputs on original forest leads to same outputs
//--- on copy created using DFSerialize
UnsetDF(df2);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
//--- serialization
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CDForest::DFAlloc(_local_serializer,df1);
_local_serializer.SStart_Str();
CDForest::DFSerialize(_local_serializer,df1);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//--- unserialization
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CDForest::DFUnserialize(_local_serializer,df2);
_local_serializer.Stop();
}
for(i=0;i<=nvars-1;i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=x1[i];
}
for(i=0;i<=nclasses-1;i++)
{
y1[i]=2*CMath::RandomReal()-1;
y2[i]=2*CMath::RandomReal()-1;
}
//--- function call
CDForest::DFProcess(df1,x1,y1);
//--- function call
CDForest::DFProcess(df2,x2,y2);
allsame=true;
//--- search errors
for(i=0;i<=nclasses-1;i++)
allsame=allsame && y1[i]==y2[i];
err=err || !allsame;
//--- Normalization properties
if(nclasses>1)
{
for(i=0;i<=nvars-1;i++)
x1[i]=2*CMath::RandomReal()-1;
//--- function call
CDForest::DFProcess(df1,x1,y1);
v=0;
//--- search errors
for(i=0;i<=nclasses-1;i++)
{
v=v+y1[i];
err=err || y1[i]<0.0;
}
err=err || MathAbs(v-1)>1000*CMath::m_machineepsilon;
}
}
}
//+------------------------------------------------------------------+
//| Basic test: one-tree forest built using full sample must |
//| remember all the training cases |
//+------------------------------------------------------------------+
static void CTestDForestUnit::BasicTest1(const int nvars,const int nclasses,
const int passcount,bool &err)
{
//--- create variables
int pass=0;
int npoints=0;
int i=0;
int j=0;
int k=0;
double s=0;
int info=0;
bool hassame;
int i_=0;
//--- create arrays
double x[];
double y[];
//--- create matrix
CMatrixDouble xy;
//--- objects of classes
CDecisionForest df;
CDFReport rep;
//--- check
if(nclasses==1)
{
//--- only classification tasks
return;
}
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- select number of points
if(pass<=3 && passcount>3)
npoints=pass;
else
npoints=100+CMath::RandomInteger(100);
//--- Prepare task
xy.Resize(npoints,nvars+1);
ArrayResize(x,nvars);
ArrayResize(y,nclasses);
//--- change values
for(i=0;i<=npoints-1;i++)
{
for(j=0;j<=nvars-1;j++)
xy[i].Set(j,2*CMath::RandomReal()-1);
xy[i].Set(nvars,CMath::RandomInteger(nclasses));
}
//--- Test
CDForest::DFBuildInternal(xy,npoints,nvars,nclasses,1,npoints,1,1,info,df,rep);
//--- check
if(info<=0)
{
err=true;
return;
}
//--- calculation
for(i=0;i<=npoints-1;i++)
{
for(i_=0;i_<=nvars-1;i_++)
x[i_]=xy[i][i_];
//--- function call
CDForest::DFProcess(df,x,y);
s=0;
for(j=0;j<=nclasses-1;j++)
{
//--- check
if(y[j]<0.0)
{
err=true;
return;
}
s=s+y[j];
}
//--- check
if(MathAbs(s-1)>1000*CMath::m_machineepsilon)
{
err=true;
return;
}
//--- check
if(MathAbs(y[(int)MathRound(xy[i][nvars])]-1)>1000*CMath::m_machineepsilon)
{
//--- not an error if there exists such K,J that XY[k].Set(j,XY[I,J]
//--- (may be we just can't distinguish two tied values).
//--- definitely error otherwise.
hassame=false;
for(k=0;k<=npoints-1;k++)
{
//--- check
if(k!=i)
{
for(j=0;j<=nvars-1;j++)
{
//--- check
if(xy[k][j]==xy[i][j])
hassame=true;
}
}
}
//--- check
if(!hassame)
{
err=true;
return;
}
}
}
}
}
//+------------------------------------------------------------------+
//| Basic test: tests generalization ability on a simple noisy |
//| classification task: |
//| * 0<x<1 - P(class=0)=1 |
//| * 1<x<2 - P(class=0)=2-x |
//| * 2<x<3 - P(class=0)=0 |
//+------------------------------------------------------------------+
static void CTestDForestUnit::BasicTest2(bool &err)
{
//--- create variables
int pass=0;
int passcount=0;
int npoints=0;
int ntrees=0;
int i=0;
int j=0;
double s=0;
int info=0;
//--- create arrays
double x[];
double y[];
//--- create matrix
CMatrixDouble xy;
//--- objects of classes
CDecisionForest df;
CDFReport rep;
//--- initialization
passcount=1;
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- select npoints and ntrees
npoints=3000;
ntrees=50;
//--- Prepare task
xy.Resize(npoints,2);
ArrayResize(x,1);
ArrayResize(y,2);
for(i=0;i<=npoints-1;i++)
{
xy[i].Set(0,3*CMath::RandomReal());
//--- check
if(xy[i][0]<=1.0)
xy[i].Set(1,0);
else
{
//--- check
if(xy[i][0]<=2.0)
{
//--- check
if(CMath::RandomReal()<xy[i][0]-1)
xy[i].Set(1,1);
else
xy[i].Set(1,0);
}
else
xy[i].Set(1,1);
}
}
//--- Test
CDForest::DFBuildInternal(xy,npoints,1,2,ntrees,(int)MathRound(0.05*npoints),1,0,info,df,rep);
//--- check
if(info<=0)
{
err=true;
return;
}
x[0]=0.0;
//--- cycle
while(x[0]<=3.0)
{
//--- function call
CDForest::DFProcess(df,x,y);
//--- Test for basic properties
s=0;
for(j=0;j<=1;j++)
{
//--- check
if(y[j]<0.0)
{
err=true;
return;
}
s=s+y[j];
}
//--- check
if(MathAbs(s-1)>1000*CMath::m_machineepsilon)
{
err=true;
return;
}
//--- test for good correlation with results
if(x[0]<1.0)
err=err || y[0]<0.8;
//--- check
if(x[0]>=1.0 && x[0]<=2.0)
err=err || MathAbs(y[1]-(x[0]-1))>0.5;
//--- check
if(x[0]>2.0)
err=err || y[1]<0.8;
x[0]=x[0]+0.01;
}
}
}
//+------------------------------------------------------------------+
//| Basic test: tests generalization ability on a simple |
//| classification task (no noise): |
//| * ||x||<1,||y||<1 |
//| * x^2+y^2<=0.25 - P(class=0)=1 |
//| * x^2+y^2>0.25 - P(class=0)=0 |
//+------------------------------------------------------------------+
static void CTestDForestUnit::BasicTest3(bool &err)
{
//--- create variables
int pass=0;
int passcount=0;
int npoints=0;
int ntrees=0;
int i=0;
int j=0;
int k=0;
double s=0;
int info=0;
int testgridsize=0;
double r=0;
//--- create arrays
double x[];
double y[];
//--- create matrix
CMatrixDouble xy;
//--- objects of classes
CDecisionForest df;
CDFReport rep;
//--- initialization
passcount=1;
testgridsize=50;
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- select npoints and ntrees
npoints=2000;
ntrees=100;
//--- Prepare task
xy.Resize(npoints,3);
ArrayResize(x,2);
ArrayResize(y,2);
//--- change values
for(i=0;i<=npoints-1;i++)
{
xy[i].Set(0,2*CMath::RandomReal()-1);
xy[i].Set(1,2*CMath::RandomReal()-1);
//--- check
if(CMath::Sqr(xy[i][0])+CMath::Sqr(xy[i][1])<=0.25)
xy[i].Set(2,0);
else
xy[i].Set(2,1);
}
//--- Test
CDForest::DFBuildInternal(xy,npoints,2,2,ntrees,(int)MathRound(0.1*npoints),1,0,info,df,rep);
//--- check
if(info<=0)
{
err=true;
return;
}
//--- calculation
for(i=-(testgridsize/2);i<=testgridsize/2;i++)
{
for(j=-(testgridsize/2);j<=testgridsize/2;j++)
{
x[0]=(double)i/(double)(testgridsize/2);
x[1]=(double)j/(double)(testgridsize/2);
//--- function call
CDForest::DFProcess(df,x,y);
//--- Test for basic properties
s=0;
for(k=0;k<=1;k++)
{
//--- check
if(y[k]<0.0)
{
err=true;
return;
}
s=s+y[k];
}
//--- check
if(MathAbs(s-1)>1000*CMath::m_machineepsilon)
{
err=true;
return;
}
//--- test for good correlation with results
r=MathSqrt(CMath::Sqr(x[0])+CMath::Sqr(x[1]));
//--- check
if(r<0.5*0.5)
err=err || y[0]<0.6;
//--- check
if(r>0.5*1.5)
err=err || y[1]<0.6;
}
}
}
}
//+------------------------------------------------------------------+
//| Basic test: simple regression task without noise: |
//| * ||x||<1,||y||<1 |
//| * F(x,y)=x^2+y |
//+------------------------------------------------------------------+
static void CTestDForestUnit::BasicTest4(bool &err)
{
//--- create variables
int pass=0;
int passcount=0;
int npoints=0;
int ntrees=0;
int ns=0;
int strongc=0;
int i=0;
int j=0;
int info=0;
int testgridsize=0;
double maxerr=0;
double maxerr2=0;
double avgerr=0;
double avgerr2=0;
int cnt=0;
double ey=0;
//--- create arrays
double x[];
double y[];
//--- create matrix
CMatrixDouble xy;
//--- objects of classes
CDecisionForest df;
CDecisionForest df2;
CDFReport rep;
CDFReport rep2;
//--- initialization
passcount=1;
testgridsize=50;
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- select npoints and ntrees
npoints=5000;
ntrees=100;
ns=(int)MathRound(0.1*npoints);
strongc=1;
//--- Prepare task
xy.Resize(npoints,3);
ArrayResize(x,2);
ArrayResize(y,1);
//--- change values
for(i=0;i<=npoints-1;i++)
{
xy[i].Set(0,2*CMath::RandomReal()-1);
xy[i].Set(1,2*CMath::RandomReal()-1);
xy[i].Set(2,CMath::Sqr(xy[i][0])+xy[i][1]);
}
//--- Test
CDForest::DFBuildInternal(xy,npoints,2,1,ntrees,ns,1,0,info,df,rep);
//--- check
if(info<=0)
{
err=true;
return;
}
//--- function call
CDForest::DFBuildInternal(xy,npoints,2,1,ntrees,ns,1,strongc,info,df2,rep2);
//--- check
if(info<=0)
{
err=true;
return;
}
//--- change values
maxerr=0;
maxerr2=0;
avgerr=0;
avgerr2=0;
cnt=0;
//--- calculation
for(i=(int)MathRound(-(0.7*testgridsize/2));i<=(int)MathRound(0.7*testgridsize/2);i++)
{
for(j=(int)MathRound(-(0.7*testgridsize/2));j<=(int)MathRound(0.7*testgridsize/2);j++)
{
x[0]=(double)i/(double)(testgridsize/2);
x[1]=(double)j/(double)(testgridsize/2);
ey=CMath::Sqr(x[0])+x[1];
//--- function call
CDForest::DFProcess(df,x,y);
maxerr=MathMax(maxerr,MathAbs(y[0]-ey));
avgerr=avgerr+MathAbs(y[0]-ey);
//--- function call
CDForest::DFProcess(df2,x,y);
maxerr2=MathMax(maxerr2,MathAbs(y[0]-ey));
avgerr2=avgerr2+MathAbs(y[0]-ey);
cnt=cnt+1;
}
}
//--- search errors
avgerr=avgerr/cnt;
avgerr2=avgerr2/cnt;
err=err || maxerr>0.2;
err=err || maxerr2>0.2;
err=err || avgerr>0.1;
err=err || avgerr2>0.1;
}
}
//+------------------------------------------------------------------+
//| Basic test: extended variable selection leads to better results. |
//| Next task CAN be solved without EVS but it is very unlikely. |
//| With EVS it can be easily and exactly solved. |
//| Task matrix: |
//| 1 0 0 0 ... 0 0 |
//| 0 1 0 0 ... 0 1 |
//| 0 0 1 0 ... 0 2 |
//| 0 0 0 1 ... 0 3 |
//| 0 0 0 0 ... 1 N-1 |
//+------------------------------------------------------------------+
static void CTestDForestUnit::BasicTest5(bool &err)
{
//--- create variables
int nvars=0;
int npoints=0;
int nfeatures=0;
int nsample=0;
int ntrees=0;
int evs=0;
int i=0;
int j=0;
bool eflag;
int info=0;
int i_=0;
//--- create arrays
double x[];
double y[];
//--- create matrix
CMatrixDouble xy;
//--- objects of classes
CDecisionForest df;
CDFReport rep;
//--- select npoints and ntrees
npoints=50;
nvars=npoints;
ntrees=1;
nsample=npoints;
evs=2;
nfeatures=1;
//--- Prepare task
xy.Resize(npoints,nvars+1);
ArrayResize(x,nvars);
ArrayResize(y,1);
for(i=0;i<=npoints-1;i++)
{
for(j=0;j<=nvars-1;j++)
{
xy[i].Set(j,0);
}
xy[i].Set(i,1);
xy[i].Set(nvars,i);
}
//--- Without EVS
CDForest::DFBuildInternal(xy,npoints,nvars,1,ntrees,nsample,nfeatures,0,info,df,rep);
//--- check
if(info<=0)
{
err=true;
return;
}
//--- calculation
eflag=false;
for(i=0;i<=npoints-1;i++)
{
for(i_=0;i_<=nvars-1;i_++)
x[i_]=xy[i][i_];
//--- function call
CDForest::DFProcess(df,x,y);
//--- check
if(MathAbs(y[0]-xy[i][nvars])>1000*CMath::m_machineepsilon)
eflag=true;
}
//--- check
if(!eflag)
{
err=true;
return;
}
//--- With EVS
CDForest::DFBuildInternal(xy,npoints,nvars,1,ntrees,nsample,nfeatures,evs,info,df,rep);
//--- check
if(info<=0)
{
err=true;
return;
}
//--- calculation
eflag=false;
for(i=0;i<=npoints-1;i++)
{
for(i_=0;i_<=nvars-1;i_++)
{
x[i_]=xy[i][i_];
}
//--- function call
CDForest::DFProcess(df,x,y);
//--- check
if(MathAbs(y[0]-xy[i][nvars])>1000*CMath::m_machineepsilon)
{
eflag=true;
}
}
//--- check
if(eflag)
{
err=true;
return;
}
}
//+------------------------------------------------------------------+
//| Random normal number |
//+------------------------------------------------------------------+
static double CTestDForestUnit::RNormal(void)
{
//--- create variables
double result=0;
double u=0;
double v=0;
double s=0;
double x1=0;
double x2=0;
//--- calculation
while(true)
{
u=2*CMath::RandomReal()-1;
v=2*CMath::RandomReal()-1;
s=CMath::Sqr(u)+CMath::Sqr(v);
//--- check
if(s>0.0 && s<1.0)
{
s=MathSqrt(-(2*MathLog(s)/s));
x1=u*s;
x2=v*s;
//--- break the cycle
break;
}
}
//--- check
if(x1!=0)
result=x1;
else
result=x2;
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Random point from sphere |
//+------------------------------------------------------------------+
static void CTestDForestUnit::RSphere(CMatrixDouble &xy,const int n,
const int i)
{
//--- create variables
int j=0;
double v=0;
int i_=0;
//--- change values
for(j=0;j<=n-1;j++)
xy[i].Set(j,RNormal());
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=xy[i][i_]*xy[i][i_];
//--- calculation
v=CMath::RandomReal()/MathSqrt(v);
for(i_=0;i_<=n-1;i_++)
xy[i].Set(i_,v*xy[i][i_]);
}
//+------------------------------------------------------------------+
//| Unsets DF |
//+------------------------------------------------------------------+
static void CTestDForestUnit::UnsetDF(CDecisionForest &df)
{
//--- create a variable
int info=0;
//--- create matrix
CMatrixDouble xy;
//--- object of class
CDFReport rep;
//--- allocation
xy.Resize(1,2);
//--- change values
xy[0].Set(0,0);
xy[0].Set(1,0);
//--- function call
CDForest::DFBuildInternal(xy,1,1,1,1,1,1,0,info,df,rep);
}
//+------------------------------------------------------------------+
//| Testing class CBlas |
//+------------------------------------------------------------------+
class CTestBlasUnit
{
private:
//--- private method
static void NaiveMatrixMatrixMultiply(CMatrixDouble &a,const int ai1,const int ai2,const int aj1,const int aj2,const bool transa,CMatrixDouble &b,const int bi1,const int bi2,const int bj1,const int bj2,const bool transb,const double alpha,CMatrixDouble &c,const int ci1,const int ci2,const int cj1,const int cj2,const double beta);
public:
//--- constructor, destructor
CTestBlasUnit(void);
~CTestBlasUnit(void);
//--- public method
static bool TestBlas(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestBlasUnit::CTestBlasUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestBlasUnit::~CTestBlasUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CBlas |
//+------------------------------------------------------------------+
static bool CTestBlasUnit::TestBlas(const bool silent)
{
//--- create variables
int pass=0;
int passcount=0;
int n=0;
int i=0;
int i1=0;
int i2=0;
int j=0;
int j1=0;
int j2=0;
int l=0;
int k=0;
int r=0;
int i3=0;
int j3=0;
int col1=0;
int col2=0;
int row1=0;
int row2=0;
double err=0;
double e1=0;
double e2=0;
double e3=0;
double v=0;
double scl1=0;
double scl2=0;
double scl3=0;
bool was1;
bool was2;
bool trans1;
bool trans2;
double threshold=0;
bool n2errors;
bool hsnerrors;
bool amaxerrors;
bool mverrors;
bool iterrors;
bool cterrors;
bool mmerrors;
bool waserrors;
int i_=0;
//--- create arrays
double x1[];
double x2[];
//--- create matrix
CMatrixDouble a;
CMatrixDouble b;
CMatrixDouble c1;
CMatrixDouble c2;
//--- initialization
n2errors=false;
amaxerrors=false;
hsnerrors=false;
mverrors=false;
iterrors=false;
cterrors=false;
mmerrors=false;
waserrors=false;
threshold=10000*CMath::m_machineepsilon;
//--- Test Norm2
passcount=1000;
e1=0;
e2=0;
e3=0;
scl2=0.5*CMath::m_maxrealnumber;
scl3=2*CMath::m_minrealnumber;
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
n=1+CMath::RandomInteger(1000);
i1=CMath::RandomInteger(10);
i2=n+i1-1;
//--- allocation
ArrayResize(x1,i2+1);
ArrayResize(x2,i2+1);
//--- change values
for(i=i1;i<=i2;i++)
x1[i]=2*CMath::RandomReal()-1;
v=0;
for(i=i1;i<=i2;i++)
v=v+CMath::Sqr(x1[i]);
v=MathSqrt(v);
//--- calculation
e1=MathMax(e1,MathAbs(v-CBlas::VectorNorm2(x1,i1,i2)));
for(i=i1;i<=i2;i++)
x2[i]=scl2*x1[i];
//--- calculation
e2=MathMax(e2,MathAbs(v*scl2-CBlas::VectorNorm2(x2,i1,i2)));
for(i=i1;i<=i2;i++)
x2[i]=scl3*x1[i];
//--- calculation
e3=MathMax(e3,MathAbs(v*scl3-CBlas::VectorNorm2(x2,i1,i2)));
}
e2=e2/scl2;
e3=e3/scl3;
//--- search errors
n2errors=(e1>=threshold || e2>=threshold) || e3>=threshold;
//--- Testing VectorAbsMax,Column/Row AbsMax
ArrayResize(x1,6);
x1[1]=2.0;
x1[2]=0.2;
x1[3]=-1.3;
x1[4]=0.7;
x1[5]=-3.0;
//--- search errors
amaxerrors=(CBlas::VectorIdxAbsMax(x1,1,5)!=5 || CBlas::VectorIdxAbsMax(x1,1,4)!=1) || CBlas::VectorIdxAbsMax(x1,2,4)!=3;
n=30;
//--- allocation
ArrayResize(x1,n+1);
a.Resize(n+1,n+1);
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- calculation
was1=false;
was2=false;
for(pass=1;pass<=1000;pass++)
{
//--- change values
j=1+CMath::RandomInteger(n);
i1=1+CMath::RandomInteger(n);
i2=i1+CMath::RandomInteger(n+1-i1);
for(i_=i1;i_<=i2;i_++)
x1[i_]=a[i_][j];
//--- check
if(CBlas::VectorIdxAbsMax(x1,i1,i2)!=CBlas::ColumnIdxAbsMax(a,i1,i2,j))
was1=true;
//--- change values
i=1+CMath::RandomInteger(n);
j1=1+CMath::RandomInteger(n);
j2=j1+CMath::RandomInteger(n+1-j1);
for(i_=j1;i_<=j2;i_++)
x1[i_]=a[i][i_];
//--- check
if(CBlas::VectorIdxAbsMax(x1,j1,j2)!=CBlas::RowIdxAbsMax(a,j1,j2,i))
was2=true;
}
//--- search errors
amaxerrors=(amaxerrors || was1) || was2;
//--- Testing upper Hessenberg 1-norm
a.Resize(4,4);
ArrayResize(x1,4);
a[1].Set(1,2);
a[1].Set(2,3);
a[1].Set(3,1);
a[2].Set(1,4);
a[2].Set(2,-5);
a[2].Set(3,8);
a[3].Set(1,99);
a[3].Set(2,3);
a[3].Set(3,1);
//--- search errors
hsnerrors=MathAbs(CBlas::UpperHessenberg1Norm(a,1,3,1,3,x1)-11)>threshold;
//--- Testing MatrixVectorMultiply
a.Resize(4,6);
ArrayResize(x1,4);
ArrayResize(x2,3);
a[2].Set(3,2);
a[2].Set(4,-1);
a[2].Set(5,-1);
a[3].Set(3,1);
a[3].Set(4,-2);
a[3].Set(5,2);
x1[1]=1;
x1[2]=2;
x1[3]=1;
x2[1]=-1;
x2[2]=-1;
//--- function calls
CBlas::MatrixVectorMultiply(a,2,3,3,5,false,x1,1,3,1.0,x2,1,2,1.0);
CBlas::MatrixVectorMultiply(a,2,3,3,5,true,x2,1,2,1.0,x1,1,3,1.0);
//--- calculation
e1=MathAbs(x1[1]+5)+MathAbs(x1[2]-8)+MathAbs(x1[3]+1)+MathAbs(x2[1]+2)+MathAbs(x2[2]+2);
x1[1]=1;
x1[2]=2;
x1[3]=1;
x2[1]=-1;
x2[2]=-1;
//--- function calls
CBlas::MatrixVectorMultiply(a,2,3,3,5,false,x1,1,3,1.0,x2,1,2,0.0);
CBlas::MatrixVectorMultiply(a,2,3,3,5,true,x2,1,2,1.0,x1,1,3,0.0);
//--- calculation
e2=MathAbs(x1[1]+3)+MathAbs(x1[2]-3)+MathAbs(x1[3]+1)+MathAbs(x2[1]+1)+MathAbs(x2[2]+1);
mverrors=e1+e2>=threshold;
//--- testing inplace transpose
n=10;
a.Resize(n+1,n+1);
b.Resize(n+1,n+1);
ArrayResize(x1,n);
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
a[i].Set(j,CMath::RandomReal());
}
//--- calculation
passcount=10000;
was1=false;
for(pass=1;pass<=passcount;pass++)
{
//--- change values
i1=1+CMath::RandomInteger(n);
i2=i1+CMath::RandomInteger(n-i1+1);
j1=1+CMath::RandomInteger(n-(i2-i1));
j2=j1+(i2-i1);
//--- function calls
CBlas::CopyMatrix(a,i1,i2,j1,j2,b,i1,i2,j1,j2);
CBlas::InplaceTranspose(b,i1,i2,j1,j2,x1);
for(i=i1;i<=i2;i++)
{
for(j=j1;j<=j2;j++)
{
//--- check
if(a[i][j]!=b[i1+(j-j1)][j1+(i-i1)])
was1=true;
}
}
}
//--- change value
iterrors=was1;
//--- testing copy and transpose
n=10;
a.Resize(n+1,n+1);
b.Resize(n+1,n+1);
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
a[i].Set(j,CMath::RandomReal());
}
//--- calculation
passcount=10000;
was1=false;
for(pass=1;pass<=passcount;pass++)
{
//--- change values
i1=1+CMath::RandomInteger(n);
i2=i1+CMath::RandomInteger(n-i1+1);
j1=1+CMath::RandomInteger(n);
j2=j1+CMath::RandomInteger(n-j1+1);
//--- function call
CBlas::CopyAndTranspose(a,i1,i2,j1,j2,b,j1,j2,i1,i2);
for(i=i1;i<=i2;i++)
{
for(j=j1;j<=j2;j++)
{
//--- check
if(a[i][j]!=b[j][i])
was1=true;
}
}
}
//--- change values
cterrors=was1;
//--- Testing MatrixMatrixMultiply
n=10;
a.Resize(2*n+1,2*n+1);
b.Resize(2*n+1,2*n+1);
c1.Resize(2*n+1,2*n+1);
c2.Resize(2*n+1,2*n+1);
ArrayResize(x1,n+1);
ArrayResize(x2,n+1);
for(i=1;i<=2*n;i++)
{
for(j=1;j<=2*n;j++)
{
a[i].Set(j,CMath::RandomReal());
b[i].Set(j,CMath::RandomReal());
}
}
//--- calculation
passcount=1000;
was1=false;
for(pass=1;pass<=passcount;pass++)
{
for(i=1;i<=2*n;i++)
{
for(j=1;j<=2*n;j++)
{
c1[i].Set(j,2.1*i+3.1*j);
c2[i].Set(j,c1[i][j]);
}
}
//--- change values
l=1+CMath::RandomInteger(n);
k=1+CMath::RandomInteger(n);
r=1+CMath::RandomInteger(n);
i1=1+CMath::RandomInteger(n);
j1=1+CMath::RandomInteger(n);
i2=1+CMath::RandomInteger(n);
j2=1+CMath::RandomInteger(n);
i3=1+CMath::RandomInteger(n);
j3=1+CMath::RandomInteger(n);
trans1=CMath::RandomReal()>0.5;
trans2=CMath::RandomReal()>0.5;
//--- check
if(trans1)
{
col1=l;
row1=k;
}
else
{
col1=k;
row1=l;
}
//--- check
if(trans2)
{
col2=k;
row2=r;
}
else
{
col2=r;
row2=k;
}
//--- change values
scl1=CMath::RandomReal();
scl2=CMath::RandomReal();
//--- function calls
CBlas::MatrixMatrixMultiply(a,i1,i1+row1-1,j1,j1+col1-1,trans1,b,i2,i2+row2-1,j2,j2+col2-1,trans2,scl1,c1,i3,i3+l-1,j3,j3+r-1,scl2,x1);
NaiveMatrixMatrixMultiply(a,i1,i1+row1-1,j1,j1+col1-1,trans1,b,i2,i2+row2-1,j2,j2+col2-1,trans2,scl1,c2,i3,i3+l-1,j3,j3+r-1,scl2);
//--- search errors
err=0;
for(i=1;i<=l;i++)
{
for(j=1;j<=r;j++)
err=MathMax(err,MathAbs(c1[i3+i-1][j3+j-1]-c2[i3+i-1][j3+j-1]));
}
//--- check
if(err>threshold)
{
was1=true;
break;
}
}
//--- change value
mmerrors=was1;
//--- report
waserrors=(((((n2errors || amaxerrors) || hsnerrors) || mverrors) || iterrors) || cterrors) || mmerrors;
//--- check
if(!silent)
{
Print("TESTING BLAS");
Print("VectorNorm2: ");
//--- check
if(n2errors)
Print("FAILED");
else
Print("OK");
Print("AbsMax (vector/row/column): ");
//--- check
if(amaxerrors)
Print("FAILED");
else
Print("OK");
Print("UpperHessenberg1Norm: ");
//--- check
if(hsnerrors)
Print("FAILED");
else
Print("OK");
Print("MatrixVectorMultiply: ");
//--- check
if(mverrors)
Print("FAILED");
else
Print("OK");
Print("InplaceTranspose: ");
//--- check
if(iterrors)
Print("FAILED");
else
Print("OK");
Print("CopyAndTranspose: ");
//--- check
if(cterrors)
Print("FAILED");
else
Print("OK");
Print("MatrixMatrixMultiply: ");
//--- check
if(mmerrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestBlasUnit::NaiveMatrixMatrixMultiply(CMatrixDouble &a,
const int ai1,
const int ai2,
const int aj1,
const int aj2,
const bool transa,
CMatrixDouble &b,
const int bi1,
const int bi2,
const int bj1,
const int bj2,
const bool transb,
const double alpha,
CMatrixDouble &c,
const int ci1,
const int ci2,
const int cj1,
const int cj2,
const double beta)
{
//--- create variables
int arows=0;
int acols=0;
int brows=0;
int bcols=0;
int i=0;
int j=0;
int k=0;
int l=0;
int r=0;
double v=0;
int i_=0;
int i1_=0;
//--- create arrays
double x1[];
double x2[];
//--- Setup
if(!transa)
{
arows=ai2-ai1+1;
acols=aj2-aj1+1;
}
else
{
arows=aj2-aj1+1;
acols=ai2-ai1+1;
}
//--- check
if(!transb)
{
brows=bi2-bi1+1;
bcols=bj2-bj1+1;
}
else
{
brows=bj2-bj1+1;
bcols=bi2-bi1+1;
}
//--- check
if(!CAp::Assert(acols==brows,"NaiveMatrixMatrixMultiply: incorrect matrix sizes!"))
return;
//--- check
if(((arows<=0 || acols<=0) || brows<=0) || bcols<=0)
return;
//--- change values
l=arows;
r=bcols;
k=acols;
//--- allocation
ArrayResize(x1,k+1);
ArrayResize(x2,k+1);
//--- calculation
for(i=1;i<=l;i++)
{
for(j=1;j<=r;j++)
{
//--- check
if(!transa)
{
//--- check
if(!transb)
{
//--- change values
i1_=aj1-bi1;
v=0.0;
for(i_=bi1;i_<=bi2;i_++)
v+=b[i_][bj1+j-1]*a[ai1+i-1][i_+i1_];
}
else
{
//--- change values
i1_=aj1-bj1;
v=0.0;
for(i_=bj1;i_<=bj2;i_++)
v+=b[bi1+j-1][i_]*a[ai1+i-1][i_+i1_];
}
}
else
{
//--- check
if(!transb)
{
//--- change values
i1_=ai1-bi1;
v=0.0;
for(i_=bi1;i_<=bi2;i_++)
v+=b[i_][bj1+j-1]*a[i_+i1_][aj1+i-1];
}
else
{
//--- change values
i1_=ai1-bj1;
v=0.0;
for(i_=bj1;i_<=bj2;i_++)
v+=b[bi1+j-1][i_]*a[i_+i1_][aj1+i-1];
}
}
//--- check
if(beta==0.0)
c[ci1+i-1].Set(cj1+j-1,alpha*v);
else
c[ci1+i-1].Set(cj1+j-1,beta*c[ci1+i-1][cj1+j-1]+alpha*v);
}
}
}
//+------------------------------------------------------------------+
//| Testing class CKMeans |
//+------------------------------------------------------------------+
class CTestKMeansUnit
{
public:
CTestKMeansUnit(void);
~CTestKMeansUnit(void);
static bool TestKMeans(const bool silent);
private:
static void SimpleTest1(const int nvars,const int nc,int passcount,bool &converrors,bool &othererrors,bool &simpleerrors);
static void RestartsTest(bool &converrors,bool &restartserrors);
static double RNormal(void);
static void RSphere(CMatrixDouble &xy,const int n,const int i);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestKMeansUnit::CTestKMeansUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestKMeansUnit::~CTestKMeansUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CKMeans |
//+------------------------------------------------------------------+
static bool CTestKMeansUnit::TestKMeans(const bool silent)
{
//--- create variables
int nf=0;
int maxnf=0;
int nc=0;
int maxnc=0;
int passcount=0;
bool waserrors;
bool converrors;
bool simpleerrors;
bool complexerrors;
bool othererrors;
bool restartserrors;
//--- Primary settings
maxnf=5;
maxnc=5;
passcount=10;
waserrors=false;
converrors=false;
othererrors=false;
simpleerrors=false;
complexerrors=false;
restartserrors=false;
//--- calculation
for(nf=1;nf<=maxnf;nf++)
{
for(nc=1;nc<=maxnc;nc++)
SimpleTest1(nf,nc,passcount,converrors,othererrors,simpleerrors);
}
RestartsTest(converrors,restartserrors);
//--- Final report
waserrors=(((converrors || othererrors) || simpleerrors) || complexerrors) || restartserrors;
//--- check
if(!silent)
{
Print("K-MEANS TEST");
Print("TOTAL RESULTS: ");
//--- check
if(!waserrors)
Print("OK");
else
Print("FAILED");
Print("* CONVERGENCE: ");
//--- check
if(!converrors)
Print("OK");
else
Print("FAILED");
Print("* SIMPLE TASKS: ");
//--- check
if(!simpleerrors)
Print("OK");
else
Print("FAILED");
Print("* COMPLEX TASKS: ");
//--- check
if(!complexerrors)
Print("OK");
else
Print("FAILED");
Print("* OTHER PROPERTIES: ");
//--- check
if(!othererrors)
Print("OK");
else
Print("FAILED");
Print("* RESTARTS PROPERTIES: ");
//--- check
if(!restartserrors)
Print("OK");
else
Print("FAILED");
//--- check
if(waserrors)
Print("TEST SUMMARY: FAILED");
else
Print("TEST SUMMARY: PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Simple test 1: ellipsoid in NF-dimensional space. |
//| compare k-means centers with random centers |
//+------------------------------------------------------------------+
static void CTestKMeansUnit::SimpleTest1(const int nvars,const int nc,
int passcount,bool &converrors,
bool &othererrors,bool &simpleerrors)
{
//--- create variables
int npoints=0;
int majoraxis=0;
double v=0;
int i=0;
int j=0;
int info=0;
int pass=0;
int restarts=0;
double ekmeans=0;
double erandom=0;
double dclosest=0;
int cclosest=0;
int i_=0;
//--- create arrays
double tmp[];
int xyc[];
//--- create matrix
CMatrixDouble xy;
CMatrixDouble c;
//--- initialization
npoints=nc*100;
restarts=5;
passcount=10;
//--- allocation
ArrayResize(tmp,nvars);
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- Fill
xy.Resize(npoints,nvars);
majoraxis=CMath::RandomInteger(nvars);
for(i=0;i<=npoints-1;i++)
{
RSphere(xy,nvars,i);
xy[i].Set(majoraxis,nc*xy[i][majoraxis]);
}
//--- Test
CKMeans::KMeansGenerate(xy,npoints,nvars,nc,restarts,info,c,xyc);
//--- check
if(info<0)
{
converrors=true;
return;
}
//--- Test that XYC is correct mapping to cluster centers
for(i=0;i<=npoints-1;i++)
{
cclosest=-1;
dclosest=CMath::m_maxrealnumber;
//--- calculation
for(j=0;j<=nc-1;j++)
{
for(i_=0;i_<=nvars-1;i_++)
tmp[i_]=xy[i][i_];
for(i_=0;i_<=nvars-1;i_++)
tmp[i_]=tmp[i_]-c[i_][j];
v=0.0;
for(i_=0;i_<=nvars-1;i_++)
v+=tmp[i_]*tmp[i_];
//--- check
if(v<dclosest)
{
cclosest=j;
dclosest=v;
}
}
//--- check
if(cclosest!=xyc[i])
{
othererrors=true;
return;
}
}
//--- Use first NC rows of XY as random centers
//--- (XY is totally random,so it is as good as any other choice).
//--- Compare potential functions.
ekmeans=0;
for(i=0;i<=npoints-1;i++)
{
for(i_=0;i_<=nvars-1;i_++)
tmp[i_]=xy[i][i_];
for(i_=0;i_<=nvars-1;i_++)
tmp[i_]=tmp[i_]-c[i_][xyc[i]];
//--- change value
v=0.0;
for(i_=0;i_<=nvars-1;i_++)
v+=tmp[i_]*tmp[i_];
ekmeans=ekmeans+v;
}
//--- calculation
erandom=0;
for(i=0;i<=npoints-1;i++)
{
dclosest=CMath::m_maxrealnumber;
v=0;
for(j=0;j<=nc-1;j++)
{
for(i_=0;i_<=nvars-1;i_++)
tmp[i_]=xy[i][i_];
for(i_=0;i_<=nvars-1;i_++)
tmp[i_]=tmp[i_]-xy[j][i_];
//--- change value
v=0.0;
for(i_=0;i_<=nvars-1;i_++)
v+=tmp[i_]*tmp[i_];
//--- check
if(v<dclosest)
dclosest=v;
}
erandom=erandom+v;
}
//--- check
if(erandom<ekmeans)
{
simpleerrors=true;
return;
}
}
}
//+------------------------------------------------------------------+
//| This non-deterministic test checks that Restarts>1 significantly |
//| improves quality of results. |
//| Subroutine generates random task 3 unit balls in 2D,each with 20 |
//| points, separated by 5 units wide gaps,and solves it with |
//| Restarts=1 and with Restarts=5. Potential functions are compared,|
//| outcome of the trial is either 0 or 1 (depending on what is |
//| better). |
//| Sequence of 1000 such tasks is olved. If Restarts>1 actually |
//| improve quality of solution,sum of outcome will be non-binomial. |
//| If it doesn't matter,it will be binomially distributed. |
//| P.S. This test was added after report from Gianluca Borello who |
//| noticed error in the handling of multiple restarts. |
//+------------------------------------------------------------------+
static void CTestKMeansUnit::RestartsTest(bool &converrors,bool &restartserrors)
{
//--- create variables
int npoints=0;
int nvars=0;
int nclusters=0;
int clustersize=0;
int restarts=0;
int passcount=0;
double sigmathreshold=0;
double p=0;
double s=0;
int i=0;
int j=0;
int info=0;
int pass=0;
double ea=0;
double eb=0;
double v=0;
int i_=0;
//--- create matrix
CMatrixDouble xy;
CMatrixDouble ca;
CMatrixDouble cb;
//--- create arrays
int xyca[];
int xycb[];
double tmp[];
//--- initialization
restarts=5;
passcount=1000;
clustersize=20;
nclusters=3;
nvars=2;
npoints=nclusters*clustersize;
sigmathreshold=5;
//--- allocation
xy.Resize(npoints,nvars);
ArrayResize(tmp,nvars);
p=0;
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- Fill
for(i=0;i<=npoints-1;i++)
{
RSphere(xy,nvars,i);
for(j=0;j<=nvars-1;j++)
xy[i].Set(j,xy[i][j]+(double)i/(double)clustersize*5);
}
//--- Test: Restarts=1
CKMeans::KMeansGenerate(xy,npoints,nvars,nclusters,1,info,ca,xyca);
//--- check
if(info<0)
{
converrors=true;
return;
}
//--- calculation
ea=0;
for(i=0;i<=npoints-1;i++)
{
for(i_=0;i_<=nvars-1;i_++)
tmp[i_]=xy[i][i_];
for(i_=0;i_<=nvars-1;i_++)
tmp[i_]=tmp[i_]-ca[i_][xyca[i]];
//--- change value
v=0.0;
for(i_=0;i_<=nvars-1;i_++)
v+=tmp[i_]*tmp[i_];
ea=ea+v;
}
//--- Test: Restarts>1
CKMeans::KMeansGenerate(xy,npoints,nvars,nclusters,restarts,info,cb,xycb);
//--- check
if(info<0)
{
converrors=true;
return;
}
//--- calculation
eb=0;
for(i=0;i<=npoints-1;i++)
{
for(i_=0;i_<=nvars-1;i_++)
tmp[i_]=xy[i][i_];
for(i_=0;i_<=nvars-1;i_++)
tmp[i_]=tmp[i_]-cb[i_][xycb[i]];
//--- change value
v=0.0;
for(i_=0;i_<=nvars-1;i_++)
v+=tmp[i_]*tmp[i_];
eb=eb+v;
}
//--- Calculate statistic.
if(ea<eb)
p=p+1;
//--- check
if(ea==eb)
p=p+0.5;
}
//--- If Restarts doesn't influence quality of centers found,P must be
//--- binomially distributed random value with mean 0.5*PassCount and
//--- standard deviation Sqrt(PassCount/4).
//--- If Restarts do influence quality of solution,P must be significantly
//--- lower than 0.5*PassCount.
s=(p-0.5*passcount)/MathSqrt((double)passcount/4.0);
restartserrors=restartserrors || s>(double)(-sigmathreshold);
}
//+------------------------------------------------------------------+
//| Random normal number |
//+------------------------------------------------------------------+
static double CTestKMeansUnit::RNormal(void)
{
//--- create variables
double result=0;
double u=0;
double v=0;
double s=0;
double x1=0;
double x2=0;
//--- calculation
while(true)
{
//--- change values
u=2*CMath::RandomReal()-1;
v=2*CMath::RandomReal()-1;
s=CMath::Sqr(u)+CMath::Sqr(v);
//--- check
if(s>0.0 && s<1.0)
{
s=MathSqrt(-(2*MathLog(s)/s));
x1=u*s;
x2=v*s;
//--- break the cycle
break;
}
}
//--- check
if(x1==0)
result=x2;
else
result=x1;
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Random point from sphere |
//+------------------------------------------------------------------+
static void CTestKMeansUnit::RSphere(CMatrixDouble &xy,const int n,const int i)
{
//--- create variables
int j=0;
double v=0;
int i_=0;
//--- change values
for(j=0;j<=n-1;j++)
xy[i].Set(j,RNormal());
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=xy[i][i_]*xy[i][i_];
//--- calculation
v=CMath::RandomReal()/MathSqrt(v);
for(i_=0;i_<=n-1;i_++)
xy[i].Set(i_,v*xy[i][i_]);
}
//+------------------------------------------------------------------+
//| Testing class CHblas |
//+------------------------------------------------------------------+
class CTestHblasUnit
{
public:
//--- constructor, destructor
CTestHblasUnit(void);
~CTestHblasUnit(void);
//--- public method
static bool TestHblas(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestHblasUnit::CTestHblasUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestHblasUnit::~CTestHblasUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CHblas |
//+------------------------------------------------------------------+
static bool CTestHblasUnit::TestHblas(const bool silent)
{
//--- create variables
int n=0;
int maxn=0;
int i=0;
int j=0;
int i1=0;
int i2=0;
bool waserrors;
double mverr=0;
double threshold=0;
complex alpha=0;
complex v=0;
int i_=0;
int i1_=0;
//--- create arrays
complex x[];
complex y1[];
complex y2[];
complex y3[];
//--- create matrix
CMatrixComplex a;
CMatrixComplex ua;
CMatrixComplex la;
//--- initialization
mverr=0;
waserrors=false;
maxn=10;
threshold=1000*CMath::m_machineepsilon;
//--- Test MV
for(n=2;n<=maxn;n++)
{
//--- allocation
a.Resize(n+1,n+1);
ua.Resize(n+1,n+1);
la.Resize(n+1,n+1);
ArrayResize(x,n+1);
ArrayResize(y1,n+1);
ArrayResize(y2,n+1);
ArrayResize(y3,n+1);
//--- fill A,UA,LA
for(i=1;i<=n;i++)
{
a[i].SetRe(i,2*CMath::RandomReal()-1);
a[i].SetIm(i,0);
//--- change values
for(j=i+1;j<=n;j++)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
a[j].Set(i,CMath::Conj(a[i][j]));
}
}
//--- change values
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
ua[i].Set(j,0);
}
//--- change values
for(i=1;i<=n;i++)
{
for(j=i;j<=n;j++)
ua[i].Set(j,a[i][j]);
}
//--- change values
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
la[i].Set(j,0);
}
//--- change values
for(i=1;i<=n;i++)
{
for(j=1;j<=i;j++)
la[i].Set(j,a[i][j]);
}
//--- test on different I1,I2
for(i1=1;i1<=n;i1++)
{
for(i2=i1;i2<=n;i2++)
{
//--- Fill X,choose Alpha
for(i=1;i<=i2-i1+1;i++)
{
x[i].re=2*CMath::RandomReal()-1;
x[i].im=2*CMath::RandomReal()-1;
}
alpha.re=2*CMath::RandomReal()-1;
alpha.im=2*CMath::RandomReal()-1;
//--- calculate A*x,UA*x,LA*x
for(i=i1;i<=i2;i++)
{
i1_=1-i1;
v=0.0;
for(i_=i1;i_<=i2;i_++)
v+=a[i][i_]*x[i_+i1_];
y1[i-i1+1]=alpha*v;
}
//--- function call
CHblas::HermitianMatrixVectorMultiply(ua,true,i1,i2,x,alpha,y2);
//--- function call
CHblas::HermitianMatrixVectorMultiply(la,false,i1,i2,x,alpha,y3);
//--- Calculate error
for(i_=1;i_<=i2-i1+1;i_++)
y2[i_]=y2[i_]-y1[i_];
//--- change value
v=0.0;
for(i_=1;i_<=i2-i1+1;i_++)
v+=y2[i_]*CMath::Conj(y2[i_]);
//--- search errors
mverr=MathMax(mverr,MathSqrt(CMath::AbsComplex(v)));
for(i_=1;i_<=i2-i1+1;i_++)
y3[i_]=y3[i_]-y1[i_];
//--- change value
v=0.0;
for(i_=1;i_<=i2-i1+1;i_++)
v+=y3[i_]*CMath::Conj(y3[i_]);
//--- search errors
mverr=MathMax(mverr,MathSqrt(CMath::AbsComplex(v)));
}
}
}
//--- report
waserrors=mverr>threshold;
//--- check
if(!silent)
{
Print("TESTING HERMITIAN BLAS");
Print("MV error: ");
Print("{0,5:E3}",mverr);
Print("Threshold: ");
Print("{0,5:E3}",threshold);
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CReflections |
//+------------------------------------------------------------------+
class CTestReflectionsUnit
{
public:
//--- constructor, destructor
CTestReflectionsUnit(void);
~CTestReflectionsUnit(void);
//--- public method
static bool TestReflections(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestReflectionsUnit::CTestReflectionsUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestReflectionsUnit::~CTestReflectionsUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CReflections |
//+------------------------------------------------------------------+
static bool CTestReflectionsUnit::TestReflections(const bool silent)
{
//--- create variables
bool result;
int i=0;
int j=0;
int n=0;
int m=0;
int maxmn=0;
double tmp=0;
double beta=0;
double tau=0;
double err=0;
double mer=0;
double mel=0;
double meg=0;
int pass=0;
int passcount=0;
double threshold=0;
int tasktype=0;
double xscale=0;
int i_=0;
//--- create arrays
double x[];
double v[];
double work[];
//--- create matrix
CMatrixDouble h;
CMatrixDouble a;
CMatrixDouble b;
CMatrixDouble c;
//--- initialization
passcount=10;
threshold=100*CMath::m_machineepsilon;
mer=0;
mel=0;
meg=0;
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
for(n=1;n<=10;n++)
{
for(m=1;m<=10;m++)
{
//--- Task
n=1+CMath::RandomInteger(10);
m=1+CMath::RandomInteger(10);
maxmn=MathMax(m,n);
//--- Initialize
ArrayResize(x,maxmn+1);
ArrayResize(v,maxmn+1);
ArrayResize(work,maxmn+1);
h.Resize(maxmn+1,maxmn+1);
a.Resize(maxmn+1,maxmn+1);
b.Resize(maxmn+1,maxmn+1);
c.Resize(maxmn+1,maxmn+1);
//--- GenerateReflection,three tasks are possible:
//--- * random X
//--- * zero X
//--- * non-zero X[1],all other are zeros
//--- * random X,near underflow scale
//--- * random X,near overflow scale
for(tasktype=0;tasktype<=4;tasktype++)
{
xscale=1;
//--- check
if(tasktype==0)
{
for(i=1;i<=n;i++)
x[i]=2*CMath::RandomReal()-1;
}
//--- check
if(tasktype==1)
{
for(i=1;i<=n;i++)
x[i]=0;
}
//--- check
if(tasktype==2)
{
x[1]=2*CMath::RandomReal()-1;
for(i=2;i<=n;i++)
x[i]=0;
}
//--- check
if(tasktype==3)
{
for(i=1;i<=n;i++)
x[i]=(CMath::RandomInteger(21)-10)*CMath::m_minrealnumber;
xscale=10*CMath::m_minrealnumber;
}
//--- check
if(tasktype==4)
{
for(i=1;i<=n;i++)
x[i]=(2*CMath::RandomReal()-1)*CMath::m_maxrealnumber;
xscale=CMath::m_maxrealnumber;
}
for(i_=1;i_<=n;i_++)
{
v[i_]=x[i_];
}
//--- function call
CReflections::GenerateReflection(v,n,tau);
//--- change values
beta=v[1];
v[1]=1;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
//--- check
if(i==j)
h[i].Set(j,1-tau*v[i]*v[j]);
else
h[i].Set(j,-(tau*v[i]*v[j]));
}
}
//--- calculation
err=0;
for(i=1;i<=n;i++)
{
tmp=0.0;
for(i_=1;i_<=n;i_++)
tmp+=h[i][i_]*x[i_];
//--- check
if(i==1)
err=MathMax(err,MathAbs(tmp-beta));
else
err=MathMax(err,MathAbs(tmp));
}
meg=MathMax(meg,err/xscale);
}
//--- ApplyReflectionFromTheLeft
for(i=1;i<=m;i++)
{
x[i]=2*CMath::RandomReal()-1;
v[i]=x[i];
}
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
a[i].Set(j,2*CMath::RandomReal()-1);
b[i].Set(j,a[i][j]);
}
}
//--- function call
CReflections::GenerateReflection(v,m,tau);
beta=v[1];
v[1]=1;
//--- function call
CReflections::ApplyReflectionFromTheLeft(b,tau,v,1,m,1,n,work);
//--- change values
for(i=1;i<=m;i++)
{
for(j=1;j<=m;j++)
{
//--- check
if(i==j)
h[i].Set(j,1-tau*v[i]*v[j]);
else
h[i].Set(j,-(tau*v[i]*v[j]));
}
}
//--- calculation
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
tmp=0.0;
for(i_=1;i_<=m;i_++)
tmp+=h[i][i_]*a[i_][j];
c[i].Set(j,tmp);
}
}
//--- change value
err=0;
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
err=MathMax(err,MathAbs(b[i][j]-c[i][j]));
}
mel=MathMax(mel,err);
//--- ApplyReflectionFromTheRight
for(i=1;i<=n;i++)
{
x[i]=2*CMath::RandomReal()-1;
v[i]=x[i];
}
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
a[i].Set(j,2*CMath::RandomReal()-1);
b[i].Set(j,a[i][j]);
}
}
//--- function call
CReflections::GenerateReflection(v,n,tau);
beta=v[1];
v[1]=1;
//--- function call
CReflections::ApplyReflectionFromTheRight(b,tau,v,1,m,1,n,work);
//--- change value
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
//--- check
if(i==j)
h[i].Set(j,1-tau*v[i]*v[j]);
else
h[i].Set(j,-(tau*v[i]*v[j]));
}
}
//--- calculation
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
tmp=0.0;
for(i_=1;i_<=n;i_++)
tmp+=a[i][i_]*h[i_][j];
c[i].Set(j,tmp);
}
}
err=0;
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
err=MathMax(err,MathAbs(b[i][j]-c[i][j]));
}
mer=MathMax(mer,err);
}
}
}
//--- Overflow crash test
ArrayResize(x,11);
ArrayResize(v,11);
for(i=1;i<=10;i++)
v[i]=CMath::m_maxrealnumber*0.01*(2*CMath::RandomReal()-1);
//--- function call
CReflections::GenerateReflection(v,10,tau);
result=(meg<=threshold && mel<=threshold) && mer<=threshold;
//--- check
if(!silent)
{
Print("TESTING REFLECTIONS");
Print("Pass count is ");
Print("{0,0:d}",passcount);
Print("Generate absolute error is ");
Print("{0,5:E3}",meg);
Print("Apply(Left) absolute error is ");
Print("{0,5:E3}",mel);
Print("Apply(Right) absolute error is ");
Print("{0,5:E3}",mer);
Print("Overflow crash test passed");
//--- check
if(result)
Print("TEST PASSED");
else
Print("TEST FAILED");
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Testing class CComplexReflections |
//+------------------------------------------------------------------+
class CTestCReflectionsUnit
{
public:
//--- constructor, destructor
CTestCReflectionsUnit(void);
~CTestCReflectionsUnit(void);
//--- public method
static bool TestCReflections(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestCReflectionsUnit::CTestCReflectionsUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestCReflectionsUnit::~CTestCReflectionsUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CComplexReflections |
//+------------------------------------------------------------------+
static bool CTestCReflectionsUnit::TestCReflections(const bool silent)
{
//--- create variables
int i=0;
int j=0;
int n=0;
int m=0;
int maxmn=0;
complex tmp=0;
complex beta=0;
complex tau=0;
double err=0;
double mer=0;
double mel=0;
double meg=0;
int pass=0;
int passcount=0;
bool waserrors;
double threshold=0;
int i_=0;
//--- create arrays
complex x[];
complex v[];
complex work[];
//--- create matrix
CMatrixComplex h;
CMatrixComplex a;
CMatrixComplex b;
CMatrixComplex c;
//--- initialization
threshold=1000*CMath::m_machineepsilon;
passcount=1000;
mer=0;
mel=0;
meg=0;
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- Task
n=1+CMath::RandomInteger(10);
m=1+CMath::RandomInteger(10);
maxmn=MathMax(m,n);
//--- Initialize
ArrayResize(x,maxmn+1);
ArrayResize(v,maxmn+1);
ArrayResize(work,maxmn+1);
h.Resize(maxmn+1,maxmn+1);
a.Resize(maxmn+1,maxmn+1);
b.Resize(maxmn+1,maxmn+1);
c.Resize(maxmn+1,maxmn+1);
//--- GenerateReflection
for(i=1;i<=n;i++)
{
x[i].re=2*CMath::RandomReal()-1;
x[i].im=2*CMath::RandomReal()-1;
v[i]=x[i];
}
//--- function call
CComplexReflections::ComplexGenerateReflection(v,n,tau);
//--- change values
beta=v[1];
v[1]=1;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
//--- check
if(i==j)
h[i].Set(j,-tau*v[i]*CMath::Conj(v[j])+1);
else
h[i].Set(j,-(tau*v[i]*CMath::Conj(v[j])));
}
}
//--- calculation
err=0;
for(i=1;i<=n;i++)
{
tmp=0.0;
for(i_=1;i_<=n;i_++)
tmp+=CMath::Conj(h[i_][i])*x[i_];
//--- check
if(i==1)
err=MathMax(err,CMath::AbsComplex(tmp-beta));
else
err=MathMax(err,CMath::AbsComplex(tmp));
}
err=MathMax(err,MathAbs(beta.im));
meg=MathMax(meg,err);
//--- ApplyReflectionFromTheLeft
for(i=1;i<=m;i++)
{
x[i].re=2*CMath::RandomReal()-1;
x[i].im=2*CMath::RandomReal()-1;
v[i]=x[i];
}
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
b[i].Set(j,a[i][j]);
}
}
//--- function call
CComplexReflections::ComplexGenerateReflection(v,m,tau);
beta=v[1];
v[1]=1;
//--- function call
CComplexReflections::ComplexApplyReflectionFromTheLeft(b,tau,v,1,m,1,n,work);
//--- change values
for(i=1;i<=m;i++)
{
for(j=1;j<=m;j++)
{
//--- check
if(i==j)
h[i].Set(j,-tau*v[i]*CMath::Conj(v[j])+1);
else
h[i].Set(j,-(tau*v[i]*CMath::Conj(v[j])));
}
}
//--- calculation
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
tmp=0.0;
for(i_=1;i_<=m;i_++)
tmp+=h[i][i_]*a[i_][j];
c[i].Set(j,tmp);
}
}
err=0;
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
err=MathMax(err,CMath::AbsComplex(b[i][j]-c[i][j]));
}
mel=MathMax(mel,err);
//--- ApplyReflectionFromTheRight
for(i=1;i<=n;i++)
{
x[i]=2*CMath::RandomReal()-1;
v[i]=x[i];
}
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
a[i].Set(j,2*CMath::RandomReal()-1);
b[i].Set(j,a[i][j]);
}
}
//--- function call
CComplexReflections::ComplexGenerateReflection(v,n,tau);
beta=v[1];
v[1]=1;
//--- function call
CComplexReflections::ComplexApplyReflectionFromTheRight(b,tau,v,1,m,1,n,work);
//--- change values
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
//--- check
if(i==j)
h[i].Set(j,-tau*v[i]*CMath::Conj(v[j])+1);
else
h[i].Set(j,-(tau*v[i]*CMath::Conj(v[j])));
}
}
//--- calculation
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
{
tmp=0.0;
for(i_=1;i_<=n;i_++)
tmp+=a[i][i_]*h[i_][j];
c[i].Set(j,tmp);
}
}
err=0;
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
err=MathMax(err,CMath::AbsComplex(b[i][j]-c[i][j]));
}
mer=MathMax(mer,err);
}
//--- Overflow crash test
ArrayResize(x,11);
ArrayResize(v,11);
for(i=1;i<=10;i++)
v[i]=CMath::m_maxrealnumber*0.01*(2*CMath::RandomReal()-1);
//--- function call
CComplexReflections::ComplexGenerateReflection(v,10,tau);
//--- report
waserrors=(meg>threshold || mel>threshold) || mer>threshold;
//--- check
if(!silent)
{
Print("TESTING COMPLEX REFLECTIONS");
Print("Generate error: ");
Print("{0,5:E3}",meg);
Print("Apply(L) error: ");
Print("{0,5:E3}",mel);
Print("Apply(R) error: ");
Print("{0,5:E3}",mer);
Print("Threshold: ");
Print("{0,5:E3}",threshold);
Print("Overflow crash test: PASSED");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CSblas |
//+------------------------------------------------------------------+
class CTestSblasUnit
{
public:
//--- constructor, destructor
CTestSblasUnit(void);
~CTestSblasUnit(void);
//--- public method
static bool TestSblas(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestSblasUnit::CTestSblasUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestSblasUnit::~CTestSblasUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CSblas |
//+------------------------------------------------------------------+
static bool CTestSblasUnit::TestSblas(const bool silent)
{
//--- create variables
int n=0;
int maxn=0;
int i=0;
int j=0;
int i1=0;
int i2=0;
bool waserrors;
double mverr=0;
double threshold=0;
double alpha=0;
double v=0;
int i_=0;
int i1_=0;
//--- create arrays
double x[];
double y1[];
double y2[];
double y3[];
//--- create matrix
CMatrixDouble a;
CMatrixDouble ua;
CMatrixDouble la;
//--- initialization
mverr=0;
waserrors=false;
maxn=10;
threshold=1000*CMath::m_machineepsilon;
//--- Test MV
for(n=2;n<=maxn;n++)
{
//--- allocation
a.Resize(n+1,n+1);
ua.Resize(n+1,n+1);
la.Resize(n+1,n+1);
ArrayResize(x,n+1);
ArrayResize(y1,n+1);
ArrayResize(y2,n+1);
ArrayResize(y3,n+1);
//--- fill A,UA,LA
for(i=1;i<=n;i++)
{
a[i].Set(i,2*CMath::RandomReal()-1);
for(j=i+1;j<=n;j++)
{
a[i].Set(j,2*CMath::RandomReal()-1);
a[j].Set(i,a[i][j]);
}
}
//--- change values
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
ua[i].Set(j,0);
}
//--- change values
for(i=1;i<=n;i++)
{
for(j=i;j<=n;j++)
ua[i].Set(j,a[i][j]);
}
//--- change values
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
la[i].Set(j,0);
}
//--- change values
for(i=1;i<=n;i++)
{
for(j=1;j<=i;j++)
la[i].Set(j,a[i][j]);
}
//--- test on different I1,I2
for(i1=1;i1<=n;i1++)
{
for(i2=i1;i2<=n;i2++)
{
//--- Fill X,choose Alpha
for(i=1;i<=i2-i1+1;i++)
x[i]=2*CMath::RandomReal()-1;
alpha=2*CMath::RandomReal()-1;
//--- calculate A*x,UA*x,LA*x
for(i=i1;i<=i2;i++)
{
i1_=1-i1;
v=0.0;
for(i_=i1;i_<=i2;i_++)
v+=a[i][i_]*x[i_+i1_];
y1[i-i1+1]=alpha*v;
}
//--- function call
CSblas::SymmetricMatrixVectorMultiply(ua,true,i1,i2,x,alpha,y2);
//--- function call
CSblas::SymmetricMatrixVectorMultiply(la,false,i1,i2,x,alpha,y3);
//--- Calculate error
for(i_=1;i_<=i2-i1+1;i_++)
y2[i_]=y2[i_]-y1[i_];
v=0.0;
for(i_=1;i_<=i2-i1+1;i_++)
v+=y2[i_]*y2[i_];
//--- search errors
mverr=MathMax(mverr,MathSqrt(v));
for(i_=1;i_<=i2-i1+1;i_++)
y3[i_]=y3[i_]-y1[i_];
v=0.0;
for(i_=1;i_<=i2-i1+1;i_++)
v+=y3[i_]*y3[i_];
//--- search errors
mverr=MathMax(mverr,MathSqrt(v));
}
}
}
//--- report
waserrors=mverr>threshold;
//--- check
if(!silent)
{
Print("TESTING SYMMETRIC BLAS");
Print("MV error: ");
Print("{0,5:E3}",mverr);
Print("Threshold: ");
Print("{0,5:E3}",threshold);
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class COrtFac |
//+------------------------------------------------------------------+
class CTestOrtFacUnit
{
private:
//--- private methods
static double RMatrixDiff(CMatrixDouble &a,CMatrixDouble &b,const int m,const int n);
static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b);
static void RMatrixFillSparseA(CMatrixDouble &a,const int m,const int n,const double sparcity);
static void CMatrixFillSparseA(CMatrixComplex &a,const int m,const int n,const double sparcity);
static void InternalMatrixMatrixMultiply(CMatrixDouble &a,const int ai1,const int ai2,const int aj1,const int aj2,const bool transa,CMatrixDouble &b,const int bi1,const int bi2,const int bj1,const int bj2,const bool transb,CMatrixDouble &c,const int ci1,const int ci2,const int cj1,const int cj2);
static void TestRQRProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &qrerrors);
static void TestCQRProblem(CMatrixComplex &a,const int m,const int n,const double threshold,bool &qrerrors);
static void TestRLQProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &lqerrors);
static void TestCLQProblem(CMatrixComplex &a,const int m,const int n,const double threshold,bool &lqerrors);
static void TestRBdProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &bderrors);
static void TestRHessProblem(CMatrixDouble &a,const int n,const double threshold,bool &hesserrors);
static void TestRTdProblem(CMatrixDouble &a,const int n,const double threshold,bool &tderrors);
static void TestCTdProblem(CMatrixComplex &a,const int n,const double threshold,bool &tderrors);
public:
//--- constructor, destructor
CTestOrtFacUnit(void);
~CTestOrtFacUnit(void);
//--- public method
static bool TestOrtFac(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestOrtFacUnit::CTestOrtFacUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestOrtFacUnit::~CTestOrtFacUnit(void)
{
}
//+------------------------------------------------------------------+
//| Main unittest subroutine |
//+------------------------------------------------------------------+
static bool CTestOrtFacUnit::TestOrtFac(const bool silent)
{
int maxmn=0;
double threshold=0;
int passcount=0;
int mx=0;
int m=0;
int n=0;
int pass=0;
int i=0;
int j=0;
bool rqrerrors;
bool rlqerrors;
bool cqrerrors;
bool clqerrors;
bool rbderrors;
bool rhesserrors;
bool rtderrors;
bool ctderrors;
bool waserrors;
//--- create matrix
CMatrixDouble ra;
CMatrixComplex ca;
//--- initialization
waserrors=false;
rqrerrors=false;
rlqerrors=false;
cqrerrors=false;
clqerrors=false;
rbderrors=false;
rhesserrors=false;
rtderrors=false;
ctderrors=false;
maxmn=3*CAblas::AblasBlockSize()+1;
passcount=1;
threshold=5*1000*CMath::m_machineepsilon;
//--- Different problems
for(mx=1;mx<=maxmn;mx++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- Rectangular factorizations: QR,LQ,bidiagonal
//--- Matrix types: zero,dense,sparse
n=1+CMath::RandomInteger(mx);
m=1+CMath::RandomInteger(mx);
//--- check
if(CMath::RandomReal()>0.5)
n=mx;
else
m=mx;
//--- allocation
ra.Resize(m,n);
ca.Resize(m,n);
//--- change values
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
ra[i].Set(j,0);
ca[i].Set(j,0);
}
}
//--- function calls
TestRQRProblem(ra,m,n,threshold,rqrerrors);
TestRLQProblem(ra,m,n,threshold,rlqerrors);
TestCQRProblem(ca,m,n,threshold,cqrerrors);
TestCLQProblem(ca,m,n,threshold,clqerrors);
TestRBdProblem(ra,m,n,threshold,rbderrors);
//--- change values
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
ra[i].Set(j,2*CMath::RandomReal()-1);
ca[i].SetRe(j,2*CMath::RandomReal()-1);
ca[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- function calls
TestRQRProblem(ra,m,n,threshold,rqrerrors);
TestRLQProblem(ra,m,n,threshold,rlqerrors);
TestCQRProblem(ca,m,n,threshold,cqrerrors);
TestCLQProblem(ca,m,n,threshold,clqerrors);
TestRBdProblem(ra,m,n,threshold,rbderrors);
//--- function calls
RMatrixFillSparseA(ra,m,n,0.95);
CMatrixFillSparseA(ca,m,n,0.95);
//--- function calls
TestRQRProblem(ra,m,n,threshold,rqrerrors);
TestRLQProblem(ra,m,n,threshold,rlqerrors);
TestCQRProblem(ca,m,n,threshold,cqrerrors);
TestCLQProblem(ca,m,n,threshold,clqerrors);
TestRBdProblem(ra,m,n,threshold,rbderrors);
//--- Square factorizations: Hessenberg,tridiagonal
//--- Matrix types: zero,dense,sparse
ra.Resize(mx,mx);
ca.Resize(mx,mx);
//--- change values
for(i=0;i<=mx-1;i++)
{
for(j=0;j<=mx-1;j++)
{
ra[i].Set(j,0);
ca[i].Set(j,0);
}
}
//--- function call
TestRHessProblem(ra,mx,threshold,rhesserrors);
//--- change values
for(i=0;i<=mx-1;i++)
{
for(j=0;j<=mx-1;j++)
{
ra[i].Set(j,2*CMath::RandomReal()-1);
ca[i].SetRe(j,2*CMath::RandomReal()-1);
ca[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- function calls
TestRHessProblem(ra,mx,threshold,rhesserrors);
RMatrixFillSparseA(ra,mx,mx,0.95);
CMatrixFillSparseA(ca,mx,mx,0.95);
TestRHessProblem(ra,mx,threshold,rhesserrors);
//--- Symetric factorizations: tridiagonal
//--- Matrix types: zero,dense,sparse
ra.Resize(mx,mx);
ca.Resize(mx,mx);
//--- change values
for(i=0;i<=mx-1;i++)
{
for(j=0;j<=mx-1;j++)
{
ra[i].Set(j,0);
ca[i].Set(j,0);
}
}
//--- function calls
TestRTdProblem(ra,mx,threshold,rtderrors);
TestCTdProblem(ca,mx,threshold,ctderrors);
//--- change values
for(i=0;i<=mx-1;i++)
{
for(j=i;j<=mx-1;j++)
{
ra[i].Set(j,2*CMath::RandomReal()-1);
ca[i].SetRe(j,2*CMath::RandomReal()-1);
ca[i].SetIm(j,2*CMath::RandomReal()-1);
ra[j].Set(i,ra[i][j]);
ca[j].Set(i,CMath::Conj(ca[i][j]));
}
}
for(i=0;i<=mx-1;i++)
ca[i].Set(i,2*CMath::RandomReal()-1);
//--- function calls
TestRTdProblem(ra,mx,threshold,rtderrors);
TestCTdProblem(ca,mx,threshold,ctderrors);
RMatrixFillSparseA(ra,mx,mx,0.95);
CMatrixFillSparseA(ca,mx,mx,0.95);
//--- change values
for(i=0;i<=mx-1;i++)
{
for(j=i;j<=mx-1;j++)
{
ra[j].Set(i,ra[i][j]);
ca[j].Set(i,CMath::Conj(ca[i][j]));
}
}
for(i=0;i<=mx-1;i++)
ca[i].Set(i,2*CMath::RandomReal()-1);
//--- function calls
TestRTdProblem(ra,mx,threshold,rtderrors);
TestCTdProblem(ca,mx,threshold,ctderrors);
}
}
//--- report
waserrors=((((((rqrerrors || rlqerrors) || cqrerrors) || clqerrors) || rbderrors) || rhesserrors) || rtderrors) || ctderrors;
//--- check
if(!silent)
{
Print("TESTING ORTFAC UNIT");
Print("RQR ERRORS: ");
//--- check
if(!rqrerrors)
Print("OK");
else
Print("FAILED");
Print("RLQ ERRORS: ");
//--- check
if(!rlqerrors)
Print("OK");
else
Print("FAILED");
Print("CQR ERRORS: ");
//--- check
if(!cqrerrors)
Print("OK");
else
Print("FAILED");
Print("CLQ ERRORS: ");
//--- check
if(!clqerrors)
Print("OK");
else
Print("FAILED");
Print("RBD ERRORS: ");
//--- check
if(!rbderrors)
Print("OK");
else
Print("FAILED");
Print("RHESS ERRORS: ");
//--- check
if(!rhesserrors)
Print("OK");
else
Print("FAILED");
Print("RTD ERRORS: ");
//--- check
if(!rtderrors)
Print("OK");
else
Print("FAILED");
Print("CTD ERRORS: ");
//--- check
if(!ctderrors)
Print("OK");
else
Print("FAILED");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Diff |
//+------------------------------------------------------------------+
static double CTestOrtFacUnit::RMatrixDiff(CMatrixDouble &a,CMatrixDouble &b,
const int m,const int n)
{
//--- create variables
double result=0;
int i=0;
int j=0;
//--- calculation
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
result=MathMax(result,MathAbs(b[i][j]-a[i][j]));
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
static void CTestOrtFacUnit::RMatrixMakeACopy(CMatrixDouble &a,const int m,
const int n,CMatrixDouble &b)
{
//--- create variables
int i=0;
int j=0;
//--- allocation
b.Resize(m,n);
//--- copy
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
b[i].Set(j,a[i][j]);
}
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
static void CTestOrtFacUnit::CMatrixMakeACopy(CMatrixComplex &a,const int m,
const int n,CMatrixComplex &b)
{
//--- create variables
int i=0;
int j=0;
//--- allocation
b.Resize(m,n);
//--- copy
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
b[i].Set(j,a[i][j]);
}
}
//+------------------------------------------------------------------+
//| Sparse fill |
//+------------------------------------------------------------------+
static void CTestOrtFacUnit::RMatrixFillSparseA(CMatrixDouble &a,const int m,
const int n,const double sparcity)
{
//--- create variables
int i=0;
int j=0;
//--- calculation
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(CMath::RandomReal()>=sparcity)
a[i].Set(j,2*CMath::RandomReal()-1);
else
a[i].Set(j,0);
}
}
}
//+------------------------------------------------------------------+
//| Sparse fill |
//+------------------------------------------------------------------+
static void CTestOrtFacUnit::CMatrixFillSparseA(CMatrixComplex &a,const int m,
const int n,const double sparcity)
{
//--- create variables
int i=0;
int j=0;
//--- calculation
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(CMath::RandomReal()>=sparcity)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
}
else
a[i].Set(j,0);
}
}
}
//+------------------------------------------------------------------+
//| Matrix multiplication |
//+------------------------------------------------------------------+
static void CTestOrtFacUnit::InternalMatrixMatrixMultiply(CMatrixDouble &a,
const int ai1,
const int ai2,
const int aj1,
const int aj2,
const bool transa,
CMatrixDouble &b,
const int bi1,
const int bi2,
const int bj1,
const int bj2,
const bool transb,
CMatrixDouble &c,
const int ci1,
const int ci2,
const int cj1,
const int cj2)
{
//--- create variables
int arows=0;
int acols=0;
int brows=0;
int bcols=0;
int crows=0;
int ccols=0;
int i=0;
int j=0;
int k=0;
int l=0;
int r=0;
double v=0;
double beta=0;
double alpha=0;
int i_=0;
int i1_=0;
//--- create array
double work[];
//--- Pre-setup
k=MathMax(ai2-ai1+1,aj2-aj1+1);
k=MathMax(k,bi2-bi1+1);
k=MathMax(k,bj2-bj1+1);
//--- allocation
ArrayResize(work,k+1);
//--- initialization
beta=0;
alpha=1;
//--- Setup
if(!transa)
{
arows=ai2-ai1+1;
acols=aj2-aj1+1;
}
else
{
arows=aj2-aj1+1;
acols=ai2-ai1+1;
}
//--- check
if(!transb)
{
brows=bi2-bi1+1;
bcols=bj2-bj1+1;
}
else
{
brows=bj2-bj1+1;
bcols=bi2-bi1+1;
}
//--- check
if(!CAp::Assert(acols==brows,"MatrixMatrixMultiply: incorrect matrix sizes!"))
return;
//--- check
if(((arows<=0 || acols<=0) || brows<=0) || bcols<=0)
return;
//--- change values
crows=arows;
ccols=bcols;
//--- Test WORK
i=MathMax(arows,acols);
i=MathMax(brows,i);
i=MathMax(i,bcols);
work[1]=0;
work[i]=0;
//--- Prepare C
if(beta==0.0)
{
for(i=ci1;i<=ci2;i++)
{
for(j=cj1;j<=cj2;j++)
c[i].Set(j,0);
}
}
else
{
for(i=ci1;i<=ci2;i++)
{
for(i_=cj1;i_<=cj2;i_++)
c[i].Set(i_,beta*c[i][i_]);
}
}
//--- A*B
if(!transa && !transb)
{
for(l=ai1;l<=ai2;l++)
{
for(r=bi1;r<=bi2;r++)
{
//--- change values
v=alpha*a[l][aj1+r-bi1];
k=ci1+l-ai1;
i1_=bj1-cj1;
//--- calculation
for(i_=cj1;i_<=cj2;i_++)
c[k].Set(i_,c[k][i_]+v*b[r][i_+i1_]);
}
}
return;
}
//--- A*B'
if(!transa && transb)
{
//--- check
if(arows*acols<brows*bcols)
{
for(r=bi1;r<=bi2;r++)
{
for(l=ai1;l<=ai2;l++)
{
//--- change values
i1_=bj1-aj1;
v=0.0;
for(i_=aj1;i_<=aj2;i_++)
v+=a[l][i_]*b[r][i_+i1_];
//--- calculation
c[ci1+l-ai1].Set(cj1+r-bi1,c[ci1+l-ai1][cj1+r-bi1]+alpha*v);
}
}
return;
}
else
{
for(l=ai1;l<=ai2;l++)
{
for(r=bi1;r<=bi2;r++)
{
//--- change values
i1_=bj1-aj1;
v=0.0;
for(i_=aj1;i_<=aj2;i_++)
v+=a[l][i_]*b[r][i_+i1_];
//--- calculation
c[ci1+l-ai1].Set(cj1+r-bi1,c[ci1+l-ai1][cj1+r-bi1]+alpha*v);
}
}
return;
}
}
//--- A'*B
if(transa && !transb)
{
for(l=aj1;l<=aj2;l++)
{
for(r=bi1;r<=bi2;r++)
{
//--- change values
v=alpha*a[ai1+r-bi1][l];
k=ci1+l-aj1;
i1_=bj1-cj1;
//--- calculation
for(i_=cj1;i_<=cj2;i_++)
c[k].Set(i_,c[k][i_]+v*b[r][i_+i1_]);
}
}
return;
}
//--- A'*B'
if(transa && transb)
{
//--- check
if(arows*acols<brows*bcols)
{
for(r=bi1;r<=bi2;r++)
{
for(i=1;i<=crows;i++)
work[i]=0.0;
for(l=ai1;l<=ai2;l++)
{
//--- change values
v=alpha*b[r][bj1+l-ai1];
k=cj1+r-bi1;
i1_=aj1-1;
//--- calculation
for(i_=1;i_<=crows;i_++)
work[i_]=work[i_]+v*a[l][i_+i1_];
}
//--- calculation
i1_=1-ci1;
for(i_=ci1;i_<=ci2;i_++)
c[i_].Set(k,c[i_][k]+work[i_+i1_]);
}
return;
}
else
{
for(l=aj1;l<=aj2;l++)
{
//--- change values
k=ai2-ai1+1;
i1_=ai1-1;
for(i_=1;i_<=k;i_++)
work[i_]=a[i_+i1_][l];
//--- calculation
for(r=bi1;r<=bi2;r++)
{
//--- change values
i1_=bj1-1;
v=0.0;
for(i_=1;i_<=k;i_++)
v+=work[i_]*b[r][i_+i1_];
c[ci1+l-aj1].Set(cj1+r-bi1,c[ci1+l-aj1][cj1+r-bi1]+alpha*v);
}
}
return;
}
}
}
//+------------------------------------------------------------------+
//| Problem testing |
//+------------------------------------------------------------------+
static void CTestOrtFacUnit::TestRQRProblem(CMatrixDouble &a,const int m,
const int n,const double threshold,
bool &qrerrors)
{
//--- create variables
int i=0;
int j=0;
int k=0;
double v=0;
int i_=0;
//--- create array
double taub[];
//--- create matrix
CMatrixDouble b;
CMatrixDouble q;
CMatrixDouble r;
CMatrixDouble q2;
//--- Test decompose-and-unpack error
RMatrixMakeACopy(a,m,n,b);
//--- function call
COrtFac::RMatrixQR(b,m,n,taub);
COrtFac::RMatrixQRUnpackQ(b,m,n,taub,m,q);
COrtFac::RMatrixQRUnpackR(b,m,n,r);
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
v=0.0;
for(i_=0;i_<=m-1;i_++)
v+=q[i][i_]*r[i_][j];
qrerrors=qrerrors || MathAbs(v-a[i][j])>threshold;
}
}
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=MathMin(i,n-1)-1;j++)
qrerrors=qrerrors || r[i][j]!=0.0;
}
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=m-1;i_++)
v+=q[i][i_]*q[j][i_];
//--- check
if(i==j)
v=v-1;
qrerrors=qrerrors || MathAbs(v)>=threshold;
}
}
//--- Test for other errors
k=1+CMath::RandomInteger(m);
//--- function call
COrtFac::RMatrixQRUnpackQ(b,m,n,taub,k,q2);
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=k-1;j++)
qrerrors=qrerrors || MathAbs(q2[i][j]-q[i][j])>10*CMath::m_machineepsilon;
}
}
//+------------------------------------------------------------------+
//| Problem testing |
//+------------------------------------------------------------------+
static void CTestOrtFacUnit::TestCQRProblem(CMatrixComplex &a,const int m,
const int n,const double threshold,
bool &qrerrors)
{
//--- create variables
int i=0;
int j=0;
int k=0;
complex v=0;
int i_=0;
//--- create array
complex taub[];
//--- create matrix
CMatrixComplex b;
CMatrixComplex q;
CMatrixComplex r;
CMatrixComplex q2;
//--- Test decompose-and-unpack error
CMatrixMakeACopy(a,m,n,b);
//--- function calls
COrtFac::CMatrixQR(b,m,n,taub);
COrtFac::CMatrixQRUnpackQ(b,m,n,taub,m,q);
COrtFac::CMatrixQRUnpackR(b,m,n,r);
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=m-1;i_++)
v+=q[i][i_]*r[i_][j];
qrerrors=qrerrors || CMath::AbsComplex(v-a[i][j])>threshold;
}
}
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=MathMin(i,n-1)-1;j++)
qrerrors=qrerrors || r[i][j]!=0;
}
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=m-1;i_++)
v+=q[i][i_]*CMath::Conj(q[j][i_]);
//--- check
if(i==j)
v=v-1;
qrerrors=qrerrors || CMath::AbsComplex(v)>=threshold;
}
}
//--- Test for other errors
k=1+CMath::RandomInteger(m);
//--- function call
COrtFac::CMatrixQRUnpackQ(b,m,n,taub,k,q2);
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=k-1;j++)
qrerrors=qrerrors || CMath::AbsComplex(q2[i][j]-q[i][j])>10*CMath::m_machineepsilon;
}
}
//+------------------------------------------------------------------+
//| Problem testing |
//+------------------------------------------------------------------+
static void CTestOrtFacUnit::TestRLQProblem(CMatrixDouble &a,const int m,
const int n,const double threshold,
bool &lqerrors)
{
//--- create variables
int i=0;
int j=0;
int k=0;
double v=0;
int i_=0;
//--- create array
double taub[];
//--- create matrix
CMatrixDouble b;
CMatrixDouble q;
CMatrixDouble l;
CMatrixDouble q2;
//--- Test decompose-and-unpack error
RMatrixMakeACopy(a,m,n,b);
//--- function calls
COrtFac::RMatrixLQ(b,m,n,taub);
COrtFac::RMatrixLQUnpackQ(b,m,n,taub,n,q);
COrtFac::RMatrixLQUnpackL(b,m,n,l);
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=l[i][i_]*q[i_][j];
lqerrors=lqerrors || MathAbs(v-a[i][j])>=threshold;
}
}
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=MathMin(i,n-1)+1;j<=n-1;j++)
lqerrors=lqerrors || l[i][j]!=0.0;
}
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=q[i][i_]*q[j][i_];
//--- check
if(i==j)
v=v-1;
lqerrors=lqerrors || MathAbs(v)>=threshold;
}
}
//--- Test for other errors
k=1+CMath::RandomInteger(n);
//--- function call
COrtFac::RMatrixLQUnpackQ(b,m,n,taub,k,q2);
//--- search errors
for(i=0;i<=k-1;i++)
{
for(j=0;j<=n-1;j++)
lqerrors=lqerrors || MathAbs(q2[i][j]-q[i][j])>10*CMath::m_machineepsilon;
}
}
//+------------------------------------------------------------------+
//| Problem testing |
//+------------------------------------------------------------------+
static void CTestOrtFacUnit::TestCLQProblem(CMatrixComplex &a,const int m,
const int n,const double threshold,
bool &lqerrors)
{
//--- create variables
int i=0;
int j=0;
int k=0;
complex v=0;
int i_=0;
//--- create array
complex taub[];
//--- create matrix
CMatrixComplex b;
CMatrixComplex q;
CMatrixComplex l;
CMatrixComplex q2;
//--- Test decompose-and-unpack error
CMatrixMakeACopy(a,m,n,b);
//--- function calls
COrtFac::CMatrixLQ(b,m,n,taub);
COrtFac::CMatrixLQUnpackQ(b,m,n,taub,n,q);
COrtFac::CMatrixLQUnpackL(b,m,n,l);
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=l[i][i_]*q[i_][j];
lqerrors=lqerrors || CMath::AbsComplex(v-a[i][j])>=threshold;
}
}
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=MathMin(i,n-1)+1;j<=n-1;j++)
lqerrors=lqerrors || l[i][j]!=0;
}
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=q[i][i_]*CMath::Conj(q[j][i_]);
//--- check
if(i==j)
v=v-1;
lqerrors=lqerrors || CMath::AbsComplex(v)>=threshold;
}
}
//--- Test for other errors
k=1+CMath::RandomInteger(n);
//--- function call
COrtFac::CMatrixLQUnpackQ(b,m,n,taub,k,q2);
//--- search errors
for(i=0;i<=k-1;i++)
{
for(j=0;j<=n-1;j++)
lqerrors=lqerrors || CMath::AbsComplex(q2[i][j]-q[i][j])>10*CMath::m_machineepsilon;
}
}
//+------------------------------------------------------------------+
//| Problem testing |
//+------------------------------------------------------------------+
static void CTestOrtFacUnit::TestRBdProblem(CMatrixDouble &a,const int m,
const int n,const double threshold,
bool &bderrors)
{
//--- create variables
int i=0;
int j=0;
int k=0;
bool up;
double v=0;
int mtsize=0;
int i_=0;
//--- create arrays
double taup[];
double tauq[];
double d[];
double e[];
//--- create matrix
CMatrixDouble t;
CMatrixDouble pt;
CMatrixDouble q;
CMatrixDouble r;
CMatrixDouble bd;
CMatrixDouble x;
CMatrixDouble r1;
CMatrixDouble r2;
//--- Bidiagonal decomposition error
RMatrixMakeACopy(a,m,n,t);
//--- function calls
COrtFac::RMatrixBD(t,m,n,tauq,taup);
COrtFac::RMatrixBDUnpackQ(t,m,n,tauq,m,q);
COrtFac::RMatrixBDUnpackPT(t,m,n,taup,n,pt);
COrtFac::RMatrixBDUnpackDiagonals(t,m,n,up,d,e);
//--- allocation
bd.Resize(m,n);
//--- change values
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
bd[i].Set(j,0);
}
for(i=0;i<=MathMin(m,n)-1;i++)
bd[i].Set(i,d[i]);
//--- check
if(up)
{
for(i=0;i<=MathMin(m,n)-2;i++)
bd[i].Set(i+1,e[i]);
}
else
{
for(i=0;i<=MathMin(m,n)-2;i++)
bd[i+1].Set(i,e[i]);
}
//--- allocation
r.Resize(m,n);
//--- calculation
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
v=0.0;
for(i_=0;i_<=m-1;i_++)
v+=q[i][i_]*bd[i_][j];
r[i].Set(j,v);
}
}
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=r[i][i_]*pt[i_][j];
bderrors=bderrors || MathAbs(v-a[i][j])>threshold;
}
}
//--- Orthogonality test for Q/PT
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=m-1;i_++)
v+=q[i_][i]*q[i_][j];
//--- check
if(i==j)
bderrors=bderrors || MathAbs(v-1)>threshold;
else
bderrors=bderrors || MathAbs(v)>threshold;
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=pt[i][i_]*pt[j][i_];
//--- check
if(i==j)
bderrors=bderrors || MathAbs(v-1)>threshold;
else
bderrors=bderrors || MathAbs(v)>threshold;
}
}
//--- Partial unpacking test
k=1+CMath::RandomInteger(m);
//--- function call
COrtFac::RMatrixBDUnpackQ(t,m,n,tauq,k,r);
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=k-1;j++)
bderrors=bderrors || MathAbs(r[i][j]-q[i][j])>10*CMath::m_machineepsilon;
}
k=1+CMath::RandomInteger(n);
//--- function call
COrtFac::RMatrixBDUnpackPT(t,m,n,taup,k,r);
//--- search errors
for(i=0;i<=k-1;i++)
{
for(j=0;j<=n-1;j++)
bderrors=bderrors || r[i][j]-pt[i][j]!=0.0;
}
//--- Multiplication test
x.Resize(MathMax(m,n),MathMax(m,n));
r.Resize(MathMax(m,n),MathMax(m,n));
r1.Resize(MathMax(m,n),MathMax(m,n));
r2.Resize(MathMax(m,n),MathMax(m,n));
//--- change values
for(i=0;i<=MathMax(m,n)-1;i++)
{
for(j=0;j<=MathMax(m,n)-1;j++)
x[i].Set(j,2*CMath::RandomReal()-1);
}
mtsize=1+CMath::RandomInteger(MathMax(m,n));
//--- function calls
RMatrixMakeACopy(x,mtsize,m,r);
InternalMatrixMatrixMultiply(r,0,mtsize-1,0,m-1,false,q,0,m-1,0,m-1,false,r1,0,mtsize-1,0,m-1);
RMatrixMakeACopy(x,mtsize,m,r2);
COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,mtsize,m,true,false);
//--- search errors
bderrors=bderrors || RMatrixDiff(r1,r2,mtsize,m)>threshold;
//--- function calls
RMatrixMakeACopy(x,mtsize,m,r);
InternalMatrixMatrixMultiply(r,0,mtsize-1,0,m-1,false,q,0,m-1,0,m-1,true,r1,0,mtsize-1,0,m-1);
RMatrixMakeACopy(x,mtsize,m,r2);
COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,mtsize,m,true,true);
//--- search errors
bderrors=bderrors || RMatrixDiff(r1,r2,mtsize,m)>threshold;
//--- function calls
RMatrixMakeACopy(x,m,mtsize,r);
InternalMatrixMatrixMultiply(q,0,m-1,0,m-1,false,r,0,m-1,0,mtsize-1,false,r1,0,m-1,0,mtsize-1);
RMatrixMakeACopy(x,m,mtsize,r2);
COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,m,mtsize,false,false);
//--- search errors
bderrors=bderrors || RMatrixDiff(r1,r2,m,mtsize)>threshold;
//--- function calls
RMatrixMakeACopy(x,m,mtsize,r);
InternalMatrixMatrixMultiply(q,0,m-1,0,m-1,true,r,0,m-1,0,mtsize-1,false,r1,0,m-1,0,mtsize-1);
RMatrixMakeACopy(x,m,mtsize,r2);
COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,m,mtsize,false,true);
//--- search errors
bderrors=bderrors || RMatrixDiff(r1,r2,m,mtsize)>threshold;
//--- function calls
RMatrixMakeACopy(x,mtsize,n,r);
InternalMatrixMatrixMultiply(r,0,mtsize-1,0,n-1,false,pt,0,n-1,0,n-1,true,r1,0,mtsize-1,0,n-1);
RMatrixMakeACopy(x,mtsize,n,r2);
COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,mtsize,n,true,false);
//--- search errors
bderrors=bderrors || RMatrixDiff(r1,r2,mtsize,n)>threshold;
//--- function calls
RMatrixMakeACopy(x,mtsize,n,r);
InternalMatrixMatrixMultiply(r,0,mtsize-1,0,n-1,false,pt,0,n-1,0,n-1,false,r1,0,mtsize-1,0,n-1);
RMatrixMakeACopy(x,mtsize,n,r2);
COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,mtsize,n,true,true);
//--- search errors
bderrors=bderrors || RMatrixDiff(r1,r2,mtsize,n)>threshold;
//--- function calls
RMatrixMakeACopy(x,n,mtsize,r);
InternalMatrixMatrixMultiply(pt,0,n-1,0,n-1,true,r,0,n-1,0,mtsize-1,false,r1,0,n-1,0,mtsize-1);
RMatrixMakeACopy(x,n,mtsize,r2);
COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,n,mtsize,false,false);
//--- search errors
bderrors=bderrors || RMatrixDiff(r1,r2,n,mtsize)>threshold;
//--- function calls
RMatrixMakeACopy(x,n,mtsize,r);
InternalMatrixMatrixMultiply(pt,0,n-1,0,n-1,false,r,0,n-1,0,mtsize-1,false,r1,0,n-1,0,mtsize-1);
RMatrixMakeACopy(x,n,mtsize,r2);
COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,n,mtsize,false,true);
//--- search errors
bderrors=bderrors || RMatrixDiff(r1,r2,n,mtsize)>threshold;
}
//+------------------------------------------------------------------+
//| Problem testing |
//+------------------------------------------------------------------+
static void CTestOrtFacUnit::TestRHessProblem(CMatrixDouble &a,const int n,
const double threshold,bool &hesserrors)
{
//--- create variables
int i=0;
int j=0;
double v=0;
int i_=0;
//--- create array
double tau[];
//--- create matrix
CMatrixDouble b;
CMatrixDouble h;
CMatrixDouble q;
CMatrixDouble t1;
CMatrixDouble t2;
//--- function call
RMatrixMakeACopy(a,n,n,b);
//--- Decomposition
COrtFac::RMatrixHessenberg(b,n,tau);
COrtFac::RMatrixHessenbergUnpackQ(b,n,tau,q);
COrtFac::RMatrixHessenbergUnpackH(b,n,h);
//--- Matrix properties
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=q[i_][i]*q[i_][j];
//--- check
if(i==j)
v=v-1;
hesserrors=hesserrors || MathAbs(v)>threshold;
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=i-2;j++)
hesserrors=hesserrors || h[i][j]!=0.0;
}
//--- Decomposition error
t1.Resize(n,n);
t2.Resize(n,n);
//--- function calls
InternalMatrixMatrixMultiply(q,0,n-1,0,n-1,false,h,0,n-1,0,n-1,false,t1,0,n-1,0,n-1);
InternalMatrixMatrixMultiply(t1,0,n-1,0,n-1,false,q,0,n-1,0,n-1,true,t2,0,n-1,0,n-1);
//--- search errors
hesserrors=hesserrors || RMatrixDiff(t2,a,n,n)>threshold;
}
//+------------------------------------------------------------------+
//| Tridiagonal tester |
//+------------------------------------------------------------------+
static void CTestOrtFacUnit::TestRTdProblem(CMatrixDouble &a,const int n,
const double threshold,bool &tderrors)
{
//--- create variables
int i=0;
int j=0;
double v=0;
int i_=0;
//--- create arrays
double tau[];
double d[];
double e[];
//--- create matrix
CMatrixDouble ua;
CMatrixDouble la;
CMatrixDouble t;
CMatrixDouble q;
CMatrixDouble t2;
CMatrixDouble t3;
//--- allocation
ua.Resize(n,n);
la.Resize(n,n);
t.Resize(n,n);
q.Resize(n,n);
t2.Resize(n,n);
t3.Resize(n,n);
//--- fill
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
ua[i].Set(j,0);
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=i;j<=n-1;j++)
ua[i].Set(j,a[i][j]);
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
la[i].Set(j,0);
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=i;j++)
la[i].Set(j,a[i][j]);
}
//--- Test 2tridiagonal: upper
COrtFac::SMatrixTD(ua,n,true,tau,d,e);
COrtFac::SMatrixTDUnpackQ(ua,n,true,tau,q);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
t[i].Set(j,0);
}
for(i=0;i<=n-1;i++)
t[i].Set(i,d[i]);
for(i=0;i<=n-2;i++)
{
t[i].Set(i+1,e[i]);
t[i+1].Set(i,e[i]);
}
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=q[i_][i]*a[i_][j];
t2[i].Set(j,v);
}
}
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=t2[i][i_]*q[i_][j];
t3[i].Set(j,v);
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
tderrors=tderrors || MathAbs(t3[i][j]-t[i][j])>threshold;
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=q[i][i_]*q[j][i_];
//--- check
if(i==j)
v=v-1;
tderrors=tderrors || MathAbs(v)>threshold;
}
}
//--- Test 2tridiagonal: lower
COrtFac::SMatrixTD(la,n,false,tau,d,e);
COrtFac::SMatrixTDUnpackQ(la,n,false,tau,q);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
t[i].Set(j,0);
}
for(i=0;i<=n-1;i++)
t[i].Set(i,d[i]);
for(i=0;i<=n-2;i++)
{
t[i].Set(i+1,e[i]);
t[i+1].Set(i,e[i]);
}
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=q[i_][i]*a[i_][j];
t2[i].Set(j,v);
}
}
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=t2[i][i_]*q[i_][j];
t3[i].Set(j,v);
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
tderrors=tderrors || MathAbs(t3[i][j]-t[i][j])>threshold;
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=q[i][i_]*q[j][i_];
//--- check
if(i==j)
v=v-1;
tderrors=tderrors || MathAbs(v)>threshold;
}
}
}
//+------------------------------------------------------------------+
//| Hermitian problem tester |
//+------------------------------------------------------------------+
static void CTestOrtFacUnit::TestCTdProblem(CMatrixComplex &a,const int n,
const double threshold,bool &tderrors)
{
//--- create variables
int i=0;
int j=0;
complex v=0;
int i_=0;
//--- create arrays
complex tau[];
double d[];
double e[];
//--- create matrix
CMatrixComplex ua;
CMatrixComplex la;
CMatrixComplex t;
CMatrixComplex q;
CMatrixComplex t2;
CMatrixComplex t3;
//--- allocation
ua.Resize(n,n);
la.Resize(n,n);
t.Resize(n,n);
q.Resize(n,n);
t2.Resize(n,n);
t3.Resize(n,n);
//--- fill
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
ua[i].Set(j,0);
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=i;j<=n-1;j++)
ua[i].Set(j,a[i][j]);
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
la[i].Set(j,0);
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=i;j++)
la[i].Set(j,a[i][j]);
}
//--- Test 2tridiagonal: upper
COrtFac::HMatrixTD(ua,n,true,tau,d,e);
COrtFac::HMatrixTDUnpackQ(ua,n,true,tau,q);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
t[i].Set(j,0);
}
for(i=0;i<=n-1;i++)
t[i].Set(i,d[i]);
for(i=0;i<=n-2;i++)
{
t[i].Set(i+1,e[i]);
t[i+1].Set(i,e[i]);
}
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=CMath::Conj(q[i_][i])*a[i_][j];
t2[i].Set(j,v);
}
}
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=t2[i][i_]*q[i_][j];
t3[i].Set(j,v);
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
tderrors=tderrors || CMath::AbsComplex(t3[i][j]-t[i][j])>threshold;
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=q[i][i_]*CMath::Conj(q[j][i_]);
//--- check
if(i==j)
v=v-1;
tderrors=tderrors || CMath::AbsComplex(v)>threshold;
}
}
//--- Test 2tridiagonal: lower
COrtFac::HMatrixTD(la,n,false,tau,d,e);
COrtFac::HMatrixTDUnpackQ(la,n,false,tau,q);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
t[i].Set(j,0);
}
for(i=0;i<=n-1;i++)
t[i].Set(i,d[i]);
for(i=0;i<=n-2;i++)
{
t[i].Set(i+1,e[i]);
t[i+1].Set(i,e[i]);
}
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=CMath::Conj(q[i_][i])*a[i_][j];
t2[i].Set(j,v);
}
}
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=t2[i][i_]*q[i_][j];
t3[i].Set(j,v);
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
tderrors=tderrors || CMath::AbsComplex(t3[i][j]-t[i][j])>threshold;
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=q[i][i_]*CMath::Conj(q[j][i_]);
//--- check
if(i==j)
v=v-1;
tderrors=tderrors || CMath::AbsComplex(v)>threshold;
}
}
}
//+------------------------------------------------------------------+
//| Testing class CEigenVDetect |
//+------------------------------------------------------------------+
class CTestEVDUnit
{
private:
//--- private methods
static void RMatrixFillSparseA(CMatrixDouble &a,const int m,const int n,const double sparcity);
static void CMatrixFillSparseA(CMatrixComplex &a,const int m,const int n,const double sparcity);
static void RMatrixSymmetricSplit(CMatrixDouble &a,const int n,CMatrixDouble &al,CMatrixDouble &au);
static void CMatrixHermitianSplit(CMatrixComplex &a,const int n,CMatrixComplex &al,CMatrixComplex &au);
static void Unset2D(CMatrixDouble &a);
static void CUnset2D(CMatrixComplex &a);
static void Unset1D(double &a[]);
static void CUnset1D(complex &a[]);
static double TdTestProduct(double &d[],double &e[],const int n,CMatrixDouble &z,double &lambdav[]);
static double TestProduct(CMatrixDouble &a,const int n,CMatrixDouble &z,double &lambdav[]);
static double TestOrt(CMatrixDouble &z,const int n);
static double TestCProduct(CMatrixComplex &a,const int n,CMatrixComplex &z,double &lambdav[]);
static double TestCOrt(CMatrixComplex &z,const int n);
static void TestSEVDProblem(CMatrixDouble &a,CMatrixDouble &al,CMatrixDouble &au,const int n,const double threshold,bool &serrors,int &failc,int &runs);
static void TestHEVDProblem(CMatrixComplex &a,CMatrixComplex &al,CMatrixComplex &au,const int n,const double threshold,bool &herrors,int &failc,int &runs);
static void TestSEVDBiProblem(CMatrixDouble &afull,CMatrixDouble &al,CMatrixDouble &au,const int n,const bool distvals,const double threshold,bool &serrors,int &failc,int &runs);
static void TestHEVDBiProblem(CMatrixComplex &afull,CMatrixComplex &al,CMatrixComplex &au,const int n,const bool distvals,const double threshold,bool &herrors,int &failc,int &runs);
static void TestTdEVDProblem(double &d[],double &e[],const int n,const double threshold,bool &tderrors,int &failc,int &runs);
static void TestTdEVDBiProblem(double &d[],double &e[],const int n,const bool distvals,const double threshold,bool &serrors,int &failc,int &runs);
static void TestNSEVDProblem(CMatrixDouble &a,const int n,const double threshold,bool &nserrors,int &failc,int &runs);
static void TestEVDSet(const int n,const double threshold,double bithreshold,int &failc,int &runs,bool &nserrors,bool &serrors,bool &herrors,bool &tderrors,bool &sbierrors,bool &hbierrors,bool &tdbierrors);
public:
//--- constructor, destructor
CTestEVDUnit(void);
~CTestEVDUnit(void);
//--- public method
static bool TestEVD(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestEVDUnit::CTestEVDUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestEVDUnit::~CTestEVDUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing symmetric EVD subroutine |
//+------------------------------------------------------------------+
static bool CTestEVDUnit::TestEVD(const bool silent)
{
//--- create variables
int n=0;
int j=0;
int failc=0;
int runs=0;
double failthreshold=0;
double threshold=0;
double bithreshold=0;
bool waserrors;
bool nserrors;
bool serrors;
bool herrors;
bool tderrors;
bool sbierrors;
bool hbierrors;
bool tdbierrors;
bool wfailed;
//--- create matrix
CMatrixDouble ra;
//--- initialization
failthreshold=0.005;
threshold=100000*CMath::m_machineepsilon;
bithreshold=1.0E-6;
nserrors=false;
serrors=false;
herrors=false;
tderrors=false;
sbierrors=false;
hbierrors=false;
tdbierrors=false;
failc=0;
runs=0;
//--- Test problems
for(n=1;n<=CAblas::AblasBlockSize();n++)
TestEVDSet(n,threshold,bithreshold,failc,runs,nserrors,serrors,herrors,tderrors,sbierrors,hbierrors,tdbierrors);
for(j=2;j<=3;j++)
{
for(n=j*CAblas::AblasBlockSize()-1;n<=j*CAblas::AblasBlockSize()+1;n++)
TestEVDSet(n,threshold,bithreshold,failc,runs,nserrors,serrors,herrors,tderrors,sbierrors,hbierrors,tdbierrors);
}
//--- report
wfailed=(double)failc/(double)runs>failthreshold;
waserrors=((((((nserrors || serrors) || herrors) || tderrors) || sbierrors) || hbierrors) || tdbierrors) || wfailed;
//--- check
if(!silent)
{
Print("TESTING EVD UNIT");
Print("NS ERRORS: ");
//--- check
if(!nserrors)
Print("OK");
else
Print("FAILED");
Print("S ERRORS: ");
//--- check
if(!serrors)
Print("OK");
else
Print("FAILED");
Print("H ERRORS: ");
//--- check
if(!herrors)
Print("OK");
else
Print("FAILED");
Print("TD ERRORS: ");
//--- check
if(!tderrors)
Print("OK");
else
Print("FAILED");
Print("SBI ERRORS: ");
//--- check
if(!sbierrors)
Print("OK");
else
Print("FAILED");
Print("HBI ERRORS: ");
//--- check
if(!hbierrors)
Print("OK");
else
Print("FAILED");
Print("TDBI ERRORS: ");
//--- check
if(!tdbierrors)
Print("OK");
else
Print("FAILED");
Print("FAILURE THRESHOLD: ");
//--- check
if(!wfailed)
Print("OK");
else
Print("FAILED");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Sparse fill |
//+------------------------------------------------------------------+
static void CTestEVDUnit::RMatrixFillSparseA(CMatrixDouble &a,const int m,
const int n,const double sparcity)
{
//--- create variables
int i=0;
int j=0;
//--- change values
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(CMath::RandomReal()>=sparcity)
a[i].Set(j,2*CMath::RandomReal()-1);
else
a[i].Set(j,0);
}
}
}
//+------------------------------------------------------------------+
//| Sparse fill |
//+------------------------------------------------------------------+
static void CTestEVDUnit::CMatrixFillSparseA(CMatrixComplex &a,const int m,
const int n,const double sparcity)
{
//--- create variables
int i=0;
int j=0;
//--- change values
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(CMath::RandomReal()>=sparcity)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
}
else
a[i].Set(j,0);
}
}
}
//+------------------------------------------------------------------+
//| Copies A to AL (lower half) and AU (upper half),filling unused |
//| parts by random garbage. |
//+------------------------------------------------------------------+
static void CTestEVDUnit::RMatrixSymmetricSplit(CMatrixDouble &a,const int n,
CMatrixDouble &al,CMatrixDouble &au)
{
//--- create variables
int i=0;
int j=0;
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=i+1;j<=n-1;j++)
{
al[i].Set(j,2*CMath::RandomReal()-1);
al[j].Set(i,a[i][j]);
au[i].Set(j,a[i][j]);
au[j].Set(i,2*CMath::RandomReal()-1);
}
al[i].Set(i,a[i][i]);
au[i].Set(i,a[i][i]);
}
}
//+------------------------------------------------------------------+
//| Copies A to AL (lower half) and AU (upper half),filling unused |
//| parts by random garbage. |
//+------------------------------------------------------------------+
static void CTestEVDUnit::CMatrixHermitianSplit(CMatrixComplex &a,const int n,
CMatrixComplex &al,CMatrixComplex &au)
{
//--- create variables
int i=0;
int j=0;
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=i+1;j<=n-1;j++)
{
al[i].Set(j,2*CMath::RandomReal()-1);
al[j].Set(i,CMath::Conj(a[i][j]));
au[i].Set(j,a[i][j]);
au[j].Set(i,2*CMath::RandomReal()-1);
}
al[i].Set(i,a[i][i]);
au[i].Set(i,a[i][i]);
}
}
//+------------------------------------------------------------------+
//| Unsets 2D array. |
//+------------------------------------------------------------------+
static void CTestEVDUnit::Unset2D(CMatrixDouble &a)
{
//--- allocation
a.Resize(1,1);
//--- change value
a[0].Set(0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets 2D array. |
//+------------------------------------------------------------------+
static void CTestEVDUnit::CUnset2D(CMatrixComplex &a)
{
//--- allocation
a.Resize(1,1);
//--- change value
a[0].Set(0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets 1D array. |
//+------------------------------------------------------------------+
static void CTestEVDUnit::Unset1D(double &a[])
{
//--- allocation
ArrayResize(a,1);
//--- change value
a[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Unsets 1D array. |
//+------------------------------------------------------------------+
static void CTestEVDUnit::CUnset1D(complex &a[])
{
//--- allocation
ArrayResize(a,1);
//--- change value
a[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Tests Z*Lambda*Z' against tridiag(D,E). |
//| Returns relative error. |
//+------------------------------------------------------------------+
static double CTestEVDUnit::TdTestProduct(double &d[],double &e[],const int n,
CMatrixDouble &z,double &lambdav[])
{
//--- create variables
double result=0;
int i=0;
int j=0;
int k=0;
double v=0;
double mx=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- Calculate V=A[i][j],A=Z*Lambda*Z'
v=0;
for(k=0;k<=n-1;k++)
v=v+z[i][k]*lambdav[k]*z[j][k];
//--- Compare
if(MathAbs(i-j)==0)
result=MathMax(result,MathAbs(v-d[i]));
//--- check
if(MathAbs(i-j)==1)
result=MathMax(result,MathAbs(v-e[MathMin(i,j)]));
//--- check
if(MathAbs(i-j)>1)
result=MathMax(result,MathAbs(v));
}
}
//--- change value
mx=0;
for(i=0;i<=n-1;i++)
mx=MathMax(mx,MathAbs(d[i]));
for(i=0;i<=n-2;i++)
mx=MathMax(mx,MathAbs(e[i]));
//--- check
if(mx==0.0)
mx=1;
//--- return result
return(result/mx);
}
//+------------------------------------------------------------------+
//| Tests Z*Lambda*Z' against A |
//| Returns relative error. |
//+------------------------------------------------------------------+
static double CTestEVDUnit::TestProduct(CMatrixDouble &a,const int n,
CMatrixDouble &z,double &lambdav[])
{
//--- create variables
double result=0;
int i=0;
int j=0;
int k=0;
double v=0;
double mx=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- Calculate V=A[i][j],A=Z*Lambda*Z'
v=0;
for(k=0;k<=n-1;k++)
v=v+z[i][k]*lambdav[k]*z[j][k];
//--- Compare
result=MathMax(result,MathAbs(v-a[i][j]));
}
}
//--- change value
mx=0;
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
mx=MathMax(mx,MathAbs(a[i][j]));
}
//--- check
if(mx==0.0)
mx=1;
//--- return result
return(result/mx);
}
//+------------------------------------------------------------------+
//| Tests Z*Z' against diag(1...1) |
//| Returns absolute error. |
//+------------------------------------------------------------------+
static double CTestEVDUnit::TestOrt(CMatrixDouble &z,const int n)
{
//--- create variables
double result=0;
int i=0;
int j=0;
double v=0;
int i_=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=z[i_][i]*z[i_][j];
//--- check
if(i==j)
v=v-1;
result=MathMax(result,MathAbs(v));
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Tests Z*Lambda*Z' against A |
//| Returns relative error. |
//+------------------------------------------------------------------+
static double CTestEVDUnit::TestCProduct(CMatrixComplex &a,const int n,
CMatrixComplex &z,double &lambdav[])
{
//--- create variables
double result=0;
int i=0;
int j=0;
int k=0;
complex v=0;
double mx=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- Calculate V=A[i][j],A=Z*Lambda*Z'
v=0;
for(k=0;k<=n-1;k++)
v=v+z[i][k]*lambdav[k]*CMath::Conj(z[j][k]);
//--- Compare
result=MathMax(result,CMath::AbsComplex(v-a[i][j]));
}
}
//--- change value
mx=0;
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
mx=MathMax(mx,CMath::AbsComplex(a[i][j]));
}
//--- check
if(mx==0.0)
mx=1;
//--- return result
return(result/mx);
}
//+------------------------------------------------------------------+
//| Tests Z*Z' against diag(1...1) |
//| Returns absolute error. |
//+------------------------------------------------------------------+
static double CTestEVDUnit::TestCOrt(CMatrixComplex &z,const int n)
{
//--- create variables
double result=0;
int i=0;
int j=0;
complex v=0;
int i_=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=z[i_][i]*CMath::Conj(z[i_][j]);
//--- check
if(i==j)
v=v-1;
result=MathMax(result,CMath::AbsComplex(v));
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Tests SEVD problem |
//+------------------------------------------------------------------+
static void CTestEVDUnit::TestSEVDProblem(CMatrixDouble &a,CMatrixDouble &al,
CMatrixDouble &au,const int n,
const double threshold,
bool &serrors,int &failc,int &runs)
{
//--- create a variable
int i=0;
//--- create arrays
double lambdav[];
double lambdaref[];
//--- create matrix
CMatrixDouble z;
//--- Test simple EVD: values and full vectors,lower A
Unset1D(lambdaref);
Unset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixEVD(al,n,1,false,lambdaref,z))
{
failc=failc+1;
return;
}
//--- search errors
serrors=serrors || TestProduct(a,n,z,lambdaref)>threshold;
serrors=serrors || TestOrt(z,n)>threshold;
for(i=0;i<=n-2;i++)
{
//--- check
if(lambdaref[i+1]<lambdaref[i])
{
serrors=true;
return;
}
}
//--- Test simple EVD: values and full vectors,upper A
Unset1D(lambdav);
Unset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixEVD(au,n,1,true,lambdav,z))
{
failc=failc+1;
return;
}
//--- search errors
serrors=serrors || TestProduct(a,n,z,lambdav)>threshold;
serrors=serrors || TestOrt(z,n)>threshold;
for(i=0;i<=n-2;i++)
{
//--- check
if(lambdav[i+1]<lambdav[i])
{
serrors=true;
return;
}
}
//--- Test simple EVD: values only,lower A
Unset1D(lambdav);
Unset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixEVD(al,n,0,false,lambdav,z))
{
failc=failc+1;
return;
}
//--- search errors
for(i=0;i<=n-1;i++)
serrors=serrors || MathAbs(lambdav[i]-lambdaref[i])>threshold;
//--- Test simple EVD: values only,upper A
Unset1D(lambdav);
Unset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixEVD(au,n,0,true,lambdav,z))
{
failc=failc+1;
return;
}
//--- search errors
for(i=0;i<=n-1;i++)
serrors=serrors || MathAbs(lambdav[i]-lambdaref[i])>threshold;
}
//+------------------------------------------------------------------+
//| Tests SEVD problem |
//+------------------------------------------------------------------+
static void CTestEVDUnit::TestHEVDProblem(CMatrixComplex &a,CMatrixComplex &al,
CMatrixComplex &au,const int n,
const double threshold,bool &herrors,
int &failc,int &runs)
{
//--- create a variable
int i=0;
//--- create arrays
double lambdav[];
double lambdaref[];
//--- create matrix
CMatrixComplex z;
//--- Test simple EVD: values and full vectors,lower A
Unset1D(lambdaref);
CUnset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::HMatrixEVD(al,n,1,false,lambdaref,z))
{
failc=failc+1;
return;
}
//--- search errors
herrors=herrors || TestCProduct(a,n,z,lambdaref)>threshold;
herrors=herrors || TestCOrt(z,n)>threshold;
for(i=0;i<=n-2;i++)
{
//--- check
if(lambdaref[i+1]<lambdaref[i])
{
herrors=true;
return;
}
}
//--- Test simple EVD: values and full vectors,upper A
Unset1D(lambdav);
CUnset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::HMatrixEVD(au,n,1,true,lambdav,z))
{
failc=failc+1;
return;
}
//--- search errors
herrors=herrors || TestCProduct(a,n,z,lambdav)>threshold;
herrors=herrors || TestCOrt(z,n)>threshold;
for(i=0;i<=n-2;i++)
{
//--- check
if(lambdav[i+1]<lambdav[i])
{
herrors=true;
return;
}
}
//--- Test simple EVD: values only,lower A
Unset1D(lambdav);
CUnset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::HMatrixEVD(al,n,0,false,lambdav,z))
{
failc=failc+1;
return;
}
//--- search errors
for(i=0;i<=n-1;i++)
herrors=herrors || MathAbs(lambdav[i]-lambdaref[i])>threshold;
//--- Test simple EVD: values only,upper A
Unset1D(lambdav);
CUnset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::HMatrixEVD(au,n,0,true,lambdav,z))
{
failc=failc+1;
return;
}
//--- search errors
for(i=0;i<=n-1;i++)
herrors=herrors || MathAbs(lambdav[i]-lambdaref[i])>threshold;
}
//+------------------------------------------------------------------+
//| Tests EVD problem |
//| DistVals - is True,when eigenvalues are distinct. Is False, |
//| when we are solving sparse task with lots of zero|
//| eigenvalues. In such cases some tests related to |
//| the eigenvectors are not performed. |
//+------------------------------------------------------------------+
static void CTestEVDUnit::TestSEVDBiProblem(CMatrixDouble &afull,
CMatrixDouble &al,CMatrixDouble &au,
const int n,const bool distvals,
const double threshold,bool &serrors,
int &failc,int &runs)
{
//--- create variables
int i=0;
int j=0;
int k=0;
int m=0;
int i1=0;
int i2=0;
double v=0;
double a=0;
double b=0;
int i_=0;
//--- create arrays
double lambdav[];
double lambdaref[];
//--- create matrix
CMatrixDouble z;
CMatrixDouble zref;
CMatrixDouble a1;
CMatrixDouble a2;
CMatrixDouble ar;
//--- allocation
ArrayResize(lambdaref,n);
zref.Resize(n,n);
a1.Resize(n,n);
a2.Resize(n,n);
//--- Reference EVD
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixEVD(afull,n,1,true,lambdaref,zref))
{
failc=failc+1;
return;
}
//--- Select random interval boundaries.
//--- If there are non-distinct eigenvalues at the boundaries,
//--- we move indexes further until values splits. It is done to
//--- avoid situations where we can't get definite answer.
i1=CMath::RandomInteger(n);
i2=i1+CMath::RandomInteger(n-i1);
//--- calculation
while(i1>0)
{
//--- check
if(MathAbs(lambdaref[i1-1]-lambdaref[i1])>10*threshold)
break;
i1=i1-1;
}
while(i2<n-1)
{
//--- check
if(MathAbs(lambdaref[i2+1]-lambdaref[i2])>10*threshold)
break;
i2=i2+1;
}
//--- Select A,B
if(i1>0)
a=0.5*(lambdaref[i1]+lambdaref[i1-1]);
else
a=lambdaref[0]-1;
//--- check
if(i2<n-1)
b=0.5*(lambdaref[i2]+lambdaref[i2+1]);
else
b=lambdaref[n-1]+1;
//--- Test interval,no vectors,lower A
Unset1D(lambdav);
Unset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixEVDR(al,n,0,false,a,b,m,lambdav,z))
{
failc=failc+1;
return;
}
//--- check
if(m!=i2-i1+1)
{
failc=failc+1;
return;
}
//--- search errors
for(k=0;k<=m-1;k++)
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- Test interval,no vectors,upper A
Unset1D(lambdav);
Unset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixEVDR(au,n,0,true,a,b,m,lambdav,z))
{
failc=failc+1;
return;
}
//--- check
if(m!=i2-i1+1)
{
failc=failc+1;
return;
}
//--- search errors
for(k=0;k<=m-1;k++)
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- Test indexes,no vectors,lower A
Unset1D(lambdav);
Unset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixEVDI(al,n,0,false,i1,i2,lambdav,z))
{
failc=failc+1;
return;
}
m=i2-i1+1;
//--- search errors
for(k=0;k<=m-1;k++)
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- Test indexes,no vectors,upper A
Unset1D(lambdav);
Unset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixEVDI(au,n,0,true,i1,i2,lambdav,z))
{
failc=failc+1;
return;
}
m=i2-i1+1;
//--- search errors
for(k=0;k<=m-1;k++)
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- Test interval,vectors,lower A
Unset1D(lambdav);
Unset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixEVDR(al,n,1,false,a,b,m,lambdav,z))
{
failc=failc+1;
return;
}
//--- check
if(m!=i2-i1+1)
{
failc=failc+1;
return;
}
//--- search errors
for(k=0;k<=m-1;k++)
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- Distinct eigenvalues,test vectors
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=z[i_][j]*zref[i_][i1+j];
//--- check
if(v<0.0)
{
for(i_=0;i_<=n-1;i_++)
z[i_].Set(j,-1*z[i_][j]);
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold;
}
}
//--- Test interval,vectors,upper A
Unset1D(lambdav);
Unset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixEVDR(au,n,1,true,a,b,m,lambdav,z))
{
failc=failc+1;
return;
}
//--- check
if(m!=i2-i1+1)
{
failc=failc+1;
return;
}
//--- search errors
for(k=0;k<=m-1;k++)
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- Distinct eigenvalues,test vectors
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=z[i_][j]*zref[i_][i1+j];
//--- check
if(v<0.0)
{
for(i_=0;i_<=n-1;i_++)
z[i_].Set(j,-1*z[i_][j]);
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold;
}
}
//--- Test indexes,vectors,lower A
Unset1D(lambdav);
Unset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixEVDI(al,n,1,false,i1,i2,lambdav,z))
{
failc=failc+1;
return;
}
m=i2-i1+1;
//--- search errors
for(k=0;k<=m-1;k++)
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- Distinct eigenvalues,test vectors
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=z[i_][j]*zref[i_][i1+j];
//--- check
if(v<0.0)
{
for(i_=0;i_<=n-1;i_++)
z[i_].Set(j,-1*z[i_][j]);
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold;
}
}
//--- Test indexes,vectors,upper A
Unset1D(lambdav);
Unset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixEVDI(au,n,1,true,i1,i2,lambdav,z))
{
failc=failc+1;
return;
}
m=i2-i1+1;
//--- search errors
for(k=0;k<=m-1;k++)
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- Distinct eigenvalues,test vectors
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=z[i_][j]*zref[i_][i1+j];
//--- check
if(v<0.0)
{
for(i_=0;i_<=n-1;i_++)
z[i_].Set(j,-1*z[i_][j]);
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold;
}
}
}
//+------------------------------------------------------------------+
//| Tests EVD problem |
//| DistVals - is True,when eigenvalues are distinct. Is False, |
//| when we are solving sparse task with lots of zero|
//| eigenvalues. In such cases some tests related to |
//| the eigenvectors are not performed. |
//+------------------------------------------------------------------+
static void CTestEVDUnit::TestHEVDBiProblem(CMatrixComplex &afull,
CMatrixComplex &al,
CMatrixComplex &au,const int n,
const bool distvals,const double threshold,
bool &herrors,int &failc,int &runs)
{
//--- create variables
int i=0;
int j=0;
int k=0;
int m=0;
int i1=0;
int i2=0;
complex v=0;
double a=0;
double b=0;
int i_=0;
//--- create arrays
double lambdav[];
double lambdaref[];
//--- create matrix
CMatrixComplex z;
CMatrixComplex zref;
CMatrixComplex a1;
CMatrixComplex a2;
CMatrixComplex ar;
//--- allocation
ArrayResize(lambdaref,n);
zref.Resize(n,n);
a1.Resize(n,n);
a2.Resize(n,n);
//--- Reference EVD
runs=runs+1;
//--- check
if(!CEigenVDetect::HMatrixEVD(afull,n,1,true,lambdaref,zref))
{
failc=failc+1;
return;
}
//--- Select random interval boundaries.
//--- If there are non-distinct eigenvalues at the boundaries,
//--- we move indexes further until values splits. It is done to
//--- avoid situations where we can't get definite answer.
i1=CMath::RandomInteger(n);
i2=i1+CMath::RandomInteger(n-i1);
//--- calculation
while(i1>0)
{
//--- check
if(MathAbs(lambdaref[i1-1]-lambdaref[i1])>10*threshold)
break;
i1=i1-1;
}
while(i2<n-1)
{
//--- check
if(MathAbs(lambdaref[i2+1]-lambdaref[i2])>10*threshold)
break;
i2=i2+1;
}
//--- Select A,B
if(i1>0)
a=0.5*(lambdaref[i1]+lambdaref[i1-1]);
else
a=lambdaref[0]-1;
//--- check
if(i2<n-1)
b=0.5*(lambdaref[i2]+lambdaref[i2+1]);
else
b=lambdaref[n-1]+1;
//--- Test interval,no vectors,lower A
Unset1D(lambdav);
CUnset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::HMatrixEVDR(al,n,0,false,a,b,m,lambdav,z))
{
failc=failc+1;
return;
}
//--- check
if(m!=i2-i1+1)
{
failc=failc+1;
return;
}
//--- search errors
for(k=0;k<=m-1;k++)
herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- Test interval,no vectors,upper A
Unset1D(lambdav);
CUnset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::HMatrixEVDR(au,n,0,true,a,b,m,lambdav,z))
{
failc=failc+1;
return;
}
//--- check
if(m!=i2-i1+1)
{
failc=failc+1;
return;
}
//--- search errors
for(k=0;k<=m-1;k++)
herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- Test indexes,no vectors,lower A
Unset1D(lambdav);
CUnset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::HMatrixEVDI(al,n,0,false,i1,i2,lambdav,z))
{
failc=failc+1;
return;
}
m=i2-i1+1;
//--- search errors
for(k=0;k<=m-1;k++)
herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- Test indexes,no vectors,upper A
Unset1D(lambdav);
CUnset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::HMatrixEVDI(au,n,0,true,i1,i2,lambdav,z))
{
failc=failc+1;
return;
}
m=i2-i1+1;
//--- search errors
for(k=0;k<=m-1;k++)
herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- Test interval,vectors,lower A
Unset1D(lambdav);
CUnset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::HMatrixEVDR(al,n,1,false,a,b,m,lambdav,z))
{
failc=failc+1;
return;
}
//--- check
if(m!=i2-i1+1)
{
failc=failc+1;
return;
}
//--- search errors
for(k=0;k<=m-1;k++)
herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- Distinct eigenvalues,test vectors
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=z[i_][j]*CMath::Conj(zref[i_][i1+j]);
v=CMath::Conj(v/CMath::AbsComplex(v));
//--- calculation
for(i_=0;i_<=n-1;i_++)
z[i_].Set(j,v*z[i_][j]);
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
herrors=herrors || CMath::AbsComplex(z[i][j]-zref[i][i1+j])>threshold;
}
}
//--- Test interval,vectors,upper A
Unset1D(lambdav);
CUnset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::HMatrixEVDR(au,n,1,true,a,b,m,lambdav,z))
{
failc=failc+1;
return;
}
//--- check
if(m!=i2-i1+1)
{
failc=failc+1;
return;
}
//--- search errors
for(k=0;k<=m-1;k++)
herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- Distinct eigenvalues,test vectors
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=z[i_][j]*CMath::Conj(zref[i_][i1+j]);
v=CMath::Conj(v/CMath::AbsComplex(v));
//--- calculation
for(i_=0;i_<=n-1;i_++)
z[i_].Set(j,v*z[i_][j]);
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
herrors=herrors || CMath::AbsComplex(z[i][j]-zref[i][i1+j])>threshold;
}
}
//--- Test indexes,vectors,lower A
Unset1D(lambdav);
CUnset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::HMatrixEVDI(al,n,1,false,i1,i2,lambdav,z))
{
failc=failc+1;
return;
}
m=i2-i1+1;
//--- search errors
for(k=0;k<=m-1;k++)
herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- Distinct eigenvalues,test vectors
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=z[i_][j]*CMath::Conj(zref[i_][i1+j]);
v=CMath::Conj(v/CMath::AbsComplex(v));
//--- calculation
for(i_=0;i_<=n-1;i_++)
z[i_].Set(j,v*z[i_][j]);
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
herrors=herrors || CMath::AbsComplex(z[i][j]-zref[i][i1+j])>threshold;
}
}
//--- Test indexes,vectors,upper A
Unset1D(lambdav);
CUnset2D(z);
runs=runs+1;
//--- check
if(!CEigenVDetect::HMatrixEVDI(au,n,1,true,i1,i2,lambdav,z))
{
failc=failc+1;
return;
}
m=i2-i1+1;
//--- search errors
for(k=0;k<=m-1;k++)
herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- Distinct eigenvalues,test vectors
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=z[i_][j]*CMath::Conj(zref[i_][i1+j]);
v=CMath::Conj(v/CMath::AbsComplex(v));
//--- calculation
for(i_=0;i_<=n-1;i_++)
z[i_].Set(j,v*z[i_][j]);
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
herrors=herrors || CMath::AbsComplex(z[i][j]-zref[i][i1+j])>threshold;
}
}
}
//+------------------------------------------------------------------+
//| Tests EVD problem |
//+------------------------------------------------------------------+
static void CTestEVDUnit::TestTdEVDProblem(double &d[],double &e[],
const int n,const double threshold,
bool &tderrors,int &failc,int &runs)
{
//--- create variables
bool wsucc;
int i=0;
int j=0;
double v=0;
int i_=0;
//--- create arrays
double lambdav[];
double ee[];
double lambda2[];
//--- create matrix
CMatrixDouble z;
CMatrixDouble zref;
CMatrixDouble a1;
CMatrixDouble a2;
//--- allocation
ArrayResize(lambdav,n);
ArrayResize(lambda2,n);
zref.Resize(n,n);
a1.Resize(n,n);
a2.Resize(n,n);
//--- check
if(n>1)
{
//--- allocation
ArrayResize(ee,n-1);
}
//--- Test simple EVD: values and full vectors
for(i=0;i<=n-1;i++)
lambdav[i]=d[i];
for(i=0;i<=n-2;i++)
ee[i]=e[i];
runs=runs+1;
wsucc=CEigenVDetect::SMatrixTdEVD(lambdav,ee,n,2,z);
//--- check
if(!wsucc)
{
failc=failc+1;
return;
}
//--- search errors
tderrors=tderrors || TdTestProduct(d,e,n,z,lambdav)>threshold;
tderrors=tderrors || TestOrt(z,n)>threshold;
for(i=0;i<=n-2;i++)
{
//--- check
if(lambdav[i+1]<lambdav[i])
{
tderrors=true;
return;
}
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
zref[i].Set(j,z[i][j]);
}
//--- Test values only variant
for(i=0;i<=n-1;i++)
lambda2[i]=d[i];
for(i=0;i<=n-2;i++)
ee[i]=e[i];
runs=runs+1;
wsucc=CEigenVDetect::SMatrixTdEVD(lambda2,ee,n,0,z);
//--- check
if(!wsucc)
{
failc=failc+1;
return;
}
for(i=0;i<=n-1;i++)
tderrors=tderrors || MathAbs(lambda2[i]-lambdav[i])>threshold;
//--- Test multiplication variant
for(i=0;i<=n-1;i++)
lambda2[i]=d[i];
for(i=0;i<=n-2;i++)
ee[i]=e[i];
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a1[i].Set(j,2*CMath::RandomReal()-1);
a2[i].Set(j,a1[i][j]);
}
}
runs=runs+1;
wsucc=CEigenVDetect::SMatrixTdEVD(lambda2,ee,n,1,a1);
//--- check
if(!wsucc)
{
failc=failc+1;
return;
}
//--- search errors
for(i=0;i<=n-1;i++)
tderrors=tderrors || MathAbs(lambda2[i]-lambdav[i])>threshold;
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a2[i][i_]*zref[i_][j];
//--- next line is a bit complicated because
//--- depending on algorithm used we can get either
//--- z or -z as eigenvector. so we compare result
//--- with both A*ZRef and -A*ZRef
tderrors=tderrors || (MathAbs(v-a1[i][j])>threshold && MathAbs(v+a1[i][j])>threshold);
}
}
//--- Test first row variant
for(i=0;i<=n-1;i++)
lambda2[i]=d[i];
for(i=0;i<=n-2;i++)
ee[i]=e[i];
runs=runs+1;
wsucc=CEigenVDetect::SMatrixTdEVD(lambda2,ee,n,3,z);
//--- check
if(!wsucc)
{
failc=failc+1;
return;
}
//--- search errors
for(i=0;i<=n-1;i++)
{
tderrors=tderrors || MathAbs(lambda2[i]-lambdav[i])>threshold;
//--- next line is a bit complicated because
//--- depending on algorithm used we can get either
//--- z or -z as eigenvector. so we compare result
//--- with both z and -z
tderrors=tderrors || (MathAbs(z[0][i]-zref[0][i])>threshold && MathAbs(z[0][i]+zref[0][i])>threshold);
}
}
//+------------------------------------------------------------------+
//| Tests EVD problem |
//| DistVals - is True,when eigenvalues are distinct. Is False, |
//| when we are solving sparse task with lots of zero|
//| eigenvalues. In such cases some tests related to |
//| the eigenvectors are not performed. |
//+------------------------------------------------------------------+
static void CTestEVDUnit::TestTdEVDBiProblem(double &d[],double &e[],
const int n,const bool distvals,
const double threshold,bool &serrors,
int &failc,int &runs)
{
//--- create variables
int i=0;
int j=0;
int k=0;
int m=0;
int i1=0;
int i2=0;
double v=0;
double a=0;
double b=0;
int i_=0;
//--- create arrays
double lambdav[];
double lambdaref[];
//--- create matrix
CMatrixDouble z;
CMatrixDouble zref;
CMatrixDouble a1;
CMatrixDouble a2;
CMatrixDouble ar;
//--- allocation
ArrayResize(lambdaref,n);
zref.Resize(n,n);
a1.Resize(n,n);
a2.Resize(n,n);
//--- Reference EVD
ArrayResize(lambdaref,n);
for(i_=0;i_<=n-1;i_++)
lambdaref[i_]=d[i_];
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixTdEVD(lambdaref,e,n,2,zref))
{
failc=failc+1;
return;
}
//--- Select random interval boundaries.
//--- If there are non-distinct eigenvalues at the boundaries,
//--- we move indexes further until values splits. It is done to
//--- avoid situations where we can't get definite answer.
i1=CMath::RandomInteger(n);
i2=i1+CMath::RandomInteger(n-i1);
//--- calculation
while(i1>0)
{
//--- check
if(MathAbs(lambdaref[i1-1]-lambdaref[i1])>10*threshold)
break;
i1=i1-1;
}
while(i2<n-1)
{
//--- check
if(MathAbs(lambdaref[i2+1]-lambdaref[i2])>10*threshold)
break;
i2=i2+1;
}
//--- Test different combinations
//--- Select A,B
if(i1>0)
a=0.5*(lambdaref[i1]+lambdaref[i1-1]);
else
a=lambdaref[0]-1;
//--- check
if(i2<n-1)
b=0.5*(lambdaref[i2]+lambdaref[i2+1]);
else
b=lambdaref[n-1]+1;
//--- Test interval,no vectors
ArrayResize(lambdav,n);
for(i=0;i<=n-1;i++)
lambdav[i]=d[i];
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixTdEVDR(lambdav,e,n,0,a,b,m,z))
{
failc=failc+1;
return;
}
//--- check
if(m!=i2-i1+1)
{
failc=failc+1;
return;
}
//--- search errors
for(k=0;k<=m-1;k++)
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- Test indexes,no vectors
ArrayResize(lambdav,n);
for(i=0;i<=n-1;i++)
lambdav[i]=d[i];
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixTdEVDI(lambdav,e,n,0,i1,i2,z))
{
failc=failc+1;
return;
}
m=i2-i1+1;
//--- search errors
for(k=0;k<=m-1;k++)
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- Test interval,transform vectors
ArrayResize(lambdav,n);
for(i=0;i<=n-1;i++)
lambdav[i]=d[i];
//--- allocation
a1.Resize(n,n);
a2.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a1[i].Set(j,2*CMath::RandomReal()-1);
a2[i].Set(j,a1[i][j]);
}
}
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixTdEVDR(lambdav,e,n,1,a,b,m,a1))
{
failc=failc+1;
return;
}
//--- check
if(m!=i2-i1+1)
{
failc=failc+1;
return;
}
//--- search errors
for(k=0;k<=m-1;k++)
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- allocation
ar.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a2[i][i_]*zref[i_][i1+j];
ar[i].Set(j,v);
}
}
//--- calculation
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a1[i_][j]*ar[i_][j];
//--- check
if(v<0.0)
{
for(i_=0;i_<=n-1;i_++)
ar[i_].Set(j,-1*ar[i_][j]);
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
serrors=serrors || MathAbs(a1[i][j]-ar[i][j])>threshold;
}
}
//--- Test indexes,transform vectors
ArrayResize(lambdav,n);
for(i=0;i<=n-1;i++)
lambdav[i]=d[i];
//--- allocation
a1.Resize(n,n);
a2.Resize(n,n);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a1[i].Set(j,2*CMath::RandomReal()-1);
a2[i].Set(j,a1[i][j]);
}
}
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixTdEVDI(lambdav,e,n,1,i1,i2,a1))
{
failc=failc+1;
return;
}
m=i2-i1+1;
//--- search errors
for(k=0;k<=m-1;k++)
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- allocation
ar.Resize(n,m);
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a2[i][i_]*zref[i_][i1+j];
ar[i].Set(j,v);
}
}
//--- calculation
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a1[i_][j]*ar[i_][j];
//--- check
if(v<0.0)
{
for(i_=0;i_<=n-1;i_++)
ar[i_].Set(j,-1*ar[i_][j]);
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
serrors=serrors || MathAbs(a1[i][j]-ar[i][j])>threshold;
}
}
//--- Test interval,do not transform vectors
ArrayResize(lambdav,n);
for(i=0;i<=n-1;i++)
lambdav[i]=d[i];
//--- allocation
z.Resize(1,1);
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixTdEVDR(lambdav,e,n,2,a,b,m,z))
{
failc=failc+1;
return;
}
//--- check
if(m!=i2-i1+1)
{
failc=failc+1;
return;
}
//--- search errors
for(k=0;k<=m-1;k++)
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=z[i_][j]*zref[i_][i1+j];
//--- check
if(v<0.0)
{
for(i_=0;i_<=n-1;i_++)
z[i_].Set(j,-1*z[i_][j]);
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold;
}
}
//--- Test indexes,do not transform vectors
ArrayResize(lambdav,n);
for(i=0;i<=n-1;i++)
lambdav[i]=d[i];
//--- allocation
z.Resize(1,1);
runs=runs+1;
//--- check
if(!CEigenVDetect::SMatrixTdEVDI(lambdav,e,n,2,i1,i2,z))
{
failc=failc+1;
return;
}
m=i2-i1+1;
//--- search errors
for(k=0;k<=m-1;k++)
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=z[i_][j]*zref[i_][i1+j];
//--- check
if(v<0.0)
{
for(i_=0;i_<=n-1;i_++)
z[i_].Set(j,-1*z[i_][j]);
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold;
}
}
}
//+------------------------------------------------------------------+
//| Non-symmetric problem |
//+------------------------------------------------------------------+
static void CTestEVDUnit::TestNSEVDProblem(CMatrixDouble &a,const int n,
const double threshold,
bool &nserrors,int &failc,
int &runs)
{
//--- create variables
double mx=0;
int i=0;
int j=0;
int k=0;
int vjob=0;
bool needl;
bool needr;
double curwr=0;
double curwi=0;
double vt=0;
double tmp=0;
int i_=0;
//--- create arrays
double wr0[];
double wi0[];
double wr1[];
double wi1[];
double wr0s[];
double wi0s[];
double wr1s[];
double wi1s[];
double vec1r[];
double vec1i[];
double vec2r[];
double vec2i[];
double vec3r[];
double vec3i[];
//--- create matrix
CMatrixDouble vl;
CMatrixDouble vr;
//--- allocation
ArrayResize(vec1r,n);
ArrayResize(vec2r,n);
ArrayResize(vec3r,n);
ArrayResize(vec1i,n);
ArrayResize(vec2i,n);
ArrayResize(vec3i,n);
ArrayResize(wr0s,n);
ArrayResize(wr1s,n);
ArrayResize(wi0s,n);
ArrayResize(wi1s,n);
//--- initialization
mx=0;
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(MathAbs(a[i][j])>mx)
mx=MathAbs(a[i][j]);
}
}
//--- check
if(mx==0.0)
mx=1;
//--- Load values-only
runs=runs+1;
//--- check
if(!CEigenVDetect::RMatrixEVD(a,n,0,wr0,wi0,vl,vr))
{
failc=failc+1;
return;
}
//--- Test different jobs
for(vjob=1;vjob<=3;vjob++)
{
needr=vjob==1 || vjob==3;
needl=vjob==2 || vjob==3;
runs=runs+1;
//--- check
if(!CEigenVDetect::RMatrixEVD(a,n,vjob,wr1,wi1,vl,vr))
{
failc=failc+1;
return;
}
//--- Test values:
//--- 1. sort by real part
//--- 2. test
for(i_=0;i_<=n-1;i_++)
wr0s[i_]=wr0[i_];
for(i_=0;i_<=n-1;i_++)
wi0s[i_]=wi0[i_];
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-2-i;j++)
{
//--- check
if(wr0s[j]>wr0s[j+1])
{
tmp=wr0s[j];
wr0s[j]=wr0s[j+1];
wr0s[j+1]=tmp;
tmp=wi0s[j];
wi0s[j]=wi0s[j+1];
wi0s[j+1]=tmp;
}
}
}
//--- copy
for(i_=0;i_<=n-1;i_++)
wr1s[i_]=wr1[i_];
for(i_=0;i_<=n-1;i_++)
wi1s[i_]=wi1[i_];
//--- swap
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-2-i;j++)
{
//--- check
if(wr1s[j]>wr1s[j+1])
{
tmp=wr1s[j];
wr1s[j]=wr1s[j+1];
wr1s[j+1]=tmp;
tmp=wi1s[j];
wi1s[j]=wi1s[j+1];
wi1s[j+1]=tmp;
}
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
nserrors=nserrors || MathAbs(wr0s[i]-wr1s[i])>threshold;
nserrors=nserrors || MathAbs(wi0s[i]-wi1s[i])>threshold;
}
//--- Test right vectors
if(needr)
{
k=0;
//--- calculation
while(k<=n-1)
{
//--- check
if(wi1[k]==0.0)
{
for(i_=0;i_<=n-1;i_++)
vec1r[i_]=vr[i_][k];
for(i=0;i<=n-1;i++)
vec1i[i]=0;
curwr=wr1[k];
curwi=0;
}
//--- check
if(wi1[k]>0.0)
{
for(i_=0;i_<=n-1;i_++)
vec1r[i_]=vr[i_][k];
for(i_=0;i_<=n-1;i_++)
vec1i[i_]=vr[i_][k+1];
curwr=wr1[k];
curwi=wi1[k];
}
//--- check
if(wi1[k]<0.0)
{
for(i_=0;i_<=n-1;i_++)
vec1r[i_]=vr[i_][k-1];
for(i_=0;i_<=n-1;i_++)
vec1i[i_]=-vr[i_][k];
curwr=wr1[k];
curwi=wi1[k];
}
//--- calculation
for(i=0;i<=n-1;i++)
{
vt=0.0;
for(i_=0;i_<=n-1;i_++)
vt+=a[i][i_]*vec1r[i_];
vec2r[i]=vt;
vt=0.0;
for(i_=0;i_<=n-1;i_++)
vt+=a[i][i_]*vec1i[i_];
vec2i[i]=vt;
}
//--- change values
for(i_=0;i_<=n-1;i_++)
vec3r[i_]=curwr*vec1r[i_];
for(i_=0;i_<=n-1;i_++)
vec3r[i_]=vec3r[i_]-curwi*vec1i[i_];
for(i_=0;i_<=n-1;i_++)
vec3i[i_]=curwi*vec1r[i_];
for(i_=0;i_<=n-1;i_++)
vec3i[i_]=vec3i[i_]+curwr*vec1i[i_];
//--- search errors
for(i=0;i<=n-1;i++)
{
nserrors=nserrors || MathAbs(vec2r[i]-vec3r[i])>threshold;
nserrors=nserrors || MathAbs(vec2i[i]-vec3i[i])>threshold;
}
k=k+1;
}
}
//--- Test left vectors
if(needl)
{
k=0;
//--- calculation
while(k<=n-1)
{
//--- check
if(wi1[k]==0.0)
{
for(i_=0;i_<=n-1;i_++)
vec1r[i_]=vl[i_][k];
for(i=0;i<=n-1;i++)
vec1i[i]=0;
curwr=wr1[k];
curwi=0;
}
//--- check
if(wi1[k]>0.0)
{
for(i_=0;i_<=n-1;i_++)
vec1r[i_]=vl[i_][k];
for(i_=0;i_<=n-1;i_++)
vec1i[i_]=vl[i_][k+1];
curwr=wr1[k];
curwi=wi1[k];
}
//--- check
if(wi1[k]<0.0)
{
for(i_=0;i_<=n-1;i_++)
vec1r[i_]=vl[i_][k-1];
for(i_=0;i_<=n-1;i_++)
vec1i[i_]=-vl[i_][k];
curwr=wr1[k];
curwi=wi1[k];
}
//--- calculation
for(j=0;j<=n-1;j++)
{
vt=0.0;
for(i_=0;i_<=n-1;i_++)
vt+=vec1r[i_]*a[i_][j];
vec2r[j]=vt;
vt=0.0;
for(i_=0;i_<=n-1;i_++)
vt+=vec1i[i_]*a[i_][j];
vec2i[j]=-vt;
}
//--- change values
for(i_=0;i_<=n-1;i_++)
vec3r[i_]=curwr*vec1r[i_];
for(i_=0;i_<=n-1;i_++)
vec3r[i_]=vec3r[i_]+curwi*vec1i[i_];
for(i_=0;i_<=n-1;i_++)
vec3i[i_]=curwi*vec1r[i_];
for(i_=0;i_<=n-1;i_++)
vec3i[i_]=vec3i[i_]-curwr*vec1i[i_];
//--- search errors
for(i=0;i<=n-1;i++)
{
nserrors=nserrors || MathAbs(vec2r[i]-vec3r[i])>threshold;
nserrors=nserrors || MathAbs(vec2i[i]-vec3i[i])>threshold;
}
k=k+1;
}
}
}
}
//+------------------------------------------------------------------+
//| Testing EVD subroutines for one N |
//| NOTES: |
//| * BIThreshold is a threshold for bisection-and-inverse-iteration |
//| subroutines. special threshold is needed because these |
//| subroutines may have much more larger error than QR-based |
//| algorithms. |
//+------------------------------------------------------------------+
static void CTestEVDUnit::TestEVDSet(const int n,const double threshold,
double bithreshold,int &failc,
int &runs,bool &nserrors,bool &serrors,
bool &herrors,bool &tderrors,bool &sbierrors,
bool &hbierrors,bool &tdbierrors)
{
//--- create variables
int i=0;
int j=0;
int mkind=0;
//--- create arrays
double d[];
double e[];
//--- create matrix
CMatrixDouble ra;
CMatrixDouble ral;
CMatrixDouble rau;
CMatrixComplex ca;
CMatrixComplex cal;
CMatrixComplex cau;
//--- Test symmetric problems
ra.Resize(n,n);
ral.Resize(n,n);
rau.Resize(n,n);
ca.Resize(n,n);
cal.Resize(n,n);
cau.Resize(n,n);
//--- Zero matrices
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
ra[i].Set(j,0);
ca[i].Set(j,0);
}
}
//--- function calls
RMatrixSymmetricSplit(ra,n,ral,rau);
CMatrixHermitianSplit(ca,n,cal,cau);
TestSEVDProblem(ra,ral,rau,n,threshold,serrors,failc,runs);
TestHEVDProblem(ca,cal,cau,n,threshold,herrors,failc,runs);
TestSEVDBiProblem(ra,ral,rau,n,false,bithreshold,sbierrors,failc,runs);
TestHEVDBiProblem(ca,cal,cau,n,false,bithreshold,hbierrors,failc,runs);
//--- Random matrix
for(i=0;i<=n-1;i++)
{
for(j=i+1;j<=n-1;j++)
{
ra[i].Set(j,2*CMath::RandomReal()-1);
ca[i].SetRe(j,2*CMath::RandomReal()-1);
ca[i].SetIm(j,2*CMath::RandomReal()-1);
ra[j].Set(i,ra[i][j]);
ca[j].Set(i,CMath::Conj(ca[i][j]));
}
//--- change values
ra[i].Set(i,2*CMath::RandomReal()-1);
ca[i].Set(i,2*CMath::RandomReal()-1);
}
//--- function calls
RMatrixSymmetricSplit(ra,n,ral,rau);
CMatrixHermitianSplit(ca,n,cal,cau);
TestSEVDProblem(ra,ral,rau,n,threshold,serrors,failc,runs);
TestHEVDProblem(ca,cal,cau,n,threshold,herrors,failc,runs);
//--- Random diagonally dominant matrix with distinct eigenvalues
for(i=0;i<=n-1;i++)
{
for(j=i+1;j<=n-1;j++)
{
ra[i].Set(j,0.1*(2*CMath::RandomReal()-1)/n);
ca[i].SetRe(j,0.1*(2*CMath::RandomReal()-1)/n);
ca[i].SetIm(j,0.1*(2*CMath::RandomReal()-1)/n);
ra[j].Set(i,ra[i][j]);
ca[j].Set(i,CMath::Conj(ca[i][j]));
}
//--- change values
ra[i].Set(i,0.1*(2*CMath::RandomReal()-1)+i);
ca[i].Set(i,0.1*(2*CMath::RandomReal()-1)+i);
}
//--- function calls
RMatrixSymmetricSplit(ra,n,ral,rau);
CMatrixHermitianSplit(ca,n,cal,cau);
TestSEVDProblem(ra,ral,rau,n,threshold,serrors,failc,runs);
TestHEVDProblem(ca,cal,cau,n,threshold,herrors,failc,runs);
TestSEVDBiProblem(ra,ral,rau,n,true,bithreshold,sbierrors,failc,runs);
TestHEVDBiProblem(ca,cal,cau,n,true,bithreshold,hbierrors,failc,runs);
//--- Sparse matrices
RMatrixFillSparseA(ra,n,n,0.995);
CMatrixFillSparseA(ca,n,n,0.995);
for(i=0;i<=n-1;i++)
{
for(j=i+1;j<=n-1;j++)
{
ra[j].Set(i,ra[i][j]);
ca[j].Set(i,CMath::Conj(ca[i][j]));
}
ca[i].SetIm(i,0);
}
//--- function calls
RMatrixSymmetricSplit(ra,n,ral,rau);
CMatrixHermitianSplit(ca,n,cal,cau);
TestSEVDProblem(ra,ral,rau,n,threshold,serrors,failc,runs);
TestHEVDProblem(ca,cal,cau,n,threshold,herrors,failc,runs);
TestSEVDBiProblem(ra,ral,rau,n,false,bithreshold,sbierrors,failc,runs);
TestHEVDBiProblem(ca,cal,cau,n,false,bithreshold,hbierrors,failc,runs);
//--- testing tridiagonal problems
for(mkind=0;mkind<=7;mkind++)
{
//--- allocation
ArrayResize(d,n);
if(n>1)
ArrayResize(e,n-1);
//--- check
if(mkind==0)
{
//--- Zero matrix
for(i=0;i<=n-1;i++)
d[i]=0;
for(i=0;i<=n-2;i++)
e[i]=0;
}
//--- check
if(mkind==1)
{
//--- Diagonal matrix
for(i=0;i<=n-1;i++)
d[i]=2*CMath::RandomReal()-1;
for(i=0;i<=n-2;i++)
e[i]=0;
}
//--- check
if(mkind==2)
{
//--- Off-diagonal matrix
for(i=0;i<=n-1;i++)
d[i]=0;
for(i=0;i<=n-2;i++)
e[i]=2*CMath::RandomReal()-1;
}
//--- check
if(mkind==3)
{
//--- Dense matrix with blocks
for(i=0;i<=n-1;i++)
d[i]=2*CMath::RandomReal()-1;
for(i=0;i<=n-2;i++)
e[i]=2*CMath::RandomReal()-1;
//--- change values
j=1;
i=2;
while(j<=n-2)
{
e[j]=0;
j=j+i;
i=i+1;
}
}
//--- check
if(mkind==4)
{
//--- dense matrix
for(i=0;i<=n-1;i++)
d[i]=2*CMath::RandomReal()-1;
for(i=0;i<=n-2;i++)
e[i]=2*CMath::RandomReal()-1;
}
//--- check
if(mkind==5)
{
//--- Diagonal matrix with distinct eigenvalues
for(i=0;i<=n-1;i++)
d[i]=0.1*(2*CMath::RandomReal()-1)+i;
for(i=0;i<=n-2;i++)
e[i]=0;
}
//--- check
if(mkind==6)
{
//--- Off-diagonal matrix with distinct eigenvalues
for(i=0;i<=n-1;i++)
d[i]=0;
for(i=0;i<=n-2;i++)
e[i]=0.1*(2*CMath::RandomReal()-1)+i+1;
}
//--- check
if(mkind==7)
{
//--- dense matrix with distinct eigenvalues
for(i=0;i<=n-1;i++)
d[i]=0.1*(2*CMath::RandomReal()-1)+i+1;
for(i=0;i<=n-2;i++)
e[i]=0.1*(2*CMath::RandomReal()-1);
}
//--- function calls
TestTdEVDProblem(d,e,n,threshold,tderrors,failc,runs);
TestTdEVDBiProblem(d,e,n,(mkind==5 || mkind==6) || mkind==7,bithreshold,tdbierrors,failc,runs);
}
//--- Test non-symmetric problems
//--- Test non-symmetric problems: zero,random,sparse matrices.
ra.Resize(n,n);
ca.Resize(n,n);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
ra[i].Set(j,0);
ca[i].Set(j,0);
}
}
//--- function call
TestNSEVDProblem(ra,n,threshold,nserrors,failc,runs);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
ra[i].Set(j,2*CMath::RandomReal()-1);
ca[i].SetRe(j,2*CMath::RandomReal()-1);
ca[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- function calls
TestNSEVDProblem(ra,n,threshold,nserrors,failc,runs);
RMatrixFillSparseA(ra,n,n,0.995);
CMatrixFillSparseA(ca,n,n,0.995);
TestNSEVDProblem(ra,n,threshold,nserrors,failc,runs);
}
//+------------------------------------------------------------------+
//| Testing class CMatGen |
//+------------------------------------------------------------------+
class CTestMatGenUnit
{
private:
//--- private methods
static void Unset2D(CMatrixDouble &a);
static void Unset2DC(CMatrixComplex &a);
static bool IsSPD(CMatrixDouble &ca,const int n,const bool isupper);
static bool IsHPD(CMatrixComplex &ca,const int n);
static double SVDCond(CMatrixDouble &a,const int n);
static bool ObsoleteSVDDecomposition(CMatrixDouble &a,const int m,const int n,double &w[],CMatrixDouble &v);
static double ExtSign(const double a,const double b);
static double MyMax(const double a,const double b);
static double PyThag(const double a,const double b);
public:
//--- class constant
static const int m_maxsvditerations;
//--- constructor, destructor
CTestMatGenUnit(void);
~CTestMatGenUnit(void);
//--- public method
static bool TestMatGen(const bool silent);
};
//+------------------------------------------------------------------+
//| Initialize constant |
//+------------------------------------------------------------------+
const int CTestMatGenUnit::m_maxsvditerations=60;
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestMatGenUnit::CTestMatGenUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestMatGenUnit::~CTestMatGenUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CMatGen |
//+------------------------------------------------------------------+
static bool CTestMatGenUnit::TestMatGen(const bool silent)
{
//--- create variables
int n=0;
int maxn=0;
int i=0;
int j=0;
int pass=0;
int passcount=0;
int equal_number=0;
bool waserrors;
double cond=0;
double threshold=0;
double vt=0;
complex ct=0;
double minw=0;
double maxw=0;
bool serr;
bool herr;
bool spderr;
bool hpderr;
bool rerr;
bool cerr;
int i_=0;
//--- create array
double w[];
//--- create matrix
CMatrixDouble a;
CMatrixDouble b;
CMatrixDouble u;
CMatrixDouble v;
CMatrixComplex ca;
CMatrixComplex cb;
CMatrixDouble r1;
CMatrixDouble r2;
CMatrixComplex c1;
CMatrixComplex c2;
//--- initialization
rerr=false;
cerr=false;
serr=false;
herr=false;
spderr=false;
hpderr=false;
waserrors=false;
maxn=20;
passcount=15;
threshold=1000*CMath::m_machineepsilon;
//--- Testing orthogonal
for(n=1;n<=maxn;n++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- allocation
r1.Resize(n,2*n);
r2.Resize(2*n,n);
c1.Resize(n,2*n);
c2.Resize(2*n,n);
//--- Random orthogonal,real
Unset2D(a);
Unset2D(b);
//--- function call
CMatGen::RMatrixRndOrthogonal(n,a);
//--- function call
CMatGen::RMatrixRndOrthogonal(n,b);
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- orthogonality test
vt=0.0;
for(i_=0;i_<=n-1;i_++)
vt+=a[i][i_]*a[j][i_];
//--- check
if(i==j)
rerr=rerr || MathAbs(vt-1)>threshold;
else
rerr=rerr || MathAbs(vt)>threshold;
//--- change value
vt=0.0;
for(i_=0;i_<=n-1;i_++)
vt+=b[i][i_]*b[j][i_];
//--- check
if(i==j)
rerr=rerr || MathAbs(vt-1)>threshold;
else
rerr=rerr || MathAbs(vt)>threshold;
//--- test for difference in A and B
if(n>=2)
rerr=rerr || a[i][j]==b[i][j];
}
}
//--- Random orthogonal,complex
Unset2DC(ca);
Unset2DC(cb);
//--- function call
CMatGen::CMatrixRndOrthogonal(n,ca);
//--- function call
CMatGen::CMatrixRndOrthogonal(n,cb);
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- orthogonality test
ct=0.0;
for(i_=0;i_<=n-1;i_++)
ct+=ca[i][i_]*CMath::Conj(ca[j][i_]);
//--- check
if(i==j)
cerr=cerr || CMath::AbsComplex(ct-1)>threshold;
else
cerr=cerr || CMath::AbsComplex(ct)>threshold;
//--- change value
ct=0.0;
for(i_=0;i_<=n-1;i_++)
ct+=cb[i][i_]*CMath::Conj(cb[j][i_]);
//--- check
if(i==j)
cerr=cerr || CMath::AbsComplex(ct-1)>threshold;
else
cerr=cerr || CMath::AbsComplex(ct)>threshold;
//--- test for difference in A and B
if(n>=2)
cerr=cerr || ca[i][j]==cb[i][j];
}
}
//--- From the right real tests:
//--- 1. E*Q is orthogonal
//--- 2. Q1<>Q2 (routine result is changing)
//--- 3. (E E)'*Q=(Q' Q')' (correct handling of non-square matrices)
Unset2D(a);
Unset2D(b);
//--- allocation
a.Resize(n,n);
b.Resize(n,n);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a[i].Set(j,0);
b[i].Set(j,0);
}
a[i].Set(i,1);
b[i].Set(i,1);
}
//--- function call
CMatGen::RMatrixRndOrthogonalFromTheRight(a,n,n);
//--- function call
CMatGen::RMatrixRndOrthogonalFromTheRight(b,n,n);
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- orthogonality test
vt=0.0;
for(i_=0;i_<=n-1;i_++)
vt+=a[i][i_]*a[j][i_];
//--- check
if(i==j)
rerr=rerr || MathAbs(vt-1)>threshold;
else
rerr=rerr || MathAbs(vt)>threshold;
//--- change value
vt=0.0;
for(i_=0;i_<=n-1;i_++)
vt+=b[i][i_]*b[j][i_];
//--- check
if(i==j)
rerr=rerr || MathAbs(vt-1)>threshold;
else
rerr=rerr || MathAbs(vt)>threshold;
//--- test for difference in A and B
if(n>=2)
rerr=rerr || a[i][j]==b[i][j];
}
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
r2[i].Set(j,2*CMath::RandomReal()-1);
r2[i+n].Set(j,r2[i][j]);
}
}
//--- function call
CMatGen::RMatrixRndOrthogonalFromTheRight(r2,2*n,n);
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
rerr=rerr || MathAbs(r2[i+n][j]-r2[i][j])>threshold;
}
//--- From the left real tests:
//--- 1. Q*E is orthogonal
//--- 2. Q1<>Q2 (routine result is changing)
//--- 3. Q*(E E)=(Q Q) (correct handling of non-square matrices)
Unset2D(a);
Unset2D(b);
//--- allocation
a.Resize(n,n);
b.Resize(n,n);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a[i].Set(j,0);
b[i].Set(j,0);
}
a[i].Set(i,1);
b[i].Set(i,1);
}
//--- function call
CMatGen::RMatrixRndOrthogonalFromTheLeft(a,n,n);
//--- function call
CMatGen::RMatrixRndOrthogonalFromTheLeft(b,n,n);
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- orthogonality test
vt=0.0;
for(i_=0;i_<=n-1;i_++)
vt+=a[i][i_]*a[j][i_];
//--- check
if(i==j)
rerr=rerr || MathAbs(vt-1)>threshold;
else
rerr=rerr || MathAbs(vt)>threshold;
//--- change value
vt=0.0;
for(i_=0;i_<=n-1;i_++)
vt+=b[i][i_]*b[j][i_];
//--- check
if(i==j)
rerr=rerr || MathAbs(vt-1)>threshold;
else
rerr=rerr || MathAbs(vt)>threshold;
//--- test for difference in A and B
if(n>=2)
rerr=rerr || a[i][j]==b[i][j];
}
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
r1[i].Set(j,2*CMath::RandomReal()-1);
r1[i].Set(j+n,r1[i][j]);
}
}
//--- function call
CMatGen::RMatrixRndOrthogonalFromTheLeft(r1,n,2*n);
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
rerr=rerr || MathAbs(r1[i][j]-r1[i][j+n])>threshold;
}
//--- From the right complex tests:
//--- 1. E*Q is orthogonal
//--- 2. Q1<>Q2 (routine result is changing)
//--- 3. (E E)'*Q=(Q' Q')' (correct handling of non-square matrices)
Unset2DC(ca);
Unset2DC(cb);
//--- allocation
ca.Resize(n,n);
cb.Resize(n,n);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
ca[i].Set(j,0);
cb[i].Set(j,0);
}
ca[i].Set(i,1);
cb[i].Set(i,1);
}
//--- function call
CMatGen::CMatrixRndOrthogonalFromTheRight(ca,n,n);
//--- function call
CMatGen::CMatrixRndOrthogonalFromTheRight(cb,n,n);
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- orthogonality test
ct=0.0;
for(i_=0;i_<=n-1;i_++)
ct+=ca[i][i_]*CMath::Conj(ca[j][i_]);
//--- check
if(i==j)
cerr=cerr || CMath::AbsComplex(ct-1)>threshold;
else
cerr=cerr || CMath::AbsComplex(ct)>threshold;
//--- change value
ct=0.0;
for(i_=0;i_<=n-1;i_++)
ct+=cb[i][i_]*CMath::Conj(cb[j][i_]);
//--- check
if(i==j)
cerr=cerr || CMath::AbsComplex(ct-1)>threshold;
else
cerr=cerr || CMath::AbsComplex(ct)>threshold;
//--- test for difference in A and B
cerr=cerr || ca[i][j]==cb[i][j];
}
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
c2[i].Set(j,2*CMath::RandomReal()-1);
c2[i+n].Set(j,c2[i][j]);
}
}
//--- function call
CMatGen::CMatrixRndOrthogonalFromTheRight(c2,2*n,n);
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
cerr=cerr || CMath::AbsComplex(c2[i+n][j]-c2[i][j])>threshold;
}
//--- From the left complex tests:
//--- 1. Q*E is orthogonal
//--- 2. Q1<>Q2 (routine result is changing)
//--- 3. Q*(E E)=(Q Q) (correct handling of non-square matrices)
Unset2DC(ca);
Unset2DC(cb);
//--- allocation
ca.Resize(n,n);
cb.Resize(n,n);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
ca[i].Set(j,0);
cb[i].Set(j,0);
}
ca[i].Set(i,1);
cb[i].Set(i,1);
}
//--- function call
CMatGen::CMatrixRndOrthogonalFromTheLeft(ca,n,n);
//--- function call
CMatGen::CMatrixRndOrthogonalFromTheLeft(cb,n,n);
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- orthogonality test
ct=0.0;
for(i_=0;i_<=n-1;i_++)
ct+=ca[i][i_]*CMath::Conj(ca[j][i_]);
//--- check
if(i==j)
cerr=cerr || CMath::AbsComplex(ct-1)>threshold;
else
cerr=cerr || CMath::AbsComplex(ct)>threshold;
//--- change value
ct=0.0;
for(i_=0;i_<=n-1;i_++)
ct+=cb[i][i_]*CMath::Conj(cb[j][i_]);
//--- check
if(i==j)
cerr=cerr || CMath::AbsComplex(ct-1)>threshold;
else
cerr=cerr || CMath::AbsComplex(ct)>threshold;
//--- test for difference in A and B
cerr=cerr || ca[i][j]==cb[i][j];
}
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
c1[i].Set(j,2*CMath::RandomReal()-1);
c1[i].Set(j+n,c1[i][j]);
}
}
//--- function call
CMatGen::CMatrixRndOrthogonalFromTheLeft(c1,n,2*n);
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
cerr=cerr || CMath::AbsComplex(c1[i][j]-c1[i][j+n])>threshold;
}
}
}
//--- Testing GCond
for(n=2;n<=maxn;n++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- real test
Unset2D(a);
cond=MathExp(MathLog(1000)*CMath::RandomReal());
//--- function call
CMatGen::RMatrixRndCond(n,cond,a);
//--- allocation
b.Resize(n+1,n+1);
//--- change values
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
b[i].Set(j,a[i-1][j-1]);
}
//--- check
if(ObsoleteSVDDecomposition(b,n,n,w,v))
{
maxw=w[1];
minw=w[1];
for(i=2;i<=n;i++)
{
//--- check
if(w[i]>maxw)
maxw=w[i];
//--- check
if(w[i]<minw)
minw=w[i];
}
vt=maxw/minw/cond;
//--- check
if(MathAbs(MathLog(vt))>MathLog(1+threshold))
rerr=true;
}
}
}
//--- Symmetric/SPD
//--- N=2 .. 30
for(n=2;n<=maxn;n++)
{
//--- SPD matrices
for(pass=1;pass<=passcount;pass++)
{
//--- Generate A
Unset2D(a);
cond=MathExp(MathLog(1000)*CMath::RandomReal());
//--- function call
CMatGen::SPDMatrixRndCond(n,cond,a);
//--- test condition number
spderr=spderr || SVDCond(a,n)/cond-1>threshold;
//--- test SPD
spderr=spderr || !IsSPD(a,n,true);
//--- test that A is symmetic
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
spderr=spderr || MathAbs(a[i][j]-a[j][i])>threshold;
}
//--- test for difference between A and B (subsequent matrix)
Unset2D(b);
//--- function call
CMatGen::SPDMatrixRndCond(n,cond,b);
//--- check
if(n>=2)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
if(a[i][j]==b[i][j])
equal_number++;
}
}
if(equal_number>2)
spderr=true;
}
}
//--- HPD matrices
for(pass=1;pass<=passcount;pass++)
{
//--- Generate A
Unset2DC(ca);
cond=MathExp(MathLog(1000)*CMath::RandomReal());
//--- function call
CMatGen::HPDMatrixRndCond(n,cond,ca);
//--- test HPD
hpderr=hpderr || !IsHPD(ca,n);
//--- test that A is Hermitian
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
hpderr=hpderr || CMath::AbsComplex(ca[i][j]-CMath::Conj(ca[j][i]))>threshold;
}
//--- test for difference between A and B (subsequent matrix)
Unset2DC(cb);
//--- function call
CMatGen::HPDMatrixRndCond(n,cond,cb);
//--- check
if(n>=2)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
hpderr=hpderr || ca[i][j]==cb[i][j];
}
}
}
}
//--- Symmetric matrices
for(pass=1;pass<=passcount;pass++)
{
//--- test condition number
Unset2D(a);
cond=MathExp(MathLog(1000)*CMath::RandomReal());
//--- function call
CMatGen::SMatrixRndCond(n,cond,a);
serr=serr || SVDCond(a,n)/cond-1>threshold;
//--- test for difference between A and B
Unset2D(b);
//--- function call
CMatGen::SMatrixRndCond(n,cond,b);
//--- check
if(n>=2)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
serr=serr || a[i][j]==b[i][j];
}
}
}
//--- Hermitian matrices
for(pass=1;pass<=passcount;pass++)
{
//--- Generate A
Unset2DC(ca);
cond=MathExp(MathLog(1000)*CMath::RandomReal());
//--- function call
CMatGen::HMatrixRndCond(n,cond,ca);
//--- test that A is Hermitian
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
herr=herr || CMath::AbsComplex(ca[i][j]-CMath::Conj(ca[j][i]))>threshold;
}
//--- test for difference between A and B (subsequent matrix)
Unset2DC(cb);
//--- function call
CMatGen::HMatrixRndCond(n,cond,cb);
//--- check
if(n>=2)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
herr=herr || ca[i][j]==cb[i][j];
}
}
}
}
//--- report
waserrors=((((rerr || cerr) || serr) || spderr) || herr) || hpderr;
//--- check
if(!silent)
{
Print("TESTING MATRIX GENERATOR");
Print("REAL TEST: ");
//--- check
if(!rerr)
Print("OK");
else
Print("FAILED");
Print("COMPLEX TEST: ");
//--- check
if(!cerr)
Print("OK");
else
Print("FAILED");
Print("SYMMETRIC TEST: ");
//--- check
if(!serr)
Print("OK");
else
Print("FAILED");
Print("HERMITIAN TEST: ");
//--- check
if(!herr)
Print("OK");
else
Print("FAILED");
Print("SPD TEST: ");
//--- check
if(!spderr)
Print("OK");
else
Print("FAILED");
Print("HPD TEST: ");
//--- check
if(!hpderr)
Print("OK");
else
Print("FAILED");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Unsets 2D array. |
//+------------------------------------------------------------------+
static void CTestMatGenUnit::Unset2D(CMatrixDouble &a)
{
//--- allocation
a.Resize(1,1);
//--- change value
a[0].Set(0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets 2D array. |
//+------------------------------------------------------------------+
static void CTestMatGenUnit::Unset2DC(CMatrixComplex &a)
{
//--- allocation
a.Resize(1,1);
//--- change value
a[0].Set(0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Test whether matrix is SPD |
//+------------------------------------------------------------------+
static bool CTestMatGenUnit::IsSPD(CMatrixDouble &ca,const int n,const bool isupper)
{
//--- create variables
bool result;
int i=0;
int j=0;
double ajj=0;
double v=0;
int i_=0;
//--- create matrix
CMatrixDouble a;
//--- copy
a=ca;
//--- Test the input parameters.
if(!CAp::Assert(n>=0,"Error in SMatrixCholesky: incorrect function arguments"))
return(false);
//--- Quick return if possible
result=true;
if(n<=0)
{
//--- return result
return(result);
}
//--- check
if(isupper)
{
//--- Compute the Cholesky factorization A=U'*U.
for(j=0;j<=n-1;j++)
{
//--- Compute U(J,J) and test for non-positive-definiteness.
v=0.0;
for(i_=0;i_<=j-1;i_++)
v+=a[i_][j]*a[i_][j];
ajj=a[j][j]-v;
//--- check
if(ajj<=0.0)
{
//--- return result
return(false);
}
//--- change values
ajj=MathSqrt(ajj);
a[j].Set(j,ajj);
//--- Compute elements J+1:N of row J.
if(j<n-1)
{
for(i=j+1;i<=n-1;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=j-1;i_++)
v+=a[i_][i]*a[i_][j];
a[j].Set(i,a[j][i]-v);
}
v=1/ajj;
for(i_=j+1;i_<=n-1;i_++)
a[j].Set(i_,v*a[j][i_]);
}
}
}
else
{
//--- Compute the Cholesky factorization A=L*L'.
for(j=0;j<=n-1;j++)
{
//--- Compute L(J,J) and test for non-positive-definiteness.
v=0.0;
for(i_=0;i_<=j-1;i_++)
v+=a[j][i_]*a[j][i_];
ajj=a[j][j]-v;
//--- check
if(ajj<=0.0)
{
//--- return result
return(false);
}
ajj=MathSqrt(ajj);
a[j].Set(j,ajj);
//--- Compute elements J+1:N of column J.
if(j<n-1)
{
for(i=j+1;i<=n-1;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=j-1;i_++)
v+=a[i][i_]*a[j][i_];
a[i].Set(j,a[i][j]-v);
}
//--- change values
v=1/ajj;
for(i_=j+1;i_<=n-1;i_++)
a[i_].Set(j,v*a[i_][j]);
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Tests whether A is HPD |
//+------------------------------------------------------------------+
static bool CTestMatGenUnit::IsHPD(CMatrixComplex &ca,const int n)
{
//--- create variables
bool result;
int j=0;
double ajj=0;
complex v=0;
double r=0;
int i=0;
int i_=0;
//--- create arrays
complex t[];
complex t2[];
complex t3[];
//--- create matrix
CMatrixComplex a1;
CMatrixComplex a;
//--- copy
a=ca;
//--- allocation
ArrayResize(t,n);
ArrayResize(t2,n);
ArrayResize(t3,n);
//--- initialization
result=true;
//--- Compute the Cholesky factorization A=U'*U.
for(j=0;j<=n-1;j++)
{
//--- Compute U(J,J) and test for non-positive-definiteness.
v=0.0;
for(i_=0;i_<=j-1;i_++)
v+=CMath::Conj(a[i_][j])*a[i_][j];
ajj=(a[j][j]-v).re;
//--- check
if(ajj<=0.0)
{
a[j].Set(j,ajj);
//--- return result
return(false);
}
ajj=MathSqrt(ajj);
a[j].Set(j,ajj);
//--- Compute elements J+1:N-1 of row J.
if(j<n-1)
{
for(i_=0;i_<=j-1;i_++)
t2[i_]=CMath::Conj(a[i_][j]);
for(i_=j+1;i_<=n-1;i_++)
t3[i_]=a[j][i_];
//--- calculation
for(i=j+1;i<=n-1;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=j-1;i_++)
v+=a[i_][i]*t2[i_];
t3[i]=t3[i]-v;
}
for(i_=j+1;i_<=n-1;i_++)
a[j].Set(i_,t3[i_]);
//--- change values
r=1/ajj;
for(i_=j+1;i_<=n-1;i_++)
a[j].Set(i_,a[j][i_]*r);
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| SVD condition number |
//+------------------------------------------------------------------+
static double CTestMatGenUnit::SVDCond(CMatrixDouble &a,const int n)
{
//--- create variables
double result=0;
int i=0;
int j=0;
double minw=0;
double maxw=0;
//--- create array
double w[];
//--- create matrix
CMatrixDouble a1;
CMatrixDouble v;
//--- allocation
a1.Resize(n+1,n+1);
//--- change values
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
a1[i].Set(j,a[i-1][j-1]);
}
//--- check
if(!ObsoleteSVDDecomposition(a1,n,n,w,v))
{
//--- return result
return(0);
}
//--- change values
minw=w[1];
maxw=w[1];
for(i=2;i<=n;i++)
{
//--- check
if(w[i]<minw)
minw=w[i];
//--- check
if(w[i]>maxw)
maxw=w[i];
}
//--- return result
return(maxw/minw);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static bool CTestMatGenUnit::ObsoleteSVDDecomposition(CMatrixDouble &a,
const int m,
const int n,
double &w[],
CMatrixDouble &v)
{
//--- create variables
bool result;
int nm=0;
int minmn=0;
int l=0;
int k=0;
int j=0;
int jj=0;
int its=0;
int i=0;
double z=0;
double y=0;
double x=0;
double vscale=0;
double s=0;
double h=0;
double g=0;
double f=0;
double c=0;
double anorm=0;
bool flag;
//--- create array
double rv1[];
//--- allocation
ArrayResize(rv1,n+1);
ArrayResize(w,n+1);
v.Resize(n+1,n+1);
//--- initialization
result=true;
//--- check
if(m<n)
minmn=m;
else
minmn=n;
//--- initialization
g=0.0;
vscale=0.0;
anorm=0.0;
//--- calculation
for(i=1;i<=n;i++)
{
l=i+1;
rv1[i]=vscale*g;
g=0;
s=0;
vscale=0;
//--- check
if(i<=m)
{
for(k=i;k<=m;k++)
vscale=vscale+MathAbs(a[k][i]);
//--- check
if(vscale!=0.0)
{
for(k=i;k<=m;k++)
{
a[k].Set(i,a[k][i]/vscale);
s=s+a[k][i]*a[k][i];
}
//--- calculation
f=a[i][i];
g=-ExtSign(MathSqrt(s),f);
h=f*g-s;
a[i].Set(i,f-g);
//--- check
if(i!=n)
{
for(j=l;j<=n;j++)
{
s=0.0;
for(k=i;k<=m;k++)
s=s+a[k][i]*a[k][j];
//--- calculation
f=s/h;
for(k=i;k<=m;k++)
a[k].Set(j,a[k][j]+f*a[k][i]);
}
}
for(k=i;k<=m;k++)
a[k].Set(i,vscale*a[k][i]);
}
}
//--- change values
w[i]=vscale*g;
g=0.0;
s=0.0;
vscale=0.0;
//--- check
if(i<=m && i!=n)
{
for(k=l;k<=n;k++)
vscale=vscale+MathAbs(a[i][k]);
//--- check
if(vscale!=0.0)
{
for(k=l;k<=n;k++)
{
a[i].Set(k,a[i][k]/vscale);
s=s+a[i][k]*a[i][k];
}
//--- calculation
f=a[i][l];
g=-ExtSign(MathSqrt(s),f);
h=f*g-s;
a[i].Set(l,f-g);
for(k=l;k<=n;k++)
rv1[k]=a[i][k]/h;
//--- check
if(i!=m)
{
//--- calculation
for(j=l;j<=m;j++)
{
s=0.0;
for(k=l;k<=n;k++)
s=s+a[j][k]*a[i][k];
for(k=l;k<=n;k++)
a[j].Set(k,a[j][k]+s*rv1[k]);
}
}
for(k=l;k<=n;k++)
a[i].Set(k,vscale*a[i][k]);
}
}
//--- change value
anorm=MyMax(anorm,MathAbs(w[i])+MathAbs(rv1[i]));
}
for(i=n;i>=1;i--)
{
//--- check
if(i<n)
{
//--- check
if(g!=0.0)
{
for(j=l;j<=n;j++)
v[j].Set(i,a[i][j]/a[i][l]/g);
//--- calculation
for(j=l;j<=n;j++)
{
s=0.0;
for(k=l;k<=n;k++)
s=s+a[i][k]*v[k][j];
for(k=l;k<=n;k++)
v[k].Set(j,v[k][j]+s*v[k][i]);
}
}
//--- change values
for(j=l;j<=n;j++)
{
v[i].Set(j,0.0);
v[j].Set(i,0.0);
}
}
//--- change values
v[i].Set(i,1.0);
g=rv1[i];
l=i;
}
//--- calculation
for(i=minmn;i>=1;i--)
{
l=i+1;
g=w[i];
//--- check
if(i<n)
{
for(j=l;j<=n;j++)
a[i].Set(j,0.0);
}
//--- check
if(g!=0.0)
{
g=1.0/g;
//--- check
if(i!=n)
{
//--- calculation
for(j=l;j<=n;j++)
{
s=0.0;
for(k=l;k<=m;k++)
s=s+a[k][i]*a[k][j];
//--- change values
f=s/a[i][i]*g;
for(k=i;k<=m;k++)
a[k].Set(j,a[k][j]+f*a[k][i]);
}
}
for(j=i;j<=m;j++)
a[j].Set(i,a[j][i]*g);
}
else
{
//--- change values
for(j=i;j<=m;j++)
a[j].Set(i,0.0);
}
a[i].Set(i,a[i][i]+1.0);
}
//--- calculation
for(k=n;k>=1;k--)
{
for(its=1;its<=m_maxsvditerations;its++)
{
flag=true;
for(l=k;l>=1;l--)
{
nm=l-1;
//--- check
if(MathAbs(rv1[l])+anorm==anorm)
{
flag=false;
break;
}
//--- check
if(MathAbs(w[nm])+anorm==anorm)
break;
}
//--- check
if(flag)
{
c=0.0;
s=1.0;
//--- calculation
for(i=l;i<=k;i++)
{
f=s*rv1[i];
//--- check
if(MathAbs(f)+anorm!=anorm)
{
//--- change values
g=w[i];
h=PyThag(f,g);
w[i]=h;
h=1.0/h;
c=g*h;
s=-(f*h);
for(j=1;j<=m;j++)
{
y=a[j][nm];
z=a[j][i];
a[j].Set(nm,y*c+z*s);
a[j].Set(i,-(y*s)+z*c);
}
}
}
}
z=w[k];
//--- check
if(l==k)
{
//--- check
if(z<0.0)
{
w[k]=-z;
for(j=1;j<=n;j++)
v[j].Set(k,-v[j][k]);
}
break;
}
//--- check
if(its==m_maxsvditerations)
{
//--- return result
return(false);
}
//--- change values
x=w[l];
nm=k-1;
y=w[nm];
g=rv1[nm];
h=rv1[k];
f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);
g=PyThag(f,1);
f=((x-z)*(x+z)+h*(y/(f+ExtSign(g,f))-h))/x;
c=1.0;
s=1.0;
//--- calculation
for(j=l;j<=nm;j++)
{
i=j+1;
g=rv1[i];
y=w[i];
h=s*g;
g=c*g;
z=PyThag(f,h);
rv1[j]=z;
c=f/z;
s=h/z;
f=x*c+g*s;
g=-(x*s)+g*c;
h=y*s;
y=y*c;
for(jj=1;jj<=n;jj++)
{
x=v[jj][j];
z=v[jj][i];
v[jj].Set(j,x*c+z*s);
v[jj].Set(i,-(x*s)+z*c);
}
z=PyThag(f,h);
w[j]=z;
//--- check
if(z!=0.0)
{
z=1.0/z;
c=f*z;
s=h*z;
}
//--- calculation
f=c*g+s*y;
x=-(s*g)+c*y;
for(jj=1;jj<=m;jj++)
{
y=a[jj][j];
z=a[jj][i];
a[jj].Set(j,y*c+z*s);
a[jj].Set(i,-(y*s)+z*c);
}
}
//--- change values
rv1[l]=0.0;
rv1[k]=f;
w[k]=x;
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static double CTestMatGenUnit::ExtSign(const double a,const double b)
{
//--- create a variable
double result=0;
//--- check
if(b>=0.0)
result=MathAbs(a);
else
result=-MathAbs(a);
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static double CTestMatGenUnit::MyMax(const double a,const double b)
{
//--- create a variable
double result=0;
//--- check
if(a>b)
result=a;
else
result=b;
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static double CTestMatGenUnit::PyThag(const double a,const double b)
{
//--- create a variable
double result=0;
//--- check
if(MathAbs(a)<MathAbs(b))
result=MathAbs(b)*MathSqrt(1+CMath::Sqr(a/b));
else
result=MathAbs(a)*MathSqrt(1+CMath::Sqr(b/a));
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Testing class CTrFac |
//+------------------------------------------------------------------+
class CTestTrFacUnit
{
private:
//--- private methods
static void TestCLUProblem(CMatrixComplex &a,const int m,const int n,const double threshold,bool &err,bool &properr);
static void TestRLUProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &err,bool &properr);
public:
//--- constructor, destructor
CTestTrFacUnit(void);
~CTestTrFacUnit(void);
//--- public method
static bool TestTrFac(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestTrFacUnit::CTestTrFacUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestTrFacUnit::~CTestTrFacUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CTrFac |
//+------------------------------------------------------------------+
static bool CTestTrFacUnit::TestTrFac(const bool silent)
{
//--- create variables
int m=0;
int n=0;
int mx=0;
int maxmn=0;
int i=0;
int j=0;
complex vc=0;
double vr=0;
bool waserrors;
bool spderr;
bool hpderr;
bool rerr;
bool cerr;
bool properr;
double threshold=0;
int i_=0;
//--- create matrix
CMatrixDouble ra;
CMatrixDouble ral;
CMatrixDouble rau;
CMatrixComplex ca;
CMatrixComplex cal;
CMatrixComplex cau;
//--- initialization
rerr=false;
spderr=false;
cerr=false;
hpderr=false;
properr=false;
waserrors=false;
maxmn=4*CAblas::AblasBlockSize()+1;
threshold=1000*CMath::m_machineepsilon*maxmn;
//--- test LU
for(mx=1;mx<=maxmn;mx++)
{
//--- Initialize N/M,both are <=MX,
//--- at least one of them is exactly equal to MX
n=1+CMath::RandomInteger(mx);
m=1+CMath::RandomInteger(mx);
//--- check
if(CMath::RandomReal()>0.5)
n=mx;
else
m=mx;
//--- First,test on zero matrix
ra.Resize(m,n);
ca.Resize(m,n);
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
ra[i].Set(j,0);
ca[i].Set(j,0);
}
}
//--- function calls
TestCLUProblem(ca,m,n,threshold,cerr,properr);
TestRLUProblem(ra,m,n,threshold,rerr,properr);
//--- Second,random matrix with moderate condition number
ra.Resize(m,n);
ca.Resize(m,n);
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
ra[i].Set(j,0);
ca[i].Set(j,0);
}
}
//--- change values
for(i=0;i<=MathMin(m,n)-1;i++)
{
ra[i].Set(i,1+10*CMath::RandomReal());
ca[i].Set(i,1+10*CMath::RandomReal());
}
//--- function call
CMatGen::CMatrixRndOrthogonalFromTheLeft(ca,m,n);
//--- function call
CMatGen::CMatrixRndOrthogonalFromTheRight(ca,m,n);
//--- function call
CMatGen::RMatrixRndOrthogonalFromTheLeft(ra,m,n);
//--- function call
CMatGen::RMatrixRndOrthogonalFromTheRight(ra,m,n);
//--- function calls
TestCLUProblem(ca,m,n,threshold,cerr,properr);
TestRLUProblem(ra,m,n,threshold,rerr,properr);
}
//--- Test Cholesky
for(n=1;n<=maxmn;n++)
{
//--- Load CA (HPD matrix with low condition number),
//--- CAL and CAU - its lower and upper triangles
CMatGen::HPDMatrixRndCond(n,1+50*CMath::RandomReal(),ca);
//--- allocation
cal.Resize(n,n);
cau.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
cal[i].Set(j,i);
cau[i].Set(j,j);
}
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(i_=0;i_<=i;i_++)
cal[i].Set(i_,ca[i][i_]);
for(i_=i;i_<=n-1;i_++)
cau[i].Set(i_,ca[i][i_]);
}
//--- Test HPDMatrixCholesky:
//--- 1. it must leave upper (lower) part unchanged
//--- 2. max(A-L*L^H) must be small
if(CTrFac::HPDMatrixCholesky(cal,n,false))
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(j>i)
hpderr=hpderr || cal[i][j]!=i;
else
{
vc=0.0;
for(i_=0;i_<=j;i_++)
vc+=cal[i][i_]*CMath::Conj(cal[j][i_]);
//--- search errors
hpderr=hpderr || CMath::AbsComplex(ca[i][j]-vc)>threshold;
}
}
}
}
else
hpderr=true;
//--- check
if(CTrFac::HPDMatrixCholesky(cau,n,true))
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(j<i)
hpderr=hpderr || cau[i][j]!=j;
else
{
vc=0.0;
for(i_=0;i_<=i;i_++)
vc+=CMath::Conj(cau[i_][i])*cau[i_][j];
//--- search errors
hpderr=hpderr || CMath::AbsComplex(ca[i][j]-vc)>threshold;
}
}
}
}
else
hpderr=true;
//--- Load RA (SPD matrix with low condition number),
//--- RAL and RAU - its lower and upper triangles
CMatGen::SPDMatrixRndCond(n,1+50*CMath::RandomReal(),ra);
//--- allocation
ral.Resize(n,n);
rau.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
ral[i].Set(j,i);
rau[i].Set(j,j);
}
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(i_=0;i_<=i;i_++)
ral[i].Set(i_,ra[i][i_]);
for(i_=i;i_<=n-1;i_++)
rau[i].Set(i_,ra[i][i_]);
}
//--- Test SPDMatrixCholesky:
//--- 1. it must leave upper (lower) part unchanged
//--- 2. max(A-L*L^H) must be small
if(CTrFac::SPDMatrixCholesky(ral,n,false))
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(j>i)
spderr=spderr || ral[i][j]!=i;
else
{
vr=0.0;
for(i_=0;i_<=j;i_++)
vr+=ral[i][i_]*ral[j][i_];
//--- search errors
spderr=spderr || MathAbs(ra[i][j]-vr)>threshold;
}
}
}
}
else
spderr=true;
//--- check
if(CTrFac::SPDMatrixCholesky(rau,n,true))
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(j<i)
spderr=spderr || rau[i][j]!=j;
else
{
vr=0.0;
for(i_=0;i_<=i;i_++)
vr+=rau[i_][i]*rau[i_][j];
//--- search errors
spderr=spderr || MathAbs(ra[i][j]-vr)>threshold;
}
}
}
}
else
spderr=true;
}
//--- report
waserrors=(((rerr || spderr) || cerr) || hpderr) || properr;
//--- check
if(!silent)
{
Print("TESTING TRIANGULAR FACTORIZATIONS");
Print("* REAL: ");
//--- check
if(rerr)
Print("FAILED");
else
Print("OK");
Print("* SPD: ");
//--- check
if(spderr)
Print("FAILED");
else
Print("OK");
Print("* COMPLEX: ");
//--- check
if(cerr)
Print("FAILED");
else
Print("OK");
Print("* HPD: ");
//--- check
if(hpderr)
Print("FAILED");
else
Print("OK");
Print("* OTHER PROPERTIES: ");
//--- check
if(properr)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestTrFacUnit::TestCLUProblem(CMatrixComplex &a,const int m,
const int n,const double threshold,
bool &err,bool &properr)
{
//--- create variables
int i=0;
int j=0;
int minmn=0;
complex v=0;
int i_=0;
//--- create arrays
complex ct[];
int p[];
//--- create matrix
CMatrixComplex ca;
CMatrixComplex cl;
CMatrixComplex cu;
CMatrixComplex ca2;
//--- initialization
minmn=MathMin(m,n);
//--- PLU test
ca.Resize(m,n);
for(i=0;i<=m-1;i++)
{
for(i_=0;i_<=n-1;i_++)
ca[i].Set(i_,a[i][i_]);
}
//--- function call
CTrFac::CMatrixPLU(ca,m,n,p);
for(i=0;i<=minmn-1;i++)
{
//--- check
if(p[i]<i || p[i]>=m)
{
properr=false;
return;
}
}
//--- allocation
cl.Resize(m,minmn);
for(j=0;j<=minmn-1;j++)
{
for(i=0;i<=j-1;i++)
cl[i].Set(j,0.0);
//--- change values
cl[j].Set(j,1.0);
for(i=j+1;i<=m-1;i++)
cl[i].Set(j,ca[i][j]);
}
//--- allocation
cu.Resize(minmn,n);
//--- change values
for(i=0;i<=minmn-1;i++)
{
for(j=0;j<=i-1;j++)
cu[i].Set(j,0.0);
for(j=i;j<=n-1;j++)
cu[i].Set(j,ca[i][j]);
}
//--- allocation
ca2.Resize(m,n);
//--- calculation
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=minmn-1;i_++)
v+=cl[i][i_]*cu[i_][j];
ca2[i].Set(j,v);
}
}
//--- allocation
ArrayResize(ct,n);
//--- change values
for(i=minmn-1;i>=0;i--)
{
//--- check
if(i!=p[i])
{
for(i_=0;i_<=n-1;i_++)
ct[i_]=ca2[i][i_];
for(i_=0;i_<=n-1;i_++)
ca2[i].Set(i_,ca2[p[i]][i_]);
for(i_=0;i_<=n-1;i_++)
ca2[p[i]].Set(i_,ct[i_]);
}
}
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
err=err || CMath::AbsComplex(a[i][j]-ca2[i][j])>threshold;
}
//--- LUP test
ca.Resize(m,n);
for(i=0;i<=m-1;i++)
{
for(i_=0;i_<=n-1;i_++)
ca[i].Set(i_,a[i][i_]);
}
//--- function call
CTrFac::CMatrixLUP(ca,m,n,p);
for(i=0;i<=minmn-1;i++)
{
//--- check
if(p[i]<i || p[i]>=n)
{
properr=false;
return;
}
}
//--- allocation
cl.Resize(m,minmn);
//--- change values
for(j=0;j<=minmn-1;j++)
{
for(i=0;i<=j-1;i++)
cl[i].Set(j,0.0);
for(i=j;i<=m-1;i++)
cl[i].Set(j,ca[i][j]);
}
//--- allocation
cu.Resize(minmn,n);
//--- change values
for(i=0;i<=minmn-1;i++)
{
for(j=0;j<=i-1;j++)
cu[i].Set(j,0.0);
cu[i].Set(i,1.0);
for(j=i+1;j<=n-1;j++)
cu[i].Set(j,ca[i][j]);
}
//--- allocation
ca2.Resize(m,n);
//--- calculation
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=minmn-1;i_++)
v+=cl[i][i_]*cu[i_][j];
ca2[i].Set(j,v);
}
}
//--- allocation
ArrayResize(ct,m);
//--- change values
for(i=minmn-1;i>=0;i--)
{
//--- check
if(i!=p[i])
{
for(i_=0;i_<=m-1;i_++)
ct[i_]=ca2[i_][i];
for(i_=0;i_<=m-1;i_++)
ca2[i_].Set(i,ca2[i_][p[i]]);
for(i_=0;i_<=m-1;i_++)
ca2[i_].Set(p[i],ct[i_]);
}
}
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
err=err || CMath::AbsComplex(a[i][j]-ca2[i][j])>threshold;
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestTrFacUnit::TestRLUProblem(CMatrixDouble &a,const int m,
const int n,const double threshold,
bool &err,bool &properr)
{
//--- create variables
int i=0;
int j=0;
int minmn=0;
double v=0;
int i_=0;
//--- create arrays
double ct[];
int p[];
//--- create matrix
CMatrixDouble ca;
CMatrixDouble cl;
CMatrixDouble cu;
CMatrixDouble ca2;
//--- initialization
minmn=MathMin(m,n);
//--- PLU test
ca.Resize(m,n);
for(i=0;i<=m-1;i++)
{
for(i_=0;i_<=n-1;i_++)
ca[i].Set(i_,a[i][i_]);
}
//--- function call
CTrFac::RMatrixPLU(ca,m,n,p);
for(i=0;i<=minmn-1;i++)
{
//--- check
if(p[i]<i || p[i]>=m)
{
properr=false;
return;
}
}
//--- allocation
cl.Resize(m,minmn);
//--- change values
for(j=0;j<=minmn-1;j++)
{
for(i=0;i<=j-1;i++)
cl[i].Set(j,0.0);
cl[j].Set(j,1.0);
for(i=j+1;i<=m-1;i++)
cl[i].Set(j,ca[i][j]);
}
//--- allocation
cu.Resize(minmn,n);
//--- change values
for(i=0;i<=minmn-1;i++)
{
for(j=0;j<=i-1;j++)
cu[i].Set(j,0.0);
for(j=i;j<=n-1;j++)
cu[i].Set(j,ca[i][j]);
}
//--- allocation
ca2.Resize(m,n);
//--- calculation
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=minmn-1;i_++)
v+=cl[i][i_]*cu[i_][j];
ca2[i].Set(j,v);
}
}
//--- allocation
ArrayResize(ct,n);
for(i=minmn-1;i>=0;i--)
{
//--- check
if(i!=p[i])
{
//--- change values
for(i_=0;i_<=n-1;i_++)
ct[i_]=ca2[i][i_];
for(i_=0;i_<=n-1;i_++)
ca2[i].Set(i_,ca2[p[i]][i_]);
for(i_=0;i_<=n-1;i_++)
ca2[p[i]].Set(i_,ct[i_]);
}
}
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
err=err || MathAbs(a[i][j]-ca2[i][j])>threshold;
}
//--- LUP test
ca.Resize(m,n);
for(i=0;i<=m-1;i++)
{
for(i_=0;i_<=n-1;i_++)
ca[i].Set(i_,a[i][i_]);
}
//--- function call
CTrFac::RMatrixLUP(ca,m,n,p);
for(i=0;i<=minmn-1;i++)
{
//--- check
if(p[i]<i || p[i]>=n)
{
properr=false;
return;
}
}
//--- allocation
cl.Resize(m,minmn);
//--- change values
for(j=0;j<=minmn-1;j++)
{
for(i=0;i<=j-1;i++)
cl[i].Set(j,0.0);
for(i=j;i<=m-1;i++)
cl[i].Set(j,ca[i][j]);
}
//--- allocation
cu.Resize(minmn,n);
//--- change values
for(i=0;i<=minmn-1;i++)
{
for(j=0;j<=i-1;j++)
cu[i].Set(j,0.0);
cu[i].Set(i,1.0);
for(j=i+1;j<=n-1;j++)
cu[i].Set(j,ca[i][j]);
}
//--- allocation
ca2.Resize(m,n);
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=minmn-1;i_++)
v+=cl[i][i_]*cu[i_][j];
ca2[i].Set(j,v);
}
}
//--- allocation
ArrayResize(ct,m);
for(i=minmn-1;i>=0;i--)
{
//--- check
if(i!=p[i])
{
//--- change values
for(i_=0;i_<=m-1;i_++)
ct[i_]=ca2[i_][i];
for(i_=0;i_<=m-1;i_++)
ca2[i_].Set(i,ca2[i_][p[i]]);
for(i_=0;i_<=m-1;i_++)
ca2[i_].Set(p[i],ct[i_]);
}
}
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
err=err || MathAbs(a[i][j]-ca2[i][j])>threshold;
}
}
//+------------------------------------------------------------------+
//| Testing class CTrLinSolve |
//+------------------------------------------------------------------+
class CTestTrLinSolveUnit
{
private:
//--- private method
static void MakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
public:
//--- constructor, destructor
CTestTrLinSolveUnit(void);
~CTestTrLinSolveUnit(void);
//--- public method
static bool TestTrLinSolve(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestTrLinSolveUnit::CTestTrLinSolveUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestTrLinSolveUnit::~CTestTrLinSolveUnit(void)
{
}
//+------------------------------------------------------------------+
//| Main unittest subroutine |
//+------------------------------------------------------------------+
static bool CTestTrLinSolveUnit::TestTrLinSolve(const bool silent)
{
//--- create variables
int maxmn=0;
int passcount=0;
double threshold=0;
int n=0;
int pass=0;
int i=0;
int j=0;
int cnts=0;
int cntu=0;
int cntt=0;
int cntm=0;
bool waserrors;
bool isupper;
bool istrans;
bool isunit;
double v=0;
double s=0;
int i_=0;
//--- create arrays
double xe[];
double b[];
//--- create matrix
CMatrixDouble aeffective;
CMatrixDouble aparam;
//--- initialization
waserrors=false;
maxmn=15;
passcount=15;
threshold=1000*CMath::m_machineepsilon;
//--- Different problems
for(n=1;n<=maxmn;n++)
{
//--- allocation
aeffective.Resize(n,n);
aparam.Resize(n,n);
ArrayResize(xe,n);
ArrayResize(b,n);
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
for(cnts=0;cnts<=1;cnts++)
{
for(cntu=0;cntu<=1;cntu++)
{
for(cntt=0;cntt<=1;cntt++)
{
for(cntm=0;cntm<=2;cntm++)
{
isupper=cnts==0;
isunit=cntu==0;
istrans=cntt==0;
//--- Skip meaningless combinations of parameters:
//--- (matrix is singular) AND (matrix is unit diagonal)
if(cntm==2 && isunit)
continue;
//--- Clear matrices
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
aeffective[i].Set(j,0);
aparam[i].Set(j,0);
}
}
//--- Prepare matrices
if(isupper)
{
for(i=0;i<=n-1;i++)
{
for(j=i;j<=n-1;j++)
{
aeffective[i].Set(j,0.9*(2*CMath::RandomReal()-1));
aparam[i].Set(j,aeffective[i][j]);
}
//--- change values
aeffective[i].Set(i,(2*CMath::RandomInteger(2)-1)*(0.8+CMath::RandomReal()));
aparam[i].Set(i,aeffective[i][i]);
}
}
else
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=i;j++)
{
aeffective[i].Set(j,0.9*(2*CMath::RandomReal()-1));
aparam[i].Set(j,aeffective[i][j]);
}
//--- change values
aeffective[i].Set(i,(2*CMath::RandomInteger(2)-1)*(0.8+CMath::RandomReal()));
aparam[i].Set(i,aeffective[i][i]);
}
}
//--- check
if(isunit)
{
for(i=0;i<=n-1;i++)
{
aeffective[i].Set(i,1);
aparam[i].Set(i,0);
}
}
//--- check
if(istrans)
{
//--- check
if(isupper)
{
for(i=0;i<=n-1;i++)
{
for(j=i+1;j<=n-1;j++)
{
aeffective[j].Set(i,aeffective[i][j]);
aeffective[i].Set(j,0);
}
}
}
else
{
for(i=0;i<=n-1;i++)
{
for(j=i+1;j<=n-1;j++)
{
aeffective[i].Set(j,aeffective[j][i]);
aeffective[j].Set(i,0);
}
}
}
}
//--- Prepare task,solve,compare
for(i=0;i<=n-1;i++)
xe[i]=2*CMath::RandomReal()-1;
for(i=0;i<=n-1;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=aeffective[i][i_]*xe[i_];
b[i]=v;
}
//--- function call
CTrLinSolve::RMatrixTrSafeSolve(aparam,n,b,s,isupper,istrans,isunit);
//--- calculation
for(i_=0;i_<=n-1;i_++)
xe[i_]=s*xe[i_];
for(i_=0;i_<=n-1;i_++)
xe[i_]=xe[i_]-b[i_];
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=xe[i_]*xe[i_];
v=MathSqrt(v);
//--- search errors
waserrors=waserrors || v>threshold;
}
}
}
}
}
}
//--- report
if(!silent)
{
Print("TESTING RMatrixTrSafeSolve");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
static void CTestTrLinSolveUnit::MakeACopy(CMatrixDouble &a,const int m,
const int n,CMatrixDouble &b)
{
//--- create variables
int i=0;
int j=0;
//--- allocation
b.Resize(m,n);
//--- copy
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
b[i].Set(j,a[i][j]);
}
}
//+------------------------------------------------------------------+
//| Testing class CSafeSolve |
//+------------------------------------------------------------------+
class CTestSafeSolveUnit
{
private:
//--- private methods
static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b);
public:
//--- constructor, destructor
CTestSafeSolveUnit(void);
~CTestSafeSolveUnit(void);
//--- public method
static bool TestSafeSolve(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestSafeSolveUnit::CTestSafeSolveUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestSafeSolveUnit::~CTestSafeSolveUnit(void)
{
}
//+------------------------------------------------------------------+
//| Main unittest subroutine |
//+------------------------------------------------------------------+
static bool CTestSafeSolveUnit::TestSafeSolve(const bool silent)
{
//--- create variables
int maxmn=0;
double threshold=0;
bool rerrors;
bool cerrors;
bool waserrors;
bool isupper;
int trans=0;
bool isunit;
double scalea=0;
double growth=0;
int i=0;
int j=0;
int n=0;
int j1=0;
int j2=0;
complex cv=0;
double rv=0;
int i_=0;
//--- create arrays
complex cxs[];
complex cxe[];
double rxs[];
double rxe[];
//--- create matrix
CMatrixComplex ca;
CMatrixComplex cea;
CMatrixComplex ctmpa;
CMatrixDouble ra;
CMatrixDouble rea;
CMatrixDouble rtmpa;
//--- initialization
maxmn=30;
threshold=100000*CMath::m_machineepsilon;
rerrors=false;
cerrors=false;
waserrors=false;
//--- Different problems: general tests
for(n=1;n<=maxmn;n++)
{
//--- test complex solver with well-conditioned matrix:
//--- 1. generate A: fill off-diagonal elements with small values,
//--- diagonal elements are filled with larger values
//--- 2. generate 'effective' A
//--- 3. prepare task (exact X is stored in CXE,right part - in CXS),
//--- solve and compare CXS and CXE
isupper=CMath::RandomReal()>0.5;
trans=CMath::RandomInteger(3);
isunit=CMath::RandomReal()>0.5;
scalea=CMath::RandomReal()+0.5;
//--- allocation
ca.Resize(n,n);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(i==j)
{
ca[i].SetRe(j,(2*CMath::RandomInteger(2)-1)*(5+CMath::RandomReal()));
ca[i].SetIm(j,(2*CMath::RandomInteger(2)-1)*(5+CMath::RandomReal()));
}
else
{
ca[i].SetRe(j,0.2*CMath::RandomReal()-0.1);
ca[i].SetIm(j,0.2*CMath::RandomReal()-0.1);
}
}
}
//--- function call
CMatrixMakeACopy(ca,n,n,ctmpa);
for(i=0;i<=n-1;i++)
{
//--- check
if(isupper)
{
j1=0;
j2=i-1;
}
else
{
j1=i+1;
j2=n-1;
}
for(j=j1;j<=j2;j++)
ctmpa[i].Set(j,0);
//--- check
if(isunit)
ctmpa[i].Set(i,1);
}
//--- allocation
cea.Resize(n,n);
for(i=0;i<=n-1;i++)
{
//--- check
if(trans==0)
{
for(i_=0;i_<=n-1;i_++)
cea[i].Set(i_,ctmpa[i][i_]*scalea);
}
//--- check
if(trans==1)
{
for(i_=0;i_<=n-1;i_++)
cea[i_].Set(i,ctmpa[i][i_]*scalea);
}
//--- check
if(trans==2)
{
for(i_=0;i_<=n-1;i_++)
cea[i_].Set(i,CMath::Conj(ctmpa[i][i_])*scalea);
}
}
//--- allocation
ArrayResize(cxe,n);
//--- change values
for(i=0;i<=n-1;i++)
{
cxe[i].re=2*CMath::RandomReal()-1;
cxe[i].im=2*CMath::RandomReal()-1;
}
//--- allocation
ArrayResize(cxs,n);
for(i=0;i<=n-1;i++)
{
//--- change value
cv=0.0;
for(i_=0;i_<=n-1;i_++)
cv+=cea[i][i_]*cxe[i_];
cxs[i]=cv;
}
//--- check
if(CSafeSolve::CMatrixScaledTrSafeSolve(ca,scalea,n,cxs,isupper,trans,isunit,MathSqrt(CMath::m_maxrealnumber)))
{
for(i=0;i<=n-1;i++)
cerrors=cerrors || CMath::AbsComplex(cxs[i]-cxe[i])>threshold;
}
else
cerrors=true;
//--- same with real
isupper=CMath::RandomReal()>0.5;
trans=CMath::RandomInteger(2);
isunit=CMath::RandomReal()>0.5;
scalea=CMath::RandomReal()+0.5;
//--- allocation
ra.Resize(n,n);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(i==j)
ra[i].Set(j,(2*CMath::RandomInteger(2)-1)*(5+CMath::RandomReal()));
else
ra[i].Set(j,0.2*CMath::RandomReal()-0.1);
}
}
//--- function call
RMatrixMakeACopy(ra,n,n,rtmpa);
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- check
if(isupper)
{
j1=0;
j2=i-1;
}
else
{
j1=i+1;
j2=n-1;
}
for(j=j1;j<=j2;j++)
rtmpa[i].Set(j,0);
//--- check
if(isunit)
rtmpa[i].Set(i,1);
}
//--- allocation
rea.Resize(n,n);
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- check
if(trans==0)
{
for(i_=0;i_<=n-1;i_++)
rea[i].Set(i_,scalea*rtmpa[i][i_]);
}
//--- check
if(trans==1)
{
for(i_=0;i_<=n-1;i_++)
rea[i_].Set(i,scalea*rtmpa[i][i_]);
}
}
//--- allocation
ArrayResize(rxe,n);
for(i=0;i<=n-1;i++)
rxe[i]=2*CMath::RandomReal()-1;
//--- allocation
ArrayResize(rxs,n);
for(i=0;i<=n-1;i++)
{
//--- change value
rv=0.0;
for(i_=0;i_<=n-1;i_++)
rv+=rea[i][i_]*rxe[i_];
rxs[i]=rv;
}
//--- check
if(CSafeSolve::RMatrixScaledTrSafeSolve(ra,scalea,n,rxs,isupper,trans,isunit,MathSqrt(CMath::m_maxrealnumber)))
{
for(i=0;i<=n-1;i++)
rerrors=rerrors || MathAbs(rxs[i]-rxe[i])>threshold;
}
else
rerrors=true;
}
//--- Special test with diagonal ill-conditioned matrix:
//--- * ability to solve it when resulting growth is less than threshold
//--- * ability to stop solve when resulting growth is greater than threshold
//--- A=diag(1,1/growth)
//--- b=(1,0.5)
n=2;
growth=10;
//--- allocation
ca.Resize(n,n);
//--- change values
ca[0].Set(0,1);
ca[0].Set(1,0);
ca[1].Set(0,0);
ca[1].Set(1,1/growth);
//--- allocation
ArrayResize(cxs,n);
//--- change values
cxs[0]=1.0;
cxs[1]=0.5;
//--- search errors
cerrors=cerrors || !CSafeSolve::CMatrixScaledTrSafeSolve(ca,1.0,n,cxs,CMath::RandomReal()>0.5,CMath::RandomInteger(3),false,1.05*MathMax(CMath::AbsComplex(cxs[1])*growth,1.0));
cerrors=cerrors || !CSafeSolve::CMatrixScaledTrSafeSolve(ca,1.0,n,cxs,CMath::RandomReal()>0.5,CMath::RandomInteger(3),false,0.95*MathMax(CMath::AbsComplex(cxs[1])*growth,1.0));
//--- allocation
ra.Resize(n,n);
//--- change values
ra[0].Set(0,1);
ra[0].Set(1,0);
ra[1].Set(0,0);
ra[1].Set(1,1/growth);
//--- allocation
ArrayResize(rxs,n);
//--- change values
rxs[0]=1.0;
rxs[1]=0.5;
//--- search errors
rerrors=rerrors || !CSafeSolve::RMatrixScaledTrSafeSolve(ra,1.0,n,rxs,CMath::RandomReal()>0.5,CMath::RandomInteger(2),false,1.05*MathMax(MathAbs(rxs[1])*growth,1.0));
rerrors=rerrors || !CSafeSolve::RMatrixScaledTrSafeSolve(ra,1.0,n,rxs,CMath::RandomReal()>0.5,CMath::RandomInteger(2),false,0.95*MathMax(MathAbs(rxs[1])*growth,1.0));
//--- Special test with diagonal degenerate matrix:
//--- * ability to solve it when resulting growth is less than threshold
//--- * ability to stop solve when resulting growth is greater than threshold
//--- A=diag(1,0)
//--- b=(1,0.5)
n=2;
ca.Resize(n,n);
//--- change values
ca[0].Set(0,1);
ca[0].Set(1,0);
ca[1].Set(0,0);
ca[1].Set(1,0);
//--- allocation
ArrayResize(cxs,n);
//--- change values
cxs[0]=1.0;
cxs[1]=0.5;
//--- search errors
cerrors=cerrors || CSafeSolve::CMatrixScaledTrSafeSolve(ca,1.0,n,cxs,CMath::RandomReal()>0.5,CMath::RandomInteger(3),false,MathSqrt(CMath::m_maxrealnumber));
//--- allocation
ra.Resize(n,n);
//--- change values
ra[0].Set(0,1);
ra[0].Set(1,0);
ra[1].Set(0,0);
ra[1].Set(1,0);
//--- allocation
ArrayResize(rxs,n);
//--- change values
rxs[0]=1.0;
rxs[1]=0.5;
//--- search errors
rerrors=rerrors || CSafeSolve::RMatrixScaledTrSafeSolve(ra,1.0,n,rxs,CMath::RandomReal()>0.5,CMath::RandomInteger(2),false,MathSqrt(CMath::m_maxrealnumber));
//--- report
waserrors=rerrors || cerrors;
//--- check
if(!silent)
{
Print("TESTING SAFE TR SOLVER");
Print("REAL: ");
//--- check
if(!rerrors)
Print("OK");
else
Print("FAILED");
Print("COMPLEX: ");
//--- check
if(!cerrors)
Print("OK");
else
Print("FAILED");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
static void CTestSafeSolveUnit::RMatrixMakeACopy(CMatrixDouble &a,
const int m,const int n,
CMatrixDouble &b)
{
//--- create variables
int i=0;
int j=0;
//--- allocation
b.Resize(m,n);
//--- copy
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
b[i].Set(j,a[i][j]);
}
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
static void CTestSafeSolveUnit::CMatrixMakeACopy(CMatrixComplex &a,
const int m,const int n,
CMatrixComplex &b)
{
//--- create variables
int i=0;
int j=0;
//--- allocation
b.Resize(m,n);
//--- copy
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
b[i].Set(j,a[i][j]);
}
}
//+------------------------------------------------------------------+
//| Testing class CRCond |
//+------------------------------------------------------------------+
class CTestRCondUnit
{
private:
//--- private methods
static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
static void RMatrixDropHalf(CMatrixDouble &a,const int n,const bool droplower);
static void CMatrixDropHalf(CMatrixComplex &a,const int n,const bool droplower);
static void RMatrixGenZero(CMatrixDouble &a0,const int n);
static bool RMatrixInvMatTr(CMatrixDouble &a,const int n,const bool isupper,const bool isunittriangular);
static bool RMatrixInvMatLU(CMatrixDouble &a,int &pivots[],const int n);
static bool RMatrixInvMat(CMatrixDouble &a,const int n);
static void RMatrixRefRCond(CMatrixDouble &a,const int n,double &rc1,double &rcinf);
static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b);
static void CMatrixGenZero(CMatrixComplex &a0,const int n);
static bool CMatrixInvMatTr(CMatrixComplex &a,const int n,const bool isupper,const bool isunittriangular);
static bool CMatrixInvMatLU(CMatrixComplex &a,int &pivots[],const int n);
static bool CMatrixInvMat(CMatrixComplex &a,const int n);
static void CMatrixRefRCond(CMatrixComplex &a,const int n,double &rc1,double &rcinf);
static bool TestRMatrixTrRCond(const int maxn,const int passcount);
static bool TestCMatrixTrRCond(const int maxn,const int passcount);
static bool TestRMatrixRCond(const int maxn,const int passcount);
static bool TestSPDMatrixRCond(const int maxn,const int passcount);
static bool TestCMatrixRCond(const int maxn,const int passcount);
static bool TestHPDMatrixRCond(const int maxn,const int passcount);
public:
//--- class constants
static const double m_threshold50;
static const double m_threshold90;
//--- constructor, destructor
CTestRCondUnit(void);
~CTestRCondUnit(void);
//--- public method
static bool TestRCond(const bool silent);
};
//+------------------------------------------------------------------+
//| Initialize constants |
//+------------------------------------------------------------------+
const double CTestRCondUnit::m_threshold50=0.25;
const double CTestRCondUnit::m_threshold90=0.10;
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestRCondUnit::CTestRCondUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestRCondUnit::~CTestRCondUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CRCond |
//+------------------------------------------------------------------+
static bool CTestRCondUnit::TestRCond(const bool silent)
{
//--- create variables
int maxn=0;
int passcount=0;
bool waserrors;
bool rtrerr;
bool ctrerr;
bool rerr;
bool cerr;
bool spderr;
bool hpderr;
//--- initialization
maxn=10;
passcount=100;
//--- report
rtrerr=!TestRMatrixTrRCond(maxn,passcount);
ctrerr=!TestCMatrixTrRCond(maxn,passcount);
rerr=!TestRMatrixRCond(maxn,passcount);
cerr=!TestCMatrixRCond(maxn,passcount);
spderr=!TestSPDMatrixRCond(maxn,passcount);
hpderr=!TestHPDMatrixRCond(maxn,passcount);
waserrors=((((rtrerr || ctrerr) || rerr) || cerr) || spderr) || hpderr;
//--- check
if(!silent)
{
Print("TESTING RCOND");
Print("REAL TRIANGULAR: ");
//--- check
if(!rtrerr)
Print("OK");
else
Print("FAILED");
Print("COMPLEX TRIANGULAR: ");
//--- check
if(!ctrerr)
Print("OK");
else
Print("FAILED");
Print("REAL: ");
//--- check
if(!rerr)
Print("OK");
else
Print("FAILED");
Print("SPD: ");
//--- check
if(!spderr)
Print("OK");
else
Print("FAILED");
Print("HPD: ");
//--- check
if(!hpderr)
Print("OK");
else
Print("FAILED");
Print("COMPLEX: ");
//--- check
if(!cerr)
Print("OK");
else
Print("FAILED");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
static void CTestRCondUnit::RMatrixMakeACopy(CMatrixDouble &a,const int m,
const int n,CMatrixDouble &b)
{
//--- create variables
int i=0;
int j=0;
//--- allocation
b.Resize(m,n);
//--- copy
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
b[i].Set(j,a[i][j]);
}
}
//+------------------------------------------------------------------+
//| Drops upper or lower half of the matrix - fills it by special |
//| pattern which may be used later to ensure that this part wasn't |
//| changed |
//+------------------------------------------------------------------+
static void CTestRCondUnit::RMatrixDropHalf(CMatrixDouble &a,const int n,
const bool droplower)
{
//--- create variables
int i=0;
int j=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if((droplower && i>j) || (!droplower && i<j))
a[i].Set(j,1+2*i+3*j);
}
}
}
//+------------------------------------------------------------------+
//| Drops upper or lower half of the matrix - fills it by special |
//| pattern which may be used later to ensure that this part wasn't |
//| changed |
//+------------------------------------------------------------------+
static void CTestRCondUnit::CMatrixDropHalf(CMatrixComplex &a,const int n,
const bool droplower)
{
//--- create variables
int i=0;
int j=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if((droplower && i>j) || (!droplower && i<j))
a[i].Set(j,1+2*i+3*j);
}
}
}
//+------------------------------------------------------------------+
//| Generate matrix with given condition number C (2-norm) |
//+------------------------------------------------------------------+
static void CTestRCondUnit::RMatrixGenZero(CMatrixDouble &a0,const int n)
{
//--- create variables
int i=0;
int j=0;
//--- allocation
a0.Resize(n,n);
//--- make zero
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a0[i].Set(j,0);
}
}
//+------------------------------------------------------------------+
//| Triangular inverse |
//+------------------------------------------------------------------+
static bool CTestRCondUnit::RMatrixInvMatTr(CMatrixDouble &a,const int n,
const bool isupper,
const bool isunittriangular)
{
//--- create variables
bool result;
bool nounit;
int i=0;
int j=0;
double v=0;
double ajj=0;
int i_=0;
//--- create array
double t[];
//--- initialization
result=true;
//--- allocation
ArrayResize(t,n);
//--- Test the input parameters.
nounit=!isunittriangular;
//--- check
if(isupper)
{
//--- Compute inverse of upper triangular matrix.
for(j=0;j<=n-1;j++)
{
//--- check
if(nounit)
{
//--- check
if(a[j][j]==0.0)
{
//--- return result
return(false);
}
a[j].Set(j,1/a[j][j]);
ajj=-a[j][j];
}
else
ajj=-1;
//--- Compute elements 1:j-1 of j-th column.
if(j>0)
{
for(i_=0;i_<=j-1;i_++)
t[i_]=a[i_][j];
for(i=0;i<=j-1;i++)
{
//--- check
if(i<j-1)
{
//--- change value
v=0.0;
for(i_=i+1;i_<=j-1;i_++)
v+=a[i][i_]*t[i_];
}
else
v=0;
//--- check
if(nounit)
a[i].Set(j,v+a[i][i]*t[i]);
else
a[i].Set(j,v+t[i]);
}
//--- calculation
for(i_=0;i_<=j-1;i_++)
a[i_].Set(j,ajj*a[i_][j]);
}
}
}
else
{
//--- Compute inverse of lower triangular matrix.
for(j=n-1;j>=0;j--)
{
//--- check
if(nounit)
{
//--- check
if(a[j][j]==0.0)
{
//--- return result
return(false);
}
a[j].Set(j,1/a[j][j]);
ajj=-a[j][j];
}
else
ajj=-1;
//--- check
if(j<n-1)
{
//--- Compute elements j+1:n of j-th column.
for(i_=j+1;i_<=n-1;i_++)
t[i_]=a[i_][j];
for(i=j+1;i<=n-1;i++)
{
//--- check
if(i>j+1)
{
//--- change value
v=0.0;
for(i_=j+1;i_<=i-1;i_++)
v+=a[i][i_]*t[i_];
}
else
v=0;
//--- check
if(nounit)
a[i].Set(j,v+a[i][i]*t[i]);
else
a[i].Set(j,v+t[i]);
}
//--- calculation
for(i_=j+1;i_<=n-1;i_++)
a[i_].Set(j,ajj*a[i_][j]);
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| LU inverse |
//+------------------------------------------------------------------+
static bool CTestRCondUnit::RMatrixInvMatLU(CMatrixDouble &a,int &pivots[],
const int n)
{
//--- create variables
bool result;
int i=0;
int j=0;
int jp=0;
double v=0;
int i_=0;
//--- create array
double work[];
//--- initialization
result=true;
//--- Quick return if possible
if(n==0)
{
//--- return result
return(result);
}
//--- allocation
ArrayResize(work,n);
//--- Form inv(U)
if(!RMatrixInvMatTr(a,n,true,false))
{
//--- return result
return(false);
}
//--- Solve the equation inv(A)*L=inv(U) for inv(A).
for(j=n-1;j>=0;j--)
{
//--- Copy current column of L to WORK and replace with zeros.
for(i=j+1;i<=n-1;i++)
{
work[i]=a[i][j];
a[i].Set(j,0);
}
//--- Compute current column of inv(A).
if(j<n-1)
{
for(i=0;i<=n-1;i++)
{
//--- change value
v=0.0;
for(i_=j+1;i_<=n-1;i_++)
v+=a[i][i_]*work[i_];
a[i].Set(j,a[i][j]-v);
}
}
}
//--- Apply column interchanges.
for(j=n-2;j>=0;j--)
{
jp=pivots[j];
//--- check
if(jp!=j)
{
for(i_=0;i_<=n-1;i_++)
work[i_]=a[i_][j];
for(i_=0;i_<=n-1;i_++)
a[i_].Set(j,a[i_][jp]);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(jp,work[i_]);
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Matrix inverse |
//+------------------------------------------------------------------+
static bool CTestRCondUnit::RMatrixInvMat(CMatrixDouble &a,const int n)
{
//--- create array
int pivots[];
//--- function call
CTrFac::RMatrixLU(a,n,n,pivots);
//--- return result
return(RMatrixInvMatLU(a,pivots,n));
}
//+------------------------------------------------------------------+
//| reference RCond |
//+------------------------------------------------------------------+
static void CTestRCondUnit::RMatrixRefRCond(CMatrixDouble &a,const int n,
double &rc1,double &rcinf)
{
//--- create variables
double nrm1a=0;
double nrminfa=0;
double nrm1inva=0;
double nrminfinva=0;
double v=0;
int k=0;
int i=0;
//--- create matrix
CMatrixDouble inva;
//--- initialization
rc1=0;
rcinf=0;
//--- inv A
RMatrixMakeACopy(a,n,n,inva);
//--- check
if(!RMatrixInvMat(inva,n))
{
rc1=0;
rcinf=0;
//--- exit the function
return;
}
//--- norm A
nrm1a=0;
nrminfa=0;
//--- calculation
for(k=0;k<=n-1;k++)
{
//--- change values
v=0;
for(i=0;i<=n-1;i++)
v=v+MathAbs(a[i][k]);
nrm1a=MathMax(nrm1a,v);
v=0;
for(i=0;i<=n-1;i++)
v=v+MathAbs(a[k][i]);
nrminfa=MathMax(nrminfa,v);
}
//--- norm inv A
nrm1inva=0;
nrminfinva=0;
//--- calculation
for(k=0;k<=n-1;k++)
{
//--- change values
v=0;
for(i=0;i<=n-1;i++)
v=v+MathAbs(inva[i][k]);
nrm1inva=MathMax(nrm1inva,v);
v=0;
for(i=0;i<=n-1;i++)
v=v+MathAbs(inva[k][i]);
nrminfinva=MathMax(nrminfinva,v);
}
//--- result
rc1=nrm1inva*nrm1a;
rcinf=nrminfinva*nrminfa;
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
static void CTestRCondUnit::CMatrixMakeACopy(CMatrixComplex &a,const int m,
const int n,CMatrixComplex &b)
{
//--- create variables
int i=0;
int j=0;
//--- allocation
b.Resize(m,n);
//--- copy
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
b[i].Set(j,a[i][j]);
}
}
//+------------------------------------------------------------------+
//| Generate matrix with given condition number C (2-norm) |
//+------------------------------------------------------------------+
static void CTestRCondUnit::CMatrixGenZero(CMatrixComplex &a0,const int n)
{
//--- create variables
int i=0;
int j=0;
//--- allocation
a0.Resize(n,n);
//--- copy
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a0[i].Set(j,0);
}
}
//+------------------------------------------------------------------+
//| triangular inverse |
//+------------------------------------------------------------------+
static bool CTestRCondUnit::CMatrixInvMatTr(CMatrixComplex &a,const int n,
const bool isupper,
const bool isunittriangular)
{
//--- create variables
bool result;
bool nounit;
int i=0;
int j=0;
complex v=0;
complex ajj=0;
complex one=1;
int i_=0;
//--- create array
complex t[];
//--- initialization
result=true;
//--- allocation
ArrayResize(t,n);
//--- Test the input parameters.
nounit=!isunittriangular;
//--- check
if(isupper)
{
//--- Compute inverse of upper triangular matrix.
for(j=0;j<=n-1;j++)
{
//--- check
if(nounit)
{
//--- check
if(a[j][j]==0)
{
//--- return result
return(false);
}
a[j].Set(j,one/a[j][j]);
ajj=-a[j][j];
}
else
ajj=-1;
//--- Compute elements 1:j-1 of j-th column.
if(j>0)
{
for(i_=0;i_<=j-1;i_++)
t[i_]=a[i_][j];
//--- calculation
for(i=0;i<=j-1;i++)
{
//--- check
if(i<j-1)
{
//--- change value
v=0.0;
for(i_=i+1;i_<=j-1;i_++)
v+=a[i][i_]*t[i_];
}
else
v=0;
//--- check
if(nounit)
a[i].Set(j,v+a[i][i]*t[i]);
else
a[i].Set(j,v+t[i]);
}
for(i_=0;i_<=j-1;i_++)
a[i_].Set(j,ajj*a[i_][j]);
}
}
}
else
{
//--- Compute inverse of lower triangular matrix.
for(j=n-1;j>=0;j--)
{
//--- check
if(nounit)
{
//--- check
if(a[j][j]==0)
{
//--- return result
return(false);
}
a[j].Set(j,one/a[j][j]);
ajj=-a[j][j];
}
else
ajj=-1;
//--- check
if(j<n-1)
{
//--- Compute elements j+1:n of j-th column.
for(i_=j+1;i_<=n-1;i_++)
t[i_]=a[i_][j];
//--- calculation
for(i=j+1;i<=n-1;i++)
{
//--- check
if(i>j+1)
{
//--- change value
v=0.0;
for(i_=j+1;i_<=i-1;i_++)
v+=a[i][i_]*t[i_];
}
else
v=0;
//--- check
if(nounit)
a[i].Set(j,v+a[i][i]*t[i]);
else
a[i].Set(j,v+t[i]);
}
for(i_=j+1;i_<=n-1;i_++)
a[i_].Set(j,ajj*a[i_][j]);
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| LU inverse |
//+------------------------------------------------------------------+
static bool CTestRCondUnit::CMatrixInvMatLU(CMatrixComplex &a,int &pivots[],
const int n)
{
//--- create variables
bool result;
int i=0;
int j=0;
int jp=0;
complex v=0;
int i_=0;
//--- create array
complex work[];
//--- initialization
result=true;
//--- Quick return if possible
if(n==0)
{
//--- return result
return(result);
}
//--- allocation
ArrayResize(work,n);
//--- Form inv(U)
if(!CMatrixInvMatTr(a,n,true,false))
{
//--- return result
return(false);
}
//--- Solve the equation inv(A)*L=inv(U) for inv(A).
for(j=n-1;j>=0;j--)
{
//--- Copy current column of L to WORK and replace with zeros.
for(i=j+1;i<=n-1;i++)
{
work[i]=a[i][j];
a[i].Set(j,0);
}
//--- Compute current column of inv(A).
if(j<n-1)
{
for(i=0;i<=n-1;i++)
{
//--- change value
v=0.0;
for(i_=j+1;i_<=n-1;i_++)
v+=a[i][i_]*work[i_];
a[i].Set(j,a[i][j]-v);
}
}
}
//--- Apply column interchanges.
for(j=n-2;j>=0;j--)
{
jp=pivots[j];
//--- check
if(jp!=j)
{
//--- change values
for(i_=0;i_<=n-1;i_++)
work[i_]=a[i_][j];
for(i_=0;i_<=n-1;i_++)
a[i_].Set(j,a[i_][jp]);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(jp,work[i_]);
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Matrix inverse |
//+------------------------------------------------------------------+
static bool CTestRCondUnit::CMatrixInvMat(CMatrixComplex &a,const int n)
{
//--- create array
int pivots[];
//--- function call
CTrFac::CMatrixLU(a,n,n,pivots);
//--- return result
return(CMatrixInvMatLU(a,pivots,n));
}
//+------------------------------------------------------------------+
//| reference RCond |
//+------------------------------------------------------------------+
static void CTestRCondUnit::CMatrixRefRCond(CMatrixComplex &a,const int n,
double &rc1,double &rcinf)
{
//--- create variables
double nrm1a=0;
double nrminfa=0;
double nrm1inva=0;
double nrminfinva=0;
double v=0;
int k=0;
int i=0;
//--- create matrix
CMatrixComplex inva;
//--- initialization
rc1=0;
rcinf=0;
//--- inv A
CMatrixMakeACopy(a,n,n,inva);
//--- check
if(!CMatrixInvMat(inva,n))
{
rc1=0;
rcinf=0;
//--- exit the function
return;
}
//--- norm A
nrm1a=0;
nrminfa=0;
//--- calculation
for(k=0;k<=n-1;k++)
{
v=0;
for(i=0;i<=n-1;i++)
v=v+CMath::AbsComplex(a[i][k]);
nrm1a=MathMax(nrm1a,v);
//--- change value
v=0;
for(i=0;i<=n-1;i++)
v=v+CMath::AbsComplex(a[k][i]);
nrminfa=MathMax(nrminfa,v);
}
//--- norm inv A
nrm1inva=0;
nrminfinva=0;
//--- calculation
for(k=0;k<=n-1;k++)
{
v=0;
for(i=0;i<=n-1;i++)
v=v+CMath::AbsComplex(inva[i][k]);
nrm1inva=MathMax(nrm1inva,v);
//--- change value
v=0;
for(i=0;i<=n-1;i++)
v=v+CMath::AbsComplex(inva[k][i]);
nrminfinva=MathMax(nrminfinva,v);
}
//--- result
rc1=nrm1inva*nrm1a;
rcinf=nrminfinva*nrminfa;
}
//+------------------------------------------------------------------+
//| Returns True for successful test,False - for failed test |
//+------------------------------------------------------------------+
static bool CTestRCondUnit::TestRMatrixTrRCond(const int maxn,const int passcount)
{
//--- create variables
bool result;
int n=0;
int i=0;
int j=0;
int j1=0;
int j2=0;
int pass=0;
bool err50;
bool err90;
bool errspec;
bool errless;
double erc1=0;
double ercinf=0;
double v=0;
bool isupper;
bool isunit;
//--- create arrays
int p[];
double q50[];
double q90[];
//--- create matrix
CMatrixDouble a;
CMatrixDouble ea;
//--- initialization
err50=false;
err90=false;
errless=false;
errspec=false;
//--- allocation
ArrayResize(q50,2);
ArrayResize(q90,2);
//--- calculation
for(n=1;n<=maxn;n++)
{
//--- special test for zero matrix
RMatrixGenZero(a,n);
//--- search errors
errspec=errspec || CRCond::RMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
errspec=errspec || CRCond::RMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
//--- general test
a.Resize(n,n);
for(i=0;i<=1;i++)
{
q50[i]=0;
q90[i]=0;
}
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- change values
isupper=CMath::RandomReal()>0.5;
isunit=CMath::RandomReal()>0.5;
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,CMath::RandomReal()-0.5);
}
for(i=0;i<=n-1;i++)
a[i].Set(i,1+CMath::RandomReal());
//--- function call
RMatrixMakeACopy(a,n,n,ea);
for(i=0;i<=n-1;i++)
{
//--- check
if(isupper)
{
j1=0;
j2=i-1;
}
else
{
j1=i+1;
j2=n-1;
}
//--- change values
for(j=j1;j<=j2;j++)
ea[i].Set(j,0);
//--- check
if(isunit)
ea[i].Set(i,1);
}
//--- function call
RMatrixRefRCond(ea,n,erc1,ercinf);
//--- 1-norm
v=1/CRCond::RMatrixTrRCond1(a,n,isupper,isunit);
//--- check
if(v>=m_threshold50*erc1)
q50[0]=q50[0]+1.0/(double)passcount;
//--- check
if(v>=m_threshold90*erc1)
q90[0]=q90[0]+1.0/(double)passcount;
//--- search errors
errless=errless || v>erc1*1.001;
//--- Inf-norm
v=1/CRCond::RMatrixTrRCondInf(a,n,isupper,isunit);
//--- check
if(v>=m_threshold50*ercinf)
q50[1]=q50[1]+1.0/(double)passcount;
//--- check
if(v>=(double)(m_threshold90*ercinf))
q90[1]=q90[1]+1.0/(double)passcount;
//--- search errors
errless=errless || v>ercinf*1.001;
}
//--- search errors
for(i=0;i<=1;i++)
{
err50=err50 || q50[i]<0.5;
err90=err90 || q90[i]<0.9;
}
//--- degenerate matrix test
if(n>=3)
{
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0.0);
}
a[0].Set(0,1);
a[n-1].Set(n-1,1);
//--- search errors
errspec=errspec || CRCond::RMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
errspec=errspec || CRCond::RMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
}
//--- near-degenerate matrix test
if(n>=2)
{
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0.0);
}
//--- change values
for(i=0;i<=n-1;i++)
a[i].Set(i,1);
i=CMath::RandomInteger(n);
a[i].Set(i,0.1*CMath::m_maxrealnumber);
//--- search errors
errspec=errspec || CRCond::RMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
errspec=errspec || CRCond::RMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
}
}
//--- report
result=!(((err50 || err90) || errless) || errspec);
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Returns True for successful test,False - for failed test |
//+------------------------------------------------------------------+
static bool CTestRCondUnit::TestCMatrixTrRCond(const int maxn,const int passcount)
{
//--- create variables
bool result;
int n=0;
int i=0;
int j=0;
int j1=0;
int j2=0;
int pass=0;
bool err50;
bool err90;
bool errspec;
bool errless;
double erc1=0;
double ercinf=0;
double v=0;
bool isupper;
bool isunit;
//--- create arrays
int p[];
double q50[];
double q90[];
//--- create matrix
CMatrixComplex a;
CMatrixComplex ea;
//--- initialization
err50=false;
err90=false;
errless=false;
errspec=false;
//--- allocation
ArrayResize(q50,2);
ArrayResize(q90,2);
//--- calculation
for(n=1;n<=maxn;n++)
{
//--- special test for zero matrix
CMatrixGenZero(a,n);
//--- search errors
errspec=errspec || CRCond::CMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
errspec=errspec || CRCond::CMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
//--- general test
a.Resize(n,n);
for(i=0;i<=1;i++)
{
q50[i]=0;
q90[i]=0;
}
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- change values
isupper=CMath::RandomReal()>0.5;
isunit=CMath::RandomReal()>0.5;
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a[i].SetRe(j,CMath::RandomReal()-0.5);
a[i].SetIm(j,CMath::RandomReal()-0.5);
}
}
//--- change values
for(i=0;i<=n-1;i++)
{
a[i].SetRe(i,1+CMath::RandomReal());
a[i].SetIm(i,1+CMath::RandomReal());
}
//--- function call
CMatrixMakeACopy(a,n,n,ea);
//--- change values
for(i=0;i<=n-1;i++)
{
//--- check
if(isupper)
{
j1=0;
j2=i-1;
}
else
{
j1=i+1;
j2=n-1;
}
for(j=j1;j<=j2;j++)
ea[i].Set(j,0);
//--- check
if(isunit)
ea[i].Set(i,1);
}
//--- function call
CMatrixRefRCond(ea,n,erc1,ercinf);
//--- 1-norm
v=1/CRCond::CMatrixTrRCond1(a,n,isupper,isunit);
if(v>=m_threshold50*erc1)
q50[0]=q50[0]+1.0/(double)passcount;
//--- check
if(v>=(double)(m_threshold90*erc1))
q90[0]=q90[0]+1.0/(double)passcount;
//--- search errors
errless=errless || v>(double)(erc1*1.001);
//--- Inf-norm
v=1/CRCond::CMatrixTrRCondInf(a,n,isupper,isunit);
//--- check
if(v>=m_threshold50*ercinf)
q50[1]=q50[1]+1.0/(double)passcount;
//--- check
if(v>=(double)(m_threshold90*ercinf))
q90[1]=q90[1]+1.0/(double)passcount;
//--- search errors
errless=errless || v>(double)(ercinf*1.001);
}
//--- search errors
for(i=0;i<=1;i++)
{
err50=err50 || q50[i]<0.5;
err90=err90 || q90[i]<0.9;
}
//--- degenerate matrix test
if(n>=3)
{
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0.0);
}
a[0].Set(0,1);
a[n-1].Set(n-1,1);
//--- search errors
errspec=errspec || CRCond::CMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
errspec=errspec || CRCond::CMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
}
//--- near-degenerate matrix test
if(n>=2)
{
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0.0);
}
//--- change values
for(i=0;i<=n-1;i++)
a[i].Set(i,1);
i=CMath::RandomInteger(n);
a[i].Set(i,0.1*CMath::m_maxrealnumber);
//--- search errors
errspec=errspec || CRCond::CMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
errspec=errspec || CRCond::CMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
}
}
//--- report
result=!(((err50 || err90) || errless) || errspec);
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Returns True for successful test,False - for failed test |
//+------------------------------------------------------------------+
static bool CTestRCondUnit::TestRMatrixRCond(const int maxn,const int passcount)
{
//--- create variables
bool result;
int n=0;
int i=0;
int j=0;
int pass=0;
bool err50;
bool err90;
bool errspec;
bool errless;
double erc1=0;
double ercinf=0;
double v=0;
//--- create array
int p[];
double q50[];
double q90[];
//--- create matrix
CMatrixDouble a;
CMatrixDouble lua;
//--- initialization
err50=false;
err90=false;
errless=false;
errspec=false;
//--- allocation
ArrayResize(q50,4);
ArrayResize(q90,4);
//--- calculation
for(n=1;n<=maxn;n++)
{
//--- special test for zero matrix
RMatrixGenZero(a,n);
RMatrixMakeACopy(a,n,n,lua);
CTrFac::RMatrixLU(lua,n,n,p);
//--- search errors
errspec=errspec || CRCond::RMatrixRCond1(a,n)!=0.0;
errspec=errspec || CRCond::RMatrixRCondInf(a,n)!=0.0;
errspec=errspec || CRCond::RMatrixLURCond1(lua,n)!=0.0;
errspec=errspec || CRCond::RMatrixLURCondInf(lua,n)!=0.0;
//--- general test
a.Resize(n,n);
for(i=0;i<=3;i++)
{
q50[i]=0;
q90[i]=0;
}
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- function call
CMatGen::RMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a);
RMatrixMakeACopy(a,n,n,lua);
CTrFac::RMatrixLU(lua,n,n,p);
RMatrixRefRCond(a,n,erc1,ercinf);
//--- 1-norm,normal
v=1/CRCond::RMatrixRCond1(a,n);
//--- check
if(v>=m_threshold50*erc1)
q50[0]=q50[0]+1.0/(double)passcount;
//--- check
if(v>=m_threshold90*erc1)
q90[0]=q90[0]+1.0/(double)passcount;
//--- search errors
errless=errless || v>(double)(erc1*1.001);
//--- 1-norm,LU
v=1/CRCond::RMatrixLURCond1(lua,n);
//--- check
if(v>=m_threshold50*erc1)
q50[1]=q50[1]+1.0/(double)passcount;
//--- check
if(v>=m_threshold90*erc1)
q90[1]=q90[1]+1.0/(double)passcount;
//--- search errors
errless=errless || v>erc1*1.001;
//--- Inf-norm,normal
v=1/CRCond::RMatrixRCondInf(a,n);
//--- check
if(v>=m_threshold50*ercinf)
q50[2]=q50[2]+1.0/(double)passcount;
//--- check
if(v>=m_threshold90*ercinf)
q90[2]=q90[2]+1.0/(double)passcount;
//--- search errors
errless=errless || v>ercinf*1.001;
//--- Inf-norm,LU
v=1/CRCond::RMatrixLURCondInf(lua,n);
//--- check
if(v>=m_threshold50*ercinf)
q50[3]=q50[3]+1.0/(double)passcount;
//--- check
if(v>=m_threshold90*ercinf)
q90[3]=q90[3]+1.0/(double)passcount;
//--- search errors
errless=errless || v>ercinf*1.001;
}
//--- search errors
for(i=0;i<=3;i++)
{
err50=err50 || q50[i]<0.5;
err90=err90 || q90[i]<0.9;
}
//--- degenerate matrix test
if(n>=3)
{
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0.0);
}
a[0].Set(0,1);
a[n-1].Set(n-1,1);
//--- search errors
errspec=errspec || CRCond::RMatrixRCond1(a,n)!=0.0;
errspec=errspec || CRCond::RMatrixRCondInf(a,n)!=0.0;
errspec=errspec || CRCond::RMatrixLURCond1(a,n)!=0.0;
errspec=errspec || CRCond::RMatrixLURCondInf(a,n)!=0.0;
}
//--- near-degenerate matrix test
if(n>=2)
{
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0.0);
}
//--- change values
for(i=0;i<=n-1;i++)
a[i].Set(i,1);
i=CMath::RandomInteger(n);
a[i].Set(i,0.1*CMath::m_maxrealnumber);
//--- search errors
errspec=errspec || CRCond::RMatrixRCond1(a,n)!=0.0;
errspec=errspec || CRCond::RMatrixRCondInf(a,n)!=0.0;
errspec=errspec || CRCond::RMatrixLURCond1(a,n)!=0.0;
errspec=errspec || CRCond::RMatrixLURCondInf(a,n)!=0.0;
}
}
//--- report
result=!(((err50 || err90) || errless) || errspec);
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Returns True for successful test,False - for failed test |
//+------------------------------------------------------------------+
static bool CTestRCondUnit::TestSPDMatrixRCond(const int maxn,const int passcount)
{
//--- create variables
bool result;
int n=0;
int i=0;
int j=0;
int pass=0;
bool err50;
bool err90;
bool errspec;
bool errless;
bool isupper;
double erc1=0;
double ercinf=0;
double v=0;
//--- create arrays
int p[];
double q50[];
double q90[];
//--- create matrix
CMatrixDouble a;
CMatrixDouble cha;
//--- initialization
err50=false;
err90=false;
errless=false;
errspec=false;
//--- allocation
ArrayResize(q50,2);
ArrayResize(q90,2);
//--- calculation
for(n=1;n<=maxn;n++)
{
isupper=CMath::RandomReal()>0.5;
//--- general test
a.Resize(n,n);
for(i=0;i<=1;i++)
{
q50[i]=0;
q90[i]=0;
}
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- function calls
CMatGen::SPDMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a);
RMatrixRefRCond(a,n,erc1,ercinf);
RMatrixDropHalf(a,n,isupper);
RMatrixMakeACopy(a,n,n,cha);
CTrFac::SPDMatrixCholesky(cha,n,isupper);
//--- normal
v=1/CRCond::SPDMatrixRCond(a,n,isupper);
//--- check
if(v>=m_threshold50*erc1)
q50[0]=q50[0]+1.0/(double)passcount;
//--- check
if(v>=m_threshold90*erc1)
q90[0]=q90[0]+1.0/(double)passcount;
//--- search errors
errless=errless || v>erc1*1.001;
//--- Cholesky
v=1/CRCond::SPDMatrixCholeskyRCond(cha,n,isupper);
if(v>=m_threshold50*erc1)
q50[1]=q50[1]+1.0/(double)passcount;
//--- check
if(v>=m_threshold90*erc1)
q90[1]=q90[1]+1.0/(double)passcount;
//--- search errors
errless=errless || v>erc1*1.001;
}
//--- search errors
for(i=0;i<=1;i++)
{
err50=err50 || q50[i]<0.5;
err90=err90 || q90[i]<0.9;
}
//--- degenerate matrix test
if(n>=3)
{
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0.0);
}
a[0].Set(0,1);
a[n-1].Set(n-1,1);
//--- search errors
errspec=errspec || CRCond::SPDMatrixRCond(a,n,isupper)!=-1.0;
errspec=errspec || CRCond::SPDMatrixCholeskyRCond(a,n,isupper)!=0.0;
}
//--- near-degenerate matrix test
if(n>=2)
{
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0.0);
}
//--- change values
for(i=0;i<=n-1;i++)
a[i].Set(i,1);
i=CMath::RandomInteger(n);
a[i].Set(i,0.1*CMath::m_maxrealnumber);
//--- search errors
errspec=errspec || CRCond::SPDMatrixRCond(a,n,isupper)!=0.0;
errspec=errspec || CRCond::SPDMatrixCholeskyRCond(a,n,isupper)!=0.0;
}
}
//--- report
result=!(((err50 || err90) || errless) || errspec);
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Returns True for successful test,False - for failed test |
//+------------------------------------------------------------------+
static bool CTestRCondUnit::TestCMatrixRCond(const int maxn,const int passcount)
{
//--- create variables
bool result;
int n=0;
int i=0;
int j=0;
int pass=0;
bool err50;
bool err90;
bool errless;
bool errspec;
double erc1=0;
double ercinf=0;
double v=0;
//--- create arrays
int p[];
double q50[];
double q90[];
//--- create matrix
CMatrixComplex a;
CMatrixComplex lua;
//--- allocation
ArrayResize(q50,4);
ArrayResize(q90,4);
//--- initialization
err50=false;
err90=false;
errless=false;
errspec=false;
//--- process
for(n=1;n<=maxn;n++)
{
//--- special test for zero matrix
CMatrixGenZero(a,n);
CMatrixMakeACopy(a,n,n,lua);
CTrFac::CMatrixLU(lua,n,n,p);
//--- search errors
errspec=errspec || CRCond::CMatrixRCond1(a,n)!=0.0;
errspec=errspec || CRCond::CMatrixRCondInf(a,n)!=0.0;
errspec=errspec || CRCond::CMatrixLURCond1(lua,n)!=0.0;
errspec=errspec || CRCond::CMatrixLURCondInf(lua,n)!=0.0;
//--- general test
a.Resize(n,n);
for(i=0;i<=3;i++)
{
q50[i]=0;
q90[i]=0;
}
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- function calls
CMatGen::CMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a);
CMatrixMakeACopy(a,n,n,lua);
CTrFac::CMatrixLU(lua,n,n,p);
CMatrixRefRCond(a,n,erc1,ercinf);
//--- 1-norm,normal
v=1/CRCond::CMatrixRCond1(a,n);
//--- check
if(v>=m_threshold50*erc1)
q50[0]=q50[0]+1.0/(double)passcount;
//--- check
if(v>=m_threshold90*erc1)
q90[0]=q90[0]+1.0/(double)passcount;
//--- search errors
errless=errless || v>erc1*1.001;
//--- 1-norm,LU
v=1/CRCond::CMatrixLURCond1(lua,n);
//--- check
if(v>=m_threshold50*erc1)
q50[1]=q50[1]+1.0/(double)passcount;
//--- check
if(v>=m_threshold90*erc1)
q90[1]=q90[1]+1.0/(double)passcount;
//--- search errors
errless=errless || v>erc1*1.001;
//--- Inf-norm,normal
v=1/CRCond::CMatrixRCondInf(a,n);
//--- check
if(v>=m_threshold50*ercinf)
q50[2]=q50[2]+1.0/(double)passcount;
//--- check
if(v>=m_threshold90*ercinf)
q90[2]=q90[2]+1.0/(double)passcount;
//--- search errors
errless=errless || v>ercinf*1.001;
//--- Inf-norm,LU
v=1/CRCond::CMatrixLURCondInf(lua,n);
//--- check
if(v>=m_threshold50*ercinf)
q50[3]=q50[3]+1.0/(double)passcount;
//--- check
if(v>=m_threshold90*ercinf)
q90[3]=q90[3]+1.0/(double)passcount;
//--- search errors
errless=errless || v>ercinf*1.001;
}
//--- search errors
for(i=0;i<=3;i++)
{
err50=err50 || q50[i]<0.5;
err90=err90 || q90[i]<0.9;
}
//--- degenerate matrix test
if(n>=3)
{
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0.0);
}
a[0].Set(0,1);
a[n-1].Set(n-1,1);
//--- search errors
errspec=errspec || CRCond::CMatrixRCond1(a,n)!=0.0;
errspec=errspec || CRCond::CMatrixRCondInf(a,n)!=0.0;
errspec=errspec || CRCond::CMatrixLURCond1(a,n)!=0.0;
errspec=errspec || CRCond::CMatrixLURCondInf(a,n)!=0.0;
}
//--- near-degenerate matrix test
if(n>=2)
{
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0.0);
}
//--- change values
for(i=0;i<=n-1;i++)
a[i].Set(i,1);
i=CMath::RandomInteger(n);
a[i].Set(i,0.1*CMath::m_maxrealnumber);
//--- search errors
errspec=errspec || CRCond::CMatrixRCond1(a,n)!=0.0;
errspec=errspec || CRCond::CMatrixRCondInf(a,n)!=0.0;
errspec=errspec || CRCond::CMatrixLURCond1(a,n)!=0.0;
errspec=errspec || CRCond::CMatrixLURCondInf(a,n)!=0.0;
}
}
//--- report
result=!(((err50 || err90) || errless) || errspec);
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Returns True for successful test,False - for failed test |
//+------------------------------------------------------------------+
static bool CTestRCondUnit::TestHPDMatrixRCond(const int maxn,const int passcount)
{
//--- create variables
bool result;
int n=0;
int i=0;
int j=0;
int pass=0;
bool err50;
bool err90;
bool errspec;
bool errless;
bool isupper;
double erc1=0;
double ercinf=0;
double v=0;
//--- create arrays
int p[];
double q50[];
double q90[];
//--- create matrix
CMatrixComplex a;
CMatrixComplex cha;
//--- initialization
err50=false;
err90=false;
errless=false;
errspec=false;
//--- allocation
ArrayResize(q50,2);
ArrayResize(q90,2);
for(n=1;n<=maxn;n++)
{
isupper=CMath::RandomReal()>0.5;
//--- general test
a.Resize(n,n);
for(i=0;i<=1;i++)
{
q50[i]=0;
q90[i]=0;
}
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- function calls
CMatGen::HPDMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a);
CMatrixRefRCond(a,n,erc1,ercinf);
CMatrixDropHalf(a,n,isupper);
CMatrixMakeACopy(a,n,n,cha);
CTrFac::HPDMatrixCholesky(cha,n,isupper);
//--- normal
v=1/CRCond::HPDMatrixRCond(a,n,isupper);
//--- check
if(v>=m_threshold50*erc1)
q50[0]=q50[0]+1.0/(double)passcount;
//--- check
if(v>=m_threshold90*erc1)
q90[0]=q90[0]+1.0/(double)passcount;
//--- search errors
errless=errless || v>erc1*1.001;
//--- Cholesky
v=1/CRCond::HPDMatrixCholeskyRCond(cha,n,isupper);
//--- check
if(v>=m_threshold50*erc1)
q50[1]=q50[1]+1.0/(double)passcount;
//--- check
if(v>=m_threshold90*erc1)
q90[1]=q90[1]+1.0/(double)passcount;
//--- search errors
errless=errless || v>erc1*1.001;
}
//--- search errors
for(i=0;i<=1;i++)
{
err50=err50 || q50[i]<0.5;
err90=err90 || q90[i]<0.9;
}
//--- degenerate matrix test
if(n>=3)
{
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0.0);
}
a[0].Set(0,1);
a[n-1].Set(n-1,1);
//--- search errors
errspec=errspec || CRCond::HPDMatrixRCond(a,n,isupper)!=-1.0;
errspec=errspec || CRCond::HPDMatrixCholeskyRCond(a,n,isupper)!=0.0;
}
//--- near-degenerate matrix test
if(n>=2)
{
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0.0);
}
//--- change values
for(i=0;i<=n-1;i++)
a[i].Set(i,1);
i=CMath::RandomInteger(n);
a[i].Set(i,0.1*CMath::m_maxrealnumber);
//--- search errors
errspec=errspec || CRCond::HPDMatrixRCond(a,n,isupper)!=0.0;
errspec=errspec || CRCond::HPDMatrixCholeskyRCond(a,n,isupper)!=0.0;
}
}
//--- report
result=!(((err50 || err90) || errless) || errspec);
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Testing class CMatInv |
//+------------------------------------------------------------------+
class CTestMatInvUnit
{
private:
//--- private methods
static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b);
static bool RMatrixCheckInverse(CMatrixDouble &a,CMatrixDouble &inva,const int n,const double threshold,const int info,CMatInvReport &rep);
static bool SPDMatrixCheckInverse(CMatrixDouble &ca,CMatrixDouble &cinva,const bool isupper,const int n,const double threshold,const int info,CMatInvReport &rep);
static bool HPDMatrixCheckInverse(CMatrixComplex &ca,CMatrixComplex &cinva,const bool isupper,const int n,const double threshold,const int info,CMatInvReport &rep);
static bool RMatrixCheckInverseSingular(CMatrixDouble &inva,const int n,const double threshold,const int info,CMatInvReport &rep);
static bool CMatrixCheckInverse(CMatrixComplex &a,CMatrixComplex &inva,const int n,const double threshold,const int info,CMatInvReport &rep);
static bool CMatrixCheckInverseSingular(CMatrixComplex &inva,const int n,const double threshold,const int info,CMatInvReport &rep);
static void RMatrixDropHalf(CMatrixDouble &a,const int n,const bool droplower);
static void CMatrixDropHalf(CMatrixComplex &a,const int n,const bool droplower);
static void TestRTRInv(const int maxn,const int passcount,const double threshold,bool &rtrerrors);
static void TestCTRInv(const int maxn,const int passcount,const double threshold,bool &ctrerrors);
static void TesTrInv(const int maxn,const int passcount,const double threshold,bool &rerrors);
static void TestCInv(const int maxn,const int passcount,const double threshold,bool &cerrors);
static void TestSPDInv(const int maxn,const int passcount,const double threshold,bool &spderrors);
static void TestHPDInv(const int maxn,const int passcount,const double threshold,bool &hpderrors);
static void Unset2D(CMatrixDouble &x);
static void Unset1D(double &x[]);
static void CUnset2D(CMatrixComplex &x);
static void CUnset1D(double &x[]);
static void UnsetRep(CMatInvReport &r);
public:
//--- constructor, destructor
CTestMatInvUnit(void);
~CTestMatInvUnit(void);
//--- public method
static bool TestMatInv(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestMatInvUnit::CTestMatInvUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestMatInvUnit::~CTestMatInvUnit(void)
{
}
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
static bool CTestMatInvUnit::TestMatInv(const bool silent)
{
//--- create variables
int maxrn=0;
int maxcn=0;
int passcount=0;
double threshold=0;
double rcondtol=0;
bool rtrerrors;
bool ctrerrors;
bool rerrors;
bool cerrors;
bool spderrors;
bool hpderrors;
bool waserrors;
//--- create matrix
CMatrixDouble emptyra;
CMatrixDouble emptyca;
//--- initialization
maxrn=3*CAblas::AblasBlockSize()+1;
maxcn=3*CAblas::AblasBlockSize()+1;
passcount=1;
threshold=10000*CMath::m_machineepsilon;
rcondtol=0.01;
rtrerrors=false;
ctrerrors=false;
rerrors=false;
cerrors=false;
spderrors=false;
hpderrors=false;
//--- function calls
TestRTRInv(maxrn,passcount,threshold,rtrerrors);
TestCTRInv(maxcn,passcount,threshold,ctrerrors);
TesTrInv(maxrn,passcount,threshold,rerrors);
TestSPDInv(maxrn,passcount,threshold,spderrors);
TestCInv(maxcn,passcount,threshold,cerrors);
TestHPDInv(maxcn,passcount,threshold,hpderrors);
//--- search errors
waserrors=((((rtrerrors || ctrerrors) || rerrors) || cerrors) || spderrors) || hpderrors;
//--- check
if(!silent)
{
Print("TESTING MATINV");
Print("* REAL TRIANGULAR: ");
//--- check
if(rtrerrors)
Print("FAILED");
else
Print("OK");
Print("* COMPLEX TRIANGULAR: ");
//--- check
if(ctrerrors)
Print("FAILED");
else
Print("OK");
Print("* REAL: ");
//--- check
if(rerrors)
Print("FAILED");
else
Print("OK");
Print("* COMPLEX: ");
//--- check
if(cerrors)
Print("FAILED");
else
Print("OK");
Print("* SPD: ");
//--- check
if(spderrors)
Print("FAILED");
else
Print("OK");
Print("* HPD: ");
//--- check
if(hpderrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
static void CTestMatInvUnit::RMatrixMakeACopy(CMatrixDouble &a,
const int m,const int n,
CMatrixDouble &b)
{
//--- create variables
int i=0;
int j=0;
//--- allocation
b.Resize(m,n);
//--- copy
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
b[i].Set(j,a[i][j]);
}
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
static void CTestMatInvUnit::CMatrixMakeACopy(CMatrixComplex &a,
const int m,const int n,
CMatrixComplex &b)
{
//--- create variables
int i=0;
int j=0;
//--- allocation
b.Resize(m,n);
//--- copy
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
b[i].Set(j,a[i][j]);
}
}
//+------------------------------------------------------------------+
//| Checks whether inverse is correct |
//| Returns True on success. |
//+------------------------------------------------------------------+
static bool CTestMatInvUnit::RMatrixCheckInverse(CMatrixDouble &a,
CMatrixDouble &inva,
const int n,
const double threshold,
const int info,
CMatInvReport &rep)
{
//--- create variables
bool result;
int i=0;
int j=0;
double v=0;
int i_=0;
//--- initialization
result=true;
//--- check
if(info<=0)
result=false;
else
{
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*inva[i_][j];
//--- check
if(i==j)
v=v-1;
result=result && MathAbs(v)<=threshold;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Checks whether inverse is correct |
//| Returns True on success. |
//+------------------------------------------------------------------+
static bool CTestMatInvUnit::SPDMatrixCheckInverse(CMatrixDouble &ca,
CMatrixDouble &cinva,
const bool isupper,
const int n,
const double threshold,
const int info,
CMatInvReport &rep)
{
//--- create variables
bool result;
int i=0;
int j=0;
double v=0;
int i_=0;
//--- create matrix
CMatrixDouble a;
CMatrixDouble inva;
//--- copy
a=ca;
inva=cinva;
//--- calculation
for(i=0;i<=n-2;i++)
{
//--- check
if(isupper)
{
//--- change values
for(i_=i+1;i_<=n-1;i_++)
a[i_].Set(i,a[i][i_]);
for(i_=i+1;i_<=n-1;i_++)
inva[i_].Set(i,inva[i][i_]);
}
else
{
//--- change values
for(i_=i+1;i_<=n-1;i_++)
a[i].Set(i_,a[i_][i]);
for(i_=i+1;i_<=n-1;i_++)
inva[i].Set(i_,inva[i_][i]);
}
}
//--- change value
result=true;
//--- check
if(info<=0)
result=false;
else
{
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*inva[i_][j];
//--- check
if(i==j)
v=v-1;
result=result && MathAbs(v)<=threshold;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Checks whether inverse is correct |
//| Returns True on success. |
//+------------------------------------------------------------------+
static bool CTestMatInvUnit::HPDMatrixCheckInverse(CMatrixComplex &ca,
CMatrixComplex &cinva,
const bool isupper,
const int n,
const double threshold,
const int info,
CMatInvReport &rep)
{
//--- create variables
bool result;
int i=0;
int j=0;
complex v=0;
int i_=0;
//--- create matrix
CMatrixComplex a;
CMatrixComplex inva;
//--- copy
a=ca;
inva=cinva;
//--- calculation
for(i=0;i<=n-2;i++)
{
//--- check
if(isupper)
{
//--- change values
for(i_=i+1;i_<=n-1;i_++)
a[i_].Set(i,CMath::Conj(a[i][i_]));
for(i_=i+1;i_<=n-1;i_++)
inva[i_].Set(i,CMath::Conj(inva[i][i_]));
}
else
{
//--- change values
for(i_=i+1;i_<=n-1;i_++)
a[i].Set(i_,CMath::Conj(a[i_][i]));
for(i_=i+1;i_<=n-1;i_++)
inva[i].Set(i_,CMath::Conj(inva[i_][i]));
}
}
//--- change value
result=true;
//--- check
if(info<=0)
result=false;
else
{
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*inva[i_][j];
//--- check
if(i==j)
v=v-1;
result=result && CMath::AbsComplex(v)<=threshold;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Checks whether inversion result indicate singular matrix |
//| Returns True on success. |
//+------------------------------------------------------------------+
static bool CTestMatInvUnit::RMatrixCheckInverseSingular(CMatrixDouble &inva,
const int n,
const double threshold,
const int info,
CMatInvReport &rep)
{
//--- create variables
bool result;
int i=0;
int j=0;
//--- initialization
result=true;
//--- check
if(info!=-3 && info!=1)
result=false;
else
{
result=result && !(rep.m_r1<0.0 || rep.m_r1>1000*CMath::m_machineepsilon);
result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon);
//--- check
if(info==-3)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
result=result && inva[i][j]==0.0;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Checks whether inverse is correct |
//| Returns True on success. |
//+------------------------------------------------------------------+
static bool CTestMatInvUnit::CMatrixCheckInverse(CMatrixComplex &a,
CMatrixComplex &inva,
const int n,
const double threshold,
const int info,
CMatInvReport &rep)
{
//--- create variables
bool result;
int i=0;
int j=0;
complex v=0;
int i_=0;
//--- initialization
result=true;
//--- check
if(info<=0)
result=false;
else
{
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*inva[i_][j];
//--- check
if(i==j)
v=v-1;
result=result && CMath::AbsComplex(v)<=threshold;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Checks whether inversion result indicate singular matrix |
//| Returns True on success. |
//+------------------------------------------------------------------+
static bool CTestMatInvUnit::CMatrixCheckInverseSingular(CMatrixComplex &inva,
const int n,
const double threshold,
const int info,
CMatInvReport &rep)
{
//--- create variables
bool result;
int i=0;
int j=0;
//--- initialization
result=true;
//--- check
if(info!=-3 && info!=1)
result=false;
else
{
result=result && !(rep.m_r1<0.0 || rep.m_r1>1000*CMath::m_machineepsilon);
result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon);
//--- check
if(info==-3)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
result=result && inva[i][j]==0;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Drops upper or lower half of the matrix - fills it by special |
//| pattern which may be used later to ensure that this part wasn't |
//| changed |
//+------------------------------------------------------------------+
static void CTestMatInvUnit::RMatrixDropHalf(CMatrixDouble &a,const int n,
const bool droplower)
{
//--- create variables
int i=0;
int j=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if((droplower && i>j) || (!droplower && i<j))
a[i].Set(j,1+2*i+3*j);
}
}
}
//+------------------------------------------------------------------+
//| Drops upper or lower half of the matrix - fills it by special |
//| pattern which may be used later to ensure that this part wasn't |
//| changed |
//+------------------------------------------------------------------+
static void CTestMatInvUnit::CMatrixDropHalf(CMatrixComplex &a,const int n,
const bool droplower)
{
//--- create variables
int i=0;
int j=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if((droplower && i>j) || (!droplower && i<j))
a[i].Set(j,1+2*i+3*j);
}
}
}
//+------------------------------------------------------------------+
//| Real TR inverse |
//+------------------------------------------------------------------+
static void CTestMatInvUnit::TestRTRInv(const int maxn,const int passcount,
const double threshold,bool &rtrerrors)
{
//--- create variables
int n=0;
int pass=0;
int i=0;
int j=0;
int task=0;
bool isupper;
bool isunit;
double v=0;
bool waserrors;
int info=0;
int i_=0;
//--- create matrix
CMatrixDouble a;
CMatrixDouble b;
//--- object of class
CMatInvReport rep;
//--- initialization
waserrors=false;
//--- Test
for(n=1;n<=maxn;n++)
{
//--- allocation
a.Resize(n,n);
b.Resize(n,n);
//--- calculation
for(task=0;task<=3;task++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- Determine task
isupper=task%2==0;
isunit=task/2%2==0;
//--- Generate matrix
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(i==j)
a[i].Set(i,1+CMath::RandomReal());
else
a[i].Set(j,0.2*CMath::RandomReal()-0.1);
b[i].Set(j,a[i][j]);
}
}
//--- Inverse
CMatInv::RMatrixTrInverse(b,n,isupper,isunit,info,rep);
//--- check
if(info<=0)
{
rtrerrors=true;
return;
}
//--- Structural test
if(isunit)
{
for(i=0;i<=n-1;i++)
rtrerrors=rtrerrors || a[i][i]!=b[i][i];
}
//--- check
if(isupper)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=i-1;j++)
rtrerrors=rtrerrors || a[i][j]!=b[i][j];
}
}
else
{
for(i=0;i<=n-1;i++)
{
for(j=i+1;j<=n-1;j++)
rtrerrors=rtrerrors || a[i][j]!=b[i][j];
}
}
//--- Inverse test
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if((j<i && isupper) || (j>i && !isupper))
{
a[i].Set(j,0);
b[i].Set(j,0);
}
}
}
//--- check
if(isunit)
{
for(i=0;i<=n-1;i++)
{
a[i].Set(i,1);
b[i].Set(i,1);
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*b[i_][j];
//--- check
if(j!=i)
rtrerrors=rtrerrors || MathAbs(v)>threshold;
else
rtrerrors=rtrerrors || MathAbs(v-1)>threshold;
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Complex TR inverse |
//+------------------------------------------------------------------+
static void CTestMatInvUnit::TestCTRInv(const int maxn,const int passcount,
const double threshold,bool &ctrerrors)
{
//--- create variables
int n=0;
int pass=0;
int i=0;
int j=0;
int task=0;
bool isupper;
bool isunit;
complex v=0;
bool waserrors;
int info=0;
int i_=0;
//--- create arrays
CMatrixComplex a;
CMatrixComplex b;
//--- object of class
CMatInvReport rep;
//--- initialization
waserrors=false;
//--- Test
for(n=1;n<=maxn;n++)
{
//--- allocation
a.Resize(n,n);
b.Resize(n,n);
//--- calculation
for(task=0;task<=3;task++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- Determine task
isupper=task%2==0;
isunit=task/2%2==0;
//--- Generate matrix
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(i==j)
{
a[i].SetRe(i,1+CMath::RandomReal());
a[i].SetIm(i,1+CMath::RandomReal());
}
else
{
a[i].SetRe(j,0.2*CMath::RandomReal()-0.1);
a[i].SetIm(j,0.2*CMath::RandomReal()-0.1);
}
b[i].Set(j,a[i][j]);
}
}
//--- Inverse
CMatInv::CMatrixTrInverse(b,n,isupper,isunit,info,rep);
//--- check
if(info<=0)
{
ctrerrors=true;
return;
}
//--- Structural test
if(isunit)
{
for(i=0;i<=n-1;i++)
ctrerrors=ctrerrors || a[i][i]!=b[i][i];
}
//--- check
if(isupper)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=i-1;j++)
ctrerrors=ctrerrors || a[i][j]!=b[i][j];
}
}
else
{
for(i=0;i<=n-1;i++)
{
for(j=i+1;j<=n-1;j++)
ctrerrors=ctrerrors || a[i][j]!=b[i][j];
}
}
//--- Inverse test
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if((j<i && isupper) || (j>i && !isupper))
{
a[i].Set(j,0);
b[i].Set(j,0);
}
}
}
//--- check
if(isunit)
{
for(i=0;i<=n-1;i++)
{
a[i].Set(i,1);
b[i].Set(i,1);
}
}
//--- search errors
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*b[i_][j];
//--- check
if(j!=i)
ctrerrors=ctrerrors || CMath::AbsComplex(v)>threshold;
else
ctrerrors=ctrerrors || CMath::AbsComplex(v-1)>threshold;
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Real test |
//+------------------------------------------------------------------+
static void CTestMatInvUnit::TesTrInv(const int maxn,const int passcount,
const double threshold,bool &rerrors)
{
//--- create variables
int i=0;
int j=0;
int k=0;
int n=0;
int pass=0;
int taskkind=0;
int info=0;
int i_=0;
//--- create array
int p[];
//--- create matrix
CMatrixDouble a;
CMatrixDouble lua;
CMatrixDouble inva;
CMatrixDouble invlua;
//--- object of class
CMatInvReport rep;
//--- General square matrices:
//--- * test general solvers
//--- * test least squares solver
for(pass=1;pass<=passcount;pass++)
{
for(n=1;n<=maxn;n++)
{
//--- ********************************************************
//--- WELL CONDITIONED TASKS
//--- ability to find correct solution is tested
//--- ********************************************************
//--- 1. generate random well conditioned matrix A.
//--- 2. generate random solution vector xe
//--- 3. generate right part b=A*xe
//--- 4. test different methods on original A
CMatGen::RMatrixRndCond(n,1000,a);
RMatrixMakeACopy(a,n,n,lua);
CTrFac::RMatrixLU(lua,n,n,p);
RMatrixMakeACopy(a,n,n,inva);
RMatrixMakeACopy(lua,n,n,invlua);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
CMatInv::RMatrixInverse(inva,n,info,rep);
//--- search errors
rerrors=rerrors || !RMatrixCheckInverse(a,inva,n,threshold,info,rep);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
CMatInv::RMatrixLUInverse(invlua,p,n,info,rep);
//--- search errors
rerrors=rerrors || !RMatrixCheckInverse(a,invlua,n,threshold,info,rep);
//--- ********************************************************
//--- EXACTLY SINGULAR MATRICES
//--- ability to detect singularity is tested
//--- ********************************************************
//--- 1. generate different types of singular matrices:
//--- * zero
//--- * with zero columns
//--- * with zero rows
//--- * with equal rows/columns
//--- 2. test different methods
for(taskkind=0;taskkind<=4;taskkind++)
{
Unset2D(a);
//--- check
if(taskkind==0)
{
//--- all zeros
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0);
}
}
//--- check
if(taskkind==1)
{
//--- there is zero column
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(k,0*a[i_][k]);
}
//--- check
if(taskkind==2)
{
//--- there is zero row
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0;i_<=n-1;i_++)
a[k].Set(i_,0*a[k][i_]);
}
//--- check
if(taskkind==3)
{
//--- equal columns
if(n<2)
continue;
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(0,a[i_][k]);
}
//--- check
if(taskkind==4)
{
//--- equal rows
if(n<2)
continue;
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0;i_<=n-1;i_++)
a[0].Set(i_,a[k][i_]);
}
//--- function calls
RMatrixMakeACopy(a,n,n,lua);
CTrFac::RMatrixLU(lua,n,n,p);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
CMatInv::RMatrixInverse(a,n,info,rep);
//--- search errors
rerrors=rerrors || !RMatrixCheckInverseSingular(a,n,threshold,info,rep);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
CMatInv::RMatrixLUInverse(lua,p,n,info,rep);
//--- search errors
rerrors=rerrors || !RMatrixCheckInverseSingular(lua,n,threshold,info,rep);
}
}
}
}
//+------------------------------------------------------------------+
//| Complex test |
//+------------------------------------------------------------------+
static void CTestMatInvUnit::TestCInv(const int maxn,const int passcount,
const double threshold,bool &cerrors)
{
//--- create variables
int i=0;
int j=0;
int k=0;
int n=0;
int pass=0;
int taskkind=0;
int info=0;
int i_=0;
//--- create array
int p[];
//--- create matrix
CMatrixComplex a;
CMatrixComplex lua;
CMatrixComplex inva;
CMatrixComplex invlua;
//--- object of class
CMatInvReport rep;
//--- General square matrices:
//--- * test general solvers
//--- * test least squares solver
for(pass=1;pass<=passcount;pass++)
{
for(n=1;n<=maxn;n++)
{
//--- ********************************************************
//--- WELL CONDITIONED TASKS
//--- ability to find correct solution is tested
//--- ********************************************************
//--- 1. generate random well conditioned matrix A.
//--- 2. generate random solution vector xe
//--- 3. generate right part b=A*xe
//--- 4. test different methods on original A
CMatGen::CMatrixRndCond(n,1000,a);
CMatrixMakeACopy(a,n,n,lua);
CTrFac::CMatrixLU(lua,n,n,p);
CMatrixMakeACopy(a,n,n,inva);
CMatrixMakeACopy(lua,n,n,invlua);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
CMatInv::CMatrixInverse(inva,n,info,rep);
//--- search errors
cerrors=cerrors || !CMatrixCheckInverse(a,inva,n,threshold,info,rep);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
CMatInv::CMatrixLUInverse(invlua,p,n,info,rep);
//--- search errors
cerrors=cerrors || !CMatrixCheckInverse(a,invlua,n,threshold,info,rep);
//--- ********************************************************
//--- EXACTLY SINGULAR MATRICES
//--- ability to detect singularity is tested
//--- ********************************************************
//--- 1. generate different types of singular matrices:
//--- * zero
//--- * with zero columns
//--- * with zero rows
//--- * with equal rows/columns
//--- 2. test different methods
for(taskkind=0;taskkind<=4;taskkind++)
{
CUnset2D(a);
//--- check
if(taskkind==0)
{
//--- all zeros
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0);
}
}
//--- check
if(taskkind==1)
{
//--- there is zero column
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(k,a[i_][k]*0);
}
//--- check
if(taskkind==2)
{
//--- there is zero row
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0;i_<=n-1;i_++)
a[k].Set(i_,a[k][i_]*0);
}
//--- check
if(taskkind==3)
{
//--- equal columns
if(n<2)
continue;
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(0,a[i_][k]);
}
//--- check
if(taskkind==4)
{
//--- equal rows
if(n<2)
continue;
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- change value
k=1+CMath::RandomInteger(n-1);
for(i_=0;i_<=n-1;i_++)
a[0].Set(i_,a[k][i_]);
}
//--- function calls
CMatrixMakeACopy(a,n,n,lua);
CTrFac::CMatrixLU(lua,n,n,p);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
CMatInv::CMatrixInverse(a,n,info,rep);
//--- search errors
cerrors=cerrors || !CMatrixCheckInverseSingular(a,n,threshold,info,rep);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
CMatInv::CMatrixLUInverse(lua,p,n,info,rep);
//--- search errors
cerrors=cerrors || !CMatrixCheckInverseSingular(lua,n,threshold,info,rep);
}
}
}
}
//+------------------------------------------------------------------+
//| SPD test |
//+------------------------------------------------------------------+
static void CTestMatInvUnit::TestSPDInv(const int maxn,const int passcount,
const double threshold,bool &spderrors)
{
//--- create variables
bool isupper;
int i=0;
int j=0;
int k=0;
int n=0;
int pass=0;
int taskkind=0;
int info=0;
int i_=0;
//--- create matrix
CMatrixDouble a;
CMatrixDouble cha;
CMatrixDouble inva;
CMatrixDouble invcha;
//--- object of class
CMatInvReport rep;
//--- General square matrices:
//--- * test general solvers
//--- * test least squares solver
for(pass=1;pass<=passcount;pass++)
{
for(n=1;n<=maxn;n++)
{
isupper=CMath::RandomReal()>0.5;
//--- ********************************************************
//--- WELL CONDITIONED TASKS
//--- ability to find correct solution is tested
//--- ********************************************************
//--- 1. generate random well conditioned matrix A.
//--- 2. generate random solution vector xe
//--- 3. generate right part b=A*xe
//--- 4. test different methods on original A
CMatGen::SPDMatrixRndCond(n,1000,a);
RMatrixDropHalf(a,n,isupper);
RMatrixMakeACopy(a,n,n,cha);
//--- check
if(!CTrFac::SPDMatrixCholesky(cha,n,isupper))
continue;
//--- function calls
RMatrixMakeACopy(a,n,n,inva);
RMatrixMakeACopy(cha,n,n,invcha);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
CMatInv::SPDMatrixInverse(inva,n,isupper,info,rep);
//--- search errors
spderrors=spderrors || !SPDMatrixCheckInverse(a,inva,isupper,n,threshold,info,rep);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
CMatInv::SPDMatrixCholeskyInverse(invcha,n,isupper,info,rep);
//--- search errors
spderrors=spderrors || !SPDMatrixCheckInverse(a,invcha,isupper,n,threshold,info,rep);
//--- ********************************************************
//--- EXACTLY SINGULAR MATRICES
//--- ability to detect singularity is tested
//--- ********************************************************
//--- 1. generate different types of singular matrices:
//--- * zero
//--- * with zero columns
//--- * with zero rows
//--- 2. test different methods
for(taskkind=0;taskkind<=2;taskkind++)
{
Unset2D(a);
//--- check
if(taskkind==0)
{
//--- all zeros
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0);
}
}
//--- check
if(taskkind==1)
{
//--- there is zero column
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(k,0*a[i_][k]);
}
//--- check
if(taskkind==2)
{
//--- there is zero row
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0;i_<=n-1;i_++)
a[k].Set(i_,0*a[k][i_]);
}
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
CMatInv::SPDMatrixCholeskyInverse(a,n,isupper,info,rep);
//--- check
if(info!=-3 && info!=1)
spderrors=true;
else
{
spderrors=(spderrors || rep.m_r1<0.0) || rep.m_r1>1000*CMath::m_machineepsilon;
spderrors=(spderrors || rep.m_rinf<0.0) || rep.m_rinf>1000*CMath::m_machineepsilon;
}
}
}
}
}
//+------------------------------------------------------------------+
//| HPD test |
//+------------------------------------------------------------------+
static void CTestMatInvUnit::TestHPDInv(const int maxn,const int passcount,
const double threshold,bool &hpderrors)
{
//--- create variables
bool isupper;
int i=0;
int j=0;
int k=0;
int n=0;
int pass=0;
int taskkind=0;
int info=0;
int i_=0;
//--- create matrix
CMatrixComplex a;
CMatrixComplex cha;
CMatrixComplex inva;
CMatrixComplex invcha;
//--- object of class
CMatInvReport rep;
//--- General square matrices:
//--- * test general solvers
//--- * test least squares solver
for(pass=1;pass<=passcount;pass++)
{
for(n=1;n<=maxn;n++)
{
isupper=CMath::RandomReal()>0.5;
//--- ********************************************************
//--- WELL CONDITIONED TASKS
//--- ability to find correct solution is tested
//--- ********************************************************
//--- 1. generate random well conditioned matrix A.
//--- 2. generate random solution vector xe
//--- 3. generate right part b=A*xe
//--- 4. test different methods on original A
CMatGen::HPDMatrixRndCond(n,1000,a);
CMatrixDropHalf(a,n,isupper);
CMatrixMakeACopy(a,n,n,cha);
//--- check
if(!CTrFac::HPDMatrixCholesky(cha,n,isupper))
continue;
//--- function calls
CMatrixMakeACopy(a,n,n,inva);
CMatrixMakeACopy(cha,n,n,invcha);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
CMatInv::HPDMatrixInverse(inva,n,isupper,info,rep);
//--- search errors
hpderrors=hpderrors || !HPDMatrixCheckInverse(a,inva,isupper,n,threshold,info,rep);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
CMatInv::HPDMatrixCholeskyInverse(invcha,n,isupper,info,rep);
//--- search errors
hpderrors=hpderrors || !HPDMatrixCheckInverse(a,invcha,isupper,n,threshold,info,rep);
//--- ********************************************************
//--- EXACTLY SINGULAR MATRICES
//--- ability to detect singularity is tested
//--- ********************************************************
//--- 1. generate different types of singular matrices:
//--- * zero
//--- * with zero columns
//--- * with zero rows
//--- 2. test different methods
for(taskkind=0;taskkind<=2;taskkind++)
{
CUnset2D(a);
//--- check
if(taskkind==0)
{
//--- all zeros
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0);
}
}
//--- check
if(taskkind==1)
{
//--- there is zero column
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(k,a[i_][k]*0);
for(i_=0;i_<=n-1;i_++)
a[k].Set(i_,a[k][i_]*0);
}
//--- check
if(taskkind==2)
{
//--- there is zero row
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0;i_<=n-1;i_++)
a[k].Set(i_,a[k][i_]*0);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(k,a[i_][k]*0);
}
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
CMatInv::HPDMatrixCholeskyInverse(a,n,isupper,info,rep);
//--- check
if(info!=-3 && info!=1)
hpderrors=true;
else
{
hpderrors=(hpderrors || rep.m_r1<0.0) || rep.m_r1>1000*CMath::m_machineepsilon;
hpderrors=(hpderrors || rep.m_rinf<0.0) || rep.m_rinf>1000*CMath::m_machineepsilon;
}
}
}
}
}
//+------------------------------------------------------------------+
//| Unsets real matrix |
//+------------------------------------------------------------------+
static void CTestMatInvUnit::Unset2D(CMatrixDouble &x)
{
//--- allocation
x.Resize(1,1);
//--- change value
x[0].Set(0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets real matrix |
//+------------------------------------------------------------------+
static void CTestMatInvUnit::Unset1D(double &x[])
{
//--- allocation
ArrayResize(x,1);
//--- change value
x[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Unsets real matrix |
//+------------------------------------------------------------------+
static void CTestMatInvUnit::CUnset2D(CMatrixComplex &x)
{
//--- allocation
x.Resize(1,1);
//--- change value
x[0].Set(0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets real vector |
//+------------------------------------------------------------------+
static void CTestMatInvUnit::CUnset1D(double &x[])
{
//--- allocation
ArrayResize(x,1);
//--- change value
x[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Unsets report |
//+------------------------------------------------------------------+
static void CTestMatInvUnit::UnsetRep(CMatInvReport &r)
{
//--- change values
r.m_r1=-1;
r.m_rinf=-1;
}
//+------------------------------------------------------------------+
//| Testing class CLDA |
//+------------------------------------------------------------------+
class CTestLDAUnit
{
private:
//--- private methods
static void GenSimpleSet(const int nfeatures,const int nclasses,const int nsamples,const int axis,CMatrixDouble &xy);
static void GenDeg1Set(const int nfeatures,const int nclasses,const int nsamples,int axis,CMatrixDouble &xy);
static double GenerateNormal(const double mean,const double sigma);
static bool TestWN(CMatrixDouble &xy,CMatrixDouble &wn,const int ns,const int nf,const int nc,const int ndeg);
static double CalcJ(const int nf,CMatrixDouble &st,CMatrixDouble &sw,double &w[],double &p,double &q);
static void Fishers(CMatrixDouble &xy,int npoints,const int nfeatures,const int nclasses,CMatrixDouble &st,CMatrixDouble &sw);
public:
//--- constructor, destructor
CTestLDAUnit(void);
~CTestLDAUnit(void);
//--- public method
static bool TestLDA(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestLDAUnit::CTestLDAUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestLDAUnit::~CTestLDAUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CLDA |
//+------------------------------------------------------------------+
static bool CTestLDAUnit::TestLDA(const bool silent)
{
//--- create variables
int maxnf=0;
int maxns=0;
int maxnc=0;
int passcount=0;
bool ldanerrors;
bool lda1errors;
bool waserrors;
int nf=0;
int nc=0;
int ns=0;
int i=0;
int info=0;
int pass=0;
int axis=0;
//--- create array
double w1[];
//--- create matrix
CMatrixDouble xy;
CMatrixDouble wn;
//--- Primary settings
maxnf=10;
maxns=1000;
maxnc=5;
passcount=1;
waserrors=false;
ldanerrors=false;
lda1errors=false;
//--- General tests
for(nf=1;nf<=maxnf;nf++)
{
for(nc=2;nc<=maxnc;nc++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- Simple test for LDA-N/LDA-1
axis=CMath::RandomInteger(nf);
ns=maxns/2+CMath::RandomInteger(maxns/2);
//--- function calls
GenSimpleSet(nf,nc,ns,axis,xy);
CLDA::FisherLDAN(xy,ns,nf,nc,info,wn);
//--- check
if(info!=1)
{
ldanerrors=true;
continue;
}
//--- search errors
ldanerrors=ldanerrors || !TestWN(xy,wn,ns,nf,nc,0);
ldanerrors=ldanerrors || MathAbs(wn[axis][0])<=0.75;
//--- function call
CLDA::FisherLDA(xy,ns,nf,nc,info,w1);
//--- search errors
for(i=0;i<=nf-1;i++)
lda1errors=lda1errors || w1[i]!=wn[i][0];
//--- Degenerate test for LDA-N
if(nf>=3)
{
ns=maxns/2+CMath::RandomInteger(maxns/2);
//--- there are two duplicate features,
//--- axis is oriented along non-duplicate feature
axis=CMath::RandomInteger(nf-2);
GenDeg1Set(nf,nc,ns,axis,xy);
CLDA::FisherLDAN(xy,ns,nf,nc,info,wn);
//--- check
if(info!=2)
{
ldanerrors=true;
continue;
}
//--- function calls
ldanerrors=ldanerrors || wn[axis][0]<=0.75;
CLDA::FisherLDA(xy,ns,nf,nc,info,w1);
//--- search errors
for(i=0;i<=nf-1;i++)
lda1errors=lda1errors || w1[i]!=wn[i][0];
}
}
}
}
//--- Final report
waserrors=ldanerrors || lda1errors;
//--- check
if(!silent)
{
Print("LDA TEST");
Print("FISHER LDA-N: ");
//--- check
if(!ldanerrors)
Print("OK");
else
Print("FAILED");
Print("FISHER LDA-1: ");
//--- check
if(!lda1errors)
Print("OK");
else
Print("FAILED");
//--- check
if(waserrors)
Print("TEST SUMMARY: FAILED");
else
Print("TEST SUMMARY: PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Generates 'simple' set - a sequence of unit 'balls' at |
//| (0,0),(1,0),(2,0) and so on. |
//+------------------------------------------------------------------+
static void CTestLDAUnit::GenSimpleSet(const int nfeatures,const int nclasses,
const int nsamples,const int axis,
CMatrixDouble &xy)
{
//--- create variables
int i=0;
int j=0;
int c=0;
//--- check
if(!CAp::Assert(axis>=0 && axis<nfeatures,"GenSimpleSet: wrong Axis!"))
return;
//--- allocation
xy.Resize(nsamples,nfeatures+1);
//--- calculation
for(i=0;i<=nsamples-1;i++)
{
for(j=0;j<=nfeatures-1;j++)
xy[i].Set(j,GenerateNormal(0.0,1.0));
//--- change values
c=i%nclasses;
xy[i].Set(axis,xy[i][axis]+c);
xy[i].Set(nfeatures,c);
}
}
//+------------------------------------------------------------------+
//| Generates 'degenerate' set #1. |
//| NFeatures>=3. |
//+------------------------------------------------------------------+
static void CTestLDAUnit::GenDeg1Set(const int nfeatures,const int nclasses,
const int nsamples,int axis,
CMatrixDouble &xy)
{
//--- create variables
int i=0;
int j=0;
int c=0;
//--- check
if(!CAp::Assert(axis>=0 && axis<nfeatures,"GenDeg1Set: wrong Axis!"))
return;
//--- check
if(!CAp::Assert(nfeatures>=3,"GenDeg1Set: wrong NFeatures!"))
return;
//--- allocation
xy.Resize(nsamples,nfeatures+1);
//--- check
if(axis>=nfeatures-2)
axis=nfeatures-3;
//--- calculation
for(i=0;i<=nsamples-1;i++)
{
for(j=0;j<=nfeatures-2;j++)
xy[i].Set(j,GenerateNormal(0.0,1.0));
//--- change values
xy[i].Set(nfeatures-1,xy[i][nfeatures-2]);
c=i%nclasses;
xy[i].Set(axis,xy[i][axis]+c);
xy[i].Set(nfeatures,c);
}
}
//+------------------------------------------------------------------+
//| Normal random number |
//+------------------------------------------------------------------+
static double CTestLDAUnit::GenerateNormal(const double mean,const double sigma)
{
//--- create variables
double result=0;
double u=0;
double v=0;
double sum=0;
//--- initialization
result=mean;
//--- calculation
while(true)
{
//--- change values
u=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal();
v=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal();
sum=u*u+v*v;
//--- check
if(sum<1.0 && sum>0.0)
{
sum=MathSqrt(-(2*MathLog(sum)/sum));
result=sigma*u*sum+mean;
//--- return result
return(result);
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Tests WN for correctness |
//+------------------------------------------------------------------+
static bool CTestLDAUnit::TestWN(CMatrixDouble &xy,CMatrixDouble &wn,
const int ns,const int nf,
const int nc,const int ndeg)
{
//--- create variables
bool result;
int i=0;
int j=0;
double v=0;
double wprev=0;
double tol=0;
double p=0;
double q=0;
int i_=0;
//--- create arrays
double tx[];
double jp[];
double jq[];
double work[];
//--- create matrix
CMatrixDouble st;
CMatrixDouble sw;
CMatrixDouble a;
CMatrixDouble z;
//--- initialization
tol=10000;
result=true;
Fishers(xy,ns,nf,nc,st,sw);
//--- Test for decreasing of J
ArrayResize(tx,nf);
ArrayResize(jp,nf);
ArrayResize(jq,nf);
//--- calculation
for(j=0;j<=nf-1;j++)
{
for(i_=0;i_<=nf-1;i_++)
tx[i_]=wn[i_][j];
v=CalcJ(nf,st,sw,tx,p,q);
jp[j]=p;
jq[j]=q;
}
//--- calculation
for(i=1;i<=nf-1-ndeg;i++)
result=result && jp[i-1]/jq[i-1]>=(1-tol*CMath::m_machineepsilon)*jp[i]/jq[i];
for(i=nf-1-ndeg+1;i<=nf-1;i++)
result=result && jp[i]<=tol*CMath::m_machineepsilon*jp[0];
//--- Test for J optimality
for(i_=0;i_<=nf-1;i_++)
tx[i_]=wn[i_][0];
v=CalcJ(nf,st,sw,tx,p,q);
//--- calculation
for(i=0;i<=nf-1;i++)
{
wprev=tx[i];
tx[i]=wprev+0.01;
result=result && v>=(1-tol*CMath::m_machineepsilon)*CalcJ(nf,st,sw,tx,p,q);
tx[i]=wprev-0.01;
result=result && v>=(1-tol*CMath::m_machineepsilon)*CalcJ(nf,st,sw,tx,p,q);
tx[i]=wprev;
}
//--- Test for linear independence of W
ArrayResize(work,nf+1);
a.Resize(nf,nf);
//--- function call
CBlas::MatrixMatrixMultiply(wn,0,nf-1,0,nf-1,false,wn,0,nf-1,0,nf-1,true,1.0,a,0,nf-1,0,nf-1,0.0,work);
//--- check
if(CEigenVDetect::SMatrixEVD(a,nf,1,true,tx,z))
result=result && tx[0]>tx[nf-1]*1000*CMath::m_machineepsilon;
//--- Test for other properties
for(j=0;j<=nf-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=nf-1;i_++)
v+=wn[i_][j]*wn[i_][j];
//--- change values
v=MathSqrt(v);
result=result && MathAbs(v-1)<=1000*CMath::m_machineepsilon;
v=0;
for(i=0;i<=nf-1;i++)
v=v+wn[i][j];
result=result && v>=0.0;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Calculates J |
//+------------------------------------------------------------------+
static double CTestLDAUnit::CalcJ(const int nf,CMatrixDouble &st,
CMatrixDouble &sw,double &w[],
double &p,double &q)
{
//--- create variables
double result=0;
int i=0;
double v=0;
int i_=0;
//--- create array
double tx[];
//--- initialization
p=0;
q=0;
//--- allocation
ArrayResize(tx,nf);
//--- calculation
for(i=0;i<=nf-1;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=nf-1;i_++)
v+=st[i][i_]*w[i_];
tx[i]=v;
}
//--- change value
v=0.0;
for(i_=0;i_<=nf-1;i_++)
v+=w[i_]*tx[i_];
p=v;
for(i=0;i<=nf-1;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=nf-1;i_++)
v+=sw[i][i_]*w[i_];
tx[i]=v;
}
//--- change value
v=0.0;
for(i_=0;i_<=nf-1;i_++)
v+=w[i_]*tx[i_];
q=v;
result=p/q;
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Calculates ST/SW |
//+------------------------------------------------------------------+
static void CTestLDAUnit::Fishers(CMatrixDouble &xy,int npoints,
const int nfeatures,const int nclasses,
CMatrixDouble &st,CMatrixDouble &sw)
{
//--- create variables
int i=0;
int j=0;
int k=0;
double v=0;
int i_=0;
//--- create arrays
int c[];
double mu[];
int nc[];
double tf[];
double work[];
//--- create matrix
CMatrixDouble muc;
//--- Prepare temporaries
ArrayResize(tf,nfeatures);
ArrayResize(work,nfeatures+1);
//--- Convert class labels from reals to integers (just for convenience)
ArrayResize(c,npoints);
for(i=0;i<=npoints-1;i++)
c[i]=(int)MathRound(xy[i][nfeatures]);
//--- Calculate class sizes and means
ArrayResize(mu,nfeatures);
muc.Resize(nclasses,nfeatures);
ArrayResize(nc,nclasses);
//--- change values
for(j=0;j<=nfeatures-1;j++)
mu[j]=0;
for(i=0;i<=nclasses-1;i++)
{
nc[i]=0;
for(j=0;j<=nfeatures-1;j++)
muc[i].Set(j,0);
}
//--- calculation
for(i=0;i<=npoints-1;i++)
{
for(i_=0;i_<=nfeatures-1;i_++)
mu[i_]=mu[i_]+xy[i][i_];
for(i_=0;i_<=nfeatures-1;i_++)
muc[c[i]].Set(i_,muc[c[i]][i_]+xy[i][i_]);
nc[c[i]]=nc[c[i]]+1;
}
//--- calculation
for(i=0;i<=nclasses-1;i++)
{
v=1.0/(double)nc[i];
for(i_=0;i_<=nfeatures-1;i_++)
muc[i].Set(i_,v*muc[i][i_]);
}
//--- change values
v=1.0/(double)npoints;
for(i_=0;i_<=nfeatures-1;i_++)
mu[i_]=v*mu[i_];
//--- Create ST matrix
st.Resize(nfeatures,nfeatures);
//--- change values
for(i=0;i<=nfeatures-1;i++)
{
for(j=0;j<=nfeatures-1;j++)
st[i].Set(j,0);
}
//--- calculation
for(k=0;k<=npoints-1;k++)
{
for(i_=0;i_<=nfeatures-1;i_++)
tf[i_]=xy[k][i_];
for(i_=0;i_<=nfeatures-1;i_++)
tf[i_]=tf[i_]-mu[i_];
//--- calculation
for(i=0;i<=nfeatures-1;i++)
{
v=tf[i];
for(i_=0;i_<=nfeatures-1;i_++)
st[i].Set(i_,st[i][i_]+v*tf[i_]);
}
}
//--- Create SW matrix
sw.Resize(nfeatures,nfeatures);
for(i=0;i<=nfeatures-1;i++)
{
for(j=0;j<=nfeatures-1;j++)
sw[i].Set(j,0);
}
//--- calculation
for(k=0;k<=npoints-1;k++)
{
for(i_=0;i_<=nfeatures-1;i_++)
tf[i_]=xy[k][i_];
for(i_=0;i_<=nfeatures-1;i_++)
tf[i_]=tf[i_]-muc[c[k]][i_];
//--- calculation
for(i=0;i<=nfeatures-1;i++)
{
v=tf[i];
for(i_=0;i_<=nfeatures-1;i_++)
sw[i].Set(i_,sw[i][i_]+v*tf[i_]);
}
}
}
//+------------------------------------------------------------------+
//| Testing class CGammaFunc |
//+------------------------------------------------------------------+
class CTestGammaFuncUnit
{
public:
//--- constructor, destructor
CTestGammaFuncUnit(void);
~CTestGammaFuncUnit(void);
//--- public method
static bool TestGammaFunc(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestGammaFuncUnit::CTestGammaFuncUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestGammaFuncUnit::~CTestGammaFuncUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CGammaFunc |
//+------------------------------------------------------------------+
static bool CTestGammaFuncUnit::TestGammaFunc(const bool silent)
{
//--- create variables
double threshold=0;
double v=0;
double s=0;
bool waserrors;
bool gammaerrors;
bool lngammaerrors;
//--- initialization
gammaerrors=false;
lngammaerrors=false;
waserrors=false;
threshold=100*CMath::m_machineepsilon;
//--- search errors
gammaerrors=gammaerrors || MathAbs(CGammaFunc::GammaFunc(0.5)-MathSqrt(M_PI))>threshold;
gammaerrors=gammaerrors || MathAbs(CGammaFunc::GammaFunc(1.5)-0.5*MathSqrt(M_PI))>threshold;
//--- function call
v=CGammaFunc::LnGamma(0.5,s);
//--- search errors
lngammaerrors=(lngammaerrors || MathAbs(v-MathLog(MathSqrt(M_PI)))>threshold) || s!=1.0;
//--- function call
v=CGammaFunc::LnGamma(1.5,s);
//--- search errors
lngammaerrors=(lngammaerrors || MathAbs(v-MathLog(0.5*MathSqrt(M_PI)))>threshold) || s!=1.0;
//--- report
waserrors=gammaerrors || lngammaerrors;
//--- check
if(!silent)
{
Print("TESTING GAMMA FUNCTION");
Print("GAMMA: ");
//--- check
if(gammaerrors)
Print("FAILED");
else
Print("OK");
Print("LN GAMMA: ");
//--- check
if(lngammaerrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- end
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CBdSingValueDecompose |
//+------------------------------------------------------------------+
class CTestBdSVDUnit
{
private:
//--- private methods
static void FillIdentity(CMatrixDouble &a,const int n);
static void FillSparseDE(double &d[],double &e[],const int n,const double sparcity);
static void GetBdSVDError(double &d[],double &e[],const int n,const bool isupper,CMatrixDouble &u,CMatrixDouble &c,double &w[],CMatrixDouble &vt,double &materr,double &orterr,bool &wsorted);
static void TestBdSVDProblem(double &d[],double &e[],const int n,double &materr,double &orterr,bool &wsorted,bool &wfailed,int &failcount,int &succcount);
public:
//--- constructor, destructor
CTestBdSVDUnit(void);
~CTestBdSVDUnit(void);
//--- public method
static bool TestBdSVD(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestBdSVDUnit::CTestBdSVDUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestBdSVDUnit::~CTestBdSVDUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing bidiagonal SVD decomposition subroutine |
//+------------------------------------------------------------------+
static bool CTestBdSVDUnit::TestBdSVD(const bool silent)
{
//--- create variables
int n=0;
int maxn=0;
int i=0;
int pass=0;
bool waserrors;
bool wsorted;
bool wfailed;
bool failcase;
double materr=0;
double orterr=0;
double threshold=0;
double failthreshold=0;
double failr=0;
int failcount=0;
int succcount=0;
//--- create arrays
double d[];
double e[];
//--- create matrix
CMatrixDouble mempty;
//--- initialization
failcount=0;
succcount=0;
materr=0;
orterr=0;
wsorted=true;
wfailed=false;
waserrors=false;
maxn=15;
threshold=5*100*CMath::m_machineepsilon;
failthreshold=1.0E-2;
//--- allocation
ArrayResize(d,maxn);
ArrayResize(e,maxn-1);
//--- special case: fail matrix
n=5;
d[0]=-8.27448347422711894000e-01;
d[1]=-8.16705832087160854600e-01;
d[2]=-2.53974358904729382800e-17;
d[3]=-1.24626684881972815700e+00;
d[4]=-4.64744131545637651000e-01;
e[0]=-3.25785088656270038800e-01;
e[1]=-1.03732413708914436580e-01;
e[2]=-9.57365642262031357700e-02;
e[3]=-2.71564153973817390400e-01;
failcase=CBdSingValueDecompose::RMatrixBdSVD(d,e,n,true,false,mempty,0,mempty,0,mempty,0);
//--- special case: zero divide matrix
//--- unfixed LAPACK routine should fail on this problem
n=7;
d[0]=-6.96462904751731892700e-01;
d[1]=0.00000000000000000000e+00;
d[2]=-5.73827770385971991400e-01;
d[3]=-6.62562624399371191700e-01;
d[4]=5.82737148001782223600e-01;
d[5]=3.84825263580925003300e-01;
d[6]=9.84087420830525472200e-01;
e[0]=-7.30307931760612871800e-02;
e[1]=-2.30079042939542843800e-01;
e[2]=-6.87824621739351216300e-01;
e[3]=-1.77306437707837570600e-02;
e[4]=1.78285126526551632000e-15;
e[5]=-4.89434737751289969400e-02;
CBdSingValueDecompose::RMatrixBdSVD(d,e,n,true,false,mempty,0,mempty,0,mempty,0);
//--- zero matrix,several cases
for(i=0;i<=maxn-1;i++)
d[i]=0;
for(i=0;i<=maxn-2;i++)
e[i]=0;
for(n=1;n<=maxn;n++)
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
//--- Dense matrix
for(n=1;n<=maxn;n++)
{
for(pass=1;pass<=10;pass++)
{
for(i=0;i<=maxn-1;i++)
d[i]=2*CMath::RandomReal()-1;
for(i=0;i<=maxn-2;i++)
e[i]=2*CMath::RandomReal()-1;
//--- function call
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
}
}
//--- Sparse matrices,very sparse matrices,incredible sparse matrices
for(n=1;n<=maxn;n++)
{
for(pass=1;pass<=10;pass++)
{
//--- function calls
FillSparseDE(d,e,n,0.5);
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
FillSparseDE(d,e,n,0.8);
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
FillSparseDE(d,e,n,0.9);
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
FillSparseDE(d,e,n,0.95);
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
}
}
//--- report
failr=(double)failcount/(double)(succcount+failcount);
waserrors=((materr>threshold || orterr>threshold) || !wsorted) || failr>failthreshold;
//--- check
if(!silent)
{
Print("TESTING BIDIAGONAL SVD DECOMPOSITION");
Print("SVD decomposition error: ");
Print("{0,5:E3}",materr);
Print("SVD orthogonality error: ");
Print("{0,5:E3}",orterr);
Print("Singular values order: ");
//--- check
if(wsorted)
Print("OK");
else
Print("FAILED");
Print("Always converged: ");
//--- check
if(!wfailed)
Print("YES");
else
{
Print("NO");
Print("Fail ratio: ");
Print("{0,5:F3}",failr);
}
Print("Fail matrix test: ");
//--- check
if(!failcase)
Print("AS EXPECTED");
else
Print("CONVERGED (UNEXPECTED)");
Print("Threshold: ");
Print("{0,5:E3}",threshold);
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestBdSVDUnit::FillIdentity(CMatrixDouble &a,const int n)
{
//--- create variables
int i=0;
int j=0;
//--- allocation
a.Resize(n,n);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(i==j)
a[i].Set(j,1);
else
a[i].Set(j,0);
}
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestBdSVDUnit::FillSparseDE(double &d[],double &e[],
const int n,const double sparcity)
{
//--- create a variable
int i=0;
//--- allocation
ArrayResize(d,n);
ArrayResize(e,(int)(MathMax(0,n-2))+1);
//--- change values
for(i=0;i<=n-1;i++)
{
//--- check
if(CMath::RandomReal()>=sparcity)
d[i]=2*CMath::RandomReal()-1;
else
d[i]=0;
}
//--- change values
for(i=0;i<=n-2;i++)
{
//--- check
if(CMath::RandomReal()>=sparcity)
e[i]=2*CMath::RandomReal()-1;
else
e[i]=0;
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestBdSVDUnit::GetBdSVDError(double &d[],double &e[],
const int n,const bool isupper,
CMatrixDouble &u,CMatrixDouble &c,
double &w[],CMatrixDouble &vt,
double &materr,double &orterr,
bool &wsorted)
{
//--- create variables
int i=0;
int j=0;
int k=0;
double locerr=0;
double sm=0;
int i_=0;
//--- decomposition error
locerr=0;
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
sm=0;
for(k=0;k<=n-1;k++)
sm=sm+w[k]*u[i][k]*vt[k][j];
//--- check
if(isupper)
{
//--- check
if(i==j)
locerr=MathMax(locerr,MathAbs(d[i]-sm));
else
{
//--- check
if(i==j-1)
locerr=MathMax(locerr,MathAbs(e[i]-sm));
else
locerr=MathMax(locerr,MathAbs(sm));
}
}
else
{
//--- check
if(i==j)
locerr=MathMax(locerr,MathAbs(d[i]-sm));
else
{
//--- check
if(i-1==j)
locerr=MathMax(locerr,MathAbs(e[j]-sm));
else
locerr=MathMax(locerr,MathAbs(sm));
}
}
}
}
//--- change value
materr=MathMax(materr,locerr);
//--- check for C=U'
//--- we consider it as decomposition error
locerr=0;
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
locerr=MathMax(locerr,MathAbs(u[i][j]-c[j][i]));
}
materr=MathMax(materr,locerr);
//--- orthogonality error
locerr=0;
for(i=0;i<=n-1;i++)
{
for(j=i;j<=n-1;j++)
{
//--- change value
sm=0.0;
for(i_=0;i_<=n-1;i_++)
sm+=u[i_][i]*u[i_][j];
//--- check
if(i!=j)
locerr=MathMax(locerr,MathAbs(sm));
else
locerr=MathMax(locerr,MathAbs(sm-1));
//--- change value
sm=0.0;
for(i_=0;i_<=n-1;i_++)
sm+=vt[i][i_]*vt[j][i_];
//--- check
if(i!=j)
locerr=MathMax(locerr,MathAbs(sm));
else
locerr=MathMax(locerr,MathAbs(sm-1));
}
}
orterr=MathMax(orterr,locerr);
//--- values order error
for(i=1;i<=n-1;i++)
{
//--- check
if(w[i]>w[i-1])
wsorted=false;
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestBdSVDUnit::TestBdSVDProblem(double &d[],double &e[],
const int n,double &materr,
double &orterr,bool &wsorted,
bool &wfailed,int &failcount,
int &succcount)
{
//--- create variables
int i=0;
double mx=0;
//--- create array
double w[];
//--- create matrix
CMatrixDouble u;
CMatrixDouble vt;
CMatrixDouble c;
//--- change value
mx=0;
for(i=0;i<=n-1;i++)
{
//--- check
if(MathAbs(d[i])>mx)
mx=MathAbs(d[i]);
}
for(i=0;i<=n-2;i++)
{
//--- check
if(MathAbs(e[i])>mx)
mx=MathAbs(e[i]);
}
//--- check
if(mx==0.0)
mx=1;
//--- Upper BDSVD tests
ArrayResize(w,n);
FillIdentity(u,n);
FillIdentity(vt,n);
FillIdentity(c,n);
for(i=0;i<=n-1;i++)
w[i]=d[i];
//--- check
if(!CBdSingValueDecompose::RMatrixBdSVD(w,e,n,true,false,u,n,c,n,vt,n))
{
failcount=failcount+1;
wfailed=true;
return;
}
//--- function calls
GetBdSVDError(d,e,n,true,u,c,w,vt,materr,orterr,wsorted);
FillIdentity(u,n);
FillIdentity(vt,n);
FillIdentity(c,n);
//--- copy
for(i=0;i<=n-1;i++)
w[i]=d[i];
//--- check
if(!CBdSingValueDecompose::RMatrixBdSVD(w,e,n,true,true,u,n,c,n,vt,n))
{
failcount=failcount+1;
wfailed=true;
return;
}
//--- function call
GetBdSVDError(d,e,n,true,u,c,w,vt,materr,orterr,wsorted);
//--- Lower BDSVD tests
ArrayResize(w,n);
FillIdentity(u,n);
FillIdentity(vt,n);
FillIdentity(c,n);
//--- copy
for(i=0;i<=n-1;i++)
w[i]=d[i];
//--- check
if(!CBdSingValueDecompose::RMatrixBdSVD(w,e,n,false,false,u,n,c,n,vt,n))
{
failcount=failcount+1;
wfailed=true;
return;
}
//--- function calls
GetBdSVDError(d,e,n,false,u,c,w,vt,materr,orterr,wsorted);
FillIdentity(u,n);
FillIdentity(vt,n);
FillIdentity(c,n);
//--- copy
for(i=0;i<=n-1;i++)
w[i]=d[i];
//--- check
if(!CBdSingValueDecompose::RMatrixBdSVD(w,e,n,false,true,u,n,c,n,vt,n))
{
failcount=failcount+1;
wfailed=true;
return;
}
//--- function call
GetBdSVDError(d,e,n,false,u,c,w,vt,materr,orterr,wsorted);
//--- update counter
succcount=succcount+1;
}
//+------------------------------------------------------------------+
//| Testing class CSingValueDecompose |
//+------------------------------------------------------------------+
class CTestSVDUnit
{
private:
//--- private methods
static void FillsParseA(CMatrixDouble &a,const int m,const int n,const double sparcity);
static void GetSVDError(CMatrixDouble &a,const int m,const int n,CMatrixDouble &u,double &w[],CMatrixDouble &vt,double &materr,double &orterr,bool &wsorted);
static void TestSVDProblem(CMatrixDouble &a,const int m,const int n,double &materr,double &orterr,double &othererr,bool &wsorted,bool &wfailed,int &failcount,int &succcount);
public:
//--- constructor, destructor
CTestSVDUnit(void);
~CTestSVDUnit(void);
//--- public method
static bool TestSVD(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestSVDUnit::CTestSVDUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestSVDUnit::~CTestSVDUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing SVD decomposition subroutine |
//+------------------------------------------------------------------+
static bool CTestSVDUnit::TestSVD(const bool silent)
{
//--- create variables
int m=0;
int n=0;
int maxmn=0;
int i=0;
int j=0;
int gpass=0;
int pass=0;
bool waserrors;
bool wsorted;
bool wfailed;
double materr=0;
double orterr=0;
double othererr=0;
double threshold=0;
double failthreshold=0;
double failr=0;
int failcount=0;
int succcount=0;
//--- create matrix
CMatrixDouble a;
//--- initialization
failcount=0;
succcount=0;
materr=0;
orterr=0;
othererr=0;
wsorted=true;
wfailed=false;
waserrors=false;
maxmn=30;
threshold=5*100*CMath::m_machineepsilon;
failthreshold=5.0E-3;
//--- allocation
a.Resize(maxmn,maxmn);
//--- TODO: div by zero fail,convergence fail
for(gpass=1;gpass<=1;gpass++)
{
//--- zero matrix,several cases
for(i=0;i<=maxmn-1;i++)
{
for(j=0;j<=maxmn-1;j++)
a[i].Set(j,0);
}
//--- function calls
for(i=1;i<=MathMin(5,maxmn);i++)
{
for(j=1;j<=MathMin(5,maxmn);j++)
TestSVDProblem(a,i,j,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
}
//--- Long dense matrix
for(i=0;i<=maxmn-1;i++)
{
for(j=0;j<=MathMin(5,maxmn)-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- function calls
for(i=1;i<=maxmn;i++)
{
for(j=1;j<=MathMin(5,maxmn);j++)
TestSVDProblem(a,i,j,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
}
//--- change values
for(i=0;i<=MathMin(5,maxmn)-1;i++)
{
for(j=0;j<=maxmn-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- function calls
for(i=1;i<=MathMin(5,maxmn);i++)
{
for(j=1;j<=maxmn;j++)
TestSVDProblem(a,i,j,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
}
//--- Dense matrices
for(m=1;m<=MathMin(10,maxmn);m++)
{
for(n=1;n<=MathMin(10,maxmn);n++)
{
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- function call
TestSVDProblem(a,m,n,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
}
}
//--- Sparse matrices,very sparse matrices,incredible sparse matrices
for(m=1;m<=10;m++)
{
for(n=1;n<=10;n++)
{
for(pass=1;pass<=2;pass++)
{
//--- function calls
FillsParseA(a,m,n,0.8);
TestSVDProblem(a,m,n,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
FillsParseA(a,m,n,0.9);
TestSVDProblem(a,m,n,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
FillsParseA(a,m,n,0.95);
TestSVDProblem(a,m,n,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
}
}
}
}
//--- report
failr=(double)failcount/(double)(succcount+failcount);
waserrors=(((materr>threshold || orterr>threshold) || othererr>threshold) || !wsorted) || failr>failthreshold;
//--- check
if(!silent)
{
Print("TESTING SVD DECOMPOSITION");
Print("SVD decomposition error: ");
Print("{0,5:E3}",materr);
Print("SVD orthogonality error: ");
Print("{0,5:E3}",orterr);
Print("SVD with different parameters error: ");
Print("{0,5:E3}",othererr);
Print("Singular values order: ");
//--- check
if(wsorted)
Print("OK");
else
Print("FAILED");
Print("Always converged: ");
//--- check
if(!wfailed)
Print("YES");
else
{
Print("NO");
Print("Fail ratio: ");
Print("{0,5:F3}",failr);
}
Print("Threshold: ");
Print("{0,5:E3}",threshold);
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestSVDUnit::FillsParseA(CMatrixDouble &a,const int m,
const int n,const double sparcity)
{
//--- create variables
int i=0;
int j=0;
//--- change values
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(CMath::RandomReal()>=sparcity)
a[i].Set(j,2*CMath::RandomReal()-1);
else
a[i].Set(j,0);
}
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestSVDUnit::GetSVDError(CMatrixDouble &a,const int m,
const int n,CMatrixDouble &u,
double &w[],CMatrixDouble &vt,
double &materr,double &orterr,
bool &wsorted)
{
//--- create variables
int i=0;
int j=0;
int k=0;
int minmn=0;
double locerr=0;
double sm=0;
int i_=0;
//--- initialization
minmn=MathMin(m,n);
//--- decomposition error
locerr=0;
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
sm=0;
for(k=0;k<=minmn-1;k++)
sm=sm+w[k]*u[i][k]*vt[k][j];
locerr=MathMax(locerr,MathAbs(a[i][j]-sm));
}
}
materr=MathMax(materr,locerr);
//--- orthogonality error
locerr=0;
for(i=0;i<=minmn-1;i++)
{
for(j=i;j<=minmn-1;j++)
{
//--- change value
sm=0.0;
for(i_=0;i_<=m-1;i_++)
sm+=u[i_][i]*u[i_][j];
//--- check
if(i!=j)
locerr=MathMax(locerr,MathAbs(sm));
else
locerr=MathMax(locerr,MathAbs(sm-1));
//--- change value
sm=0.0;
for(i_=0;i_<=n-1;i_++)
sm+=vt[i][i_]*vt[j][i_];
//--- check
if(i!=j)
locerr=MathMax(locerr,MathAbs(sm));
else
locerr=MathMax(locerr,MathAbs(sm-1));
}
}
orterr=MathMax(orterr,locerr);
//--- values order error
for(i=1;i<=minmn-1;i++)
{
//--- check
if(w[i]>w[i-1])
wsorted=false;
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestSVDUnit::TestSVDProblem(CMatrixDouble &a,const int m,
const int n,double &materr,
double &orterr,double &othererr,
bool &wsorted,bool &wfailed,
int &failcount,int &succcount)
{
//--- create variables
int i=0;
int j=0;
int ujob=0;
int vtjob=0;
int memjob=0;
int ucheck=0;
int vtcheck=0;
//--- create arrays
double w[];
double w2[];
//--- create matrix
CMatrixDouble u;
CMatrixDouble vt;
CMatrixDouble u2;
CMatrixDouble vt2;
//--- Main SVD test
if(!CSingValueDecompose::RMatrixSVD(a,m,n,2,2,2,w,u,vt))
{
failcount=failcount+1;
wfailed=true;
//--- exit the function
return;
}
//--- function call
GetSVDError(a,m,n,u,w,vt,materr,orterr,wsorted);
//--- Additional SVD tests
for(ujob=0;ujob<=2;ujob++)
{
for(vtjob=0;vtjob<=2;vtjob++)
{
for(memjob=0;memjob<=2;memjob++)
{
//--- check
if(!CSingValueDecompose::RMatrixSVD(a,m,n,ujob,vtjob,memjob,w2,u2,vt2))
{
failcount=failcount+1;
wfailed=true;
//--- exit the function
return;
}
//--- change value
ucheck=0;
//--- check
if(ujob==1)
ucheck=MathMin(m,n);
//--- check
if(ujob==2)
ucheck=m;
//--- change value
vtcheck=0;
//--- check
if(vtjob==1)
vtcheck=MathMin(m,n);
//--- check
if(vtjob==2)
vtcheck=n;
//--- search errors
for(i=0;i<=m-1;i++)
{
for(j=0;j<=ucheck-1;j++)
othererr=MathMax(othererr,MathAbs(u[i][j]-u2[i][j]));
}
//--- search errors
for(i=0;i<=vtcheck-1;i++)
{
for(j=0;j<=n-1;j++)
othererr=MathMax(othererr,MathAbs(vt[i][j]-vt2[i][j]));
}
//--- search errors
for(i=0;i<=MathMin(m,n)-1;i++)
othererr=MathMax(othererr,MathAbs(w[i]-w2[i]));
}
}
}
//--- update counter
succcount=succcount+1;
}
//+------------------------------------------------------------------+
//| Testing class CLinReg |
//+------------------------------------------------------------------+
class CTestLinRegUnit
{
public:
//--- constructor, destructor
CTestLinRegUnit(void);
~CTestLinRegUnit(void);
//--- public methods
static bool TestLinReg(const bool silent);
static void GenerateRandomTask(const double xl,const double xr,const bool randomx,const double ymin,const double ymax,const double smin,const double smax,const int n,CMatrixDouble &xy,double &s[]);
static void GenerateTask(const double a,const double b,const double xl,const double xr,const bool randomx,const double smin,const double smax,const int n,CMatrixDouble &xy,double &s[]);
static void FillTaskWithY(const double a,const double b,const int n,CMatrixDouble &xy,double &s[]);
static double GenerateNormal(const double mean,const double sigma);
static void CalculateMV(double &x[],const int n,double &mean,double &means,double &stddev,double &stddevs);
static void UnsetLR(CLinearModel &lr);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestLinRegUnit::CTestLinRegUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestLinRegUnit::~CTestLinRegUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CLinReg |
//+------------------------------------------------------------------+
static bool CTestLinRegUnit::TestLinReg(const bool silent)
{
//--- create variables
double sigmathreshold=0;
int maxn=0;
int maxm=0;
int passcount=0;
int estpasscount=0;
double threshold=0;
int n=0;
int i=0;
int j=0;
int k=0;
int tmpi=0;
int pass=0;
int epass=0;
int m=0;
int tasktype=0;
int modeltype=0;
int m1=0;
int m2=0;
int n1=0;
int n2=0;
int info=0;
int info2=0;
double y1=0;
double y2=0;
bool allsame;
double ea=0;
double eb=0;
double varatested=0;
double varbtested=0;
double a=0;
double b=0;
double vara=0;
double varb=0;
double a2=0;
double b2=0;
double covab=0;
double corrab=0;
double p=0;
int qcnt=0;
double f=0;
double fp=0;
double fm=0;
double v=0;
double vv=0;
double cvrmserror=0;
double cvavgerror=0;
double cvavgrelerror=0;
double rmserror=0;
double avgerror=0;
double avgrelerror=0;
bool nondefect;
double sinshift=0;
double tasklevel=0;
double noiselevel=0;
double hstep=0;
double sigma=0;
double mean=0;
double means=0;
double stddev=0;
double stddevs=0;
bool slcerrors;
bool slerrors;
bool grcoverrors;
bool gropterrors;
bool gresterrors;
bool grothererrors;
bool grconverrors;
bool waserrors;
int i_=0;
//--- create arrays
double s[];
double s2[];
double w2[];
double x[];
double ta[];
double tb[];
double tc[];
double xy0[];
double tmpweights[];
double x1[];
double x2[];
double qtbl[];
double qvals[];
double qsigma[];
//--- create matrix
CMatrixDouble xy;
CMatrixDouble xy2;
//--- objects of classes
CLinearModel w;
CLinearModel wt;
CLinearModel wt2;
CLRReport ar;
CLRReport ar2;
//--- Primary settings
maxn=40;
maxm=5;
passcount=3;
estpasscount=1000;
sigmathreshold=7;
threshold=1000000*CMath::m_machineepsilon;
slerrors=false;
slcerrors=false;
grcoverrors=false;
gropterrors=false;
gresterrors=false;
grothererrors=false;
grconverrors=false;
waserrors=false;
//--- Quantiles table setup
qcnt=5;
ArrayResize(qtbl,qcnt);
ArrayResize(qvals,qcnt);
ArrayResize(qsigma,qcnt);
qtbl[0]=0.5;
qtbl[1]=0.25;
qtbl[2]=0.10;
qtbl[3]=0.05;
qtbl[4]=0.025;
for(i=0;i<=qcnt-1;i++)
qsigma[i]=MathSqrt(qtbl[i]*(1-qtbl[i])/estpasscount);
//--- Other setup
ArrayResize(ta,estpasscount);
ArrayResize(tb,estpasscount);
//--- Test straight line regression
for(n=2;n<=maxn;n++)
{
//--- Fail/pass test
GenerateRandomTask(-1,1,false,-1,1,1,2,n,xy,s);
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
//--- search errors
slcerrors=slcerrors || info!=1;
//--- function calls
GenerateRandomTask(1,1,false,-1,1,1,2,n,xy,s);
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
//--- search errors
slcerrors=slcerrors || info!=-3;
//--- function calls
GenerateRandomTask(-1,1,false,-1,1,-1,-1,n,xy,s);
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
//--- search errors
slcerrors=slcerrors || info!=-2;
//--- function calls
GenerateRandomTask(-1,1,false,-1,1,2,1,2,xy,s);
CLinReg::LRLines(xy,s,1,info,a,b,vara,varb,covab,corrab,p);
//--- search errors
slcerrors=slcerrors || info!=-1;
//--- Multipass tests
for(pass=1;pass<=passcount;pass++)
{
//--- Test S variant against non-S variant
ea=2*CMath::RandomReal()-1;
eb=2*CMath::RandomReal()-1;
//--- function calls
GenerateTask(ea,eb,-(5*CMath::RandomReal()),5*CMath::RandomReal(),CMath::RandomReal()>0.5,1,1,n,xy,s);
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
CLinReg::LRLine(xy,n,info2,a2,b2);
//--- check
if(info!=1 || info2!=1)
slcerrors=true;
else
slerrors=(slerrors || MathAbs(a-a2)>threshold) || MathAbs(b-b2)>threshold;
//--- Test for A/B
//--- Generate task with exact,non-perturbed y[i],
//--- then make non-zero s[i]
ea=2*CMath::RandomReal()-1;
eb=2*CMath::RandomReal()-1;
GenerateTask(ea,eb,-(5*CMath::RandomReal()),5*CMath::RandomReal(),n>4,0.0,0.0,n,xy,s);
for(i=0;i<=n-1;i++)
s[i]=1+CMath::RandomReal();
//--- function call
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
//--- check
if(info!=1)
slcerrors=true;
else
slerrors=(slerrors || MathAbs(a-ea)>0.001) || MathAbs(b-eb)>0.001;
//--- Test for VarA,VarB,P (P is being tested only for N>2)
for(i=0;i<=qcnt-1;i++)
qvals[i]=0;
ea=2*CMath::RandomReal()-1;
eb=2*CMath::RandomReal()-1;
//--- function calls
GenerateTask(ea,eb,-(5*CMath::RandomReal()),5*CMath::RandomReal(),n>4,1.0,2.0,n,xy,s);
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
//--- check
if(info!=1)
{
slcerrors=true;
continue;
}
//--- change values
varatested=vara;
varbtested=varb;
//--- calculation
for(epass=0;epass<=estpasscount-1;epass++)
{
//--- Generate
FillTaskWithY(ea,eb,n,xy,s);
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
//--- check
if(info!=1)
{
slcerrors=true;
continue;
}
//--- A,B,P
//--- (P is being tested for uniformity,additional p-tests are below)
ta[epass]=a;
tb[epass]=b;
for(i=0;i<=qcnt-1;i++)
{
//--- check
if(p<=qtbl[i])
qvals[i]=qvals[i]+1.0/(double)estpasscount;
}
}
//--- function call
CalculateMV(ta,estpasscount,mean,means,stddev,stddevs);
//--- search errors
slerrors=slerrors || MathAbs(mean-ea)/means>=sigmathreshold;
slerrors=slerrors || MathAbs(stddev-MathSqrt(varatested))/stddevs>=sigmathreshold;
//--- function call
CalculateMV(tb,estpasscount,mean,means,stddev,stddevs);
//--- search errors
slerrors=slerrors || MathAbs(mean-eb)/means>=sigmathreshold;
slerrors=slerrors || MathAbs(stddev-MathSqrt(varbtested))/stddevs>=sigmathreshold;
//--- check
if(n>2)
{
for(i=0;i<=qcnt-1;i++)
{
//--- check
if(MathAbs(qtbl[i]-qvals[i])/qsigma[i]>sigmathreshold)
slerrors=true;
}
}
//--- Additional tests for P: correlation with fit quality
if(n>2)
{
GenerateTask(ea,eb,-(5*CMath::RandomReal()),5*CMath::RandomReal(),false,0.0,0.0,n,xy,s);
for(i=0;i<=n-1;i++)
s[i]=1+CMath::RandomReal();
//--- function call
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
//--- check
if(info!=1)
{
slcerrors=true;
continue;
}
//--- search errors
slerrors=slerrors || p<(double)(0.999);
//--- function call
GenerateTask(0,0,-(5*CMath::RandomReal()),5*CMath::RandomReal(),false,1.0,1.0,n,xy,s);
for(i=0;i<=n-1;i++)
{
//--- check
if(i%2==0)
xy[i].Set(1,5.0);
else
xy[i].Set(1,-5.0);
}
//--- check
if(n%2!=0)
xy[n-1].Set(1,0);
//--- function call
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
//--- check
if(info!=1)
{
slcerrors=true;
continue;
}
//--- search errors
slerrors=slerrors || p>0.001;
}
}
}
//--- General regression tests:
//--- Simple linear tests (small sample,optimum point,covariance)
for(n=3;n<=maxn;n++)
{
ArrayResize(s,n);
//--- Linear tests:
//--- a. random points,sigmas
//--- b. no sigmas
xy.Resize(n,2);
for(i=0;i<=n-1;i++)
{
xy[i].Set(0,2*CMath::RandomReal()-1);
xy[i].Set(1,2*CMath::RandomReal()-1);
s[i]=1+CMath::RandomReal();
}
//--- function call
CLinReg::LRBuildS(xy,s,n,1,info,wt,ar);
//--- check
if(info!=1)
{
grconverrors=true;
continue;
}
//--- function calls
CLinReg::LRUnpack(wt,tmpweights,tmpi);
//--- search errors
CLinReg::LRLines(xy,s,n,info2,a,b,vara,varb,covab,corrab,p);
gropterrors=gropterrors || MathAbs(a-tmpweights[1])>threshold;
gropterrors=gropterrors || MathAbs(b-tmpweights[0])>threshold;
grcoverrors=grcoverrors || MathAbs(vara-ar.m_c[1][1])>threshold;
grcoverrors=grcoverrors || MathAbs(varb-ar.m_c[0][0])>threshold;
grcoverrors=grcoverrors || MathAbs(covab-ar.m_c[1][0])>threshold;
grcoverrors=grcoverrors || MathAbs(covab-ar.m_c[0][1])>threshold;
//--- function call
CLinReg::LRBuild(xy,n,1,info,wt,ar);
//--- check
if(info!=1)
{
grconverrors=true;
continue;
}
//--- function calls
CLinReg::LRUnpack(wt,tmpweights,tmpi);
CLinReg::LRLine(xy,n,info2,a,b);
//--- search errors
gropterrors=gropterrors || MathAbs(a-tmpweights[1])>threshold;
gropterrors=gropterrors || MathAbs(b-tmpweights[0])>threshold;
}
//--- S covariance versus S-less covariance.
//--- Slightly skewed task,large sample size.
//--- Will S-less subroutine estimate covariance matrix good enough?
n=1000+CMath::RandomInteger(3000);
sigma=0.1+CMath::RandomReal()*1.9;
//--- allocation
xy.Resize(n,2);
ArrayResize(s,n);
//--- change values
for(i=0;i<=n-1;i++)
{
xy[i].Set(0,1.5*CMath::RandomReal()-0.5);
xy[i].Set(1,1.2*xy[i][0]-0.3+GenerateNormal(0,sigma));
s[i]=sigma;
}
//--- function calls
CLinReg::LRBuild(xy,n,1,info,wt,ar);
CLinReg::LRLines(xy,s,n,info2,a,b,vara,varb,covab,corrab,p);
//--- check
if(info!=1 || info2!=1)
grconverrors=true;
else
{
grcoverrors=grcoverrors || MathAbs(MathLog(ar.m_c[0][0]/varb))>MathLog(1.2);
grcoverrors=grcoverrors || MathAbs(MathLog(ar.m_c[1][1]/vara))>MathLog(1.2);
grcoverrors=grcoverrors || MathAbs(MathLog(ar.m_c[0][1]/covab))>MathLog(1.2);
grcoverrors=grcoverrors || MathAbs(MathLog(ar.m_c[1][0]/covab))>MathLog(1.2);
}
//--- General tests:
//--- * basis functions - up to cubic
//--- * task types:
//--- * data set is noisy sine half-period with random shift
//--- * tests:
//--- unpacking/packing
//--- optimality
//--- error estimates
//--- * tasks:
//--- 0=noised sine
//--- 1=degenerate task with 1-of-n encoded categorical variables
//--- 2=random task with large variation (for 1-type models)
//--- 3=random task with small variation (for 1-type models)
//--- Additional tasks TODO
//--- specially designed task with defective vectors which leads to
//--- the failure of the fast CV formula.
for(modeltype=0;modeltype<=1;modeltype++)
{
for(tasktype=0;tasktype<=3;tasktype++)
{
//--- check
if(tasktype==0)
{
m1=1;
m2=3;
}
//--- check
if(tasktype==1)
{
m1=9;
m2=9;
}
//--- check
if(tasktype==2 || tasktype==3)
{
m1=9;
m2=9;
}
//--- calculation
for(m=m1;m<=m2;m++)
{
//--- check
if(tasktype==0)
{
n1=m+3;
n2=m+20;
}
//--- check
if(tasktype==1)
{
n1=70+CMath::RandomInteger(70);
n2=n1;
}
//--- check
if(tasktype==2 || tasktype==3)
{
n1=100;
n2=n1;
}
for(n=n1;n<=n2;n++)
{
//--- allocation
xy.Resize(n,m+1);
ArrayResize(xy0,n);
ArrayResize(s,n);
hstep=0.001;
noiselevel=0.2;
//--- Prepare task
if(tasktype==0)
{
for(i=0;i<=n-1;i++)
xy[i].Set(0,2*CMath::RandomReal()-1);
for(i=0;i<=n-1;i++)
{
for(j=1;j<=m-1;j++)
xy[i].Set(j,xy[i][0]*xy[i][j-1]);
}
sinshift=CMath::RandomReal()*M_PI;
//--- calculation
for(i=0;i<=n-1;i++)
{
xy0[i]=MathSin(sinshift+M_PI*0.5*(xy[i][0]+1));
xy[i].Set(m,xy0[i]+noiselevel*GenerateNormal(0,1));
}
}
//--- check
if(tasktype==1)
{
//--- check
if(!CAp::Assert(m==9))
return(false);
//--- allocation
ArrayResize(ta,9);
//--- change values
ta[0]=1;
ta[1]=2;
ta[2]=3;
ta[3]=0.25;
ta[4]=0.5;
ta[5]=0.75;
ta[6]=0.06;
ta[7]=0.12;
ta[8]=0.18;
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
xy[i].Set(j,0);
xy[i].Set(i%3,1);
xy[i].Set(3+i/3%3,1);
xy[i].Set(6+i/9%3,1);
//--- change value
v=0.0;
for(i_=0;i_<=8;i_++)
v+=xy[i][i_]*ta[i_];
xy0[i]=v;
xy[i].Set(m,v+noiselevel*GenerateNormal(0,1));
}
}
//--- check
if(tasktype==2 || tasktype==3)
{
//--- check
if(!CAp::Assert(m==9))
return(false);
//--- allocation
ArrayResize(ta,9);
//--- change values
ta[0]=1;
ta[1]=-2;
ta[2]=3;
ta[3]=0.25;
ta[4]=-0.5;
ta[5]=0.75;
ta[6]=-0.06;
ta[7]=0.12;
ta[8]=-0.18;
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- check
if(tasktype==2)
xy[i].Set(j,1+GenerateNormal(0,3));
else
xy[i].Set(j,1+GenerateNormal(0,0.05));
}
//--- change value
v=0.0;
for(i_=0;i_<=8;i_++)
v+=xy[i][i_]*ta[i_];
xy0[i]=v;
xy[i].Set(m,v+noiselevel*GenerateNormal(0,1));
}
}
for(i=0;i<=n-1;i++)
s[i]=1+CMath::RandomReal();
//--- Solve (using S-variant,non-S-variant is not tested)
if(modeltype==0)
CLinReg::LRBuildS(xy,s,n,m,info,wt,ar);
else
CLinReg::LRBuildZS(xy,s,n,m,info,wt,ar);
//--- check
if(info!=1)
{
grconverrors=true;
continue;
}
//--- function call
CLinReg::LRUnpack(wt,tmpweights,tmpi);
//--- LRProcess test
ArrayResize(x,m);
//--- change values
v=tmpweights[m];
for(i=0;i<=m-1;i++)
{
x[i]=2*CMath::RandomReal()-1;
v=v+tmpweights[i]*x[i];
}
//--- search errors
grothererrors=grothererrors || MathAbs(v-CLinReg::LRProcess(wt,x))/MathMax(MathAbs(v),1)>threshold;
//--- LRPack test
CLinReg::LRPack(tmpweights,m,wt2);
ArrayResize(x,m);
for(i=0;i<=m-1;i++)
x[i]=2*CMath::RandomReal()-1;
v=CLinReg::LRProcess(wt,x);
grothererrors=grothererrors || MathAbs(v-CLinReg::LRProcess(wt2,x))/MathAbs(v)>threshold;
//--- Optimality test
for(k=0;k<=m;k++)
{
//--- check
if(modeltype==1 && k==m)
{
//--- 0-type models (with non-zero constant term)
//--- are tested for optimality of all coefficients.
//--- 1-type models (with zero constant term)
//--- are tested for optimality of non-constant terms only.
continue;
}
//--- change values
f=0;
fp=0;
fm=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
v=tmpweights[m];
for(j=0;j<=m-1;j++)
v=v+xy[i][j]*tmpweights[j];
f=f+CMath::Sqr((v-xy[i][m])/s[i]);
//--- check
if(k<m)
vv=xy[i][k];
else
vv=1;
//--- calculation
fp=fp+CMath::Sqr((v+vv*hstep-xy[i][m])/s[i]);
fm=fm+CMath::Sqr((v-vv*hstep-xy[i][m])/s[i]);
}
//--- search errors
gropterrors=(gropterrors || f>fp) || f>fm;
}
//--- Covariance matrix test:
//--- generate random vector,project coefficients on it,
//--- compare variance of projection with estimate provided
//--- by cov.matrix
ArrayResize(ta,estpasscount);
ArrayResize(tb,m+1);
ArrayResize(tc,m+1);
xy2.Resize(n,m+1);
for(i=0;i<=m;i++)
tb[i]=GenerateNormal(0,1);
//--- calculation
for(epass=0;epass<=estpasscount-1;epass++)
{
for(i=0;i<=n-1;i++)
{
for(i_=0;i_<=m-1;i_++)
xy2[i].Set(i_,xy[i][i_]);
xy2[i].Set(m,xy0[i]+s[i]*GenerateNormal(0,1));
}
//--- check
if(modeltype==0)
CLinReg::LRBuildS(xy2,s,n,m,info,wt,ar2);
else
CLinReg::LRBuildZS(xy2,s,n,m,info,wt,ar2);
//--- check
if(info!=1)
{
ta[epass]=0;
grconverrors=true;
continue;
}
//--- function call
CLinReg::LRUnpack(wt,w2,tmpi);
//--- change value
v=0.0;
for(i_=0;i_<=m;i_++)
v+=tb[i_]*w2[i_];
ta[epass]=v;
}
//--- function call
CalculateMV(ta,estpasscount,mean,means,stddev,stddevs);
for(i=0;i<=m;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=m;i_++)
v+=tb[i_]*ar.m_c[i_][i];
tc[i]=v;
}
//--- change value
v=0.0;
for(i_=0;i_<=m;i_++)
v+=tc[i_]*tb[i_];
//--- search errors
grcoverrors=grcoverrors || MathAbs((MathSqrt(v)-stddev)/stddevs)>=sigmathreshold;
//--- Test for the fast CV error:
//--- calculate CV error by definition (leaving out N
//--- points and recalculating solution).
//--- Test for the training set error
cvrmserror=0;
cvavgerror=0;
cvavgrelerror=0;
rmserror=0;
avgerror=0;
avgrelerror=0;
xy2.Resize(n-1,m+1);
ArrayResize(s2,n-1);
//--- change values
for(i=0;i<=n-2;i++)
{
for(i_=0;i_<=m;i_++)
xy2[i].Set(i_,xy[i+1][i_]);
s2[i]=s[i+1];
}
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- Trn
v=0.0;
for(i_=0;i_<=m-1;i_++)
v+=xy[i][i_]*tmpweights[i_];
v=v+tmpweights[m];
//--- search errors
rmserror=rmserror+CMath::Sqr(v-xy[i][m]);
avgerror=avgerror+MathAbs(v-xy[i][m]);
avgrelerror=avgrelerror+MathAbs((v-xy[i][m])/xy[i][m]);
//--- CV: non-defect vectors only
nondefect=true;
for(k=0;k<=ar.m_ncvdefects-1;k++)
{
//--- check
if(ar.m_cvdefects[k]==i)
nondefect=false;
}
//--- check
if(nondefect)
{
//--- check
if(modeltype==0)
CLinReg::LRBuildS(xy2,s2,n-1,m,info2,wt,ar2);
else
CLinReg::LRBuildZS(xy2,s2,n-1,m,info2,wt,ar2);
//--- check
if(info2!=1)
{
grconverrors=true;
continue;
}
//--- function call
CLinReg::LRUnpack(wt,w2,tmpi);
//--- change value
v=0.0;
for(i_=0;i_<=m-1;i_++)
v+=xy[i][i_]*w2[i_];
v=v+w2[m];
//--- search errors
cvrmserror=cvrmserror+CMath::Sqr(v-xy[i][m]);
cvavgerror=cvavgerror+MathAbs(v-xy[i][m]);
cvavgrelerror=cvavgrelerror+MathAbs((v-xy[i][m])/xy[i][m]);
}
//--- Next set
if(i!=n-1)
{
for(i_=0;i_<=m;i_++)
xy2[i].Set(i_,xy[i][i_]);
s2[i]=s[i];
}
}
//--- search errors
cvrmserror=MathSqrt(cvrmserror/(n-ar.m_ncvdefects));
cvavgerror=cvavgerror/(n-ar.m_ncvdefects);
cvavgrelerror=cvavgrelerror/(n-ar.m_ncvdefects);
rmserror=MathSqrt(rmserror/n);
avgerror=avgerror/n;
avgrelerror=avgrelerror/n;
gresterrors=gresterrors || MathAbs(MathLog(ar.m_cvrmserror/cvrmserror))>MathLog(1+1.0E-5);
gresterrors=gresterrors || MathAbs(MathLog(ar.m_cvavgerror/cvavgerror))>MathLog(1+1.0E-5);
gresterrors=gresterrors || MathAbs(MathLog(ar.m_cvavgrelerror/cvavgrelerror))>MathLog(1+1.0E-5);
gresterrors=gresterrors || MathAbs(MathLog(ar.m_rmserror/rmserror))>MathLog(1+1.0E-5);
gresterrors=gresterrors || MathAbs(MathLog(ar.m_avgerror/avgerror))>MathLog(1+1.0E-5);
gresterrors=gresterrors || MathAbs(MathLog(ar.m_avgrelerror/avgrelerror))>MathLog(1+1.0E-5);
}
}
}
}
//--- Additional subroutines
for(pass=1;pass<=50;pass++)
{
n=2;
//--- cycle
do
{
noiselevel=CMath::RandomReal()+0.1;
tasklevel=2*CMath::RandomReal()-1;
}
while(MathAbs(noiselevel-tasklevel)<=0.05);
//--- allocation
xy.Resize(3*n,2);
//--- change values
for(i=0;i<=n-1;i++)
{
xy[3*i+0].Set(0,i);
xy[3*i+1].Set(0,i);
xy[3*i+2].Set(0,i);
xy[3*i+0].Set(1,tasklevel-noiselevel);
xy[3*i+1].Set(1,tasklevel);
xy[3*i+2].Set(1,tasklevel+noiselevel);
}
//--- function call
CLinReg::LRBuild(xy,3*n,1,info,wt,ar);
//--- check
if(info==1)
{
//--- function calls
CLinReg::LRUnpack(wt,tmpweights,tmpi);
v=CLinReg::LRRMSError(wt,xy,3*n);
//--- search errors
grothererrors=grothererrors || MathAbs(v-noiselevel*MathSqrt(2.0/3.0))>threshold;
//--- function call
v=CLinReg::LRAvgError(wt,xy,3*n);
//--- search errors
grothererrors=grothererrors || MathAbs(v-noiselevel*(2.0/3.0))>threshold;
//--- function call
v=CLinReg::LRAvgRelError(wt,xy,3*n);
vv=(MathAbs(noiselevel/(tasklevel-noiselevel))+MathAbs(noiselevel/(tasklevel+noiselevel)))/3;
//--- search errors
grothererrors=grothererrors || MathAbs(v-vv)>threshold*vv;
}
else
grothererrors=true;
//--- change values
for(i=0;i<=n-1;i++)
{
xy[3*i+0].Set(0,i);
xy[3*i+1].Set(0,i);
xy[3*i+2].Set(0,i);
xy[3*i+0].Set(1,-noiselevel);
xy[3*i+1].Set(1,0);
xy[3*i+2].Set(1,noiselevel);
}
//--- function call
CLinReg::LRBuild(xy,3*n,1,info,wt,ar);
//--- check
if(info==1)
{
//--- function calls
CLinReg::LRUnpack(wt,tmpweights,tmpi);
v=CLinReg::LRAvgRelError(wt,xy,3*n);
//--- search errors
grothererrors=grothererrors || MathAbs(v-1)>threshold;
}
else
grothererrors=true;
}
//--- calculation
for(pass=1;pass<=10;pass++)
{
m=1+CMath::RandomInteger(5);
n=10+CMath::RandomInteger(10);
//--- allocation
xy.Resize(n,m+1);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m;j++)
xy[i].Set(j,2*CMath::RandomReal()-1);
}
//--- function call
CLinReg::LRBuild(xy,n,m,info,w,ar);
//--- check
if(info<0)
{
grothererrors=true;
break;
}
//--- allocation
ArrayResize(x1,m);
ArrayResize(x2,m);
//--- Same inputs on original leads to same outputs
//--- on copy created using LRCopy
UnsetLR(wt);
CLinReg::LRCopy(w,wt);
//--- change values
for(i=0;i<=m-1;i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=x1[i];
}
//--- change values
y1=CLinReg::LRProcess(w,x1);
y2=CLinReg::LRProcess(wt,x2);
allsame=y1==y2;
//--- search errors
grothererrors=grothererrors || !allsame;
}
//--- TODO: Degenerate tests (when design matrix and right part are zero)
//--- Final report
waserrors=(((((slerrors || slcerrors) || gropterrors) || grcoverrors) || gresterrors) || grothererrors) || grconverrors;
//--- check
if(!silent)
{
Print("REGRESSION TEST");
Print("STRAIGHT LINE REGRESSION: ");
//--- check
if(!slerrors)
Print("OK");
else
Print("FAILED");
Print("STRAIGHT LINE REGRESSION CONVERGENCE: ");
//--- check
if(!slcerrors)
Print("OK");
else
Print("FAILED");
Print("GENERAL LINEAR REGRESSION: ");
//--- check
if(!((((gropterrors || grcoverrors) || gresterrors) || grothererrors) || grconverrors))
Print("OK");
else
Print("FAILED");
Print("* OPTIMALITY: ");
//--- check
if(!gropterrors)
Print("OK");
else
Print("FAILED");
Print("* COV. MATRIX: ");
//--- check
if(!grcoverrors)
Print("OK");
else
Print("FAILED");
Print("* ERROR ESTIMATES: ");
//--- check
if(!gresterrors)
Print("OK");
else
Print("FAILED");
Print("* CONVERGENCE: ");
//--- check
if(!grconverrors)
Print("OK");
else
Print("FAILED");
Print("* OTHER SUBROUTINES: ");
//--- check
if(!grothererrors)
Print("OK");
else
Print("FAILED");
//--- check
if(waserrors)
Print("TEST SUMMARY: FAILED");
else
Print("TEST SUMMARY: PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Task generation. Meaningless task,just random numbers. |
//+------------------------------------------------------------------+
static void CTestLinRegUnit::GenerateRandomTask(const double xl,const double xr,
const bool randomx,const double ymin,
const double ymax,const double smin,
const double smax,const int n,
CMatrixDouble &xy,double &s[])
{
//--- create a variable
int i=0;
//--- allocation
xy.Resize(n,2);
ArrayResize(s,n);
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- check
if(randomx)
xy[i].Set(0,xl+(xr-xl)*CMath::RandomReal());
else
xy[i].Set(0,xl+(xr-xl)*i/(n-1));
//--- calculation
xy[i].Set(1,ymin+(ymax-ymin)*CMath::RandomReal());
s[i]=smin+(smax-smin)*CMath::RandomReal();
}
}
//+------------------------------------------------------------------+
//| Task generation. |
//+------------------------------------------------------------------+
static void CTestLinRegUnit::GenerateTask(const double a,const double b,
const double xl,const double xr,
const bool randomx,const double smin,
const double smax,const int n,
CMatrixDouble &xy,double &s[])
{
//--- create a variable
int i=0;
//--- allocation
xy.Resize(n,2);
ArrayResize(s,n);
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- check
if(randomx)
xy[i].Set(0,xl+(xr-xl)*CMath::RandomReal());
else
xy[i].Set(0,xl+(xr-xl)*i/(n-1));
//--- change values
s[i]=smin+(smax-smin)*CMath::RandomReal();
xy[i].Set(1,a+b*xy[i][0]+GenerateNormal(0,s[i]));
}
}
//+------------------------------------------------------------------+
//| Task generation. |
//| y[i] are filled based on A,B,X[I],S[I] |
//+------------------------------------------------------------------+
static void CTestLinRegUnit::FillTaskWithY(const double a,const double b,
const int n,CMatrixDouble &xy,
double &s[])
{
//--- create variables
int i=0;
//--- change values
for(i=0;i<=n-1;i++)
xy[i].Set(1,a+b*xy[i][0]+GenerateNormal(0,s[i]));
}
//+------------------------------------------------------------------+
//| Normal random numbers |
//+------------------------------------------------------------------+
static double CTestLinRegUnit::GenerateNormal(const double mean,const double sigma)
{
//--- create variables
double result=0;
double u=0;
double v=0;
double sum=0;
//--- initialization
result=mean;
//--- calculation
while(true)
{
//--- change values
u=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal();
v=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal();
sum=u*u+v*v;
//--- check
if(sum<1.0 && sum>0.0)
{
sum=MathSqrt(-(2*MathLog(sum)/sum));
result=sigma*u*sum+mean;
//--- return result
return(result);
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Moments estimates and their errors |
//+------------------------------------------------------------------+
static void CTestLinRegUnit::CalculateMV(double &x[],const int n,double &mean,
double &means,double &stddev,
double &stddevs)
{
//--- create variables
int i=0;
double v1=0;
double v2=0;
double variance=0;
//--- initialization
mean=0;
means=1;
stddev=0;
stddevs=1;
variance=0;
//--- check
if(n<=1)
return;
//--- Mean
for(i=0;i<=n-1;i++)
mean=mean+x[i];
mean=mean/n;
//--- Variance (using corrected two-pass algorithm)
if(n!=1)
{
//--- change value
v1=0;
for(i=0;i<=n-1;i++)
v1=v1+CMath::Sqr(x[i]-mean);
//--- change value
v2=0;
for(i=0;i<=n-1;i++)
v2=v2+(x[i]-mean);
v2=CMath::Sqr(v2)/n;
//--- calculation
variance=(v1-v2)/(n-1);
//--- check
if(variance<0.0)
variance=0;
stddev=MathSqrt(variance);
}
//--- Errors
means=stddev/MathSqrt(n);
stddevs=stddev*MathSqrt(2)/MathSqrt(n-1);
}
//+------------------------------------------------------------------+
//| Unsets LR |
//+------------------------------------------------------------------+
static void CTestLinRegUnit::UnsetLR(CLinearModel &lr)
{
//--- create variables
int info=0;
int i=0;
//--- create matrix
CMatrixDouble xy;
//--- object of class
CLRReport rep;
//--- allocation
xy.Resize(6,2);
//--- change values
for(i=0;i<=5;i++)
{
xy[i].Set(0,0);
xy[i].Set(1,0);
}
//--- function call
CLinReg::LRBuild(xy,6,1,info,lr,rep);
//--- check
if(!CAp::Assert(info>0))
return;
}
//+------------------------------------------------------------------+
//| Testing class CXblas |
//+------------------------------------------------------------------+
class CTestXBlasUnit
{
public:
//--- constructor, destructor
CTestXBlasUnit(void);
~CTestXBlasUnit(void);
//--- public method
static bool TestXBlas(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestXBlasUnit::CTestXBlasUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestXBlasUnit::~CTestXBlasUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CXblas |
//+------------------------------------------------------------------+
static bool CTestXBlasUnit::TestXBlas(const bool silent)
{
//--- create variables
bool approxerrors;
bool exactnesserrors;
bool waserrors;
double approxthreshold=0;
int maxn=0;
int passcount=0;
int n=0;
int i=0;
int pass=0;
double rv1=0;
double rv2=0;
double rv2err=0;
complex cv1=0;
complex cv2=0;
double cv2err=0;
double s=0;
int i_=0;
//--- create arrays
double rx[];
double ry[];
complex cx[];
complex cy[];
double temp[];
//--- initialization
approxerrors=false;
exactnesserrors=false;
waserrors=false;
approxthreshold=1000*CMath::m_machineepsilon;
maxn=1000;
passcount=10;
//--- tests:
//--- 1. ability to calculate dot product
//--- 2. higher precision
for(n=1;n<=maxn;n++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- ability to approximately calculate real dot product
ArrayResize(rx,n);
ArrayResize(ry,n);
ArrayResize(temp,n);
//--- change values
for(i=0;i<=n-1;i++)
{
//--- check
if(CMath::RandomReal()>0.2)
rx[i]=2*CMath::RandomReal()-1;
else
rx[i]=0;
//--- check
if(CMath::RandomReal()>0.2)
ry[i]=2*CMath::RandomReal()-1;
else
ry[i]=0;
}
//--- change value
rv1=0.0;
for(i_=0;i_<=n-1;i_++)
rv1+=rx[i_]*ry[i_];
//--- function call
CXblas::XDot(rx,ry,n,temp,rv2,rv2err);
//--- search errors
approxerrors=approxerrors || MathAbs(rv1-rv2)>approxthreshold;
//--- ability to approximately calculate complex dot product
ArrayResize(cx,n);
ArrayResize(cy,n);
ArrayResize(temp,2*n);
//--- change values
for(i=0;i<=n-1;i++)
{
//--- check
if(CMath::RandomReal()>0.2)
{
cx[i].re=2*CMath::RandomReal()-1;
cx[i].im=2*CMath::RandomReal()-1;
}
else
cx[i]=0;
//--- check
if(CMath::RandomReal()>0.2)
{
cy[i].re=2*CMath::RandomReal()-1;
cy[i].im=2*CMath::RandomReal()-1;
}
else
cy[i]=0;
}
//--- change value
cv1=0.0;
for(i_=0;i_<=n-1;i_++)
cv1+=cx[i_]*cy[i_];
//--- function call
CXblas::XCDot(cx,cy,n,temp,cv2,cv2err);
//--- search errors
approxerrors=approxerrors || CMath::AbsComplex(cv1-cv2)>approxthreshold;
}
}
//--- test of precision: real
n=50000;
ArrayResize(rx,n);
ArrayResize(ry,n);
ArrayResize(temp,n);
//--- calculation
for(pass=0;pass<=passcount-1;pass++)
{
//--- check
if(!CAp::Assert(n%2==0))
return(false);
//--- First test: X + X + ... + X - X - X - ... - X=1*X
s=MathExp(MathMax(pass,50));
//--- check
if(pass==passcount-1 && pass>1)
s=1E300;
//--- change values
ry[0]=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
for(i=1;i<=n-1;i++)
ry[i]=ry[0];
for(i=0;i<=n/2-1;i++)
rx[i]=1;
for(i=n/2;i<=n-2;i++)
rx[i]=-1;
rx[n-1]=0;
//--- function call
CXblas::XDot(rx,ry,n,temp,rv2,rv2err);
//--- search errors
exactnesserrors=exactnesserrors || rv2err<0.0;
exactnesserrors=exactnesserrors || rv2err>4*CMath::m_machineepsilon*MathAbs(ry[0]);
exactnesserrors=exactnesserrors || MathAbs(rv2-ry[0])>rv2err;
//--- First test: X + X + ... + X=N*X
s=MathExp(MathMax(pass,50));
//--- check
if(pass==passcount-1 && pass>1)
s=1E300;
//--- change values
ry[0]=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
for(i=1;i<=n-1;i++)
ry[i]=ry[0];
for(i=0;i<=n-1;i++)
rx[i]=1;
//--- function call
CXblas::XDot(rx,ry,n,temp,rv2,rv2err);
//--- search errors
exactnesserrors=exactnesserrors || rv2err<0.0;
exactnesserrors=exactnesserrors || rv2err>4*CMath::m_machineepsilon*MathAbs(ry[0])*n;
exactnesserrors=exactnesserrors || MathAbs(rv2-n*ry[0])>rv2err;
}
//--- test of precision: complex
n=50000;
ArrayResize(cx,n);
ArrayResize(cy,n);
ArrayResize(temp,2*n);
//--- calculation
for(pass=0;pass<=passcount-1;pass++)
{
//--- check
if(!CAp::Assert(n%2==0))
return(false);
//--- First test: X + X + ... + X - X - X - ... - X=1*X
s=MathExp(MathMax(pass,50));
//--- check
if(pass==passcount-1 && pass>1)
s=1E300;
//--- change values
cy[0].re=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
cy[0].im=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
for(i=1;i<=n-1;i++)
cy[i]=cy[0];
for(i=0;i<=n/2-1;i++)
cx[i]=1;
for(i=n/2;i<=n-2;i++)
cx[i]=-1;
cx[n-1]=0;
//--- function call
CXblas::XCDot(cx,cy,n,temp,cv2,cv2err);
//--- search errors
exactnesserrors=exactnesserrors || cv2err<0.0;
exactnesserrors=exactnesserrors || cv2err>4*CMath::m_machineepsilon*CMath::AbsComplex(cy[0]);
exactnesserrors=exactnesserrors || CMath::AbsComplex(cv2-cy[0])>cv2err;
//--- First test: X + X + ... + X=N*X
s=MathExp(MathMax(pass,50));
//--- check
if(pass==passcount-1 && pass>1)
s=1E300;
//--- change values
cy[0]=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
for(i=1;i<=n-1;i++)
cy[i]=cy[0];
for(i=0;i<=n-1;i++)
cx[i]=1;
//--- function call
CXblas::XCDot(cx,cy,n,temp,cv2,cv2err);
//--- search errors
exactnesserrors=exactnesserrors || cv2err<0.0;
exactnesserrors=exactnesserrors || cv2err>4*CMath::m_machineepsilon*CMath::AbsComplex(cy[0])*n;
exactnesserrors=exactnesserrors || CMath::AbsComplex(cv2-cy[0]*n)>cv2err;
}
//--- report
waserrors=approxerrors || exactnesserrors;
//--- check
if(!silent)
{
Print("TESTING XBLAS");
Print("APPROX.TESTS: ");
//--- check
if(approxerrors)
Print("FAILED");
else
Print("OK");
Print("EXACT TESTS: ");
//--- check
if(exactnesserrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- end
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CDenseSolver |
//+------------------------------------------------------------------+
class CTestDenseSolverUnit
{
private:
//--- private methods
static bool RMatrixCheckSolutionM(CMatrixDouble &xe,const int n,const int m,const double threshold,const int info,CDenseSolverReport &rep,CMatrixDouble &xs);
static bool RMatrixCheckSolution(CMatrixDouble &xe,const int n,const double threshold,const int info,CDenseSolverReport &rep,double &xs[]);
static bool RMatrixCheckSingularM(const int n,const int m,const int info,CDenseSolverReport &rep,CMatrixDouble &xs);
static bool RMatrixCheckSingular(const int n,const int info,CDenseSolverReport &rep,double &xs[]);
static bool CMatrixCheckSolutionM(CMatrixComplex &xe,const int n,const int m,const double threshold,const int info,CDenseSolverReport &rep,CMatrixComplex &xs);
static bool CMatrixCheckSolution(CMatrixComplex &xe,const int n,const double threshold,const int info,CDenseSolverReport &rep,complex &xs[]);
static bool CMatrixCheckSingularM(const int n,const int m,const int info,CDenseSolverReport &rep,CMatrixComplex &xs);
static bool CMatrixCheckSingular(const int n,const int info,CDenseSolverReport &rep,complex &xs[]);
static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b);
static void RMatrixDropHalf(CMatrixDouble &a,const int n,const bool droplower);
static void CMatrixDropHalf(CMatrixComplex &a,const int n,const bool droplower);
static void TestRSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &rerrors,bool &rfserrors);
static void TestSPDSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &spderrors,bool &rfserrors);
static void TestCSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &cerrors,bool &rfserrors);
static void TestHPDSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &hpderrors,bool &rfserrors);
static void Unset2D(CMatrixDouble &x);
static void Unset1D(double &x[]);
static void CUnset2D(CMatrixComplex &x);
static void CUnset1D(complex &x[]);
static void UnsetRep(CDenseSolverReport &r);
static void UnsetLSRep(CDenseSolverLSReport &r);
public:
//--- constructor, destructor
CTestDenseSolverUnit(void);
~CTestDenseSolverUnit(void);
//--- public method
static bool TestDenseSolver(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestDenseSolverUnit::CTestDenseSolverUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestDenseSolverUnit::~CTestDenseSolverUnit(void)
{
}
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
static bool CTestDenseSolverUnit::TestDenseSolver(const bool silent)
{
//--- create variables
int maxn=0;
int maxm=0;
int passcount=0;
double threshold=0;
bool rerrors;
bool cerrors;
bool spderrors;
bool hpderrors;
bool rfserrors;
bool waserrors;
//--- initialization
maxn=10;
maxm=5;
passcount=5;
threshold=10000*CMath::m_machineepsilon;
rfserrors=false;
rerrors=false;
cerrors=false;
spderrors=false;
hpderrors=false;
//--- function calls
TestRSolver(maxn,maxm,passcount,threshold,rerrors,rfserrors);
TestSPDSolver(maxn,maxm,passcount,threshold,spderrors,rfserrors);
TestCSolver(maxn,maxm,passcount,threshold,cerrors,rfserrors);
TestHPDSolver(maxn,maxm,passcount,threshold,hpderrors,rfserrors);
//--- search errors
waserrors=(((rerrors || cerrors) || spderrors) || hpderrors) || rfserrors;
//--- check
if(!silent)
{
Print("TESTING DENSE SOLVER");
Print("* REAL: ");
//--- check
if(rerrors)
Print("FAILED");
else
Print("OK");
Print("* COMPLEX: ");
//--- check
if(cerrors)
Print("FAILED");
else
Print("OK");
Print("* SPD: ");
//--- check
if(spderrors)
Print("FAILED");
else
Print("OK");
Print("* HPD: ");
//--- check
if(hpderrors)
Print("FAILED");
else
Print("OK");
Print("* ITERATIVE IMPROVEMENT: ");
//--- check
if(rfserrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Checks whether solver results are correct solution. |
//| Returns True on success. |
//+------------------------------------------------------------------+
static bool CTestDenseSolverUnit::RMatrixCheckSolutionM(CMatrixDouble &xe,
const int n,const int m,
const double threshold,
const int info,
CDenseSolverReport &rep,
CMatrixDouble &xs)
{
//--- create variables
bool result;
int i=0;
int j=0;
//--- initialization
result=true;
//--- check
if(info<=0)
result=false;
else
{
//--- calculation
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
result=result && MathAbs(xe[i][j]-xs[i][j])<=threshold;
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Checks whether solver results are correct solution. |
//| Returns True on success. |
//+------------------------------------------------------------------+
static bool CTestDenseSolverUnit::RMatrixCheckSolution(CMatrixDouble &xe,
const int n,
const double threshold,
const int info,
CDenseSolverReport &rep,
double &xs[])
{
//--- create a variable
int i_=0;
//--- create matrix
CMatrixDouble xsm;
//--- allocation
xsm.Resize(n,1);
//--- change values
for(i_=0;i_<=n-1;i_++)
xsm[i_].Set(0,xs[i_]);
//--- return result
return(RMatrixCheckSolutionM(xe,n,1,threshold,info,rep,xsm));
}
//+------------------------------------------------------------------+
//| Checks whether solver results indicate singular matrix. |
//| Returns True on success. |
//+------------------------------------------------------------------+
static bool CTestDenseSolverUnit::RMatrixCheckSingularM(const int n,
const int m,
const int info,
CDenseSolverReport &rep,
CMatrixDouble &xs)
{
//--- create variables
bool result;
int i=0;
int j=0;
//--- initialization
result=true;
//--- check
if(info!=-3 && info!=1)
result=false;
else
{
//--- calculation
result=result && !(rep.m_r1<0.0 || rep.m_r1>1000*CMath::m_machineepsilon);
result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon);
//--- check
if(info==-3)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
result=result && xs[i][j]==0.0;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Checks whether solver results indicate singular matrix. |
//| Returns True on success. |
//+------------------------------------------------------------------+
static bool CTestDenseSolverUnit::RMatrixCheckSingular(const int n,
const int info,
CDenseSolverReport &rep,
double &xs[])
{
//--- create a variable
int i_=0;
//--- create matrix
CMatrixDouble xsm;
//--- allocation
xsm.Resize(n,1);
//--- change values
for(i_=0;i_<=n-1;i_++)
xsm[i_].Set(0,xs[i_]);
//--- return result
return(RMatrixCheckSingularM(n,1,info,rep,xsm));
}
//+------------------------------------------------------------------+
//| Checks whether solver results are correct solution. |
//| Returns True on success. |
//+------------------------------------------------------------------+
static bool CTestDenseSolverUnit::CMatrixCheckSolutionM(CMatrixComplex &xe,
const int n,const int m,
const double threshold,
const int info,
CDenseSolverReport &rep,
CMatrixComplex &xs)
{
//--- create variables
bool result;
int i=0;
int j=0;
//--- initialization
result=true;
//--- check
if(info<=0)
result=false;
else
{
//--- calculation
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
result=result && CMath::AbsComplex(xe[i][j]-xs[i][j])<=threshold;
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Checks whether solver results are correct solution. |
//| Returns True on success. |
//+------------------------------------------------------------------+
static bool CTestDenseSolverUnit::CMatrixCheckSolution(CMatrixComplex &xe,
const int n,
const double threshold,
const int info,
CDenseSolverReport &rep,
complex &xs[])
{
//--- create a variable
int i_=0;
//--- create matrix
CMatrixComplex xsm;
//--- allocation
xsm.Resize(n,1);
//--- change values
for(i_=0;i_<=n-1;i_++)
xsm[i_].Set(0,xs[i_]);
//--- return result
return(CMatrixCheckSolutionM(xe,n,1,threshold,info,rep,xsm));
}
//+------------------------------------------------------------------+
//| Checks whether solver results indicate singular matrix. |
//| Returns True on success. |
//+------------------------------------------------------------------+
static bool CTestDenseSolverUnit::CMatrixCheckSingularM(const int n,
const int m,
const int info,
CDenseSolverReport &rep,
CMatrixComplex &xs)
{
//--- create variables
bool result;
int i=0;
int j=0;
//--- initialization
result=true;
//--- check
if(info!=-3 && info!=1)
result=false;
else
{
//--- calculation
result=result && !(rep.m_r1<0.0 || rep.m_r1>1000*CMath::m_machineepsilon);
result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon);
//--- check
if(info==-3)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
result=result && xs[i][j]==0;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Checks whether solver results indicate singular matrix. |
//| Returns True on success. |
//+------------------------------------------------------------------+
static bool CTestDenseSolverUnit::CMatrixCheckSingular(const int n,
const int info,
CDenseSolverReport &rep,
complex &xs[])
{
//--- create variables
int i_=0;
//--- create matrix
CMatrixComplex xsm;
//--- allocation
xsm.Resize(n,1);
//--- change values
for(i_=0;i_<=n-1;i_++)
xsm[i_].Set(0,xs[i_]);
//--- return result
return(CMatrixCheckSingularM(n,1,info,rep,xsm));
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
static void CTestDenseSolverUnit::RMatrixMakeACopy(CMatrixDouble &a,
const int m,
const int n,
CMatrixDouble &b)
{
//--- create variables
int i=0;
int j=0;
//--- allocation
b.Resize(m,n);
//--- copy
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
b[i].Set(j,a[i][j]);
}
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
static void CTestDenseSolverUnit::CMatrixMakeACopy(CMatrixComplex &a,
const int m,
const int n,
CMatrixComplex &b)
{
//--- create variables
int i=0;
int j=0;
//--- allocation
b.Resize(m,n);
//--- copy
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
b[i].Set(j,a[i][j]);
}
}
//+------------------------------------------------------------------+
//| Drops upper or lower half of the matrix - fills it by special |
//| pattern which may be used later to ensure that this part wasn't |
//| changed |
//+------------------------------------------------------------------+
static void CTestDenseSolverUnit::RMatrixDropHalf(CMatrixDouble &a,
const int n,
const bool droplower)
{
//--- create variables
int i=0;
int j=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if((droplower && i>j) || (!droplower && i<j))
a[i].Set(j,1+2*i+3*j);
}
}
}
//+------------------------------------------------------------------+
//| Drops upper or lower half of the matrix - fills it by special |
//| pattern which may be used later to ensure that this part wasn't |
//| changed |
//+------------------------------------------------------------------+
static void CTestDenseSolverUnit::CMatrixDropHalf(CMatrixComplex &a,
const int n,
const bool droplower)
{
//--- create variables
int i=0;
int j=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if((droplower && i>j) || (!droplower && i<j))
a[i].Set(j,1+2*i+3*j);
}
}
}
//+------------------------------------------------------------------+
//| Real test |
//+------------------------------------------------------------------+
static void CTestDenseSolverUnit::TestRSolver(const int maxn,const int maxm,
const int passcount,
const double threshold,
bool &rerrors,bool &rfserrors)
{
//--- create variables
int i=0;
int j=0;
int k=0;
int n=0;
int m=0;
int pass=0;
int taskkind=0;
double v=0;
double verr=0;
int info=0;
int i_=0;
int i1_=0;
//--- create arrays
int p[];
double bv[];
double xv[];
double y[];
double tx[];
//--- create matrix
CMatrixDouble a;
CMatrixDouble lua;
CMatrixDouble atmp;
CMatrixDouble xe;
CMatrixDouble b;
CMatrixDouble x;
//--- objects of classes
CDenseSolverReport rep;
CDenseSolverLSReport repls;
//--- General square matrices:
//--- * test general solvers
//--- * test least squares solver
for(pass=1;pass<=passcount;pass++)
{
for(n=1;n<=maxn;n++)
{
for(m=1;m<=maxm;m++)
{
//--- ********************************************************
//--- WELL CONDITIONED TASKS
//--- ability to find correct solution is tested
//--- ********************************************************
//--- 1. generate random well conditioned matrix A.
//--- 2. generate random solution vector xe
//--- 3. generate right part b=A*xe
//--- 4. test different methods on original A
CMatGen::RMatrixRndCond(n,1000,a);
RMatrixMakeACopy(a,n,n,lua);
CTrFac::RMatrixLU(lua,n,n,p);
//--- allocation
xe.Resize(n,m);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
xe[i].Set(j,2*CMath::RandomReal()-1);
}
//--- allocation
b.Resize(n,m);
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*xe[i_][j];
b[i].Set(j,v);
}
}
//--- Test solvers
info=0;
//--- function calls
UnsetRep(rep);
Unset2D(x);
CDenseSolver::RMatrixSolveM(a,n,b,m,CMath::RandomReal()>0.5,info,rep,x);
//--- search errors
rerrors=rerrors || !RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
Unset1D(xv);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::RMatrixSolve(a,n,bv,info,rep,xv);
//--- search errors
rerrors=rerrors || !RMatrixCheckSolution(xe,n,threshold,info,rep,xv);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
Unset2D(x);
CDenseSolver::RMatrixLUSolveM(lua,p,n,b,m,info,rep,x);
//--- search errors
rerrors=rerrors || !RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
Unset1D(xv);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::RMatrixLUSolve(lua,p,n,bv,info,rep,xv);
//--- search errors
rerrors=rerrors || !RMatrixCheckSolution(xe,n,threshold,info,rep,xv);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
Unset2D(x);
CDenseSolver::RMatrixMixedSolveM(a,lua,p,n,b,m,info,rep,x);
//--- search errors
rerrors=rerrors || !RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
Unset1D(xv);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::RMatrixMixedSolve(a,lua,p,n,bv,info,rep,xv);
//--- search errors
rerrors=rerrors || !RMatrixCheckSolution(xe,n,threshold,info,rep,xv);
//--- Test DenseSolverRLS():
//--- * test on original system A*x=b
//--- * test on overdetermined system with the same solution: (A' A')'*x=(b' b')'
//--- * test on underdetermined system with the same solution: (A 0 0 0 ) * z=b
info=0;
UnsetLSRep(repls);
Unset1D(xv);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::RMatrixSolveLS(a,n,n,bv,0.0,info,repls,xv);
//--- check
if(info<=0)
rerrors=true;
else
{
//--- search errors
rerrors=(rerrors || repls.m_r2<100*CMath::m_machineepsilon) || repls.m_r2>1+1000*CMath::m_machineepsilon;
rerrors=(rerrors || repls.m_n!=n) || repls.m_k!=0;
for(i=0;i<=n-1;i++)
rerrors=rerrors || MathAbs(xe[i][0]-xv[i])>threshold;
}
//--- change value
info=0;
//--- function calls
UnsetLSRep(repls);
Unset1D(xv);
//--- allocation
ArrayResize(bv,2*n);
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- change value
i1_=-n;
for(i_=n;i_<=2*n-1;i_++)
bv[i_]=b[i_+i1_][0];
//--- allocation
atmp.Resize(2*n,n);
//--- function calls
CBlas::CopyMatrix(a,0,n-1,0,n-1,atmp,0,n-1,0,n-1);
CBlas::CopyMatrix(a,0,n-1,0,n-1,atmp,n,2*n-1,0,n-1);
CDenseSolver::RMatrixSolveLS(atmp,2*n,n,bv,0.0,info,repls,xv);
//--- check
if(info<=0)
rerrors=true;
else
{
//--- search errors
rerrors=(rerrors || repls.m_r2<100*CMath::m_machineepsilon) || repls.m_r2>1+1000*CMath::m_machineepsilon;
rerrors=(rerrors || repls.m_n!=n) || repls.m_k!=0;
for(i=0;i<=n-1;i++)
rerrors=rerrors || MathAbs(xe[i][0]-xv[i])>threshold;
}
//--- change value
info=0;
//--- function calls
UnsetLSRep(repls);
Unset1D(xv);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- allocation
atmp.Resize(n,2*n);
CBlas::CopyMatrix(a,0,n-1,0,n-1,atmp,0,n-1,0,n-1);
for(i=0;i<=n-1;i++)
{
for(j=n;j<=2*n-1;j++)
atmp[i].Set(j,0);
}
//--- function call
CDenseSolver::RMatrixSolveLS(atmp,n,2*n,bv,0.0,info,repls,xv);
//--- check
if(info<=0)
rerrors=true;
else
{
//--- search errors
rerrors=rerrors || repls.m_r2!=0.0;
rerrors=(rerrors || repls.m_n!=2*n) || repls.m_k!=n;
for(i=0;i<=n-1;i++)
rerrors=rerrors || MathAbs(xe[i][0]-xv[i])>threshold;
for(i=n;i<=2*n-1;i++)
rerrors=rerrors || MathAbs(xv[i])>threshold;
}
//--- ********************************************************
//--- EXACTLY SINGULAR MATRICES
//--- ability to detect singularity is tested
//--- ********************************************************
//--- 1. generate different types of singular matrices:
//--- * zero
//--- * with zero columns
//--- * with zero rows
//--- * with equal rows/columns
//--- 2. generate random solution vector xe
//--- 3. generate right part b=A*xe
//--- 4. test different methods
//
for(taskkind=0;taskkind<=4;taskkind++)
{
Unset2D(a);
//--- check
if(taskkind==0)
{
//--- all zeros
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0);
}
}
//--- check
if(taskkind==1)
{
//--- there is zero column
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(k,0*a[i_][k]);
}
//--- check
if(taskkind==2)
{
//--- there is zero row
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0;i_<=n-1;i_++)
a[k].Set(i_,0*a[k][i_]);
}
//--- check
if(taskkind==3)
{
//--- equal columns
if(n<2)
continue;
//--- change values
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(0,a[i_][k]);
}
//--- check
if(taskkind==4)
{
//--- equal rows
if(n<2)
continue;
//--- change values
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0;i_<=n-1;i_++)
a[0].Set(i_,a[k][i_]);
}
//--- allocation
xe.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
xe[i].Set(j,2*CMath::RandomReal()-1);
}
//--- allocation
b.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*xe[i_][j];
b[i].Set(j,v);
}
}
//--- function calls
RMatrixMakeACopy(a,n,n,lua);
CTrFac::RMatrixLU(lua,n,n,p);
//--- Test RMatrixSolveM()
info=0;
//--- function calls
UnsetRep(rep);
Unset2D(x);
CDenseSolver::RMatrixSolveM(a,n,b,m,CMath::RandomReal()>0.5,info,rep,x);
//--- search errors
rerrors=rerrors || !RMatrixCheckSingularM(n,m,info,rep,x);
//--- Test RMatrixSolve()
info=0;
//--- function calls
UnsetRep(rep);
Unset2D(x);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::RMatrixSolve(a,n,bv,info,rep,xv);
//--- search errors
rerrors=rerrors || !RMatrixCheckSingular(n,info,rep,xv);
//--- Test RMatrixLUSolveM()
info=0;
//--- function calls
UnsetRep(rep);
Unset2D(x);
CDenseSolver::RMatrixLUSolveM(lua,p,n,b,m,info,rep,x);
//--- search errors
rerrors=rerrors || !RMatrixCheckSingularM(n,m,info,rep,x);
//--- Test RMatrixLUSolve()
info=0;
//--- function calls
UnsetRep(rep);
Unset2D(x);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::RMatrixLUSolve(lua,p,n,bv,info,rep,xv);
//--- search errors
rerrors=rerrors || !RMatrixCheckSingular(n,info,rep,xv);
//--- Test RMatrixMixedSolveM()
info=0;
//--- function calls
UnsetRep(rep);
Unset2D(x);
CDenseSolver::RMatrixMixedSolveM(a,lua,p,n,b,m,info,rep,x);
//--- search errors
rerrors=rerrors || !RMatrixCheckSingularM(n,m,info,rep,x);
//--- Test RMatrixMixedSolve()
info=0;
//--- function calls
UnsetRep(rep);
Unset2D(x);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::RMatrixMixedSolve(a,lua,p,n,bv,info,rep,xv);
//--- search errors
rerrors=rerrors || !RMatrixCheckSingular(n,info,rep,xv);
}
}
}
}
//--- test iterative improvement
for(pass=1;pass<=passcount;pass++)
{
//--- Test iterative improvement matrices
//--- A matrix/right part are constructed such that both matrix
//--- and solution components are within (-1,+1). Such matrix/right part
//--- have nice properties - system can be solved using iterative
//--- improvement with ||A*x-b|| about several ulps of max(1,||b||).
n=100;
a.Resize(n,n);
b.Resize(n,1);
ArrayResize(bv,n);
ArrayResize(tx,n);
ArrayResize(xv,n);
ArrayResize(y,n);
//--- change values
for(i=0;i<=n-1;i++)
xv[i]=2*CMath::RandomReal()-1;
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
for(i_=0;i_<=n-1;i_++)
y[i_]=a[i][i_];
//--- function call
CXblas::XDot(y,xv,n,tx,v,verr);
bv[i]=v;
}
//--- change values
for(i_=0;i_<=n-1;i_++)
b[i_].Set(0,bv[i_]);
//--- Test RMatrixSolveM()
Unset2D(x);
CDenseSolver::RMatrixSolveM(a,n,b,1,true,info,rep,x);
//--- check
if(info<=0)
rfserrors=true;
else
{
//--- allocation
ArrayResize(xv,n);
for(i_=0;i_<=n-1;i_++)
xv[i_]=x[i_][0];
for(i=0;i<=n-1;i++)
{
for(i_=0;i_<=n-1;i_++)
y[i_]=a[i][i_];
//--- function call
CXblas::XDot(y,xv,n,tx,v,verr);
//--- search errors
rfserrors=rfserrors || MathAbs(v-b[i][0])>8*CMath::m_machineepsilon*MathMax(1,MathAbs(b[i][0]));
}
}
//--- Test RMatrixSolve()
Unset1D(xv);
CDenseSolver::RMatrixSolve(a,n,bv,info,rep,xv);
//--- check
if(info<=0)
rfserrors=true;
else
{
for(i=0;i<=n-1;i++)
{
for(i_=0;i_<=n-1;i_++)
y[i_]=a[i][i_];
//--- function call
CXblas::XDot(y,xv,n,tx,v,verr);
//--- search errors
rfserrors=rfserrors || MathAbs(v-bv[i])>8*CMath::m_machineepsilon*MathMax(1,MathAbs(bv[i]));
}
}
//--- Test LS-solver on the same matrix
CDenseSolver::RMatrixSolveLS(a,n,n,bv,0.0,info,repls,xv);
//--- check
if(info<=0)
rfserrors=true;
else
{
for(i=0;i<=n-1;i++)
{
for(i_=0;i_<=n-1;i_++)
y[i_]=a[i][i_];
//--- function call
CXblas::XDot(y,xv,n,tx,v,verr);
//--- search errors
rfserrors=rfserrors || MathAbs(v-bv[i])>8*CMath::m_machineepsilon*MathMax(1,MathAbs(bv[i]));
}
}
}
}
//+------------------------------------------------------------------+
//| SPD test |
//+------------------------------------------------------------------+
static void CTestDenseSolverUnit::TestSPDSolver(const int maxn,const int maxm,
const int passcount,
const double threshold,
bool &spderrors,bool &rfserrors)
{
//--- create variables
int i=0;
int j=0;
int k=0;
int n=0;
int m=0;
int pass=0;
int taskkind=0;
double v=0;
bool isupper;
int info=0;
int i_=0;
//--- create arrays
int p[];
double bv[];
double xv[];
double y[];
double tx[];
//--- create matrix
CMatrixDouble a;
CMatrixDouble cha;
CMatrixDouble atmp;
CMatrixDouble xe;
CMatrixDouble b;
CMatrixDouble x;
//--- objects of class
CDenseSolverReport rep;
CDenseSolverLSReport repls;
//--- General square matrices:
//--- * test general solvers
//--- * test least squares solver
for(pass=1;pass<=passcount;pass++)
{
for(n=1;n<=maxn;n++)
{
for(m=1;m<=maxm;m++)
{
//--- ********************************************************
//--- WELL CONDITIONED TASKS
//--- ability to find correct solution is tested
//--- ********************************************************
//--- 1. generate random well conditioned matrix A.
//--- 2. generate random solution vector xe
//--- 3. generate right part b=A*xe
//--- 4. test different methods on original A
isupper=CMath::RandomReal()>0.5;
CMatGen::SPDMatrixRndCond(n,1000,a);
RMatrixMakeACopy(a,n,n,cha);
//--- check
if(!CTrFac::SPDMatrixCholesky(cha,n,isupper))
{
spderrors=true;
return;
}
//--- allocation
xe.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
xe[i].Set(j,2*CMath::RandomReal()-1);
}
//--- allocation
b.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*xe[i_][j];
b[i].Set(j,v);
}
}
//--- function calls
RMatrixDropHalf(a,n,isupper);
RMatrixDropHalf(cha,n,isupper);
//--- Test solvers
info=0;
//--- function calls
UnsetRep(rep);
Unset2D(x);
CDenseSolver::SPDMatrixSolveM(a,n,isupper,b,m,info,rep,x);
//--- search errors
spderrors=spderrors || !RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
Unset1D(xv);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::SPDMatrixSolve(a,n,isupper,bv,info,rep,xv);
//--- search errors
spderrors=spderrors || !RMatrixCheckSolution(xe,n,threshold,info,rep,xv);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
Unset2D(x);
CDenseSolver::SPDMatrixCholeskySolveM(cha,n,isupper,b,m,info,rep,x);
//--- search errors
spderrors=spderrors || !RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
//--- change value
info=0;
//--- function calls
UnsetRep(rep);
Unset1D(xv);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::SPDMatrixCholeskySolve(cha,n,isupper,bv,info,rep,xv);
//--- search errors
spderrors=spderrors || !RMatrixCheckSolution(xe,n,threshold,info,rep,xv);
//--- ********************************************************
//--- EXACTLY SINGULAR MATRICES
//--- ability to detect singularity is tested
//--- ********************************************************
//--- 1. generate different types of singular matrices:
//--- * zero
//--- * with zero columns
//--- * with zero rows
//--- * with equal rows/columns
//--- 2. generate random solution vector xe
//--- 3. generate right part b=A*xe
//--- 4. test different methods
for(taskkind=0;taskkind<=3;taskkind++)
{
Unset2D(a);
//--- check
if(taskkind==0)
{
//--- all zeros
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0);
}
}
//--- check
if(taskkind==1)
{
//--- there is zero column
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=i;j<=n-1;j++)
{
a[i].Set(j,2*CMath::RandomReal()-1);
a[j].Set(i,a[i][j]);
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(k,0*a[i_][k]);
for(i_=0;i_<=n-1;i_++)
a[k].Set(i_,0*a[k][i_]);
}
//--- check
if(taskkind==2)
{
//--- there is zero row
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=i;j<=n-1;j++)
{
a[i].Set(j,2*CMath::RandomReal()-1);
a[j].Set(i,a[i][j]);
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0;i_<=n-1;i_++)
a[k].Set(i_,0*a[k][i_]);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(k,0*a[i_][k]);
}
//--- check
if(taskkind==3)
{
//--- equal columns/rows
if(n<2)
continue;
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=i;j<=n-1;j++)
{
a[i].Set(j,2*CMath::RandomReal()-1);
a[j].Set(i,a[i][j]);
}
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(0,a[i_][k]);
for(i_=0;i_<=n-1;i_++)
a[0].Set(i_,a[k][i_]);
}
//--- allocation
xe.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
xe[i].Set(j,2*CMath::RandomReal()-1);
}
//--- allocation
b.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*xe[i_][j];
b[i].Set(j,v);
}
}
//--- function calls
RMatrixMakeACopy(a,n,n,cha);
RMatrixDropHalf(a,n,isupper);
RMatrixDropHalf(cha,n,isupper);
//--- Test SPDMatrixSolveM()
info=0;
//--- function calls
UnsetRep(rep);
Unset2D(x);
CDenseSolver::SPDMatrixSolveM(a,n,isupper,b,m,info,rep,x);
//--- search errors
spderrors=spderrors || !RMatrixCheckSingularM(n,m,info,rep,x);
//--- Test SPDMatrixSolve()
info=0;
UnsetRep(rep);
Unset2D(x);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::SPDMatrixSolve(a,n,isupper,bv,info,rep,xv);
//--- search errors
spderrors=spderrors || !RMatrixCheckSingular(n,info,rep,xv);
//--- 'equal columns/rows' are degenerate,but
//--- Cholesky matrix with equal columns/rows IS NOT degenerate,
//--- so it is not used for testing purposes.
if(taskkind!=3)
{
//--- Test SPDMatrixLUSolveM()
info=0;
//--- function calls
UnsetRep(rep);
Unset2D(x);
CDenseSolver::SPDMatrixCholeskySolveM(cha,n,isupper,b,m,info,rep,x);
//--- search errors
spderrors=spderrors || !RMatrixCheckSingularM(n,m,info,rep,x);
//--- Test SPDMatrixLUSolve()
info=0;
UnsetRep(rep);
Unset2D(x);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::SPDMatrixCholeskySolve(cha,n,isupper,bv,info,rep,xv);
//--- search errors
spderrors=spderrors || !RMatrixCheckSingular(n,info,rep,xv);
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Real test |
//+------------------------------------------------------------------+
static void CTestDenseSolverUnit::TestCSolver(const int maxn,const int maxm,
const int passcount,
const double threshold,
bool &cerrors,bool &rfserrors)
{
//--- create variables
int i=0;
int j=0;
int k=0;
int n=0;
int m=0;
int pass=0;
int taskkind=0;
double verr=0;
complex v=0;
int info=0;
int i_=0;
//--- create arrays
int p[];
complex bv[];
complex xv[];
complex y[];
double tx[];
//--- create matrix
CMatrixComplex a;
CMatrixComplex lua;
CMatrixComplex atmp;
CMatrixComplex xe;
CMatrixComplex b;
CMatrixComplex x;
//--- objects of classes
CDenseSolverReport rep;
CDenseSolverLSReport repls;
//--- General square matrices:
//--- * test general solvers
//--- * test least squares solver
for(pass=1;pass<=passcount;pass++)
{
for(n=1;n<=maxn;n++)
{
for(m=1;m<=maxm;m++)
{
//--- ********************************************************
//--- WELL CONDITIONED TASKS
//--- ability to find correct solution is tested
//--- ********************************************************
//--- 1. generate random well conditioned matrix A.
//--- 2. generate random solution vector xe
//--- 3. generate right part b=A*xe
//--- 4. test different methods on original A
CMatGen::CMatrixRndCond(n,1000,a);
CMatrixMakeACopy(a,n,n,lua);
CTrFac::CMatrixLU(lua,n,n,p);
//--- allocation
xe.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
{
xe[i].SetRe(j,2*CMath::RandomReal()-1);
xe[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- allocation
b.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*xe[i_][j];
b[i].Set(j,v);
}
}
//--- Test solvers
info=0;
//--- function calls
UnsetRep(rep);
CUnset2D(x);
CDenseSolver::CMatrixSolveM(a,n,b,m,CMath::RandomReal()>0.5,info,rep,x);
//--- search errors
cerrors=cerrors || !CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
//--- change values
info=0;
//--- function calls
UnsetRep(rep);
CUnset1D(xv);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::CMatrixSolve(a,n,bv,info,rep,xv);
//--- search errors
cerrors=cerrors || !CMatrixCheckSolution(xe,n,threshold,info,rep,xv);
//--- change values
info=0;
//--- function calls
UnsetRep(rep);
CUnset2D(x);
CDenseSolver::CMatrixLUSolveM(lua,p,n,b,m,info,rep,x);
//--- search errors
cerrors=cerrors || !CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
//--- change values
info=0;
//--- function calls
UnsetRep(rep);
CUnset1D(xv);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::CMatrixLUSolve(lua,p,n,bv,info,rep,xv);
//--- search errors
cerrors=cerrors || !CMatrixCheckSolution(xe,n,threshold,info,rep,xv);
//--- change values
info=0;
//--- function calls
UnsetRep(rep);
CUnset2D(x);
CDenseSolver::CMatrixMixedSolveM(a,lua,p,n,b,m,info,rep,x);
//--- search errors
cerrors=cerrors || !CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
//--- change values
info=0;
//--- function calls
UnsetRep(rep);
CUnset1D(xv);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::CMatrixMixedSolve(a,lua,p,n,bv,info,rep,xv);
//--- search errors
cerrors=cerrors || !CMatrixCheckSolution(xe,n,threshold,info,rep,xv);
//--- ********************************************************
//--- EXACTLY SINGULAR MATRICES
//--- ability to detect singularity is tested
//--- ********************************************************
//--- 1. generate different types of singular matrices:
//--- * zero
//--- * with zero columns
//--- * with zero rows
//--- * with equal rows/columns
//--- 2. generate random solution vector xe
//--- 3. generate right part b=A*xe
//--- 4. test different methods
for(taskkind=0;taskkind<=4;taskkind++)
{
CUnset2D(a);
//--- check
if(taskkind==0)
{
//--- all zeros
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0);
}
}
//--- check
if(taskkind==1)
{
//--- there is zero column
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(k,a[i_][k]*0);
}
//--- check
if(taskkind==2)
{
//--- there is zero row
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0;i_<=n-1;i_++)
a[k].Set(i_,a[k][i_]*0);
}
//--- check
if(taskkind==3)
{
//--- equal columns
if(n<2)
continue;
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(0,a[i_][k]);
}
//--- check
if(taskkind==4)
{
//--- equal rows
if(n<2)
continue;
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0;i_<=n-1;i_++)
a[0].Set(i_,a[k][i_]);
}
//--- allocation
xe.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
xe[i].Set(j,2*CMath::RandomReal()-1);
}
//--- allocation
b.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*xe[i_][j];
b[i].Set(j,v);
}
}
//--- function calls
CMatrixMakeACopy(a,n,n,lua);
CTrFac::CMatrixLU(lua,n,n,p);
//--- Test CMatrixSolveM()
info=0;
//--- function calls
UnsetRep(rep);
CUnset2D(x);
CDenseSolver::CMatrixSolveM(a,n,b,m,CMath::RandomReal()>0.5,info,rep,x);
//--- search errors
cerrors=cerrors || !CMatrixCheckSingularM(n,m,info,rep,x);
//--- Test CMatrixSolve()
info=0;
//--- function calls
UnsetRep(rep);
CUnset2D(x);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::CMatrixSolve(a,n,bv,info,rep,xv);
//--- search errors
cerrors=cerrors || !CMatrixCheckSingular(n,info,rep,xv);
//--- Test CMatrixLUSolveM()
info=0;
//--- function calls
UnsetRep(rep);
CUnset2D(x);
CDenseSolver::CMatrixLUSolveM(lua,p,n,b,m,info,rep,x);
//--- search errors
cerrors=cerrors || !CMatrixCheckSingularM(n,m,info,rep,x);
//--- Test CMatrixLUSolve()
info=0;
//--- function calls
UnsetRep(rep);
CUnset2D(x);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::CMatrixLUSolve(lua,p,n,bv,info,rep,xv);
//--- search errors
cerrors=cerrors || !CMatrixCheckSingular(n,info,rep,xv);
//--- Test CMatrixMixedSolveM()
info=0;
//--- function calls
UnsetRep(rep);
CUnset2D(x);
CDenseSolver::CMatrixMixedSolveM(a,lua,p,n,b,m,info,rep,x);
//--- search errors
cerrors=cerrors || !CMatrixCheckSingularM(n,m,info,rep,x);
//--- Test CMatrixMixedSolve()
info=0;
//--- function calls
UnsetRep(rep);
CUnset2D(x);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::CMatrixMixedSolve(a,lua,p,n,bv,info,rep,xv);
//--- search errors
cerrors=cerrors || !CMatrixCheckSingular(n,info,rep,xv);
}
}
}
}
//--- test iterative improvement
for(pass=1;pass<=passcount;pass++)
{
//--- Test iterative improvement matrices
//--- A matrix/right part are constructed such that both matrix
//--- and solution components magnitudes are within (-1,+1).
//--- Such matrix/right part have nice properties - system can
//--- be solved using iterative improvement with ||A*x-b|| about
//--- several ulps of max(1,||b||).
n=100;
a.Resize(n,n);
b.Resize(n,1);
ArrayResize(bv,n);
ArrayResize(tx,2*n);
ArrayResize(xv,n);
ArrayResize(y,n);
//--- change values
for(i=0;i<=n-1;i++)
{
xv[i].re=2*CMath::RandomReal()-1;
xv[i].im=2*CMath::RandomReal()-1;
}
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
}
for(i_=0;i_<=n-1;i_++)
y[i_]=a[i][i_];
//--- function call
CXblas::XCDot(y,xv,n,tx,v,verr);
bv[i]=v;
}
for(i_=0;i_<=n-1;i_++)
b[i_].Set(0,bv[i_]);
//--- Test CMatrixSolveM()
CUnset2D(x);
CDenseSolver::CMatrixSolveM(a,n,b,1,true,info,rep,x);
//--- check
if(info<=0)
rfserrors=true;
else
{
//--- allocation
ArrayResize(xv,n);
for(i_=0;i_<=n-1;i_++)
xv[i_]=x[i_][0];
for(i=0;i<=n-1;i++)
{
for(i_=0;i_<=n-1;i_++)
y[i_]=a[i][i_];
//--- function call
CXblas::XCDot(y,xv,n,tx,v,verr);
//--- search errors
rfserrors=rfserrors || CMath::AbsComplex(v-b[i][0])>8*CMath::m_machineepsilon*MathMax(1,CMath::AbsComplex(b[i][0]));
}
}
//--- Test CMatrixSolve()
CUnset1D(xv);
CDenseSolver::CMatrixSolve(a,n,bv,info,rep,xv);
//--- check
if(info<=0)
rfserrors=true;
else
{
for(i=0;i<=n-1;i++)
{
for(i_=0;i_<=n-1;i_++)
y[i_]=a[i][i_];
//--- function call
CXblas::XCDot(y,xv,n,tx,v,verr);
//--- search errors
rfserrors=rfserrors || CMath::AbsComplex(v-bv[i])>8*CMath::m_machineepsilon*MathMax(1,CMath::AbsComplex(bv[i]));
}
}
//--- TODO: Test LS-solver on the same matrix
}
}
//+------------------------------------------------------------------+
//| HPD test |
//+------------------------------------------------------------------+
static void CTestDenseSolverUnit::TestHPDSolver(const int maxn,const int maxm,
const int passcount,
const double threshold,
bool &hpderrors,bool &rfserrors)
{
//--- create variables
int i=0;
int j=0;
int k=0;
int n=0;
int m=0;
int pass=0;
int taskkind=0;
complex v=0;
bool isupper;
int info=0;
int i_=0;
//--- create arrays
int p[];
complex bv[];
complex xv[];
complex y[];
complex tx[];
//--- create matrix
CMatrixComplex a;
CMatrixComplex cha;
CMatrixComplex atmp;
CMatrixComplex xe;
CMatrixComplex b;
CMatrixComplex x;
//--- objects of classes
CDenseSolverReport rep;
CDenseSolverLSReport repls;
//--- General square matrices:
//--- * test general solvers
//--- * test least squares solver
for(pass=1;pass<=passcount;pass++)
{
for(n=1;n<=maxn;n++)
{
for(m=1;m<=maxm;m++)
{
//--- ********************************************************
//--- WELL CONDITIONED TASKS
//--- ability to find correct solution is tested
//--- ********************************************************
//--- 1. generate random well conditioned matrix A.
//--- 2. generate random solution vector xe
//--- 3. generate right part b=A*xe
//--- 4. test different methods on original A
isupper=CMath::RandomReal()>0.5;
CMatGen::HPDMatrixRndCond(n,1000,a);
CMatrixMakeACopy(a,n,n,cha);
//--- check
if(!CTrFac::HPDMatrixCholesky(cha,n,isupper))
{
hpderrors=true;
return;
}
//--- allocation
xe.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
{
xe[i].SetRe(j,2*CMath::RandomReal()-1);
xe[i].SetIm(j,2*CMath::RandomReal()-1);
}
}
//--- allocation
b.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*xe[i_][j];
b[i].Set(j,v);
}
}
//--- function calls
CMatrixDropHalf(a,n,isupper);
CMatrixDropHalf(cha,n,isupper);
//--- Test solvers
info=0;
//--- function calls
UnsetRep(rep);
CUnset2D(x);
CDenseSolver::HPDMatrixSolveM(a,n,isupper,b,m,info,rep,x);
//--- search errors
hpderrors=hpderrors || !CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
//--- change values
info=0;
//--- function calls
UnsetRep(rep);
CUnset1D(xv);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::HPDMatrixSolve(a,n,isupper,bv,info,rep,xv);
//--- search errors
hpderrors=hpderrors || !CMatrixCheckSolution(xe,n,threshold,info,rep,xv);
//--- change values
info=0;
//--- function calls
UnsetRep(rep);
CUnset2D(x);
CDenseSolver::HPDMatrixCholeskySolveM(cha,n,isupper,b,m,info,rep,x);
//--- search errors
hpderrors=hpderrors || !CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
//--- change values
info=0;
//--- function calls
UnsetRep(rep);
CUnset1D(xv);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::HPDMatrixCholeskySolve(cha,n,isupper,bv,info,rep,xv);
//--- search errors
hpderrors=hpderrors || !CMatrixCheckSolution(xe,n,threshold,info,rep,xv);
//--- ********************************************************
//--- EXACTLY SINGULAR MATRICES
//--- ability to detect singularity is tested
//--- ********************************************************
//--- 1. generate different types of singular matrices:
//--- * zero
//--- * with zero columns
//--- * with zero rows
//--- * with equal rows/columns
//--- 2. generate random solution vector xe
//--- 3. generate right part b=A*xe
//--- 4. test different methods
for(taskkind=0;taskkind<=3;taskkind++)
{
CUnset2D(a);
//--- check
if(taskkind==0)
{
//--- all zeros
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,0);
}
}
//--- check
if(taskkind==1)
{
//--- there is zero column
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=i;j<=n-1;j++)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
//--- check
if(i==j)
a[i].SetIm(j,0);
a[j].Set(i,a[i][j]);
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(k,a[i_][k]*0);
for(i_=0;i_<=n-1;i_++)
a[k].Set(i_,a[k][i_]*0);
}
//--- check
if(taskkind==2)
{
//--- there is zero row
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=i;j<=n-1;j++)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
//--- check
if(i==j)
a[i].SetIm(j,0);
a[j].Set(i,a[i][j]);
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0;i_<=n-1;i_++)
a[k].Set(i_,a[k][i_]*0);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(k,a[i_][k]*0);
}
//--- check
if(taskkind==3)
{
//--- equal columns/rows
if(n<2)
continue;
//--- allocation
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=i;j<=n-1;j++)
{
a[i].SetRe(j,2*CMath::RandomReal()-1);
a[i].SetIm(j,2*CMath::RandomReal()-1);
//--- check
if(i==j)
a[i].SetIm(j,0);
a[j].Set(i,a[i][j]);
}
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(0,a[i_][k]);
for(i_=0;i_<=n-1;i_++)
a[0].Set(i_,a[k][i_]);
}
//--- allocation
xe.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
xe[i].Set(j,2*CMath::RandomReal()-1);
}
//--- allocation
b.Resize(n,m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*xe[i_][j];
b[i].Set(j,v);
}
}
//--- function calls
CMatrixMakeACopy(a,n,n,cha);
CMatrixDropHalf(a,n,isupper);
CMatrixDropHalf(cha,n,isupper);
//--- Test SPDMatrixSolveM()
info=0;
//--- function calls
UnsetRep(rep);
CUnset2D(x);
CDenseSolver::HPDMatrixSolveM(a,n,isupper,b,m,info,rep,x);
//--- search errors
hpderrors=hpderrors || !CMatrixCheckSingularM(n,m,info,rep,x);
//--- Test SPDMatrixSolve()
info=0;
//--- function calls
UnsetRep(rep);
CUnset2D(x);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::HPDMatrixSolve(a,n,isupper,bv,info,rep,xv);
//--- search errors
hpderrors=hpderrors || !CMatrixCheckSingular(n,info,rep,xv);
//--- 'equal columns/rows' are degenerate,but
//--- Cholesky matrix with equal columns/rows IS NOT degenerate,
//--- so it is not used for testing purposes.
if(taskkind!=3)
{
//--- Test SPDMatrixLUSolveM()
info=0;
//--- function calls
UnsetRep(rep);
CUnset2D(x);
CDenseSolver::HPDMatrixCholeskySolveM(cha,n,isupper,b,m,info,rep,x);
//--- search errors
hpderrors=hpderrors || !CMatrixCheckSingularM(n,m,info,rep,x);
//--- Test SPDMatrixLUSolve()
info=0;
//--- function calls
UnsetRep(rep);
CUnset2D(x);
//--- allocation
ArrayResize(bv,n);
//--- change values
for(i_=0;i_<=n-1;i_++)
bv[i_]=b[i_][0];
//--- function call
CDenseSolver::HPDMatrixCholeskySolve(cha,n,isupper,bv,info,rep,xv);
//--- search errors
hpderrors=hpderrors || !CMatrixCheckSingular(n,info,rep,xv);
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Unsets real matrix |
//+------------------------------------------------------------------+
static void CTestDenseSolverUnit::Unset2D(CMatrixDouble &x)
{
//--- allocation
x.Resize(1,1);
//--- change value
x[0].Set(0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets real vector |
//+------------------------------------------------------------------+
static void CTestDenseSolverUnit::Unset1D(double &x[])
{
//--- allocation
ArrayResize(x,1);
//--- change value
x[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Unsets real matrix |
//+------------------------------------------------------------------+
static void CTestDenseSolverUnit::CUnset2D(CMatrixComplex &x)
{
//--- allocation
x.Resize(1,1);
//--- change value
x[0].Set(0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets real vector |
//+------------------------------------------------------------------+
static void CTestDenseSolverUnit::CUnset1D(complex &x[])
{
//--- allocation
ArrayResize(x,1);
//--- change value
x[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Unsets report |
//+------------------------------------------------------------------+
static void CTestDenseSolverUnit::UnsetRep(CDenseSolverReport &r)
{
//--- change values
r.m_r1=-1;
r.m_rinf=-1;
}
//+------------------------------------------------------------------+
//| Unsets report |
//+------------------------------------------------------------------+
static void CTestDenseSolverUnit::UnsetLSRep(CDenseSolverLSReport &r)
{
//--- change values
r.m_r2=-1;
r.m_n=-1;
r.m_k=-1;
//--- function call
Unset2D(r.m_cx);
}
//+------------------------------------------------------------------+
//| Testing class CLinMin |
//+------------------------------------------------------------------+
class CTestLinMinUnit
{
public:
static bool TestLinMin(const bool silent)
{
//--- create variables
bool waserrors;
//--- initialization
waserrors=false;
//--- check
if(!silent)
{
Print("TESTING LINMIN");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
};
//+------------------------------------------------------------------+
//| Testing class CMinCG |
//+------------------------------------------------------------------+
class CTestMinCGUnit
{
private:
//--- private methods
static void TestFunc1(CMinCGState &state);
static void TestFunc2(CMinCGState &state);
static void TestFunc3(CMinCGState &state);
static void CalcIIP2(CMinCGState &state,const int n);
static void CalcLowRank(CMinCGState &state,const int n,const int vcnt,double &d[],CMatrixDouble &v,double &vd[],double &x0[]);
static void TestPreconditioning(bool &err);
public:
//--- constructor, destructor
CTestMinCGUnit(void);
~CTestMinCGUnit(void);
//--- public methods
static bool TestMinCG(const bool silent);
static void TestOther(bool &err);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestMinCGUnit::CTestMinCGUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestMinCGUnit::~CTestMinCGUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CMinCG |
//+------------------------------------------------------------------+
static bool CTestMinCGUnit::TestMinCG(const bool silent)
{
//--- create variables
bool waserrors;
bool referror;
bool eqerror;
bool linerror1;
bool linerror2;
bool restartserror;
bool precerror;
bool converror;
bool othererrors;
int n=0;
int i=0;
int j=0;
double v=0;
int cgtype=0;
int difftype=0;
double diffstep=0;
int i_=0;
//--- create arrays
double x[];
double xe[];
double b[];
double xlast[];
double diagh[];
//--- create matrix
CMatrixDouble a;
//--- objects of classes
CMinCGState state;
CMinCGReport rep;
//--- initialization
waserrors=false;
referror=false;
linerror1=false;
linerror2=false;
eqerror=false;
converror=false;
restartserror=false;
othererrors=false;
precerror=false;
//--- function calls
TestPreconditioning(precerror);
TestOther(othererrors);
//--- calculation
for(difftype=0;difftype<=1;difftype++)
{
for(cgtype=-1;cgtype<=1;cgtype++)
{
//--- Reference problem
ArrayResize(x,3);
//--- change values
n=3;
diffstep=1.0E-6;
x[0]=100*CMath::RandomReal()-50;
x[1]=100*CMath::RandomReal()-50;
x[2]=100*CMath::RandomReal()-50;
//--- check
if(difftype==0)
CMinCG::MinCGCreate(n,x,state);
//--- check
if(difftype==1)
CMinCG::MinCGCreateF(n,x,diffstep,state);
//--- function call
CMinCG::MinCGSetCGType(state,cgtype);
//--- cycle
while(CMinCG::MinCGIteration(state))
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=CMath::Sqr(state.m_x[0]-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
//--- check
if(state.m_needfg)
{
state.m_g[0]=2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2]);
state.m_g[1]=2*state.m_x[1];
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
}
}
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- search errors
referror=(((referror || rep.m_terminationtype<=0) || MathAbs(x[0]-2)>0.001) || MathAbs(x[1])>0.001) || MathAbs(x[2]-2)>0.001;
//--- F2 problem with restarts:
//--- * make several iterations and restart BEFORE termination
//--- * iterate and restart AFTER termination
//--- NOTE: step is bounded from above to avoid premature convergence
ArrayResize(x,3);
//--- change values
n=3;
diffstep=1.0E-6;
x[0]=10+10*CMath::RandomReal();
x[1]=10+10*CMath::RandomReal();
x[2]=10+10*CMath::RandomReal();
//--- check
if(difftype==0)
CMinCG::MinCGCreate(n,x,state);
//--- check
if(difftype==1)
CMinCG::MinCGCreateF(n,x,diffstep,state);
//--- function calls
CMinCG::MinCGSetCGType(state,cgtype);
CMinCG::MinCGSetStpMax(state,0.1);
CMinCG::MinCGSetCond(state,0.0000001,0.0,0.0,0);
//--- calculation
for(i=0;i<=10;i++)
{
//--- check
if(!CMinCG::MinCGIteration(state))
break;
//--- function call
TestFunc2(state);
}
//--- change values
x[0]=10+10*CMath::RandomReal();
x[1]=10+10*CMath::RandomReal();
x[2]=10+10*CMath::RandomReal();
//--- function call
CMinCG::MinCGRestartFrom(state,x);
//--- cycle
while(CMinCG::MinCGIteration(state))
TestFunc2(state);
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- search errors
restartserror=(((restartserror || rep.m_terminationtype<=0) || MathAbs(x[0]-MathLog(2))>0.01) || MathAbs(x[1])>0.01) || MathAbs(x[2]-MathLog(2))>0.01;
//--- change values
x[0]=10+10*CMath::RandomReal();
x[1]=10+10*CMath::RandomReal();
x[2]=10+10*CMath::RandomReal();
//--- function call
CMinCG::MinCGRestartFrom(state,x);
//--- cycle
while(CMinCG::MinCGIteration(state))
TestFunc2(state);
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- search errors
restartserror=(((restartserror || rep.m_terminationtype<=0) || MathAbs(x[0]-MathLog(2))>0.01) || MathAbs(x[1])>0.01) || MathAbs(x[2]-MathLog(2))>0.01;
//--- 1D problem #1
ArrayResize(x,1);
//--- change values
n=1;
diffstep=1.0E-6;
x[0]=100*CMath::RandomReal()-50;
//--- check
if(difftype==0)
CMinCG::MinCGCreate(n,x,state);
//--- check
if(difftype==1)
CMinCG::MinCGCreateF(n,x,diffstep,state);
//--- function call
CMinCG::MinCGSetCGType(state,cgtype);
//--- cycle
while(CMinCG::MinCGIteration(state))
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=-MathCos(state.m_x[0]);
//--- check
if(state.m_needfg)
state.m_g[0]=MathSin(state.m_x[0]);
}
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- search errors
linerror1=(linerror1 || rep.m_terminationtype<=0) || MathAbs(x[0]/M_PI-(int)MathRound(x[0]/M_PI))>0.001;
//--- 1D problem #2
ArrayResize(x,1);
//--- change values
n=1;
diffstep=1.0E-6;
x[0]=100*CMath::RandomReal()-50;
//--- check
if(difftype==0)
CMinCG::MinCGCreate(n,x,state);
//--- check
if(difftype==1)
CMinCG::MinCGCreateF(n,x,diffstep,state);
//--- function call
CMinCG::MinCGSetCGType(state,cgtype);
//--- cycle
while(CMinCG::MinCGIteration(state))
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=CMath::Sqr(state.m_x[0])/(1+CMath::Sqr(state.m_x[0]));
//--- check
if(state.m_needfg)
state.m_g[0]=(2*state.m_x[0]*(1+CMath::Sqr(state.m_x[0]))-CMath::Sqr(state.m_x[0])*2*state.m_x[0])/CMath::Sqr(1+CMath::Sqr(state.m_x[0]));
}
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- search errors
linerror2=(linerror2 || rep.m_terminationtype<=0) || MathAbs(x[0])>0.001;
//--- Linear equations
diffstep=1.0E-6;
for(n=1;n<=10;n++)
{
//--- Prepare task
a.Resize(n,n);
ArrayResize(x,n);
ArrayResize(xe,n);
ArrayResize(b,n);
for(i=0;i<=n-1;i++)
xe[i]=2*CMath::RandomReal()-1;
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
a[i].Set(i,a[i][i]+3*MathSign(a[i][i]));
}
for(i=0;i<=n-1;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*xe[i_];
b[i]=v;
}
//--- Solve task
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- check
if(difftype==0)
CMinCG::MinCGCreate(n,x,state);
//--- check
if(difftype==1)
CMinCG::MinCGCreateF(n,x,diffstep,state);
//--- function call
CMinCG::MinCGSetCGType(state,cgtype);
//--- cycle
while(CMinCG::MinCGIteration(state))
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=0;
//--- check
if(state.m_needfg)
{
for(i=0;i<=n-1;i++)
state.m_g[i]=0;
}
for(i=0;i<=n-1;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*state.m_x[i_];
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=state.m_f+CMath::Sqr(v-b[i]);
//--- check
if(state.m_needfg)
{
for(j=0;j<=n-1;j++)
state.m_g[j]=state.m_g[j]+2*(v-b[i])*a[i][j];
}
}
}
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- search errors
eqerror=eqerror || rep.m_terminationtype<=0;
for(i=0;i<=n-1;i++)
eqerror=eqerror || MathAbs(x[i]-xe[i])>0.001;
}
//--- Testing convergence properties
diffstep=1.0E-6;
n=3;
//--- allocation
ArrayResize(x,n);
for(i=0;i<=n-1;i++)
x[i]=6*CMath::RandomReal()-3;
//--- check
if(difftype==0)
CMinCG::MinCGCreate(n,x,state);
//--- check
if(difftype==1)
CMinCG::MinCGCreateF(n,x,diffstep,state);
//--- function calls
CMinCG::MinCGSetCond(state,0.001,0.0,0.0,0);
CMinCG::MinCGSetCGType(state,cgtype);
//--- cycle
while(CMinCG::MinCGIteration(state))
TestFunc3(state);
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- search errors
converror=converror || rep.m_terminationtype!=4;
for(i=0;i<=n-1;i++)
x[i]=6*CMath::RandomReal()-3;
//--- check
if(difftype==0)
CMinCG::MinCGCreate(n,x,state);
//--- check
if(difftype==1)
CMinCG::MinCGCreateF(n,x,diffstep,state);
//--- function calls
CMinCG::MinCGSetCond(state,0.0,0.001,0.0,0);
CMinCG::MinCGSetCGType(state,cgtype);
//--- cycle
while(CMinCG::MinCGIteration(state))
TestFunc3(state);
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- search errors
converror=converror || rep.m_terminationtype!=1;
//--- change values
for(i=0;i<=n-1;i++)
x[i]=6*CMath::RandomReal()-3;
//--- check
if(difftype==0)
CMinCG::MinCGCreate(n,x,state);
//--- check
if(difftype==1)
CMinCG::MinCGCreateF(n,x,diffstep,state);
//--- function calls
CMinCG::MinCGSetCond(state,0.0,0.0,0.001,0);
CMinCG::MinCGSetCGType(state,cgtype);
//--- cycle
while(CMinCG::MinCGIteration(state))
TestFunc3(state);
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- search errors
converror=converror || rep.m_terminationtype!=2;
//--- change values
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- check
if(difftype==0)
CMinCG::MinCGCreate(n,x,state);
//--- check
if(difftype==1)
CMinCG::MinCGCreateF(n,x,diffstep,state);
//--- function calls
CMinCG::MinCGSetCond(state,0.0,0.0,0.0,10);
CMinCG::MinCGSetCGType(state,cgtype);
//--- cycle
while(CMinCG::MinCGIteration(state))
TestFunc3(state);
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- search errors
converror=converror || !((rep.m_terminationtype==5 && rep.m_iterationscount==10) || rep.m_terminationtype==7);
}
}
//--- end
waserrors=((((((referror || eqerror) || linerror1) || linerror2) || converror) || othererrors) || restartserror) || precerror;
//--- check
if(!silent)
{
Print("TESTING CG OPTIMIZATION");
Print("REFERENCE PROBLEM: ");
//--- check
if(referror)
Print("FAILED");
else
Print("OK");
Print("LIN-1 PROBLEM: ");
//--- check
if(linerror1)
Print("FAILED");
else
Print("OK");
Print("LIN-2 PROBLEM: ");
//--- check
if(linerror2)
Print("FAILED");
else
Print("OK");
Print("LINEAR EQUATIONS: ");
//--- check
if(eqerror)
Print("FAILED");
else
Print("OK");
Print("RESTARTS: ");
//--- check
if(restartserror)
Print("FAILED");
else
Print("OK");
Print("PRECONDITIONING: ");
//--- check
if(precerror)
Print("FAILED");
else
Print("OK");
Print("CONVERGENCE PROPERTIES: ");
//--- check
if(converror)
Print("FAILED");
else
Print("OK");
Print("OTHER PROPERTIES: ");
//--- check
if(othererrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Other properties |
//+------------------------------------------------------------------+
static void CTestMinCGUnit::TestOther(bool &err)
{
//--- create variables
int n=0;
double fprev=0;
double xprev=0;
double stpmax=0;
int i=0;
int cgtype=0;
double tmpeps=0;
double epsg=0;
double v=0;
double r=0;
bool hasxlast;
double lastscaledstep=0;
int pkind=0;
int ckind=0;
int mkind=0;
int dkind=0;
double diffstep=0;
double vc=0;
double vm=0;
bool wasf;
bool wasfg;
int i_=0;
//--- create arrays
double x[];
double s[];
double a[];
double h[];
double xlast[];
//--- objects of classes
CMinCGState state;
CMinCGReport rep;
//--- calculation
for(cgtype=-1;cgtype<=1;cgtype++)
{
//--- Test reports (F should form monotone sequence)
n=50;
ArrayResize(x,n);
ArrayResize(xlast,n);
//--- change values
for(i=0;i<=n-1;i++)
x[i]=1;
//--- function calls
CMinCG::MinCGCreate(n,x,state);
CMinCG::MinCGSetCond(state,0,0,0,100);
CMinCG::MinCGSetXRep(state,true);
fprev=CMath::m_maxrealnumber;
//--- cycle
while(CMinCG::MinCGIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=0;
for(i=0;i<=n-1;i++)
{
state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]);
state.m_g[i]=2*(1+i)*state.m_x[i];
}
}
//--- check
if(state.m_xupdated)
{
err=err || state.m_f>fprev;
//--- check
if(fprev==CMath::m_maxrealnumber)
{
for(i=0;i<=n-1;i++)
err=err || state.m_x[i]!=x[i];
}
//--- change values
fprev=state.m_f;
for(i_=0;i_<=n-1;i_++)
xlast[i_]=state.m_x[i_];
}
}
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- search errors
for(i=0;i<=n-1;i++)
err=err || x[i]!=xlast[i];
//--- Test differentiation vs. analytic gradient
//--- (first one issues NeedF requests,second one issues NeedFG requests)
n=50;
diffstep=1.0E-6;
for(dkind=0;dkind<=1;dkind++)
{
//--- allocation
ArrayResize(x,n);
ArrayResize(xlast,n);
for(i=0;i<=n-1;i++)
x[i]=1;
//--- check
if(dkind==0)
CMinCG::MinCGCreate(n,x,state);
//--- check
if(dkind==1)
CMinCG::MinCGCreateF(n,x,diffstep,state);
//--- function call
CMinCG::MinCGSetCond(state,0,0,0,n/2);
//--- change values
wasf=false;
wasfg=false;
//--- cycle
while(CMinCG::MinCGIteration(state))
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=0;
for(i=0;i<=n-1;i++)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]);
//--- check
if(state.m_needfg)
state.m_g[i]=2*(1+i)*state.m_x[i];
}
//--- search errors
wasf=wasf || state.m_needf;
wasfg=wasfg || state.m_needfg;
}
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- check
if(dkind==0)
err=(err || wasf) || !wasfg;
//--- check
if(dkind==1)
err=(err || !wasf) || wasfg;
}
//--- Test that numerical differentiation uses scaling.
//--- In order to test that we solve simple optimization
//--- problem: min(x^2) with initial x equal to 0.0.
//--- We choose random DiffStep and S,then we check that
//--- optimizer evaluates function at +-DiffStep*S only.
ArrayResize(x,1);
ArrayResize(s,1);
diffstep=CMath::RandomReal()*1.0E-6;
s[0]=MathExp(CMath::RandomReal()*4-2);
x[0]=0;
//--- function calls
CMinCG::MinCGCreateF(1,x,diffstep,state);
CMinCG::MinCGSetCond(state,1.0E-6,0,0,0);
CMinCG::MinCGSetScale(state,s);
v=0;
//--- cycle
while(CMinCG::MinCGIteration(state))
{
state.m_f=CMath::Sqr(state.m_x[0]);
v=MathMax(v,MathAbs(state.m_x[0]));
}
//--- function call
CMinCG::MinCGResults(state,x,rep);
r=v/(s[0]*diffstep);
//--- search errors
err=err || MathAbs(MathLog(r))>MathLog(1+1000*CMath::m_machineepsilon);
//--- Test maximum step
n=1;
ArrayResize(x,n);
x[0]=100;
stpmax=0.05+0.05*CMath::RandomReal();
//--- function calls
CMinCG::MinCGCreate(n,x,state);
CMinCG::MinCGSetCond(state,1.0E-9,0,0,0);
CMinCG::MinCGSetStpMax(state,stpmax);
CMinCG::MinCGSetXRep(state,true);
xprev=x[0];
//--- cycle
while(CMinCG::MinCGIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]);
state.m_g[0]=MathExp(state.m_x[0])-MathExp(-state.m_x[0]);
//--- search errors
err=err || MathAbs(state.m_x[0]-xprev)>(1+MathSqrt(CMath::m_machineepsilon))*stpmax;
}
//--- check
if(state.m_xupdated)
{
//--- search errors
err=err || MathAbs(state.m_x[0]-xprev)>(1+MathSqrt(CMath::m_machineepsilon))*stpmax;
xprev=state.m_x[0];
}
}
//--- Test correctness of the scaling:
//--- * initial point is random point from [+1,+2]^N
//--- * f(x)=SUM(A[i]*x[i]^4),C[i] is random from [0.01,100]
//--- * we use random scaling matrix
//--- * we test different variants of the preconditioning:
//--- 0) unit preconditioner
//--- 1) random diagonal from [0.01,100]
//--- 2) scale preconditioner
//--- * we set stringent stopping conditions (we try EpsG and EpsX)
//--- * and we test that in the extremum stopping conditions are
//--- satisfied subject to the current scaling coefficients.
tmpeps=1.0E-10;
for(n=1;n<=10;n++)
{
for(pkind=0;pkind<=2;pkind++)
{
//--- allocation
ArrayResize(x,n);
ArrayResize(xlast,n);
ArrayResize(a,n);
ArrayResize(s,n);
ArrayResize(h,n);
//--- change values
for(i=0;i<=n-1;i++)
{
x[i]=CMath::RandomReal()+1;
a[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
s[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
h[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
}
//--- function calls
CMinCG::MinCGCreate(n,x,state);
CMinCG::MinCGSetScale(state,s);
CMinCG::MinCGSetXRep(state,true);
//--- check
if(pkind==1)
CMinCG::MinCGSetPrecDiag(state,h);
//--- check
if(pkind==2)
CMinCG::MinCGSetPrecScale(state);
//--- Test gradient-based stopping condition
for(i=0;i<=n-1;i++)
x[i]=CMath::RandomReal()+1;
//--- function calls
CMinCG::MinCGSetCond(state,tmpeps,0,0,0);
CMinCG::MinCGRestartFrom(state,x);
//--- cycle
while(CMinCG::MinCGIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=0;
for(i=0;i<=n-1;i++)
{
state.m_f=state.m_f+a[i]*MathPow(state.m_x[i],4);
state.m_g[i]=4*a[i]*MathPow(state.m_x[i],3);
}
}
}
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- check
if(rep.m_terminationtype<=0)
{
err=true;
return;
}
//--- change value
v=0;
for(i=0;i<=n-1;i++)
v=v+CMath::Sqr(s[i]*4*a[i]*MathPow(x[i],3));
v=MathSqrt(v);
//--- search errors
err=err || v>tmpeps;
//--- Test step-based stopping condition
for(i=0;i<=n-1;i++)
x[i]=CMath::RandomReal()+1;
hasxlast=false;
//--- function calls
CMinCG::MinCGSetCond(state,0,0,tmpeps,0);
CMinCG::MinCGRestartFrom(state,x);
//--- cycle
while(CMinCG::MinCGIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=0;
for(i=0;i<=n-1;i++)
{
state.m_f=state.m_f+a[i]*MathPow(state.m_x[i],4);
state.m_g[i]=4*a[i]*MathPow(state.m_x[i],3);
}
}
//--- check
if(state.m_xupdated)
{
//--- check
if(hasxlast)
{
lastscaledstep=0;
for(i=0;i<=n-1;i++)
lastscaledstep=lastscaledstep+CMath::Sqr(state.m_x[i]-xlast[i])/CMath::Sqr(s[i]);
lastscaledstep=MathSqrt(lastscaledstep);
}
else
lastscaledstep=0;
for(i_=0;i_<=n-1;i_++)
xlast[i_]=state.m_x[i_];
hasxlast=true;
}
}
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- check
if(rep.m_terminationtype<=0)
{
err=true;
return;
}
//--- search errors
err=err || lastscaledstep>tmpeps;
}
}
//--- Check correctness of the "trimming".
//--- Trimming is a technique which is used to help algorithm
//--- cope with unbounded functions. In order to check this
//--- technique we will try to solve following optimization
//--- problem:
//--- min f(x) subject to no constraints on X
//--- { 1/(1-x) + 1/(1+x) + c*x,if -0.999999<x<0.999999
//--- f(x)={
//--- { M,if x<=-0.999999 or x>=0.999999
//--- where c is either 1.0 or 1.0E+6,M is either 1.0E8,1.0E20 or +INF
//--- (we try different combinations)
for(ckind=0;ckind<=1;ckind++)
{
for(mkind=0;mkind<=2;mkind++)
{
//--- Choose c and M
if(ckind==0)
vc=1.0;
//--- check
if(ckind==1)
vc=1.0E+6;
//--- check
if(mkind==0)
vm=1.0E+8;
//--- check
if(mkind==1)
vm=1.0E+20;
//--- check
if(mkind==2)
vm=CInfOrNaN::PositiveInfinity();
//--- Create optimizer,solve optimization problem
epsg=1.0E-6*vc;
ArrayResize(x,1);
x[0]=0.0;
//--- function calls
CMinCG::MinCGCreate(1,x,state);
CMinCG::MinCGSetCond(state,epsg,0,0,0);
CMinCG::MinCGSetCGType(state,cgtype);
//--- cycle
while(CMinCG::MinCGIteration(state))
{
//--- check
if(state.m_needfg)
{
//--- check
if(-0.999999<state.m_x[0] && state.m_x[0]<0.999999)
{
state.m_f=1/(1-state.m_x[0])+1/(1+state.m_x[0])+vc*state.m_x[0];
state.m_g[0]=1/CMath::Sqr(1-state.m_x[0])-1/CMath::Sqr(1+state.m_x[0])+vc;
}
else
state.m_f=vm;
}
}
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- check
if(rep.m_terminationtype<=0)
{
err=true;
return;
}
//--- search errors
err=err || MathAbs(1/CMath::Sqr(1-x[0])-1/CMath::Sqr(1+x[0])+vc)>epsg;
}
}
}
}
//+------------------------------------------------------------------+
//| Calculate test function #1 |
//+------------------------------------------------------------------+
static void CTestMinCGUnit::TestFunc1(CMinCGState &state)
{
//--- check
if(state.m_x[0]<100.0)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
//--- check
if(state.m_needfg)
{
state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
state.m_g[1]=2*state.m_x[1];
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
}
}
else
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=MathSqrt(CMath::m_maxrealnumber);
//--- check
if(state.m_needfg)
{
state.m_g[0]=MathSqrt(CMath::m_maxrealnumber);
state.m_g[1]=0;
state.m_g[2]=0;
}
}
}
//+------------------------------------------------------------------+
//| Calculate test function #2 |
//| Simple variation of #1,much more nonlinear,which makes unlikely |
//| premature convergence of algorithm . |
//+------------------------------------------------------------------+
static void CTestMinCGUnit::TestFunc2(CMinCGState &state)
{
//--- check
if(state.m_x[0]<100.0)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1]))+CMath::Sqr(state.m_x[2]-state.m_x[0]);
//--- check
if(state.m_needfg)
{
state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
state.m_g[1]=4*state.m_x[1]*CMath::Sqr(state.m_x[1]);
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
}
}
else
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=MathSqrt(CMath::m_maxrealnumber);
//--- check
if(state.m_needfg)
{
state.m_g[0]=MathSqrt(CMath::m_maxrealnumber);
state.m_g[1]=0;
state.m_g[2]=0;
}
}
}
//+------------------------------------------------------------------+
//| Calculate test function #3 |
//| Simple variation of #1,much more nonlinear,with non-zero value at|
//| minimum. |
//| It achieve two goals: |
//| * makes unlikely premature convergence of algorithm. |
//| * solves some issues with EpsF stopping condition which arise |
//| when F(minimum) is zero |
//+------------------------------------------------------------------+
static void CTestMinCGUnit::TestFunc3(CMinCGState &state)
{
//--- create a variable
double s=0;
//--- initialization
s=0.001;
//--- check
if(state.m_x[0]<100.0)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1])+s)+CMath::Sqr(state.m_x[2]-state.m_x[0]);
//--- check
if(state.m_needfg)
{
state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
state.m_g[1]=2*(CMath::Sqr(state.m_x[1])+s)*2*state.m_x[1];
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
}
}
else
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=MathSqrt(CMath::m_maxrealnumber);
//--- check
if(state.m_needfg)
{
state.m_g[0]=MathSqrt(CMath::m_maxrealnumber);
state.m_g[1]=0;
state.m_g[2]=0;
}
}
}
//+------------------------------------------------------------------+
//| Calculate test function IIP2 |
//| f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1) |
//| It has high condition number which makes fast convergence |
//| unlikely without good preconditioner. |
//+------------------------------------------------------------------+
static void CTestMinCGUnit::CalcIIP2(CMinCGState &state,const int n)
{
//--- create a variable
int i=0;
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=state.m_f+CMath::Sqr(i*i+1)*CMath::Sqr(state.m_x[i]);
//--- check
if(state.m_needfg)
state.m_g[i]=CMath::Sqr(i*i+1)*2*state.m_x[i];
}
}
//+------------------------------------------------------------------+
//| Calculate test function f(x)=0.5*(x-x0)'*A*(x-x0),A=D+V'*Vd*V |
//+------------------------------------------------------------------+
static void CTestMinCGUnit::CalcLowRank(CMinCGState &state,const int n,
const int vcnt,double &d[],
CMatrixDouble &v,double &vd[],
double &x0[])
{
//--- create variables
int i=0;
int j=0;
double dx=0;
double t=0;
double t2=0;
int i_=0;
//--- change values
state.m_f=0;
for(i=0;i<=n-1;i++)
state.m_g[i]=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
dx=state.m_x[i]-x0[i];
state.m_f=state.m_f+0.5*dx*d[i]*dx;
state.m_g[i]=state.m_g[i]+d[i]*dx;
}
//--- calculation
for(i=0;i<=vcnt-1;i++)
{
t=0;
for(j=0;j<=n-1;j++)
t=t+v[i][j]*(state.m_x[j]-x0[j]);
//--- change values
state.m_f=state.m_f+0.5*t*vd[i]*t;
t2=t*vd[i];
for(i_=0;i_<=n-1;i_++)
state.m_g[i_]=state.m_g[i_]+t2*v[i][i_];
}
}
//+------------------------------------------------------------------+
//| This function tests preconditioning |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
static void CTestMinCGUnit::TestPreconditioning(bool &err)
{
//--- create variables
int pass=0;
int n=0;
int i=0;
int j=0;
int k=0;
int vs=0;
int cntb1=0;
int cntg1=0;
int cntb2=0;
int cntg2=0;
double epsg=0;
int cgtype=0;
//--- create arrays
double x[];
double x0[];
double vd[];
double d[];
double s[];
double diagh[];
//--- create matrix
CMatrixDouble v;
//--- objects of classes
CMinCGState state;
CMinCGReport rep;
//--- initialization
k=50;
epsg=1.0E-10;
//--- calculation
for(cgtype=-1;cgtype<=1;cgtype++)
{
//--- Preconditioner test 1.
//--- If
//--- * B1 is default preconditioner
//--- * G1 is diagonal precomditioner based on approximate diagonal of Hessian matrix
//--- then "bad" preconditioner is worse than "good" one.
//--- "Worse" means more iterations to converge.
//--- We test it using f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1).
//--- N - problem size
//--- K - number of repeated passes (should be large enough to average out random factors)
for(n=10;n<=15;n++)
{
//--- allocation
ArrayResize(x,n);
for(i=0;i<=n-1;i++)
x[i]=0;
//--- function calls
CMinCG::MinCGCreate(n,x,state);
CMinCG::MinCGSetCGType(state,cgtype);
//--- Test it with default preconditioner
CMinCG::MinCGSetPrecDefault(state);
//--- change values
cntb1=0;
for(pass=0;pass<=k-1;pass++)
{
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinCG::MinCGRestartFrom(state,x);
//--- cycle
while(CMinCG::MinCGIteration(state))
CalcIIP2(state,n);
//--- function call
CMinCG::MinCGResults(state,x,rep);
cntb1=cntb1+rep.m_iterationscount;
//--- search errors
err=err || rep.m_terminationtype<=0;
}
//--- Test it with perturbed diagonal preconditioner
ArrayResize(diagh,n);
for(i=0;i<=n-1;i++)
diagh[i]=2*CMath::Sqr(i*i+1)*(0.8+0.4*CMath::RandomReal());
//--- function call
CMinCG::MinCGSetPrecDiag(state,diagh);
//--- change values
cntg1=0;
for(pass=0;pass<=k-1;pass++)
{
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinCG::MinCGRestartFrom(state,x);
//--- cycle
while(CMinCG::MinCGIteration(state))
CalcIIP2(state,n);
//--- function call
CMinCG::MinCGResults(state,x,rep);
cntg1=cntg1+rep.m_iterationscount;
//--- search errors
err=err || rep.m_terminationtype<=0;
}
//--- Compare
err=err || cntb1<cntg1;
}
//--- Preconditioner test 2.
//--- If
//--- * B1 is default preconditioner
//--- * G1 is low rank exact preconditioner
//--- then "bad" preconditioner is worse than "good" one.
//--- "Worse" means more iterations to converge.
//--- Target function is f(x)=0.5*(x-x0)'*A*(x-x0),A=D+V'*Vd*V
//--- N - problem size
//--- K - number of repeated passes (should be large enough to average out random factors)
for(n=10;n<=15;n++)
{
for(vs=0;vs<=5;vs++)
{
//--- allocation
ArrayResize(x,n);
ArrayResize(x0,n);
ArrayResize(d,n);
//--- change values
for(i=0;i<=n-1;i++)
{
x[i]=0;
x0[i]=2*CMath::RandomReal()-1;
d[i]=MathExp(2*CMath::RandomReal());
}
//--- check
if(vs>0)
{
//--- allocation
v.Resize(vs,n);
ArrayResize(vd,vs);
//--- change values
for(i=0;i<=vs-1;i++)
{
for(j=0;j<=n-1;j++)
v[i].Set(j,2*CMath::RandomReal()-1);
vd[i]=MathExp(2*CMath::RandomReal());
}
}
//--- function calls
CMinCG::MinCGCreate(n,x,state);
CMinCG::MinCGSetCGType(state,cgtype);
//--- Test it with default preconditioner
CMinCG::MinCGSetPrecDefault(state);
//--- change values
cntb1=0;
for(pass=0;pass<=k-1;pass++)
{
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinCG::MinCGRestartFrom(state,x);
//--- cycle
while(CMinCG::MinCGIteration(state))
CalcLowRank(state,n,vs,d,v,vd,x0);
//--- function call
CMinCG::MinCGResults(state,x,rep);
cntb1=cntb1+rep.m_iterationscount;
//--- search errors
err=err || rep.m_terminationtype<=0;
}
//--- Test it with low rank preconditioner
CMinCG::MinCGSetPrecLowRankFast(state,d,vd,v,vs);
//--- change values
cntg1=0;
for(pass=0;pass<=k-1;pass++)
{
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinCG::MinCGRestartFrom(state,x);
//--- cycle
while(CMinCG::MinCGIteration(state))
CalcLowRank(state,n,vs,d,v,vd,x0);
//--- function call
CMinCG::MinCGResults(state,x,rep);
cntg1=cntg1+rep.m_iterationscount;
//--- search errors
err=err || rep.m_terminationtype<=0;
}
//--- Compare
err=err || cntb1<cntg1;
}
}
//--- Preconditioner test 3.
//--- If
//--- * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
//--- * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
//--- then B2 is worse than G2.
//--- "Worse" means more iterations to converge.
for(n=10;n<=15;n++)
{
//--- allocation
ArrayResize(x,n);
for(i=0;i<=n-1;i++)
x[i]=0;
//--- function calls
CMinCG::MinCGCreate(n,x,state);
ArrayResize(s,n);
for(i=0;i<=n-1;i++)
s[i]=1/MathSqrt(2*MathPow(i*i+1,2)*(0.8+0.4*CMath::RandomReal()));
//--- function calls
CMinCG::MinCGSetPrecDefault(state);
CMinCG::MinCGSetScale(state,s);
//--- change values
cntb2=0;
for(pass=0;pass<=k-1;pass++)
{
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinCG::MinCGRestartFrom(state,x);
//--- cycle
while(CMinCG::MinCGIteration(state))
CalcIIP2(state,n);
//--- function call
CMinCG::MinCGResults(state,x,rep);
cntb2=cntb2+rep.m_iterationscount;
//--- search errors
err=err || rep.m_terminationtype<=0;
}
//--- function calls
CMinCG::MinCGSetPrecScale(state);
CMinCG::MinCGSetScale(state,s);
//--- change values
cntg2=0;
for(pass=0;pass<=k-1;pass++)
{
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinCG::MinCGRestartFrom(state,x);
//--- cycle
while(CMinCG::MinCGIteration(state))
CalcIIP2(state,n);
//--- function call
CMinCG::MinCGResults(state,x,rep);
cntg2=cntg2+rep.m_iterationscount;
//--- search errors
err=err || rep.m_terminationtype<=0;
}
//--- search errors
err=err || cntb2<cntg2;
}
}
}
//+------------------------------------------------------------------+
//| Testing class CMinBLEIC |
//+------------------------------------------------------------------+
class CTestMinBLEICUnit
{
private:
//--- private method
static void CheckBounds(double &x[],double &bndl[],double &bndu[],const int n,bool &err);
static void CalcIIP2(CMinBLEICState &state,const int n,const int fk);
static void TestFeasibility(bool &feaserr,bool &converr,bool &interr);
static void TestOther(bool &err);
static void TestConv(bool &err);
static void TestPreconditioning(bool &err);
static void SetRandomPreconditioner(CMinBLEICState &state,const int n,const int preckind);
public:
//--- constructor, destructor
CTestMinBLEICUnit(void);
~CTestMinBLEICUnit(void);
//--- public method
static bool TestMinBLEIC(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestMinBLEICUnit::CTestMinBLEICUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestMinBLEICUnit::~CTestMinBLEICUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CMinBLEIC |
//+------------------------------------------------------------------+
static bool CTestMinBLEICUnit::TestMinBLEIC(const bool silent)
{
//--- create variables
bool waserrors;
bool feasibilityerrors;
bool othererrors;
bool precerrors;
bool interrors;
bool converrors;
//--- initialization
waserrors=false;
feasibilityerrors=false;
othererrors=false;
precerrors=false;
interrors=false;
converrors=false;
//--- function calls
TestFeasibility(feasibilityerrors,converrors,interrors);
TestOther(othererrors);
TestConv(converrors);
TestPreconditioning(precerrors);
//--- end
waserrors=(((feasibilityerrors || othererrors) || converrors) || interrors) || precerrors;
//--- check
if(!silent)
{
Print("TESTING BLEIC OPTIMIZATION");
Print("FEASIBILITY PROPERTIES: ");
//--- check
if(feasibilityerrors)
Print("FAILED");
else
Print("OK");
Print("PRECONDITIONING: ");
//--- check
if(precerrors)
Print("FAILED");
else
Print("OK");
Print("OTHER PROPERTIES: ");
//--- check
if(othererrors)
Print("FAILED");
else
Print("OK");
Print("CONVERGENCE PROPERTIES: ");
//--- check
if(converrors)
Print("FAILED");
else
Print("OK");
Print("INTERNAL ERRORS: ");
//--- check
if(interrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Checks that X is bounded with respect to BndL/BndU. |
//| If it is not,True is assigned to the Err variable (which is not |
//| changed otherwise). |
//+------------------------------------------------------------------+
static void CTestMinBLEICUnit::CheckBounds(double &x[],double &bndl[],
double &bndu[],const int n,
bool &err)
{
//--- create a variable
int i=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- check
if(x[i]<bndl[i] || x[i]>bndu[i])
err=true;
}
}
//+------------------------------------------------------------------+
//| Calculate test function IIP2 |
//| f(x)=sum( ((i*i+1)^FK*x[i])^2,i=0..N-1) |
//| It has high condition number which makes fast convergence |
//| unlikely without good preconditioner. |
//+------------------------------------------------------------------+
static void CTestMinBLEICUnit::CalcIIP2(CMinBLEICState &state,const int n,
const int fk)
{
//--- create a variable
int i=0;
//--- check
if(state.m_needfg)
state.m_f=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- check
if(state.m_needfg)
{
state.m_f=state.m_f+MathPow(i*i+1,2*fk)*CMath::Sqr(state.m_x[i]);
state.m_g[i]=MathPow(i*i+1,2*fk)*2*state.m_x[i];
}
}
}
//+------------------------------------------------------------------+
//| This function test feasibility properties. |
//| It launches a sequence of problems and examines their solutions. |
//| Most of the attention is directed towards feasibility properties,|
//| although we make some quick checks to ensure that actual solution|
//| is found. |
//| On failure sets FeasErr (or ConvErr,depending on failure type) |
//| to True, or leaves it unchanged otherwise. |
//| IntErr is set to True on internal errors (errors in the control |
//| flow). |
//+------------------------------------------------------------------+
static void CTestMinBLEICUnit::TestFeasibility(bool &feaserr,bool &converr,
bool &interr)
{
//--- create variables
int pkind=0;
int preckind=0;
int passcount=0;
int pass=0;
int n=0;
int nmax=0;
int i=0;
int j=0;
int k=0;
int p=0;
double v=0;
double v2=0;
double v3=0;
double vv=0;
double epsc=0;
double epsg=0;
int dkind=0;
double diffstep=0;
int i_=0;
//--- create arrays
double bl[];
double bu[];
double x[];
double g[];
double x0[];
double xs[];
int ct[];
//--- create matrix
CMatrixDouble c;
//--- objects of classes
CMinBLEICState state;
CMinBLEICReport rep;
//--- initialization
nmax=5;
epsc=1.0E-4;
epsg=1.0E-8;
passcount=10;
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- Test problem 1:
//--- * no boundary and inequality constraints
//--- * randomly generated plane as equality constraint
//--- * random point (not necessarily on the plane)
//--- * f=||x||^P,P={2,4} is used as target function
//--- * preconditioner is chosen at random (we just want to be
//--- sure that preconditioning won't prevent us from converging
//--- to the feasible point):
//--- * unit preconditioner
//--- * random diagonal-based preconditioner
//--- * random scale-based preconditioner
//--- * either analytic gradient or numerical differentiation are used
//--- * we check that after work is over we are on the plane and
//--- that we are in the stationary point of constrained F
diffstep=1.0E-6;
for(dkind=0;dkind<=1;dkind++)
{
for(preckind=0;preckind<=2;preckind++)
{
for(pkind=1;pkind<=2;pkind++)
{
for(n=1;n<=nmax;n++)
{
//--- Generate X,BL,BU,CT and left part of C.
//--- Right part of C is generated using somewhat complex algo:
//--- * we generate random vector and multiply it by C.
//--- * result is used as the right part.
//--- * calculations are done on the fly,vector itself is not stored
//--- We use such algo to be sure that our system is consistent.
p=2*pkind;
ArrayResize(x,n);
ArrayResize(g,n);
c.Resize(1,n+1);
ArrayResize(ct,1);
c[0].Set(n,0);
//--- calculation
for(i=0;i<=n-1;i++)
{
x[i]=2*CMath::RandomReal()-1;
c[0].Set(i,2*CMath::RandomReal()-1);
v=2*CMath::RandomReal()-1;
c[0].Set(n,c[0][n]+c[0][i]*v);
}
ct[0]=0;
//--- Create and optimize
if(dkind==0)
CMinBLEIC::MinBLEICCreate(n,x,state);
//--- check
if(dkind==1)
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
//--- function calls
CMinBLEIC::MinBLEICSetLC(state,c,ct,1);
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0);
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
SetRandomPreconditioner(state,n,preckind);
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=0;
for(i=0;i<=n-1;i++)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=state.m_f+MathPow(state.m_x[i],p);
//--- check
if(state.m_needfg)
state.m_g[i]=p*MathPow(state.m_x[i],p-1);
}
}
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
//--- check
if(rep.m_terminationtype<=0)
{
converr=true;
return;
}
//--- Test feasibility of solution
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=c[0][i_]*x[i_];
//--- search errors
feaserr=feaserr || MathAbs(v-c[0][n])>epsc;
//--- if C is nonzero,test that result is
//--- a stationary point of constrained F.
//--- NOTE: this check is done only if C is nonzero
vv=0.0;
for(i_=0;i_<=n-1;i_++)
vv+=c[0][i_]*c[0][i_];
//--- check
if(vv!=0.0)
{
//--- Calculate gradient at the result
//--- Project gradient into C
//--- Check projected norm
for(i=0;i<=n-1;i++)
g[i]=p*MathPow(x[i],p-1);
v2=0.0;
for(i_=0;i_<=n-1;i_++)
v2+=c[0][i_]*c[0][i_];
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=c[0][i_]*g[i_];
vv=v/v2;
for(i_=0;i_<=n-1;i_++)
{
g[i_]=g[i_]-vv*c[0][i_];
}
v3=0.0;
for(i_=0;i_<=n-1;i_++)
v3+=g[i_]*g[i_];
//--- search errors
converr=converr || MathSqrt(v3)>0.001;
}
}
}
}
}
//--- Test problem 2 (multiple equality constraints):
//--- * 1<=N<=NMax,1<=K<=N
//--- * no boundary constraints
//--- * N-dimensional space
//--- * randomly generated point xs
//--- * K randomly generated hyperplanes which all pass through xs
//--- define K equality constraints: (a[k],x)=b[k]
//--- * preconditioner is chosen at random (we just want to be
//--- sure that preconditioning won't prevent us from converging
//--- to the feasible point):
//--- * unit preconditioner
//--- * random diagonal-based preconditioner
//--- * random scale-based preconditioner
//--- * f(x)=||x-x0||^2,x0=xs+a[0]
//--- * either analytic gradient or numerical differentiation are used
//--- * extremum of f(x) is exactly xs because:
//--- * xs is the closest point in the plane defined by (a[0],x)=b[0]
//--- * xs is feasible by definition
diffstep=1.0E-6;
for(dkind=0;dkind<=1;dkind++)
{
for(preckind=0;preckind<=2;preckind++)
{
for(n=2;n<=nmax;n++)
{
for(k=1;k<=n;k++)
{
//--- Generate X,X0,XS,BL,BU,CT and left part of C.
//--- Right part of C is generated using somewhat complex algo:
//--- * we generate random vector and multiply it by C.
//--- * result is used as the right part.
//--- * calculations are done on the fly,vector itself is not stored
//--- We use such algo to be sure that our system is consistent.
p=2*pkind;
ArrayResize(x,n);
ArrayResize(x0,n);
ArrayResize(xs,n);
ArrayResize(g,n);
c.Resize(k,n+1);
ArrayResize(ct,k);
c[0].Set(n,0);
//--- change values
for(i=0;i<=n-1;i++)
{
x[i]=2*CMath::RandomReal()-1;
xs[i]=2*CMath::RandomReal()-1;
}
for(i=0;i<=k-1;i++)
{
for(j=0;j<=n-1;j++)
c[i].Set(j,2*CMath::RandomReal()-1);
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=c[i][i_]*xs[i_];
c[i].Set(n,v);
ct[i]=0;
}
//--- copy
for(i_=0;i_<=n-1;i_++)
x0[i_]=xs[i_];
for(i_=0;i_<=n-1;i_++)
x0[i_]=x0[i_]+c[0][i_];
//--- Create and optimize
//
if(dkind==0)
CMinBLEIC::MinBLEICCreate(n,x,state);
//--- check
if(dkind==1)
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
//--- function calls
CMinBLEIC::MinBLEICSetLC(state,c,ct,k);
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0);
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
SetRandomPreconditioner(state,n,preckind);
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=0;
for(i=0;i<=n-1;i++)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=state.m_f+CMath::Sqr(state.m_x[i]-x0[i]);
//--- check
if(state.m_needfg)
state.m_g[i]=2*(state.m_x[i]-x0[i]);
}
}
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
//--- check
if(rep.m_terminationtype<=0)
{
converr=true;
return;
}
//--- check feasiblity properties
for(i=0;i<=k-1;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=c[i][i_]*x[i_];
//--- search errors
feaserr=feaserr || MathAbs(v-c[i][n])>epsc;
}
//--- Compare with XS
v=0;
for(i=0;i<=n-1;i++)
v=v+CMath::Sqr(x[i]-xs[i]);
v=MathSqrt(v);
//--- search errors
converr=converr || MathAbs(v)>0.001;
}
}
}
}
//--- Another simple problem:
//--- * bound constraints 0 <=x[i] <=1
//--- * no linear constraints
//--- * preconditioner is chosen at random (we just want to be
//--- sure that preconditioning won't prevent us from converging
//--- to the feasible point):
//--- * unit preconditioner
//--- * random diagonal-based preconditioner
//--- * random scale-based preconditioner
//--- * F(x)=||x-x0||^P,where P={2,4} and x0 is randomly selected from [-1,+2]^N
//--- * with such simple boundaries and function it is easy to find
//--- analytic form of solution: S[i]=bound(x0[i],0,1)
//--- * we also check that both final solution and subsequent iterates
//--- are strictly feasible
diffstep=1.0E-6;
for(dkind=0;dkind<=1;dkind++)
{
for(preckind=0;preckind<=2;preckind++)
{
for(pkind=1;pkind<=2;pkind++)
{
for(n=1;n<=nmax;n++)
{
//--- Generate X,BL,BU.
p=2*pkind;
ArrayResize(bl,n);
ArrayResize(bu,n);
ArrayResize(x,n);
ArrayResize(x0,n);
//--- change values
for(i=0;i<=n-1;i++)
{
bl[i]=0;
bu[i]=1;
x[i]=CMath::RandomReal();
x0[i]=3*CMath::RandomReal()-1;
}
//--- Create and optimize
if(dkind==0)
CMinBLEIC::MinBLEICCreate(n,x,state);
//--- check
if(dkind==1)
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
//--- function calls
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0);
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
SetRandomPreconditioner(state,n,preckind);
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=0;
for(i=0;i<=n-1;i++)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=state.m_f+MathPow(state.m_x[i]-x0[i],p);
//--- check
if(state.m_needfg)
state.m_g[i]=p*MathPow(state.m_x[i]-x0[i],p-1);
//--- search errors
feaserr=feaserr || state.m_x[i]<0.0;
feaserr=feaserr || state.m_x[i]>1.0;
}
}
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
//--- check
if(rep.m_terminationtype<=0)
{
converr=true;
return;
}
//--- * compare solution with analytic one
//--- * check feasibility
for(i=0;i<=n-1;i++)
{
//--- search errors
converr=converr || MathAbs(x[i]-CApServ::BoundVal(x0[i],0.0,1.0))>0.01;
feaserr=feaserr || x[i]<0.0;
feaserr=feaserr || x[i]>1.0;
}
}
}
}
}
//--- Same as previous one,but with bound constraints posed
//--- as general linear ones:
//--- * no bound constraints
//--- * 2*N linear constraints 0 <=x[i] <=1
//--- * preconditioner is chosen at random (we just want to be
//--- sure that preconditioning won't prevent us from converging
//--- to the feasible point):
//--- * unit preconditioner
//--- * random diagonal-based preconditioner
//--- * random scale-based preconditioner
//--- * F(x)=||x-x0||^P,where P={2,4} and x0 is randomly selected from [-1,+2]^N
//--- * with such simple constraints and function it is easy to find
//--- analytic form of solution: S[i]=bound(x0[i],0,1).
//--- * however,we can't guarantee that solution is strictly feasible
//--- with respect to nonlinearity constraint,so we check
//--- for approximate feasibility.
for(preckind=0;preckind<=2;preckind++)
{
for(pkind=1;pkind<=2;pkind++)
{
for(n=1;n<=nmax;n++)
{
//--- Generate X,BL,BU.
p=2*pkind;
ArrayResize(x,n);
ArrayResize(x0,n);
c.Resize(2*n,n+1);
ArrayResize(ct,2*n);
//--- change values
for(i=0;i<=n-1;i++)
{
x[i]=CMath::RandomReal();
x0[i]=3*CMath::RandomReal()-1;
for(j=0;j<=n;j++)
{
c[2*i].Set(j,0);
c[2*i+1].Set(j,0);
}
c[2*i+0].Set(i,1);
c[2*i+0].Set(n,0);
ct[2*i+0]=1;
c[2*i+1].Set(i,1);
c[2*i+1].Set(n,1);
ct[2*i+1]=-1;
}
//--- Create and optimize
CMinBLEIC::MinBLEICCreate(n,x,state);
CMinBLEIC::MinBLEICSetLC(state,c,ct,2*n);
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0);
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
SetRandomPreconditioner(state,n,preckind);
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=0;
for(i=0;i<=n-1;i++)
{
state.m_f=state.m_f+MathPow(state.m_x[i]-x0[i],p);
state.m_g[i]=p*MathPow(state.m_x[i]-x0[i],p-1);
}
continue;
}
//--- Unknown protocol specified
interr=true;
return;
}
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
//--- check
if(rep.m_terminationtype<=0)
{
converr=true;
return;
}
//--- * compare solution with analytic one
//--- * check feasibility
for(i=0;i<=n-1;i++)
{
//--- search errors
converr=converr || MathAbs(x[i]-CApServ::BoundVal(x0[i],0.0,1.0))>0.05;
feaserr=feaserr || x[i]<0.0-epsc;
feaserr=feaserr || x[i]>1.0+epsc;
}
}
}
}
//--- Infeasible problem:
//--- * all bound constraints are 0 <=x[i] <=1 except for one
//--- * that one is 0 >=x[i] >=1
//--- * no linear constraints
//--- * preconditioner is chosen at random (we just want to be
//--- sure that preconditioning won't prevent us from detecting
//--- infeasible point):
//--- * unit preconditioner
//--- * random diagonal-based preconditioner
//--- * random scale-based preconditioner
//--- * F(x)=||x-x0||^P,where P={2,4} and x0 is randomly selected from [-1,+2]^N
//--- * algorithm must return correct error code on such problem
for(preckind=0;preckind<=2;preckind++)
{
for(pkind=1;pkind<=2;pkind++)
{
for(n=1;n<=nmax;n++)
{
//--- Generate X,BL,BU.
p=2*pkind;
ArrayResize(bl,n);
ArrayResize(bu,n);
ArrayResize(x,n);
ArrayResize(x0,n);
//--- change values
for(i=0;i<=n-1;i++)
{
bl[i]=0;
bu[i]=1;
x[i]=CMath::RandomReal();
x0[i]=3*CMath::RandomReal()-1;
}
i=CMath::RandomInteger(n);
bl[i]=1;
bu[i]=0;
//--- Create and optimize
CMinBLEIC::MinBLEICCreate(n,x,state);
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0);
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
SetRandomPreconditioner(state,n,preckind);
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=0;
for(i=0;i<=n-1;i++)
{
state.m_f=state.m_f+MathPow(state.m_x[i]-x0[i],p);
state.m_g[i]=p*MathPow(state.m_x[i]-x0[i],p-1);
}
continue;
}
//--- Unknown protocol specified
interr=true;
return;
}
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
//--- search errors
feaserr=feaserr || rep.m_terminationtype!=-3;
}
}
}
//--- Infeasible problem (2):
//--- * no bound and inequality constraints
//--- * 1<=K<=N arbitrary equality constraints
//--- * (K+1)th constraint which is equal to the first constraint a*x=c,
//--- but with c:=c+1. I.e. we have both a*x=c and a*x=c+1,which can't
//--- be true (other constraints may be inconsistent too,but we don't
//--- have to check it).
//--- * preconditioner is chosen at random (we just want to be
//--- sure that preconditioning won't prevent us from detecting
//--- infeasible point):
//--- * unit preconditioner
//--- * random diagonal-based preconditioner
//--- * random scale-based preconditioner
//--- * F(x)=||x||^P,where P={2,4}
//--- * algorithm must return correct error code on such problem
for(preckind=0;preckind<=2;preckind++)
{
for(pkind=1;pkind<=2;pkind++)
{
for(n=1;n<=nmax;n++)
{
for(k=1;k<=n;k++)
{
//--- Generate X,BL,BU.
p=2*pkind;
ArrayResize(x,n);
c.Resize(k+1,n+1);
ArrayResize(ct,k+1);
for(i=0;i<=n-1;i++)
x[i]=CMath::RandomReal();
//--- change values
for(i=0;i<=k-1;i++)
{
for(j=0;j<=n;j++)
c[i].Set(j,2*CMath::RandomReal()-1);
ct[i]=0;
}
ct[k]=0;
for(i_=0;i_<=n-1;i_++)
c[k].Set(i_,c[0][i_]);
c[k].Set(n,c[0][n]+1);
//--- Create and optimize
CMinBLEIC::MinBLEICCreate(n,x,state);
CMinBLEIC::MinBLEICSetLC(state,c,ct,k+1);
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0);
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
SetRandomPreconditioner(state,n,preckind);
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=0;
for(i=0;i<=n-1;i++)
{
state.m_f=state.m_f+MathPow(state.m_x[i],p);
state.m_g[i]=p*MathPow(state.m_x[i],p-1);
}
continue;
}
//--- Unknown protocol specified
interr=true;
return;
}
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
//--- search errors
feaserr=feaserr || rep.m_terminationtype!=-3;
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| This function additional properties. |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
static void CTestMinBLEICUnit::TestOther(bool &err)
{
//--- create variables
int passcount=0;
int pass=0;
int n=0;
int nmax=0;
int i=0;
double fprev=0;
double xprev=0;
double stpmax=0;
double v=0;
int pkind=0;
int ckind=0;
int mkind=0;
double vc=0;
double vm=0;
double epsc=0;
double epsg=0;
double tmpeps=0;
double diffstep=0;
int dkind=0;
bool wasf;
bool wasfg;
double r=0;
int i_=0;
//--- create arrays
double bl[];
double bu[];
double x[];
double xf[];
double xlast[];
double a[];
double s[];
double h[];
int ct[];
//--- create matrix
CMatrixDouble c;
//--- objects of classes
CMinBLEICState state;
CMinBLEICReport rep;
//--- initialization
nmax=5;
epsc=1.0E-4;
epsg=1.0E-8;
passcount=10;
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- Test reports:
//--- * first value must be starting point
//--- * last value must be last point
n=50;
ArrayResize(x,n);
ArrayResize(xlast,n);
ArrayResize(bl,n);
ArrayResize(bu,n);
//--- change values
for(i=0;i<=n-1;i++)
{
x[i]=10;
bl[i]=2*CMath::RandomReal()-1;
bu[i]=CInfOrNaN::PositiveInfinity();
}
//--- function calls
CMinBLEIC::MinBLEICCreate(n,x,state);
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
CMinBLEIC::MinBLEICSetInnerCond(state,0,0,0);
CMinBLEIC::MinBLEICSetMaxIts(state,10);
CMinBLEIC::MinBLEICSetOuterCond(state,1.0E-64,1.0E-64);
CMinBLEIC::MinBLEICSetXRep(state,true);
fprev=CMath::m_maxrealnumber;
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=0;
for(i=0;i<=n-1;i++)
{
state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]);
state.m_g[i]=2*(1+i)*state.m_x[i];
}
}
//--- check
if(state.m_xupdated)
{
//--- check
if(fprev==CMath::m_maxrealnumber)
{
for(i=0;i<=n-1;i++)
err=err || state.m_x[i]!=x[i];
}
//--- change values
fprev=state.m_f;
for(i_=0;i_<=n-1;i_++)
xlast[i_]=state.m_x[i_];
}
}
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
//--- search errors
for(i=0;i<=n-1;i++)
err=err || x[i]!=xlast[i];
//--- Test differentiation vs. analytic gradient
//--- (first one issues NeedF requests,second one issues NeedFG requests)
n=50;
diffstep=1.0E-6;
for(dkind=0;dkind<=1;dkind++)
{
//--- allocation
ArrayResize(x,n);
ArrayResize(xlast,n);
for(i=0;i<=n-1;i++)
x[i]=1;
//--- check
if(dkind==0)
CMinBLEIC::MinBLEICCreate(n,x,state);
//--- check
if(dkind==1)
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
//--- function calls
CMinBLEIC::MinBLEICSetInnerCond(state,1.0E-10,0,0);
CMinBLEIC::MinBLEICSetOuterCond(state,1.0E-6,1.0E-6);
wasf=false;
wasfg=false;
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]);
//--- check
if(state.m_needfg)
state.m_g[i]=2*(1+i)*state.m_x[i];
}
//--- search errors
wasf=wasf || state.m_needf;
wasfg=wasfg || state.m_needfg;
}
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
//--- check
if(dkind==0)
err=(err || wasf) || !wasfg;
//--- check
if(dkind==1)
err=(err || !wasf) || wasfg;
}
//--- Test that numerical differentiation uses scaling.
//--- In order to test that we solve simple optimization
//--- problem: min(x^2) with initial x equal to 0.0.
//--- We choose random DiffStep and S,then we check that
//--- optimizer evaluates function at +-DiffStep*S only.
ArrayResize(x,1);
ArrayResize(s,1);
diffstep=CMath::RandomReal()*1.0E-6;
s[0]=MathExp(CMath::RandomReal()*4-2);
x[0]=0;
//--- function calls
CMinBLEIC::MinBLEICCreateF(1,x,diffstep,state);
CMinBLEIC::MinBLEICSetInnerCond(state,1.0E-6,0,0);
CMinBLEIC::MinBLEICSetScale(state,s);
v=0;
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
{
state.m_f=CMath::Sqr(state.m_x[0]);
v=MathMax(v,MathAbs(state.m_x[0]));
}
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
r=v/(s[0]*diffstep);
//--- search errors
err=err || MathAbs(MathLog(r))>MathLog(1+1000*CMath::m_machineepsilon);
//--- Test stpmax
n=1;
ArrayResize(x,n);
ArrayResize(bl,n);
ArrayResize(bu,n);
//--- change values
x[0]=100;
bl[0]=2*CMath::RandomReal()-1;
bu[0]=CInfOrNaN::PositiveInfinity();
stpmax=0.05+0.05*CMath::RandomReal();
//--- function calls
CMinBLEIC::MinBLEICCreate(n,x,state);
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
CMinBLEIC::MinBLEICSetXRep(state,true);
CMinBLEIC::MinBLEICSetStpMax(state,stpmax);
xprev=x[0];
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]);
state.m_g[0]=MathExp(state.m_x[0])-MathExp(-state.m_x[0]);
err=err || MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax);
}
//--- check
if(state.m_xupdated)
{
err=err || MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax);
xprev=state.m_x[0];
}
}
//--- Ability to solve problems with function which is unbounded from below
n=1;
ArrayResize(x,n);
ArrayResize(bl,n);
ArrayResize(bu,n);
bl[0]=4*CMath::RandomReal()+1;
bu[0]=bl[0]+1;
x[0]=0.5*(bl[0]+bu[0]);
//--- function calls
CMinBLEIC::MinBLEICCreate(n,x,state);
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=-(1.0E8*CMath::Sqr(state.m_x[0]));
state.m_g[0]=-(2.0E8*state.m_x[0]);
}
}
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
//--- search errors
err=err || MathAbs(x[0]-bu[0])>epsc;
//--- Test correctness of the scaling:
//--- * initial point is random point from [+1,+2]^N
//--- * f(x)=SUM(A[i]*x[i]^4),C[i] is random from [0.01,100]
//--- * function is EFFECTIVELY unconstrained;it has formal constraints,
//--- but they are inactive at the solution;we try different variants
//--- in order to explore different control paths of the optimizer:
//--- 0) absense of constraints
//--- 1) bound constraints -100000<=x[i]<=100000
//--- 2) one linear constraint 0*x=0
//--- 3) combination of (1) and (2)
//--- * we use random scaling matrix
//--- * we test different variants of the preconditioning:
//--- 0) unit preconditioner
//--- 1) random diagonal from [0.01,100]
//--- 2) scale preconditioner
//--- * we set very mild outer stopping conditions - OuterEpsX=1.0,but
//--- inner conditions are very stringent
//--- * and we test that in the extremum inner stopping conditions are
//--- satisfied subject to the current scaling coefficients.
tmpeps=1.0E-10;
for(n=1;n<=10;n++)
{
for(ckind=0;ckind<=3;ckind++)
{
for(pkind=0;pkind<=2;pkind++)
{
//--- allocation
ArrayResize(x,n);
ArrayResize(a,n);
ArrayResize(s,n);
ArrayResize(h,n);
ArrayResize(bl,n);
ArrayResize(bu,n);
c.Resize(1,n+1);
ArrayResize(ct,1);
ct[0]=0;
c[0].Set(n,0);
//--- change values
for(i=0;i<=n-1;i++)
{
x[i]=CMath::RandomReal()+1;
bl[i]=-100000;
bu[i]=100000;
c[0].Set(i,0);
a[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
s[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
h[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
}
CMinBLEIC::MinBLEICCreate(n,x,state);
//--- check
if(ckind==1 || ckind==3)
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
//--- check
if(ckind==2 || ckind==3)
CMinBLEIC::MinBLEICSetLC(state,c,ct,1);
//--- check
if(pkind==1)
CMinBLEIC::MinBLEICSetPrecDiag(state,h);
//--- check
if(pkind==2)
CMinBLEIC::MinBLEICSetPrecScale(state);
//--- function calls
CMinBLEIC::MinBLEICSetInnerCond(state,tmpeps,0,0);
CMinBLEIC::MinBLEICSetOuterCond(state,1.0,1.0E-8);
CMinBLEIC::MinBLEICSetScale(state,s);
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=0;
for(i=0;i<=n-1;i++)
{
state.m_f=state.m_f+a[i]*MathPow(state.m_x[i],4);
state.m_g[i]=4*a[i]*MathPow(state.m_x[i],3);
}
}
}
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
//--- check
if(rep.m_terminationtype<=0)
{
err=true;
return;
}
//--- change value
v=0;
for(i=0;i<=n-1;i++)
v=v+CMath::Sqr(s[i]*4*a[i]*MathPow(x[i],3));
v=MathSqrt(v);
//--- search errors
err=err || v>tmpeps;
}
}
}
//--- Check correctness of the "trimming".
//--- Trimming is a technique which is used to help algorithm
//--- cope with unbounded functions. In order to check this
//--- technique we will try to solve following optimization
//--- problem:
//--- min f(x) subject to no constraints on X
//--- { 1/(1-x) + 1/(1+x) + c*x,if -0.999999<x<0.999999
//--- f(x)={
//--- { M,if x<=-0.999999 or x>=0.999999
//--- where c is either 1.0 or 1.0E+6,M is either 1.0E8,1.0E20 or +INF
//--- (we try different combinations)
//
for(ckind=0;ckind<=1;ckind++)
{
for(mkind=0;mkind<=2;mkind++)
{
//--- Choose c and M
if(ckind==0)
vc=1.0;
//--- check
if(ckind==1)
vc=1.0E+6;
//--- check
if(mkind==0)
vm=1.0E+8;
//--- check
if(mkind==1)
vm=1.0E+20;
//--- check
if(mkind==2)
vm=CInfOrNaN::PositiveInfinity();
//--- Create optimizer,solve optimization problem
epsg=1.0E-6*vc;
ArrayResize(x,1);
x[0]=0.0;
//--- function calls
CMinBLEIC::MinBLEICCreate(1,x,state);
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
CMinBLEIC::MinBLEICSetOuterCond(state,1.0E-6,1.0E-6);
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
{
//--- check
if(state.m_needfg)
{
//--- check
if(-0.999999<state.m_x[0] && state.m_x[0]<0.999999)
{
state.m_f=1/(1-state.m_x[0])+1/(1+state.m_x[0])+vc*state.m_x[0];
state.m_g[0]=1/CMath::Sqr(1-state.m_x[0])-1/CMath::Sqr(1+state.m_x[0])+vc;
}
else
state.m_f=vm;
}
}
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
//--- check
if(rep.m_terminationtype<=0)
{
err=true;
return;
}
//--- search errors
err=err || MathAbs(1/CMath::Sqr(1-x[0])-1/CMath::Sqr(1+x[0])+vc)>epsg;
}
}
}
}
//+------------------------------------------------------------------+
//| This function tests convergence properties. |
//| We solve several simple problems with different combinations of |
//| constraints |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
static void CTestMinBLEICUnit::TestConv(bool &err)
{
//--- create variables
int passcount=0;
int pass=0;
double epsc=0;
double epsg=0;
double tol=0;
//--- create arrays
double bl[];
double bu[];
double x[];
int ct[];
//--- create matrix
CMatrixDouble c;
//--- objects of classes
CMinBLEICState state;
CMinBLEICReport rep;
//--- initialization
epsc=1.0E-4;
epsg=1.0E-8;
tol=0.001;
passcount=10;
//--- Three closely connected problems:
//--- * 2-dimensional space
//--- * octagonal area bounded by:
//--- * -1<=x<=+1
//--- * -1<=y<=+1
//--- * x+y<=1.5
//--- * x-y<=1.5
//--- * -x+y<=1.5
//--- * -x-y<=1.5
//--- * several target functions:
//--- * f0=x+0.001*y,minimum at x=-1,y=-0.5
//--- * f1=(x+10)^2+y^2,minimum at x=-1,y=0
//--- * f2=(x+10)^2+(y-0.6)^2,minimum at x=-1,y=0.5
ArrayResize(x,2);
ArrayResize(bl,2);
ArrayResize(bu,2);
c.Resize(4,3);
ArrayResize(ct,4);
//--- change values
bl[0]=-1;
bl[1]=-1;
bu[0]=1;
bu[1]=1;
c[0].Set(0,1);
c[0].Set(1,1);
c[0].Set(2,1.5);
ct[0]=-1;
c[1].Set(0,1);
c[1].Set(1,-1);
c[1].Set(2,1.5);
ct[1]=-1;
c[2].Set(0,-1);
c[2].Set(1,1);
c[2].Set(2,1.5);
ct[2]=-1;
c[3].Set(0,-1);
c[3].Set(1,-1);
c[3].Set(2,1.5);
ct[3]=-1;
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- f0
x[0]=0.2*CMath::RandomReal()-0.1;
x[1]=0.2*CMath::RandomReal()-0.1;
//--- function call
CMinBLEIC::MinBLEICCreate(2,x,state);
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
CMinBLEIC::MinBLEICSetLC(state,c,ct,4);
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=state.m_x[0]+0.001*state.m_x[1];
state.m_g[0]=1;
state.m_g[1]=0.001;
}
}
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
//--- check
if(rep.m_terminationtype>0)
{
//--- search errors
err=err || MathAbs(x[0]+1)>tol;
err=err || MathAbs(x[1]+0.5)>tol;
}
else
err=true;
//--- f1
x[0]=0.2*CMath::RandomReal()-0.1;
x[1]=0.2*CMath::RandomReal()-0.1;
//--- function calls
CMinBLEIC::MinBLEICCreate(2,x,state);
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
CMinBLEIC::MinBLEICSetLC(state,c,ct,4);
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=CMath::Sqr(state.m_x[0]+10)+CMath::Sqr(state.m_x[1]);
state.m_g[0]=2*(state.m_x[0]+10);
state.m_g[1]=2*state.m_x[1];
}
}
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
//--- check
if(rep.m_terminationtype>0)
{
//--- search errors
err=err || MathAbs(x[0]+1)>tol;
err=err || MathAbs(x[1])>tol;
}
else
err=true;
//--- f2
x[0]=0.2*CMath::RandomReal()-0.1;
x[1]=0.2*CMath::RandomReal()-0.1;
//--- function calls
CMinBLEIC::MinBLEICCreate(2,x,state);
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
CMinBLEIC::MinBLEICSetLC(state,c,ct,4);
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=CMath::Sqr(state.m_x[0]+10)+CMath::Sqr(state.m_x[1]-0.6);
state.m_g[0]=2*(state.m_x[0]+10);
state.m_g[1]=2*(state.m_x[1]-0.6);
}
}
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
//--- check
if(rep.m_terminationtype>0)
{
//--- search errors
err=err || MathAbs(x[0]+1)>tol;
err=err || MathAbs(x[1]-0.5)>tol;
}
else
err=true;
}
}
//+------------------------------------------------------------------+
//| This function tests preconditioning |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
static void CTestMinBLEICUnit::TestPreconditioning(bool &err)
{
//--- create variables
int pass=0;
int n=0;
int i=0;
int k=0;
int cntb1=0;
int cntb2=0;
int cntg1=0;
int cntg2=0;
double epsg=0;
int fkind=0;
int ckind=0;
int fk=0;
//--- create arrays
double x[];
double x0[];
int ct[];
double bl[];
double bu[];
double vd[];
double d[];
double units[];
double s[];
double diagh[];
//--- create matrix
CMatrixDouble v;
CMatrixDouble c;
//--- objects of classes
CMinBLEICState state;
CMinBLEICReport rep;
//--- Preconditioner test 1.
//--- If
//--- * B1 is default preconditioner with unit scale
//--- * G1 is diagonal preconditioner based on approximate diagonal of Hessian matrix
//--- * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
//--- * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
//--- then B1 is worse than G1,B2 is worse than G2.
//--- "Worse" means more iterations to converge.
//--- Test problem setup:
//--- * f(x)=sum( ((i*i+1)^FK*x[i])^2,i=0..N-1)
//--- * FK is either +1 or -1 (we try both to test different aspects of preconditioning)
//--- * constraints:
//--- 0) absent
//--- 1) boundary only
//--- 2) linear equality only
//--- 3) combination of boundary and linear equality constraints
//--- N - problem size
//--- K - number of repeated passes (should be large enough to average out random factors)
k=30;
epsg=1.0E-10;
for(n=5;n<=8;n++)
{
for(fkind=0;fkind<=1;fkind++)
{
for(ckind=0;ckind<=3;ckind++)
{
fk=1-2*fkind;
//--- allocation
ArrayResize(x,n);
ArrayResize(units,n);
for(i=0;i<=n-1;i++)
{
x[i]=0;
units[i]=1;
}
//--- function call
CMinBLEIC::MinBLEICCreate(n,x,state);
//--- check
if(ckind==1 || ckind==3)
{
//--- allocation
ArrayResize(bl,n);
ArrayResize(bu,n);
for(i=0;i<=n-1;i++)
{
bl[i]=-1;
bu[i]=1;
}
//--- function call
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
}
//--- check
if(ckind==2 || ckind==3)
{
//--- allocation
c.Resize(1,n+1);
ArrayResize(ct,1);
//--- change value
ct[0]=CMath::RandomInteger(3)-1;
for(i=0;i<=n-1;i++)
c[0].Set(i,2*CMath::RandomReal()-1);
c[0].Set(n,0);
//--- function call
CMinBLEIC::MinBLEICSetLC(state,c,ct,1);
}
//--- Test it with default preconditioner VS. perturbed diagonal preconditioner
CMinBLEIC::MinBLEICSetPrecDefault(state);
CMinBLEIC::MinBLEICSetScale(state,units);
//--- calculation
cntb1=0;
for(pass=0;pass<=k-1;pass++)
{
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinBLEIC::MinBLEICRestartFrom(state,x);
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
CalcIIP2(state,n,fk);
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
cntb1=cntb1+rep.m_inneriterationscount;
//--- search errors
err=err || rep.m_terminationtype<=0;
}
//--- allocation
ArrayResize(diagh,n);
for(i=0;i<=n-1;i++)
diagh[i]=2*MathPow(i*i+1,2*fk)*(0.8+0.4*CMath::RandomReal());
//--- function calls
CMinBLEIC::MinBLEICSetPrecDiag(state,diagh);
CMinBLEIC::MinBLEICSetScale(state,units);
//--- calculation
cntg1=0;
for(pass=0;pass<=k-1;pass++)
{
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinBLEIC::MinBLEICRestartFrom(state,x);
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
CalcIIP2(state,n,fk);
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
cntg1=cntg1+rep.m_inneriterationscount;
//--- search errors
err=err || rep.m_terminationtype<=0;
}
//--- search errors
err=err || cntb1<cntg1;
//--- Test it with scale-based preconditioner
ArrayResize(s,n);
for(i=0;i<=n-1;i++)
s[i]=1/MathSqrt(2*MathPow(i*i+1,2*fk)*(0.8+0.4*CMath::RandomReal()));
//--- function calls
CMinBLEIC::MinBLEICSetPrecDefault(state);
CMinBLEIC::MinBLEICSetScale(state,s);
//--- change values
cntb2=0;
for(pass=0;pass<=k-1;pass++)
{
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinBLEIC::MinBLEICRestartFrom(state,x);
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
CalcIIP2(state,n,fk);
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
cntb2=cntb2+rep.m_inneriterationscount;
//--- search errors
err=err || rep.m_terminationtype<=0;
}
//--- function calls
CMinBLEIC::MinBLEICSetPrecScale(state);
CMinBLEIC::MinBLEICSetScale(state,s);
//--- change values
cntg2=0;
for(pass=0;pass<=k-1;pass++)
{
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinBLEIC::MinBLEICRestartFrom(state,x);
//--- cycle
while(CMinBLEIC::MinBLEICIteration(state))
CalcIIP2(state,n,fk);
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
cntg2=cntg2+rep.m_inneriterationscount;
//--- search errors
err=err || rep.m_terminationtype<=0;
}
//--- search errors
err=err || cntb2<cntg2;
}
}
}
}
//+------------------------------------------------------------------+
//| This function sets random preconditioner: |
//| * unit one,for PrecKind=0 |
//| * diagonal-based one,for PrecKind=1 |
//| * scale-based one,for PrecKind=2 |
//+------------------------------------------------------------------+
static void CTestMinBLEICUnit::SetRandomPreconditioner(CMinBLEICState &state,
const int n,
const int preckind)
{
//--- create a variable
int i=0;
//--- create array
double p[];
//--- check
if(preckind==1)
{
//--- allocation
ArrayResize(p,n);
for(i=0;i<=n-1;i++)
p[i]=MathExp(10*CMath::RandomReal()-5);
//--- function call
CMinBLEIC::MinBLEICSetPrecDiag(state,p);
}
else
CMinBLEIC::MinBLEICSetPrecDefault(state);
}
//+------------------------------------------------------------------+
//| Testing class CMarkovCPD |
//+------------------------------------------------------------------+
class CTestMCPDUnit
{
private:
//--- private methods
static void TestSimple(bool &err);
static void TestEntryExit(bool &err);
static void TestEC(bool &err);
static void TestBC(bool &err);
static void TestLC(bool &err);
static void CreateEE(const int n,const int entrystate,const int exitstate,CMCPDState &s);
public:
//--- constructor, destructor
CTestMCPDUnit(void);
~CTestMCPDUnit(void);
//--- public method
static bool TestMCPD(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestMCPDUnit::CTestMCPDUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestMCPDUnit::~CTestMCPDUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CMarkovCPD |
//+------------------------------------------------------------------+
static bool CTestMCPDUnit::TestMCPD(const bool silent)
{
//--- create variables
bool waserrors;
bool simpleerrors;
bool entryexiterrors;
bool ecerrors;
bool bcerrors;
bool lcerrors;
bool othererrors;
//--- Init
waserrors=false;
othererrors=false;
simpleerrors=false;
entryexiterrors=false;
ecerrors=false;
bcerrors=false;
lcerrors=false;
//--- Test
TestSimple(simpleerrors);
TestEntryExit(entryexiterrors);
TestEC(ecerrors);
TestBC(bcerrors);
TestLC(lcerrors);
//--- Final report
waserrors=((((othererrors || simpleerrors) || entryexiterrors) || ecerrors) || bcerrors) || lcerrors;
//--- check
if(!silent)
{
Print("MCPD TEST");
Print("TOTAL RESULTS: ");
//--- check
if(!waserrors)
Print("OK");
else
Print("FAILED");
Print("* SIMPLE: ");
//--- check
if(!simpleerrors)
Print("OK");
else
Print("FAILED");
Print("* ENTRY/EXIT: ");
//--- check
if(!entryexiterrors)
Print("OK");
else
Print("FAILED");
Print("* EQUALITY CONSTRAINTS: ");
//--- check
if(!ecerrors)
Print("OK");
else
Print("FAILED");
Print("* BOUND CONSTRAINTS: ");
//--- check
if(!bcerrors)
Print("OK");
else
Print("FAILED");
Print("* LINEAR CONSTRAINTS: ");
//--- check
if(!lcerrors)
Print("OK");
else
Print("FAILED");
Print("* OTHER PROPERTIES: ");
//--- check
if(!othererrors)
Print("OK");
else
Print("FAILED");
//--- check
if(waserrors)
Print("TEST SUMMARY: FAILED");
else
Print("TEST SUMMARY: PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Simple test with no "entry"/"exit" states |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
static void CTestMCPDUnit::TestSimple(bool &err)
{
//--- create variables
int n=0;
double threshold=0;
int i=0;
int j=0;
double v=0;
double v0=0;
double offdiagonal=0;
//--- objects of classes
CMCPDState s;
CMCPDReport rep;
//--- create matrix
CMatrixDouble pexact;
CMatrixDouble xy;
CMatrixDouble p;
//--- initialization
threshold=1.0E-2;
//--- First test:
//--- * N-dimensional problem
//--- * proportional data
//--- * no "entry"/"exit" states
//--- * N tracks,each includes only two states
//--- * first record in I-th track is [0 ... 1 ... 0] with 1 is in I-th position
//--- * all tracks are modelled using randomly generated transition matrix P
for(n=1;n<=5;n++)
{
//--- Initialize "exact" P:
//--- * fill by random values
//--- * make sure that each column sums to non-zero value
//--- * normalize
pexact.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
pexact[i].Set(j,CMath::RandomReal());
}
for(j=0;j<=n-1;j++)
{
i=CMath::RandomInteger(n);
pexact[i].Set(j,pexact[i][j]+0.1);
}
//--- calculation
for(j=0;j<=n-1;j++)
{
v=0;
for(i=0;i<=n-1;i++)
v=v+pexact[i][j];
for(i=0;i<=n-1;i++)
pexact[i].Set(j,pexact[i][j]/v);
}
//--- Initialize solver:
//--- * create object
//--- * add tracks
CMarkovCPD::MCPDCreate(n,s);
for(i=0;i<=n-1;i++)
{
xy.Resize(2,n);
//--- change values
for(j=0;j<=n-1;j++)
xy[0].Set(j,0);
xy[0].Set(i,1);
for(j=0;j<=n-1;j++)
xy[1].Set(j,pexact[j][i]);
CMarkovCPD::MCPDAddTrack(s,xy,2);
}
//--- Solve and test
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- check
if(rep.m_terminationtype>0)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
err=err || MathAbs(p[i][j]-pexact[i][j])>threshold;
}
}
else
err=true;
}
//--- Second test:
//--- * N-dimensional problem
//--- * proportional data
//--- * no "entry"/"exit" states
//--- * N tracks,each includes only two states
//--- * first record in I-th track is [0 ...0.1 0.8 0.1 ... 0] with 0.8 is in I-th position
//--- * all tracks are modelled using randomly generated transition matrix P
offdiagonal=0.1;
for(n=1;n<=5;n++)
{
//--- Initialize "exact" P:
//--- * fill by random values
//--- * make sure that each column sums to non-zero value
//--- * normalize
pexact.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
pexact[i].Set(j,CMath::RandomReal());
}
//--- calculation
for(j=0;j<=n-1;j++)
{
i=CMath::RandomInteger(n);
pexact[i].Set(j,pexact[i][j]+0.1);
}
for(j=0;j<=n-1;j++)
{
v=0;
for(i=0;i<=n-1;i++)
v=v+pexact[i][j];
for(i=0;i<=n-1;i++)
pexact[i].Set(j,pexact[i][j]/v);
}
//--- Initialize solver:
//--- * create object
//--- * add tracks
CMarkovCPD::MCPDCreate(n,s);
for(i=0;i<=n-1;i++)
{
//--- allocation
xy.Resize(2,n);
for(j=0;j<=n-1;j++)
xy[0].Set(j,0);
//--- "main" element
xy[0].Set(i,1.0-2*offdiagonal);
for(j=0;j<=n-1;j++)
xy[1].Set(j,(1.0-2*offdiagonal)*pexact[j][i]);
//--- off-diagonal ones
if(i>0)
{
xy[0].Set(i-1,offdiagonal);
for(j=0;j<=n-1;j++)
xy[1].Set(j,xy[1][j]+offdiagonal*pexact[j][i-1]);
}
//--- check
if(i<n-1)
{
xy[0].Set(i+1,offdiagonal);
for(j=0;j<=n-1;j++)
xy[1].Set(j,xy[1][j]+offdiagonal*pexact[j][i+1]);
}
//--- function call
CMarkovCPD::MCPDAddTrack(s,xy,2);
}
//--- Solve and test
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- check
if(rep.m_terminationtype>0)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
err=err || MathAbs(p[i][j]-pexact[i][j])>threshold;
}
}
else
err=true;
}
//--- Third test:
//--- * N-dimensional problem
//--- * population data
//--- * no "entry"/"exit" states
//--- * N tracks,each includes only two states
//--- * first record in I-th track is V*[0 ...0.1 0.8 0.1 ... 0] with 0.8 is in I-th position,V in [1,10]
//--- * all tracks are modelled using randomly generated transition matrix P
offdiagonal=0.1;
for(n=1;n<=5;n++)
{
//--- Initialize "exact" P:
//--- * fill by random values
//--- * make sure that each column sums to non-zero value
//--- * normalize
pexact.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
pexact[i].Set(j,CMath::RandomReal());
}
//--- change values
for(j=0;j<=n-1;j++)
{
i=CMath::RandomInteger(n);
pexact[i].Set(j,pexact[i][j]+0.1);
}
for(j=0;j<=n-1;j++)
{
v=0;
for(i=0;i<=n-1;i++)
v=v+pexact[i][j];
for(i=0;i<=n-1;i++)
pexact[i].Set(j,pexact[i][j]/v);
}
//--- Initialize solver:
//--- * create object
//--- * add tracks
CMarkovCPD::MCPDCreate(n,s);
for(i=0;i<=n-1;i++)
{
//--- allocation
xy.Resize(2,n);
for(j=0;j<=n-1;j++)
xy[0].Set(j,0);
//--- "main" element
v0=9*CMath::RandomReal()+1;
xy[0].Set(i,v0*(1.0-2*offdiagonal));
for(j=0;j<=n-1;j++)
xy[1].Set(j,v0*(1.0-2*offdiagonal)*pexact[j][i]);
//--- off-diagonal ones
if(i>0)
{
xy[0].Set(i-1,v0*offdiagonal);
for(j=0;j<=n-1;j++)
xy[1].Set(j,xy[1][j]+v0*offdiagonal*pexact[j][i-1]);
}
//--- check
if(i<n-1)
{
xy[0].Set(i+1,v0*offdiagonal);
for(j=0;j<=n-1;j++)
xy[1].Set(j,xy[1][j]+v0*offdiagonal*pexact[j][i+1]);
}
//--- function call
CMarkovCPD::MCPDAddTrack(s,xy,2);
}
//--- Solve and test
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- check
if(rep.m_terminationtype>0)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
err=err || MathAbs(p[i][j]-pexact[i][j])>threshold;
}
}
else
err=true;
}
}
//+------------------------------------------------------------------+
//| Test for different combinations of "entry"/"exit" models |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
static void CTestMCPDUnit::TestEntryExit(bool &err)
{
//--- create variables
int n=0;
double threshold=0;
int entrystate=0;
int exitstate=0;
int entrykind=0;
int exitkind=0;
int popkind=0;
int i=0;
int j=0;
int k=0;
double v=0;
int i_=0;
//--- create matrix
CMatrixDouble p;
CMatrixDouble pexact;
CMatrixDouble xy;
//--- objects of classes
CMCPDState s;
CMCPDReport rep;
//--- initialization
threshold=1.0E-3;
//--- calculation
for(n=2;n<=5;n++)
{
for(entrykind=0;entrykind<=1;entrykind++)
{
for(exitkind=0;exitkind<=1;exitkind++)
{
for(popkind=0;popkind<=1;popkind++)
{
//--- Generate EntryState/ExitState such that one of the following is True:
//--- * EntryState<>ExitState
//--- * EntryState=-1 or ExitState=-1
do
{
//--- check
if(entrykind==0)
entrystate=-1;
else
entrystate=CMath::RandomInteger(n);
//--- check
if(exitkind==0)
exitstate=-1;
else
exitstate=CMath::RandomInteger(n);
}
while(!((entrystate==-1 || exitstate==-1) || entrystate!=exitstate));
//--- Generate transition matrix P such that:
//--- * columns corresponding to non-exit states sums to 1.0
//--- * columns corresponding to exit states sums to 0.0
//--- * rows corresponding to entry states are zero
pexact.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
pexact[i].Set(j,1+CMath::RandomInteger(5));
//--- check
if(i==entrystate)
pexact[i].Set(j,0.0);
//--- check
if(j==exitstate)
pexact[i].Set(j,0.0);
}
}
//--- calculation
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i=0;i<=n-1;i++)
v=v+pexact[i][j];
//--- check
if(v!=0.0)
{
for(i=0;i<=n-1;i++)
pexact[i].Set(j,pexact[i][j]/v);
}
}
//--- Create MCPD solver
if(entrystate<0 && exitstate<0)
CMarkovCPD::MCPDCreate(n,s);
//--- check
if(entrystate>=0 && exitstate<0)
CMarkovCPD::MCPDCreateEntry(n,entrystate,s);
//--- check
if(entrystate<0 && exitstate>=0)
CMarkovCPD::MCPDCreateExit(n,exitstate,s);
//--- check
if(entrystate>=0 && exitstate>=0)
CMarkovCPD::MCPDCreateEntryExit(n,entrystate,exitstate,s);
//--- Add N tracks.
//--- K-th track starts from vector with large value of
//--- K-th component and small random noise in other components.
//--- Track contains from 2 to 4 elements.
//--- Tracks contain proportional (normalized) or
//--- population data,depending on PopKind variable.
for(k=0;k<=n-1;k++)
{
//--- Generate track whose length is in 2..4
xy.Resize(2+CMath::RandomInteger(3),n);
for(j=0;j<=n-1;j++)
xy[0].Set(j,0.05*CMath::RandomReal());
xy[0].Set(k,1+CMath::RandomReal());
//--- calculation
for(i=1;i<=CAp::Rows(xy)-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(j!=entrystate)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=pexact[j][i_]*xy[i-1][i_];
xy[i].Set(j,v);
}
else
xy[i].Set(j,CMath::RandomReal());
}
}
//--- Normalize,if needed
if(popkind==1)
{
for(i=0;i<=CAp::Rows(xy)-1;i++)
{
//--- change value
v=0.0;
for(j=0;j<=n-1;j++)
v=v+xy[i][j];
//--- check
if(v>0.0)
{
for(j=0;j<=n-1;j++)
xy[i].Set(j,xy[i][j]/v);
}
}
}
//--- Add track
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
}
//--- Solve and test
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- check
if(rep.m_terminationtype>0)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
err=err || MathAbs(p[i][j]-pexact[i][j])>threshold;
}
}
else
err=true;
}
}
}
}
}
//+------------------------------------------------------------------+
//| Test equality constraints. |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
static void CTestMCPDUnit::TestEC(bool &err)
{
//--- create variables
int n=0;
int entrystate=0;
int exitstate=0;
int entrykind=0;
int exitkind=0;
int i=0;
int j=0;
int ic=0;
int jc=0;
double vc=0;
//--- create matrix
CMatrixDouble p;
CMatrixDouble ec;
CMatrixDouble xy;
//--- objects of classes
CMCPDState s;
CMCPDReport rep;
//--- We try different problems with following properties:
//--- * N is large enough - we won't have problems with inconsistent constraints
//--- * first state is either "entry" or "normal"
//--- * last state is either "exit" or "normal"
//--- * we have one long random track
//--- We test several properties which are described in comments below
for(n=4;n<=6;n++)
{
for(entrykind=0;entrykind<=1;entrykind++)
{
for(exitkind=0;exitkind<=1;exitkind++)
{
//--- Prepare problem
if(entrykind==0)
entrystate=-1;
else
entrystate=0;
//--- check
if(exitkind==0)
exitstate=-1;
else
exitstate=n-1;
//--- allocation
xy.Resize(2*n,n);
for(i=0;i<=CAp::Rows(xy)-1;i++)
{
for(j=0;j<=CAp::Cols(xy)-1;j++)
xy[i].Set(j,CMath::RandomReal());
}
//--- Test that single equality constraint on non-entry
//--- non-exit elements of P is satisfied.
//--- NOTE: this test needs N>=4 because smaller values
//--- can give us inconsistent constraints
if(!CAp::Assert(n>=4,"TestEC: expectation failed"))
return;
ic=1+CMath::RandomInteger(n-2);
jc=1+CMath::RandomInteger(n-2);
vc=CMath::RandomReal();
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDAddEC(s,ic,jc,vc);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- check
if(rep.m_terminationtype>0)
err=err || p[ic][jc]!=vc;
else
err=true;
//--- Test interaction with default "sum-to-one" constraint
//--- on columns of P.
//--- We set N-1 equality constraints on random non-exit column
//--- of P,which are inconsistent with this default constraint
//--- (sum will be greater that 1.0).
//--- Algorithm must detect inconsistency.
//--- NOTE:
//--- 1. we do not set constraints for the first element of
//--- the column,because this element may be constrained by
//--- "exit state" constraint.
//--- 2. this test needs N>=3
if(!CAp::Assert(n>=3,"TestEC: expectation failed"))
return;
jc=CMath::RandomInteger(n-1);
vc=0.95;
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
for(i=1;i<=n-1;i++)
CMarkovCPD::MCPDAddEC(s,i,jc,vc);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- search errors
err=err || rep.m_terminationtype!=-3;
//--- Test interaction with constrains on entry states.
//--- When model has entry state,corresponding row of P
//--- must be zero. We try to set two kinds of constraints
//--- on random element of this row:
//--- * zero equality constraint,which must be consistent
//--- * non-zero equality constraint,which must be inconsistent
if(entrystate>=0)
{
jc=CMath::RandomInteger(n);
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDAddEC(s,entrystate,jc,0.0);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- search errors
err=err || rep.m_terminationtype<=0;
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDAddEC(s,entrystate,jc,0.5);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- search errors
err=err || rep.m_terminationtype!=-3;
}
//--- Test interaction with constrains on exit states.
//--- When model has exit state,corresponding column of P
//--- must be zero. We try to set two kinds of constraints
//--- on random element of this column:
//--- * zero equality constraint,which must be consistent
//--- * non-zero equality constraint,which must be inconsistent
if(exitstate>=0)
{
ic=CMath::RandomInteger(n);
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDAddEC(s,ic,exitstate,0.0);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- search errors
err=err || rep.m_terminationtype<=0;
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDAddEC(s,ic,exitstate,0.5);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- search errors
err=err || rep.m_terminationtype!=-3;
}
//--- Test SetEC() call - we constrain subset of non-entry
//--- non-exit elements and test it.
if(!CAp::Assert(n>=4,"TestEC: expectation failed"))
return;
//--- allocation
ec.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
ec[i].Set(j,CInfOrNaN::NaN());
}
for(j=1;j<=n-2;j++)
ec[1+CMath::RandomInteger(n-2)].Set(j,0.1+0.1*CMath::RandomReal());
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDSetEC(s,ec);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- check
if(rep.m_terminationtype>0)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(CMath::IsFinite(ec[i][j]))
err=err || p[i][j]!=ec[i][j];
}
}
}
else
err=true;
}
}
}
}
//+------------------------------------------------------------------+
//| Test bound constraints. |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
static void CTestMCPDUnit::TestBC(bool &err)
{
//--- create variables
int n=0;
int entrystate=0;
int exitstate=0;
int entrykind=0;
int exitkind=0;
int i=0;
int j=0;
int ic=0;
int jc=0;
double vl=0;
double vu=0;
//--- create matrix
CMatrixDouble p;
CMatrixDouble bndl;
CMatrixDouble bndu;
CMatrixDouble xy;
//---
CMCPDState s;
CMCPDReport rep;
//--- We try different problems with following properties:
//--- * N is large enough - we won't have problems with inconsistent constraints
//--- * first state is either "entry" or "normal"
//--- * last state is either "exit" or "normal"
//--- * we have one long random track
//--- We test several properties which are described in comments below
for(n=4;n<=6;n++)
{
for(entrykind=0;entrykind<=1;entrykind++)
{
for(exitkind=0;exitkind<=1;exitkind++)
{
//--- Prepare problem
if(entrykind==0)
entrystate=-1;
else
entrystate=0;
//--- check
if(exitkind==0)
exitstate=-1;
else
exitstate=n-1;
//--- allocation
xy.Resize(2*n,n);
for(i=0;i<=CAp::Rows(xy)-1;i++)
{
for(j=0;j<=CAp::Cols(xy)-1;j++)
xy[i].Set(j,CMath::RandomReal());
}
//--- Test that single bound constraint on non-entry
//--- non-exit elements of P is satisfied.
//--- NOTE 1: this test needs N>=4 because smaller values
//--- can give us inconsistent constraints
if(!CAp::Assert(n>=4,"TestBC: expectation failed"))
return;
//--- change values
ic=1+CMath::RandomInteger(n-2);
jc=1+CMath::RandomInteger(n-2);
//--- check
if(CMath::RandomReal()>0.5)
vl=0.3*CMath::RandomReal();
else
vl=CInfOrNaN::NegativeInfinity();
//--- check
if(CMath::RandomReal()>0.5)
vu=0.5+0.3*CMath::RandomReal();
else
vu=CInfOrNaN::PositiveInfinity();
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDAddBC(s,ic,jc,vl,vu);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- check
if(rep.m_terminationtype>0)
{
err=err || p[ic][jc]<vl;
err=err || p[ic][jc]>vu;
}
else
err=true;
//--- Test interaction with default "sum-to-one" constraint
//--- on columns of P.
//--- We set N-1 bound constraints on random non-exit column
//--- of P,which are inconsistent with this default constraint
//--- (sum will be greater that 1.0).
//--- Algorithm must detect inconsistency.
//--- NOTE:
//--- 1. we do not set constraints for the first element of
//--- the column,because this element may be constrained by
//--- "exit state" constraint.
//--- 2. this test needs N>=3
if(!CAp::Assert(n>=3,"TestEC: expectation failed"))
return;
jc=CMath::RandomInteger(n-1);
vl=0.85;
vu=0.95;
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
for(i=1;i<=n-1;i++)
CMarkovCPD::MCPDAddBC(s,i,jc,vl,vu);
//--- function calls
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- search errors
err=err || rep.m_terminationtype!=-3;
//--- Test interaction with constrains on entry states.
//--- When model has entry state,corresponding row of P
//--- must be zero. We try to set two kinds of constraints
//--- on random element of this row:
//--- * bound constraint with zero lower bound,which must be consistent
//--- * bound constraint with non-zero lower bound,which must be inconsistent
if(entrystate>=0)
{
jc=CMath::RandomInteger(n);
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDAddBC(s,entrystate,jc,0.0,1.0);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- search errors
err=err || rep.m_terminationtype<=0;
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDAddBC(s,entrystate,jc,0.5,1.0);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- search errors
err=err || rep.m_terminationtype!=-3;
}
//--- Test interaction with constrains on exit states.
//--- When model has exit state,corresponding column of P
//--- must be zero. We try to set two kinds of constraints
//--- on random element of this column:
//--- * bound constraint with zero lower bound,which must be consistent
//--- * bound constraint with non-zero lower bound,which must be inconsistent
if(exitstate>=0)
{
ic=CMath::RandomInteger(n);
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDAddBC(s,ic,exitstate,0.0,1.0);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- search errors
err=err || rep.m_terminationtype<=0;
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDAddBC(s,ic,exitstate,0.5,1.0);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- search errors
err=err || rep.m_terminationtype!=-3;
}
//--- Test SetBC() call - we constrain subset of non-entry
//--- non-exit elements and test it.
if(!CAp::Assert(n>=4,"TestBC: expectation failed"))
return;
//--- allocation
bndl.Resize(n,n);
bndu.Resize(n,n);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
bndl[i].Set(j,CInfOrNaN::NegativeInfinity());
bndu[i].Set(j,CInfOrNaN::PositiveInfinity());
}
}
//--- change values
for(j=1;j<=n-2;j++)
{
i=1+CMath::RandomInteger(n-2);
bndl[i].Set(j,0.5-0.1*CMath::RandomReal());
bndu[i].Set(j,0.5+0.1*CMath::RandomReal());
}
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDSetBC(s,bndl,bndu);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- check
if(rep.m_terminationtype>0)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
err=err || p[i][j]<bndl[i][j];
err=err || p[i][j]>bndu[i][j];
}
}
}
else
err=true;
}
}
}
}
//+------------------------------------------------------------------+
//| Test bound constraints. |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
static void CTestMCPDUnit::TestLC(bool &err)
{
//--- create variables
int n=0;
int entrystate=0;
int exitstate=0;
int entrykind=0;
int exitkind=0;
int i=0;
int j=0;
int k=0;
int t=0;
int jc=0;
double v=0;
double threshold=0;
//--- create array
int ct[];
//--- create matrix
CMatrixDouble p;
CMatrixDouble c;
CMatrixDouble xy;
//--- objects of classes
CMCPDState s;
CMCPDReport rep;
//--- initialization
threshold=1.0E5*CMath::m_machineepsilon;
//--- We try different problems with following properties:
//--- * N is large enough - we won't have problems with inconsistent constraints
//--- * first state is either "entry" or "normal"
//--- * last state is either "exit" or "normal"
//--- * we have one long random track
//--- We test several properties which are described in comments below
for(n=4;n<=6;n++)
{
for(entrykind=0;entrykind<=1;entrykind++)
{
for(exitkind=0;exitkind<=1;exitkind++)
{
//--- Prepare problem
if(entrykind==0)
entrystate=-1;
else
entrystate=0;
//--- check
if(exitkind==0)
exitstate=-1;
else
exitstate=n-1;
//--- allocation
xy.Resize(2*n,n);
for(i=0;i<=CAp::Rows(xy)-1;i++)
{
for(j=0;j<=CAp::Cols(xy)-1;j++)
xy[i].Set(j,CMath::RandomReal());
}
//--- Test that single linear equality/inequality constraint
//--- on non-entry non-exit elements of P is satisfied.
//--- NOTE 1: this test needs N>=4 because smaller values
//--- can give us inconsistent constraints
//--- NOTE 2: Constraints are generated is such a way that P=(1/N ... 1/N)
//--- is always feasible. It guarantees that there always exists
//--- at least one feasible point
//--- NOTE 3: If we have inequality constraint,we "shift" right part
//--- in order to make feasible some neighborhood of P=(1/N ... 1/N).
if(!CAp::Assert(n>=4,"TestLC: expectation failed"))
return;
//--- allocation
c.Resize(1,n*n+1);
ArrayResize(ct,1);
//--- calculation
v=0;
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(((i==0 || i==n-1) || j==0) || j==n-1)
c[0].Set(i*n+j,0);
else
{
c[0].Set(i*n+j,CMath::RandomReal());
v=v+c[0][i*n+j]*(1.0/(double)n);
}
}
}
//--- change value
c[0].Set(n*n,v);
ct[0]=CMath::RandomInteger(3)-1;
//--- check
if(ct[0]<0)
c[0].Set(n*n,c[0][n*n]+0.1);
//--- check
if(ct[0]>0)
c[0].Set(n*n,c[0][n*n]-0.1);
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDSetLC(s,c,ct,1);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- check
if(rep.m_terminationtype>0)
{
v=0;
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
v=v+p[i][j]*c[0][i*n+j];
}
//--- check
if(ct[0]<0)
err=err || v>=c[0][n*n]+threshold;
//--- check
if(ct[0]==0)
err=err || MathAbs(v-c[0][n*n])>=threshold;
//--- check
if(ct[0]>0)
err=err || v<=c[0][n*n]-threshold;
}
else
err=true;
//--- Test interaction with default "sum-to-one" constraint
//--- on columns of P.
//--- We set linear constraint which has for "sum-to-X" on
//--- on random non-exit column of P. This constraint can be
//--- either consistent (X=1.0) or inconsistent (X<>1.0) with
//--- this default constraint.
//--- Algorithm must detect inconsistency.
//--- NOTE:
//--- 1. this test needs N>=2
if(!CAp::Assert(n>=2,"TestLC: expectation failed"))
return;
jc=CMath::RandomInteger(n-1);
//--- allocation
c.Resize(1,n*n+1);
ArrayResize(ct,1);
//--- change values
for(i=0;i<=n*n-1;i++)
c[0].Set(i,0.0);
for(i=0;i<=n-1;i++)
c[0].Set(n*i+jc,1.0);
c[0].Set(n*n,1.0);
ct[0]=0;
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDSetLC(s,c,ct,1);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- search errors
err=err || rep.m_terminationtype<=0;
c[0].Set(n*n,2.0);
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDSetLC(s,c,ct,1);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- search errors
err=err || rep.m_terminationtype!=-3;
//--- Test interaction with constrains on entry states.
//--- When model has entry state,corresponding row of P
//--- must be zero. We try to set two kinds of constraints
//--- on elements of this row:
//--- * sums-to-zero constraint,which must be consistent
//--- * sums-to-one constraint,which must be inconsistent
if(entrystate>=0)
{
c.Resize(1,n*n+1);
ArrayResize(ct,1);
for(i=0;i<=n*n-1;i++)
c[0].Set(i,0.0);
for(j=0;j<=n-1;j++)
c[0].Set(n*entrystate+j,1.0);
ct[0]=0;
c[0].Set(n*n,0.0);
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDSetLC(s,c,ct,1);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- search errors
err=err || rep.m_terminationtype<=0;
c[0].Set(n*n,1.0);
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDSetLC(s,c,ct,1);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- search errors
err=err || rep.m_terminationtype!=-3;
}
//--- Test interaction with constrains on exit states.
//--- When model has exit state,corresponding column of P
//--- must be zero. We try to set two kinds of constraints
//--- on elements of this column:
//--- * sums-to-zero constraint,which must be consistent
//--- * sums-to-one constraint,which must be inconsistent
if(exitstate>=0)
{
c.Resize(1,n*n+1);
ArrayResize(ct,1);
//--- change values
for(i=0;i<=n*n-1;i++)
c[0].Set(i,0.0);
for(i=0;i<=n-1;i++)
c[0].Set(n*i+exitstate,1.0);
ct[0]=0;
c[0].Set(n*n,0.0);
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDSetLC(s,c,ct,1);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- search errors
err=err || rep.m_terminationtype<=0;
c[0].Set(n*n,1.0);
//--- function calls
CreateEE(n,entrystate,exitstate,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDSetLC(s,c,ct,1);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- search errors
err=err || rep.m_terminationtype!=-3;
}
}
}
}
//--- Final test - we generate several random constraints and
//--- test SetLC() function.
//--- NOTES:
//--- 1. Constraints are generated is such a way that P=(1/N ... 1/N)
//--- is always feasible. It guarantees that there always exists
//--- at least one feasible point
//--- 2. For simplicity of the test we do not use entry/exit states
//--- in our model
for(n=1;n<=4;n++)
{
for(k=1;k<=2*n;k++)
{
//--- Generate track
xy.Resize(2*n,n);
for(i=0;i<=CAp::Rows(xy)-1;i++)
{
for(j=0;j<=CAp::Cols(xy)-1;j++)
xy[i].Set(j,CMath::RandomReal());
}
//--- Generate random constraints
c.Resize(k,n*n+1);
ArrayResize(ct,k);
//--- calculation
for(i=0;i<=k-1;i++)
{
//--- Generate constraint and its right part
c[i].Set(n*n,0);
for(j=0;j<=n*n-1;j++)
{
c[i].Set(j,2*CMath::RandomReal()-1);
c[i].Set(n*n,c[i][n*n]+c[i][j]*(1.0/(double)n));
}
ct[i]=CMath::RandomInteger(3)-1;
//--- If we have inequality constraint,we "shift" right part
//--- in order to make feasible some neighborhood of P=(1/N ... 1/N).
if(ct[i]<0)
c[i].Set(n*n,c[i][n*n]+0.1);
//--- check
if(ct[i]>0)
c[i].Set(n*n,c[i][n*n]-0.1);
}
//--- Test
CreateEE(n,-1,-1,s);
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
CMarkovCPD::MCPDSetLC(s,c,ct,k);
CMarkovCPD::MCPDSolve(s);
CMarkovCPD::MCPDResults(s,p,rep);
//--- check
if(rep.m_terminationtype>0)
{
for(t=0;t<=k-1;t++)
{
//--- change values
v=0;
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
v=v+p[i][j]*c[t][i*n+j];
}
//--- check
if(ct[t]<0)
err=err || v>=c[t][n*n]+threshold;
//--- check
if(ct[t]==0)
err=err || MathAbs(v-c[t][n*n])>=threshold;
//--- check
if(ct[t]>0)
err=err || v<=c[t][n*n]-threshold;
}
}
else
err=true;
}
}
}
//+------------------------------------------------------------------+
//| This function is used to create MCPD object with arbitrary |
//| combination of entry and exit states |
//+------------------------------------------------------------------+
static void CTestMCPDUnit::CreateEE(const int n,const int entrystate,
const int exitstate,CMCPDState &s)
{
//--- check
if(entrystate<0 && exitstate<0)
CMarkovCPD::MCPDCreate(n,s);
//--- check
if(entrystate>=0 && exitstate<0)
CMarkovCPD::MCPDCreateEntry(n,entrystate,s);
//--- check
if(entrystate<0 && exitstate>=0)
CMarkovCPD::MCPDCreateExit(n,exitstate,s);
//--- check
if(entrystate>=0 && exitstate>=0)
CMarkovCPD::MCPDCreateEntryExit(n,entrystate,exitstate,s);
}
//+------------------------------------------------------------------+
//| Testing class CFbls |
//+------------------------------------------------------------------+
class CTestFblsUnit
{
public:
//--- constructor, destructor
CTestFblsUnit(void);
~CTestFblsUnit(void);
//--- public method
static bool TestFbls(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestFblsUnit::CTestFblsUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestFblsUnit::~CTestFblsUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing |
//+------------------------------------------------------------------+
static bool CTestFblsUnit::TestFbls(const bool silent)
{
//--- create variables
int n=0;
int m=0;
int mx=0;
int i=0;
int j=0;
bool waserrors;
bool cgerrors;
double v=0;
double v1=0;
double v2=0;
double alpha=0;
double e1=0;
double e2=0;
int i_=0;
//--- create arrays
double tmp1[];
double tmp2[];
double b[];
double x[];
double xe[];
double buf[];
//--- create matrix
CMatrixDouble a;
//--- object of class
CFblsLinCgState cgstate;
//--- initialization
mx=10;
waserrors=false;
cgerrors=false;
//--- Test CG solver:
//--- * generate problem (A,B,Alpha,XE - exact solution) and initial approximation X
//--- * E1=||A'A*x-b||
//--- * solve
//--- * E2=||A'A*x-b||
//--- * test that E2<0.001*E1
for(n=1;n<=mx;n++)
{
for(m=1;m<=mx;m++)
{
//--- allocation
a.Resize(m,n);
ArrayResize(b,n);
ArrayResize(x,n);
ArrayResize(xe,n);
ArrayResize(tmp1,m);
ArrayResize(tmp2,n);
//--- init A,alpha,B,X (initial approximation),XE (exact solution)
//--- X is initialized in such way that is has no chances to be equal to XE.
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- change values
alpha=CMath::RandomReal()+0.1;
for(i=0;i<=n-1;i++)
{
b[i]=2*CMath::RandomReal()-1;
xe[i]=2*CMath::RandomReal()-1;
x[i]=(2*CMath::RandomInteger(2)-1)*(2+CMath::RandomReal());
}
//--- Test dense CG (which solves A'A*x=b and accepts dense A)
for(i=0;i<=n-1;i++)
x[i]=(2*CMath::RandomInteger(2)-1)*(2+CMath::RandomReal());
//--- function calls
CAblas::RMatrixMVect(m,n,a,0,0,0,x,0,tmp1,0);
CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,tmp2,0);
//--- calculation
for(i_=0;i_<=n-1;i_++)
tmp2[i_]=tmp2[i_]+alpha*x[i_];
for(i_=0;i_<=n-1;i_++)
tmp2[i_]=tmp2[i_]-b[i_];
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=tmp2[i_]*tmp2[i_];
e1=MathSqrt(v);
//--- function calls
CFbls::FblsSolveCGx(a,m,n,alpha,b,x,buf);
CAblas::RMatrixMVect(m,n,a,0,0,0,x,0,tmp1,0);
CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,tmp2,0);
//--- calculation
for(i_=0;i_<=n-1;i_++)
tmp2[i_]=tmp2[i_]+alpha*x[i_];
for(i_=0;i_<=n-1;i_++)
tmp2[i_]=tmp2[i_]-b[i_];
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=tmp2[i_]*tmp2[i_];
e2=MathSqrt(v);
//--- search errors
cgerrors=cgerrors || e2>0.001*e1;
//--- Test sparse CG (which relies on reverse communication)
for(i=0;i<=n-1;i++)
x[i]=(2*CMath::RandomInteger(2)-1)*(2+CMath::RandomReal());
//--- function calls
CAblas::RMatrixMVect(m,n,a,0,0,0,x,0,tmp1,0);
CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,tmp2,0);
//--- calculation
for(i_=0;i_<=n-1;i_++)
tmp2[i_]=tmp2[i_]+alpha*x[i_];
for(i_=0;i_<=n-1;i_++)
tmp2[i_]=tmp2[i_]-b[i_];
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=tmp2[i_]*tmp2[i_];
e1=MathSqrt(v);
//--- function call
CFbls::FblsCGCreate(x,b,n,cgstate);
//--- cycle
while(CFbls::FblsCGIteration(cgstate))
{
//--- function calls
CAblas::RMatrixMVect(m,n,a,0,0,0,cgstate.m_x,0,tmp1,0);
CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,cgstate.m_ax,0);
for(i_=0;i_<=n-1;i_++)
cgstate.m_ax[i_]=cgstate.m_ax[i_]+alpha*cgstate.m_x[i_];
//--- change values
v1=0.0;
for(i_=0;i_<=m-1;i_++)
v1+=tmp1[i_]*tmp1[i_];
v2=0.0;
for(i_=0;i_<=n-1;i_++)
v2+=cgstate.m_x[i_]*cgstate.m_x[i_];
cgstate.m_xax=v1+alpha*v2;
}
//--- function calls
CAblas::RMatrixMVect(m,n,a,0,0,0,cgstate.m_xk,0,tmp1,0);
CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,tmp2,0);
//--- calculation
for(i_=0;i_<=n-1;i_++)
tmp2[i_]=tmp2[i_]+alpha*cgstate.m_xk[i_];
for(i_=0;i_<=n-1;i_++)
tmp2[i_]=tmp2[i_]-b[i_];
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=tmp2[i_]*tmp2[i_];
e2=MathSqrt(v);
//--- search errors
cgerrors=cgerrors || MathAbs(e1-cgstate.m_e1)>100*CMath::m_machineepsilon*e1;
cgerrors=cgerrors || MathAbs(e2-cgstate.m_e2)>100*CMath::m_machineepsilon*e1;
cgerrors=cgerrors || e2>0.001*e1;
}
}
//--- report
waserrors=cgerrors;
//--- check
if(!silent)
{
Print("TESTING FBLS");
Print("CG ERRORS: ");
//--- check
if(cgerrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CMinLBFGS |
//+------------------------------------------------------------------+
class CTestMinLBFGSUnit
{
private:
//--- private methods
static void TestFunc1(CMinLBFGSState &state);
static void TestFunc2(CMinLBFGSState &state);
static void TestFunc3(CMinLBFGSState &state);
static void CalcIIP2(CMinLBFGSState &state,const int n);
static void TestPreconditioning(bool &err);
static void TestOther(bool &err);
public:
//--- constructor, destructor
CTestMinLBFGSUnit(void);
~CTestMinLBFGSUnit(void);
//--- public method
static bool TestMinLBFGS(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestMinLBFGSUnit::CTestMinLBFGSUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestMinLBFGSUnit::~CTestMinLBFGSUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CMinLBFGS |
//+------------------------------------------------------------------+
static bool CTestMinLBFGSUnit::TestMinLBFGS(const bool silent)
{
//--- create variables
bool waserrors;
bool referror;
bool nonconverror;
bool eqerror;
bool converror;
bool crashtest;
bool othererrors;
bool restartserror;
bool precerror;
int n=0;
int m=0;
int i=0;
int j=0;
double v=0;
int maxits=0;
double diffstep=0;
int dkind=0;
int i_=0;
//--- create arrays
double x[];
double xe[];
double b[];
double xlast[];
double diagh[];
//--- create matrix
CMatrixDouble a;
//--- objects of classes
CMinLBFGSState state;
CMinLBFGSReport rep;
//--- initialization
waserrors=false;
precerror=false;
nonconverror=false;
restartserror=false;
eqerror=false;
converror=false;
crashtest=false;
othererrors=false;
referror=false;
//--- function calls
TestPreconditioning(precerror);
TestOther(othererrors);
//--- Reference problem
diffstep=1.0E-6;
for(dkind=0;dkind<=1;dkind++)
{
//--- allocation
ArrayResize(x,3);
n=3;
m=2;
x[0]=100*CMath::RandomReal()-50;
x[1]=100*CMath::RandomReal()-50;
x[2]=100*CMath::RandomReal()-50;
//--- check
if(dkind==0)
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
//--- check
if(dkind==1)
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
//--- function call
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,0);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=CMath::Sqr(state.m_x[0]-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
//--- check
if(state.m_needfg)
{
state.m_g[0]=2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2]);
state.m_g[1]=2*state.m_x[1];
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
}
}
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- search errors
referror=((rep.m_terminationtype<=0 || MathAbs(x[0]-2)>0.001) || MathAbs(x[1])>0.001) || MathAbs(x[2]-2)>0.001;
}
//--- nonconvex problems with complex surface: we start from point with very small
//--- gradient,but we need ever smaller gradient in the next step due to
//--- Wolfe conditions.
diffstep=1.0E-6;
for(dkind=0;dkind<=1;dkind++)
{
//--- allocation
ArrayResize(x,1);
n=1;
m=1;
v=-100;
//--- calculation
while(v<0.1)
{
x[0]=v;
//--- check
if(dkind==0)
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
//--- check
if(dkind==1)
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
//--- function call
CMinLBFGS::MinLBFGSSetCond(state,1.0E-9,0,0,0);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=CMath::Sqr(state.m_x[0])/(1+CMath::Sqr(state.m_x[0]));
//--- check
if(state.m_needfg)
state.m_g[0]=(2*state.m_x[0]*(1+CMath::Sqr(state.m_x[0]))-CMath::Sqr(state.m_x[0])*2*state.m_x[0])/CMath::Sqr(1+CMath::Sqr(state.m_x[0]));
}
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- search errors
nonconverror=(nonconverror || rep.m_terminationtype<=0) || MathAbs(x[0])>0.001;
v=v+0.1;
}
}
//--- F2 problem with restarts:
//--- * make several iterations and restart BEFORE termination
//--- * iterate and restart AFTER termination
//--- NOTE: step is bounded from above to avoid premature convergence
diffstep=1.0E-6;
for(dkind=0;dkind<=1;dkind++)
{
//--- allocation
ArrayResize(x,3);
//--- change values
n=3;
m=2;
x[0]=10+10*CMath::RandomReal();
x[1]=10+10*CMath::RandomReal();
x[2]=10+10*CMath::RandomReal();
//--- check
if(dkind==0)
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
//--- check
if(dkind==1)
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
//--- function calls
CMinLBFGS::MinLBFGSSetStpMax(state,0.1);
CMinLBFGS::MinLBFGSSetCond(state,0.0000001,0.0,0.0,0);
//--- calculation
for(i=0;i<=10;i++)
{
//--- check
if(!CMinLBFGS::MinLBFGSIteration(state))
break;
TestFunc2(state);
}
//--- change values
x[0]=10+10*CMath::RandomReal();
x[1]=10+10*CMath::RandomReal();
x[2]=10+10*CMath::RandomReal();
//--- function call
CMinLBFGS::MinLBFGSRestartFrom(state,x);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
TestFunc2(state);
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- search errors
restartserror=(((restartserror || rep.m_terminationtype<=0) || MathAbs(x[0]-MathLog(2))>0.01) || MathAbs(x[1])>0.01) || MathAbs(x[2]-MathLog(2))>0.01;
//--- change values
x[0]=10+10*CMath::RandomReal();
x[1]=10+10*CMath::RandomReal();
x[2]=10+10*CMath::RandomReal();
//--- function call
CMinLBFGS::MinLBFGSRestartFrom(state,x);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
TestFunc2(state);
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- search errors
restartserror=(((restartserror || rep.m_terminationtype<=0) || MathAbs(x[0]-MathLog(2))>0.01) || MathAbs(x[1])>0.01) || MathAbs(x[2]-MathLog(2))>0.01;
}
//--- Linear equations
diffstep=1.0E-6;
for(n=1;n<=10;n++)
{
//--- Prepare task
a.Resize(n,n);
ArrayResize(x,n);
ArrayResize(xe,n);
ArrayResize(b,n);
//--- change values
for(i=0;i<=n-1;i++)
xe[i]=2*CMath::RandomReal()-1;
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
a[i].Set(i,a[i][i]+3*MathSign(a[i][i]));
}
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*xe[i_];
b[i]=v;
}
//--- Test different M/DKind
for(m=1;m<=n;m++)
{
for(dkind=0;dkind<=1;dkind++)
{
//--- Solve task
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- check
if(dkind==0)
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
//--- check
if(dkind==1)
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
//--- function call
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,0);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=0;
//--- check
if(state.m_needfg)
{
for(i=0;i<=n-1;i++)
state.m_g[i]=0;
}
for(i=0;i<=n-1;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*state.m_x[i_];
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=state.m_f+CMath::Sqr(v-b[i]);
//--- check
if(state.m_needfg)
{
for(j=0;j<=n-1;j++)
state.m_g[j]=state.m_g[j]+2*(v-b[i])*a[i][j];
}
}
}
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- search errors
eqerror=eqerror || rep.m_terminationtype<=0;
for(i=0;i<=n-1;i++)
eqerror=eqerror || MathAbs(x[i]-xe[i])>0.001;
}
}
}
//--- Testing convergence properties
diffstep=1.0E-6;
for(dkind=0;dkind<=1;dkind++)
{
//--- allocation
ArrayResize(x,3);
//--- change values
n=3;
m=2;
for(i=0;i<=2;i++)
x[i]=6*CMath::RandomReal()-3;
//--- check
if(dkind==0)
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
//--- check
if(dkind==1)
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
//--- function call
CMinLBFGS::MinLBFGSSetCond(state,0.001,0,0,0);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
TestFunc3(state);
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- search errors
converror=converror || rep.m_terminationtype!=4;
//--- change values
for(i=0;i<=2;i++)
x[i]=6*CMath::RandomReal()-3;
//--- check
if(dkind==0)
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
//--- check
if(dkind==1)
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
//--- function call
CMinLBFGS::MinLBFGSSetCond(state,0,0.001,0,0);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
TestFunc3(state);
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- search errors
converror=converror || rep.m_terminationtype!=1;
//--- change values
for(i=0;i<=2;i++)
x[i]=6*CMath::RandomReal()-3;
//--- check
if(dkind==0)
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
//--- check
if(dkind==1)
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
//--- function call
CMinLBFGS::MinLBFGSSetCond(state,0,0,0.001,0);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
TestFunc3(state);
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- search errors
converror=converror || rep.m_terminationtype!=2;
//--- change values
for(i=0;i<=2;i++)
x[i]=2*CMath::RandomReal()-1;
//--- check
if(dkind==0)
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
//--- check
if(dkind==1)
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
//--- function call
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,10);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
TestFunc3(state);
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- search errors
converror=(converror || rep.m_terminationtype!=5) || rep.m_iterationscount!=10;
}
//--- Crash test: too many iterations on a simple tasks
//--- May fail when encounter zero step,underflow or something like that
ArrayResize(x,3);
n=3;
m=2;
maxits=10000;
for(i=0;i<=2;i++)
x[i]=6*CMath::RandomReal()-3;
//--- function calls
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,maxits);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
{
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
state.m_g[1]=2*state.m_x[1];
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
}
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- search errors
crashtest=crashtest || rep.m_terminationtype<=0;
//--- end
waserrors=((((((referror || nonconverror) || eqerror) || converror) || crashtest) || othererrors) || restartserror) || precerror;
//--- check
if(!silent)
{
Print("TESTING L-BFGS OPTIMIZATION");
Print("REFERENCE PROBLEM: ");
//--- check
if(referror)
Print("FAILED");
else
Print("OK");
Print("NON-CONVEX PROBLEM: ");
//--- check
if(nonconverror)
Print("FAILED");
else
Print("OK");
Print("LINEAR EQUATIONS: ");
//--- check
if(eqerror)
Print("FAILED");
else
Print("OK");
Print("RESTARTS: ");
//--- check
if(restartserror)
Print("FAILED");
else
Print("OK");
Print("PRECONDITIONER: ");
//--- check
if(precerror)
Print("FAILED");
else
Print("OK");
Print("CONVERGENCE PROPERTIES: ");
//--- check
if(converror)
Print("FAILED");
else
Print("OK");
Print("CRASH TEST: ");
//--- check
if(crashtest)
Print("FAILED");
else
Print("OK");
Print("OTHER PROPERTIES: ");
//--- check
if(othererrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Calculate test function #1 |
//| It may show very interesting behavior when optimized with |
//| 'x[0]>=ln(2)' constraint. |
//+------------------------------------------------------------------+
static void CTestMinLBFGSUnit::TestFunc1(CMinLBFGSState &state)
{
//--- check
if(state.m_x[0]<100.0)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
//--- check
if(state.m_needfg)
{
//--- calculation
state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
state.m_g[1]=2*state.m_x[1];
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
}
}
else
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=MathSqrt(CMath::m_maxrealnumber);
//--- check
if(state.m_needfg)
{
//--- calculation
state.m_g[0]=MathSqrt(CMath::m_maxrealnumber);
state.m_g[1]=0;
state.m_g[2]=0;
}
}
}
//+------------------------------------------------------------------+
//| Calculate test function #2 |
//| Simple variation of #1,much more nonlinear,which makes unlikely |
//| premature convergence of algorithm. |
//+------------------------------------------------------------------+
static void CTestMinLBFGSUnit::TestFunc2(CMinLBFGSState &state)
{
//--- check
if(state.m_x[0]<100.0)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1]))+CMath::Sqr(state.m_x[2]-state.m_x[0]);
//--- check
if(state.m_needfg)
{
//--- calculation
state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
state.m_g[1]=4*state.m_x[1]*CMath::Sqr(state.m_x[1]);
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
}
}
else
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=MathSqrt(CMath::m_maxrealnumber);
//--- check
if(state.m_needfg)
{
//--- calculation
state.m_g[0]=MathSqrt(CMath::m_maxrealnumber);
state.m_g[1]=0;
state.m_g[2]=0;
}
}
}
//+------------------------------------------------------------------+
//| Calculate test function #3 |
//| Simple variation of #1, much more nonlinear, with non-zero value |
//| at minimum. It achieve two goals: |
//| * makes unlikely premature convergence of algorithm . |
//| * solves some issues with EpsF stopping condition which arise |
//| when F(minimum) is zero |
//+------------------------------------------------------------------+
static void CTestMinLBFGSUnit::TestFunc3(CMinLBFGSState &state)
{
//--- create a variable
double s=0;
//--- initialization
s=0.001;
//--- check
if(state.m_x[0]<100.0)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1])+s)+CMath::Sqr(state.m_x[2]-state.m_x[0]);
//--- check
if(state.m_needfg)
{
//--- calculation
state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
state.m_g[1]=2*(CMath::Sqr(state.m_x[1])+s)*2*state.m_x[1];
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
}
}
else
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=MathSqrt(CMath::m_maxrealnumber);
//--- check
if(state.m_needfg)
{
//--- calculation
state.m_g[0]=MathSqrt(CMath::m_maxrealnumber);
state.m_g[1]=0;
state.m_g[2]=0;
}
}
}
//+------------------------------------------------------------------+
//| Calculate test function IIP2 |
//| f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1) |
//| It has high condition number which makes fast convergence |
//| unlikely without good preconditioner. |
//+------------------------------------------------------------------+
static void CTestMinLBFGSUnit::CalcIIP2(CMinLBFGSState &state,const int n)
{
//--- create variables
int i=0;
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=state.m_f+CMath::Sqr(i*i+1)*CMath::Sqr(state.m_x[i]);
//--- check
if(state.m_needfg)
state.m_g[i]=CMath::Sqr(i*i+1)*2*state.m_x[i];
}
}
//+------------------------------------------------------------------+
//| This function tests preconditioning |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
static void CTestMinLBFGSUnit::TestPreconditioning(bool &err)
{
//--- create variables
int pass=0;
int n=0;
int m=0;
int i=0;
int j=0;
int k=0;
int cntb1=0;
int cntb2=0;
int cntg1=0;
int cntg2=0;
double epsg=0;
int pkind=0;
//--- create arrays
double x[];
double s[];
double diagh[];
//--- create matrix
CMatrixDouble a;
//--- objects of classes
CMinLBFGSState state;
CMinLBFGSReport rep;
//--- initialization
m=1;
k=50;
epsg=1.0E-10;
//--- Preconditioner test1.
//--- If
//--- * B1 is default preconditioner
//--- * B2 is Cholesky preconditioner with unit diagonal
//--- * G1 is Cholesky preconditioner based on exact Hessian with perturbations
//--- * G2 is diagonal precomditioner based on approximate diagonal of Hessian matrix
//--- then "bad" preconditioners (B1/B2/..) are worse than "good" ones (G1/G2/..).
//--- "Worse" means more iterations to converge.
//--- We test it using f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1) and L-BFGS
//--- optimizer with deliberately small M=1.
//--- N - problem size
//--- PKind - zero for upper triangular preconditioner,one for lower triangular.
//--- K - number of repeated passes (should be large enough to average out random factors)
for(n=10;n<=15;n++)
{
pkind=CMath::RandomInteger(2);
ArrayResize(x,n);
for(i=0;i<=n-1;i++)
x[i]=0;
//--- function call
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
//--- Test it with default preconditioner
CMinLBFGS::MinLBFGSSetPrecDefault(state);
cntb1=0;
for(pass=0;pass<=k-1;pass++)
{
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinLBFGS::MinLBFGSRestartFrom(state,x);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
CalcIIP2(state,n);
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
cntb1=cntb1+rep.m_iterationscount;
//--- search errors
err=err || rep.m_terminationtype<=0;
}
//--- Test it with unit preconditioner
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(i==j)
a[i].Set(i,1);
else
a[i].Set(j,0);
}
}
//--- function call
CMinLBFGS::MinLBFGSSetPrecCholesky(state,a,pkind==0);
//--- change values
cntb2=0;
for(pass=0;pass<=k-1;pass++)
{
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinLBFGS::MinLBFGSRestartFrom(state,x);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
CalcIIP2(state,n);
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
cntb2=cntb2+rep.m_iterationscount;
//--- search errors
err=err || rep.m_terminationtype<=0;
}
//--- Test it with perturbed Hessian preconditioner
a.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(i==j)
a[i].Set(i,(i*i+1)*(0.8+0.4*CMath::RandomReal()));
else
{
//--- check
if((pkind==0 && j>i) || (pkind==1 && j<i))
a[i].Set(j,0.1*CMath::RandomReal()-0.05);
else
a[i].Set(j,CInfOrNaN::NaN());
}
}
}
//--- function call
CMinLBFGS::MinLBFGSSetPrecCholesky(state,a,pkind==0);
cntg1=0;
for(pass=0;pass<=k-1;pass++)
{
//--- change values
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinLBFGS::MinLBFGSRestartFrom(state,x);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
CalcIIP2(state,n);
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
cntg1=cntg1+rep.m_iterationscount;
//--- search errors
err=err || rep.m_terminationtype<=0;
}
//--- Test it with perturbed diagonal preconditioner
ArrayResize(diagh,n);
for(i=0;i<=n-1;i++)
diagh[i]=2*CMath::Sqr(i*i+1)*(0.8+0.4*CMath::RandomReal());
//--- function call
CMinLBFGS::MinLBFGSSetPrecDiag(state,diagh);
cntg2=0;
for(pass=0;pass<=k-1;pass++)
{
//--- change values
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinLBFGS::MinLBFGSRestartFrom(state,x);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
CalcIIP2(state,n);
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
cntg2=cntg2+rep.m_iterationscount;
//--- search errors
err=err || rep.m_terminationtype<=0;
}
//--- Compare
err=err || cntb1<cntg1;
err=err || cntb2<cntg1;
err=err || cntb1<cntg2;
err=err || cntb2<cntg2;
}
//--- Preconditioner test 2.
//--- If
//--- * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
//--- * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
//--- then B2 is worse than G2.
//--- "Worse" means more iterations to converge.
for(n=10;n<=15;n++)
{
//--- allocation
ArrayResize(x,n);
for(i=0;i<=n-1;i++)
x[i]=0;
//--- function call
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
//--- allocation
ArrayResize(s,n);
for(i=0;i<=n-1;i++)
s[i]=1/MathSqrt(2*MathPow(i*i+1,2)*(0.8+0.4*CMath::RandomReal()));
//--- function calls
CMinLBFGS::MinLBFGSSetPrecDefault(state);
CMinLBFGS::MinLBFGSSetScale(state,s);
cntb2=0;
//--- calculation
for(pass=0;pass<=k-1;pass++)
{
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinLBFGS::MinLBFGSRestartFrom(state,x);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
CalcIIP2(state,n);
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
cntb2=cntb2+rep.m_iterationscount;
//--- search errors
err=err || rep.m_terminationtype<=0;
}
//--- function calls
CMinLBFGS::MinLBFGSSetPrecScale(state);
CMinLBFGS::MinLBFGSSetScale(state,s);
cntg2=0;
//--- calculation
for(pass=0;pass<=k-1;pass++)
{
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinLBFGS::MinLBFGSRestartFrom(state,x);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
CalcIIP2(state,n);
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
cntg2=cntg2+rep.m_iterationscount;
//--- search errors
err=err || rep.m_terminationtype<=0;
}
//--- search errors
err=err || cntb2<cntg2;
}
}
//+------------------------------------------------------------------+
//| This function tests other properties |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
static void CTestMinLBFGSUnit::TestOther(bool &err)
{
//--- create variables
int n=0;
int m=0;
bool hasxlast;
double lastscaledstep=0;
int i=0;
double fprev=0;
double xprev=0;
double v=0;
double stpmax=0;
double tmpeps=0;
double epsg=0;
int pkind=0;
int ckind=0;
int mkind=0;
double vc=0;
double vm=0;
double diffstep=0;
int dkind=0;
bool wasf;
bool wasfg;
double r=0;
int i_=0;
//--- create arrays
double x[];
double a[];
double s[];
double h[];
double xlast[];
//--- objects of classes
CMinLBFGSState state;
CMinLBFGSReport rep;
//--- Test reports (F should form monotone sequence)
n=50;
m=2;
//--- allocation
ArrayResize(x,n);
ArrayResize(xlast,n);
for(i=0;i<=n-1;i++)
x[i]=1;
//--- function calls
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,100);
CMinLBFGS::MinLBFGSSetXRep(state,true);
fprev=CMath::m_maxrealnumber;
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=0;
for(i=0;i<=n-1;i++)
{
state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]);
state.m_g[i]=2*(1+i)*state.m_x[i];
}
}
//--- check
if(state.m_xupdated)
{
err=err || state.m_f>fprev;
//--- check
if(fprev==CMath::m_maxrealnumber)
{
for(i=0;i<=n-1;i++)
err=err || state.m_x[i]!=x[i];
}
//--- change values
fprev=state.m_f;
for(i_=0;i_<=n-1;i_++)
xlast[i_]=state.m_x[i_];
}
}
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- search errors
for(i=0;i<=n-1;i++)
err=err || x[i]!=xlast[i];
//--- Test differentiation vs. analytic gradient
//--- (first one issues NeedF requests,second one issues NeedFG requests)
n=50;
m=5;
diffstep=1.0E-6;
//--- calculation
for(dkind=0;dkind<=1;dkind++)
{
//--- allocation
ArrayResize(x,n);
ArrayResize(xlast,n);
for(i=0;i<=n-1;i++)
x[i]=1;
//--- check
if(dkind==0)
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
//--- check
if(dkind==1)
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
//--- function call
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,n/2);
wasf=false;
wasfg=false;
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=0;
for(i=0;i<=n-1;i++)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]);
//--- check
if(state.m_needfg)
state.m_g[i]=2*(1+i)*state.m_x[i];
}
//--- search errors
wasf=wasf || state.m_needf;
wasfg=wasfg || state.m_needfg;
}
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- check
if(dkind==0)
err=(err || wasf) || !wasfg;
//--- check
if(dkind==1)
err=(err || !wasf) || wasfg;
}
//--- Test that numerical differentiation uses scaling.
//--- In order to test that we solve simple optimization
//--- problem: min(x^2) with initial x equal to 0.0.
//--- We choose random DiffStep and S,then we check that
//--- optimizer evaluates function at +-DiffStep*S only.
ArrayResize(x,1);
ArrayResize(s,1);
//--- change values
diffstep=CMath::RandomReal()*1.0E-6;
s[0]=MathExp(CMath::RandomReal()*4-2);
x[0]=0;
//--- function calls
CMinLBFGS::MinLBFGSCreateF(1,1,x,diffstep,state);
CMinLBFGS::MinLBFGSSetCond(state,1.0E-6,0,0,0);
CMinLBFGS::MinLBFGSSetScale(state,s);
v=0;
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
{
state.m_f=CMath::Sqr(state.m_x[0]);
v=MathMax(v,MathAbs(state.m_x[0]));
}
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
r=v/(s[0]*diffstep);
//--- search errors
err=err || MathAbs(MathLog(r))>MathLog(1+1000*CMath::m_machineepsilon);
//--- test maximum step
n=1;
m=1;
//--- allocation
ArrayResize(x,n);
x[0]=100;
stpmax=0.05+0.05*CMath::RandomReal();
//--- function calls
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
CMinLBFGS::MinLBFGSSetCond(state,1.0E-9,0,0,0);
CMinLBFGS::MinLBFGSSetStpMax(state,stpmax);
CMinLBFGS::MinLBFGSSetXRep(state,true);
xprev=x[0];
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]);
state.m_g[0]=MathExp(state.m_x[0])-MathExp(-state.m_x[0]);
//--- search errors
err=err || MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax);
}
//--- check
if(state.m_xupdated)
{
//--- search errors
err=err || MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax);
xprev=state.m_x[0];
}
}
//--- Test correctness of the scaling:
//--- * initial point is random point from [+1,+2]^N
//--- * f(x)=SUM(A[i]*x[i]^4),C[i] is random from [0.01,100]
//--- * we use random scaling matrix
//--- * we test different variants of the preconditioning:
//--- 0) unit preconditioner
//--- 1) random diagonal from [0.01,100]
//--- 2) scale preconditioner
//--- * we set stringent stopping conditions (we try EpsG and EpsX)
//--- * and we test that in the extremum stopping conditions are
//--- satisfied subject to the current scaling coefficients.
tmpeps=1.0E-10;
m=1;
for(n=1;n<=10;n++)
{
for(pkind=0;pkind<=2;pkind++)
{
//--- allocation
ArrayResize(x,n);
ArrayResize(xlast,n);
ArrayResize(a,n);
ArrayResize(s,n);
ArrayResize(h,n);
//--- change values
for(i=0;i<=n-1;i++)
{
x[i]=CMath::RandomReal()+1;
a[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
s[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
h[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
}
//--- function call
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
CMinLBFGS::MinLBFGSSetScale(state,s);
CMinLBFGS::MinLBFGSSetXRep(state,true);
//--- check
if(pkind==1)
CMinLBFGS::MinLBFGSSetPrecDiag(state,h);
//--- check
if(pkind==2)
CMinLBFGS::MinLBFGSSetPrecScale(state);
//--- Test gradient-based stopping condition
for(i=0;i<=n-1;i++)
x[i]=CMath::RandomReal()+1;
//--- function calls
CMinLBFGS::MinLBFGSSetCond(state,tmpeps,0,0,0);
CMinLBFGS::MinLBFGSRestartFrom(state,x);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=0;
for(i=0;i<=n-1;i++)
{
state.m_f=state.m_f+a[i]*MathPow(state.m_x[i],4);
state.m_g[i]=4*a[i]*MathPow(state.m_x[i],3);
}
}
}
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- check
if(rep.m_terminationtype<=0)
{
err=true;
return;
}
//--- change value
v=0;
for(i=0;i<=n-1;i++)
v=v+CMath::Sqr(s[i]*4*a[i]*MathPow(x[i],3));
v=MathSqrt(v);
//--- search errors
err=err || v>tmpeps;
//--- Test step-based stopping condition
for(i=0;i<=n-1;i++)
x[i]=CMath::RandomReal()+1;
hasxlast=false;
//--- function call
CMinLBFGS::MinLBFGSSetCond(state,0,0,tmpeps,0);
CMinLBFGS::MinLBFGSRestartFrom(state,x);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
{
//--- check
if(state.m_needfg)
{
state.m_f=0;
for(i=0;i<=n-1;i++)
{
state.m_f=state.m_f+a[i]*MathPow(state.m_x[i],4);
state.m_g[i]=4*a[i]*MathPow(state.m_x[i],3);
}
}
//--- check
if(state.m_xupdated)
{
//--- check
if(hasxlast)
{
lastscaledstep=0;
for(i=0;i<=n-1;i++)
lastscaledstep=lastscaledstep+CMath::Sqr(state.m_x[i]-xlast[i])/CMath::Sqr(s[i]);
lastscaledstep=MathSqrt(lastscaledstep);
}
else
lastscaledstep=0;
//--- change values
for(i_=0;i_<=n-1;i_++)
xlast[i_]=state.m_x[i_];
hasxlast=true;
}
}
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- check
if(rep.m_terminationtype<=0)
{
err=true;
return;
}
//--- search errors
err=err || lastscaledstep>tmpeps;
}
}
//--- Check correctness of the "trimming".
//--- Trimming is a technique which is used to help algorithm
//--- cope with unbounded functions. In order to check this
//--- technique we will try to solve following optimization
//--- problem:
//--- min f(x) subject to no constraints on X
//--- { 1/(1-x) + 1/(1+x) + c*x,if -0.999999<x<0.999999
//--- f(x)={
//--- { M,if x<=-0.999999 or x>=0.999999
//--- where c is either 1.0 or 1.0E+6,M is either 1.0E8,1.0E20 or +INF
//--- (we try different combinations)
for(ckind=0;ckind<=1;ckind++)
{
for(mkind=0;mkind<=2;mkind++)
{
//--- Choose c and M
if(ckind==0)
vc=1.0;
//--- check
if(ckind==1)
vc=1.0E+6;
//--- check
if(mkind==0)
vm=1.0E+8;
//--- check
if(mkind==1)
vm=1.0E+20;
//--- check
if(mkind==2)
vm=CInfOrNaN::PositiveInfinity();
//--- Create optimizer,solve optimization problem
epsg=1.0E-6*vc;
ArrayResize(x,1);
x[0]=0.0;
//--- function calls
CMinLBFGS::MinLBFGSCreate(1,1,x,state);
CMinLBFGS::MinLBFGSSetCond(state,epsg,0,0,0);
//--- cycle
while(CMinLBFGS::MinLBFGSIteration(state))
{
//--- check
if(state.m_needfg)
{
//--- check
if(-0.999999<state.m_x[0] && state.m_x[0]<0.999999)
{
state.m_f=1/(1-state.m_x[0])+1/(1+state.m_x[0])+vc*state.m_x[0];
state.m_g[0]=1/CMath::Sqr(1-state.m_x[0])-1/CMath::Sqr(1+state.m_x[0])+vc;
}
else
state.m_f=vm;
}
}
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- check
if(rep.m_terminationtype<=0)
{
err=true;
return;
}
//--- search errors
err=err || MathAbs(1/CMath::Sqr(1-x[0])-1/CMath::Sqr(1+x[0])+vc)>epsg;
}
}
}
//+------------------------------------------------------------------+
//| Testing class CMLPBase CMLPTrain |
//+------------------------------------------------------------------+
class CTestMLPTrainUnit
{
private:
//--- private methods
static void CreateNetwork(CMultilayerPerceptron &network,const int nkind,const double a1,const double a2,const int nin,const int nhid1,const int nhid2,const int nout);
static void UnsetNetwork(CMultilayerPerceptron &network);
static void TestInformational(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err);
static void TestProcessing(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err);
static void TestGradient(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err);
static void TestHessian(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err);
public:
//--- constructor, destructor
CTestMLPTrainUnit(void);
~CTestMLPTrainUnit(void);
//--- public method
static bool TestMLPTrain(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestMLPTrainUnit::CTestMLPTrainUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestMLPTrainUnit::~CTestMLPTrainUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CMLPBase CMLPTrain |
//+------------------------------------------------------------------+
static bool CTestMLPTrainUnit::TestMLPTrain(const bool silent)
{
//--- create variables
bool waserrors;
int passcount=0;
int maxn=0;
int maxhid=0;
int info=0;
int nf=0;
int nl=0;
int nhid1=0;
int nhid2=0;
int nkind=0;
int i=0;
int ncount=0;
bool inferrors;
bool procerrors;
bool graderrors;
bool hesserrors;
bool trnerrors;
//--- objects of classes
CMultilayerPerceptron network;
CMultilayerPerceptron network2;
CMLPReport rep;
CMLPCVReport cvrep;
CMatrixDouble xy;
CMatrixDouble valxy;
//--- initialization
waserrors=false;
inferrors=false;
procerrors=false;
graderrors=false;
hesserrors=false;
trnerrors=false;
passcount=10;
maxn=4;
maxhid=4;
//--- General multilayer network tests
for(nf=1;nf<=maxn;nf++)
{
for(nl=1;nl<=maxn;nl++)
{
for(nhid1=0;nhid1<=maxhid;nhid1++)
{
for(nhid2=0;nhid2<=0;nhid2++)
{
for(nkind=0;nkind<=3;nkind++)
{
//--- Skip meaningless parameters combinations
if(nkind==1 && nl<2)
continue;
//--- check
if(nhid1==0 && nhid2!=0)
continue;
//--- Tests
TestInformational(nkind,nf,nhid1,nhid2,nl,passcount,inferrors);
TestProcessing(nkind,nf,nhid1,nhid2,nl,passcount,procerrors);
TestGradient(nkind,nf,nhid1,nhid2,nl,passcount,graderrors);
TestHessian(nkind,nf,nhid1,nhid2,nl,passcount,hesserrors);
}
}
}
}
}
//--- Test network training on simple XOR problem
xy.Resize(4,3);
xy[0].Set(0,-1);
xy[0].Set(1,-1);
xy[0].Set(2,-1);
xy[1].Set(0,1);
xy[1].Set(1,-1);
xy[1].Set(2,1);
xy[2].Set(0,-1);
xy[2].Set(1,1);
xy[2].Set(2,1);
xy[3].Set(0,1);
xy[3].Set(1,1);
xy[3].Set(2,-1);
//--- function calls
CMLPBase::MLPCreate1(2,2,1,network);
CMLPTrain::MLPTrainLM(network,xy,4,0.001,10,info,rep);
//--- search errors
trnerrors=trnerrors || CMLPBase::MLPRMSError(network,xy,4)>0.1;
//--- Test CV on random noisy problem
ncount=100;
xy.Resize(ncount,2);
//--- change values
for(i=0;i<=ncount-1;i++)
{
xy[i].Set(0,2*CMath::RandomReal()-1);
xy[i].Set(1,CMath::RandomInteger(4));
}
//--- function calls
CMLPBase::MLPCreateC0(1,4,network);
CMLPTrain::MLPKFoldCVLM(network,xy,ncount,0.001,5,10,info,rep,cvrep);
//--- Final report
waserrors=(((inferrors || procerrors) || graderrors) || hesserrors) || trnerrors;
//--- check
if(!silent)
{
Print("MLP TEST");
Print("INFORMATIONAL FUNCTIONS: ");
//--- check
if(!inferrors)
Print("OK");
else
Print("FAILED");
Print("BASIC PROCESSING: ");
//--- check
if(!procerrors)
Print("OK");
else
Print("FAILED");
Print("GRADIENT CALCULATION: ");
//--- check
if(!graderrors)
Print("OK");
else
Print("FAILED");
Print("HESSIAN CALCULATION: ");
//--- check
if(!hesserrors)
Print("OK");
else
Print("FAILED");
Print("TRAINING: ");
//--- check
if(!trnerrors)
Print("OK");
else
Print("FAILED");
//--- check
if(waserrors)
Print("TEST SUMMARY: FAILED");
else
Print("TEST SUMMARY: PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Network creation |
//| This function creates network with desired structure. Network is |
//| created using one of the three methods: |
//| a) straighforward creation using MLPCreate???() |
//| b) MLPCreate???() for proxy object,which is copied with |
//| PassThroughSerializer() |
//| c) MLPCreate???() for proxy object,which is copied with MLPCopy()|
//| One of these methods is chosen with probability 1/3. |
//+------------------------------------------------------------------+
static void CTestMLPTrainUnit::CreateNetwork(CMultilayerPerceptron &network,
const int nkind,const double a1,
const double a2,const int nin,
const int nhid1,const int nhid2,
const int nout)
{
//--- create a variable
int mkind=0;
//--- object of class
CMultilayerPerceptron tmp;
//--- check
if(!CAp::Assert(((nin>0 && nhid1>=0) && nhid2>=0) && nout>0,"CreateNetwork error"))
return;
//--- check
if(!CAp::Assert(nhid1!=0 || nhid2==0,"CreateNetwork error"))
return;
//--- check
if(!CAp::Assert(nkind!=1 || nout>=2,"CreateNetwork error"))
return;
//--- change value
mkind=CMath::RandomInteger(3);
//--- check
if(nhid1==0)
{
//--- No hidden layers
if(nkind==0)
{
//--- check
if(mkind==0)
CMLPBase::MLPCreate0(nin,nout,network);
//--- check
if(mkind==1)
{
//--- function call
CMLPBase::MLPCreate0(nin,nout,tmp);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
//--- serialization
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//--- unserialization
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
//--- check
if(mkind==2)
{
CMLPBase::MLPCreate0(nin,nout,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
//--- check
if(nkind==1)
{
//--- check
if(mkind==0)
CMLPBase::MLPCreateC0(nin,nout,network);
//--- check
if(mkind==1)
{
//--- function call
CMLPBase::MLPCreateC0(nin,nout,tmp);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
//--- serialization
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//--- unserialization
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
//--- check
if(mkind==2)
{
CMLPBase::MLPCreateC0(nin,nout,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
//--- check
if(nkind==2)
{
//--- check
if(mkind==0)
CMLPBase::MLPCreateB0(nin,nout,a1,a2,network);
//--- check
if(mkind==1)
{
//--- function call
CMLPBase::MLPCreateB0(nin,nout,a1,a2,tmp);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
//--- serialization
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//--- unserialization
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
//--- check
if(mkind==2)
{
CMLPBase::MLPCreateB0(nin,nout,a1,a2,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
//--- check
if(nkind==3)
{
//--- check
if(mkind==0)
CMLPBase::MLPCreateR0(nin,nout,a1,a2,network);
//--- check
if(mkind==1)
{
//--- function call
CMLPBase::MLPCreateR0(nin,nout,a1,a2,tmp);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
//--- serialization
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//--- unserialization
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
//--- check
if(mkind==2)
{
CMLPBase::MLPCreateR0(nin,nout,a1,a2,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
}
}
}
//--- function call
CMLPBase::MLPRandomizeFull(network);
return;
}
//--- check
if(nhid2==0)
{
//--- One hidden layer
if(nkind==0)
{
//--- check
if(mkind==0)
CMLPBase::MLPCreate1(nin,nhid1,nout,network);
//--- check
if(mkind==1)
{
//--- function call
CMLPBase::MLPCreate1(nin,nhid1,nout,tmp);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
//--- serialization
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//--- unserialization
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
//--- check
if(mkind==2)
{
CMLPBase::MLPCreate1(nin,nhid1,nout,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
//--- check
if(nkind==1)
{
//--- check
if(mkind==0)
CMLPBase::MLPCreateC1(nin,nhid1,nout,network);
//--- check
if(mkind==1)
{
//--- function call
CMLPBase::MLPCreateC1(nin,nhid1,nout,tmp);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
//--- serialization
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//--- unserialization
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
//--- check
if(mkind==2)
{
CMLPBase::MLPCreateC1(nin,nhid1,nout,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
//--- check
if(nkind==2)
{
//--- check
if(mkind==0)
CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,network);
//--- check
if(mkind==1)
{
//--- function call
CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,tmp);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
//--- serialization
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//--- unserialization
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
//--- check
if(mkind==2)
{
CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
//--- check
if(nkind==3)
{
//--- check
if(mkind==0)
CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,network);
//--- check
if(mkind==1)
{
//--- function call
CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,tmp);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
//--- serialization
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//--- unserialization
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
//--- check
if(mkind==2)
{
CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
}
}
}
//--- function call
CMLPBase::MLPRandomizeFull(network);
return;
}
//--- Two hidden layers
if(nkind==0)
{
//--- check
if(mkind==0)
CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,network);
//--- check
if(mkind==1)
{
//--- function call
CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,tmp);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
//--- serialization
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//--- unserialization
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
//--- check
if(mkind==2)
{
CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
//--- check
if(nkind==1)
{
//--- check
if(mkind==0)
CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,network);
//--- check
if(mkind==1)
{
//--- function call
CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,tmp);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
//--- serialization
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//--- unserialization
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
//--- check
if(mkind==2)
{
CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
//--- check
if(nkind==2)
{
//--- check
if(mkind==0)
CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,network);
//--- check
if(mkind==1)
{
//--- function call
CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,tmp);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
//--- serialization
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//--- unserialization
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
//--- check
if(mkind==2)
{
CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
//--- check
if(nkind==3)
{
//--- check
if(mkind==0)
CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,network);
//--- check
if(mkind==1)
{
//--- function call
CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,tmp);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
//--- serialization
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//--- unserialization
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
//--- check
if(mkind==2)
{
CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
}
}
}
//--- function call
CMLPBase::MLPRandomizeFull(network);
}
//+------------------------------------------------------------------+
//| Unsets network (initialize it to smallest network possible |
//+------------------------------------------------------------------+
static void CTestMLPTrainUnit::UnsetNetwork(CMultilayerPerceptron &network)
{
//--- function call
CMLPBase::MLPCreate0(1,1,network);
}
//+------------------------------------------------------------------+
//| Informational functions test |
//+------------------------------------------------------------------+
static void CTestMLPTrainUnit::TestInformational(const int nkind,const int nin,
const int nhid1,const int nhid2,
const int nout,const int passcount,
bool &err)
{
//--- create variables
int n1=0;
int n2=0;
int wcount=0;
int i=0;
int j=0;
int k=0;
double threshold=0;
int nlayers=0;
int nmax=0;
bool issoftmax;
double mean=0;
double sigma=0;
int fkind=0;
double c=0;
double f=0;
double df=0;
double d2f=0;
double s=0;
//--- create arrays
double x[];
double y[];
//--- create matrix
CMatrixDouble neurons;
//--- object of class
CMultilayerPerceptron network;
//--- initialization
threshold=100000*CMath::m_machineepsilon;
//--- function call
CreateNetwork(network,nkind,0.0,0.0,nin,nhid1,nhid2,nout);
//--- test MLPProperties()
CMLPBase::MLPProperties(network,n1,n2,wcount);
//--- search errors
err=((err || n1!=nin) || n2!=nout) || wcount<=0;
//--- Test network geometry functions
//--- In order to do this we calculate neural network output using
//--- informational functions only,and compare results with ones
//--- obtained with MLPProcess():
//--- 1. we allocate 2-dimensional array of neurons and fill it by zeros
//--- 2. we full first layer of neurons by input values
//--- 3. we move through array,calculating values of subsequent layers
//--- 4. if we have classification network,we SOFTMAX-normalize output layer
//--- 5. we apply scaling to the outputs
//--- 6. we compare results with ones obtained by MLPProcess()
//--- NOTE: it is important to do (4) before (5),because on SOFTMAX network
//--- MLPGetOutputScaling() must return Mean=0 and Sigma=1. In order
//--- to test it implicitly,we apply it to the classifier results
//--- (already normalized). If one of the coefficients deviates from
//--- expected values,we will get error during (6).
nlayers=2;
nmax=MathMax(nin,nout);
issoftmax=nkind==1;
//--- check
if(nhid1!=0)
{
nlayers=3;
nmax=MathMax(nmax,nhid1);
}
//--- check
if(nhid2!=0)
{
nlayers=4;
nmax=MathMax(nmax,nhid2);
}
//--- allocation
neurons.Resize(nlayers,nmax);
for(i=0;i<=nlayers-1;i++)
{
for(j=0;j<=nmax-1;j++)
neurons[i].Set(j,0);
}
//--- allocation
ArrayResize(x,nin);
//--- change values
for(i=0;i<=nin-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- allocation
ArrayResize(y,nout);
//--- change values
for(i=0;i<=nout-1;i++)
y[i]=2*CMath::RandomReal()-1;
for(j=0;j<=nin-1;j++)
{
//--- function call
CMLPBase::MLPGetInputScaling(network,j,mean,sigma);
neurons[0].Set(j,(x[j]-mean)/sigma);
}
//--- calculation
for(i=1;i<=nlayers-1;i++)
{
for(j=0;j<=CMLPBase::MLPGetLayerSize(network,i)-1;j++)
{
for(k=0;k<=CMLPBase::MLPGetLayerSize(network,i-1)-1;k++)
neurons[i].Set(j,neurons[i][j]+CMLPBase::MLPGetWeight(network,i-1,k,i,j)*neurons[i-1][k]);
//--- function calls
CMLPBase::MLPGetNeuronInfo(network,i,j,fkind,c);
CMLPBase::MLPActivationFunction(neurons[i][j]-c,fkind,f,df,d2f);
neurons[i].Set(j,f);
}
}
//--- check
if(nkind==1)
{
s=0;
for(j=0;j<=nout-1;j++)
s=s+MathExp(neurons[nlayers-1][j]);
for(j=0;j<=nout-1;j++)
neurons[nlayers-1].Set(j,MathExp(neurons[nlayers-1][j])/s);
}
//--- calculation
for(j=0;j<=nout-1;j++)
{
//--- function call
CMLPBase::MLPGetOutputScaling(network,j,mean,sigma);
neurons[nlayers-1].Set(j,neurons[nlayers-1][j]*sigma+mean);
}
//--- function call
CMLPBase::MLPProcess(network,x,y);
//--- search errors
for(j=0;j<=nout-1;j++)
err=err || MathAbs(neurons[nlayers-1][j]-y[j])>threshold;
}
//+------------------------------------------------------------------+
//| Processing functions test |
//+------------------------------------------------------------------+
static void CTestMLPTrainUnit::TestProcessing(const int nkind,const int nin,
const int nhid1,const int nhid2,
const int nout,const int passcount,
bool &err)
{
//--- create variables
int n1=0;
int n2=0;
int wcount=0;
bool zeronet;
double a1=0;
double a2=0;
int pass=0;
int i=0;
bool allsame;
double v=0;
int i_=0;
//--- create arrays
double x1[];
double x2[];
double y1[];
double y2[];
//--- objects of classes
CMultilayerPerceptron network;
CMultilayerPerceptron network2;
//--- check
if(!CAp::Assert(passcount>=2,"PassCount<2!"))
return;
//--- Prepare network
a1=0;
a2=0;
//--- check
if(nkind==2)
{
a1=1000*CMath::RandomReal()-500;
a2=2*CMath::RandomReal()-1;
}
//--- check
if(nkind==3)
{
a1=1000*CMath::RandomReal()-500;
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
}
//--- function calls
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
CMLPBase::MLPProperties(network,n1,n2,wcount);
//--- Initialize arrays
ArrayResize(x1,nin);
ArrayResize(x2,nin);
ArrayResize(y1,nout);
ArrayResize(y2,nout);
//--- Main cycle
for(pass=1;pass<=passcount;pass++)
{
//--- Last run is made on zero network
CMLPBase::MLPRandomizeFull(network);
zeronet=false;
//--- check
if(pass==passcount)
{
for(i_=0;i_<=wcount-1;i_++)
network.m_weights[i_]=0*network.m_weights[i_];
zeronet=true;
}
//--- Same inputs leads to same outputs
for(i=0;i<=nin-1;i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=x1[i];
}
for(i=0;i<=nout-1;i++)
{
y1[i]=2*CMath::RandomReal()-1;
y2[i]=2*CMath::RandomReal()-1;
}
//--- function calls
CMLPBase::MLPProcess(network,x1,y1);
CMLPBase::MLPProcess(network,x2,y2);
//--- search errors
allsame=true;
for(i=0;i<=nout-1;i++)
allsame=allsame && y1[i]==y2[i];
err=err || !allsame;
//--- Same inputs on original network leads to same outputs
//--- on copy created using MLPCopy
UnsetNetwork(network2);
CMLPBase::MLPCopy(network,network2);
//--- change values
for(i=0;i<=nin-1;i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=x1[i];
}
for(i=0;i<=nout-1;i++)
{
y1[i]=2*CMath::RandomReal()-1;
y2[i]=2*CMath::RandomReal()-1;
}
//--- function calls
CMLPBase::MLPProcess(network,x1,y1);
CMLPBase::MLPProcess(network2,x2,y2);
//--- search errors
allsame=true;
for(i=0;i<=nout-1;i++)
allsame=allsame && y1[i]==y2[i];
err=err || !allsame;
//--- Same inputs on original network leads to same outputs
//--- on copy created using MLPSerialize
UnsetNetwork(network2);
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
//--- serialization
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,network);
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,network);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//--- unserialization
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network2);
_local_serializer.Stop();
//--- change values
for(i=0;i<=nin-1;i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=x1[i];
}
for(i=0;i<=nout-1;i++)
{
y1[i]=2*CMath::RandomReal()-1;
y2[i]=2*CMath::RandomReal()-1;
}
//--- function calls
CMLPBase::MLPProcess(network,x1,y1);
CMLPBase::MLPProcess(network2,x2,y2);
//--- search errors
allsame=true;
for(i=0;i<=nout-1;i++)
allsame=allsame && y1[i]==y2[i];
err=err || !allsame;
//--- Different inputs leads to different outputs (non-zero network)
if(!zeronet)
{
for(i=0;i<=nin-1;i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=2*CMath::RandomReal()-1;
}
for(i=0;i<=nout-1;i++)
{
y1[i]=2*CMath::RandomReal()-1;
y2[i]=y1[i];
}
//--- function calls
CMLPBase::MLPProcess(network,x1,y1);
CMLPBase::MLPProcess(network,x2,y2);
//--- search errors
allsame=true;
for(i=0;i<=nout-1;i++)
allsame=allsame && y1[i]==y2[i];
err=err || allsame;
}
//--- Randomization changes outputs (when inputs are unchanged,non-zero network)
if(!zeronet)
{
for(i=0;i<=nin-1;i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=2*CMath::RandomReal()-1;
}
for(i=0;i<=nout-1;i++)
{
y1[i]=2*CMath::RandomReal()-1;
y2[i]=y1[i];
}
//--- function calls
CMLPBase::MLPCopy(network,network2);
CMLPBase::MLPRandomize(network2);
CMLPBase::MLPProcess(network,x1,y1);
CMLPBase::MLPProcess(network2,x1,y2);
//--- search errors
allsame=true;
for(i=0;i<=nout-1;i++)
allsame=allsame && y1[i]==y2[i];
err=err || allsame;
}
//--- Full randomization changes outputs (when inputs are unchanged,non-zero network)
if(!zeronet)
{
for(i=0;i<=nin-1;i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=2*CMath::RandomReal()-1;
}
for(i=0;i<=nout-1;i++)
{
y1[i]=2*CMath::RandomReal()-1;
y2[i]=y1[i];
}
//--- function calls
CMLPBase::MLPCopy(network,network2);
CMLPBase::MLPRandomizeFull(network2);
CMLPBase::MLPProcess(network,x1,y1);
CMLPBase::MLPProcess(network2,x1,y2);
//--- search errors
allsame=true;
for(i=0;i<=nout-1;i++)
allsame=allsame && y1[i]==y2[i];
err=err || allsame;
}
//--- Normalization properties
if(nkind==1)
{
//--- Classifier network outputs are normalized
for(i=0;i<=nin-1;i++)
x1[i]=2*CMath::RandomReal()-1;
//--- function call
CMLPBase::MLPProcess(network,x1,y1);
v=0;
for(i=0;i<=nout-1;i++)
{
v=v+y1[i];
//--- search errors
err=err || y1[i]<0.0;
}
//--- search errors
err=err || MathAbs(v-1)>1000*CMath::m_machineepsilon;
}
//--- check
if(nkind==2)
{
//--- B-type network outputs are bounded from above/below
for(i=0;i<=nin-1;i++)
x1[i]=2*CMath::RandomReal()-1;
//--- function call
CMLPBase::MLPProcess(network,x1,y1);
for(i=0;i<=nout-1;i++)
{
//--- check
if(a2>=0.0)
err=err || y1[i]<a1;
else
err=err || y1[i]>a1;
}
}
//--- check
if(nkind==3)
{
//--- R-type network outputs are within [A1,A2] (or [A2,A1])
for(i=0;i<=nin-1;i++)
x1[i]=2*CMath::RandomReal()-1;
//--- function call
CMLPBase::MLPProcess(network,x1,y1);
//--- search errors
for(i=0;i<=nout-1;i++)
err=(err || y1[i]<MathMin(a1,a2)) || y1[i]>MathMax(a1,a2);
}
}
}
//+------------------------------------------------------------------+
//| Gradient functions test |
//+------------------------------------------------------------------+
static void CTestMLPTrainUnit::TestGradient(const int nkind,const int nin,
const int nhid1,const int nhid2,
const int nout,const int passcount,
bool &err)
{
//--- create variables
int n1=0;
int n2=0;
int wcount=0;
double h=0;
double etol=0;
double a1=0;
double a2=0;
int pass=0;
int i=0;
int j=0;
int ssize=0;
double v=0;
double e=0;
double e1=0;
double e2=0;
double v1=0;
double v2=0;
double v3=0;
double v4=0;
double wprev=0;
int i_=0;
int i1_=0;
//--- create arrays
double grad1[];
double grad2[];
double x[];
double y[];
double x1[];
double x2[];
double y1[];
double y2[];
//--- create matrix
CMatrixDouble xy;
//--- object of class
CMultilayerPerceptron network;
//--- check
if(!CAp::Assert(passcount>=2,"PassCount<2!"))
return;
//--- change values
a1=0;
a2=0;
//--- check
if(nkind==2)
{
a1=1000*CMath::RandomReal()-500;
a2=2*CMath::RandomReal()-1;
}
//--- check
if(nkind==3)
{
a1=1000*CMath::RandomReal()-500;
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
}
//--- function calls
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
CMLPBase::MLPProperties(network,n1,n2,wcount);
//--- change values
h=0.0001;
etol=0.01;
//--- Initialize
ArrayResize(x,nin);
ArrayResize(x1,nin);
ArrayResize(x2,nin);
ArrayResize(y,nout);
ArrayResize(y1,nout);
ArrayResize(y2,nout);
ArrayResize(grad1,wcount);
ArrayResize(grad2,wcount);
//--- Process
for(pass=1;pass<=passcount;pass++)
{
//--- function call
CMLPBase::MLPRandomizeFull(network);
//--- Test error/gradient calculation (least squares)
xy.Resize(1,nin+nout);
for(i=0;i<=nin-1;i++)
x[i]=4*CMath::RandomReal()-2;
for(i_=0;i_<=nin-1;i_++)
xy[0].Set(i_,x[i_]);
//--- check
if(CMLPBase::MLPIsSoftMax(network))
{
for(i=0;i<=nout-1;i++)
y[i]=0;
xy[0].Set(nin,CMath::RandomInteger(nout));
y[(int)MathRound(xy[0][nin])]=1;
}
else
{
for(i=0;i<=nout-1;i++)
y[i]=4*CMath::RandomReal()-2;
i1_=-nin;
for(i_=nin;i_<=nin+nout-1;i_++)
xy[0].Set(i_,y[i_+i1_]);
}
//--- function calls
CMLPBase::MLPGrad(network,x,y,e,grad2);
CMLPBase::MLPProcess(network,x,y2);
for(i_=0;i_<=nout-1;i_++)
y2[i_]=y2[i_]-y[i_];
//--- change value
v=0.0;
for(i_=0;i_<=nout-1;i_++)
v+=y2[i_]*y2[i_];
v=v/2;
//--- search errors
err=err || MathAbs((v-e)/v)>etol;
err=err || MathAbs((CMLPBase::MLPError(network,xy,1)-v)/v)>etol;
for(i=0;i<=wcount-1;i++)
{
wprev=network.m_weights[i];
network.m_weights[i]=wprev-2*h;
//--- function call
CMLPBase::MLPProcess(network,x,y1);
for(i_=0;i_<=nout-1;i_++)
y1[i_]=y1[i_]-y[i_];
//--- change value
v1=0.0;
for(i_=0;i_<=nout-1;i_++)
v1+=y1[i_]*y1[i_];
v1=v1/2;
network.m_weights[i]=wprev-h;
//--- function call
CMLPBase::MLPProcess(network,x,y1);
for(i_=0;i_<=nout-1;i_++)
y1[i_]=y1[i_]-y[i_];
//--- change value
v2=0.0;
for(i_=0;i_<=nout-1;i_++)
v2+=y1[i_]*y1[i_];
v2=v2/2;
network.m_weights[i]=wprev+h;
//--- function call
CMLPBase::MLPProcess(network,x,y1);
for(i_=0;i_<=nout-1;i_++)
y1[i_]=y1[i_]-y[i_];
//--- change value
v3=0.0;
for(i_=0;i_<=nout-1;i_++)
v3+=y1[i_]*y1[i_];
v3=v3/2;
network.m_weights[i]=wprev+2*h;
//--- function call
CMLPBase::MLPProcess(network,x,y1);
for(i_=0;i_<=nout-1;i_++)
y1[i_]=y1[i_]-y[i_];
//--- change value
v4=0.0;
for(i_=0;i_<=nout-1;i_++)
v4+=y1[i_]*y1[i_];
v4=v4/2;
network.m_weights[i]=wprev;
grad1[i]=(v1-8*v2+8*v3-v4)/(12*h);
//--- check
if(MathAbs(grad1[i])>1.0E-3)
err=err || MathAbs((grad2[i]-grad1[i])/grad1[i])>etol;
else
err=err || MathAbs(grad2[i]-grad1[i])>etol;
}
//--- Test error/gradient calculation (natural).
//--- Testing on non-random structure networks
//--- (because NKind is representative only in that case).
xy.Resize(1,nin+nout);
for(i=0;i<=nin-1;i++)
x[i]=4*CMath::RandomReal()-2;
for(i_=0;i_<=nin-1;i_++)
xy[0].Set(i_,x[i_]);
//--- check
if(CMLPBase::MLPIsSoftMax(network))
{
for(i=0;i<=nout-1;i++)
y[i]=0;
xy[0].Set(nin,CMath::RandomInteger(nout));
y[(int)MathRound(xy[0][nin])]=1;
}
else
{
for(i=0;i<=nout-1;i++)
y[i]=4*CMath::RandomReal()-2;
i1_=-nin;
for(i_=nin;i_<=nin+nout-1;i_++)
xy[0].Set(i_,y[i_+i1_]);
}
//--- function calls
CMLPBase::MLPGradN(network,x,y,e,grad2);
CMLPBase::MLPProcess(network,x,y2);
v=0;
//--- check
if(nkind!=1)
{
for(i=0;i<=nout-1;i++)
v=v+0.5*CMath::Sqr(y2[i]-y[i]);
}
else
{
for(i=0;i<=nout-1;i++)
{
//--- check
if(y[i]!=0.0)
{
//--- check
if(y2[i]==0.0)
v=v+y[i]*MathLog(CMath::m_maxrealnumber);
else
v=v+y[i]*MathLog(y[i]/y2[i]);
}
}
}
//--- search errors
err=err || MathAbs((v-e)/v)>etol;
err=err || MathAbs((CMLPBase::MLPErrorN(network,xy,1)-v)/v)>etol;
for(i=0;i<=wcount-1;i++)
{
wprev=network.m_weights[i];
network.m_weights[i]=wprev+h;
//--- function call
CMLPBase::MLPProcess(network,x,y2);
network.m_weights[i]=wprev-h;
//--- function call
CMLPBase::MLPProcess(network,x,y1);
network.m_weights[i]=wprev;
v=0;
//--- check
if(nkind!=1)
{
for(j=0;j<=nout-1;j++)
v=v+0.5*(CMath::Sqr(y2[j]-y[j])-CMath::Sqr(y1[j]-y[j]))/(2*h);
}
else
{
for(j=0;j<=nout-1;j++)
{
//--- check
if(y[j]!=0.0)
{
//--- check
if(y2[j]==0.0)
v=v+y[j]*MathLog(CMath::m_maxrealnumber);
else
v=v+y[j]*MathLog(y[j]/y2[j]);
//--- check
if(y1[j]==0.0)
v=v-y[j]*MathLog(CMath::m_maxrealnumber);
else
v=v-y[j]*MathLog(y[j]/y1[j]);
}
}
v=v/(2*h);
}
grad1[i]=v;
//--- check
if(MathAbs(grad1[i])>1.0E-3)
err=err || MathAbs((grad2[i]-grad1[i])/grad1[i])>etol;
else
err=err || MathAbs(grad2[i]-grad1[i])>etol;
}
//--- Test gradient calculation: batch (least squares)
ssize=1+CMath::RandomInteger(10);
xy.Resize(ssize,nin+nout);
for(i=0;i<=wcount-1;i++)
grad1[i]=0;
//--- calculation
e1=0;
for(i=0;i<=ssize-1;i++)
{
for(j=0;j<=nin-1;j++)
x1[j]=4*CMath::RandomReal()-2;
for(i_=0;i_<=nin-1;i_++)
xy[i].Set(i_,x1[i_]);
//--- check
if(CMLPBase::MLPIsSoftMax(network))
{
for(j=0;j<=nout-1;j++)
y1[j]=0;
//--- change values
xy[i].Set(nin,CMath::RandomInteger(nout));
y1[(int)MathRound(xy[i][nin])]=1;
}
else
{
//--- change values
for(j=0;j<=nout-1;j++)
y1[j]=4*CMath::RandomReal()-2;
i1_=-nin;
for(i_=nin;i_<=nin+nout-1;i_++)
xy[i].Set(i_,y1[i_+i1_]);
}
//--- function call
CMLPBase::MLPGrad(network,x1,y1,v,grad2);
//--- change values
e1=e1+v;
for(i_=0;i_<=wcount-1;i_++)
grad1[i_]=grad1[i_]+grad2[i_];
}
//--- function call
CMLPBase::MLPGradBatch(network,xy,ssize,e2,grad2);
//--- search errors
err=err || MathAbs(e1-e2)/e1>0.01;
for(i=0;i<=wcount-1;i++)
{
//--- check
if(grad1[i]!=0.0)
err=err || MathAbs((grad2[i]-grad1[i])/grad1[i])>etol;
else
err=err || grad2[i]!=grad1[i];
}
//--- Test gradient calculation: batch (natural error func)
ssize=1+CMath::RandomInteger(10);
xy.Resize(ssize,nin+nout);
for(i=0;i<=wcount-1;i++)
grad1[i]=0;
//--- calculation
e1=0;
for(i=0;i<=ssize-1;i++)
{
for(j=0;j<=nin-1;j++)
x1[j]=4*CMath::RandomReal()-2;
for(i_=0;i_<=nin-1;i_++)
xy[i].Set(i_,x1[i_]);
//--- check
if(CMLPBase::MLPIsSoftMax(network))
{
for(j=0;j<=nout-1;j++)
y1[j]=0;
//--- change values
xy[i].Set(nin,CMath::RandomInteger(nout));
y1[(int)MathRound(xy[i][nin])]=1;
}
else
{
//--- change values
for(j=0;j<=nout-1;j++)
y1[j]=4*CMath::RandomReal()-2;
i1_=-nin;
for(i_=nin;i_<=nin+nout-1;i_++)
xy[i].Set(i_,y1[i_+i1_]);
}
//--- function call
CMLPBase::MLPGradN(network,x1,y1,v,grad2);
//--- change values
e1=e1+v;
for(i_=0;i_<=wcount-1;i_++)
grad1[i_]=grad1[i_]+grad2[i_];
}
//--- function call
CMLPBase::MLPGradNBatch(network,xy,ssize,e2,grad2);
//--- search errors
err=err || MathAbs(e1-e2)/e1>etol;
for(i=0;i<=wcount-1;i++)
{
//--- check
if(grad1[i]!=0.0)
err=err || MathAbs((grad2[i]-grad1[i])/grad1[i])>etol;
else
err=err || grad2[i]!=grad1[i];
}
}
}
//+------------------------------------------------------------------+
//| Hessian functions test |
//+------------------------------------------------------------------+
static void CTestMLPTrainUnit::TestHessian(const int nkind,const int nin,
const int nhid1,const int nhid2,
const int nout,const int passcount,
bool &err)
{
//--- create variables
int hkind=0;
int n1=0;
int n2=0;
int wcount=0;
double h=0;
double etol=0;
int pass=0;
int i=0;
int j=0;
int ssize=0;
double a1=0;
double a2=0;
double v=0;
double e1=0;
double e2=0;
double wprev=0;
int i_=0;
int i1_=0;
//--- create arrays
double grad1[];
double grad2[];
double grad3[];
double x[];
double y[];
double x1[];
double x2[];
double y1[];
double y2[];
//--- create matrix
CMatrixDouble xy;
CMatrixDouble h1;
CMatrixDouble h2;
//---
CMultilayerPerceptron network;
//--- check
if(!CAp::Assert(passcount>=2,"PassCount<2!"))
return;
//--- change values
a1=0;
a2=0;
//--- check
if(nkind==2)
{
a1=1000*CMath::RandomReal()-500;
a2=2*CMath::RandomReal()-1;
}
//--- check
if(nkind==3)
{
a1=1000*CMath::RandomReal()-500;
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
}
//--- function calls
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
CMLPBase::MLPProperties(network,n1,n2,wcount);
//--- change values
h=0.0001;
etol=0.05;
//--- Initialize
ArrayResize(x,nin);
ArrayResize(x1,nin);
ArrayResize(x2,nin);
ArrayResize(y,nout);
ArrayResize(y1,nout);
ArrayResize(y2,nout);
ArrayResize(grad1,wcount);
ArrayResize(grad2,wcount);
ArrayResize(grad3,wcount);
h1.Resize(wcount,wcount);
h2.Resize(wcount,wcount);
//--- Process
for(pass=1;pass<=passcount;pass++)
{
//--- function call
CMLPBase::MLPRandomizeFull(network);
//--- Test hessian calculation .
//--- E1 contains total error (calculated using MLPGrad/MLPGradN)
//--- Grad1 contains total gradient (calculated using MLPGrad/MLPGradN)
//--- H1 contains Hessian calculated using differences of gradients
//--- E2,Grad2 and H2 contains corresponing values calculated using MLPHessianBatch/MLPHessianNBatch
for(hkind=0;hkind<=1;hkind++)
{
ssize=1+CMath::RandomInteger(10);
xy.Resize(ssize,nin+nout);
for(i=0;i<=wcount-1;i++)
grad1[i]=0;
//--- change values
for(i=0;i<=wcount-1;i++)
{
for(j=0;j<=wcount-1;j++)
h1[i].Set(j,0);
}
//--- calculation
e1=0;
for(i=0;i<=ssize-1;i++)
{
//--- X,Y
for(j=0;j<=nin-1;j++)
x1[j]=4*CMath::RandomReal()-2;
for(i_=0;i_<=nin-1;i_++)
xy[i].Set(i_,x1[i_]);
//--- check
if(CMLPBase::MLPIsSoftMax(network))
{
for(j=0;j<=nout-1;j++)
y1[j]=0;
//--- change values
xy[i].Set(nin,CMath::RandomInteger(nout));
y1[(int)MathRound(xy[i][nin])]=1;
}
else
{
//--- change values
for(j=0;j<=nout-1;j++)
y1[j]=4*CMath::RandomReal()-2;
i1_=-nin;
for(i_=nin;i_<=nin+nout-1;i_++)
xy[i].Set(i_,y1[i_+i1_]);
}
//--- E1,Grad1
if(hkind==0)
CMLPBase::MLPGrad(network,x1,y1,v,grad2);
else
CMLPBase::MLPGradN(network,x1,y1,v,grad2);
//--- change values
e1=e1+v;
for(i_=0;i_<=wcount-1;i_++)
grad1[i_]=grad1[i_]+grad2[i_];
//--- H1
for(j=0;j<=wcount-1;j++)
{
wprev=network.m_weights[j];
network.m_weights[j]=wprev-2*h;
//--- check
if(hkind==0)
CMLPBase::MLPGrad(network,x1,y1,v,grad2);
else
CMLPBase::MLPGradN(network,x1,y1,v,grad2);
network.m_weights[j]=wprev-h;
//--- check
if(hkind==0)
CMLPBase::MLPGrad(network,x1,y1,v,grad3);
else
CMLPBase::MLPGradN(network,x1,y1,v,grad3);
//--- change values
for(i_=0;i_<=wcount-1;i_++)
grad2[i_]=grad2[i_]-8*grad3[i_];
network.m_weights[j]=wprev+h;
//--- check
if(hkind==0)
CMLPBase::MLPGrad(network,x1,y1,v,grad3);
else
CMLPBase::MLPGradN(network,x1,y1,v,grad3);
//--- change values
for(i_=0;i_<=wcount-1;i_++)
grad2[i_]=grad2[i_]+8*grad3[i_];
network.m_weights[j]=wprev+2*h;
//--- check
if(hkind==0)
CMLPBase::MLPGrad(network,x1,y1,v,grad3);
else
CMLPBase::MLPGradN(network,x1,y1,v,grad3);
//--- change values
for(i_=0;i_<=wcount-1;i_++)
grad2[i_]=grad2[i_]-grad3[i_];
v=1/(12*h);
for(i_=0;i_<=wcount-1;i_++)
h1[j].Set(i_,h1[j][i_]+v*grad2[i_]);
network.m_weights[j]=wprev;
}
}
//--- check
if(hkind==0)
CMLPBase::MLPHessianBatch(network,xy,ssize,e2,grad2,h2);
else
CMLPBase::MLPHessianNBatch(network,xy,ssize,e2,grad2,h2);
//--- search errors
err=err || MathAbs(e1-e2)/e1>etol;
for(i=0;i<=wcount-1;i++)
{
//--- check
if(MathAbs(grad1[i])>1.0E-2)
err=err || MathAbs((grad2[i]-grad1[i])/grad1[i])>etol;
else
err=err || MathAbs(grad2[i]-grad1[i])>etol;
}
//--- search errors
for(i=0;i<=wcount-1;i++)
{
for(j=0;j<=wcount-1;j++)
{
//--- check
if(MathAbs(h1[i][j])>5.0E-2)
err=err || MathAbs((h1[i][j]-h2[i][j])/h1[i][j])>etol;
else
err=err || MathAbs(h2[i][j]-h1[i][j])>etol;
}
}
}
}
}
//+------------------------------------------------------------------+
//| Testing class CMLPE |
//+------------------------------------------------------------------+
class CTestMLPEUnit
{
private:
//--- private methods
static void CreateEnsemble(CMLPEnsemble &ensemble,const int nkind,const double a1,const double a2,const int nin,const int nhid1,const int nhid2,const int nout,const int ec);
static void UnsetEnsemble(CMLPEnsemble &ensemble);
static void TestInformational(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int ec,const int passcount,bool &err);
static void TestProcessing(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int ec,const int passcount,bool &err);
public:
//--- constructor, destructor
CTestMLPEUnit(void);
~CTestMLPEUnit(void);
//--- public method
static bool TestMLPE(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestMLPEUnit::CTestMLPEUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestMLPEUnit::~CTestMLPEUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CMLPE |
//+------------------------------------------------------------------+
static bool CTestMLPEUnit::TestMLPE(const bool silent)
{
//--- create variables
bool waserrors;
int passcount=0;
int maxn=0;
int maxhid=0;
int nf=0;
int nhid=0;
int nl=0;
int nhid1=0;
int nhid2=0;
int ec=0;
int nkind=0;
int algtype=0;
int tasktype=0;
int pass=0;
int i=0;
int j=0;
int nin=0;
int nout=0;
int npoints=0;
double e=0;
int info=0;
int nless=0;
int nall=0;
int nclasses=0;
bool inferrors;
bool procerrors;
bool trnerrors;
//--- create matrix
CMatrixDouble xy;
//--- objects of classes
CMLPEnsemble ensemble;
CMLPReport rep;
CMLPCVReport oobrep;
//--- initialization
waserrors=false;
inferrors=false;
procerrors=false;
trnerrors=false;
passcount=10;
maxn=4;
maxhid=4;
//--- General MLP ensembles tests
for(nf=1;nf<=maxn;nf++)
{
for(nl=1;nl<=maxn;nl++)
{
for(nhid1=0;nhid1<=maxhid;nhid1++)
{
for(nhid2=0;nhid2<=0;nhid2++)
{
for(nkind=0;nkind<=3;nkind++)
{
for(ec=1;ec<=3;ec++)
{
//--- Skip meaningless parameters combinations
if(nkind==1 && nl<2)
continue;
//--- check
if(nhid1==0 && nhid2!=0)
continue;
//--- Tests
TestInformational(nkind,nf,nhid1,nhid2,nl,ec,passcount,inferrors);
TestProcessing(nkind,nf,nhid1,nhid2,nl,ec,passcount,procerrors);
}
}
}
}
}
}
//--- network training must reduce error
//--- test on random regression task
nin=3;
nout=2;
nhid=5;
npoints=100;
nless=0;
nall=0;
//--- calculation
for(pass=1;pass<=10;pass++)
{
for(algtype=0;algtype<=1;algtype++)
{
for(tasktype=0;tasktype<=1;tasktype++)
{
//--- check
if(tasktype==0)
{
//--- allocation
xy.Resize(npoints,nin+nout);
//--- change values
for(i=0;i<=npoints-1;i++)
{
for(j=0;j<=nin+nout-1;j++)
xy[i].Set(j,2*CMath::RandomReal()-1);
}
//--- function call
CMLPE::MLPECreate1(nin,nhid,nout,1+CMath::RandomInteger(3),ensemble);
}
else
{
//--- allocation
xy.Resize(npoints,nin+1);
//--- change values
nclasses=2+CMath::RandomInteger(2);
for(i=0;i<=npoints-1;i++)
{
for(j=0;j<=nin-1;j++)
xy[i].Set(j,2*CMath::RandomReal()-1);
xy[i].Set(nin,CMath::RandomInteger(nclasses));
}
//--- function call
CMLPE::MLPECreateC1(nin,nhid,nclasses,1+CMath::RandomInteger(3),ensemble);
}
e=CMLPE::MLPERMSError(ensemble,xy,npoints);
//--- check
if(algtype==0)
CMLPE::MLPEBaggingLM(ensemble,xy,npoints,0.001,1,info,rep,oobrep);
else
CMLPE::MLPEBaggingLBFGS(ensemble,xy,npoints,0.001,1,0.01,0,info,rep,oobrep);
//--- check
if(info<0)
trnerrors=true;
else
{
//--- check
if(CMLPE::MLPERMSError(ensemble,xy,npoints)<e)
nless=nless+1;
}
//--- change value
nall=nall+1;
}
}
}
//--- search errors
trnerrors=trnerrors || nall-nless>0.3*nall;
//--- Final report
waserrors=(inferrors || procerrors) || trnerrors;
//--- check
if(!silent)
{
Print("MLP ENSEMBLE TEST");
Print("INFORMATIONAL FUNCTIONS: ");
//--- check
if(!inferrors)
Print("OK");
else
Print("FAILED");
Print("BASIC PROCESSING: ");
//--- check
if(!procerrors)
Print("OK");
else
Print("FAILED");
Print("TRAINING: ");
//--- check
if(!trnerrors)
Print("OK");
else
Print("FAILED");
//--- check
if(waserrors)
Print("TEST SUMMARY: FAILED");
else
Print("TEST SUMMARY: PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Network creation |
//+------------------------------------------------------------------+
static void CTestMLPEUnit::CreateEnsemble(CMLPEnsemble &ensemble,const int nkind,
const double a1,const double a2,
const int nin,const int nhid1,
const int nhid2,const int nout,
const int ec)
{
//--- check
if(!CAp::Assert(((nin>0 && nhid1>=0) && nhid2>=0) && nout>0,"CreateNetwork error"))
return;
//--- check
if(!CAp::Assert(nhid1!=0 || nhid2==0,"CreateNetwork error"))
return;
//--- check
if(!CAp::Assert(nkind!=1 || nout>=2,"CreateNetwork error"))
return;
//--- check
if(nhid1==0)
{
//--- No hidden layers
if(nkind==0)
CMLPE::MLPECreate0(nin,nout,ec,ensemble);
else
{
//--- check
if(nkind==1)
CMLPE::MLPECreateC0(nin,nout,ec,ensemble);
else
{
//--- check
if(nkind==2)
CMLPE::MLPECreateB0(nin,nout,a1,a2,ec,ensemble);
else
{
//--- check
if(nkind==3)
CMLPE::MLPECreateR0(nin,nout,a1,a2,ec,ensemble);
}
}
}
//--- exit the function
return;
}
//--- check
if(nhid2==0)
{
//--- One hidden layer
if(nkind==0)
CMLPE::MLPECreate1(nin,nhid1,nout,ec,ensemble);
else
{
//--- check
if(nkind==1)
CMLPE::MLPECreateC1(nin,nhid1,nout,ec,ensemble);
else
{
//--- check
if(nkind==2)
CMLPE::MLPECreateB1(nin,nhid1,nout,a1,a2,ec,ensemble);
else
{
//--- check
if(nkind==3)
CMLPE::MLPECreateR1(nin,nhid1,nout,a1,a2,ec,ensemble);
}
}
}
//--- exit the function
return;
}
//--- Two hidden layers
if(nkind==0)
CMLPE::MLPECreate2(nin,nhid1,nhid2,nout,ec,ensemble);
else
{
//--- check
if(nkind==1)
CMLPE::MLPECreateC2(nin,nhid1,nhid2,nout,ec,ensemble);
else
{
//--- check
if(nkind==2)
CMLPE::MLPECreateB2(nin,nhid1,nhid2,nout,a1,a2,ec,ensemble);
else
{
//--- check
if(nkind==3)
CMLPE::MLPECreateR2(nin,nhid1,nhid2,nout,a1,a2,ec,ensemble);
}
}
}
}
//+------------------------------------------------------------------+
//| Unsets network (initialize it to smallest network possible |
//+------------------------------------------------------------------+
static void CTestMLPEUnit::UnsetEnsemble(CMLPEnsemble &ensemble)
{
//--- function call
CMLPE::MLPECreate0(1,1,1,ensemble);
}
//+------------------------------------------------------------------+
//| Iformational functions test |
//+------------------------------------------------------------------+
static void CTestMLPEUnit::TestInformational(const int nkind,const int nin,
const int nhid1,const int nhid2,
const int nout,const int ec,
const int passcount,bool &err)
{
//--- create variables
int n1=0;
int n2=0;
//--- object of class
CMLPEnsemble ensemble;
//--- function calls
CreateEnsemble(ensemble,nkind,-1.0,1.0,nin,nhid1,nhid2,nout,ec);
CMLPE::MLPEProperties(ensemble,n1,n2);
//--- search errors
err=(err || n1!=nin) || n2!=nout;
}
//+------------------------------------------------------------------+
//| Processing functions test |
//+------------------------------------------------------------------+
static void CTestMLPEUnit::TestProcessing(const int nkind,const int nin,
const int nhid1,const int nhid2,
const int nout,const int ec,
const int passcount,bool &err)
{
//--- create variables
double a1=0;
double a2=0;
int pass=0;
int i=0;
bool allsame;
int rlen=0;
double v=0;
//--- create arrays
double x1[];
double x2[];
double y1[];
double y2[];
double ra[];
double ra2[];
//--- objects of classes
CMLPEnsemble ensemble;
CMLPEnsemble ensemble2;
//--- Prepare network
a1=0;
a2=0;
//--- check
if(nkind==2)
{
a1=1000*CMath::RandomReal()-500;
a2=2*CMath::RandomReal()-1;
}
//--- check
if(nkind==3)
{
a1=1000*CMath::RandomReal()-500;
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
}
//--- Initialize arrays
ArrayResize(x1,nin);
ArrayResize(x2,nin);
ArrayResize(y1,nout);
ArrayResize(y2,nout);
//--- Main cycle
for(pass=1;pass<=passcount;pass++)
{
//--- function call
CreateEnsemble(ensemble,nkind,a1,a2,nin,nhid1,nhid2,nout,ec);
//--- Same inputs leads to same outputs
for(i=0;i<=nin-1;i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=x1[i];
}
for(i=0;i<=nout-1;i++)
{
y1[i]=2*CMath::RandomReal()-1;
y2[i]=2*CMath::RandomReal()-1;
}
//--- function calls
CMLPE::MLPEProcess(ensemble,x1,y1);
CMLPE::MLPEProcess(ensemble,x2,y2);
//--- search errors
allsame=true;
for(i=0;i<=nout-1;i++)
allsame=allsame && y1[i]==y2[i];
err=err || !allsame;
//--- Same inputs on original network leads to same outputs
//--- on copy created using MLPCopy
UnsetEnsemble(ensemble2);
CMLPE::MLPECopy(ensemble,ensemble2);
for(i=0;i<=nin-1;i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=x1[i];
}
for(i=0;i<=nout-1;i++)
{
y1[i]=2*CMath::RandomReal()-1;
y2[i]=2*CMath::RandomReal()-1;
}
//--- function calls
CMLPE::MLPEProcess(ensemble,x1,y1);
CMLPE::MLPEProcess(ensemble2,x2,y2);
//--- search errors
allsame=true;
for(i=0;i<=nout-1;i++)
allsame=allsame && y1[i]==y2[i];
err=err || !allsame;
//--- Same inputs on original network leads to same outputs
//--- on copy created using MLPSerialize
UnsetEnsemble(ensemble2);
CMLPE::MLPESerialize(ensemble,ra,rlen);
//--- allocation
ArrayResize(ra2,rlen);
//--- copy
for(i=0;i<=rlen-1;i++)
ra2[i]=ra[i];
//--- function call
CMLPE::MLPEUnserialize(ra2,ensemble2);
for(i=0;i<=nin-1;i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=x1[i];
}
for(i=0;i<=nout-1;i++)
{
y1[i]=2*CMath::RandomReal()-1;
y2[i]=2*CMath::RandomReal()-1;
}
//--- function calls
CMLPE::MLPEProcess(ensemble,x1,y1);
CMLPE::MLPEProcess(ensemble2,x2,y2);
//--- search errors
allsame=true;
for(i=0;i<=nout-1;i++)
allsame=allsame && y1[i]==y2[i];
err=err || !allsame;
//--- Different inputs leads to different outputs (non-zero network)
for(i=0;i<=nin-1;i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=2*CMath::RandomReal()-1;
}
for(i=0;i<=nout-1;i++)
{
y1[i]=2*CMath::RandomReal()-1;
y2[i]=y1[i];
}
//--- function calls
CMLPE::MLPEProcess(ensemble,x1,y1);
CMLPE::MLPEProcess(ensemble,x2,y2);
//--- search errors
allsame=true;
for(i=0;i<=nout-1;i++)
allsame=allsame && y1[i]==y2[i];
err=err || allsame;
//--- Randomization changes outputs (when inputs are unchanged,non-zero network)
for(i=0;i<=nin-1;i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=2*CMath::RandomReal()-1;
}
for(i=0;i<=nout-1;i++)
{
y1[i]=2*CMath::RandomReal()-1;
y2[i]=y1[i];
}
//--- function calls
CMLPE::MLPECopy(ensemble,ensemble2);
CMLPE::MLPERandomize(ensemble2);
CMLPE::MLPEProcess(ensemble,x1,y1);
CMLPE::MLPEProcess(ensemble2,x1,y2);
//--- search errors
allsame=true;
for(i=0;i<=nout-1;i++)
allsame=allsame && y1[i]==y2[i];
err=err || allsame;
//--- Normalization properties
if(nkind==1)
{
//--- Classifier network outputs are normalized
for(i=0;i<=nin-1;i++)
x1[i]=2*CMath::RandomReal()-1;
//--- function call
CMLPE::MLPEProcess(ensemble,x1,y1);
v=0;
//--- search errors
for(i=0;i<=nout-1;i++)
{
v=v+y1[i];
err=err || y1[i]<0.0;
}
err=err || MathAbs(v-1)>1000*CMath::m_machineepsilon;
}
//--- check
if(nkind==2)
{
//--- B-type network outputs are bounded from above/below
for(i=0;i<=nin-1;i++)
x1[i]=2*CMath::RandomReal()-1;
//--- function call
CMLPE::MLPEProcess(ensemble,x1,y1);
for(i=0;i<=nout-1;i++)
{
//--- check
if(a2>=0.0)
err=err || y1[i]<a1;
else
err=err || y1[i]>a1;
}
}
//--- check
if(nkind==3)
{
//--- R-type network outputs are within [A1,A2] (or [A2,A1])
for(i=0;i<=nin-1;i++)
x1[i]=2*CMath::RandomReal()-1;
//--- function call
CMLPE::MLPEProcess(ensemble,x1,y1);
//--- search errors
for(i=0;i<=nout-1;i++)
err=(err || y1[i]<MathMin(a1,a2)) || y1[i]>MathMax(a1,a2);
}
}
}
//+------------------------------------------------------------------+
//| Testing class CPCAnalysis |
//+------------------------------------------------------------------+
class CTestPCAUnit
{
private:
//--- private method
static void CalculateMV(double &x[],const int n,double &mean,double &means,double &stddev,double &stddevs);
public:
//--- constructor, destructor
CTestPCAUnit(void);
~CTestPCAUnit(void);
//--- public method
static bool TestPCA(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestPCAUnit::CTestPCAUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestPCAUnit::~CTestPCAUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CPCAnalysis |
//+------------------------------------------------------------------+
static bool CTestPCAUnit::TestPCA(const bool silent)
{
//--- create variables
int passcount=0;
int maxn=0;
int maxm=0;
double threshold=0;
int m=0;
int n=0;
int i=0;
int j=0;
int k=0;
int info=0;
double t=0;
double h=0;
double tmean=0;
double tmeans=0;
double tstddev=0;
double tstddevs=0;
double tmean2=0;
double tmeans2=0;
double tstddev2=0;
double tstddevs2=0;
bool pcaconverrors;
bool pcaorterrors;
bool pcavarerrors;
bool pcaopterrors;
bool waserrors;
int i_=0;
//--- create arrays
double means[];
double s[];
double t2[];
double t3[];
//--- create matrix
CMatrixDouble v;
CMatrixDouble x;
//--- Primary settings
maxm=10;
maxn=100;
passcount=1;
threshold=1000*CMath::m_machineepsilon;
waserrors=false;
pcaconverrors=false;
pcaorterrors=false;
pcavarerrors=false;
pcaopterrors=false;
//--- Test 1: N random points in M-dimensional space
for(m=1;m<=maxm;m++)
{
for(n=1;n<=maxn;n++)
{
//--- Generate task
x.Resize(n,m);
ArrayResize(means,m);
//--- change values
for(j=0;j<=m-1;j++)
means[j]=1.5*CMath::RandomReal()-0.75;
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
x[i].Set(j,means[j]+(2*CMath::RandomReal()-1));
}
//--- Solve
CPCAnalysis::PCABuildBasis(x,n,m,info,s,v);
//--- check
if(info!=1)
{
pcaconverrors=true;
continue;
}
//--- Orthogonality test
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- change value
t=0.0;
for(i_=0;i_<=m-1;i_++)
t+=v[i_][i]*v[i_][j];
//--- check
if(i==j)
t=t-1;
//--- search errors
pcaorterrors=pcaorterrors || MathAbs(t)>threshold;
}
}
//--- Variance test
ArrayResize(t2,n);
for(k=0;k<=m-1;k++)
{
for(i=0;i<=n-1;i++)
{
//--- change value
t=0.0;
for(i_=0;i_<=m-1;i_++)
t+=x[i][i_]*v[i_][k];
t2[i]=t;
}
//--- function call
CalculateMV(t2,n,tmean,tmeans,tstddev,tstddevs);
//--- check
if(n!=1)
t=CMath::Sqr(tstddev)*n/(n-1);
else
t=0;
//--- search errors
pcavarerrors=pcavarerrors || MathAbs(t-s[k])>threshold;
}
//--- search errors
for(k=0;k<=m-2;k++)
pcavarerrors=pcavarerrors || s[k]<s[k+1];
//--- Optimality: different perturbations in V[..,0] can't
//--- increase variance of projection - can only decrease.
ArrayResize(t2,n);
ArrayResize(t3,n);
for(i=0;i<=n-1;i++)
{
//--- change values
t=0.0;
for(i_=0;i_<=m-1;i_++)
t+=x[i][i_]*v[i_][0];
t2[i]=t;
}
//--- function call
CalculateMV(t2,n,tmean,tmeans,tstddev,tstddevs);
//--- calculation
for(k=0;k<=2*m-1;k++)
{
h=0.001;
//--- check
if(k%2!=0)
h=-h;
for(i_=0;i_<=n-1;i_++)
t3[i_]=t2[i_];
for(i_=0;i_<=n-1;i_++)
t3[i_]=t3[i_]+h*x[i_][k/2];
//--- change value
t=0;
for(j=0;j<=m-1;j++)
{
//--- check
if(j!=k/2)
t=t+CMath::Sqr(v[j][0]);
else
t=t+CMath::Sqr(v[j][0]+h);
}
t=1/MathSqrt(t);
for(i_=0;i_<=n-1;i_++)
t3[i_]=t*t3[i_];
//--- function call
CalculateMV(t3,n,tmean2,tmeans2,tstddev2,tstddevs2);
//--- search errors
pcaopterrors=pcaopterrors || tstddev2>tstddev+threshold;
}
}
}
//--- Special test for N=0
for(m=1;m<=maxm;m++)
{
//--- Solve
CPCAnalysis::PCABuildBasis(x,0,m,info,s,v);
//--- check
if(info!=1)
{
pcaconverrors=true;
continue;
}
//--- Orthogonality test
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
{
//--- change value
t=0.0;
for(i_=0;i_<=m-1;i_++)
t+=v[i_][i]*v[i_][j];
//--- check
if(i==j)
t=t-1;
//--- search errors
pcaorterrors=pcaorterrors || MathAbs(t)>threshold;
}
}
}
//--- Final report
waserrors=((pcaconverrors || pcaorterrors) || pcavarerrors) || pcaopterrors;
//--- check
if(!silent)
{
Print("PCA TEST");
Print("TOTAL RESULTS: ");
//--- check
if(!waserrors)
Print("OK");
else
Print("FAILED");
Print("* CONVERGENCE ");
//--- check
if(!pcaconverrors)
Print("OK");
else
Print("FAILED");
Print("* ORTOGONALITY ");
//--- check
if(!pcaorterrors)
Print("OK");
else
Print("FAILED");
Print("* VARIANCE REPORT ");
//--- check
if(!pcavarerrors)
Print("OK");
else
Print("FAILED");
Print("* OPTIMALITY ");
//--- check
if(!pcaopterrors)
Print("OK");
else
Print("FAILED");
//--- check
if(waserrors)
Print("TEST SUMMARY: FAILED");
else
Print("TEST SUMMARY: PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Moments estimates and their errors |
//+------------------------------------------------------------------+
static void CTestPCAUnit::CalculateMV(double &x[],const int n,double &mean,
double &means,double &stddev,double &stddevs)
{
//--- create variables
int i=0;
double v1=0;
double v2=0;
double variance=0;
//--- initialization
mean=0;
means=1;
stddev=0;
stddevs=1;
variance=0;
//--- check
if(n<=1)
return;
//--- Mean
for(i=0;i<=n-1;i++)
mean=mean+x[i];
mean=mean/n;
//--- Variance (using corrected two-pass algorithm)
if(n!=1)
{
//--- change values
v1=0;
for(i=0;i<=n-1;i++)
v1=v1+CMath::Sqr(x[i]-mean);
v2=0;
for(i=0;i<=n-1;i++)
v2=v2+(x[i]-mean);
v2=CMath::Sqr(v2)/n;
variance=(v1-v2)/n;
//--- check
if(variance<0.0)
variance=0;
stddev=MathSqrt(variance);
}
//--- Errors
means=stddev/MathSqrt(n);
stddevs=stddev*MathSqrt(2)/MathSqrt(n-1);
}
//+------------------------------------------------------------------+
//| Testing class CODESolver |
//+------------------------------------------------------------------+
class CTestODESolverUnit
{
private:
//--- private methods
static void Unset2D(CMatrixDouble &x);
static void Unset1D(double &x[]);
static void UnsetRep(CODESolverReport &rep);
public:
//--- constructor, destructor
CTestODESolverUnit(void);
~CTestODESolverUnit(void);
//--- public method
static bool TestODESolver(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestODESolverUnit::CTestODESolverUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestODESolverUnit::~CTestODESolverUnit(void)
{
}
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
static bool CTestODESolverUnit::TestODESolver(const bool silent)
{
//--- create variables
int passcount=0;
bool curerrors;
bool rkckerrors;
bool waserrors;
double h=0;
double eps=0;
int solver=0;
int pass=0;
int mynfev=0;
double v=0;
int m=0;
int m2=0;
int i=0;
double err=0;
int i_=0;
//--- create arrays
double xtbl[];
double xg[];
double y[];
//--- create matrix
CMatrixDouble ytbl;
//--- objects of classes
CODESolverReport rep;
CODESolverState state;
//--- initialization
rkckerrors=false;
waserrors=false;
passcount=10;
//--- simple test: just A*sin(x)+B*cos(x)
if(!CAp::Assert(passcount>=2))
return(false);
for(pass=0;pass<=passcount-1;pass++)
{
for(solver=0;solver<=0;solver++)
{
//--- prepare
h=1.0E-2;
eps=1.0E-5;
//--- check
if(pass%2==0)
eps=-eps;
//--- allocation
ArrayResize(y,2);
//--- change values
for(i=0;i<=1;i++)
y[i]=2*CMath::RandomReal()-1;
m=2+CMath::RandomInteger(10);
//--- allocation
ArrayResize(xg,m);
xg[0]=(m-1)*CMath::RandomReal();
for(i=1;i<=m-1;i++)
xg[i]=xg[i-1]+CMath::RandomReal();
//--- change values
v=2*M_PI/(xg[m-1]-xg[0]);
for(i_=0;i_<=m-1;i_++)
xg[i_]=v*xg[i_];
//--- check
if(CMath::RandomReal()>0.5)
{
for(i_=0;i_<=m-1;i_++)
xg[i_]=-1*xg[i_];
}
mynfev=0;
//--- choose solver
if(solver==0)
CODESolver::ODESolverRKCK(y,2,xg,m,eps,h,state);
//--- solve
while(CODESolver::ODESolverIteration(state))
{
state.m_dy[0]=state.m_y[1];
state.m_dy[1]=-state.m_y[0];
mynfev=mynfev+1;
}
//--- function call
CODESolver::ODESolverResults(state,m2,xtbl,ytbl,rep);
//--- check results
curerrors=false;
//--- check
if(rep.m_terminationtype<=0)
curerrors=true;
else
{
//--- search errors
curerrors=curerrors || m2!=m;
err=0;
for(i=0;i<=m-1;i++)
{
err=MathMax(err,MathAbs(ytbl[i][0]-(y[0]*MathCos(xtbl[i]-xtbl[0])+y[1]*MathSin(xtbl[i]-xtbl[0]))));
err=MathMax(err,MathAbs(ytbl[i][1]-(-(y[0]*MathSin(xtbl[i]-xtbl[0]))+y[1]*MathCos(xtbl[i]-xtbl[0]))));
}
curerrors=curerrors || err>10*MathAbs(eps);
curerrors=curerrors || mynfev!=rep.m_nfev;
}
//--- check
if(solver==0)
rkckerrors=rkckerrors || curerrors;
}
}
//--- another test:
//--- y(0)=0
//--- dy/dx=f(x,y)
//--- f(x,y)=0, x<1
//--- x-1,x>=1
//--- with BOTH absolute and fractional tolerances.
//--- Starting from zero will be real challenge for
//--- fractional tolerance.
if(!CAp::Assert(passcount>=2))
return(false);
for(pass=0;pass<=passcount-1;pass++)
{
h=1.0E-4;
eps=1.0E-4;
//--- check
if(pass%2==0)
eps=-eps;
//--- allocation
ArrayResize(y,1);
y[0]=0;
m=21;
//--- allocation
ArrayResize(xg,m);
for(i=0;i<=m-1;i++)
xg[i]=(double)(2*i)/(double)(m-1);
mynfev=0;
//--- function call
CODESolver::ODESolverRKCK(y,1,xg,m,eps,h,state);
//--- cycle
while(CODESolver::ODESolverIteration(state))
{
state.m_dy[0]=MathMax(state.m_x-1,0);
mynfev=mynfev+1;
}
//--- function call
CODESolver::ODESolverResults(state,m2,xtbl,ytbl,rep);
//--- check
if(rep.m_terminationtype<=0)
rkckerrors=true;
else
{
//--- search errors
rkckerrors=rkckerrors || m2!=m;
err=0;
for(i=0;i<=m-1;i++)
err=MathMax(err,MathAbs(ytbl[i][0]-CMath::Sqr(MathMax(xg[i]-1,0))/2));
rkckerrors=rkckerrors || err>MathAbs(eps);
rkckerrors=rkckerrors || mynfev!=rep.m_nfev;
}
}
//--- end
waserrors=rkckerrors;
//--- check
if(!silent)
{
Print("TESTING ODE SOLVER");
Print("* RK CASH-KARP: ");
//--- check
if(rkckerrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Unsets real matrix |
//+------------------------------------------------------------------+
static void CTestODESolverUnit::Unset2D(CMatrixDouble &x)
{
//--- allocation
x.Resize(1,1);
//--- change value
x[0].Set(0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets real vector |
//+------------------------------------------------------------------+
static void CTestODESolverUnit::Unset1D(double &x[])
{
//--- allocation
ArrayResize(x,1);
//--- change value
x[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Unsets report |
//+------------------------------------------------------------------+
static void CTestODESolverUnit::UnsetRep(CODESolverReport &rep)
{
//--- change value
rep.m_nfev=0;
}
//+------------------------------------------------------------------+
//| Testing class CFastFourierTransform |
//+------------------------------------------------------------------+
class CTestFFTUnit
{
private:
//--- private methods
static void RefFFTC1D(complex &a[],const int n);
static void RefFFTC1DInv(complex &a[],const int n);
static void RefInternalCFFT(double &a[],const int nn,const bool inversefft);
static void RefInternalRFFT(double &a[],const int nn,complex &f[]);
public:
//--- constructor, destructor
CTestFFTUnit(void);
~CTestFFTUnit(void);
//--- public method
static bool TestFFT(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestFFTUnit::CTestFFTUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestFFTUnit::~CTestFFTUnit(void)
{
}
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
static bool CTestFFTUnit::TestFFT(const bool silent)
{
//--- create variables
int n=0;
int i=0;
int k=0;
int maxn=0;
double bidierr=0;
double bidirerr=0;
double referr=0;
double refrerr=0;
double reinterr=0;
double errtol=0;
bool referrors;
bool bidierrors;
bool refrerrors;
bool bidirerrors;
bool reinterrors;
bool waserrors;
int i_=0;
//--- create arrays
complex a1[];
complex a2[];
complex a3[];
double r1[];
double r2[];
double buf[];
//--- object of class
CFtPlan plan;
//--- initialization
maxn=128;
errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon;
bidierrors=false;
referrors=false;
bidirerrors=false;
refrerrors=false;
reinterrors=false;
waserrors=false;
//--- Test bi-directional error: norm(x-invFFT(FFT(x)))
bidierr=0;
bidirerr=0;
for(n=1;n<=maxn;n++)
{
//--- Complex FFT/invFFT
ArrayResize(a1,n);
ArrayResize(a2,n);
ArrayResize(a3,n);
for(i=0;i<=n-1;i++)
{
a1[i].re=2*CMath::RandomReal()-1;
a1[i].im=2*CMath::RandomReal()-1;
a2[i]=a1[i];
a3[i]=a1[i];
}
//--- function calls
CFastFourierTransform::FFTC1D(a2,n);
CFastFourierTransform::FFTC1DInv(a2,n);
CFastFourierTransform::FFTC1DInv(a3,n);
CFastFourierTransform::FFTC1D(a3,n);
//--- search errors
for(i=0;i<=n-1;i++)
{
bidierr=MathMax(bidierr,CMath::AbsComplex(a1[i]-a2[i]));
bidierr=MathMax(bidierr,CMath::AbsComplex(a1[i]-a3[i]));
}
//--- Real
ArrayResize(r1,n);
ArrayResize(r2,n);
//--- change values
for(i=0;i<=n-1;i++)
{
r1[i]=2*CMath::RandomReal()-1;
r2[i]=r1[i];
}
//--- function call
CFastFourierTransform::FFTR1D(r2,n,a1);
for(i_=0;i_<=n-1;i_++)
r2[i_]=0*r2[i_];
//--- function call
CFastFourierTransform::FFTR1DInv(a1,n,r2);
//--- search errors
for(i=0;i<=n-1;i++)
bidirerr=MathMax(bidirerr,CMath::AbsComplex(r1[i]-r2[i]));
}
//--- search errors
bidierrors=bidierrors || bidierr>errtol;
bidirerrors=bidirerrors || bidirerr>errtol;
//--- Test against reference O(N^2) implementation
referr=0;
refrerr=0;
for(n=1;n<=maxn;n++)
{
//--- Complex FFT
ArrayResize(a1,n);
ArrayResize(a2,n);
for(i=0;i<=n-1;i++)
{
a1[i].re=2*CMath::RandomReal()-1;
a1[i].im=2*CMath::RandomReal()-1;
a2[i]=a1[i];
}
//--- function calls
CFastFourierTransform::FFTC1D(a1,n);
RefFFTC1D(a2,n);
//--- search errors
for(i=0;i<=n-1;i++)
referr=MathMax(referr,CMath::AbsComplex(a1[i]-a2[i]));
//--- Complex inverse FFT
ArrayResize(a1,n);
ArrayResize(a2,n);
for(i=0;i<=n-1;i++)
{
a1[i].re=2*CMath::RandomReal()-1;
a1[i].im=2*CMath::RandomReal()-1;
a2[i]=a1[i];
}
//--- function calls
CFastFourierTransform::FFTC1DInv(a1,n);
RefFFTC1DInv(a2,n);
//--- search errors
for(i=0;i<=n-1;i++)
referr=MathMax(referr,CMath::AbsComplex(a1[i]-a2[i]));
//--- Real forward/inverse FFT:
//--- * calculate and check forward FFT
//--- * use precalculated FFT to check backward FFT
//--- fill unused parts of frequencies array with random numbers
//--- to ensure that they are not really used
ArrayResize(r1,n);
ArrayResize(r2,n);
for(i=0;i<=n-1;i++)
{
r1[i]=2*CMath::RandomReal()-1;
r2[i]=r1[i];
}
//--- function calls
CFastFourierTransform::FFTR1D(r1,n,a1);
RefInternalRFFT(r2,n,a2);
//--- search errors
for(i=0;i<=n-1;i++)
refrerr=MathMax(refrerr,CMath::AbsComplex(a1[i]-a2[i]));
//--- allocation
ArrayResize(a3,(int)MathFloor((double)n/2.0)+1);
for(i=0;i<=(int)MathFloor((double)n/2.0);i++)
a3[i]=a2[i];
a3[0].im=2*CMath::RandomReal()-1;
//--- check
if(n%2==0)
a3[(int)MathFloor((double)n/2.0)].im=2*CMath::RandomReal()-1;
for(i=0;i<=n-1;i++)
r1[i]=0;
//--- function call
CFastFourierTransform::FFTR1DInv(a3,n,r1);
//--- search errors
for(i=0;i<=n-1;i++)
refrerr=MathMax(refrerr,MathAbs(r2[i]-r1[i]));
}
//--- search errors
referrors=referrors || referr>errtol;
refrerrors=refrerrors || refrerr>errtol;
//--- test internal real even FFT
reinterr=0;
for(k=1;k<=maxn/2;k++)
{
n=2*k;
//--- Real forward FFT
ArrayResize(r1,n);
ArrayResize(r2,n);
for(i=0;i<=n-1;i++)
{
r1[i]=2*CMath::RandomReal()-1;
r2[i]=r1[i];
}
//--- function call
CFtBase::FtBaseGenerateComplexFFtPlan(n/2,plan);
//--- allocation
ArrayResize(buf,n);
//--- function calls
CFastFourierTransform::FFTR1DInternalEven(r1,n,buf,plan);
RefInternalRFFT(r2,n,a2);
//--- search errors
reinterr=MathMax(reinterr,MathAbs(r1[0]-a2[0].re));
reinterr=MathMax(reinterr,MathAbs(r1[1]-a2[n/2].re));
for(i=1;i<=n/2-1;i++)
{
reinterr=MathMax(reinterr,MathAbs(r1[2*i+0]-a2[i].re));
reinterr=MathMax(reinterr,MathAbs(r1[2*i+1]-a2[i].im));
}
//--- Real backward FFT
ArrayResize(r1,n);
for(i=0;i<=n-1;i++)
r1[i]=2*CMath::RandomReal()-1;
ArrayResize(a2,(int)MathFloor((double)n/2.0)+1);
a2[0]=r1[0];
for(i=1;i<=(int)MathFloor((double)n/2.0)-1;i++)
{
a2[i].re=r1[2*i+0];
a2[i].im=r1[2*i+1];
}
a2[(int)MathFloor((double)n/2.0)]=r1[1];
//--- function call
CFtBase::FtBaseGenerateComplexFFtPlan(n/2,plan);
//--- allocation
ArrayResize(buf,n);
//--- function calls
CFastFourierTransform::FFTR1DInvInternalEven(r1,n,buf,plan);
CFastFourierTransform::FFTR1DInv(a2,n,r2);
//--- search errors
for(i=0;i<=n-1;i++)
reinterr=MathMax(reinterr,MathAbs(r1[i]-r2[i]));
}
//--- search errors
reinterrors=reinterrors || reinterr>errtol;
//--- end
waserrors=(((bidierrors || bidirerrors) || referrors) || refrerrors) || reinterrors;
//--- check
if(!silent)
{
Print("TESTING FFT");
Print("FINAL RESULT: ");
//--- check
if(waserrors)
Print("FAILED");
else
Print("OK");
Print("* BI-DIRECTIONAL COMPLEX TEST: ");
//--- check
if(bidierrors)
Print("FAILED");
else
Print("OK");
Print("* AGAINST REFERENCE COMPLEX FFT: ");
//--- check
if(referrors)
Print("FAILED");
else
Print("OK");
Print("* BI-DIRECTIONAL REAL TEST: ");
//--- check
if(bidirerrors)
Print("FAILED");
else
Print("OK");
Print("* AGAINST REFERENCE REAL FFT: ");
//--- check
if(refrerrors)
Print("FAILED");
else
Print("OK");
Print("* INTERNAL EVEN FFT: ");
//--- check
if(reinterrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Reference FFT |
//+------------------------------------------------------------------+
static void CTestFFTUnit::RefFFTC1D(complex &a[],const int n)
{
//--- create variables
int i=0;
//--- create array
double buf[];
//--- check
if(!CAp::Assert(n>0,"FFTC1D: incorrect N!"))
return;
//--- allocation
ArrayResize(buf,2*n);
//--- copy
for(i=0;i<=n-1;i++)
{
buf[2*i+0]=a[i].re;
buf[2*i+1]=a[i].im;
}
//--- function call
RefInternalCFFT(buf,n,false);
//--- copy
for(i=0;i<=n-1;i++)
{
a[i].re=buf[2*i+0];
a[i].im=buf[2*i+1];
}
}
//+------------------------------------------------------------------+
//| Reference inverse FFT |
//+------------------------------------------------------------------+
static void CTestFFTUnit::RefFFTC1DInv(complex &a[],const int n)
{
//--- create variables
int i=0;
//--- create array
double buf[];
//--- check
if(!CAp::Assert(n>0,"FFTC1DInv: incorrect N!"))
return;
//--- allocation
ArrayResize(buf,2*n);
//--- copy
for(i=0;i<=n-1;i++)
{
buf[2*i+0]=a[i].re;
buf[2*i+1]=a[i].im;
}
//--- function call
RefInternalCFFT(buf,n,true);
//--- copy
for(i=0;i<=n-1;i++)
{
a[i].re=buf[2*i+0];
a[i].im=buf[2*i+1];
}
}
//+------------------------------------------------------------------+
//| Internal complex FFT stub. |
//| Uses straightforward formula with O(N^2) complexity. |
//+------------------------------------------------------------------+
static void CTestFFTUnit::RefInternalCFFT(double &a[],const int nn,
const bool inversefft)
{
//--- create variables
int i=0;
int k=0;
double hre=0;
double him=0;
double c=0;
double s=0;
double re=0;
double im=0;
//--- create array
double tmp[];
//--- allocation
ArrayResize(tmp,2*nn);
//--- check
if(!inversefft)
{
for(i=0;i<=nn-1;i++)
{
//--- change values
hre=0;
him=0;
//--- calculation
for(k=0;k<=nn-1;k++)
{
re=a[2*k];
im=a[2*k+1];
c=MathCos(-(2*M_PI*k*i/nn));
s=MathSin(-(2*M_PI*k*i/nn));
hre=hre+c*re-s*im;
him=him+c*im+s*re;
}
//--- change values
tmp[2*i]=hre;
tmp[2*i+1]=him;
}
for(i=0;i<=2*nn-1;i++)
a[i]=tmp[i];
}
else
{
for(k=0;k<=nn-1;k++)
{
//--- change values
hre=0;
him=0;
//--- calculation
for(i=0;i<=nn-1;i++)
{
re=a[2*i];
im=a[2*i+1];
c=MathCos(2*M_PI*k*i/nn);
s=MathSin(2*M_PI*k*i/nn);
hre=hre+c*re-s*im;
him=him+c*im+s*re;
}
//--- change values
tmp[2*k]=hre/nn;
tmp[2*k+1]=him/nn;
}
for(i=0;i<=2*nn-1;i++)
a[i]=tmp[i];
}
}
//+------------------------------------------------------------------+
//| Internal real FFT stub. |
//| Uses straightforward formula with O(N^2) complexity. |
//+------------------------------------------------------------------+
static void CTestFFTUnit::RefInternalRFFT(double &a[],const int nn,complex &f[])
{
//--- create a variable
int i=0;
//--- create array
double tmp[];
//--- allocation
ArrayResize(tmp,2*nn);
//--- copy
for(i=0;i<=nn-1;i++)
{
tmp[2*i]=a[i];
tmp[2*i+1]=0;
}
//--- function call
RefInternalCFFT(tmp,nn,false);
//--- allocation
ArrayResize(f,nn);
//--- copy
for(i=0;i<=nn-1;i++)
{
f[i].re=tmp[2*i+0];
f[i].im=tmp[2*i+1];
}
}
//+------------------------------------------------------------------+
//| Testing class CConv |
//+------------------------------------------------------------------+
class CTestConvUnit
{
private:
//--- private methods
static void RefConvC1D(complex &a[],const int m,complex &b[],const int n,complex &r[]);
static void RefConvC1DCircular(complex &a[],const int m,complex &b[],const int n,complex &r[]);
static void RefConvR1D(double &a[],const int m,double &b[],const int n,double &r[]);
static void RefConvR1DCircular(double &a[],const int m,double &b[],const int n,double &r[]);
public:
//--- constructor, destructor
CTestConvUnit(void);
~CTestConvUnit(void);
//--- public method
static bool TestConv(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestConvUnit::CTestConvUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestConvUnit::~CTestConvUnit(void)
{
}
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
static bool CTestConvUnit::TestConv(const bool silent)
{
//--- create variables
int m=0;
int n=0;
int i=0;
int rkind=0;
int circkind=0;
int maxn=0;
double referr=0;
double refrerr=0;
double inverr=0;
double invrerr=0;
double errtol=0;
bool referrors;
bool refrerrors;
bool inverrors;
bool invrerrors;
bool waserrors;
//--- create arrays
double ra[];
double rb[];
double rr1[];
double rr2[];
complex ca[];
complex cb[];
complex cr1[];
complex cr2[];
//--- initialization
maxn=32;
errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon;
referrors=false;
refrerrors=false;
inverrors=false;
invrerrors=false;
waserrors=false;
//--- Test against reference O(N^2) implementation.
//--- Automatic ConvC1D() and different algorithms of ConvC1DX() are tested.
referr=0;
refrerr=0;
for(m=1;m<=maxn;m++)
{
for(n=1;n<=maxn;n++)
{
for(circkind=0;circkind<=1;circkind++)
{
for(rkind=-3;rkind<=1;rkind++)
{
//--- skip impossible combinations of parameters:
//--- * circular convolution,M<N,RKind<>-3 - internal subroutine does not support M<N.
if((circkind!=0 && m<n) && rkind!=-3)
continue;
//--- Complex convolution
ArrayResize(ca,m);
for(i=0;i<=m-1;i++)
{
ca[i].re=2*CMath::RandomReal()-1;
ca[i].im=2*CMath::RandomReal()-1;
}
//--- allocation
ArrayResize(cb,n);
for(i=0;i<=n-1;i++)
{
cb[i].re=2*CMath::RandomReal()-1;
cb[i].im=2*CMath::RandomReal()-1;
}
//--- allocation
ArrayResize(cr1,1);
//--- check
if(rkind==-3)
{
//--- test wrapper subroutine:
//--- * circular/non-circular
if(circkind==0)
CConv::ConvC1D(ca,m,cb,n,cr1);
else
CConv::ConvC1DCircular(ca,m,cb,n,cr1);
}
else
{
//--- test internal subroutine
if(m>=n)
{
//--- test internal subroutine:
//--- * circular/non-circular mode
CConv::ConvC1DX(ca,m,cb,n,circkind!=0,rkind,0,cr1);
}
else
{
//--- test internal subroutine - circular mode only
if(!CAp::Assert(circkind==0,"Convolution test: internal error!"))
return(false);
//--- function call
CConv::ConvC1DX(cb,n,ca,m,false,rkind,0,cr1);
}
}
//--- check
if(circkind==0)
RefConvC1D(ca,m,cb,n,cr2);
else
RefConvC1DCircular(ca,m,cb,n,cr2);
//--- check
if(circkind==0)
{
for(i=0;i<=m+n-2;i++)
referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i]));
}
else
{
for(i=0;i<=m-1;i++)
referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i]));
}
//--- Real convolution
ArrayResize(ra,m);
for(i=0;i<=m-1;i++)
ra[i]=2*CMath::RandomReal()-1;
//--- allocation
ArrayResize(rb,n);
for(i=0;i<=n-1;i++)
rb[i]=2*CMath::RandomReal()-1;
//--- allocation
ArrayResize(rr1,1);
//--- check
if(rkind==-3)
{
//--- test wrapper subroutine:
//--- * circular/non-circular
if(circkind==0)
CConv::ConvR1D(ra,m,rb,n,rr1);
else
CConv::ConvR1DCircular(ra,m,rb,n,rr1);
}
else
{
//--- check
if(m>=n)
{
//--- test internal subroutine:
//--- * circular/non-circular mode
CConv::ConvR1DX(ra,m,rb,n,circkind!=0,rkind,0,rr1);
}
else
{
//--- test internal subroutine - non-circular mode only
CConv::ConvR1DX(rb,n,ra,m,circkind!=0,rkind,0,rr1);
}
}
//--- check
if(circkind==0)
RefConvR1D(ra,m,rb,n,rr2);
else
RefConvR1DCircular(ra,m,rb,n,rr2);
//--- check
if(circkind==0)
{
for(i=0;i<=m+n-2;i++)
refrerr=MathMax(refrerr,MathAbs(rr1[i]-rr2[i]));
}
else
{
for(i=0;i<=m-1;i++)
refrerr=MathMax(refrerr,MathAbs(rr1[i]-rr2[i]));
}
}
}
}
}
//--- search errors
referrors=referrors || referr>errtol;
refrerrors=refrerrors || refrerr>errtol;
//--- Test inverse convolution
inverr=0;
invrerr=0;
for(m=1;m<=maxn;m++)
{
for(n=1;n<=maxn;n++)
{
//--- Complex circilar and non-circular
ArrayResize(ca,m);
for(i=0;i<=m-1;i++)
{
ca[i].re=2*CMath::RandomReal()-1;
ca[i].im=2*CMath::RandomReal()-1;
}
//--- allocation
ArrayResize(cb,n);
for(i=0;i<=n-1;i++)
{
cb[i].re=2*CMath::RandomReal()-1;
cb[i].im=2*CMath::RandomReal()-1;
}
//--- allocation
ArrayResize(cr1,1);
ArrayResize(cr2,1);
//--- function calls
CConv::ConvC1D(ca,m,cb,n,cr2);
CConv::ConvC1DInv(cr2,m+n-1,cb,n,cr1);
//--- search errors
for(i=0;i<=m-1;i++)
{
inverr=MathMax(inverr,CMath::AbsComplex(cr1[i]-ca[i]));
}
//--- allocation
ArrayResize(cr1,1);
ArrayResize(cr2,1);
//--- function calls
CConv::ConvC1DCircular(ca,m,cb,n,cr2);
CConv::ConvC1DCircularInv(cr2,m,cb,n,cr1);
//--- search errors
for(i=0;i<=m-1;i++)
inverr=MathMax(inverr,CMath::AbsComplex(cr1[i]-ca[i]));
//--- Real circilar and non-circular
ArrayResize(ra,m);
for(i=0;i<=m-1;i++)
ra[i]=2*CMath::RandomReal()-1;
//--- allocation
ArrayResize(rb,n);
for(i=0;i<=n-1;i++)
rb[i]=2*CMath::RandomReal()-1;
//--- allocation
ArrayResize(rr1,1);
ArrayResize(rr2,1);
//--- function calls
CConv::ConvR1D(ra,m,rb,n,rr2);
CConv::ConvR1DInv(rr2,m+n-1,rb,n,rr1);
//--- search errors
for(i=0;i<=m-1;i++)
invrerr=MathMax(invrerr,MathAbs(rr1[i]-ra[i]));
//--- allocation
ArrayResize(rr1,1);
ArrayResize(rr2,1);
//--- function calls
CConv::ConvR1DCircular(ra,m,rb,n,rr2);
CConv::ConvR1DCircularInv(rr2,m,rb,n,rr1);
//--- search errors
for(i=0;i<=m-1;i++)
invrerr=MathMax(invrerr,MathAbs(rr1[i]-ra[i]));
}
}
//--- search errors
inverrors=inverrors || inverr>errtol;
invrerrors=invrerrors || invrerr>errtol;
//--- end
waserrors=((referrors || refrerrors) || inverrors) || invrerrors;
//--- check
if(!silent)
{
Print("TESTING CONVOLUTION");
Print("FINAL RESULT: ");
//--- check
if(waserrors)
Print("FAILED");
else
Print("OK");
Print("* AGAINST REFERENCE COMPLEX CONV: ");
//--- check
if(referrors)
Print("FAILED");
else
Print("OK");
Print("* AGAINST REFERENCE REAL CONV: ");
//--- check
if(refrerrors)
Print("FAILED");
else
Print("OK");
Print("* COMPLEX INVERSE: ");
//--- check
if(inverrors)
Print("FAILED");
else
Print("OK");
Print("* REAL INVERSE: ");
//--- check
if(invrerrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
static void CTestConvUnit::RefConvC1D(complex &a[],const int m,complex &b[],
const int n,complex &r[])
{
//--- create variables
int i=0;
complex v=0;
int i_=0;
int i1_=0;
//--- allocation
ArrayResize(r,m+n-1);
//--- initialization
for(i=0;i<=m+n-2;i++)
r[i]=0;
//--- calculation
for(i=0;i<=m-1;i++)
{
v=a[i];
i1_=-i;
for(i_=i;i_<=i+n-1;i_++)
r[i_]=r[i_]+v*b[i_+i1_];
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
static void CTestConvUnit::RefConvC1DCircular(complex &a[],const int m,
complex &b[],const int n,
complex &r[])
{
//--- create variables
int i1=0;
int i2=0;
int j2=0;
int i_=0;
int i1_=0;
//--- create array
complex buf[];
//--- function call
RefConvC1D(a,m,b,n,buf);
//--- allocation
ArrayResize(r,m);
//--- copy
for(i_=0;i_<=m-1;i_++)
r[i_]=buf[i_];
//--- calculation
i1=m;
while(i1<=m+n-2)
{
//--- change values
i2=MathMin(i1+m-1,m+n-2);
j2=i2-i1;
i1_=i1;
for(i_=0;i_<=j2;i_++)
r[i_]=r[i_]+buf[i_+i1_];
i1=i1+m;
}
}
//+------------------------------------------------------------------+
//| Reference FFT |
//+------------------------------------------------------------------+
static void CTestConvUnit::RefConvR1D(double &a[],const int m,double &b[],
const int n,double &r[])
{
//--- create variables
int i=0;
double v=0;
int i_=0;
int i1_=0;
//--- allocation
ArrayResize(r,m+n-1);
//--- initialization
for(i=0;i<=m+n-2;i++)
r[i]=0;
//--- calculation
for(i=0;i<=m-1;i++)
{
v=a[i];
i1_=-i;
for(i_=i;i_<=i+n-1;i_++)
r[i_]=r[i_]+v*b[i_+i1_];
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
static void CTestConvUnit::RefConvR1DCircular(double &a[],const int m,
double &b[],const int n,
double &r[])
{
//--- create variables
int i1=0;
int i2=0;
int j2=0;
int i_=0;
int i1_=0;
//--- create array
double buf[];
//--- function call
RefConvR1D(a,m,b,n,buf);
//--- allocation
ArrayResize(r,m);
//--- copy
for(i_=0;i_<=m-1;i_++)
r[i_]=buf[i_];
//--- calculation
i1=m;
while(i1<=m+n-2)
{
//--- change values
i2=MathMin(i1+m-1,m+n-2);
j2=i2-i1;
i1_=i1;
for(i_=0;i_<=j2;i_++)
r[i_]=r[i_]+buf[i_+i1_];
i1=i1+m;
}
}
//+------------------------------------------------------------------+
//| Testing class CCorr |
//+------------------------------------------------------------------+
class CTestCorrUnit
{
private:
//--- private methods
static void RefCorrC1D(complex &signal[],const int n,complex &pattern[],const int m,complex &r[]);
static void RefCorrC1DCircular(complex &signal[],const int n,complex &pattern[],const int m,complex &r[]);
static void RefCorrR1D(double &signal[],const int n,double &pattern[],const int m,double &r[]);
static void RefCorrR1DCircular(double &signal[],const int n,double &pattern[],const int m,double &r[]);
static void RefConvC1D(complex &a[],const int m,complex &b[],const int n,complex &r[]);
static void RefConvC1DCircular(complex &a[],const int m,complex &b[],const int n,complex &r[]);
static void RefConvR1D(double &a[],const int m,double &b[],const int n,double &r[]);
static void RefConvR1DCircular(double &a[],const int m,double &b[],const int n,double &r[]);
public:
//--- constructor, destructor
CTestCorrUnit(void);
~CTestCorrUnit(void);
//--- public method
static bool TestCorr(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestCorrUnit::CTestCorrUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestCorrUnit::~CTestCorrUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CCorr |
//+------------------------------------------------------------------+
static bool CTestCorrUnit::TestCorr(const bool silent)
{
//--- create variables
int m=0;
int n=0;
int i=0;
int maxn=0;
double referr=0;
double refrerr=0;
double errtol=0;
bool referrors;
bool refrerrors;
bool inverrors;
bool invrerrors;
bool waserrors;
//--- create arrays
double ra[];
double rb[];
double rr1[];
double rr2[];
complex ca[];
complex cb[];
complex cr1[];
complex cr2[];
//--- initialization
maxn=32;
errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon;
referrors=false;
refrerrors=false;
inverrors=false;
invrerrors=false;
waserrors=false;
//--- Test against reference O(N^2) implementation.
referr=0;
refrerr=0;
for(m=1;m<=maxn;m++)
{
for(n=1;n<=maxn;n++)
{
//--- Complex correlation
ArrayResize(ca,m);
for(i=0;i<=m-1;i++)
{
ca[i].re=2*CMath::RandomReal()-1;
ca[i].im=2*CMath::RandomReal()-1;
}
//--- allocation
ArrayResize(cb,n);
for(i=0;i<=n-1;i++)
{
cb[i].re=2*CMath::RandomReal()-1;
cb[i].im=2*CMath::RandomReal()-1;
}
//--- allocation
ArrayResize(cr1,1);
//--- function calls
CCorr::CorrC1D(ca,m,cb,n,cr1);
RefCorrC1D(ca,m,cb,n,cr2);
//--- search errors
for(i=0;i<=m+n-2;i++)
referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i]));
//--- allocation
ArrayResize(cr1,1);
//--- function calls
CCorr::CorrC1DCircular(ca,m,cb,n,cr1);
RefCorrC1DCircular(ca,m,cb,n,cr2);
//--- search errors
for(i=0;i<=m-1;i++)
referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i]));
//--- Real correlation
ArrayResize(ra,m);
for(i=0;i<=m-1;i++)
ra[i]=2*CMath::RandomReal()-1;
//--- allocation
ArrayResize(rb,n);
for(i=0;i<=n-1;i++)
rb[i]=2*CMath::RandomReal()-1;
//--- allocation
ArrayResize(rr1,1);
//--- function calls
CCorr::CorrR1D(ra,m,rb,n,rr1);
RefCorrR1D(ra,m,rb,n,rr2);
//--- search errors
for(i=0;i<=m+n-2;i++)
refrerr=MathMax(refrerr,MathAbs(rr1[i]-rr2[i]));
//--- allocation
ArrayResize(rr1,1);
//--- function calls
CCorr::CorrR1DCircular(ra,m,rb,n,rr1);
RefCorrR1DCircular(ra,m,rb,n,rr2);
//--- search errors
for(i=0;i<=m-1;i++)
refrerr=MathMax(refrerr,MathAbs(rr1[i]-rr2[i]));
}
}
//--- search errors
referrors=referrors || referr>errtol;
refrerrors=refrerrors || refrerr>errtol;
//--- end
waserrors=referrors || refrerrors;
//--- check
if(!silent)
{
Print("TESTING CORRELATION");
Print("FINAL RESULT: ");
//--- check
if(waserrors)
Print("FAILED");
else
Print("OK");
Print("* AGAINST REFERENCE COMPLEX CORR: ");
//--- check
if(referrors)
Print("FAILED");
else
Print("OK");
Print("* AGAINST REFERENCE REAL CORR: ");
//--- check
if(refrerrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
static void CTestCorrUnit::RefCorrC1D(complex &signal[],const int n,
complex &pattern[],const int m,
complex &r[])
{
//--- create variables
int i=0;
int j=0;
complex v=0;
int i_=0;
//--- create array
complex s[];
//--- allocation
ArrayResize(s,m+n-1);
//--- change values
for(i_=0;i_<=n-1;i_++)
s[i_]=signal[i_];
for(i=n;i<=m+n-2;i++)
s[i]=0;
//--- allocation
ArrayResize(r,m+n-1);
//--- calculation
for(i=0;i<=n-1;i++)
{
v=0;
for(j=0;j<=m-1;j++)
{
//--- check
if(i+j>=n)
break;
v=v+CMath::Conj(pattern[j])*s[i+j];
}
r[i]=v;
}
//--- calculation
for(i=1;i<=m-1;i++)
{
v=0;
for(j=i;j<=m-1;j++)
v=v+CMath::Conj(pattern[j])*s[j-i];
r[m+n-1-i]=v;
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
static void CTestCorrUnit::RefCorrC1DCircular(complex &signal[],const int n,
complex &pattern[],const int m,
complex &r[])
{
//--- create variables
int i=0;
int j=0;
complex v=0;
//--- allocation
ArrayResize(r,n);
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- change value
v=0;
for(j=0;j<=m-1;j++)
v=v+CMath::Conj(pattern[j])*signal[(i+j)%n];
r[i]=v;
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
static void CTestCorrUnit::RefCorrR1D(double &signal[],const int n,
double &pattern[],const int m,
double &r[])
{
//--- create variables
int i=0;
int j=0;
double v=0;
int i_=0;
//--- create array
double s[];
//--- allocation
ArrayResize(s,m+n-1);
//--- change values
for(i_=0;i_<=n-1;i_++)
s[i_]=signal[i_];
for(i=n;i<=m+n-2;i++)
s[i]=0;
//--- allocation
ArrayResize(r,m+n-1);
//--- calculation
for(i=0;i<=n-1;i++)
{
v=0;
for(j=0;j<=m-1;j++)
{
//--- check
if(i+j>=n)
break;
v=v+pattern[j]*s[i+j];
}
r[i]=v;
}
//--- calculation
for(i=1;i<=m-1;i++)
{
v=0;
for(j=i;j<=m-1;j++)
v=v+pattern[j]*s[-i+j];
r[m+n-1-i]=v;
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
static void CTestCorrUnit::RefCorrR1DCircular(double &signal[],const int n,
double &pattern[],const int m,
double &r[])
{
//--- create variables
int i=0;
int j=0;
double v=0;
//--- allocation
ArrayResize(r,n);
//--- calculation
for(i=0;i<=n-1;i++)
{
v=0;
for(j=0;j<=m-1;j++)
v=v+pattern[j]*signal[(i+j)%n];
r[i]=v;
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
static void CTestCorrUnit::RefConvC1D(complex &a[],const int m,complex &b[],
const int n,complex &r[])
{
//--- create variables
int i=0;
complex v=0;
int i_=0;
int i1_=0;
//--- allocation
ArrayResize(r,m+n-1);
//--- initialization
for(i=0;i<=m+n-2;i++)
r[i]=0;
//--- calculation
for(i=0;i<=m-1;i++)
{
v=a[i];
i1_=-i;
for(i_=i;i_<=i+n-1;i_++)
r[i_]=r[i_]+v*b[i_+i1_];
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
static void CTestCorrUnit::RefConvC1DCircular(complex &a[],const int m,
complex &b[],const int n,
complex &r[])
{
//--- create variables
int i1=0;
int i2=0;
int j2=0;
int i_=0;
int i1_=0;
//--- create array
complex buf[];
//--- function call
RefConvC1D(a,m,b,n,buf);
//--- allocation
ArrayResize(r,m);
//--- copy
for(i_=0;i_<=m-1;i_++)
r[i_]=buf[i_];
//--- calculation
i1=m;
while(i1<=m+n-2)
{
//--- change values
i2=MathMin(i1+m-1,m+n-2);
j2=i2-i1;
i1_=i1;
for(i_=0;i_<=j2;i_++)
r[i_]=r[i_]+buf[i_+i1_];
i1=i1+m;
}
}
//+------------------------------------------------------------------+
//| Reference FFT |
//+------------------------------------------------------------------+
static void CTestCorrUnit::RefConvR1D(double &a[],const int m,double &b[],
const int n,double &r[])
{
//--- create variables
int i=0;
double v=0;
int i_=0;
int i1_=0;
//--- allocation
ArrayResize(r,m+n-1);
//--- initialization
for(i=0;i<=m+n-2;i++)
r[i]=0;
//--- calculation
for(i=0;i<=m-1;i++)
{
v=a[i];
i1_=-i;
for(i_=i;i_<=i+n-1;i_++)
r[i_]=r[i_]+v*b[i_+i1_];
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
static void CTestCorrUnit::RefConvR1DCircular(double &a[],const int m,
double &b[],const int n,
double &r[])
{
//--- create variables
int i1=0;
int i2=0;
int j2=0;
int i_=0;
int i1_=0;
//--- create array
double buf[];
//--- function call
RefConvR1D(a,m,b,n,buf);
//--- allocation
ArrayResize(r,m);
//--- copy
for(i_=0;i_<=m-1;i_++)
r[i_]=buf[i_];
//--- calculation
i1=m;
while(i1<=m+n-2)
{
//--- change values
i2=MathMin(i1+m-1,m+n-2);
j2=i2-i1;
i1_=i1;
for(i_=0;i_<=j2;i_++)
r[i_]=r[i_]+buf[i_+i1_];
i1=i1+m;
}
}
//+------------------------------------------------------------------+
//| Testing class CFastHartleyTransform |
//+------------------------------------------------------------------+
class CTestFHTUnit
{
private:
//--- private methods
static void RefFHTR1D(double &a[],const int n);
static void RefFHTR1DInv(double &a[],const int n);
public:
//--- constructor, destructor
CTestFHTUnit(void);
~CTestFHTUnit(void);
//--- public method
static bool TestFHT(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestFHTUnit::CTestFHTUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestFHTUnit::~CTestFHTUnit(void)
{
}
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
static bool CTestFHTUnit::TestFHT(const bool silent)
{
//--- create variables
int n=0;
int i=0;
int maxn=0;
double bidierr=0;
double referr=0;
double errtol=0;
bool referrors;
bool bidierrors;
bool waserrors;
//--- create arrays
double r1[];
double r2[];
double r3[];
//--- initialization
maxn=128;
errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon;
bidierrors=false;
referrors=false;
waserrors=false;
//--- Test bi-directional error: norm(x-invFHT(FHT(x)))
bidierr=0;
for(n=1;n<=maxn;n++)
{
//--- FHT/invFHT
ArrayResize(r1,n);
ArrayResize(r2,n);
ArrayResize(r3,n);
for(i=0;i<=n-1;i++)
{
r1[i]=2*CMath::RandomReal()-1;
r2[i]=r1[i];
r3[i]=r1[i];
}
//--- function calls
CFastHartleyTransform::FHTR1D(r2,n);
CFastHartleyTransform::FHTR1DInv(r2,n);
CFastHartleyTransform::FHTR1DInv(r3,n);
CFastHartleyTransform::FHTR1D(r3,n);
//--- search errors
for(i=0;i<=n-1;i++)
{
bidierr=MathMax(bidierr,MathAbs(r1[i]-r2[i]));
bidierr=MathMax(bidierr,MathAbs(r1[i]-r3[i]));
}
}
//--- search errors
bidierrors=bidierrors || bidierr>errtol;
//--- Test against reference O(N^2) implementation
referr=0;
for(n=1;n<=maxn;n++)
{
//--- FHT
ArrayResize(r1,n);
ArrayResize(r2,n);
for(i=0;i<=n-1;i++)
{
r1[i]=2*CMath::RandomReal()-1;
r2[i]=r1[i];
}
//--- function calls
CFastHartleyTransform::FHTR1D(r1,n);
RefFHTR1D(r2,n);
//--- search errors
for(i=0;i<=n-1;i++)
referr=MathMax(referr,MathAbs(r1[i]-r2[i]));
//--- inverse FHT
ArrayResize(r1,n);
ArrayResize(r2,n);
for(i=0;i<=n-1;i++)
{
r1[i]=2*CMath::RandomReal()-1;
r2[i]=r1[i];
}
//--- function calls
CFastHartleyTransform::FHTR1DInv(r1,n);
RefFHTR1DInv(r2,n);
//--- search errors
for(i=0;i<=n-1;i++)
referr=MathMax(referr,MathAbs(r1[i]-r2[i]));
}
//--- search errors
referrors=referrors || referr>errtol;
//--- end
waserrors=bidierrors || referrors;
//--- check
if(!silent)
{
Print("TESTING FHT");
Print("FINAL RESULT: ");
//--- check
if(waserrors)
Print("FAILED");
else
Print("OK");
Print("* BI-DIRECTIONAL TEST: ");
//--- check
if(bidierrors)
Print("FAILED");
else
Print("OK");
Print("* AGAINST REFERENCE FHT: ");
//--- check
if(referrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Reference FHT |
//+------------------------------------------------------------------+
static void CTestFHTUnit::RefFHTR1D(double &a[],const int n)
{
//--- create variables
int i=0;
int j=0;
double v=0;
//--- create array
double buf[];
//--- check
if(!CAp::Assert(n>0,"RefFHTR1D: incorrect N!"))
return;
//--- allocation
ArrayResize(buf,n);
//--- calculation
for(i=0;i<=n-1;i++)
{
v=0;
for(j=0;j<=n-1;j++)
v=v+a[j]*(MathCos(2*M_PI*i*j/n)+MathSin(2*M_PI*i*j/n));
buf[i]=v;
}
//--- copy
for(i=0;i<=n-1;i++)
a[i]=buf[i];
}
//+------------------------------------------------------------------+
//| Reference inverse FHT |
//+------------------------------------------------------------------+
static void CTestFHTUnit::RefFHTR1DInv(double &a[],const int n)
{
//--- create a variable
int i=0;
//--- check
if(!CAp::Assert(n>0,"RefFHTR1DInv: incorrect N!"))
return;
//--- function call
RefFHTR1D(a,n);
//--- change values
for(i=0;i<=n-1;i++)
a[i]=a[i]/n;
}
//+------------------------------------------------------------------+
//| Testing class CGaussQ |
//+------------------------------------------------------------------+
class CTestGQUnit
{
private:
//--- private methods
static double MapKind(const int k);
static void BuildGaussLegendreQuadrature(const int n,double &x[],double &w[]);
static void BuildGaussJacobiQuadrature(const int n,const double alpha,const double beta,double &x[],double &w[]);
static void BuildGaussLaguerreQuadrature(const int n,const double alpha,double &x[],double &w[]);
static void BuildGaussHermiteQuadrature(const int n,double &x[],double &w[]);
public:
//--- constructor, destructor
CTestGQUnit(void);
~CTestGQUnit(void);
//--- public method
static bool TestGQ(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestGQUnit::CTestGQUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestGQUnit::~CTestGQUnit(void)
{
}
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
static bool CTestGQUnit::TestGQ(const bool silent)
{
//--- create variables
double err=0;
int n=0;
int i=0;
int info=0;
int akind=0;
int bkind=0;
double alphac=0;
double betac=0;
double errtol=0;
double nonstricterrtol=0;
double stricterrtol=0;
bool recerrors;
bool specerrors;
bool waserrors;
//--- create arrays
double alpha[];
double beta[];
double x[];
double w[];
double x2[];
double w2[];
//--- initialization
recerrors=false;
specerrors=false;
waserrors=false;
errtol=1.0E-12;
nonstricterrtol=1.0E-6;
stricterrtol=1000*CMath::m_machineepsilon;
//--- Three tests for rec-based Gauss quadratures with known weights/nodes:
//--- 1. Gauss-Legendre with N=2
//--- 2. Gauss-Legendre with N=5
//--- 3. Gauss-Chebyshev with N=1,2,4,8,...,512
err=0;
ArrayResize(alpha,2);
ArrayResize(beta,2);
alpha[0]=0;
alpha[1]=0;
beta[1]=1.0/(double)(4*1*1-1);
//--- function call
CGaussQ::GQGenerateRec(alpha,beta,2.0,2,info,x,w);
//--- check
if(info>0)
{
//--- search errors
err=MathMax(err,MathAbs(x[0]+MathSqrt(3)/3));
err=MathMax(err,MathAbs(x[1]-MathSqrt(3)/3));
err=MathMax(err,MathAbs(w[0]-1));
err=MathMax(err,MathAbs(w[1]-1));
for(i=0;i<=0;i++)
recerrors=recerrors || x[i]>=x[i+1];
}
else
recerrors=true;
//--- allocation
ArrayResize(alpha,5);
ArrayResize(beta,5);
//--- change values
alpha[0]=0;
for(i=1;i<=4;i++)
{
alpha[i]=0;
beta[i]=CMath::Sqr(i)/(4*CMath::Sqr(i)-1);
}
//--- function call
CGaussQ::GQGenerateRec(alpha,beta,2.0,5,info,x,w);
//--- check
if(info>0)
{
//--- search errors
err=MathMax(err,MathAbs(x[0]+MathSqrt(245+14*MathSqrt(70))/21));
err=MathMax(err,MathAbs(x[0]+x[4]));
err=MathMax(err,MathAbs(x[1]+MathSqrt(245-14*MathSqrt(70))/21));
err=MathMax(err,MathAbs(x[1]+x[3]));
err=MathMax(err,MathAbs(x[2]));
err=MathMax(err,MathAbs(w[0]-(322-13*MathSqrt(70))/900));
err=MathMax(err,MathAbs(w[0]-w[4]));
err=MathMax(err,MathAbs(w[1]-(322+13*MathSqrt(70))/900));
err=MathMax(err,MathAbs(w[1]-w[3]));
err=MathMax(err,MathAbs(w[2]-128.0/225.0));
for(i=0;i<=3;i++)
recerrors=recerrors || x[i]>=x[i+1];
}
else
recerrors=true;
//--- calculation
n=1;
while(n<=512)
{
//--- allocation
ArrayResize(alpha,n);
ArrayResize(beta,n);
for(i=0;i<=n-1;i++)
{
alpha[i]=0;
//--- check
if(i==0)
beta[i]=0;
//--- check
if(i==1)
beta[i]=1.0/2.0;
//--- check
if(i>1)
beta[i]=1.0/4.0;
}
//--- function call
CGaussQ::GQGenerateRec(alpha,beta,M_PI,n,info,x,w);
//--- check
if(info>0)
{
//--- search errors
for(i=0;i<=n-1;i++)
{
err=MathMax(err,MathAbs(x[i]-MathCos(M_PI*(n-i-0.5)/n)));
err=MathMax(err,MathAbs(w[i]-M_PI/n));
}
for(i=0;i<=n-2;i++)
recerrors=recerrors || x[i]>=x[i+1];
}
else
recerrors=true;
n=n*2;
}
//--- search errors
recerrors=recerrors || err>errtol;
//--- Three tests for rec-based Gauss-Lobatto quadratures with known weights/nodes:
//--- 1. Gauss-Lobatto with N=3
//--- 2. Gauss-Lobatto with N=4
//--- 3. Gauss-Lobatto with N=6
err=0;
ArrayResize(alpha,2);
ArrayResize(beta,2);
alpha[0]=0;
alpha[1]=0;
beta[0]=0;
beta[1]=(double)(1*1)/(double)(4*1*1-1);
//--- function call
CGaussQ::GQGenerateGaussLobattoRec(alpha,beta,2.0,-1,1,3,info,x,w);
//--- check
if(info>0)
{
//--- search errors
err=MathMax(err,MathAbs(x[0]+1));
err=MathMax(err,MathAbs(x[1]));
err=MathMax(err,MathAbs(x[2]-1));
err=MathMax(err,MathAbs(w[0]-1.0/3.0));
err=MathMax(err,MathAbs(w[1]-4.0/3.0));
err=MathMax(err,MathAbs(w[2]-1.0/3.0));
for(i=0;i<=1;i++)
recerrors=recerrors || x[i]>=x[i+1];
}
else
recerrors=true;
//--- allocation
ArrayResize(alpha,3);
ArrayResize(beta,3);
//--- change values
alpha[0]=0;
alpha[1]=0;
alpha[2]=0;
beta[0]=0;
beta[1]=(double)(1*1)/(double)(4*1*1-1);
beta[2]=(double)(2*2)/(double)(4*2*2-1);
//--- function call
CGaussQ::GQGenerateGaussLobattoRec(alpha,beta,2.0,-1,1,4,info,x,w);
//--- check
if(info>0)
{
//--- search errors
err=MathMax(err,MathAbs(x[0]+1));
err=MathMax(err,MathAbs(x[1]+MathSqrt(5)/5));
err=MathMax(err,MathAbs(x[2]-MathSqrt(5)/5));
err=MathMax(err,MathAbs(x[3]-1));
err=MathMax(err,MathAbs(w[0]-1.0/6.0));
err=MathMax(err,MathAbs(w[1]-5.0/6.0));
err=MathMax(err,MathAbs(w[2]-5.0/6.0));
err=MathMax(err,MathAbs(w[3]-1.0/6.0));
for(i=0;i<=2;i++)
recerrors=recerrors || x[i]>=x[i+1];
}
else
recerrors=true;
//--- allocation
ArrayResize(alpha,5);
ArrayResize(beta,5);
//--- change values
alpha[0]=0;
alpha[1]=0;
alpha[2]=0;
alpha[3]=0;
alpha[4]=0;
beta[0]=0;
beta[1]=(double)(1*1)/(double)(4*1*1-1);
beta[2]=(double)(2*2)/(double)(4*2*2-1);
beta[3]=(double)(3*3)/(double)(4*3*3-1);
beta[4]=(double)(4*4)/(double)(4*4*4-1);
//--- function call
CGaussQ::GQGenerateGaussLobattoRec(alpha,beta,2.0,-1,1,6,info,x,w);
//--- check
if(info>0)
{
//--- search errors
err=MathMax(err,MathAbs(x[0]+1));
err=MathMax(err,MathAbs(x[1]+MathSqrt((7+2*MathSqrt(7))/21)));
err=MathMax(err,MathAbs(x[2]+MathSqrt((7-2*MathSqrt(7))/21)));
err=MathMax(err,MathAbs(x[3]-MathSqrt((7-2*MathSqrt(7))/21)));
err=MathMax(err,MathAbs(x[4]-MathSqrt((7+2*MathSqrt(7))/21)));
err=MathMax(err,MathAbs(x[5]-1));
err=MathMax(err,MathAbs(w[0]-1.0/(double)15));
err=MathMax(err,MathAbs(w[1]-(14-MathSqrt(7))/30));
err=MathMax(err,MathAbs(w[2]-(14+MathSqrt(7))/30));
err=MathMax(err,MathAbs(w[3]-(14+MathSqrt(7))/30));
err=MathMax(err,MathAbs(w[4]-(14-MathSqrt(7))/30));
err=MathMax(err,MathAbs(w[5]-1.0/(double)15));
for(i=0;i<=4;i++)
recerrors=recerrors || x[i]>=x[i+1];
}
else
recerrors=true;
recerrors=recerrors || err>errtol;
//--- Three tests for rec-based Gauss-Radau quadratures with known weights/nodes:
//--- 1. Gauss-Radau with N=2
//--- 2. Gauss-Radau with N=3
//--- 3. Gauss-Radau with N=3 (another case)
err=0;
ArrayResize(alpha,1);
ArrayResize(beta,2);
alpha[0]=0;
beta[0]=0;
beta[1]=(double)(1*1)/(double)(4*1*1-1);
//--- function call
CGaussQ::GQGenerateGaussRadauRec(alpha,beta,2.0,-1,2,info,x,w);
//--- check
if(info>0)
{
//--- search errors
err=MathMax(err,MathAbs(x[0]+1));
err=MathMax(err,MathAbs(x[1]-1.0/3.0));
err=MathMax(err,MathAbs(w[0]-0.5));
err=MathMax(err,MathAbs(w[1]-1.5));
for(i=0;i<=0;i++)
recerrors=recerrors || x[i]>=x[i+1];
}
else
recerrors=true;
//--- allocation
ArrayResize(alpha,2);
ArrayResize(beta,3);
//--- change values
alpha[0]=0;
alpha[1]=0;
for(i=0;i<=2;i++)
beta[i]=CMath::Sqr(i)/(4*CMath::Sqr(i)-1);
//--- function call
CGaussQ::GQGenerateGaussRadauRec(alpha,beta,2.0,-1,3,info,x,w);
//--- check
if(info>0)
{
//--- search errors
err=MathMax(err,MathAbs(x[0]+1));
err=MathMax(err,MathAbs(x[1]-(1-MathSqrt(6))/5));
err=MathMax(err,MathAbs(x[2]-(1+MathSqrt(6))/5));
err=MathMax(err,MathAbs(w[0]-2.0/9.0));
err=MathMax(err,MathAbs(w[1]-(16+MathSqrt(6))/18));
err=MathMax(err,MathAbs(w[2]-(16-MathSqrt(6))/18));
for(i=0;i<=1;i++)
recerrors=recerrors || x[i]>=x[i+1];
}
else
recerrors=true;
//--- allocation
ArrayResize(alpha,2);
ArrayResize(beta,3);
alpha[0]=0;
alpha[1]=0;
for(i=0;i<=2;i++)
beta[i]=CMath::Sqr(i)/(4*CMath::Sqr(i)-1);
//--- function call
CGaussQ::GQGenerateGaussRadauRec(alpha,beta,2.0,1,3,info,x,w);
//--- check
if(info>0)
{
//--- search errors
err=MathMax(err,MathAbs(x[2]-1));
err=MathMax(err,MathAbs(x[1]+(1-MathSqrt(6))/5));
err=MathMax(err,MathAbs(x[0]+(1+MathSqrt(6))/5));
err=MathMax(err,MathAbs(w[2]-2.0/9.0));
err=MathMax(err,MathAbs(w[1]-(16+MathSqrt(6))/18));
err=MathMax(err,MathAbs(w[0]-(16-MathSqrt(6))/18));
for(i=0;i<=1;i++)
recerrors=recerrors || x[i]>=x[i+1];
}
else
recerrors=true;
//--- search errors
recerrors=recerrors || err>errtol;
//--- test recurrence-based special cases (Legendre,Jacobi,Hermite,...)
//--- against another implementation (polynomial root-finder)
for(n=1;n<=20;n++)
{
//--- test gauss-legendre
err=0;
CGaussQ::GQGenerateGaussLegendre(n,info,x,w);
//--- check
if(info>0)
{
BuildGaussLegendreQuadrature(n,x2,w2);
//--- search errors
for(i=0;i<=n-1;i++)
{
err=MathMax(err,MathAbs(x[i]-x2[i]));
err=MathMax(err,MathAbs(w[i]-w2[i]));
}
}
else
specerrors=true;
//--- search errors
specerrors=specerrors || err>errtol;
//--- Test Gauss-Jacobi.
//--- Since task is much more difficult we will use less strict
//--- threshold.
err=0;
for(akind=0;akind<=9;akind++)
{
for(bkind=0;bkind<=9;bkind++)
{
alphac=MapKind(akind);
betac=MapKind(bkind);
//--- function call
CGaussQ::GQGenerateGaussJacobi(n,alphac,betac,info,x,w);
//--- check
if(info>0)
{
BuildGaussJacobiQuadrature(n,alphac,betac,x2,w2);
//--- search errors
for(i=0;i<=n-1;i++)
{
err=MathMax(err,MathAbs(x[i]-x2[i]));
err=MathMax(err,MathAbs(w[i]-w2[i]));
}
}
else
specerrors=true;
}
}
//--- search errors
specerrors=specerrors || err>nonstricterrtol;
//--- special test for Gauss-Jacobi (Chebyshev weight
//--- function with analytically known nodes/weights)
err=0;
CGaussQ::GQGenerateGaussJacobi(n,-0.5,-0.5,info,x,w);
//--- check
if(info>0)
{
//--- search errors
for(i=0;i<=n-1;i++)
{
err=MathMax(err,MathAbs(x[i]+MathCos(M_PI*(i+0.5)/n)));
err=MathMax(err,MathAbs(w[i]-M_PI/n));
}
}
else
specerrors=true;
//--- search errors
specerrors=specerrors || err>stricterrtol;
//--- Test Gauss-Laguerre
err=0;
for(akind=0;akind<=9;akind++)
{
alphac=MapKind(akind);
//--- function call
CGaussQ::GQGenerateGaussLaguerre(n,alphac,info,x,w);
//--- check
if(info>0)
{
BuildGaussLaguerreQuadrature(n,alphac,x2,w2);
//--- search errors
for(i=0;i<=n-1;i++)
{
err=MathMax(err,MathAbs(x[i]-x2[i]));
err=MathMax(err,MathAbs(w[i]-w2[i]));
}
}
else
specerrors=true;
}
//--- search errors
specerrors=specerrors || err>nonstricterrtol;
//--- Test Gauss-Hermite
err=0;
CGaussQ::GQGenerateGaussHermite(n,info,x,w);
//--- check
if(info>0)
{
BuildGaussHermiteQuadrature(n,x2,w2);
//--- search errors
for(i=0;i<=n-1;i++)
{
err=MathMax(err,MathAbs(x[i]-x2[i]));
err=MathMax(err,MathAbs(w[i]-w2[i]));
}
}
else
specerrors=true;
//--- search errors
specerrors=specerrors || err>nonstricterrtol;
}
//--- end
waserrors=recerrors || specerrors;
//--- check
if(!silent)
{
Print("TESTING GAUSS QUADRATURES");
Print("FINAL RESULT: ");
//--- check
if(waserrors)
Print("FAILED");
else
Print("OK");
Print("* SPECIAL CASES (LEGENDRE/JACOBI/..) ");
//--- check
if(specerrors)
Print("FAILED");
else
Print("OK");
Print("* RECURRENCE-BASED: ");
//--- check
if(recerrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Maps: |
//| 0=> -0.9 |
//| 1=> -0.5 |
//| 2=> -0.1 |
//| 3=> 0.0 |
//| 4=> +0.1 |
//| 5=> +0.5 |
//| 6=> +0.9 |
//| 7=> +1.0 |
//| 8=> +1.5 |
//| 9=> +2.0 |
//+------------------------------------------------------------------+
static double CTestGQUnit::MapKind(const int k)
{
//--- create a variable
double result=0;
//--- check
if(k==0)
result=-0.9;
//--- check
if(k==1)
result=-0.5;
//--- check
if(k==2)
result=-0.1;
//--- check
if(k==3)
result=0.0;
//--- check
if(k==4)
result=0.1;
//--- check
if(k==5)
result=0.5;
//--- check
if(k==6)
result=0.9;
//--- check
if(k==7)
result=1.0;
//--- check
if(k==8)
result=1.5;
//--- check
if(k==9)
result=2.0;
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Gauss-Legendre, another variant |
//+------------------------------------------------------------------+
static void CTestGQUnit::BuildGaussLegendreQuadrature(const int n,double &x[],
double &w[])
{
//--- create variables
int i=0;
int j=0;
double r=0;
double r1=0;
double p1=0;
double p2=0;
double p3=0;
double dp3=0;
double tmp=0;
//--- allocation
ArrayResize(x,n);
ArrayResize(w,n);
//--- calculation
for(i=0;i<=(n+1)/2-1;i++)
{
r=MathCos(M_PI*(4*i+3)/(4*n+2));
//--- cycle
do
{
//--- change values
p2=0;
p3=1;
//--- calculation
for(j=0;j<=n-1;j++)
{
p1=p2;
p2=p3;
p3=((2*j+1)*r*p2-j*p1)/(j+1);
}
dp3=n*(r*p3-p2)/(r*r-1);
r1=r;
r=r-p3/dp3;
}
while(MathAbs(r-r1)>=CMath::m_machineepsilon*(1+MathAbs(r))*100);
//--- calculation
x[i]=r;
x[n-1-i]=-r;
w[i]=2/((1-r*r)*dp3*dp3);
w[n-1-i]=2/((1-r*r)*dp3*dp3);
}
//--- shift
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-2-i;j++)
{
//--- check
if(x[j]>=x[j+1])
{
tmp=x[j];
x[j]=x[j+1];
x[j+1]=tmp;
tmp=w[j];
w[j]=w[j+1];
w[j+1]=tmp;
}
}
}
}
//+------------------------------------------------------------------+
//| Gauss-Jacobi, another variant |
//+------------------------------------------------------------------+
static void CTestGQUnit::BuildGaussJacobiQuadrature(const int n,const double alpha,
const double beta,double &x[],
double &w[])
{
//--- create variables
int i=0;
int j=0;
double r=0;
double r1=0;
double t1=0;
double t2=0;
double t3=0;
double p1=0;
double p2=0;
double p3=0;
double pp=0;
double an=0;
double bn=0;
double a=0;
double b=0;
double c=0;
double tmpsgn=0;
double tmp=0;
double alfbet=0;
double temp=0;
//--- allocation
ArrayResize(x,n);
ArrayResize(w,n);
for(i=0;i<=n-1;i++)
{
//--- check
if(i==0)
{
//--- calculation
an=alpha/n;
bn=beta/n;
t1=(1+alpha)*(2.78/(4+n*n)+0.768*an/n);
t2=1+1.48*an+0.96*bn+0.452*an*an+0.83*an*bn;
r=(t2-t1)/t2;
}
else
{
//--- check
if(i==1)
{
//--- calculation
t1=(4.1+alpha)/((1+alpha)*(1+0.156*alpha));
t2=1+0.06*(n-8)*(1+0.12*alpha)/n;
t3=1+0.012*beta*(1+0.25*MathAbs(alpha))/n;
r=r-t1*t2*t3*(1-r);
}
else
{
//--- check
if(i==2)
{
//--- calculation
t1=(1.67+0.28*alpha)/(1+0.37*alpha);
t2=1+0.22*(n-8)/n;
t3=1+8*beta/((6.28+beta)*n*n);
r=r-t1*t2*t3*(x[0]-r);
}
else
{
//--- check
if(i<n-2)
r=3*x[i-1]-3*x[i-2]+x[i-3];
else
{
//--- check
if(i==n-2)
{
//--- calculation
t1=(1+0.235*beta)/(0.766+0.119*beta);
t2=1/(1+0.639*(n-4)/(1+0.71*(n-4)));
t3=1/(1+20*alpha/((7.5+alpha)*n*n));
r=r+t1*t2*t3*(r-x[i-2]);
}
else
{
//--- check
if(i==n-1)
{
//--- calculation
t1=(1+0.37*beta)/(1.67+0.28*beta);
t2=1/(1+0.22*(n-8)/n);
t3=1/(1+8*alpha/((6.28+alpha)*n*n));
r=r+t1*t2*t3*(r-x[i-2]);
}
}
}
}
}
}
alfbet=alpha+beta;
//--- cycle
do
{
//--- change values
temp=2+alfbet;
p1=(alpha-beta+temp*r)*0.5;
p2=1;
//--- calculation
for(j=2;j<=n;j++)
{
p3=p2;
p2=p1;
temp=2*j+alfbet;
a=2*j*(j+alfbet)*(temp-2);
b=(temp-1)*(alpha*alpha-beta*beta+temp*(temp-2)*r);
c=2*(j-1+alpha)*(j-1+beta)*temp;
p1=(b*p2-c*p3)/a;
}
pp=(n*(alpha-beta-temp*r)*p1+2*(n+alpha)*(n+beta)*p2)/(temp*(1-r*r));
r1=r;
r=r1-p1/pp;
}
while(MathAbs(r-r1)>=CMath::m_machineepsilon*(1+MathAbs(r))*100);
//--- change values
x[i]=r;
w[i]=MathExp(CGammaFunc::LnGamma(alpha+n,tmpsgn)+CGammaFunc::LnGamma(beta+n,tmpsgn)-CGammaFunc::LnGamma(n+1,tmpsgn)-CGammaFunc::LnGamma(n+alfbet+1,tmpsgn))*temp*MathPow(2,alfbet)/(pp*p2);
}
//--- shift
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-2-i;j++)
{
//--- check
if(x[j]>=x[j+1])
{
tmp=x[j];
x[j]=x[j+1];
x[j+1]=tmp;
tmp=w[j];
w[j]=w[j+1];
w[j+1]=tmp;
}
}
}
}
//+------------------------------------------------------------------+
//| Gauss-Laguerre, another variant |
//+------------------------------------------------------------------+
static void CTestGQUnit::BuildGaussLaguerreQuadrature(const int n,const double alpha,
double &x[],double &w[])
{
//--- create variables
int i=0;
int j=0;
double r=0;
double r1=0;
double p1=0;
double p2=0;
double p3=0;
double dp3=0;
double tsg=0;
double tmp=0;
//--- allocation
ArrayResize(x,n);
ArrayResize(w,n);
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- check
if(i==0)
r=(1+alpha)*(3+0.92*alpha)/(1+2.4*n+1.8*alpha);
else
{
//--- check
if(i==1)
r=r+(15+6.25*alpha)/(1+0.9*alpha+2.5*n);
else
r=r+((1+2.55*(i-1))/(1.9*(i-1))+1.26*(i-1)*alpha/(1+3.5*(i-1)))/(1+0.3*alpha)*(r-x[i-2]);
}
do
{
//--- change values
p2=0;
p3=1;
//--- calculation
for(j=0;j<=n-1;j++)
{
p1=p2;
p2=p3;
p3=((-r+2*j+alpha+1)*p2-(j+alpha)*p1)/(j+1);
}
dp3=(n*p3-(n+alpha)*p2)/r;
r1=r;
r=r-p3/dp3;
}
//--- change values
while(MathAbs(r-r1)>=CMath::m_machineepsilon*(1+MathAbs(r))*100);
//--- change values
x[i]=r;
w[i]=-(MathExp(CGammaFunc::LnGamma(alpha+n,tsg)-CGammaFunc::LnGamma(n,tsg))/(dp3*n*p2));
}
//--- shift
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-2-i;j++)
{
//--- check
if(x[j]>=x[j+1])
{
tmp=x[j];
x[j]=x[j+1];
x[j+1]=tmp;
tmp=w[j];
w[j]=w[j+1];
w[j+1]=tmp;
}
}
}
}
//+------------------------------------------------------------------+
//| Gauss-Hermite, another variant |
//+------------------------------------------------------------------+
static void CTestGQUnit::BuildGaussHermiteQuadrature(const int n,double &x[],
double &w[])
{
//--- create variables
int i=0;
int j=0;
double r=0;
double r1=0;
double p1=0;
double p2=0;
double p3=0;
double dp3=0;
double pipm4=0;
double tmp=0;
//--- allocation
ArrayResize(x,n);
ArrayResize(w,n);
//--- calculation
pipm4=MathPow(M_PI,-0.25);
for(i=0;i<=(n+1)/2-1;i++)
{
//--- check
if(i==0)
r=MathSqrt(2*n+1)-1.85575*MathPow(2*n+1,-(1.0/6.0));
else
{
//--- check
if(i==1)
r=r-1.14*MathPow(n,0.426)/r;
else
{
//--- check
if(i==2)
r=1.86*r-0.86*x[0];
else
{
//--- check
if(i==3)
r=1.91*r-0.91*x[1];
else
r=2*r-x[i-2];
}
}
}
//--- cycle
do
{
//--- change values
p2=0;
p3=pipm4;
//--- calculation
for(j=0;j<=n-1;j++)
{
p1=p2;
p2=p3;
p3=p2*r*MathSqrt(2.0/(double)(j+1))-p1*MathSqrt((double)j/(double)(j+1));
}
dp3=MathSqrt(2*j)*p2;
r1=r;
r=r-p3/dp3;
}
while(MathAbs(r-r1)>=CMath::m_machineepsilon*(1+MathAbs(r))*100);
//--- change values
x[i]=r;
w[i]=2/(dp3*dp3);
x[n-1-i]=-x[i];
w[n-1-i]=w[i];
}
//--- shift
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-2-i;j++)
{
//--- check
if(x[j]>=x[j+1])
{
tmp=x[j];
x[j]=x[j+1];
x[j+1]=tmp;
tmp=w[j];
w[j]=w[j+1];
w[j+1]=tmp;
}
}
}
}
//+------------------------------------------------------------------+
//| Testing class CGaussKronrodQ |
//+------------------------------------------------------------------+
class CTestGKQUnit
{
private:
//--- private method
static double MapKind(const int k);
public:
//--- constructor, destructor
CTestGKQUnit(void);
~CTestGKQUnit(void);
//--- public method
static bool TestGKQ(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestGKQUnit::CTestGKQUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestGKQUnit::~CTestGKQUnit(void)
{
}
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
static bool CTestGKQUnit::TestGKQ(const bool silent)
{
//--- create variables
int pkind=0;
double errtol=0;
double eps=0;
double nonstricterrtol=0;
int n=0;
int i=0;
int k=0;
int info=0;
double err=0;
int akind=0;
int bkind=0;
double alphac=0;
double betac=0;
int info1=0;
int info2=0;
bool successatleastonce;
bool intblerrors;
bool vstblerrors;
bool generrors;
bool waserrors;
//--- create arrays
double x1[];
double wg1[];
double wk1[];
double x2[];
double wg2[];
double wk2[];
//--- initialization
intblerrors=false;
vstblerrors=false;
generrors=false;
waserrors=false;
errtol=10000*CMath::m_machineepsilon;
nonstricterrtol=1000*errtol;
//--- test recurrence-based Legendre nodes against the precalculated table
for(pkind=0;pkind<=5;pkind++)
{
n=0;
//--- check
if(pkind==0)
n=15;
//--- check
if(pkind==1)
n=21;
//--- check
if(pkind==2)
n=31;
//--- check
if(pkind==3)
n=41;
//--- check
if(pkind==4)
n=51;
//--- check
if(pkind==5)
n=61;
//--- function calls
CGaussKronrodQ::GKQLegendreCalc(n,info,x1,wk1,wg1);
CGaussKronrodQ::GKQLegendreTbl(n,x2,wk2,wg2,eps);
//--- check
if(info<=0)
{
generrors=true;
break;
}
//--- search errors
for(i=0;i<=n-1;i++)
{
vstblerrors=vstblerrors || MathAbs(x1[i]-x2[i])>errtol;
vstblerrors=vstblerrors || MathAbs(wk1[i]-wk2[i])>errtol;
vstblerrors=vstblerrors || MathAbs(wg1[i]-wg2[i])>errtol;
}
}
//--- Test recurrence-baced Gauss-Kronrod nodes against Gauss-only nodes
//--- calculated with subroutines from GQ unit.
for(k=1;k<=30;k++)
{
n=2*k+1;
//--- Gauss-Legendre
err=0;
CGaussKronrodQ::GKQGenerateGaussLegendre(n,info1,x1,wk1,wg1);
CGaussQ::GQGenerateGaussLegendre(k,info2,x2,wg2);
//--- check
if(info1>0 && info2>0)
{
//--- search errors
for(i=0;i<=k-1;i++)
{
err=MathMax(err,MathAbs(x1[2*i+1]-x2[i]));
err=MathMax(err,MathAbs(wg1[2*i+1]-wg2[i]));
}
}
else
generrors=true;
//--- search errors
generrors=generrors || err>errtol;
}
//--- calculation
for(k=1;k<=15;k++)
{
n=2*k+1;
//--- Gauss-Jacobi
successatleastonce=false;
err=0;
for(akind=0;akind<=9;akind++)
{
for(bkind=0;bkind<=9;bkind++)
{
//--- change values
alphac=MapKind(akind);
betac=MapKind(bkind);
//--- function calls
CGaussKronrodQ::GKQGenerateGaussJacobi(n,alphac,betac,info1,x1,wk1,wg1);
CGaussQ::GQGenerateGaussJacobi(k,alphac,betac,info2,x2,wg2);
//--- check
if(info1>0 && info2>0)
{
successatleastonce=true;
//--- search errors
for(i=0;i<=k-1;i++)
{
err=MathMax(err,MathAbs(x1[2*i+1]-x2[i]));
err=MathMax(err,MathAbs(wg1[2*i+1]-wg2[i]));
}
}
else
generrors=generrors || info1!=-5;
}
}
//--- search errors
generrors=(generrors || err>errtol) || !successatleastonce;
}
//--- end
waserrors=(intblerrors || vstblerrors) || generrors;
//--- check
if(!silent)
{
Print("TESTING GAUSS-KRONROD QUADRATURES");
Print("FINAL RESULT: ");
//--- check
if(waserrors)
Print("FAILED");
else
Print("OK");
Print("* PRE-CALCULATED TABLE: ");
//--- check
if(intblerrors)
Print("FAILED");
else
Print("OK");
Print("* CALCULATED AGAINST THE TABLE: ");
//--- check
if(vstblerrors)
Print("FAILED");
else
Print("OK");
Print("* GENERAL PROPERTIES: ");
//--- check
if(generrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Maps: |
//| 0=> -0.9 |
//| 1=> -0.5 |
//| 2=> -0.1 |
//| 3=> 0.0 |
//| 4=> +0.1 |
//| 5=> +0.5 |
//| 6=> +0.9 |
//| 7=> +1.0 |
//| 8=> +1.5 |
//| 9=> +2.0 |
//+------------------------------------------------------------------+
static double CTestGKQUnit::MapKind(const int k)
{
//--- create variables
double result=0;
//--- check
if(k==0)
result=-0.9;
//--- check
if(k==1)
result=-0.5;
//--- check
if(k==2)
result=-0.1;
//--- check
if(k==3)
result=0.0;
//--- check
if(k==4)
result=0.1;
//--- check
if(k==5)
result=0.5;
//--- check
if(k==6)
result=0.9;
//--- check
if(k==7)
result=1.0;
//--- check
if(k==8)
result=1.5;
//--- check
if(k==9)
result=2.0;
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Testing class CAutoGK |
//+------------------------------------------------------------------+
class CTestAutoGKUnit
{
public:
//--- constructor, destructor
CTestAutoGKUnit(void);
~CTestAutoGKUnit(void);
//--- public method
static bool TestAutoGK(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestAutoGKUnit::CTestAutoGKUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestAutoGKUnit::~CTestAutoGKUnit(void)
{
}
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
static bool CTestAutoGKUnit::TestAutoGK(const bool silent)
{
//--- create variables
double a=0;
double b=0;
double v=0;
double exact=0;
double eabs=0;
double alpha=0;
int pkind=0;
double errtol=0;
bool simpleerrors;
bool sngenderrors;
bool waserrors;
//--- objects of classes
CAutoGKState state;
CAutoGKReport rep;
//--- initialization
simpleerrors=false;
sngenderrors=false;
waserrors=false;
errtol=10000*CMath::m_machineepsilon;
//--- Simple test: integral(exp(x),+-1,+-2),no maximum width requirements
a=(2*CMath::RandomInteger(2)-1)*1.0;
b=(2*CMath::RandomInteger(2)-1)*2.0;
//--- function call
CAutoGK::AutoGKSmooth(a,b,state);
//--- cycle
while(CAutoGK::AutoGKIteration(state))
state.m_f=MathExp(state.m_x);
//--- function call
CAutoGK::AutoGKResults(state,v,rep);
//--- change values
exact=MathExp(b)-MathExp(a);
eabs=MathAbs(MathExp(b)-MathExp(a));
//--- check
if(rep.m_terminationtype<=0)
simpleerrors=true;
else
simpleerrors=simpleerrors || MathAbs(exact-v)>errtol*eabs;
//--- Simple test: integral(exp(x),+-1,+-2),XWidth=0.1
a=(2*CMath::RandomInteger(2)-1)*1.0;
b=(2*CMath::RandomInteger(2)-1)*2.0;
//--- function call
CAutoGK::AutoGKSmoothW(a,b,0.1,state);
//--- cycle
while(CAutoGK::AutoGKIteration(state))
state.m_f=MathExp(state.m_x);
//--- function call
CAutoGK::AutoGKResults(state,v,rep);
//--- change values
exact=MathExp(b)-MathExp(a);
eabs=MathAbs(MathExp(b)-MathExp(a));
//--- check
if(rep.m_terminationtype<=0)
simpleerrors=true;
else
simpleerrors=simpleerrors || MathAbs(exact-v)>errtol*eabs;
//--- Simple test: integral(cos(100*x),0,2*pi),no maximum width requirements
a=0;
b=2*M_PI;
//--- function call
CAutoGK::AutoGKSmooth(a,b,state);
//--- cycle
while(CAutoGK::AutoGKIteration(state))
state.m_f=MathCos(100*state.m_x);
//--- function call
CAutoGK::AutoGKResults(state,v,rep);
//--- change values
exact=0;
eabs=4;
//--- check
if(rep.m_terminationtype<=0)
simpleerrors=true;
else
simpleerrors=simpleerrors || MathAbs(exact-v)>errtol*eabs;
//--- Simple test: integral(cos(100*x),0,2*pi),XWidth=0.3
a=0;
b=2*M_PI;
//--- function call
CAutoGK::AutoGKSmoothW(a,b,0.3,state);
//--- cycle
while(CAutoGK::AutoGKIteration(state))
state.m_f=MathCos(100*state.m_x);
//--- function call
CAutoGK::AutoGKResults(state,v,rep);
//--- change values
exact=0;
eabs=4;
//--- check
if(rep.m_terminationtype<=0)
simpleerrors=true;
else
simpleerrors=simpleerrors || MathAbs(exact-v)>errtol*eabs;
//--- singular problem on [a,b]=[0.1,0.5]
//--- f2(x)=(1+x)*(b-x)^alpha,-1 < alpha < 1
for(pkind=0;pkind<=6;pkind++)
{
a=0.1;
b=0.5;
//--- check
if(pkind==0)
alpha=-0.9;
//--- check
if(pkind==1)
alpha=-0.5;
//--- check
if(pkind==2)
alpha=-0.1;
//--- check
if(pkind==3)
alpha=0.0;
//--- check
if(pkind==4)
alpha=0.1;
//--- check
if(pkind==5)
alpha=0.5;
//--- check
if(pkind==6)
alpha=0.9;
//--- f1(x)=(1+x)*(x-a)^alpha,-1 < alpha < 1
//--- 1. use singular integrator for [a,b]
//--- 2. use singular integrator for [b,a]
exact=MathPow(b-a,alpha+2)/(alpha+2)+(1+a)*MathPow(b-a,alpha+1)/(alpha+1);
eabs=MathAbs(exact);
//--- function call
CAutoGK::AutoGKSingular(a,b,alpha,0.0,state);
//--- cycle
while(CAutoGK::AutoGKIteration(state))
{
//--- check
if(state.m_xminusa<0.01)
state.m_f=MathPow(state.m_xminusa,alpha)*(1+state.m_x);
else
state.m_f=MathPow(state.m_x-a,alpha)*(1+state.m_x);
}
//--- function call
CAutoGK::AutoGKResults(state,v,rep);
//--- check
if(rep.m_terminationtype<=0)
sngenderrors=true;
else
sngenderrors=sngenderrors || MathAbs(v-exact)>errtol*eabs;
//--- function call
CAutoGK::AutoGKSingular(b,a,0.0,alpha,state);
//--- cycle
while(CAutoGK::AutoGKIteration(state))
{
//--- check
if(state.m_bminusx>-0.01)
state.m_f=MathPow(-state.m_bminusx,alpha)*(1+state.m_x);
else
state.m_f=MathPow(state.m_x-a,alpha)*(1+state.m_x);
}
//--- function call
CAutoGK::AutoGKResults(state,v,rep);
//--- check
if(rep.m_terminationtype<=0)
sngenderrors=true;
else
sngenderrors=sngenderrors || MathAbs(-v-exact)>errtol*eabs;
//--- f1(x)=(1+x)*(b-x)^alpha,-1 < alpha < 1
//--- 1. use singular integrator for [a,b]
//--- 2. use singular integrator for [b,a]
exact=(1+b)*MathPow(b-a,alpha+1)/(alpha+1)-MathPow(b-a,alpha+2)/(alpha+2);
eabs=MathAbs(exact);
//--- function call
CAutoGK::AutoGKSingular(a,b,0.0,alpha,state);
//--- cycle
while(CAutoGK::AutoGKIteration(state))
{
//--- check
if(state.m_bminusx<0.01)
state.m_f=MathPow(state.m_bminusx,alpha)*(1+state.m_x);
else
state.m_f=MathPow(b-state.m_x,alpha)*(1+state.m_x);
}
//--- function call
CAutoGK::AutoGKResults(state,v,rep);
//--- check
if(rep.m_terminationtype<=0)
sngenderrors=true;
else
sngenderrors=sngenderrors || MathAbs(v-exact)>errtol*eabs;
//--- function call
CAutoGK::AutoGKSingular(b,a,alpha,0.0,state);
//--- cycle
while(CAutoGK::AutoGKIteration(state))
{
//--- check
if(state.m_xminusa>-0.01)
state.m_f=MathPow(-state.m_xminusa,alpha)*(1+state.m_x);
else
state.m_f=MathPow(b-state.m_x,alpha)*(1+state.m_x);
}
//--- function call
CAutoGK::AutoGKResults(state,v,rep);
//--- check
if(rep.m_terminationtype<=0)
sngenderrors=true;
else
sngenderrors=sngenderrors || MathAbs(-v-exact)>errtol*eabs;
}
//--- end
waserrors=simpleerrors || sngenderrors;
//--- check
if(!silent)
{
Print("TESTING AUTOGK");
Print("INTEGRATION WITH GIVEN ACCURACY: ");
//--- check
if(simpleerrors || sngenderrors)
Print("FAILED");
else
Print("OK");
Print("* SIMPLE PROBLEMS: ");
//--- check
if(simpleerrors)
Print("FAILED");
else
Print("OK");
Print("* SINGULAR PROBLEMS (ENDS OF INTERVAL): ");
//--- check
if(sngenderrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CIDWInt |
//+------------------------------------------------------------------+
class CTestIDWIntUnit
{
private:
//--- private methods
static void Unset2D(CMatrixComplex &a);
static void Unset1D(double &a[]);
static void TestXY(CMatrixDouble &xy,const int n,const int nx,const int d,const int nq,const int nw,bool &idwerrors);
static void TestRXY(CMatrixDouble &xy,const int n,const int nx,const double r,bool &idwerrors);
static void TestDegree(const int n,const int nx,const int d,const int dtask,bool &idwerrors);
static void TestNoisy(bool &idwerrors);
public:
//--- constructor, destructor
CTestIDWIntUnit(void);
~CTestIDWIntUnit(void);
//--- public method
static bool TestIDWInt(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestIDWIntUnit::CTestIDWIntUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestIDWIntUnit::~CTestIDWIntUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing IDW interpolation |
//+------------------------------------------------------------------+
static bool CTestIDWIntUnit::TestIDWInt(const bool silent)
{
//--- create variables
int i=0;
int j=0;
double vx=0;
double vy=0;
double vz=0;
int d=0;
int dtask=0;
int nx=0;
int nq=0;
int nw=0;
int smalln=0;
int largen=0;
bool waserrors;
bool idwerrors;
//--- create matrix
CMatrixDouble xy;
//--- initialization
idwerrors=false;
smalln=256;
largen=1024;
nq=10;
nw=18;
//--- Simple test:
//--- * F=x^3 + sin(pi*y)*z^2 - (x+y)^2
//--- * space is either R1=[-1,+1] (other dimensions are
//--- fixed at 0),R1^2 or R1^3.
//* D=-1,0,1,2
for(nx=1;nx<=2;nx++)
{
//--- allocation
xy.Resize(largen,nx+1);
for(i=0;i<=largen-1;i++)
{
for(j=0;j<=nx-1;j++)
xy[i].Set(j,2*CMath::RandomReal()-1);
//--- check
if(nx>=1)
vx=xy[i][0];
else
vx=0;
//--- check
if(nx>=2)
vy=xy[i][1];
else
vy=0;
//--- check
if(nx>=3)
vz=xy[i][2];
else
vz=0;
//--- calculation
xy[i].Set(nx,vx*vx*vx+MathSin(M_PI*vy)*CMath::Sqr(vz)-CMath::Sqr(vx+vy));
}
for(d=-1;d<=2;d++)
TestXY(xy,largen,nx,d,nq,nw,idwerrors);
}
//--- Another simple test:
//--- * five points in 2D - (0,0),(0,1),(1,0),(-1,0) (0,-1)
//--- * F is random
//--- * D=-1,0,1,2
nx=2;
xy.Resize(5,nx+1);
//--- change values
xy[0].Set(0,0);
xy[0].Set(1,0);
xy[0].Set(2,2*CMath::RandomReal()-1);
xy[1].Set(0,1);
xy[1].Set(1,0);
xy[1].Set(2,2*CMath::RandomReal()-1);
xy[2].Set(0,0);
xy[2].Set(1,1);
xy[2].Set(2,2*CMath::RandomReal()-1);
xy[3].Set(0,-1);
xy[3].Set(1,0);
xy[3].Set(2,2*CMath::RandomReal()-1);
xy[4].Set(0,0);
xy[4].Set(1,-1);
xy[4].Set(2,2*CMath::RandomReal()-1);
//--- calculation
for(d=-1;d<=2;d++)
TestXY(xy,5,nx,d,nq,nw,idwerrors);
//--- Degree test.
//--- F is either:
//--- * constant (DTask=0)
//--- * linear (DTask=1)
//--- * quadratic (DTask=2)
//--- Nodal functions are either
//--- * constant (D=0)
//--- * linear (D=1)
//--- * quadratic (D=2)
//--- When DTask<=D,we can interpolate without errors.
//--- When DTask>D,we MUST have errors.
for(nx=1;nx<=3;nx++)
{
for(d=0;d<=2;d++)
for(dtask=0;dtask<=2;dtask++)
{
TestDegree(smalln,nx,d,dtask,idwerrors);
}
}
//--- Noisy test
TestNoisy(idwerrors);
//--- report
waserrors=idwerrors;
//--- check
if(!silent)
{
Print("TESTING INVERSE DISTANCE WEIGHTING");
Print("* IDW: ");
//--- check
if(!idwerrors)
Print("OK");
else
Print("FAILED");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Unsets 2D array. |
//+------------------------------------------------------------------+
static void CTestIDWIntUnit::Unset2D(CMatrixComplex &a)
{
//--- allocation
a.Resize(1,1);
//--- change value
a[0].Set(0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets 1D array. |
//+------------------------------------------------------------------+
static void CTestIDWIntUnit::Unset1D(double &a[])
{
//--- allocation
ArrayResize(a,1);
//--- change value
a[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Testing IDW: |
//| * generate model using N/NX/D/NQ/NW |
//| * test basic properties |
//+------------------------------------------------------------------+
static void CTestIDWIntUnit::TestXY(CMatrixDouble &xy,const int n,const int nx,
const int d,const int nq,const int nw,
bool &idwerrors)
{
//--- create variables
double threshold=0;
double lipschitzstep=0;
int i=0;
int i1=0;
int i2=0;
double v=0;
double v1=0;
double v2=0;
double t=0;
double l1=0;
double l2=0;
int i_=0;
//--- create array
double x[];
//--- object of class
CIDWInterpolant z1;
//--- initialization
threshold=1000*CMath::m_machineepsilon;
lipschitzstep=0.001;
//--- allocation
ArrayResize(x,nx);
//--- build
CIDWInt::IDWBuildModifiedShepard(xy,n,nx,d,nq,nw,z1);
//--- first,test interpolation properties at nodes
for(i=0;i<=n-1;i++)
{
for(i_=0;i_<=nx-1;i_++)
x[i_]=xy[i][i_];
//--- search errors
idwerrors=idwerrors || CIDWInt::IDWCalc(z1,x)!=xy[i][nx];
}
//--- test Lipschitz continuity
i1=CMath::RandomInteger(n);
do
{
i2=CMath::RandomInteger(n);
}
while(i2==i1);
//--- change values
l1=0;
t=0;
//--- cycle
while(t<1.0)
{
//--- calculation
v=1-t;
for(i_=0;i_<=nx-1;i_++)
x[i_]=v*xy[i1][i_];
v=t;
for(i_=0;i_<=nx-1;i_++)
x[i_]=x[i_]+v*xy[i2][i_];
v1=CIDWInt::IDWCalc(z1,x);
v=1-(t+lipschitzstep);
//--- calculation
for(i_=0;i_<=nx-1;i_++)
x[i_]=v*xy[i1][i_];
v=t+lipschitzstep;
for(i_=0;i_<=nx-1;i_++)
x[i_]=x[i_]+v*xy[i2][i_];
v2=CIDWInt::IDWCalc(z1,x);
l1=MathMax(l1,MathAbs(v2-v1)/lipschitzstep);
t=t+lipschitzstep;
}
//--- change values
l2=0;
t=0;
//--- cycle
while(t<1.0)
{
//--- calculation
v=1-t;
for(i_=0;i_<=nx-1;i_++)
x[i_]=v*xy[i1][i_];
v=t;
for(i_=0;i_<=nx-1;i_++)
x[i_]=x[i_]+v*xy[i2][i_];
v1=CIDWInt::IDWCalc(z1,x);
v=1-(t+lipschitzstep/3);
//--- calculation
for(i_=0;i_<=nx-1;i_++)
x[i_]=v*xy[i1][i_];
v=t+lipschitzstep/3;
for(i_=0;i_<=nx-1;i_++)
x[i_]=x[i_]+v*xy[i2][i_];
v2=CIDWInt::IDWCalc(z1,x);
l2=MathMax(l2,MathAbs(v2-v1)/(lipschitzstep/3));
t=t+lipschitzstep/3;
}
//--- search errors
idwerrors=idwerrors || l2>2.0*l1;
}
//+------------------------------------------------------------------+
//| Testing IDW: |
//| * generate model using R-based model |
//| * test basic properties |
//+------------------------------------------------------------------+
static void CTestIDWIntUnit::TestRXY(CMatrixDouble &xy,const int n,const int nx,
const double r,bool &idwerrors)
{
//--- create variables
double threshold=0;
double lipschitzstep=0;
int i=0;
int i1=0;
int i2=0;
double v=0;
double v1=0;
double v2=0;
double t=0;
double l1=0;
double l2=0;
int i_=0;
//--- create array
double x[];
//--- object of class
CIDWInterpolant z1;
//--- initialization
threshold=1000*CMath::m_machineepsilon;
lipschitzstep=0.001;
//--- allocation
ArrayResize(x,nx);
//--- build
CIDWInt::IDWBuildModifiedShepardR(xy,n,nx,r,z1);
//--- first,test interpolation properties at nodes
for(i=0;i<=n-1;i++)
{
for(i_=0;i_<=nx-1;i_++)
x[i_]=xy[i][i_];
//--- search errors
idwerrors=idwerrors || CIDWInt::IDWCalc(z1,x)!=xy[i][nx];
}
//--- test Lipschitz continuity
i1=CMath::RandomInteger(n);
do
{
i2=CMath::RandomInteger(n);
}
while(i2==i1);
//--- change values
l1=0;
t=0;
//--- cycle
while(t<1.0)
{
//--- calculation
v=1-t;
for(i_=0;i_<=nx-1;i_++)
x[i_]=v*xy[i1][i_];
v=t;
for(i_=0;i_<=nx-1;i_++)
x[i_]=x[i_]+v*xy[i2][i_];
v1=CIDWInt::IDWCalc(z1,x);
v=1-(t+lipschitzstep);
//--- calculation
for(i_=0;i_<=nx-1;i_++)
x[i_]=v*xy[i1][i_];
v=t+lipschitzstep;
for(i_=0;i_<=nx-1;i_++)
x[i_]=x[i_]+v*xy[i2][i_];
v2=CIDWInt::IDWCalc(z1,x);
l1=MathMax(l1,MathAbs(v2-v1)/lipschitzstep);
t=t+lipschitzstep;
}
//--- change values
l2=0;
t=0;
//--- cycle
while(t<1.0)
{
//--- calculation
v=1-t;
for(i_=0;i_<=nx-1;i_++)
x[i_]=v*xy[i1][i_];
v=t;
for(i_=0;i_<=nx-1;i_++)
x[i_]=x[i_]+v*xy[i2][i_];
v1=CIDWInt::IDWCalc(z1,x);
v=1-(t+lipschitzstep/3);
//--- calculation
for(i_=0;i_<=nx-1;i_++)
x[i_]=v*xy[i1][i_];
v=t+lipschitzstep/3;
for(i_=0;i_<=nx-1;i_++)
x[i_]=x[i_]+v*xy[i2][i_];
v2=CIDWInt::IDWCalc(z1,x);
l2=MathMax(l2,MathAbs(v2-v1)/(lipschitzstep/3));
t=t+lipschitzstep/3;
}
//--- search errors
idwerrors=idwerrors || l2>2.0*l1;
}
//+------------------------------------------------------------------+
//| Testing degree properties |
//| F is either: |
//| * constant (DTask=0) |
//| * linear (DTask=1) |
//| * quadratic (DTask=2) |
//| Nodal functions are either |
//| * constant (D=0) |
//| * linear (D=1) |
//| * quadratic (D=2) |
//| When DTask<=D,we can interpolate without errors. |
//| When DTask>D,we MUST have errors. |
//+------------------------------------------------------------------+
static void CTestIDWIntUnit::TestDegree(const int n,const int nx,const int d,
const int dtask,bool &idwerrors)
{
//--- create variables
double threshold=0;
int nq=0;
int nw=0;
int i=0;
int j=0;
double v=0;
double c0=0;
double v1=0;
double v2=0;
int i_=0;
//--- create arrays
double c1[];
double x[];
//--- create matrix
CMatrixDouble c2;
CMatrixDouble xy;
//--- object of class
CIDWInterpolant z1;
//--- initialization
threshold=1.0E6*CMath::m_machineepsilon;
nq=2*(nx*nx+nx+1);
nw=10;
//--- check
if(!CAp::Assert(nq<=n,"TestDegree: internal error"))
return;
//--- prepare model
c0=2*CMath::RandomReal()-1;
//--- allocation
ArrayResize(c1,nx);
for(i=0;i<=nx-1;i++)
c1[i]=2*CMath::RandomReal()-1;
//--- allocation
c2.Resize(nx,nx);
for(i=0;i<=nx-1;i++)
{
for(j=i+1;j<=nx-1;j++)
{
c2[i].Set(j,2*CMath::RandomReal()-1);
c2[j].Set(i,c2[i][j]);
}
//--- cycle
do
{
c2[i].Set(i,2*CMath::RandomReal()-1);
}
while(MathAbs(c2[i][i])<=0.3);
}
//--- prepare points
xy.Resize(n,nx+1);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=nx-1;j++)
xy[i].Set(j,4*CMath::RandomReal()-2);
xy[i].Set(nx,c0);
//--- check
if(dtask>=1)
{
//--- change value
v=0.0;
for(i_=0;i_<=nx-1;i_++)
v+=c1[i_]*xy[i][i_];
xy[i].Set(nx,xy[i][nx]+v);
}
//--- check
if(dtask==2)
{
for(j=0;j<=nx-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=nx-1;i_++)
v+=c2[j][i_]*xy[i][i_];
xy[i].Set(nx,xy[i][nx]+xy[i][j]*v);
}
}
}
//--- build interpolant,calculate value at random point
CIDWInt::IDWBuildModifiedShepard(xy,n,nx,d,nq,nw,z1);
ArrayResize(x,nx);
for(i=0;i<=nx-1;i++)
x[i]=4*CMath::RandomReal()-2;
v1=CIDWInt::IDWCalc(z1,x);
//--- calculate model value at the same point
v2=c0;
//--- check
if(dtask>=1)
{
//--- change value
v=0.0;
for(i_=0;i_<=nx-1;i_++)
v+=c1[i_]*x[i_];
v2=v2+v;
}
//--- check
if(dtask==2)
{
for(j=0;j<=nx-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=nx-1;i_++)
v+=c2[j][i_]*x[i_];
v2=v2+x[j]*v;
}
}
//--- Compare
if(dtask<=d)
idwerrors=idwerrors || MathAbs(v2-v1)>threshold;
else
idwerrors=idwerrors || MathAbs(v2-v1)<threshold;
}
//+------------------------------------------------------------------+
//| Noisy test: |
//| * F=x^2 + y^2 + z^2 + noise on [-1,+1]^3 |
//| * space is either R1=[-1,+1] (other dimensions are |
//| fixed at 0),R1^2 or R1^3. |
//| * D=1,2 |
//| * 4096 points is used for function generation, |
//| 4096 points - for testing |
//| * RMS error of "noisy" model on test set must be |
//| lower than RMS error of interpolation model. |
//+------------------------------------------------------------------+
static void CTestIDWIntUnit::TestNoisy(bool &idwerrors)
{
//--- create variables
double noiselevel=0;
int nq=0;
int nw=0;
int d=0;
int nx=0;
int ntrn=0;
int ntst=0;
int i=0;
int j=0;
double v=0;
double t=0;
double v1=0;
double v2=0;
double ve=0;
double rms1=0;
double rms2=0;
//--- create array
double x[];
//--- create matrix
CMatrixDouble xy;
//--- objects of classes
CIDWInterpolant z1;
CIDWInterpolant z2;
//--- initialization
nq=20;
nw=40;
noiselevel=0.2;
ntrn=256;
ntst=1024;
//--- calculation
for(d=1;d<=2;d++)
{
for(nx=1;nx<=2;nx++)
{
//--- prepare dataset
xy.Resize(ntrn,nx+1);
for(i=0;i<=ntrn-1;i++)
{
v=noiselevel*(2*CMath::RandomReal()-1);
//--- change values
for(j=0;j<=nx-1;j++)
{
t=2*CMath::RandomReal()-1;
v=v+CMath::Sqr(t);
xy[i].Set(j,t);
}
xy[i].Set(nx,v);
}
//--- build interpolants
CIDWInt::IDWBuildModifiedShepard(xy,ntrn,nx,d,nq,nw,z1);
CIDWInt::IDWBuildNoisy(xy,ntrn,nx,d,nq,nw,z2);
//--- calculate RMS errors
ArrayResize(x,nx);
rms1=0;
rms2=0;
for(i=0;i<=ntst-1;i++)
{
ve=0;
for(j=0;j<=nx-1;j++)
{
t=2*CMath::RandomReal()-1;
x[j]=t;
ve=ve+CMath::Sqr(t);
}
//--- change values
v1=CIDWInt::IDWCalc(z1,x);
v2=CIDWInt::IDWCalc(z2,x);
rms1=rms1+CMath::Sqr(v1-ve);
rms2=rms2+CMath::Sqr(v2-ve);
}
//--- search errors
idwerrors=idwerrors || rms2>rms1;
}
}
}
//+------------------------------------------------------------------+
//| Testing class CRatInt |
//+------------------------------------------------------------------+
class CTestRatIntUnit
{
private:
//--- private methods
static void PolDiff2(double &x[],double &cf[],int n,double t,double &p,double &dp,double &d2p);
static void BRCunSet(CBarycentricInterpolant &b);
static bool Is1DSolution(const int n,double &y[],double &w[],double c);
public:
//--- constructor, destructor
CTestRatIntUnit(void);
~CTestRatIntUnit(void);
//--- public method
static bool TestRatInt(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestRatIntUnit::CTestRatIntUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestRatIntUnit::~CTestRatIntUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CRatInt |
//+------------------------------------------------------------------+
static bool CTestRatIntUnit::TestRatInt(const bool silent)
{
//--- create variables
bool waserrors;
bool bcerrors;
bool nperrors;
double threshold=0;
double lipschitztol=0;
int maxn=0;
int passcount=0;
double h=0;
double s1=0;
double s2=0;
int n=0;
int n2=0;
int i=0;
int j=0;
int k=0;
int d=0;
int pass=0;
double maxerr=0;
double t=0;
double a=0;
double b=0;
double s=0;
double v0=0;
double v1=0;
double v2=0;
double v3=0;
double d0=0;
double d1=0;
double d2=0;
//--- create arrays
double x[];
double x2[];
double y[];
double y2[];
double w[];
double w2[];
double xc[];
double yc[];
int dc[];
//--- objects of classes
CBarycentricInterpolant b1;
CBarycentricInterpolant b2;
//--- initialization
nperrors=false;
bcerrors=false;
waserrors=false;
//--- PassCount number of repeated passes
//--- Threshold error tolerance
//--- LipschitzTol Lipschitz constant increase allowed
//--- when calculating constant on a twice denser grid
passcount=5;
maxn=15;
threshold=1000000*CMath::m_machineepsilon;
lipschitztol=1.3;
//--- Basic barycentric functions
for(n=1;n<=10;n++)
{
//--- randomized tests
for(pass=1;pass<=passcount;pass++)
{
//--- generate weights from polynomial interpolation
v0=1+0.4*CMath::RandomReal()-0.2;
v1=2*CMath::RandomReal()-1;
v2=2*CMath::RandomReal()-1;
v3=2*CMath::RandomReal()-1;
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(w,n);
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- check
if(n==1)
x[i]=0;
else
x[i]=v0*MathCos(i*M_PI/(n-1));
y[i]=MathSin(v1*x[i])+MathCos(v2*x[i])+MathExp(v3*x[i]);
}
//--- calculation
for(j=0;j<=n-1;j++)
{
w[j]=1;
for(k=0;k<=n-1;k++)
{
//--- check
if(k!=j)
w[j]=w[j]/(x[j]-x[k]);
}
}
//--- function call
CRatInt::BarycentricBuildXYW(x,y,w,n,b1);
//--- unpack,then pack again and compare
BRCunSet(b2);
CRatInt::BarycentricUnpack(b1,n2,x2,y2,w2);
//--- search errors
bcerrors=bcerrors || n2!=n;
//--- function call
CRatInt::BarycentricBuildXYW(x2,y2,w2,n2,b2);
t=2*CMath::RandomReal()-1;
//--- search errors
bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,t)-CRatInt::BarycentricCalc(b2,t))>threshold;
//--- copy,compare
BRCunSet(b2);
CRatInt::BarycentricCopy(b1,b2);
t=2*CMath::RandomReal()-1;
//--- search errors
bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,t)-CRatInt::BarycentricCalc(b2,t))>threshold;
//--- test interpolation properties
for(i=0;i<=n-1;i++)
{
//--- test interpolation at nodes
bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,x[i])-y[i])>threshold*MathAbs(y[i]);
//--- compare with polynomial interpolation
t=2*CMath::RandomReal()-1;
PolDiff2(x,y,n,t,v0,v1,v2);
//--- search errors
bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,t)-v0)>threshold*MathMax(MathAbs(v0),1);
//--- test continuity between nodes
//--- calculate Lipschitz constant on two grids -
//--- dense and even more dense. If Lipschitz constant
//--- on a denser grid is significantly increased,
//--- continuity test is failed
t=3.0;
k=100;
s1=0;
for(j=0;j<=k-1;j++)
{
v1=x[i]+(t-x[i])*j/k;
v2=x[i]+(t-x[i])*(j+1)/k;
s1=MathMax(s1,MathAbs(CRatInt::BarycentricCalc(b1,v2)-CRatInt::BarycentricCalc(b1,v1))/MathAbs(v2-v1));
}
//--- change values
k=2*k;
s2=0;
for(j=0;j<=k-1;j++)
{
v1=x[i]+(t-x[i])*j/k;
v2=x[i]+(t-x[i])*(j+1)/k;
s2=MathMax(s2,MathAbs(CRatInt::BarycentricCalc(b1,v2)-CRatInt::BarycentricCalc(b1,v1))/MathAbs(v2-v1));
}
//--- search errors
bcerrors=bcerrors || (s2>lipschitztol*s1 && s1>threshold*k);
}
//--- test differentiation properties
for(i=0;i<=n-1;i++)
{
t=2*CMath::RandomReal()-1;
PolDiff2(x,y,n,t,v0,v1,v2);
d0=0;
d1=0;
d2=0;
//--- function call
CRatInt::BarycentricDiff1(b1,t,d0,d1);
//--- search errors
bcerrors=bcerrors || MathAbs(v0-d0)>threshold*MathMax(MathAbs(v0),1);
bcerrors=bcerrors || MathAbs(v1-d1)>threshold*MathMax(MathAbs(v1),1);
//--- change values
d0=0;
d1=0;
d2=0;
//--- function call
CRatInt::BarycentricDiff2(b1,t,d0,d1,d2);
//--- search errors
bcerrors=bcerrors || MathAbs(v0-d0)>threshold*MathMax(MathAbs(v0),1);
bcerrors=bcerrors || MathAbs(v1-d1)>threshold*MathMax(MathAbs(v1),1);
bcerrors=bcerrors || MathAbs(v2-d2)>MathSqrt(threshold)*MathMax(MathAbs(v2),1);
}
//--- test linear translation
t=2*CMath::RandomReal()-1;
a=2*CMath::RandomReal()-1;
b=2*CMath::RandomReal()-1;
//--- function calls
BRCunSet(b2);
CRatInt::BarycentricCopy(b1,b2);
CRatInt::BarycentricLinTransX(b2,a,b);
//--- search errors
bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,a*t+b)-CRatInt::BarycentricCalc(b2,t))>threshold;
//--- change values
a=0;
b=2*CMath::RandomReal()-1;
//--- function calls
BRCunSet(b2);
CRatInt::BarycentricCopy(b1,b2);
CRatInt::BarycentricLinTransX(b2,a,b);
//--- search errors
bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,a*t+b)-CRatInt::BarycentricCalc(b2,t))>threshold;
//--- change values
a=2*CMath::RandomReal()-1;
b=2*CMath::RandomReal()-1;
//--- function calls
BRCunSet(b2);
CRatInt::BarycentricCopy(b1,b2);
CRatInt::BarycentricLinTransY(b2,a,b);
//--- search errors
bcerrors=bcerrors || MathAbs(a*CRatInt::BarycentricCalc(b1,t)+b-CRatInt::BarycentricCalc(b2,t))>threshold;
}
}
//--- calculation
for(pass=0;pass<=3;pass++)
{
//--- Crash-test: small numbers,large numbers
ArrayResize(x,4);
ArrayResize(y,4);
ArrayResize(w,4);
h=1;
//--- check
if(pass%2==0)
h=100*CMath::m_minrealnumber;
//--- check
if(pass%2==1)
h=0.01*CMath::m_maxrealnumber;
//--- change values
x[0]=0*h;
x[1]=1*h;
x[2]=2*h;
x[3]=3*h;
y[0]=0*h;
y[1]=1*h;
y[2]=2*h;
y[3]=3*h;
w[0]=-(1/(x[1]-x[0]));
w[1]=1*(1/(x[1]-x[0])+1/(x[2]-x[1]));
w[2]=-(1*(1/(x[2]-x[1])+1/(x[3]-x[2])));
w[3]=1/(x[3]-x[2]);
//--- check
if(pass/2==0)
v0=0;
//--- check
if(pass/2==1)
v0=0.6*h;
//--- function calls
CRatInt::BarycentricBuildXYW(x,y,w,4,b1);
t=CRatInt::BarycentricCalc(b1,v0);
//--- change values
d0=0;
d1=0;
d2=0;
//--- function call
CRatInt::BarycentricDiff1(b1,v0,d0,d1);
//--- search errors
bcerrors=bcerrors || MathAbs(t-v0)>threshold*v0;
bcerrors=bcerrors || MathAbs(d0-v0)>threshold*v0;
bcerrors=bcerrors || MathAbs(d1-1)>1000*threshold;
}
//--- crash test: large abscissas,small argument
//--- test for errors in D0 is not very strict
//--- because renormalization used in Diff1()
//--- destroys part of precision.
ArrayResize(x,4);
ArrayResize(y,4);
ArrayResize(w,4);
//--- change values
h=0.01*CMath::m_maxrealnumber;
x[0]=0*h;
x[1]=1*h;
x[2]=2*h;
x[3]=3*h;
y[0]=0*h;
y[1]=1*h;
y[2]=2*h;
y[3]=3*h;
w[0]=-(1/(x[1]-x[0]));
w[1]=1*(1/(x[1]-x[0])+1/(x[2]-x[1]));
w[2]=-(1*(1/(x[2]-x[1])+1/(x[3]-x[2])));
w[3]=1/(x[3]-x[2]);
v0=100*CMath::m_minrealnumber;
//--- function call
CRatInt::BarycentricBuildXYW(x,y,w,4,b1);
t=CRatInt::BarycentricCalc(b1,v0);
//--- change values
d0=0;
d1=0;
d2=0;
//--- function call
CRatInt::BarycentricDiff1(b1,v0,d0,d1);
//--- search errors
bcerrors=bcerrors || MathAbs(t)>v0*(1+threshold);
bcerrors=bcerrors || MathAbs(d0)>v0*(1+threshold);
bcerrors=bcerrors || MathAbs(d1-1)>1000*threshold;
//--- crash test: test safe barycentric formula
ArrayResize(x,4);
ArrayResize(y,4);
ArrayResize(w,4);
//--- change values
h=2*CMath::m_minrealnumber;
x[0]=0*h;
x[1]=1*h;
x[2]=2*h;
x[3]=3*h;
y[0]=0*h;
y[1]=1*h;
y[2]=2*h;
y[3]=3*h;
w[0]=-(1/(x[1]-x[0]));
w[1]=1*(1/(x[1]-x[0])+1/(x[2]-x[1]));
w[2]=-(1*(1/(x[2]-x[1])+1/(x[3]-x[2])));
w[3]=1/(x[3]-x[2]);
v0=CMath::m_minrealnumber;
//--- function calls
CRatInt::BarycentricBuildXYW(x,y,w,4,b1);
t=CRatInt::BarycentricCalc(b1,v0);
//--- search errors
bcerrors=bcerrors || MathAbs(t-v0)/v0>threshold;
//--- Testing "No Poles" interpolation
maxerr=0;
for(pass=1;pass<=passcount-1;pass++)
{
//--- allocation
ArrayResize(x,1);
ArrayResize(y,1);
x[0]=2*CMath::RandomReal()-1;
y[0]=2*CMath::RandomReal()-1;
//--- function call
CRatInt::BarycentricBuildFloaterHormann(x,y,1,1,b1);
//--- search errors
maxerr=MathMax(maxerr,MathAbs(CRatInt::BarycentricCalc(b1,2*CMath::RandomReal()-1)-y[0]));
}
//--- calculation
for(n=2;n<=10;n++)
{
//--- compare interpolant built by subroutine
//--- with interpolant built by hands
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(w,n);
ArrayResize(w2,n);
//--- D=1,non-equidistant nodes
for(pass=1;pass<=passcount;pass++)
{
//--- Initialize X,Y,W
a=-1-1*CMath::RandomReal();
b=1+1*CMath::RandomReal();
for(i=0;i<=n-1;i++)
x[i]=MathArctan((b-a)*i/(n-1)+a);
for(i=0;i<=n-1;i++)
y[i]=2*CMath::RandomReal()-1;
w[0]=-(1/(x[1]-x[0]));
s=1;
//--- calculation
for(i=1;i<=n-2;i++)
{
w[i]=s*(1/(x[i]-x[i-1])+1/(x[i+1]-x[i]));
s=-s;
}
w[n-1]=s/(x[n-1]-x[n-2]);
//--- calculation
for(i=0;i<=n-1;i++)
{
k=CMath::RandomInteger(n);
//--- check
if(k!=i)
{
t=x[i];
x[i]=x[k];
x[k]=t;
t=y[i];
y[i]=y[k];
y[k]=t;
t=w[i];
w[i]=w[k];
w[k]=t;
}
}
//--- Build and test
CRatInt::BarycentricBuildFloaterHormann(x,y,n,1,b1);
CRatInt::BarycentricBuildXYW(x,y,w,n,b2);
//--- search errors
for(i=1;i<=2*n;i++)
{
t=a+(b-a)*CMath::RandomReal();
maxerr=MathMax(maxerr,MathAbs(CRatInt::BarycentricCalc(b1,t)-CRatInt::BarycentricCalc(b2,t)));
}
}
//--- D=0,1,2. Equidistant nodes.
for(d=0;d<=2;d++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- Skip incorrect (N,D) pairs
if(n<2*d)
continue;
//--- Initialize X,Y,W
a=-1-1*CMath::RandomReal();
b=1+1*CMath::RandomReal();
for(i=0;i<=n-1;i++)
x[i]=(b-a)*i/(n-1)+a;
for(i=0;i<=n-1;i++)
y[i]=2*CMath::RandomReal()-1;
s=1;
//--- check
if(d==0)
{
for(i=0;i<=n-1;i++)
{
w[i]=s;
s=-s;
}
}
//--- check
if(d==1)
{
w[0]=-s;
for(i=1;i<=n-2;i++)
{
w[i]=2*s;
s=-s;
}
w[n-1]=s;
}
//--- check
if(d==2)
{
//--- calculation
w[0]=s;
w[1]=-(3*s);
for(i=2;i<=n-3;i++)
{
w[i]=4*s;
s=-s;
}
w[n-2]=3*s;
w[n-1]=-s;
}
//--- Mix
for(i=0;i<=n-1;i++)
{
k=CMath::RandomInteger(n);
//--- check
if(k!=i)
{
t=x[i];
x[i]=x[k];
x[k]=t;
t=y[i];
y[i]=y[k];
y[k]=t;
t=w[i];
w[i]=w[k];
w[k]=t;
}
}
//--- Build and test
CRatInt::BarycentricBuildFloaterHormann(x,y,n,d,b1);
CRatInt::BarycentricBuildXYW(x,y,w,n,b2);
//--- search errors
for(i=1;i<=2*n;i++)
{
t=a+(b-a)*CMath::RandomReal();
maxerr=MathMax(maxerr,MathAbs(CRatInt::BarycentricCalc(b1,t)-CRatInt::BarycentricCalc(b2,t)));
}
}
}
}
//--- check
if(maxerr>threshold)
nperrors=true;
//--- report
waserrors=bcerrors || nperrors;
//--- check
if(!silent)
{
Print("TESTING RATIONAL INTERPOLATION");
Print("BASIC BARYCENTRIC FUNCTIONS: ");
//--- check
if(bcerrors)
Print("FAILED");
else
Print("OK");
Print("FLOATER-HORMANN: ");
//--- check
if(nperrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- end
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestRatIntUnit::PolDiff2(double &x[],double &cf[],int n,
double t,double &p,double &dp,
double &d2p)
{
//--- create variables
int m=0;
int i=0;
//--- create arrays
double df[];
double d2f[];
double f[];
//--- copy
ArrayCopy(f,cf);
//--- change values
p=0;
dp=0;
d2p=0;
n=n-1;
//--- allocation
ArrayResize(df,n+1);
ArrayResize(d2f,n+1);
for(i=0;i<=n;i++)
{
d2f[i]=0;
df[i]=0;
}
//--- calculation
for(m=1;m<=n;m++)
{
for(i=0;i<=n-m;i++)
{
d2f[i]=((t-x[i+m])*d2f[i]+(x[i]-t)*d2f[i+1]+2*df[i]-2*df[i+1])/(x[i]-x[i+m]);
df[i]=((t-x[i+m])*df[i]+f[i]+(x[i]-t)*df[i+1]-f[i+1])/(x[i]-x[i+m]);
f[i]=((t-x[i+m])*f[i]+(x[i]-t)*f[i+1])/(x[i]-x[i+m]);
}
}
//--- change values
p=f[0];
dp=df[0];
d2p=d2f[0];
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestRatIntUnit::BRCunSet(CBarycentricInterpolant &b)
{
//--- create arrays
double x[];
double y[];
double w[];
//--- allocation
ArrayResize(x,1);
ArrayResize(y,1);
ArrayResize(w,1);
//--- change values
x[0]=0;
y[0]=0;
w[0]=1;
//--- function call
CRatInt::BarycentricBuildXYW(x,y,w,1,b);
}
//+------------------------------------------------------------------+
//| Tests whether constant C is solution of 1D LLS problem |
//+------------------------------------------------------------------+
static bool CTestRatIntUnit::Is1DSolution(const int n,double &y[],
double &w[],double c)
{
//--- create variables
bool result;
int i=0;
double s1=0;
double s2=0;
double s3=0;
double delta=0;
//--- initialization
delta=0.001;
//--- Test result
s1=0;
for(i=0;i<=n-1;i++)
s1=s1+CMath::Sqr(w[i]*(c-y[i]));
//--- calculation
s2=0;
s3=0;
for(i=0;i<=n-1;i++)
{
s2=s2+CMath::Sqr(w[i]*(c+delta-y[i]));
s3=s3+CMath::Sqr(w[i]*(c-delta-y[i]));
}
result=s2>=s1 && s3>=s1;
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Testing class CPolInt |
//+------------------------------------------------------------------+
class CTestPolIntUnit
{
private:
//--- private methods
static double InternalPolInt(double &x[],double &cf[],int n,const double t);
static void BRCunSet(CBarycentricInterpolant &b);
public:
//--- constructor, destructor
CTestPolIntUnit(void);
~CTestPolIntUnit(void);
//--- public method
static bool TestPolInt(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestPolIntUnit::CTestPolIntUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestPolIntUnit::~CTestPolIntUnit(void)
{
}
//+------------------------------------------------------------------+
//| Unit test |
//+------------------------------------------------------------------+
static bool CTestPolIntUnit::TestPolInt(const bool silent)
{
//--- create variables
bool waserrors;
bool interrors;
double threshold=0;
double a=0;
double b=0;
double t=0;
int i=0;
int k=0;
double v=0;
double v0=0;
double v1=0;
double v2=0;
double v3=0;
double v4=0;
double pscale=0;
double poffset=0;
int n=0;
int maxn=0;
int pass=0;
int passcount=0;
//--- create arrays
double x[];
double y[];
double w[];
double c[];
double x2[];
double y2[];
double w2[];
double xfull[];
double yfull[];
double xc[];
double yc[];
int dc[];
//--- objects of classes
CBarycentricInterpolant p;
CBarycentricInterpolant p1;
CBarycentricInterpolant p2;
//--- initialization
waserrors=false;
interrors=false;
maxn=5;
passcount=20;
threshold=1.0E8*CMath::m_machineepsilon;
//--- Test equidistant interpolation
for(pass=1;pass<=passcount;pass++)
{
for(n=1;n<=maxn;n++)
{
//--- prepare task:
//--- * equidistant points
//--- * random Y
//--- * T in [A,B] or near (within 10% of its width)
do
{
a=2*CMath::RandomReal()-1;
b=2*CMath::RandomReal()-1;
}
while(MathAbs(a-b)<=0.2);
//--- change value
t=a+(1.2*CMath::RandomReal()-0.1)*(b-a);
//--- function call
CApServ::TaskGenInt1DEquidist(a,b,n,x,y);
//--- test "fast" equidistant interpolation (no barycentric model)
interrors=interrors || MathAbs(CPolInt::PolynomialCalcEqDist(a,b,y,n,t)-InternalPolInt(x,y,n,t))>threshold;
//--- test "slow" equidistant interpolation (create barycentric model)
BRCunSet(p);
CPolInt::PolynomialBuild(x,y,n,p);
//--- search errors
interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
//--- test "fast" interpolation (create "fast" barycentric model)
BRCunSet(p);
CPolInt::PolynomialBuildEqDist(a,b,y,n,p);
//--- search errors
interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
}
}
//--- Test Chebyshev-1 interpolation
for(pass=1;pass<=passcount;pass++)
{
for(n=1;n<=maxn;n++)
{
//--- prepare task:
//--- * equidistant points
//--- * random Y
//--- * T in [A,B] or near (within 10% of its width)
do
{
a=2*CMath::RandomReal()-1;
b=2*CMath::RandomReal()-1;
}
while(MathAbs(a-b)<=0.2);
//--- change value
t=a+(1.2*CMath::RandomReal()-0.1)*(b-a);
//--- function call
CApServ::TaskGenInt1DCheb1(a,b,n,x,y);
//--- test "fast" interpolation (no barycentric model)
interrors=interrors || MathAbs(CPolInt::PolynomialCalcCheb1(a,b,y,n,t)-InternalPolInt(x,y,n,t))>threshold;
//--- test "slow" interpolation (create barycentric model)
BRCunSet(p);
CPolInt::PolynomialBuild(x,y,n,p);
//--- search errors
interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
//--- test "fast" interpolation (create "fast" barycentric model)
BRCunSet(p);
CPolInt::PolynomialBuildCheb1(a,b,y,n,p);
//--- search errors
interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
}
}
//--- Test Chebyshev-2 interpolation
for(pass=1;pass<=passcount;pass++)
{
for(n=1;n<=maxn;n++)
{
//--- prepare task:
//--- * equidistant points
//--- * random Y
//--- * T in [A,B] or near (within 10% of its width)
do
{
a=2*CMath::RandomReal()-1;
b=2*CMath::RandomReal()-1;
}
while(MathAbs(a-b)<=0.2);
//--- change value
t=a+(1.2*CMath::RandomReal()-0.1)*(b-a);
//--- function call
CApServ::TaskGenInt1DCheb2(a,b,n,x,y);
//--- test "fast" interpolation (no barycentric model)
interrors=interrors || MathAbs(CPolInt::PolynomialCalcCheb2(a,b,y,n,t)-InternalPolInt(x,y,n,t))>threshold;
//--- test "slow" interpolation (create barycentric model)
BRCunSet(p);
CPolInt::PolynomialBuild(x,y,n,p);
//--- search errors
interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
//--- test "fast" interpolation (create "fast" barycentric model)
BRCunSet(p);
CPolInt::PolynomialBuildCheb2(a,b,y,n,p);
//--- search errors
interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
}
}
//--- Testing conversion Barycentric<->Chebyshev
for(pass=1;pass<=passcount;pass++)
{
for(k=1;k<=3;k++)
{
//--- Allocate
ArrayResize(x,k);
ArrayResize(y,k);
//--- Generate problem
a=2*CMath::RandomReal()-1;
b=a+(0.1+CMath::RandomReal())*(2*CMath::RandomInteger(2)-1);
v0=2*CMath::RandomReal()-1;
v1=2*CMath::RandomReal()-1;
v2=2*CMath::RandomReal()-1;
//--- check
if(k==1)
{
x[0]=0.5*(a+b);
y[0]=v0;
}
//--- check
if(k==2)
{
x[0]=a;
y[0]=v0-v1;
x[1]=b;
y[1]=v0+v1;
}
//--- check
if(k==3)
{
x[0]=a;
y[0]=v0-v1+v2;
x[1]=0.5*(a+b);
y[1]=v0-v2;
x[2]=b;
y[2]=v0+v1+v2;
}
//--- Test forward conversion
CPolInt::PolynomialBuild(x,y,k,p);
ArrayResize(c,1);
CPolInt::PolynomialBar2Cheb(p,a,b,c);
//--- search errors
interrors=interrors || CAp::Len(c)!=k;
//--- check
if(k>=1)
interrors=interrors || MathAbs(c[0]-v0)>threshold;
//--- check
if(k>=2)
interrors=interrors || MathAbs(c[1]-v1)>threshold;
//--- check
if(k>=3)
interrors=interrors || MathAbs(c[2]-v2)>threshold;
//--- Test backward conversion
CPolInt::PolynomialCheb2Bar(c,k,a,b,p2);
v=a+CMath::RandomReal()*(b-a);
//--- search errors
interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,v)-CRatInt::BarycentricCalc(p2,v))>threshold;
}
}
//--- Testing conversion Barycentric<->Power
for(pass=1;pass<=passcount;pass++)
{
for(k=1;k<=5;k++)
{
//--- Allocate
ArrayResize(x,k);
ArrayResize(y,k);
//--- Generate problem
poffset=2*CMath::RandomReal()-1;
pscale=(0.1+CMath::RandomReal())*(2*CMath::RandomInteger(2)-1);
v0=2*CMath::RandomReal()-1;
v1=2*CMath::RandomReal()-1;
v2=2*CMath::RandomReal()-1;
v3=2*CMath::RandomReal()-1;
v4=2*CMath::RandomReal()-1;
//--- check
if(k==1)
{
x[0]=poffset;
y[0]=v0;
}
//--- check
if(k==2)
{
x[0]=poffset-pscale;
y[0]=v0-v1;
x[1]=poffset+pscale;
y[1]=v0+v1;
}
//--- check
if(k==3)
{
x[0]=poffset-pscale;
y[0]=v0-v1+v2;
x[1]=poffset;
y[1]=v0;
x[2]=poffset+pscale;
y[2]=v0+v1+v2;
}
//--- check
if(k==4)
{
x[0]=poffset-pscale;
y[0]=v0-v1+v2-v3;
x[1]=poffset-0.5*pscale;
y[1]=v0-0.5*v1+0.25*v2-0.125*v3;
x[2]=poffset+0.5*pscale;
y[2]=v0+0.5*v1+0.25*v2+0.125*v3;
x[3]=poffset+pscale;
y[3]=v0+v1+v2+v3;
}
//--- check
if(k==5)
{
x[0]=poffset-pscale;
y[0]=v0-v1+v2-v3+v4;
x[1]=poffset-0.5*pscale;
y[1]=v0-0.5*v1+0.25*v2-0.125*v3+0.0625*v4;
x[2]=poffset;
y[2]=v0;
x[3]=poffset+0.5*pscale;
y[3]=v0+0.5*v1+0.25*v2+0.125*v3+0.0625*v4;
x[4]=poffset+pscale;
y[4]=v0+v1+v2+v3+v4;
}
//--- Test forward conversion
CPolInt::PolynomialBuild(x,y,k,p);
ArrayResize(c,1);
CPolInt::PolynomialBar2Pow(p,poffset,pscale,c);
//--- search errors
interrors=interrors || CAp::Len(c)!=k;
//--- check
if(k>=1)
interrors=interrors || MathAbs(c[0]-v0)>threshold;
//--- check
if(k>=2)
interrors=interrors || MathAbs(c[1]-v1)>threshold;
//--- check
if(k>=3)
interrors=interrors || MathAbs(c[2]-v2)>threshold;
//--- check
if(k>=4)
interrors=interrors || MathAbs(c[3]-v3)>threshold;
//--- check
if(k>=5)
interrors=interrors || MathAbs(c[4]-v4)>threshold;
//--- Test backward conversion
CPolInt::PolynomialPow2Bar(c,k,poffset,pscale,p2);
v=poffset+(2*CMath::RandomReal()-1)*pscale;
//--- search errors
interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,v)-CRatInt::BarycentricCalc(p2,v))>threshold;
}
}
//--- crash-test: ability to solve tasks which will overflow/underflow
//--- weights with straightforward implementation
for(n=1;n<=20;n++)
{
a=-(0.1*CMath::m_maxrealnumber);
b=0.1*CMath::m_maxrealnumber;
CApServ::TaskGenInt1DEquidist(a,b,n,x,y);
CPolInt::PolynomialBuild(x,y,n,p);
//--- search errors
for(i=0;i<=n-1;i++)
interrors=interrors || p.m_w[i]==0.0;
}
//--- report
waserrors=interrors;
//--- check
if(!silent)
{
Print("TESTING POLYNOMIAL INTERPOLATION");
//--- Normal tests
Print("INTERPOLATION TEST: ");
//--- check
if(interrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- end
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static double CTestPolIntUnit::InternalPolInt(double &x[],double &cf[],
int n,const double t)
{
//--- create variables
int i=0;
int j=0;
//--- create array
double f[];
//--- copy
ArrayCopy(f,cf);
//--- calculation
n=n-1;
for(j=0;j<=n-1;j++)
{
for(i=j+1;i<=n;i++)
f[i]=((t-x[j])*f[i]-(t-x[i])*f[j])/(x[i]-x[j]);
}
//--- return result
return(f[n]);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestPolIntUnit::BRCunSet(CBarycentricInterpolant &b)
{
//--- create arrays
double x[];
double y[];
double w[];
//--- allocation
ArrayResize(x,1);
ArrayResize(y,1);
ArrayResize(w,1);
//--- change values
x[0]=0;
y[0]=0;
w[0]=1;
//--- function call
CRatInt::BarycentricBuildXYW(x,y,w,1,b);
}
//+------------------------------------------------------------------+
//| Testing class CSpline1D |
//+------------------------------------------------------------------+
class CTestSpline1DUnit
{
private:
//--- private methods
static void LConst(const double a,const double b,CSpline1DInterpolant &c,const double lstep,double &l0,double &l1,double &l2);
static bool TestUnpack(CSpline1DInterpolant &c,double &x[]);
static void UnsetSpline1D(CSpline1DInterpolant &c);
static void Unset1D(double &x[]);
static bool Is1DSolution(const int n,double &y[],double &w[],const double c);
public:
//--- constructor, destructor
CTestSpline1DUnit(void);
~CTestSpline1DUnit(void);
//--- public method
static bool TestSpline1D(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestSpline1DUnit::CTestSpline1DUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestSpline1DUnit::~CTestSpline1DUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CSpline1D |
//+------------------------------------------------------------------+
static bool CTestSpline1DUnit::TestSpline1D(const bool silent)
{
//--- create variables
bool waserrors;
bool crserrors;
bool cserrors;
bool hserrors;
bool aserrors;
bool lserrors;
bool dserrors;
bool uperrors;
bool cperrors;
bool lterrors;
bool ierrors;
double nonstrictthreshold=0;
double threshold=0;
int passcount=0;
double lstep=0;
double h=0;
int maxn=0;
int bltype=0;
int brtype=0;
bool periodiccond;
int n=0;
int i=0;
int k=0;
int pass=0;
double a=0;
double b=0;
double bl=0;
double br=0;
double t=0;
double sa=0;
double sb=0;
int n2=0;
double v=0;
double l10=0;
double l11=0;
double l12=0;
double l20=0;
double l21=0;
double l22=0;
double p0=0;
double p1=0;
double p2=0;
double s=0;
double ds=0;
double d2s=0;
double s2=0;
double ds2=0;
double d2s2=0;
double vl=0;
double vm=0;
double vr=0;
double err=0;
double tension=0;
double intab=0;
int i_=0;
//--- create arrays
double x[];
double y[];
double yp[];
double w[];
double w2[];
double y2[];
double d[];
double xc[];
double yc[];
double tmp0[];
double tmp1[];
double tmp2[];
double tmpx[];
int dc[];
//--- objects of classes
CSpline1DInterpolant c;
CSpline1DInterpolant c2;
//--- initialization
waserrors=false;
passcount=20;
lstep=0.005;
h=0.00001;
maxn=10;
threshold=10000*CMath::m_machineepsilon;
nonstrictthreshold=0.00001;
lserrors=false;
cserrors=false;
crserrors=false;
hserrors=false;
aserrors=false;
dserrors=false;
cperrors=false;
uperrors=false;
lterrors=false;
ierrors=false;
//--- General test: linear,cubic,Hermite,Akima
for(n=2;n<=maxn;n++)
{
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(yp,n);
ArrayResize(d,n);
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- Prepare task:
//--- * X contains abscissas from [A,B]
//--- * Y contains function values
//--- * YP contains periodic function values
a=-1-CMath::RandomReal();
b=1+CMath::RandomReal();
bl=2*CMath::RandomReal()-1;
br=2*CMath::RandomReal()-1;
for(i=0;i<=n-1;i++)
{
x[i]=0.5*(b+a)+0.5*(b-a)*MathCos(M_PI*(2*i+1)/(2*n));
//--- check
if(i==0)
x[i]=a;
//--- check
if(i==n-1)
x[i]=b;
//--- change values
y[i]=MathCos(1.3*M_PI*x[i]+0.4);
yp[i]=y[i];
d[i]=-(1.3*M_PI*MathSin(1.3*M_PI*x[i]+0.4));
}
yp[n-1]=yp[0];
//--- swap
for(i=0;i<=n-1;i++)
{
k=CMath::RandomInteger(n);
//--- check
if(k!=i)
{
t=x[i];
x[i]=x[k];
x[k]=t;
t=y[i];
y[i]=y[k];
y[k]=t;
t=yp[i];
yp[i]=yp[k];
yp[k]=t;
t=d[i];
d[i]=d[k];
d[k]=t;
}
}
//--- Build linear spline
//--- Test for general interpolation scheme properties:
//--- * values at nodes
//--- * continuous function
//--- Test for specific properties is implemented below.
CSpline1D::Spline1DBuildLinear(x,y,n,c);
//--- search errors
err=0;
for(i=0;i<=n-1;i++)
err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i])));
//--- search errors
lserrors=lserrors || err>threshold;
LConst(a,b,c,lstep,l10,l11,l12);
LConst(a,b,c,lstep/3,l20,l21,l22);
//--- search errors
lserrors=lserrors || l20/l10>1.2;
//--- Build cubic spline.
//--- Test for interpolation scheme properties:
//--- * values at nodes
//--- * boundary conditions
//--- * continuous function
//--- * continuous first derivative
//--- * continuous second derivative
//--- * periodicity properties
//--- * Spline1DGridDiff(),Spline1DGridDiff2() and Spline1DDiff()
//--- calls must return same results
for(bltype=-1;bltype<=2;bltype++)
{
for(brtype=-1;brtype<=2;brtype++)
{
//--- skip meaningless combination of boundary conditions
//--- (one condition is periodic,another is not)
periodiccond=bltype==-1 || brtype==-1;
//--- check
if(periodiccond && bltype!=brtype)
continue;
//--- build
if(periodiccond)
CSpline1D::Spline1DBuildCubic(x,yp,n,bltype,bl,brtype,br,c);
else
CSpline1D::Spline1DBuildCubic(x,y,n,bltype,bl,brtype,br,c);
//--- interpolation properties
err=0;
//--- check
if(periodiccond)
{
//--- * check values at nodes;spline is periodic so
//--- we add random number of periods to nodes
//--- * we also test for periodicity of derivatives
for(i=0;i<=n-1;i++)
{
v=x[i];
vm=v+(b-a)*(CMath::RandomInteger(5)-2);
t=yp[i]-CSpline1D::Spline1DCalc(c,vm);
//--- search errors
err=MathMax(err,MathAbs(t));
//--- function calls
CSpline1D::Spline1DDiff(c,v,s,ds,d2s);
CSpline1D::Spline1DDiff(c,vm,s2,ds2,d2s2);
//--- search errors
err=MathMax(err,MathAbs(s-s2));
err=MathMax(err,MathAbs(ds-ds2));
err=MathMax(err,MathAbs(d2s-d2s2));
}
//--- periodicity between nodes
v=a+(b-a)*CMath::RandomReal();
vm=v+(b-a)*(CMath::RandomInteger(5)-2);
//--- search errors
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,v)-CSpline1D::Spline1DCalc(c,vm)));
//--- function calls
CSpline1D::Spline1DDiff(c,v,s,ds,d2s);
CSpline1D::Spline1DDiff(c,vm,s2,ds2,d2s2);
//--- search errors
err=MathMax(err,MathAbs(s-s2));
err=MathMax(err,MathAbs(ds-ds2));
err=MathMax(err,MathAbs(d2s-d2s2));
}
else
{
//--- * check values at nodes
for(i=0;i<=n-1;i++)
err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i])));
}
//--- search errors
cserrors=cserrors || err>threshold;
//--- check boundary conditions
err=0;
//--- check
if(bltype==0)
{
//--- function calls
CSpline1D::Spline1DDiff(c,a-h,s,ds,d2s);
CSpline1D::Spline1DDiff(c,a+h,s2,ds2,d2s2);
t=(d2s2-d2s)/(2*h);
//--- search errors
err=MathMax(err,MathAbs(t));
}
//--- check
if(bltype==1)
{
t=(CSpline1D::Spline1DCalc(c,a+h)-CSpline1D::Spline1DCalc(c,a-h))/(2*h);
//--- search errors
err=MathMax(err,MathAbs(bl-t));
}
//--- check
if(bltype==2)
{
t=(CSpline1D::Spline1DCalc(c,a+h)-2*CSpline1D::Spline1DCalc(c,a)+CSpline1D::Spline1DCalc(c,a-h))/CMath::Sqr(h);
//--- search errors
err=MathMax(err,MathAbs(bl-t));
}
//--- check
if(brtype==0)
{
CSpline1D::Spline1DDiff(c,b-h,s,ds,d2s);
CSpline1D::Spline1DDiff(c,b+h,s2,ds2,d2s2);
t=(d2s2-d2s)/(2*h);
//--- search errors
err=MathMax(err,MathAbs(t));
}
//--- check
if(brtype==1)
{
t=(CSpline1D::Spline1DCalc(c,b+h)-CSpline1D::Spline1DCalc(c,b-h))/(2*h);
//--- search errors
err=MathMax(err,MathAbs(br-t));
}
//--- check
if(brtype==2)
{
t=(CSpline1D::Spline1DCalc(c,b+h)-2*CSpline1D::Spline1DCalc(c,b)+CSpline1D::Spline1DCalc(c,b-h))/CMath::Sqr(h);
//--- search errors
err=MathMax(err,MathAbs(br-t));
}
//--- check
if(bltype==-1 || brtype==-1)
{
//--- function calls
CSpline1D::Spline1DDiff(c,a+100*CMath::m_machineepsilon,s,ds,d2s);
CSpline1D::Spline1DDiff(c,b-100*CMath::m_machineepsilon,s2,ds2,d2s2);
//--- search errors
err=MathMax(err,MathAbs(s-s2));
err=MathMax(err,MathAbs(ds-ds2));
err=MathMax(err,MathAbs(d2s-d2s2));
}
//--- search errors
cserrors=cserrors || err>1.0E-3;
//--- Check Lipschitz continuity
LConst(a,b,c,lstep,l10,l11,l12);
LConst(a,b,c,lstep/3,l20,l21,l22);
//--- check
if(l10>1.0E-6)
cserrors=cserrors || l20/l10>1.2;
//--- check
if(l11>1.0E-6)
cserrors=cserrors || l21/l11>1.2;
//--- check
if(l12>1.0E-6)
cserrors=cserrors || l22/l12>1.2;
//--- compare spline1dgriddiff() and spline1ddiff() results
err=0;
//--- check
if(periodiccond)
CSpline1D::Spline1DGridDiffCubic(x,yp,n,bltype,bl,brtype,br,tmp1);
else
CSpline1D::Spline1DGridDiffCubic(x,y,n,bltype,bl,brtype,br,tmp1);
//--- check
if(!CAp::Assert(CAp::Len(tmp1)>=n))
return(false);
for(i=0;i<=n-1;i++)
{
CSpline1D::Spline1DDiff(c,x[i],s,ds,d2s);
//--- search errors
err=MathMax(err,MathAbs(ds-tmp1[i]));
}
//--- check
if(periodiccond)
CSpline1D::Spline1DGridDiff2Cubic(x,yp,n,bltype,bl,brtype,br,tmp1,tmp2);
else
CSpline1D::Spline1DGridDiff2Cubic(x,y,n,bltype,bl,brtype,br,tmp1,tmp2);
for(i=0;i<=n-1;i++)
{
CSpline1D::Spline1DDiff(c,x[i],s,ds,d2s);
//--- search errors
err=MathMax(err,MathAbs(ds-tmp1[i]));
err=MathMax(err,MathAbs(d2s-tmp2[i]));
}
//--- search errors
cserrors=cserrors || err>threshold;
//--- compare spline1dconv()/convdiff()/convdiff2() and spline1ddiff() results
n2=2+CMath::RandomInteger(2*n);
ArrayResize(tmpx,n2);
//--- calculation
for(i=0;i<=n2-1;i++)
tmpx[i]=0.5*(a+b)+(a-b)*(2*CMath::RandomReal()-1);
err=0;
//--- check
if(periodiccond)
CSpline1D::Spline1DConvCubic(x,yp,n,bltype,bl,brtype,br,tmpx,n2,tmp0);
else
CSpline1D::Spline1DConvCubic(x,y,n,bltype,bl,brtype,br,tmpx,n2,tmp0);
for(i=0;i<=n2-1;i++)
{
CSpline1D::Spline1DDiff(c,tmpx[i],s,ds,d2s);
//--- search errors
err=MathMax(err,MathAbs(s-tmp0[i]));
}
//--- check
if(periodiccond)
CSpline1D::Spline1DConvDiffCubic(x,yp,n,bltype,bl,brtype,br,tmpx,n2,tmp0,tmp1);
else
CSpline1D::Spline1DConvDiffCubic(x,y,n,bltype,bl,brtype,br,tmpx,n2,tmp0,tmp1);
for(i=0;i<=n2-1;i++)
{
CSpline1D::Spline1DDiff(c,tmpx[i],s,ds,d2s);
//--- search errors
err=MathMax(err,MathAbs(s-tmp0[i]));
err=MathMax(err,MathAbs(ds-tmp1[i]));
}
//--- check
if(periodiccond)
CSpline1D::Spline1DConvDiff2Cubic(x,yp,n,bltype,bl,brtype,br,tmpx,n2,tmp0,tmp1,tmp2);
else
CSpline1D::Spline1DConvDiff2Cubic(x,y,n,bltype,bl,brtype,br,tmpx,n2,tmp0,tmp1,tmp2);
for(i=0;i<=n2-1;i++)
{
CSpline1D::Spline1DDiff(c,tmpx[i],s,ds,d2s);
//--- search errors
err=MathMax(err,MathAbs(s-tmp0[i]));
err=MathMax(err,MathAbs(ds-tmp1[i]));
err=MathMax(err,MathAbs(d2s-tmp2[i]));
}
//--- search errors
cserrors=cserrors || err>threshold;
}
}
//--- Build Catmull-Rom spline.
//--- Test for interpolation scheme properties:
//--- * values at nodes
//--- * boundary conditions
//--- * continuous function
//--- * continuous first derivative
//--- * periodicity properties
for(bltype=-1;bltype<=0;bltype++)
{
periodiccond=bltype==-1;
//--- select random tension value,then build
if(CMath::RandomReal()>0.5)
{
//--- check
if(CMath::RandomReal()>0.5)
tension=0;
else
tension=1;
}
else
tension=CMath::RandomReal();
//--- check
if(periodiccond)
CSpline1D::Spline1DBuildCatmullRom(x,yp,n,bltype,tension,c);
else
CSpline1D::Spline1DBuildCatmullRom(x,y,n,bltype,tension,c);
//--- interpolation properties
err=0;
//--- check
if(periodiccond)
{
//--- * check values at nodes;spline is periodic so
//--- we add random number of periods to nodes
//--- * we also test for periodicity of first derivative
for(i=0;i<=n-1;i++)
{
v=x[i];
vm=v+(b-a)*(CMath::RandomInteger(5)-2);
t=yp[i]-CSpline1D::Spline1DCalc(c,vm);
//--- search errors
err=MathMax(err,MathAbs(t));
//--- function calls
CSpline1D::Spline1DDiff(c,v,s,ds,d2s);
CSpline1D::Spline1DDiff(c,vm,s2,ds2,d2s2);
//--- search errors
err=MathMax(err,MathAbs(s-s2));
err=MathMax(err,MathAbs(ds-ds2));
}
//--- periodicity between nodes
v=a+(b-a)*CMath::RandomReal();
vm=v+(b-a)*(CMath::RandomInteger(5)-2);
//--- search errors
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,v)-CSpline1D::Spline1DCalc(c,vm)));
//--- function calls
CSpline1D::Spline1DDiff(c,v,s,ds,d2s);
CSpline1D::Spline1DDiff(c,vm,s2,ds2,d2s2);
//--- search errors
err=MathMax(err,MathAbs(s-s2));
err=MathMax(err,MathAbs(ds-ds2));
}
else
{
//--- * check values at nodes
for(i=0;i<=n-1;i++)
err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i])));
}
//--- search errors
crserrors=crserrors || err>threshold;
//--- check boundary conditions
err=0;
//--- check
if(bltype==0)
{
//--- function calls
CSpline1D::Spline1DDiff(c,a-h,s,ds,d2s);
CSpline1D::Spline1DDiff(c,a+h,s2,ds2,d2s2);
t=(d2s2-d2s)/(2*h);
//--- search errors
err=MathMax(err,MathAbs(t));
//--- function calls
CSpline1D::Spline1DDiff(c,b-h,s,ds,d2s);
CSpline1D::Spline1DDiff(c,b+h,s2,ds2,d2s2);
t=(d2s2-d2s)/(2*h);
//--- search errors
err=MathMax(err,MathAbs(t));
}
//--- check
if(bltype==-1)
{
//--- function calls
CSpline1D::Spline1DDiff(c,a+100*CMath::m_machineepsilon,s,ds,d2s);
CSpline1D::Spline1DDiff(c,b-100*CMath::m_machineepsilon,s2,ds2,d2s2);
//--- search errors
err=MathMax(err,MathAbs(s-s2));
err=MathMax(err,MathAbs(ds-ds2));
}
//--- search errors
crserrors=crserrors || err>1.0E-3;
//--- Check Lipschitz continuity
LConst(a,b,c,lstep,l10,l11,l12);
LConst(a,b,c,lstep/3,l20,l21,l22);
//--- check
if(l10>1.0E-6)
crserrors=crserrors || l20/l10>1.2;
//--- check
if(l11>1.0E-6)
crserrors=crserrors || l21/l11>1.2;
}
//--- Build Hermite spline.
//--- Test for interpolation scheme properties:
//--- * values and derivatives at nodes
//--- * continuous function
//--- * continuous first derivative
CSpline1D::Spline1DBuildHermite(x,y,d,n,c);
//--- search errors
err=0;
for(i=0;i<=n-1;i++)
err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i])));
//--- search errors
hserrors=hserrors || err>threshold;
err=0;
for(i=0;i<=n-1;i++)
{
t=(CSpline1D::Spline1DCalc(c,x[i]+h)-CSpline1D::Spline1DCalc(c,x[i]-h))/(2*h);
err=MathMax(err,MathAbs(d[i]-t));
}
//--- search errors
hserrors=hserrors || err>1.0E-3;
LConst(a,b,c,lstep,l10,l11,l12);
LConst(a,b,c,lstep/3,l20,l21,l22);
//--- search errors
hserrors=hserrors || l20/l10>1.2;
hserrors=hserrors || l21/l11>1.2;
//--- Build Akima spline
//--- Test for general interpolation scheme properties:
//--- * values at nodes
//--- * continuous function
//--- * continuous first derivative
//--- Test for specific properties is implemented below.
if(n>=5)
{
CSpline1D::Spline1DBuildAkima(x,y,n,c);
//--- search errors
err=0;
for(i=0;i<=n-1;i++)
err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i])));
//--- search errors
aserrors=aserrors || err>threshold;
LConst(a,b,c,lstep,l10,l11,l12);
LConst(a,b,c,lstep/3,l20,l21,l22);
//--- search errors
hserrors=hserrors || l20/l10>1.2;
hserrors=hserrors || l21/l11>1.2;
}
}
}
//--- Special linear spline test:
//--- test for linearity between x[i] and x[i+1]
for(n=2;n<=maxn;n++)
{
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
//--- Prepare task
a=-1;
b=1;
for(i=0;i<=n-1;i++)
{
x[i]=a+(b-a)*i/(n-1);
y[i]=2*CMath::RandomReal()-1;
}
//--- function call
CSpline1D::Spline1DBuildLinear(x,y,n,c);
//--- Test
err=0;
for(k=0;k<=n-2;k++)
{
a=x[k];
b=x[k+1];
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
t=a+(b-a)*CMath::RandomReal();
v=y[k]+(t-a)/(b-a)*(y[k+1]-y[k]);
//--- search errors
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-v));
}
}
//--- search errors
lserrors=lserrors || err>threshold;
}
//--- Special Akima test: test outlier sensitivity
//--- Spline value at (x[i],x[i+1]) should depend from
//--- f[i-2],f[i-1],f[i],f[i+1],f[i+2],f[i+3] only.
for(n=5;n<=maxn;n++)
{
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(y2,n);
//--- Prepare unperturbed Akima spline
a=-1;
b=1;
for(i=0;i<=n-1;i++)
{
x[i]=a+(b-a)*i/(n-1);
y[i]=MathCos(1.3*M_PI*x[i]+0.4);
}
//--- function call
CSpline1D::Spline1DBuildAkima(x,y,n,c);
//--- Process perturbed tasks
err=0;
for(k=0;k<=n-1;k++)
{
for(i_=0;i_<=n-1;i_++)
y2[i_]=y[i_];
y2[k]=5;
//--- function call
CSpline1D::Spline1DBuildAkima(x,y2,n,c2);
//--- Test left part independence
if(k-3>=1)
{
a=-1;
b=x[k-3];
for(pass=1;pass<=passcount;pass++)
{
t=a+(b-a)*CMath::RandomReal();
//--- search errors
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-CSpline1D::Spline1DCalc(c2,t)));
}
}
//--- Test right part independence
if(k+3<=n-2)
{
a=x[k+3];
b=1;
for(pass=1;pass<=passcount;pass++)
{
t=a+(b-a)*CMath::RandomReal();
//--- search errors
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-CSpline1D::Spline1DCalc(c2,t)));
}
}
}
//--- search errors
aserrors=aserrors || err>threshold;
}
//--- Differentiation,copy/unpack test
for(n=2;n<=maxn;n++)
{
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
//--- Prepare cubic spline
a=-1-CMath::RandomReal();
b=1+CMath::RandomReal();
for(i=0;i<=n-1;i++)
{
x[i]=a+(b-a)*i/(n-1);
y[i]=MathCos(1.3*M_PI*x[i]+0.4);
}
//--- function call
CSpline1D::Spline1DBuildCubic(x,y,n,2,0.0,2,0.0,c);
//--- Test diff
err=0;
for(pass=1;pass<=passcount;pass++)
{
t=a+(b-a)*CMath::RandomReal();
//--- function calls
CSpline1D::Spline1DDiff(c,t,s,ds,d2s);
vl=CSpline1D::Spline1DCalc(c,t-h);
vm=CSpline1D::Spline1DCalc(c,t);
vr=CSpline1D::Spline1DCalc(c,t+h);
//--- search errors
err=MathMax(err,MathAbs(s-vm));
err=MathMax(err,MathAbs(ds-(vr-vl)/(2*h)));
err=MathMax(err,MathAbs(d2s-(vr-2*vm+vl)/CMath::Sqr(h)));
}
//--- search errors
dserrors=dserrors || err>0.001;
//--- Test copy
UnsetSpline1D(c2);
CSpline1D::Spline1DCopy(c,c2);
//--- search errors
err=0;
for(pass=1;pass<=passcount;pass++)
{
t=a+(b-a)*CMath::RandomReal();
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-CSpline1D::Spline1DCalc(c2,t)));
}
//--- search errors
cperrors=cperrors || err>threshold;
//--- Test unpack
uperrors=uperrors || !TestUnpack(c,x);
//--- Test lin.trans.
err=0;
for(pass=1;pass<=passcount;pass++)
{
//--- LinTransX,general A
sa=4*CMath::RandomReal()-2;
sb=2*CMath::RandomReal()-1;
t=a+(b-a)*CMath::RandomReal();
//--- function calls
CSpline1D::Spline1DCopy(c,c2);
CSpline1D::Spline1DLinTransX(c2,sa,sb);
//--- search errors
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-CSpline1D::Spline1DCalc(c2,(t-sb)/sa)));
//--- LinTransX,special case: A=0
sb=2*CMath::RandomReal()-1;
t=a+(b-a)*CMath::RandomReal();
//--- function calls
CSpline1D::Spline1DCopy(c,c2);
CSpline1D::Spline1DLinTransX(c2,0,sb);
//--- search errors
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,sb)-CSpline1D::Spline1DCalc(c2,t)));
//--- LinTransY
sa=2*CMath::RandomReal()-1;
sb=2*CMath::RandomReal()-1;
t=a+(b-a)*CMath::RandomReal();
//--- function calls
CSpline1D::Spline1DCopy(c,c2);
CSpline1D::Spline1DLinTransY(c2,sa,sb);
//--- search errors
err=MathMax(err,MathAbs(sa*CSpline1D::Spline1DCalc(c,t)+sb-CSpline1D::Spline1DCalc(c2,t)));
}
//--- search errors
lterrors=lterrors || err>threshold;
}
//--- Testing integration.
//--- Three tests are performed:
//--- * approximate test (well behaved smooth function,many points,
//--- integration inside [a,b]),non-periodic spline
//--- * exact test (integration of parabola,outside of [a,b],non-periodic spline
//--- * approximate test for periodic splines. F(x)=cos(2*pi*x)+1.
//--- Period length is equals to 1.0,so all operations with
//--- multiples of period are done exactly. For each value of PERIOD
//--- we calculate and test integral at four points:
//--- - 0 < t0 < PERIOD
//--- - t1=PERIOD-eps
//--- - t2=PERIOD
//--- - t3=PERIOD+eps
err=0;
for(n=20;n<=35;n++)
{
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
for(pass=1;pass<=passcount;pass++)
{
//--- Prepare cubic spline
a=-1-0.2*CMath::RandomReal();
b=1+0.2*CMath::RandomReal();
for(i=0;i<=n-1;i++)
{
x[i]=a+(b-a)*i/(n-1);
y[i]=MathSin(M_PI*x[i]+0.4)+MathExp(x[i]);
}
//--- change values
bl=M_PI*MathCos(M_PI*a+0.4)+MathExp(a);
br=M_PI*MathCos(M_PI*b+0.4)+MathExp(b);
//--- function call
CSpline1D::Spline1DBuildCubic(x,y,n,1,bl,1,br,c);
//--- Test
t=a+(b-a)*CMath::RandomReal();
v=-(MathCos(M_PI*a+0.4)/M_PI)+MathExp(a);
v=-(MathCos(M_PI*t+0.4)/M_PI)+MathExp(t)-v;
v=v-CSpline1D::Spline1DIntegrate(c,t);
//--- search errors
err=MathMax(err,MathAbs(v));
}
}
//--- search errors
ierrors=ierrors || err>0.001;
p0=2*CMath::RandomReal()-1;
p1=2*CMath::RandomReal()-1;
p2=2*CMath::RandomReal()-1;
a=-CMath::RandomReal()-0.5;
b=CMath::RandomReal()+0.5;
n=2;
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(d,n);
x[0]=a;
y[0]=p0+p1*a+p2*CMath::Sqr(a);
d[0]=p1+2*p2*a;
x[1]=b;
y[1]=p0+p1*b+p2*CMath::Sqr(b);
d[1]=p1+2*p2*b;
//--- function call
CSpline1D::Spline1DBuildHermite(x,y,d,n,c);
bl=MathMin(a,b)-MathAbs(b-a);
br=MathMin(a,b)+MathAbs(b-a);
err=0;
//--- calculation
for(pass=1;pass<=100;pass++)
{
t=bl+(br-bl)*CMath::RandomReal();
v=p0*t+p1*CMath::Sqr(t)/2+p2*CMath::Sqr(t)*t/3-(p0*a+p1*CMath::Sqr(a)/2+p2*CMath::Sqr(a)*a/3);
v=v-CSpline1D::Spline1DIntegrate(c,t);
//--- search errors
err=MathMax(err,MathAbs(v));
}
//--- search errors
ierrors=ierrors || err>threshold;
n=100;
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
for(i=0;i<=n-1;i++)
{
x[i]=(double)i/(double)(n-1);
y[i]=MathCos(2*M_PI*x[i])+1;
}
//--- change values
y[0]=2;
y[n-1]=2;
//--- function calls
CSpline1D::Spline1DBuildCubic(x,y,n,-1,0.0,-1,0.0,c);
intab=CSpline1D::Spline1DIntegrate(c,1.0);
v=CMath::RandomReal();
vr=CSpline1D::Spline1DIntegrate(c,v);
//--- search errors
ierrors=ierrors || MathAbs(intab-1)>0.001;
for(i=-10;i<=10;i++)
{
ierrors=ierrors || MathAbs(CSpline1D::Spline1DIntegrate(c,i+v)-(i*intab+vr))>0.001;
ierrors=ierrors || MathAbs(CSpline1D::Spline1DIntegrate(c,i-1000*CMath::m_machineepsilon)-i*intab)>0.001;
ierrors=ierrors || MathAbs(CSpline1D::Spline1DIntegrate(c,i)-i*intab)>0.001;
ierrors=ierrors || MathAbs(CSpline1D::Spline1DIntegrate(c,i+1000*CMath::m_machineepsilon)-i*intab)>0.001;
}
//--- report
waserrors=((((((((lserrors || cserrors) || crserrors) || hserrors) || aserrors) || dserrors) || cperrors) || uperrors) || lterrors) || ierrors;
//--- check
if(!silent)
{
Print("TESTING SPLINE INTERPOLATION");
//--- Normal tests
Print("LINEAR SPLINE TEST: ");
//--- check
if(lserrors)
Print("FAILED");
else
Print("OK");
Print("CUBIC SPLINE TEST: ");
//--- check
if(cserrors)
Print("FAILED");
else
Print("OK");
Print("CATMULL-ROM SPLINE TEST: ");
//--- check
if(crserrors)
Print("FAILED");
else
Print("OK");
Print("HERMITE SPLINE TEST: ");
//--- check
if(hserrors)
Print("FAILED");
else
Print("OK");
Print("AKIMA SPLINE TEST: ");
//--- check
if(aserrors)
Print("FAILED");
else
Print("OK");
Print("DIFFERENTIATION TEST: ");
//--- check
if(dserrors)
Print("FAILED");
else
Print("OK");
Print("COPY/SERIALIZATION TEST: ");
//--- check
if(cperrors)
Print("FAILED");
else
Print("OK");
Print("UNPACK TEST: ");
//--- check
if(uperrors)
Print("FAILED");
else
Print("OK");
Print("LIN.TRANS. TEST: ");
//--- check
if(lterrors)
Print("FAILED");
else
Print("OK");
Print("INTEGRATION TEST: ");
//--- check
if(ierrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- end
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Lipschitz constants for spline inself,first and second |
//| derivatives. |
//+------------------------------------------------------------------+
static void CTestSpline1DUnit::LConst(const double a,const double b,
CSpline1DInterpolant &c,
const double lstep,double &l0,
double &l1,double &l2)
{
//--- create variables
double t=0;
double vl=0;
double vm=0;
double vr=0;
double prevf=0;
double prevd=0;
double prevd2=0;
double f=0;
double d=0;
double d2=0;
//--- change values
l0=0;
l1=0;
l2=0;
t=a-0.1;
vl=CSpline1D::Spline1DCalc(c,t-2*lstep);
vm=CSpline1D::Spline1DCalc(c,t-lstep);
vr=CSpline1D::Spline1DCalc(c,t);
f=vm;
d=(vr-vl)/(2*lstep);
d2=(vr-2*vm+vl)/CMath::Sqr(lstep);
//--- calculation
while(t<=b+0.1)
{
//--- change values
prevf=f;
prevd=d;
prevd2=d2;
vl=vm;
vm=vr;
vr=CSpline1D::Spline1DCalc(c,t+lstep);
f=vm;
d=(vr-vl)/(2*lstep);
d2=(vr-2*vm+vl)/CMath::Sqr(lstep);
l0=MathMax(l0,MathAbs((f-prevf)/lstep));
l1=MathMax(l1,MathAbs((d-prevd)/lstep));
l2=MathMax(l2,MathAbs((d2-prevd2)/lstep));
t=t+lstep;
}
}
//+------------------------------------------------------------------+
//| Unpack testing |
//+------------------------------------------------------------------+
static bool CTestSpline1DUnit::TestUnpack(CSpline1DInterpolant &c,double &x[])
{
//--- create variables
bool result;
int i=0;
int n=0;
double err=0;
double t=0;
double v1=0;
double v2=0;
int pass=0;
int passcount=0;
//--- create matrix
CMatrixDouble tbl;
//--- initialization
passcount=20;
err=0;
//--- function call
CSpline1D::Spline1DUnpack(c,n,tbl);
//--- calculation
for(i=0;i<=n-2;i++)
{
for(pass=1;pass<=passcount;pass++)
{
t=CMath::RandomReal()*(tbl[i][1]-tbl[i][0]);
v1=tbl[i][2]+t*tbl[i][3]+CMath::Sqr(t)*tbl[i][4]+t*CMath::Sqr(t)*tbl[i][5];
v2=CSpline1D::Spline1DCalc(c,tbl[i][0]+t);
//--- search errors
err=MathMax(err,MathAbs(v1-v2));
}
}
//--- search errors
for(i=0;i<=n-2;i++)
err=MathMax(err,MathAbs(x[i]-tbl[i][0]));
//--- search errors
for(i=0;i<=n-2;i++)
err=MathMax(err,MathAbs(x[i+1]-tbl[i][1]));
//--- get result
result=err<100*CMath::m_machineepsilon;
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Unset spline,i.e. initialize it with random garbage |
//+------------------------------------------------------------------+
static void CTestSpline1DUnit::UnsetSpline1D(CSpline1DInterpolant &c)
{
//--- create arrays
double x[];
double y[];
double d[];
//--- allocation
ArrayResize(x,2);
ArrayResize(y,2);
ArrayResize(d,2);
//--- change values
x[0]=-1;
y[0]=CMath::RandomReal();
d[0]=CMath::RandomReal();
x[1]=1;
y[1]=CMath::RandomReal();
d[1]=CMath::RandomReal();
//--- function call
CSpline1D::Spline1DBuildHermite(x,y,d,2,c);
}
//+------------------------------------------------------------------+
//| Unsets real vector |
//+------------------------------------------------------------------+
static void CTestSpline1DUnit::Unset1D(double &x[])
{
//--- allocation
ArrayResize(x,1);
//--- change value
x[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Tests whether constant C is solution of 1D LLS problem |
//+------------------------------------------------------------------+
static bool CTestSpline1DUnit::Is1DSolution(const int n,double &y[],
double &w[],const double c)
{
//--- create variables
bool result;
int i=0;
double s1=0;
double s2=0;
double s3=0;
double delta=0;
//--- initialization
delta=0.001;
//--- Test result
s1=0;
for(i=0;i<=n-1;i++)
s1=s1+CMath::Sqr(w[i]*(c-y[i]));
s2=0;
s3=0;
//--- calculation
for(i=0;i<=n-1;i++)
s2=s2+CMath::Sqr(w[i]*(c+delta-y[i]));
s3=s3+CMath::Sqr(w[i]*(c-delta-y[i]));
result=s2>=s1 && s3>=s1;
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Testing class CMinLM |
//+------------------------------------------------------------------+
class CTestMinLMUnit
{
public:
//--- constructor, destructor
CTestMinLMUnit(void);
~CTestMinLMUnit(void);
//--- public methods
static bool TestMinLM(const bool silent);
static bool RKindVSStateCheck(const int rkind,CMinLMState &state);
static void AXMB(CMinLMState &state,CMatrixDouble &a,double &b[],const int n);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestMinLMUnit::CTestMinLMUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestMinLMUnit::~CTestMinLMUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CMinLM |
//+------------------------------------------------------------------+
static bool CTestMinLMUnit::TestMinLM(const bool silent)
{
//--- create variables
bool waserrors;
bool referror;
bool lin1error;
bool lin2error;
bool eqerror;
bool converror;
bool scerror;
bool restartserror;
bool othererrors;
int rkind=0;
int ckind=0;
int tmpkind=0;
double epsf=0;
double epsx=0;
double epsg=0;
int maxits=0;
int n=0;
int m=0;
int i=0;
int j=0;
double v=0;
double s=0;
double stpmax=0;
double h=0;
double fprev=0;
double xprev=0;
int i_=0;
//--- create arrays
double x[];
double xe[];
double b[];
double bl[];
double bu[];
double xlast[];
//--- create matrix
CMatrixDouble a;
//--- objects of classes
CMinLMState state;
CMinLMReport rep;
//--- initialization
waserrors=false;
referror=false;
lin1error=false;
lin2error=false;
eqerror=false;
converror=false;
scerror=false;
othererrors=false;
restartserror=false;
//--- Reference problem.
//--- See comments for RKindVsStateCheck() for more info about RKind.
//--- NOTES: we also test negative RKind's corresponding to "inexact" schemes
//--- which use approximate finite difference Jacobian.
ArrayResize(x,3);
n=3;
m=3;
h=0.0001;
//--- calculation
for(rkind=-2;rkind<=5;rkind++)
{
//--- change values
x[0]=100*CMath::RandomReal()-50;
x[1]=100*CMath::RandomReal()-50;
x[2]=100*CMath::RandomReal()-50;
//--- check
if(rkind==-2)
{
CMinLM::MinLMCreateV(n,m,x,h,state);
CMinLM::MinLMSetAccType(state,1);
}
//--- check
if(rkind==-1)
{
CMinLM::MinLMCreateV(n,m,x,h,state);
CMinLM::MinLMSetAccType(state,0);
}
//--- check
if(rkind==0)
CMinLM::MinLMCreateFJ(n,m,x,state);
//--- check
if(rkind==1)
CMinLM::MinLMCreateFGJ(n,m,x,state);
//--- check
if(rkind==2)
CMinLM::MinLMCreateFGH(n,x,state);
//--- check
if(rkind==3)
{
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,0);
}
//--- check
if(rkind==4)
{
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,1);
}
//--- check
if(rkind==5)
{
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,2);
}
//--- cycle
while(CMinLM::MinLMIteration(state))
{
//--- (x-2)^2 + y^2 + (z-x)^2
if(state.m_needfi)
{
state.m_fi[0]=state.m_x[0]-2;
state.m_fi[1]=state.m_x[1];
state.m_fi[2]=state.m_x[2]-state.m_x[0];
}
//--- check
if(state.m_needfij)
{
state.m_fi[0]=state.m_x[0]-2;
state.m_fi[1]=state.m_x[1];
state.m_fi[2]=state.m_x[2]-state.m_x[0];
state.m_j[0].Set(0,1);
state.m_j[0].Set(1,0);
state.m_j[0].Set(2,0);
state.m_j[1].Set(0,0);
state.m_j[1].Set(1,1);
state.m_j[1].Set(2,0);
state.m_j[2].Set(0,-1);
state.m_j[2].Set(1,0);
state.m_j[2].Set(2,1);
}
//--- check
if((state.m_needf || state.m_needfg) || state.m_needfgh)
state.m_f=CMath::Sqr(state.m_x[0]-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
//--- check
if(state.m_needfg || state.m_needfgh)
{
state.m_g[0]=2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2]);
state.m_g[1]=2*state.m_x[1];
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
}
//--- check
if(state.m_needfgh)
{
state.m_h[0].Set(0,4);
state.m_h[0].Set(1,0);
state.m_h[0].Set(2,-2);
state.m_h[1].Set(0,0);
state.m_h[1].Set(1,2);
state.m_h[1].Set(2,0);
state.m_h[2].Set(0,-2);
state.m_h[2].Set(1,0);
state.m_h[2].Set(2,2);
}
//--- search errors
scerror=scerror || !RKindVSStateCheck(rkind,state);
}
//--- function call
CMinLM::MinLMResults(state,x,rep);
//--- search errors
referror=(((referror || rep.m_terminationtype<=0) || MathAbs(x[0]-2)>0.001) || MathAbs(x[1])>0.001) || MathAbs(x[2]-2)>0.001;
}
//--- Reference bound constrained problem:
//--- min sum((x[i]-xe[i])^4) subject to 0<=x[i]<=1
//--- NOTES:
//--- 1. we test only two optimization modes - V and FGH,
//--- because from algorithm internals we can assume that actual
//--- mode being used doesn't matter for bound constrained optimization
//--- process.
for(tmpkind=0;tmpkind<=1;tmpkind++)
{
for(n=1;n<=5;n++)
{
//--- allocation
ArrayResize(bl,n);
ArrayResize(bu,n);
ArrayResize(xe,n);
ArrayResize(x,n);
for(i=0;i<=n-1;i++)
{
bl[i]=0;
bu[i]=1;
xe[i]=3*CMath::RandomReal()-1;
x[i]=CMath::RandomReal();
}
//--- check
if(tmpkind==0)
CMinLM::MinLMCreateFGH(n,x,state);
//--- check
if(tmpkind==1)
CMinLM::MinLMCreateV(n,n,x,1.0E-3,state);
//--- function calls
CMinLM::MinLMSetCond(state,1.0E-6,0,0,0);
CMinLM::MinLMSetBC(state,bl,bu);
//--- cycle
while(CMinLM::MinLMIteration(state))
{
//--- check
if(state.m_needfi)
{
for(i=0;i<=n-1;i++)
state.m_fi[i]=MathPow(state.m_x[i]-xe[i],2);
}
//--- check
if((state.m_needf || state.m_needfg) || state.m_needfgh)
{
//--- change value
state.m_f=0;
for(i=0;i<=n-1;i++)
state.m_f=state.m_f+MathPow(state.m_x[i]-xe[i],4);
}
//--- check
if(state.m_needfg || state.m_needfgh)
{
for(i=0;i<=n-1;i++)
state.m_g[i]=4*MathPow(state.m_x[i]-xe[i],3);
}
//--- check
if(state.m_needfgh)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
state.m_h[i].Set(j,0);
}
for(i=0;i<=n-1;i++)
state.m_h[i].Set(i,12*MathPow(state.m_x[i]-xe[i],2));
}
}
//--- function call
CMinLM::MinLMResults(state,x,rep);
//--- check
if(rep.m_terminationtype==4)
{
for(i=0;i<=n-1;i++)
referror=referror || MathAbs(x[i]-CApServ::BoundVal(xe[i],bl[i],bu[i]))>(double)(5.0E-2);
}
else
referror=true;
}
}
//--- 1D problem #1
//--- NOTES: we also test negative RKind's corresponding to "inexact" schemes
//--- which use approximate finite difference Jacobian.
for(rkind=-2;rkind<=5;rkind++)
{
//--- allocation
ArrayResize(x,1);
n=1;
m=1;
h=0.00001;
x[0]=100*CMath::RandomReal()-50;
//--- check
if(rkind==-2)
{
CMinLM::MinLMCreateV(n,m,x,h,state);
CMinLM::MinLMSetAccType(state,1);
}
//--- check
if(rkind==-1)
{
CMinLM::MinLMCreateV(n,m,x,h,state);
CMinLM::MinLMSetAccType(state,0);
}
//--- check
if(rkind==0)
CMinLM::MinLMCreateFJ(n,m,x,state);
//--- check
if(rkind==1)
CMinLM::MinLMCreateFGJ(n,m,x,state);
//--- check
if(rkind==2)
CMinLM::MinLMCreateFGH(n,x,state);
//--- check
if(rkind==3)
{
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,0);
}
//--- check
if(rkind==4)
{
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,1);
}
//--- check
if(rkind==5)
{
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,2);
}
//--- cycle
while(CMinLM::MinLMIteration(state))
{
//--- check
if(state.m_needfi)
state.m_fi[0]=MathSin(state.m_x[0]);
//--- check
if(state.m_needfij)
{
state.m_fi[0]=MathSin(state.m_x[0]);
state.m_j[0].Set(0,MathCos(state.m_x[0]));
}
//--- check
if((state.m_needf || state.m_needfg) || state.m_needfgh)
state.m_f=CMath::Sqr(MathSin(state.m_x[0]));
//--- check
if(state.m_needfg || state.m_needfgh)
state.m_g[0]=2*MathSin(state.m_x[0])*MathCos(state.m_x[0]);
//--- check
if(state.m_needfgh)
state.m_h[0].Set(0,2*(MathCos(state.m_x[0])*MathCos(state.m_x[0])-MathSin(state.m_x[0])*MathSin(state.m_x[0])));
//--- search errors
scerror=scerror || !RKindVSStateCheck(rkind,state);
}
//--- function call
CMinLM::MinLMResults(state,x,rep);
//--- search errors
lin1error=rep.m_terminationtype<=0 || MathAbs(x[0]/M_PI-(int)MathRound(x[0]/M_PI))>0.001;
}
//--- Linear equations: test normal optimization and optimization with restarts
for(n=1;n<=10;n++)
{
//--- Prepare task
h=0.00001;
//--- function call
CMatGen::RMatrixRndCond(n,100,a);
//--- allocation
ArrayResize(x,n);
ArrayResize(xe,n);
ArrayResize(b,n);
//--- change values
for(i=0;i<=n-1;i++)
xe[i]=2*CMath::RandomReal()-1;
for(i=0;i<=n-1;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*xe[i_];
b[i]=v;
}
//--- Test different RKind
//--- NOTES: we also test negative RKind's corresponding to "inexact" schemes
//--- which use approximate finite difference Jacobian.
for(rkind=-2;rkind<=5;rkind++)
{
//--- Solve task (first attempt)
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- check
if(rkind==-2)
{
CMinLM::MinLMCreateV(n,n,x,h,state);
CMinLM::MinLMSetAccType(state,1);
}
//--- check
if(rkind==-1)
{
CMinLM::MinLMCreateV(n,n,x,h,state);
CMinLM::MinLMSetAccType(state,0);
}
//--- check
if(rkind==0)
CMinLM::MinLMCreateFJ(n,n,x,state);
//--- check
if(rkind==1)
CMinLM::MinLMCreateFGJ(n,n,x,state);
//--- check
if(rkind==2)
CMinLM::MinLMCreateFGH(n,x,state);
//--- check
if(rkind==3)
{
CMinLM::MinLMCreateVJ(n,n,x,state);
CMinLM::MinLMSetAccType(state,0);
}
//--- check
if(rkind==4)
{
CMinLM::MinLMCreateVJ(n,n,x,state);
CMinLM::MinLMSetAccType(state,1);
}
//--- check
if(rkind==5)
{
CMinLM::MinLMCreateVJ(n,n,x,state);
CMinLM::MinLMSetAccType(state,2);
}
//--- cycle
while(CMinLM::MinLMIteration(state))
{
AXMB(state,a,b,n);
scerror=scerror || !RKindVSStateCheck(rkind,state);
}
//--- function call
CMinLM::MinLMResults(state,x,rep);
//--- search errors
eqerror=eqerror || rep.m_terminationtype<=0;
for(i=0;i<=n-1;i++)
eqerror=eqerror || MathAbs(x[i]-xe[i])>0.001;
//--- Now we try to restart algorithm from new point
for(i=0;i<=n-1;i++)
x[i]=2*CMath::RandomReal()-1;
//--- function call
CMinLM::MinLMRestartFrom(state,x);
//--- cycle
while(CMinLM::MinLMIteration(state))
{
AXMB(state,a,b,n);
scerror=scerror || !RKindVSStateCheck(rkind,state);
}
//--- function call
CMinLM::MinLMResults(state,x,rep);
//--- search errors
restartserror=restartserror || rep.m_terminationtype<=0;
for(i=0;i<=n-1;i++)
restartserror=restartserror || MathAbs(x[i]-xe[i])>0.001;
}
}
//--- Testing convergence properties using
//--- different optimizer types and different conditions.
//--- Only limited subset of optimizers is tested because some
//--- optimizers converge too quickly.
s=100;
for(rkind=0;rkind<=5;rkind++)
{
//--- Skip FGH optimizer - it converges too quickly
if(rkind==2)
continue;
//--- Test
for(ckind=0;ckind<=3;ckind++)
{
epsg=0;
epsf=0;
epsx=0;
maxits=0;
//--- check
if(ckind==0)
epsf=0.000001;
//--- check
if(ckind==1)
epsx=0.000001;
//--- check
if(ckind==2)
maxits=2;
//--- check
if(ckind==3)
epsg=0.0001;
//--- allocation
ArrayResize(x,3);
//--- change values
n=3;
m=3;
for(i=0;i<=2;i++)
x[i]=6;
//--- check
if(rkind==0)
CMinLM::MinLMCreateFJ(n,m,x,state);
//--- check
if(rkind==1)
CMinLM::MinLMCreateFGJ(n,m,x,state);
//--- check
if(!CAp::Assert(rkind!=2))
return(false);
//--- check
if(rkind==3)
{
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,0);
}
//--- check
if(rkind==4)
{
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,1);
}
//--- check
if(rkind==5)
{
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,2);
}
//--- function call
CMinLM::MinLMSetCond(state,epsg,epsf,epsx,maxits);
//--- cycle
while(CMinLM::MinLMIteration(state))
{
//--- check
if(state.m_needfi || state.m_needfij)
{
state.m_fi[0]=s*(MathExp(state.m_x[0])-2);
state.m_fi[1]=CMath::Sqr(state.m_x[1])+1;
state.m_fi[2]=state.m_x[2]-state.m_x[0];
}
//--- check
if(state.m_needfij)
{
state.m_j[0].Set(0,s*MathExp(state.m_x[0]));
state.m_j[0].Set(1,0);
state.m_j[0].Set(2,0);
state.m_j[1].Set(0,0);
state.m_j[1].Set(1,2*state.m_x[1]);
state.m_j[1].Set(2,0);
state.m_j[2].Set(0,-1);
state.m_j[2].Set(1,0);
state.m_j[2].Set(2,1);
}
//--- check
if((state.m_needf || state.m_needfg) || state.m_needfgh)
state.m_f=s*CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1])+1)+CMath::Sqr(state.m_x[2]-state.m_x[0]);
//--- check
if(state.m_needfg || state.m_needfgh)
{
state.m_g[0]=s*2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
state.m_g[1]=2*(CMath::Sqr(state.m_x[1])+1)*2*state.m_x[1];
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
}
//--- check
if(state.m_needfgh)
{
state.m_h[0].Set(0,s*(4*CMath::Sqr(MathExp(state.m_x[0]))-4*MathExp(state.m_x[0]))+2);
state.m_h[0].Set(1,0);
state.m_h[0].Set(2,-2);
state.m_h[1].Set(0,0);
state.m_h[1].Set(1,12*CMath::Sqr(state.m_x[1])+4);
state.m_h[1].Set(2,0);
state.m_h[2].Set(0,-2);
state.m_h[2].Set(1,0);
state.m_h[2].Set(2,2);
}
//--- search errors
scerror=scerror || !RKindVSStateCheck(rkind,state);
}
//--- function call
CMinLM::MinLMResults(state,x,rep);
//--- check
if(ckind==0)
{
//--- search errors
converror=converror || MathAbs(x[0]-MathLog(2))>0.05;
converror=converror || MathAbs(x[1])>0.05;
converror=converror || MathAbs(x[2]-MathLog(2))>0.05;
converror=converror || rep.m_terminationtype!=1;
}
//--- check
if(ckind==1)
{
//--- search errors
converror=converror || MathAbs(x[0]-MathLog(2))>0.05;
converror=converror || MathAbs(x[1])>0.05;
converror=converror || MathAbs(x[2]-MathLog(2))>0.05;
converror=converror || rep.m_terminationtype!=2;
}
//--- check
if(ckind==2)
{
//--- search errors
converror=(converror || rep.m_terminationtype!=5) || rep.m_iterationscount!=maxits;
}
//--- check
if(ckind==3)
{
//--- search errors
converror=converror || MathAbs(x[0]-MathLog(2))>0.05;
converror=converror || MathAbs(x[1])>0.05;
converror=converror || MathAbs(x[2]-MathLog(2))>0.05;
converror=converror || rep.m_terminationtype!=4;
}
}
}
//--- Other properties:
//--- 1. test reports (F should form monotone sequence)
//--- 2. test maximum step
for(rkind=0;rkind<=5;rkind++)
{
//--- reports:
//--- * check that first report is initial point
//--- * check that F is monotone decreasing
//--- * check that last report is final result
n=3;
m=3;
s=100;
//--- allocation
ArrayResize(x,n);
ArrayResize(xlast,n);
for(i=0;i<=n-1;i++)
x[i]=6;
//--- check
if(rkind==0)
CMinLM::MinLMCreateFJ(n,m,x,state);
//--- check
if(rkind==1)
CMinLM::MinLMCreateFGJ(n,m,x,state);
//--- check
if(rkind==2)
CMinLM::MinLMCreateFGH(n,x,state);
//--- check
if(rkind==3)
{
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,0);
}
//--- check
if(rkind==4)
{
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,1);
}
//--- check
if(rkind==5)
{
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,2);
}
//--- function calls
CMinLM::MinLMSetCond(state,0,0,0,4);
CMinLM::MinLMSetXRep(state,true);
//--- change value
fprev=CMath::m_maxrealnumber;
//--- cycle
while(CMinLM::MinLMIteration(state))
{
//--- check
if(state.m_needfi || state.m_needfij)
{
state.m_fi[0]=MathSqrt(s)*(MathExp(state.m_x[0])-2);
state.m_fi[1]=state.m_x[1];
state.m_fi[2]=state.m_x[2]-state.m_x[0];
}
//--- check
if(state.m_needfij)
{
state.m_j[0].Set(0,MathSqrt(s)*MathExp(state.m_x[0]));
state.m_j[0].Set(1,0);
state.m_j[0].Set(2,0);
state.m_j[1].Set(0,0);
state.m_j[1].Set(1,1);
state.m_j[1].Set(2,0);
state.m_j[2].Set(0,-1);
state.m_j[2].Set(1,0);
state.m_j[2].Set(2,1);
}
//--- check
if((state.m_needf || state.m_needfg) || state.m_needfgh)
state.m_f=s*CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
//--- check
if(state.m_needfg || state.m_needfgh)
{
state.m_g[0]=s*2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
state.m_g[1]=2*state.m_x[1];
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
}
//--- check
if(state.m_needfgh)
{
state.m_h[0].Set(0,s*(4*CMath::Sqr(MathExp(state.m_x[0]))-4*MathExp(state.m_x[0]))+2);
state.m_h[0].Set(1,0);
state.m_h[0].Set(2,-2);
state.m_h[1].Set(0,0);
state.m_h[1].Set(1,2);
state.m_h[1].Set(2,0);
state.m_h[2].Set(0,-2);
state.m_h[2].Set(1,0);
state.m_h[2].Set(2,2);
}
//--- search errors
scerror=scerror || !RKindVSStateCheck(rkind,state);
//--- check
if(state.m_xupdated)
{
othererrors=othererrors || state.m_f>fprev;
//--- check
if(fprev==CMath::m_maxrealnumber)
{
for(i=0;i<=n-1;i++)
othererrors=othererrors || state.m_x[i]!=x[i];
}
fprev=state.m_f;
for(i_=0;i_<=n-1;i_++)
xlast[i_]=state.m_x[i_];
}
}
//--- function call
CMinLM::MinLMResults(state,x,rep);
//--- search errors
for(i=0;i<=n-1;i++)
othererrors=othererrors || x[i]!=xlast[i];
}
n=1;
//--- allocation
ArrayResize(x,n);
x[0]=100;
stpmax=0.05+0.05*CMath::RandomReal();
//--- function calls
CMinLM::MinLMCreateFGH(n,x,state);
CMinLM::MinLMSetCond(state,1.0E-9,0,0,0);
CMinLM::MinLMSetStpMax(state,stpmax);
CMinLM::MinLMSetXRep(state,true);
xprev=x[0];
//--- cycle
while(CMinLM::MinLMIteration(state))
{
//--- check
if((state.m_needf || state.m_needfg) || state.m_needfgh)
state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]);
//--- check
if(state.m_needfg || state.m_needfgh)
state.m_g[0]=MathExp(state.m_x[0])-MathExp(-state.m_x[0]);
//--- check
if(state.m_needfgh)
state.m_h[0].Set(0,MathExp(state.m_x[0])+MathExp(-state.m_x[0]));
//--- search errors
othererrors=othererrors || MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax);
//--- check
if(state.m_xupdated)
xprev=state.m_x[0];
}
//--- end
waserrors=((((((referror || lin1error) || lin2error) || eqerror) || converror) || scerror) || othererrors) || restartserror;
//--- check
if(!silent)
{
Print("TESTING LEVENBERG-MARQUARDT OPTIMIZATION");
Print("REFERENCE PROBLEMS: ");
//--- check
if(referror)
Print("FAILED");
else
Print("OK");
Print("1-D PROBLEM #1: ");
//--- check
if(lin1error)
Print("FAILED");
else
Print("OK");
Print("1-D PROBLEM #2: ");
//--- check
if(lin2error)
Print("FAILED");
else
Print("OK");
Print("LINEAR EQUATIONS: ");
//--- check
if(eqerror)
Print("FAILED");
else
Print("OK");
Print("RESTARTS: ");
//--- check
if(restartserror)
Print("FAILED");
else
Print("OK");
Print("CONVERGENCE PROPERTIES: ");
//--- check
if(converror)
Print("FAILED");
else
Print("OK");
Print("STATE FIELDS CONSISTENCY: ");
//--- check
if(scerror)
Print("FAILED");
else
Print("OK");
Print("OTHER PROPERTIES: ");
//--- check
if(othererrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Asserts that State fields are consistent with RKind. |
//| Returns False otherwise. |
//| RKind is an algorithm selector: |
//| * -2=V,AccType=1 |
//| * -1=V,AccType=0 |
//| * 0=FJ |
//| * 1=FGJ |
//| * 2=FGH |
//| * 3=VJ,AccType=0 |
//| * 4=VJ,AccType=1 |
//| * 5=VJ,AccType=2 |
//+------------------------------------------------------------------+
static bool CTestMinLMUnit::RKindVSStateCheck(const int rkind,CMinLMState &state)
{
//--- create a variable
int nset=0;
//--- check
if(state.m_needfi)
nset=nset+1;
//--- check
if(state.m_needf)
nset=nset+1;
//--- check
if(state.m_needfg)
nset=nset+1;
//--- check
if(state.m_needfij)
nset=nset+1;
//--- check
if(state.m_needfgh)
nset=nset+1;
//--- check
if(state.m_xupdated)
nset=nset+1;
//--- check
if(nset!=1)
return(false);
//--- check
if(rkind==-2)
return(state.m_needfi || state.m_xupdated);
//--- check
if(rkind==-1)
return(state.m_needfi || state.m_xupdated);
//--- check
if(rkind==0)
return(state.m_needf || state.m_needfij || state.m_xupdated);
//--- check
if(rkind==1)
return(state.m_needf || state.m_needfij || state.m_needfg || state.m_xupdated);
//--- check
if(rkind==2)
return(state.m_needf || state.m_needfg || state.m_needfgh || state.m_xupdated);
//--- check
if(rkind==3)
return(state.m_needfi || state.m_needfij || state.m_xupdated);
//--- check
if(rkind==4)
return(state.m_needfi || state.m_needfij || state.m_xupdated);
//--- check
if(rkind==5)
return(state.m_needfi || state.m_needfij || state.m_xupdated);
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| Calculates FI/F/G/H for problem min(||Ax-b||) |
//+------------------------------------------------------------------+
static void CTestMinLMUnit::AXMB(CMinLMState &state,CMatrixDouble &a,
double &b[],const int n)
{
//--- create variables
int i=0;
int j=0;
int k=0;
double v=0;
int i_=0;
//--- check
if((state.m_needf || state.m_needfg) || state.m_needfgh)
state.m_f=0;
//--- check
if(state.m_needfg || state.m_needfgh)
{
for(i=0;i<=n-1;i++)
state.m_g[i]=0;
}
//--- check
if(state.m_needfgh)
{
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
state.m_h[i].Set(j,0);
}
}
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=a[i][i_]*state.m_x[i_];
//--- check
if((state.m_needf || state.m_needfg) || state.m_needfgh)
state.m_f=state.m_f+CMath::Sqr(v-b[i]);
//--- check
if(state.m_needfg || state.m_needfgh)
{
for(j=0;j<=n-1;j++)
state.m_g[j]=state.m_g[j]+2*(v-b[i])*a[i][j];
}
//--- check
if(state.m_needfgh)
{
for(j=0;j<=n-1;j++)
{
for(k=0;k<=n-1;k++)
state.m_h[j].Set(k,state.m_h[j][k]+2*a[i][j]*a[i][k]);
}
}
//--- check
if(state.m_needfi)
state.m_fi[i]=v-b[i];
//--- check
if(state.m_needfij)
{
state.m_fi[i]=v-b[i];
for(i_=0;i_<=n-1;i_++)
state.m_j[i].Set(i_,a[i][i_]);
}
}
}
//+------------------------------------------------------------------+
//| Testing class CLSFit |
//+------------------------------------------------------------------+
class CTestLSFitUnit
{
private:
//--- private methods
static void TestPolynomialFitting(bool &fiterrors);
static void TestRationalFitting(bool &fiterrors);
static void TestSplineFitting(bool &fiterrors);
static void TestGeneralFitting(bool &llserrors,bool &nlserrors);
static bool IsGLSSolution(const int n,const int m,const int k,double &y[],double &w[],CMatrixDouble &fmatrix,CMatrixDouble &cmatrix,double &cc[]);
static double GetGLSError(const int n,const int m,double &y[],double &w[],CMatrixDouble &fmatrix,double &c[]);
static void FitLinearNonlinear(const int m,const int deravailable,CMatrixDouble &xy,CLSFitState &state,bool &nlserrors);
public:
//--- constructor, destructor
CTestLSFitUnit(void);
~CTestLSFitUnit(void);
//--- public method
static bool TestLSFit(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestLSFitUnit::CTestLSFitUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestLSFitUnit::~CTestLSFitUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CLSFit |
//+------------------------------------------------------------------+
static bool CTestLSFitUnit::TestLSFit(const bool silent)
{
//--- create variables
bool waserrors;
bool llserrors;
bool nlserrors;
bool polfiterrors;
bool ratfiterrors;
bool splfiterrors;
//--- initialization
waserrors=false;
//--- function calls
TestPolynomialFitting(polfiterrors);
TestRationalFitting(ratfiterrors);
TestSplineFitting(splfiterrors);
TestGeneralFitting(llserrors,nlserrors);
//--- report
waserrors=(((llserrors || nlserrors) || polfiterrors) || ratfiterrors) || splfiterrors;
//--- check
if(!silent)
{
Print("TESTING LEAST SQUARES");
Print("POLYNOMIAL LEAST SQUARES: ");
//--- check
if(polfiterrors)
Print("FAILED");
else
Print("OK");
Print("RATIONAL LEAST SQUARES: ");
//--- check
if(ratfiterrors)
Print("FAILED");
else
Print("OK");
Print("SPLINE LEAST SQUARES: ");
//--- check
if(splfiterrors)
Print("FAILED");
else
Print("OK");
Print("LINEAR LEAST SQUARES: ");
//--- check
if(llserrors)
Print("FAILED");
else
Print("OK");
Print("NON-LINEAR LEAST SQUARES: ");
//--- check
if(nlserrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- end
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Unit test |
//+------------------------------------------------------------------+
static void CTestLSFitUnit::TestPolynomialFitting(bool &fiterrors)
{
//--- create variables
double threshold=0;
double t=0;
int i=0;
int k=0;
int info=0;
int info2=0;
double v=0;
double v0=0;
double v1=0;
double v2=0;
double s=0;
double xmin=0;
double xmax=0;
double refrms=0;
double refavg=0;
double refavgrel=0;
double refmax=0;
int n=0;
int m=0;
int maxn=0;
int pass=0;
int passcount=0;
//--- create arrays
double x[];
double y[];
double w[];
double x2[];
double y2[];
double w2[];
double xfull[];
double yfull[];
double xc[];
double yc[];
int dc[];
//--- objects of classes
CBarycentricInterpolant p;
CBarycentricInterpolant p1;
CBarycentricInterpolant p2;
CPolynomialFitReport rep;
CPolynomialFitReport rep2;
//--- initialization
fiterrors=false;
maxn=5;
passcount=20;
threshold=1.0E8*CMath::m_machineepsilon;
//--- Test polunomial fitting
for(pass=1;pass<=passcount;pass++)
{
for(n=1;n<=maxn;n++)
{
//--- N=M+K fitting (i.e. interpolation)
for(k=0;k<=n-1;k++)
{
CApServ::TaskGenInt1D(-1,1,n,xfull,yfull);
//--- allocation
ArrayResize(x,n-k);
ArrayResize(y,n-k);
ArrayResize(w,n-k);
//--- check
if(k>0)
{
//--- allocation
ArrayResize(xc,k);
ArrayResize(yc,k);
ArrayResize(dc,k);
}
//--- change values
for(i=0;i<=n-k-1;i++)
{
x[i]=xfull[i];
y[i]=yfull[i];
w[i]=1+CMath::RandomReal();
}
//--- change values
for(i=0;i<=k-1;i++)
{
xc[i]=xfull[n-k+i];
yc[i]=yfull[n-k+i];
dc[i]=0;
}
//--- function call
CLSFit::PolynomialFitWC(x,y,w,n-k,xc,yc,dc,k,n,info,p1,rep);
//--- check
if(info<=0)
fiterrors=true;
else
{
//--- search errors
for(i=0;i<=n-k-1;i++)
fiterrors=fiterrors || MathAbs(CRatInt::BarycentricCalc(p1,x[i])-y[i])>threshold;
for(i=0;i<=k-1;i++)
fiterrors=fiterrors || MathAbs(CRatInt::BarycentricCalc(p1,xc[i])-yc[i])>threshold;
}
}
//--- Testing constraints on derivatives.
//--- Special tasks which will always have solution:
//--- 1. P(0)=YC[0]
//--- 2. P(0)=YC[0],P'(0)=YC[1]
if(n>1)
{
for(m=3;m<=5;m++)
{
for(k=1;k<=2;k++)
{
CApServ::TaskGenInt1D(-1,1,n,x,y);
//--- allocation
ArrayResize(w,n);
ArrayResize(xc,2);
ArrayResize(yc,2);
ArrayResize(dc,2);
//--- change values
for(i=0;i<=n-1;i++)
w[i]=1+CMath::RandomReal();
xc[0]=0;
yc[0]=2*CMath::RandomReal()-1;
dc[0]=0;
xc[1]=0;
yc[1]=2*CMath::RandomReal()-1;
dc[1]=1;
//--- function call
CLSFit::PolynomialFitWC(x,y,w,n,xc,yc,dc,k,m,info,p1,rep);
//--- check
if(info<=0)
fiterrors=true;
else
{
//--- function call
CRatInt::BarycentricDiff1(p1,0.0,v0,v1);
fiterrors=fiterrors || MathAbs(v0-yc[0])>threshold;
//--- check
if(k==2)
fiterrors=fiterrors || MathAbs(v1-yc[1])>threshold;
}
}
}
}
}
}
//--- calculation
for(m=2;m<=8;m++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- General fitting
//--- interpolating function through M nodes should have
//--- greater RMS error than fitting it through the same M nodes
n=100;
ArrayResize(x2,n);
ArrayResize(y2,n);
ArrayResize(w2,n);
xmin=0;
xmax=2*M_PI;
//--- change values
for(i=0;i<=n-1;i++)
{
x2[i]=2*M_PI*CMath::RandomReal();
y2[i]=MathSin(x2[i]);
w2[i]=1;
}
//--- allocation
ArrayResize(x,m);
ArrayResize(y,m);
for(i=0;i<=m-1;i++)
{
x[i]=xmin+(xmax-xmin)*i/(m-1);
y[i]=MathSin(x[i]);
}
//--- function calls
CPolInt::PolynomialBuild(x,y,m,p1);
CLSFit::PolynomialFitWC(x2,y2,w2,n,xc,yc,dc,0,m,info,p2,rep);
//--- check
if(info<=0)
fiterrors=true;
else
{
//--- calculate P1 (interpolant) RMS error,compare with P2 error
v1=0;
v2=0;
for(i=0;i<=n-1;i++)
{
v1=v1+CMath::Sqr(CRatInt::BarycentricCalc(p1,x2[i])-y2[i]);
v2=v2+CMath::Sqr(CRatInt::BarycentricCalc(p2,x2[i])-y2[i]);
}
v1=MathSqrt(v1/n);
v2=MathSqrt(v2/n);
//--- search errors
fiterrors=fiterrors || v2>v1;
fiterrors=fiterrors || MathAbs(v2-rep.m_rmserror)>threshold;
}
//--- compare weighted and non-weighted
n=20;
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(w,n);
for(i=0;i<=n-1;i++)
{
x[i]=2*CMath::RandomReal()-1;
y[i]=2*CMath::RandomReal()-1;
w[i]=1;
}
//--- function calls
CLSFit::PolynomialFitWC(x,y,w,n,xc,yc,dc,0,m,info,p1,rep);
CLSFit::PolynomialFit(x,y,n,m,info2,p2,rep2);
//--- check
if(info<=0 || info2<=0)
fiterrors=true;
else
{
//--- calculate P1 (interpolant),compare with P2 error
//--- compare RMS errors
t=2*CMath::RandomReal()-1;
v1=CRatInt::BarycentricCalc(p1,t);
v2=CRatInt::BarycentricCalc(p2,t);
//--- search errors
fiterrors=fiterrors || v2!=v1;
fiterrors=fiterrors || rep.m_rmserror!=rep2.m_rmserror;
fiterrors=fiterrors || rep.m_avgerror!=rep2.m_avgerror;
fiterrors=fiterrors || rep.m_avgrelerror!=rep2.m_avgrelerror;
fiterrors=fiterrors || rep.m_maxerror!=rep2.m_maxerror;
}
}
}
//--- calculation
for(m=1;m<=maxn;m++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- check
if(!CAp::Assert(passcount>=2,"PassCount should be 2 or greater!"))
return;
//--- solve simple task (all X[] are the same,Y[] are specially
//--- calculated to ensure simple form of all types of errors)
//--- and check correctness of the errors calculated by subroutines
//--- First pass is done with zero Y[],other passes - with random Y[].
//--- It should test both ability to correctly calculate errors and
//--- ability to not fail while working with zeros :)
n=4*maxn;
//--- check
if(pass==1)
{
v1=0;
v2=0;
v=0;
}
else
{
v1=CMath::RandomReal();
v2=CMath::RandomReal();
v=1+CMath::RandomReal();
}
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(w,n);
//--- change values
for(i=0;i<=maxn-1;i++)
{
x[4*i+0]=i;
y[4*i+0]=v-v2;
w[4*i+0]=1;
x[4*i+1]=i;
y[4*i+1]=v-v1;
w[4*i+1]=1;
x[4*i+2]=i;
y[4*i+2]=v+v1;
w[4*i+2]=1;
x[4*i+3]=i;
y[4*i+3]=v+v2;
w[4*i+3]=1;
}
//--- change values
refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2);
refavg=(MathAbs(v1)+MathAbs(v2))/2;
//--- check
if(pass==1)
refavgrel=0;
else
refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2));
refmax=MathMax(v1,v2);
//--- Test errors correctness
CLSFit::PolynomialFit(x,y,n,m,info,p,rep);
//--- check
if(info<=0)
fiterrors=true;
else
{
s=CRatInt::BarycentricCalc(p,0);
//--- search errors
fiterrors=fiterrors || MathAbs(s-v)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_rmserror-refrms)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_avgerror-refavg)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_maxerror-refmax)>threshold;
}
}
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestLSFitUnit::TestRationalFitting(bool &fiterrors)
{
//--- create variables
double threshold=0;
int maxn=0;
int passcount=0;
int n=0;
int m=0;
int i=0;
int k=0;
int pass=0;
double t=0;
double s=0;
double v=0;
double v0=0;
double v1=0;
double v2=0;
int info=0;
int info2=0;
double xmin=0;
double xmax=0;
double refrms=0;
double refavg=0;
double refavgrel=0;
double refmax=0;
//--- create arrays
double x[];
double x2[];
double y[];
double y2[];
double w[];
double w2[];
double xc[];
double yc[];
int dc[];
//--- objects of classes
CBarycentricInterpolant b1;
CBarycentricInterpolant b2;
CBarycentricFitReport rep;
CBarycentricFitReport rep2;
//--- initialization
fiterrors=false;
//--- PassCount number of repeated passes
//--- Threshold error tolerance
//--- LipschitzTol Lipschitz constant increase allowed
//--- when calculating constant on a twice denser grid
passcount=5;
maxn=15;
threshold=1000000*CMath::m_machineepsilon;
//--- Test rational fitting:
for(pass=1;pass<=passcount;pass++)
{
for(n=2;n<=maxn;n++)
{
//--- N=M+K fitting (i.e. interpolation)
for(k=0;k<=n-1;k++)
{
//--- allocation
ArrayResize(x,n-k);
ArrayResize(y,n-k);
ArrayResize(w,n-k);
//--- check
if(k>0)
{
//--- allocation
ArrayResize(xc,k);
ArrayResize(yc,k);
ArrayResize(dc,k);
}
//--- change values
for(i=0;i<=n-k-1;i++)
{
x[i]=(double)i/(double)(n-1);
y[i]=2*CMath::RandomReal()-1;
w[i]=1+CMath::RandomReal();
}
for(i=0;i<=k-1;i++)
{
xc[i]=(double)(n-k+i)/(double)(n-1);
yc[i]=2*CMath::RandomReal()-1;
dc[i]=0;
}
//--- function call
CLSFit::BarycentricFitFloaterHormannWC(x,y,w,n-k,xc,yc,dc,k,n,info,b1,rep);
//--- check
if(info<=0)
fiterrors=true;
else
{
//--- search errors
for(i=0;i<=n-k-1;i++)
fiterrors=fiterrors || MathAbs(CRatInt::BarycentricCalc(b1,x[i])-y[i])>threshold;
for(i=0;i<=k-1;i++)
fiterrors=fiterrors || MathAbs(CRatInt::BarycentricCalc(b1,xc[i])-yc[i])>threshold;
}
}
//--- Testing constraints on derivatives:
//--- * several M's are tried
//--- * several K's are tried - 1,2.
//--- * constraints at the ends of the interval
for(m=3;m<=5;m++)
{
for(k=1;k<=2;k++)
{
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(w,n);
ArrayResize(xc,2);
ArrayResize(yc,2);
ArrayResize(dc,2);
for(i=0;i<=n-1;i++)
{
x[i]=2*CMath::RandomReal()-1;
y[i]=2*CMath::RandomReal()-1;
w[i]=1+CMath::RandomReal();
}
//--- change values
xc[0]=-1;
yc[0]=2*CMath::RandomReal()-1;
dc[0]=0;
xc[1]=1;
yc[1]=2*CMath::RandomReal()-1;
dc[1]=0;
//--- function call
CLSFit::BarycentricFitFloaterHormannWC(x,y,w,n,xc,yc,dc,k,m,info,b1,rep);
//--- check
if(info<=0)
fiterrors=true;
else
{
for(i=0;i<=k-1;i++)
{
CRatInt::BarycentricDiff1(b1,xc[i],v0,v1);
//--- search errors
fiterrors=fiterrors || MathAbs(v0-yc[i])>threshold;
}
}
}
}
}
}
//--- calculation
for(m=2;m<=8;m++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- General fitting
//--- interpolating function through M nodes should have
//--- greater RMS error than fitting it through the same M nodes
n=100;
ArrayResize(x2,n);
ArrayResize(y2,n);
ArrayResize(w2,n);
//--- change values
xmin=CMath::m_maxrealnumber;
xmax=-CMath::m_maxrealnumber;
for(i=0;i<=n-1;i++)
{
x2[i]=2*M_PI*CMath::RandomReal();
y2[i]=MathSin(x2[i]);
w2[i]=1;
xmin=MathMin(xmin,x2[i]);
xmax=MathMax(xmax,x2[i]);
}
//--- allocation
ArrayResize(x,m);
ArrayResize(y,m);
for(i=0;i<=m-1;i++)
{
x[i]=xmin+(xmax-xmin)*i/(m-1);
y[i]=MathSin(x[i]);
}
//--- function call
CRatInt::BarycentricBuildFloaterHormann(x,y,m,3,b1);
CLSFit::BarycentricFitFloaterHormannWC(x2,y2,w2,n,xc,yc,dc,0,m,info,b2,rep);
//--- check
if(info<=0)
fiterrors=true;
else
{
//--- calculate B1 (interpolant) RMS error,compare with B2 error
v1=0;
v2=0;
for(i=0;i<=n-1;i++)
{
v1=v1+CMath::Sqr(CRatInt::BarycentricCalc(b1,x2[i])-y2[i]);
v2=v2+CMath::Sqr(CRatInt::BarycentricCalc(b2,x2[i])-y2[i]);
}
v1=MathSqrt(v1/n);
v2=MathSqrt(v2/n);
//--- search errors
fiterrors=fiterrors || v2>v1;
fiterrors=fiterrors || MathAbs(v2-rep.m_rmserror)>threshold;
}
//--- compare weighted and non-weighted
n=20;
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(w,n);
//--- change values
for(i=0;i<=n-1;i++)
{
x[i]=2*CMath::RandomReal()-1;
y[i]=2*CMath::RandomReal()-1;
w[i]=1;
}
//--- function calls
CLSFit::BarycentricFitFloaterHormannWC(x,y,w,n,xc,yc,dc,0,m,info,b1,rep);
CLSFit::BarycentricFitFloaterHormann(x,y,n,m,info2,b2,rep2);
//--- check
if(info<=0 || info2<=0)
fiterrors=true;
else
{
//--- calculate B1 (interpolant),compare with B2
//--- compare RMS errors
t=2*CMath::RandomReal()-1;
v1=CRatInt::BarycentricCalc(b1,t);
v2=CRatInt::BarycentricCalc(b2,t);
//--- search errors
fiterrors=fiterrors || v2!=v1;
fiterrors=fiterrors || rep.m_rmserror!=rep2.m_rmserror;
fiterrors=fiterrors || rep.m_avgerror!=rep2.m_avgerror;
fiterrors=fiterrors || rep.m_avgrelerror!=rep2.m_avgrelerror;
fiterrors=fiterrors || rep.m_maxerror!=rep2.m_maxerror;
}
}
}
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- check
if(!CAp::Assert(passcount>=2,"PassCount should be 2 or greater!"))
return;
//--- solve simple task (all X[] are the same,Y[] are specially
//--- calculated to ensure simple form of all types of errors)
//--- and check correctness of the errors calculated by subroutines
//--- First pass is done with zero Y[],other passes - with random Y[].
//--- It should test both ability to correctly calculate errors and
//--- ability to not fail while working with zeros :)
n=4;
if(pass==1)
{
v1=0;
v2=0;
v=0;
}
else
{
v1=CMath::RandomReal();
v2=CMath::RandomReal();
v=1+CMath::RandomReal();
}
//--- allocation
ArrayResize(x,4);
ArrayResize(y,4);
ArrayResize(w,4);
//--- change values
x[0]=0;
y[0]=v-v2;
w[0]=1;
x[1]=0;
y[1]=v-v1;
w[1]=1;
x[2]=0;
y[2]=v+v1;
w[2]=1;
x[3]=0;
y[3]=v+v2;
w[3]=1;
refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2);
refavg=(MathAbs(v1)+MathAbs(v2))/2;
//--- check
if(pass==1)
refavgrel=0;
else
refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2));
refmax=MathMax(v1,v2);
//--- Test errors correctness
CLSFit::BarycentricFitFloaterHormann(x,y,4,2,info,b1,rep);
//--- check
if(info<=0)
fiterrors=true;
else
{
s=CRatInt::BarycentricCalc(b1,0);
//--- search errors
fiterrors=fiterrors || MathAbs(s-v)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_rmserror-refrms)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_avgerror-refavg)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_maxerror-refmax)>threshold;
}
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestLSFitUnit::TestSplineFitting(bool &fiterrors)
{
//--- create variables
double threshold=0;
double nonstrictthreshold=0;
int passcount=0;
int n=0;
int m=0;
int i=0;
int k=0;
int pass=0;
double sa=0;
double sb=0;
int info=0;
int info1=0;
int info2=0;
double s=0;
double ds=0;
double d2s=0;
int stype=0;
double t=0;
double v=0;
double v1=0;
double v2=0;
double refrms=0;
double refavg=0;
double refavgrel=0;
double refmax=0;
double rho=0;
//--- create arrays
double x[];
double y[];
double w[];
double w2[];
double xc[];
double yc[];
double d[];
int dc[];
//--- objects of classes
CSpline1DInterpolant c;
CSpline1DInterpolant c2;
CSpline1DFitReport rep;
CSpline1DFitReport rep2;
//--- Valyes:
//--- * pass count
//--- * threshold - for tests which must be satisfied exactly
//--- * nonstrictthreshold - for approximate tests
passcount=20;
threshold=10000*CMath::m_machineepsilon;
nonstrictthreshold=1.0E-4;
fiterrors=false;
//--- Test fitting by Cubic and Hermite splines (obsolete,but still supported)
for(pass=1;pass<=passcount;pass++)
{
//--- Cubic splines
//--- Ability to handle boundary constraints (1-4 constraints on F,dF/dx).
for(m=4;m<=8;m++)
{
for(k=1;k<=4;k++)
{
//--- check
if(k>=m)
continue;
n=100;
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(w,n);
ArrayResize(xc,4);
ArrayResize(yc,4);
ArrayResize(dc,4);
//--- change values
sa=1+CMath::RandomReal();
sb=2*CMath::RandomReal()-1;
for(i=0;i<=n-1;i++)
{
x[i]=sa*CMath::RandomReal()+sb;
y[i]=2*CMath::RandomReal()-1;
w[i]=1+CMath::RandomReal();
}
xc[0]=sb;
yc[0]=2*CMath::RandomReal()-1;
dc[0]=0;
xc[1]=sb;
yc[1]=2*CMath::RandomReal()-1;
dc[1]=1;
xc[2]=sa+sb;
yc[2]=2*CMath::RandomReal()-1;
dc[2]=0;
xc[3]=sa+sb;
yc[3]=2*CMath::RandomReal()-1;
dc[3]=1;
//--- function call
CLSFit::Spline1DFitCubicWC(x,y,w,n,xc,yc,dc,k,m,info,c,rep);
//--- check
if(info<=0)
fiterrors=true;
else
{
//--- Check that constraints are satisfied
for(i=0;i<=k-1;i++)
{
CSpline1D::Spline1DDiff(c,xc[i],s,ds,d2s);
//--- check
if(dc[i]==0)
fiterrors=fiterrors || MathAbs(s-yc[i])>threshold;
//--- check
if(dc[i]==1)
fiterrors=fiterrors || MathAbs(ds-yc[i])>threshold;
//--- check
if(dc[i]==2)
fiterrors=fiterrors || MathAbs(d2s-yc[i])>threshold;
}
}
}
}
//--- Cubic splines
//--- Ability to handle one internal constraint
for(m=4;m<=8;m++)
{
n=100;
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(w,n);
ArrayResize(xc,1);
ArrayResize(yc,1);
ArrayResize(dc,1);
//--- change values
sa=1+CMath::RandomReal();
sb=2*CMath::RandomReal()-1;
for(i=0;i<=n-1;i++)
{
x[i]=sa*CMath::RandomReal()+sb;
y[i]=2*CMath::RandomReal()-1;
w[i]=1+CMath::RandomReal();
}
xc[0]=sa*CMath::RandomReal()+sb;
yc[0]=2*CMath::RandomReal()-1;
dc[0]=CMath::RandomInteger(2);
//--- function call
CLSFit::Spline1DFitCubicWC(x,y,w,n,xc,yc,dc,1,m,info,c,rep);
//--- check
if(info<=0)
fiterrors=true;
else
{
//--- Check that constraints are satisfied
CSpline1D::Spline1DDiff(c,xc[0],s,ds,d2s);
//--- check
if(dc[0]==0)
fiterrors=fiterrors || MathAbs(s-yc[0])>threshold;
//--- check
if(dc[0]==1)
fiterrors=fiterrors || MathAbs(ds-yc[0])>threshold;
//--- check
if(dc[0]==2)
fiterrors=fiterrors || MathAbs(d2s-yc[0])>threshold;
}
}
//--- Hermite splines
//--- Ability to handle boundary constraints (1-4 constraints on F,dF/dx).
for(m=4;m<=8;m++)
{
for(k=1;k<=4;k++)
{
//--- check
if(k>=m)
continue;
//--- check
if(m%2!=0)
continue;
n=100;
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(w,n);
ArrayResize(xc,4);
ArrayResize(yc,4);
ArrayResize(dc,4);
//--- change values
sa=1+CMath::RandomReal();
sb=2*CMath::RandomReal()-1;
for(i=0;i<=n-1;i++)
{
x[i]=sa*CMath::RandomReal()+sb;
y[i]=2*CMath::RandomReal()-1;
w[i]=1+CMath::RandomReal();
}
xc[0]=sb;
yc[0]=2*CMath::RandomReal()-1;
dc[0]=0;
xc[1]=sb;
yc[1]=2*CMath::RandomReal()-1;
dc[1]=1;
xc[2]=sa+sb;
yc[2]=2*CMath::RandomReal()-1;
dc[2]=0;
xc[3]=sa+sb;
yc[3]=2*CMath::RandomReal()-1;
dc[3]=1;
//--- function call
CLSFit::Spline1DFitHermiteWC(x,y,w,n,xc,yc,dc,k,m,info,c,rep);
//--- check
if(info<=0)
fiterrors=true;
else
{
//--- Check that constraints are satisfied
for(i=0;i<=k-1;i++)
{
CSpline1D::Spline1DDiff(c,xc[i],s,ds,d2s);
//--- check
if(dc[i]==0)
fiterrors=fiterrors || MathAbs(s-yc[i])>threshold;
//--- check
if(dc[i]==1)
fiterrors=fiterrors || MathAbs(ds-yc[i])>threshold;
//--- check
if(dc[i]==2)
fiterrors=fiterrors || MathAbs(d2s-yc[i])>threshold;
}
}
}
}
//--- Hermite splines
//--- Ability to handle one internal constraint
for(m=4;m<=8;m++)
{
//--- check
if(m%2!=0)
continue;
n=100;
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(w,n);
ArrayResize(xc,1);
ArrayResize(yc,1);
ArrayResize(dc,1);
//--- change values
sa=1+CMath::RandomReal();
sb=2*CMath::RandomReal()-1;
for(i=0;i<=n-1;i++)
{
x[i]=sa*CMath::RandomReal()+sb;
y[i]=2*CMath::RandomReal()-1;
w[i]=1+CMath::RandomReal();
}
xc[0]=sa*CMath::RandomReal()+sb;
yc[0]=2*CMath::RandomReal()-1;
dc[0]=CMath::RandomInteger(2);
//--- function call
CLSFit::Spline1DFitHermiteWC(x,y,w,n,xc,yc,dc,1,m,info,c,rep);
//--- check
if(info<=0)
fiterrors=true;
else
{
//--- Check that constraints are satisfied
CSpline1D::Spline1DDiff(c,xc[0],s,ds,d2s);
//--- check
if(dc[0]==0)
fiterrors=fiterrors || MathAbs(s-yc[0])>threshold;
//--- check
if(dc[0]==1)
fiterrors=fiterrors || MathAbs(ds-yc[0])>threshold;
//--- check
if(dc[0]==2)
fiterrors=fiterrors || MathAbs(d2s-yc[0])>threshold;
}
}
}
//--- calculation
for(m=4;m<=8;m++)
{
for(stype=0;stype<=1;stype++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- check
if(stype==1 && m%2!=0)
continue;
//--- cubic/Hermite spline fitting:
//--- * generate "template spline" C2
//--- * generate 2*N points from C2,such that result of
//--- ideal fit should be equal to C2
//--- * fit,store in C
//--- * compare C and C2
sa=1+CMath::RandomReal();
sb=2*CMath::RandomReal()-1;
//--- check
if(stype==0)
{
//--- allocation
ArrayResize(x,m-2);
ArrayResize(y,m-2);
//--- change values
for(i=0;i<=m-2-1;i++)
{
x[i]=sa*i/(m-2-1)+sb;
y[i]=2*CMath::RandomReal()-1;
}
//--- function call
CSpline1D::Spline1DBuildCubic(x,y,m-2,1,2*CMath::RandomReal()-1,1,2*CMath::RandomReal()-1,c2);
}
//--- check
if(stype==1)
{
//--- allocation
ArrayResize(x,m/2);
ArrayResize(y,m/2);
ArrayResize(d,m/2);
//--- change values
for(i=0;i<=m/2-1;i++)
{
x[i]=sa*i/(m/2-1)+sb;
y[i]=2*CMath::RandomReal()-1;
d[i]=2*CMath::RandomReal()-1;
}
//--- function call
CSpline1D::Spline1DBuildHermite(x,y,d,m/2,c2);
}
n=50;
//--- allocation
ArrayResize(x,2*n);
ArrayResize(y,2*n);
ArrayResize(w,2*n);
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- "if i=0" and "if i=1" are needed to
//--- synchronize interval size for C2 and
//--- spline being fitted (i.e. C).
t=CMath::RandomReal();
x[i]=sa*CMath::RandomReal()+sb;
//--- check
if(i==0)
x[i]=sb;
//--- check
if(i==1)
x[i]=sa+sb;
//--- change values
v=CSpline1D::Spline1DCalc(c2,x[i]);
y[i]=v+t;
w[i]=1+CMath::RandomReal();
x[n+i]=x[i];
y[n+i]=v-t;
w[n+i]=w[i];
}
//--- check
if(stype==0)
CLSFit::Spline1DFitCubicWC(x,y,w,2*n,xc,yc,dc,0,m,info,c,rep);
//--- check
if(stype==1)
CLSFit::Spline1DFitHermiteWC(x,y,w,2*n,xc,yc,dc,0,m,info,c,rep);
//--- check
if(info<=0)
fiterrors=true;
else
{
for(i=0;i<=n-1;i++)
{
v=sa*CMath::RandomReal()+sb;
//--- search errors
fiterrors=fiterrors || MathAbs(CSpline1D::Spline1DCalc(c,v)-CSpline1D::Spline1DCalc(c2,v))>threshold;
}
}
}
}
}
//--- calculation
for(m=4;m<=8;m++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- prepare points/weights
sa=1+CMath::RandomReal();
sb=2*CMath::RandomReal()-1;
n=10+CMath::RandomInteger(10);
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(w,n);
for(i=0;i<=n-1;i++)
{
x[i]=sa*CMath::RandomReal()+sb;
y[i]=2*CMath::RandomReal()-1;
w[i]=1;
}
//--- Fit cubic with unity weights,without weights,then compare
if(m>=4)
{
//--- function calls
CLSFit::Spline1DFitCubicWC(x,y,w,n,xc,yc,dc,0,m,info1,c,rep);
CLSFit::Spline1DFitCubic(x,y,n,m,info2,c2,rep2);
//--- check
if(info1<=0 || info2<=0)
fiterrors=true;
else
{
for(i=0;i<=n-1;i++)
{
v=sa*CMath::RandomReal()+sb;
//--- search errors
fiterrors=fiterrors || CSpline1D::Spline1DCalc(c,v)!=CSpline1D::Spline1DCalc(c2,v);
fiterrors=fiterrors || rep.m_taskrcond!=rep2.m_taskrcond;
fiterrors=fiterrors || rep.m_rmserror!=rep2.m_rmserror;
fiterrors=fiterrors || rep.m_avgerror!=rep2.m_avgerror;
fiterrors=fiterrors || rep.m_avgrelerror!=rep2.m_avgrelerror;
fiterrors=fiterrors || rep.m_maxerror!=rep2.m_maxerror;
}
}
}
//--- Fit Hermite with unity weights,without weights,then compare
if(m>=4 && m%2==0)
{
CLSFit::Spline1DFitHermiteWC(x,y,w,n,xc,yc,dc,0,m,info1,c,rep);
CLSFit::Spline1DFitHermite(x,y,n,m,info2,c2,rep2);
//--- check
if(info1<=0 || info2<=0)
fiterrors=true;
else
{
for(i=0;i<=n-1;i++)
{
v=sa*CMath::RandomReal()+sb;
//--- search errors
fiterrors=fiterrors || CSpline1D::Spline1DCalc(c,v)!=CSpline1D::Spline1DCalc(c2,v);
fiterrors=fiterrors || rep.m_taskrcond!=rep2.m_taskrcond;
fiterrors=fiterrors || rep.m_rmserror!=rep2.m_rmserror;
fiterrors=fiterrors || rep.m_avgerror!=rep2.m_avgerror;
fiterrors=fiterrors || rep.m_avgrelerror!=rep2.m_avgrelerror;
fiterrors=fiterrors || rep.m_maxerror!=rep2.m_maxerror;
}
}
}
}
}
//--- check basic properties of penalized splines which are
//--- preserved independently of Rho parameter.
for(m=4;m<=10;m++)
{
for(k=-5;k<=5;k++)
{
rho=k;
//--- when we have two points (even with different weights),
//--- resulting spline must be equal to the straight line
ArrayResize(x,2);
ArrayResize(y,2);
ArrayResize(w,2);
x[0]=-0.5-CMath::RandomReal();
y[0]=0.5+CMath::RandomReal();
w[0]=1+CMath::RandomReal();
x[1]=0.5+CMath::RandomReal();
y[1]=0.5+CMath::RandomReal();
w[1]=1+CMath::RandomReal();
//--- function call
CLSFit::Spline1DFitPenalized(x,y,2,m,rho,info,c,rep);
//--- check
if(info>0)
{
v=2*CMath::RandomReal()-1;
v1=(v-x[0])/(x[1]-x[0])*y[1]+(v-x[1])/(x[0]-x[1])*y[0];
//--- search errors
fiterrors=fiterrors || MathAbs(v1-CSpline1D::Spline1DCalc(c,v))>nonstrictthreshold;
}
else
fiterrors=true;
//--- function call
CLSFit::Spline1DFitPenalizedW(x,y,w,2,m,rho,info,c,rep);
//--- check
if(info>0)
{
v=2*CMath::RandomReal()-1;
v1=(v-x[0])/(x[1]-x[0])*y[1]+(v-x[1])/(x[0]-x[1])*y[0];
//--- search errors
fiterrors=fiterrors || MathAbs(v1-CSpline1D::Spline1DCalc(c,v))>nonstrictthreshold;
}
else
fiterrors=true;
//--- spline fitting is invariant with respect to
//--- scaling of weights (of course,ANY fitting algorithm
//--- must be invariant,but we want to test this property
//--- just to be sure that it is correctly implemented)
for(n=2;n<=2*m;n++)
{
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(w,n);
ArrayResize(w2,n);
//--- change values
s=1+MathExp(10*CMath::RandomReal());
for(i=0;i<=n-1;i++)
{
x[i]=(double)i/(double)(n-1);
y[i]=CMath::RandomReal();
w[i]=0.1+CMath::RandomReal();
w2[i]=w[i]*s;
}
//--- function calls
CLSFit::Spline1DFitPenalizedW(x,y,w,n,m,rho,info,c,rep);
CLSFit::Spline1DFitPenalizedW(x,y,w2,n,m,rho,info2,c2,rep2);
//--- check
if(info>0 && info2>0)
{
v=CMath::RandomReal();
v1=CSpline1D::Spline1DCalc(c,v);
v2=CSpline1D::Spline1DCalc(c2,v);
//--- search errors
fiterrors=fiterrors || MathAbs(v1-v2)>nonstrictthreshold;
}
else
fiterrors=true;
}
}
}
//--- Advanced proprties:
//--- * penalized spline with M about 5*N and sufficiently small Rho
//--- must pass through all points on equidistant grid
for(n=2;n<=10;n++)
{
m=5*n;
rho=-5;
//--- allocation
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(w,n);
for(i=0;i<=n-1;i++)
{
x[i]=(double)i/(double)(n-1);
y[i]=CMath::RandomReal();
w[i]=0.1+CMath::RandomReal();
}
//--- function call
CLSFit::Spline1DFitPenalized(x,y,n,m,rho,info,c,rep);
//--- check
if(info>0)
{
//--- search errors
for(i=0;i<=n-1;i++)
fiterrors=fiterrors || MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i]))>nonstrictthreshold;
}
else
fiterrors=true;
//--- function call
CLSFit::Spline1DFitPenalizedW(x,y,w,n,m,rho,info,c,rep);
//--- check
if(info>0)
{
//--- search errors
for(i=0;i<=n-1;i++)
fiterrors=fiterrors || MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i]))>nonstrictthreshold;
}
else
fiterrors=true;
}
//--- Check correctness of error reports
for(pass=1;pass<=passcount;pass++)
{
//--- check
if(!CAp::Assert(passcount>=2,"PassCount should be 2 or greater!"))
return;
//--- solve simple task (all X[] are the same,Y[] are specially
//--- calculated to ensure simple form of all types of errors)
//--- and check correctness of the errors calculated by subroutines
//--- First pass is done with zero Y[],other passes - with random Y[].
//--- It should test both ability to correctly calculate errors and
//--- ability to not fail while working with zeros :)
n=4;
if(pass==1)
{
v1=0;
v2=0;
v=0;
}
else
{
v1=CMath::RandomReal();
v2=CMath::RandomReal();
v=1+CMath::RandomReal();
}
//--- allocation
ArrayResize(x,4);
ArrayResize(y,4);
ArrayResize(w,4);
//--- change values
x[0]=0;
y[0]=v-v2;
w[0]=1;
x[1]=0;
y[1]=v-v1;
w[1]=1;
x[2]=0;
y[2]=v+v1;
w[2]=1;
x[3]=0;
y[3]=v+v2;
w[3]=1;
refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2);
refavg=(MathAbs(v1)+MathAbs(v2))/2;
//--- check
if(pass==1)
refavgrel=0;
else
refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2));
refmax=MathMax(v1,v2);
//--- Test penalized spline
CLSFit::Spline1DFitPenalizedW(x,y,w,4,4,0.0,info,c,rep);
//--- check
if(info<=0)
fiterrors=true;
else
{
s=CSpline1D::Spline1DCalc(c,0);
//--- search errors
fiterrors=fiterrors || MathAbs(s-v)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_rmserror-refrms)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_avgerror-refavg)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_maxerror-refmax)>threshold;
}
//--- Test cubic fitting
CLSFit::Spline1DFitCubic(x,y,4,4,info,c,rep);
//--- check
if(info<=0)
fiterrors=true;
else
{
s=CSpline1D::Spline1DCalc(c,0);
//--- search errors
fiterrors=fiterrors || MathAbs(s-v)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_rmserror-refrms)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_avgerror-refavg)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_maxerror-refmax)>threshold;
}
//--- Test Hermite fitting
CLSFit::Spline1DFitHermite(x,y,4,4,info,c,rep);
//--- check
if(info<=0)
fiterrors=true;
else
{
s=CSpline1D::Spline1DCalc(c,0);
//--- search errors
fiterrors=fiterrors || MathAbs(s-v)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_rmserror-refrms)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_avgerror-refavg)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
fiterrors=fiterrors || MathAbs(rep.m_maxerror-refmax)>threshold;
}
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestLSFitUnit::TestGeneralFitting(bool &llserrors,bool &nlserrors)
{
//--- create variables
double threshold=0;
double nlthreshold=0;
int maxn=0;
int maxm=0;
int passcount=0;
int n=0;
int m=0;
int i=0;
int j=0;
int k=0;
int pass=0;
double xscale=0;
double diffstep=0;
double v=0;
double v1=0;
double v2=0;
int info=0;
int info2=0;
double refrms=0;
double refavg=0;
double refavgrel=0;
double refmax=0;
//--- create arrays
double x[];
double y[];
double w[];
double w2[];
double c[];
double c2[];
//--- create matrix
CMatrixDouble a;
CMatrixDouble a2;
CMatrixDouble cm;
//--- objects of classes
CLSFitReport rep;
CLSFitReport rep2;
CLSFitState state;
//--- initialization
llserrors=false;
nlserrors=false;
threshold=10000*CMath::m_machineepsilon;
nlthreshold=0.00001;
diffstep=0.0001;
maxn=6;
maxm=6;
passcount=4;
//--- Testing unconstrained least squares (linear/nonlinear)
for(n=1;n<=maxn;n++)
{
for(m=1;m<=maxm;m++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- Solve non-degenerate linear least squares task
//--- Use Chebyshev basis. Its condition number is very good.
a.Resize(n,m);
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(w,n);
xscale=0.9+0.1*CMath::RandomReal();
for(i=0;i<=n-1;i++)
{
//--- check
if(n==1)
x[i]=2*CMath::RandomReal()-1;
else
x[i]=xscale*((double)(2*i)/(double)(n-1)-1);
//--- change values
y[i]=3*x[i]+MathExp(x[i]);
w[i]=1+CMath::RandomReal();
a[i].Set(0,1);
//--- check
if(m>1)
a[i].Set(1,x[i]);
for(j=2;j<=m-1;j++)
a[i].Set(j,2*x[i]*a[i][j-1]-a[i][j-2]);
}
//--- 1. test weighted fitting (optimality)
//--- 2. Solve degenerate least squares task built on the basis
//--- of previous task
CLSFit::LSFitLinearW(y,w,a,n,m,info,c,rep);
//--- check
if(info<=0)
llserrors=true;
else
llserrors=llserrors || !IsGLSSolution(n,m,0,y,w,a,cm,c);
//--- allocation
a2.Resize(n,2*m);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=m-1;j++)
{
a2[i].Set(2*j+0,a[i][j]);
a2[i].Set(2*j+1,a[i][j]);
}
}
//--- function call
CLSFit::LSFitLinearW(y,w,a2,n,2*m,info,c2,rep);
//--- check
if(info<=0)
llserrors=true;
else
{
//--- test answer correctness using design matrix properties
//--- and previous task solution
for(j=0;j<=m-1;j++)
llserrors=llserrors || MathAbs(c2[2*j+0]+c2[2*j+1]-c[j])>threshold;
}
//--- test non-weighted fitting
ArrayResize(w2,n);
for(i=0;i<=n-1;i++)
w2[i]=1;
//--- function calls
CLSFit::LSFitLinearW(y,w2,a,n,m,info,c,rep);
CLSFit::LSFitLinear(y,a,n,m,info2,c2,rep2);
//--- check
if(info<=0 || info2<=0)
llserrors=true;
else
{
//--- test answer correctness
for(j=0;j<=m-1;j++)
llserrors=llserrors || MathAbs(c[j]-c2[j])>threshold;
//--- search errors
llserrors=llserrors || MathAbs(rep.m_taskrcond-rep2.m_taskrcond)>threshold;
}
//--- test nonlinear fitting on the linear task
//--- (only non-degenerate tasks are tested)
//--- and compare with answer from linear fitting subroutine
if(n>=m)
{
//--- allocation
ArrayResize(c2,m);
//--- test function/gradient/Hessian-based weighted fitting
CLSFit::LSFitLinearW(y,w,a,n,m,info,c,rep);
for(i=0;i<=m-1;i++)
c2[i]=2*CMath::RandomReal()-1;
//--- function calls
CLSFit::LSFitCreateWF(a,y,w,c2,n,m,m,diffstep,state);
CLSFit::LSFitSetCond(state,0.0,nlthreshold,0);
FitLinearNonlinear(m,0,a,state,nlserrors);
CLSFit::LSFitResults(state,info,c2,rep2);
//--- check
if(info<=0)
nlserrors=true;
else
{
//--- search errors
for(i=0;i<=m-1;i++)
nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold;
}
//--- change values
for(i=0;i<=m-1;i++)
c2[i]=2*CMath::RandomReal()-1;
//--- function calls
CLSFit::LSFitCreateWFG(a,y,w,c2,n,m,m,CMath::RandomReal()>0.5,state);
CLSFit::LSFitSetCond(state,0.0,nlthreshold,0);
FitLinearNonlinear(m,1,a,state,nlserrors);
CLSFit::LSFitResults(state,info,c2,rep2);
//--- check
if(info<=0)
nlserrors=true;
else
{
//--- search errors
for(i=0;i<=m-1;i++)
nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold;
}
//--- change values
for(i=0;i<=m-1;i++)
c2[i]=2*CMath::RandomReal()-1;
//--- function calls
CLSFit::LSFitCreateWFGH(a,y,w,c2,n,m,m,state);
CLSFit::LSFitSetCond(state,0.0,nlthreshold,0);
FitLinearNonlinear(m,2,a,state,nlserrors);
CLSFit::LSFitResults(state,info,c2,rep2);
//--- check
if(info<=0)
nlserrors=true;
else
{
//--- search errors
for(i=0;i<=m-1;i++)
nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold;
}
//--- test gradient-only or Hessian-based fitting without weights
CLSFit::LSFitLinear(y,a,n,m,info,c,rep);
for(i=0;i<=m-1;i++)
c2[i]=2*CMath::RandomReal()-1;
//--- function calls
CLSFit::LSFitCreateF(a,y,c2,n,m,m,diffstep,state);
CLSFit::LSFitSetCond(state,0.0,nlthreshold,0);
FitLinearNonlinear(m,0,a,state,nlserrors);
CLSFit::LSFitResults(state,info,c2,rep2);
//--- check
if(info<=0)
nlserrors=true;
else
{
//--- search errors
for(i=0;i<=m-1;i++)
nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold;
}
//--- change values
for(i=0;i<=m-1;i++)
c2[i]=2*CMath::RandomReal()-1;
//--- function calls
CLSFit::LSFitCreateFG(a,y,c2,n,m,m,CMath::RandomReal()>0.5,state);
CLSFit::LSFitSetCond(state,0.0,nlthreshold,0);
FitLinearNonlinear(m,1,a,state,nlserrors);
CLSFit::LSFitResults(state,info,c2,rep2);
//--- check
if(info<=0)
nlserrors=true;
else
{
//--- search errors
for(i=0;i<=m-1;i++)
nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold;
}
//--- change values
for(i=0;i<=m-1;i++)
c2[i]=2*CMath::RandomReal()-1;
//--- function calls
CLSFit::LSFitCreateFGH(a,y,c2,n,m,m,state);
CLSFit::LSFitSetCond(state,0.0,nlthreshold,0);
FitLinearNonlinear(m,2,a,state,nlserrors);
CLSFit::LSFitResults(state,info,c2,rep2);
//--- check
if(info<=0)
nlserrors=true;
else
{
//--- search errors
for(i=0;i<=m-1;i++)
nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold;
}
}
}
}
//--- test correctness of the RCond field
a.Resize(n,n);
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(w,n);
//--- change values
v1=CMath::m_maxrealnumber;
v2=CMath::m_minrealnumber;
//--- calculation
for(i=0;i<=n-1;i++)
{
x[i]=0.1+0.9*CMath::RandomReal();
y[i]=0.1+0.9*CMath::RandomReal();
w[i]=1;
for(j=0;j<=n-1;j++)
{
//--- check
if(i==j)
{
a[i].Set(i,0.1+0.9*CMath::RandomReal());
v1=MathMin(v1,a[i][i]);
v2=MathMax(v2,a[i][i]);
}
else
a[i].Set(j,0);
}
}
//--- function call
CLSFit::LSFitLinearW(y,w,a,n,n,info,c,rep);
//--- check
if(info<=0)
llserrors=true;
else
llserrors=llserrors || MathAbs(rep.m_taskrcond-v1/v2)>threshold;
}
//--- Test constrained least squares
for(pass=1;pass<=passcount;pass++)
{
for(n=1;n<=maxn;n++)
{
for(m=1;m<=maxm;m++)
{
//--- test for K<>0
for(k=1;k<=m-1;k++)
{
//--- Prepare Chebyshev basis. Its condition number is very good.
//--- Prepare constraints (random numbers)
a.Resize(n,m);
ArrayResize(x,n);
ArrayResize(y,n);
ArrayResize(w,n);
xscale=0.9+0.1*CMath::RandomReal();
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- check
if(n==1)
x[i]=2*CMath::RandomReal()-1;
else
x[i]=xscale*((double)(2*i)/(double)(n-1)-1);
//--- change values
y[i]=3*x[i]+MathExp(x[i]);
w[i]=1+CMath::RandomReal();
a[i].Set(0,1);
//--- check
if(m>1)
a[i].Set(1,x[i]);
for(j=2;j<=m-1;j++)
a[i].Set(j,2*x[i]*a[i][j-1]-a[i][j-2]);
}
//--- allocation
cm.Resize(k,m+1);
for(i=0;i<=k-1;i++)
{
for(j=0;j<=m;j++)
cm[i].Set(j,2*CMath::RandomReal()-1);
}
//--- Solve constrained task
CLSFit::LSFitLinearWC(y,w,a,cm,n,m,k,info,c,rep);
//--- check
if(info<=0)
llserrors=true;
else
llserrors=llserrors || !IsGLSSolution(n,m,k,y,w,a,cm,c);
//--- test non-weighted fitting
ArrayResize(w2,n);
for(i=0;i<=n-1;i++)
w2[i]=1;
//--- function calls
CLSFit::LSFitLinearWC(y,w2,a,cm,n,m,k,info,c,rep);
CLSFit::LSFitLinearC(y,a,cm,n,m,k,info2,c2,rep2);
//--- check
if(info<=0 || info2<=0)
llserrors=true;
else
{
//--- test answer correctness
for(j=0;j<=m-1;j++)
llserrors=llserrors || MathAbs(c[j]-c2[j])>threshold;
llserrors=llserrors || MathAbs(rep.m_taskrcond-rep2.m_taskrcond)>threshold;
}
}
}
}
}
//--- nonlinear task for nonlinear fitting:
//--- f(X,C)=1/(1+C*X^2),
//--- C(true)=2.
n=100;
ArrayResize(c,1);
c[0]=1+2*CMath::RandomReal();
//--- allocation
a.Resize(n,1);
ArrayResize(y,n);
for(i=0;i<=n-1;i++)
{
a[i].Set(0,4*CMath::RandomReal()-2);
y[i]=1/(1+2*CMath::Sqr(a[i][0]));
}
//--- function call
CLSFit::LSFitCreateFG(a,y,c,n,1,1,true,state);
CLSFit::LSFitSetCond(state,0.0,nlthreshold,0);
//--- cycle
while(CLSFit::LSFitIteration(state))
{
//--- check
if(state.m_needf)
state.m_f=1/(1+state.m_c[0]*CMath::Sqr(state.m_x[0]));
//--- check
if(state.m_needfg)
{
state.m_f=1/(1+state.m_c[0]*CMath::Sqr(state.m_x[0]));
state.m_g[0]=-(CMath::Sqr(state.m_x[0])/CMath::Sqr(1+state.m_c[0]*CMath::Sqr(state.m_x[0])));
}
}
//--- function call
CLSFit::LSFitResults(state,info,c,rep);
//--- check
if(info<=0)
nlserrors=true;
else
nlserrors=nlserrors || MathAbs(c[0]-2)>100*nlthreshold;
//--- solve simple task (fitting by constant function) and check
//--- correctness of the errors calculated by subroutines
for(pass=1;pass<=passcount;pass++)
{
//--- test on task with non-zero Yi
n=4;
v1=CMath::RandomReal();
v2=CMath::RandomReal();
v=1+CMath::RandomReal();
//--- allocation
ArrayResize(c,1);
c[0]=1+2*CMath::RandomReal();
//--- allocation
a.Resize(4,1);
ArrayResize(y,4);
//--- change values
a[0].Set(0,1);
y[0]=v-v2;
a[1].Set(0,1);
y[1]=v-v1;
a[2].Set(0,1);
y[2]=v+v1;
a[3].Set(0,1);
y[3]=v+v2;
refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2);
refavg=(MathAbs(v1)+MathAbs(v2))/2;
refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2));
refmax=MathMax(v1,v2);
//--- Test LLS
CLSFit::LSFitLinear(y,a,4,1,info,c,rep);
//--- check
if(info<=0)
llserrors=true;
else
{
//--- search errors
llserrors=llserrors || MathAbs(c[0]-v)>threshold;
llserrors=llserrors || MathAbs(rep.m_rmserror-refrms)>threshold;
llserrors=llserrors || MathAbs(rep.m_avgerror-refavg)>threshold;
llserrors=llserrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
llserrors=llserrors || MathAbs(rep.m_maxerror-refmax)>threshold;
}
//--- Test NLS
CLSFit::LSFitCreateFG(a,y,c,4,1,1,true,state);
CLSFit::LSFitSetCond(state,0.0,nlthreshold,0);
//--- cycle
while(CLSFit::LSFitIteration(state))
{
//--- check
if(state.m_needf)
state.m_f=state.m_c[0];
//--- check
if(state.m_needfg)
{
state.m_f=state.m_c[0];
state.m_g[0]=1;
}
}
//--- function call
CLSFit::LSFitResults(state,info,c,rep);
//--- check
if(info<=0)
nlserrors=true;
else
{
//--- search errors
nlserrors=nlserrors || MathAbs(c[0]-v)>threshold;
nlserrors=nlserrors || MathAbs(rep.m_rmserror-refrms)>threshold;
nlserrors=nlserrors || MathAbs(rep.m_avgerror-refavg)>threshold;
nlserrors=nlserrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
nlserrors=nlserrors || MathAbs(rep.m_maxerror-refmax)>threshold;
}
}
}
//+------------------------------------------------------------------+
//| Tests whether C is solution of (possibly) constrained LLS problem|
//+------------------------------------------------------------------+
static bool CTestLSFitUnit::IsGLSSolution(const int n,const int m,const int k,
double &y[],double &w[],CMatrixDouble &fmatrix,
CMatrixDouble &cmatrix,double &cc[])
{
//--- create variables
bool result;
int i=0;
int j=0;
double v=0;
double s1=0;
double s2=0;
double s3=0;
double delta=0;
double threshold=0;
int i_=0;
//--- create arrays
double c[];
double c2[];
double sv[];
double deltac[];
double deltaproj[];
//--- create matrix
CMatrixDouble u;
CMatrixDouble vt;
//--- copy array
ArrayCopy(c,cc);
//--- Setup.
//--- Threshold is small because CMatrix may be ill-conditioned
delta=0.001;
threshold=MathSqrt(CMath::m_machineepsilon);
//--- allocation
ArrayResize(c2,m);
ArrayResize(deltac,m);
ArrayResize(deltaproj,m);
//--- test whether C is feasible point or not (projC must be close to C)
for(i=0;i<=k-1;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=m-1;i_++)
v+=cmatrix[i][i_]*c[i_];
//--- check
if(MathAbs(v-cmatrix[i][m])>threshold)
{
//--- return result
return(false);
}
}
//--- find orthogonal basis of Null(CMatrix) (stored in rows from K to M-1)
if(k>0)
CSingValueDecompose::RMatrixSVD(cmatrix,k,m,0,2,2,sv,u,vt);
//--- Test result
result=true;
s1=GetGLSError(n,m,y,w,fmatrix,c);
//--- calculation
for(j=0;j<=m-1;j++)
{
//--- prepare modification of C which leave us in the feasible set.
//--- let deltaC be increment on Jth coordinate,then project
//--- deltaC in the Null(CMatrix) and store result in DeltaProj
for(i_=0;i_<=m-1;i_++)
c2[i_]=c[i_];
for(i=0;i<=m-1;i++)
{
//--- check
if(i==j)
deltac[i]=delta;
else
deltac[i]=0;
}
//--- check
if(k==0)
{
for(i_=0;i_<=m-1;i_++)
deltaproj[i_]=deltac[i_];
}
else
{
for(i=0;i<=m-1;i++)
deltaproj[i]=0;
for(i=k;i<=m-1;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=m-1;i_++)
v+=vt[i][i_]*deltac[i_];
for(i_=0;i_<=m-1;i_++)
deltaproj[i_]=deltaproj[i_]+v*vt[i][i_];
}
}
//--- now we have DeltaProj such that if C is feasible,
//--- then C+DeltaProj is feasible too
for(i_=0;i_<=m-1;i_++)
c2[i_]=c[i_];
for(i_=0;i_<=m-1;i_++)
c2[i_]=c2[i_]+deltaproj[i_];
s2=GetGLSError(n,m,y,w,fmatrix,c2);
for(i_=0;i_<=m-1;i_++)
c2[i_]=c[i_];
for(i_=0;i_<=m-1;i_++)
c2[i_]=c2[i_]-deltaproj[i_];
s3=GetGLSError(n,m,y,w,fmatrix,c2);
result=(result && s2>=(double)(s1/(1+threshold))) && s3>=(double)(s1/(1+threshold));
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Tests whether C is solution of LLS problem |
//+------------------------------------------------------------------+
static double CTestLSFitUnit::GetGLSError(const int n,const int m,double &y[],
double &w[],CMatrixDouble &fmatrix,double &c[])
{
//--- create variables
double result=0;
int i=0;
double v=0;
int i_=0;
//--- calculation
for(i=0;i<=n-1;i++)
{
//--- change value
v=0.0;
for(i_=0;i_<=m-1;i_++)
v+=fmatrix[i][i_]*c[i_];
result=result+CMath::Sqr(w[i]*(v-y[i]));
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Subroutine for nonlinear fitting of linear problem |
//| DerAvailable: |
//| * 0 when only function value should be used |
//| * 1 when we can provide gradient/function |
//| * 2 when we can provide Hessian/gradient/function |
//| When something which is not permitted by DerAvailable is |
//| requested, this function sets NLSErrors to True. |
//+------------------------------------------------------------------+
static void CTestLSFitUnit::FitLinearNonlinear(const int m,const int deravailable,
CMatrixDouble &xy,CLSFitState &state,
bool &nlserrors)
{
//--- create variables
int i=0;
int j=0;
double v=0;
int i_=0;
//--- cycle
while(CLSFit::LSFitIteration(state))
{
//--- assume that one and only one of flags is set
//--- test that we didn't request hessian in hessian-free setting
if(deravailable<1 && state.m_needfg)
nlserrors=true;
//--- check
if(deravailable<2 && state.m_needfgh)
nlserrors=true;
i=0;
//--- check
if(state.m_needf)
i=i+1;
//--- check
if(state.m_needfg)
i=i+1;
//--- check
if(state.m_needfgh)
i=i+1;
//--- check
if(i!=1)
nlserrors=true;
//--- test that PointIndex is consistent with actual point passed
for(i=0;i<=m-1;i++)
nlserrors=nlserrors || xy[state.m_pointindex][i]!=state.m_x[i];
//--- calculate
if(state.m_needf)
{
//--- change value
v=0.0;
for(i_=0;i_<=m-1;i_++)
v+=state.m_x[i_]*state.m_c[i_];
state.m_f=v;
continue;
}
//--- check
if(state.m_needfg)
{
//--- change value
v=0.0;
for(i_=0;i_<=m-1;i_++)
v+=state.m_x[i_]*state.m_c[i_];
state.m_f=v;
//--- copy
for(i_=0;i_<=m-1;i_++)
state.m_g[i_]=state.m_x[i_];
continue;
}
//--- check
if(state.m_needfgh)
{
//--- change value
v=0.0;
for(i_=0;i_<=m-1;i_++)
v+=state.m_x[i_]*state.m_c[i_];
state.m_f=v;
//--- copy
for(i_=0;i_<=m-1;i_++)
state.m_g[i_]=state.m_x[i_];
for(i=0;i<=m-1;i++)
{
for(j=0;j<=m-1;j++)
state.m_h[i].Set(j,0);
}
continue;
}
}
}
//+------------------------------------------------------------------+
//| Testing class CPSpline |
//+------------------------------------------------------------------+
class CTestPSplineUnit
{
private:
//--- private methods
static void UnsetP2(CPSpline2Interpolant &p);
static void UnsetP3(CPSpline3Interpolant &p);
static void Unset1D(double &x[]);
public:
//--- constructor, destructor
CTestPSplineUnit(void);
~CTestPSplineUnit(void);
//--- public method
static bool TestPSpline(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestPSplineUnit::CTestPSplineUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestPSplineUnit::~CTestPSplineUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CPSpline |
//+------------------------------------------------------------------+
static bool CTestPSplineUnit::TestPSpline(const bool silent)
{
//--- create variables
bool waserrors;
bool p2errors;
bool p3errors;
double nonstrictthreshold=0;
double threshold=0;
int passcount=0;
double lstep=0;
double h=0;
int maxn=0;
int periodicity=0;
int skind=0;
int pkind=0;
bool periodic;
double a=0;
double b=0;
int n=0;
int tmpn=0;
int i=0;
double vx=0;
double vy=0;
double vz=0;
double vx2=0;
double vy2=0;
double vz2=0;
double vdx=0;
double vdy=0;
double vdz=0;
double vdx2=0;
double vdy2=0;
double vdz2=0;
double vd2x=0;
double vd2y=0;
double vd2z=0;
double vd2x2=0;
double vd2y2=0;
double vd2z2=0;
double v0=0;
double v1=0;
int i_=0;
//--- create arrays
double x[];
double y[];
double z[];
double t[];
double t2[];
double t3[];
//--- create matrix
CMatrixDouble xy;
CMatrixDouble xyz;
//--- objects of classes
CPSpline2Interpolant p2;
CPSpline3Interpolant p3;
CSpline1DInterpolant s;
//--- initialization
waserrors=false;
passcount=20;
lstep=0.005;
h=0.00001;
maxn=10;
threshold=10000*CMath::m_machineepsilon;
nonstrictthreshold=0.00001;
p2errors=false;
p3errors=false;
//--- Test basic properties of 2- and 3-dimensional splines:
//--- * PSpline2ParameterValues() properties
//--- * values at nodes
//--- * for periodic splines - periodicity properties
//--- Variables used:
//--- * N points count
//--- * SKind spline
//--- * PKind parameterization
//--- * Periodicity whether we have periodic spline or not
for(n=2;n<=maxn;n++)
{
for(skind=0;skind<=2;skind++)
{
for(pkind=0;pkind<=2;pkind++)
{
for(periodicity=0;periodicity<=1;periodicity++)
{
periodic=periodicity==1;
//--- skip unsupported combinations of parameters
if(periodic && n<3)
continue;
//--- check
if(periodic && skind==0)
continue;
//--- check
if(n<5 && skind==0)
continue;
//--- init
xy.Resize(n,2);
xyz.Resize(n,3);
//--- function call
CApServ::TaskGenInt1DEquidist(-1,1,n,t2,x);
//--- change values
for(i_=0;i_<=n-1;i_++)
xy[i_].Set(0,x[i_]);
for(i_=0;i_<=n-1;i_++)
xyz[i_].Set(0,x[i_]);
//--- function call
CApServ::TaskGenInt1DEquidist(-1,1,n,t2,y);
//--- change values
for(i_=0;i_<=n-1;i_++)
xy[i_].Set(1,y[i_]);
for(i_=0;i_<=n-1;i_++)
xyz[i_].Set(1,y[i_]);
//--- function call
CApServ::TaskGenInt1DEquidist(-1,1,n,t2,z);
//--- change values
for(i_=0;i_<=n-1;i_++)
xyz[i_].Set(2,z[i_]);
//--- function calls
UnsetP2(p2);
UnsetP3(p3);
//--- check
if(periodic)
{
CPSpline::PSpline2BuildPeriodic(xy,n,skind,pkind,p2);
CPSpline::PSpline3BuildPeriodic(xyz,n,skind,pkind,p3);
}
else
{
CPSpline::PSpline2Build(xy,n,skind,pkind,p2);
CPSpline::PSpline3Build(xyz,n,skind,pkind,p3);
}
//--- PSpline2ParameterValues() properties
CPSpline::PSpline2ParameterValues(p2,tmpn,t2);
//--- check
if(tmpn!=n)
{
p2errors=true;
continue;
}
//--- function call
CPSpline::PSpline3ParameterValues(p3,tmpn,t3);
//--- check
if(tmpn!=n)
{
p3errors=true;
continue;
}
//--- search errors
p2errors=p2errors || t2[0]!=0.0;
p3errors=p3errors || t3[0]!=0.0;
for(i=1;i<=n-1;i++)
{
p2errors=p2errors || t2[i]<=t2[i-1];
p3errors=p3errors || t3[i]<=t3[i-1];
}
//--- check
if(periodic)
{
p2errors=p2errors || t2[n-1]>=1.0;
p3errors=p3errors || t3[n-1]>=1.0;
}
else
{
p2errors=p2errors || t2[n-1]!=1.0;
p3errors=p3errors || t3[n-1]!=1.0;
}
//--- Now we have parameter values stored at T,
//--- and want to test whether the actully correspond to
//--- points
for(i=0;i<=n-1;i++)
{
//--- 2-dimensional test
CPSpline::PSpline2Calc(p2,t2[i],vx,vy);
p2errors=p2errors || MathAbs(vx-x[i])>threshold;
p2errors=p2errors || MathAbs(vy-y[i])>threshold;
//--- 3-dimensional test
CPSpline::PSpline3Calc(p3,t3[i],vx,vy,vz);
p3errors=p3errors || MathAbs(vx-x[i])>threshold;
p3errors=p3errors || MathAbs(vy-y[i])>threshold;
p3errors=p3errors || MathAbs(vz-z[i])>threshold;
}
//--- Test periodicity (if needed)
if(periodic)
{
//--- periodicity at nodes
for(i=0;i<=n-1;i++)
{
//--- 2-dimensional test
CPSpline::PSpline2Calc(p2,t2[i]+CMath::RandomInteger(10)-5,vx,vy);
//--- search errors
p2errors=p2errors || MathAbs(vx-x[i])>threshold;
p2errors=p2errors || MathAbs(vy-y[i])>threshold;
//--- function call
CPSpline::PSpline2Diff(p2,t2[i]+CMath::RandomInteger(10)-5,vx,vdx,vy,vdy);
//--- search errors
p2errors=p2errors || MathAbs(vx-x[i])>threshold;
p2errors=p2errors || MathAbs(vy-y[i])>threshold;
//--- function call
CPSpline::PSpline2Diff2(p2,t2[i]+CMath::RandomInteger(10)-5,vx,vdx,vd2x,vy,vdy,vd2y);
//--- search errors
p2errors=p2errors || MathAbs(vx-x[i])>threshold;
p2errors=p2errors || MathAbs(vy-y[i])>threshold;
//--- 3-dimensional test
CPSpline::PSpline3Calc(p3,t3[i]+CMath::RandomInteger(10)-5,vx,vy,vz);
//--- search errors
p3errors=p3errors || MathAbs(vx-x[i])>threshold;
p3errors=p3errors || MathAbs(vy-y[i])>threshold;
p3errors=p3errors || MathAbs(vz-z[i])>threshold;
//--- function call
CPSpline::PSpline3Diff(p3,t3[i]+CMath::RandomInteger(10)-5,vx,vdx,vy,vdy,vz,vdz);
//--- search errors
p3errors=p3errors || MathAbs(vx-x[i])>threshold;
p3errors=p3errors || MathAbs(vy-y[i])>threshold;
p3errors=p3errors || MathAbs(vz-z[i])>threshold;
//--- function call
CPSpline::PSpline3Diff2(p3,t3[i]+CMath::RandomInteger(10)-5,vx,vdx,vd2x,vy,vdy,vd2y,vz,vdz,vd2z);
//--- search errors
p3errors=p3errors || MathAbs(vx-x[i])>threshold;
p3errors=p3errors || MathAbs(vy-y[i])>threshold;
p3errors=p3errors || MathAbs(vz-z[i])>threshold;
}
//--- periodicity between nodes
v0=CMath::RandomReal();
CPSpline::PSpline2Calc(p2,v0,vx,vy);
CPSpline::PSpline2Calc(p2,v0+CMath::RandomInteger(10)-5,vx2,vy2);
//--- search errors
p2errors=p2errors || MathAbs(vx-vx2)>threshold;
p2errors=p2errors || MathAbs(vy-vy2)>threshold;
//--- function calls
CPSpline::PSpline3Calc(p3,v0,vx,vy,vz);
CPSpline::PSpline3Calc(p3,v0+CMath::RandomInteger(10)-5,vx2,vy2,vz2);
//--- search errors
p3errors=p3errors || MathAbs(vx-vx2)>threshold;
p3errors=p3errors || MathAbs(vy-vy2)>threshold;
p3errors=p3errors || MathAbs(vz-vz2)>threshold;
//--- near-boundary test for continuity of function values and derivatives:
//--- 2-dimensional curve
if(!CAp::Assert(skind==1 || skind==2,"TEST: unexpected spline type!"))
return(false);
//--- change values
v0=100*CMath::m_machineepsilon;
v1=1-v0;
//--- function calls
CPSpline::PSpline2Calc(p2,v0,vx,vy);
CPSpline::PSpline2Calc(p2,v1,vx2,vy2);
//--- search errors
p2errors=p2errors || MathAbs(vx-vx2)>threshold;
p2errors=p2errors || MathAbs(vy-vy2)>threshold;
//--- function calls
CPSpline::PSpline2Diff(p2,v0,vx,vdx,vy,vdy);
CPSpline::PSpline2Diff(p2,v1,vx2,vdx2,vy2,vdy2);
//--- search errors
p2errors=p2errors || MathAbs(vx-vx2)>threshold;
p2errors=p2errors || MathAbs(vy-vy2)>threshold;
p2errors=p2errors || MathAbs(vdx-vdx2)>nonstrictthreshold;
p2errors=p2errors || MathAbs(vdy-vdy2)>nonstrictthreshold;
//--- function calls
CPSpline::PSpline2Diff2(p2,v0,vx,vdx,vd2x,vy,vdy,vd2y);
CPSpline::PSpline2Diff2(p2,v1,vx2,vdx2,vd2x2,vy2,vdy2,vd2y2);
//--- search errors
p2errors=p2errors || MathAbs(vx-vx2)>threshold;
p2errors=p2errors || MathAbs(vy-vy2)>threshold;
p2errors=p2errors || MathAbs(vdx-vdx2)>nonstrictthreshold;
p2errors=p2errors || MathAbs(vdy-vdy2)>nonstrictthreshold;
//--- check
if(skind==2)
{
//--- second derivative test only for cubic splines
p2errors=p2errors || MathAbs(vd2x-vd2x2)>nonstrictthreshold;
p2errors=p2errors || MathAbs(vd2y-vd2y2)>nonstrictthreshold;
}
//--- near-boundary test for continuity of function values and derivatives:
//--- 3-dimensional curve
if(!CAp::Assert(skind==1 || skind==2,"TEST: unexpected spline type!"))
return(false);
//--- change values
v0=100*CMath::m_machineepsilon;
v1=1-v0;
//--- function calls
CPSpline::PSpline3Calc(p3,v0,vx,vy,vz);
CPSpline::PSpline3Calc(p3,v1,vx2,vy2,vz2);
//--- search errors
p3errors=p3errors || MathAbs(vx-vx2)>threshold;
p3errors=p3errors || MathAbs(vy-vy2)>threshold;
p3errors=p3errors || MathAbs(vz-vz2)>threshold;
//--- function calls
CPSpline::PSpline3Diff(p3,v0,vx,vdx,vy,vdy,vz,vdz);
CPSpline::PSpline3Diff(p3,v1,vx2,vdx2,vy2,vdy2,vz2,vdz2);
//--- search errors
p3errors=p3errors || MathAbs(vx-vx2)>threshold;
p3errors=p3errors || MathAbs(vy-vy2)>threshold;
p3errors=p3errors || MathAbs(vz-vz2)>threshold;
p3errors=p3errors || MathAbs(vdx-vdx2)>nonstrictthreshold;
p3errors=p3errors || MathAbs(vdy-vdy2)>nonstrictthreshold;
p3errors=p3errors || MathAbs(vdz-vdz2)>nonstrictthreshold;
//--- function calls
CPSpline::PSpline3Diff2(p3,v0,vx,vdx,vd2x,vy,vdy,vd2y,vz,vdz,vd2z);
CPSpline::PSpline3Diff2(p3,v1,vx2,vdx2,vd2x2,vy2,vdy2,vd2y2,vz2,vdz2,vd2z2);
//--- search errors
p3errors=p3errors || MathAbs(vx-vx2)>threshold;
p3errors=p3errors || MathAbs(vy-vy2)>threshold;
p3errors=p3errors || MathAbs(vz-vz2)>threshold;
p3errors=p3errors || MathAbs(vdx-vdx2)>nonstrictthreshold;
p3errors=p3errors || MathAbs(vdy-vdy2)>nonstrictthreshold;
p3errors=p3errors || MathAbs(vdz-vdz2)>nonstrictthreshold;
//--- check
if(skind==2)
{
//--- second derivative test only for cubic splines
p3errors=p3errors || MathAbs(vd2x-vd2x2)>nonstrictthreshold;
p3errors=p3errors || MathAbs(vd2y-vd2y2)>nonstrictthreshold;
p3errors=p3errors || MathAbs(vd2z-vd2z2)>nonstrictthreshold;
}
}
}
}
}
}
//--- Test differentiation,tangents,calculation between nodes.
//--- Because differentiation is done in parameterization/spline/periodicity
//--- oblivious manner,we don't have to test all possible combinations
//--- of spline types and parameterizations.
//--- Actually we test special combination with properties which allow us
//--- to easily solve this problem:
//--- * 2 (3) variables
//--- * first variable is sampled from equidistant grid on [0][1]
//--- * other variables are random
//--- * uniform parameterization is used
//--- * periodicity - none
//--- * spline type - any (we use cubic splines)
//--- Same problem allows us to test calculation BETWEEN nodes.
for(n=2;n<=maxn;n++)
{
//--- init
xy.Resize(n,2);
xyz.Resize(n,3);
//--- function call
CApServ::TaskGenInt1DEquidist(0,1,n,t,x);
//--- change values
for(i_=0;i_<=n-1;i_++)
xy[i_].Set(0,x[i_]);
for(i_=0;i_<=n-1;i_++)
xyz[i_].Set(0,x[i_]);
//--- function call
CApServ::TaskGenInt1DEquidist(0,1,n,t,y);
//--- change values
for(i_=0;i_<=n-1;i_++)
xy[i_].Set(1,y[i_]);
for(i_=0;i_<=n-1;i_++)
xyz[i_].Set(1,y[i_]);
//--- function call
CApServ::TaskGenInt1DEquidist(0,1,n,t,z);
//--- change values
for(i_=0;i_<=n-1;i_++)
xyz[i_].Set(2,z[i_]);
//--- function call
UnsetP2(p2);
UnsetP3(p3);
CPSpline::PSpline2Build(xy,n,2,0,p2);
CPSpline::PSpline3Build(xyz,n,2,0,p3);
//--- Test 2D/3D spline:
//--- * build non-parametric cubic spline from T and X/Y
//--- * calculate its value and derivatives at V0
//--- * compare with Spline2Calc/Spline2Diff/Spline2Diff2
//--- Because of task properties both variants should
//--- return same answer.
v0=CMath::RandomReal();
CSpline1D::Spline1DBuildCubic(t,x,n,0,0.0,0,0.0,s);
CSpline1D::Spline1DDiff(s,v0,vx2,vdx2,vd2x2);
CSpline1D::Spline1DBuildCubic(t,y,n,0,0.0,0,0.0,s);
CSpline1D::Spline1DDiff(s,v0,vy2,vdy2,vd2y2);
CSpline1D::Spline1DBuildCubic(t,z,n,0,0.0,0,0.0,s);
CSpline1D::Spline1DDiff(s,v0,vz2,vdz2,vd2z2);
//--- 2D test
CPSpline::PSpline2Calc(p2,v0,vx,vy);
//--- search errors
p2errors=p2errors || MathAbs(vx-vx2)>threshold;
p2errors=p2errors || MathAbs(vy-vy2)>threshold;
//--- function call
CPSpline::PSpline2Diff(p2,v0,vx,vdx,vy,vdy);
//--- search errors
p2errors=p2errors || MathAbs(vx-vx2)>threshold;
p2errors=p2errors || MathAbs(vy-vy2)>threshold;
p2errors=p2errors || MathAbs(vdx-vdx2)>threshold;
p2errors=p2errors || MathAbs(vdy-vdy2)>threshold;
//--- function call
CPSpline::PSpline2Diff2(p2,v0,vx,vdx,vd2x,vy,vdy,vd2y);
//--- search errors
p2errors=p2errors || MathAbs(vx-vx2)>threshold;
p2errors=p2errors || MathAbs(vy-vy2)>threshold;
p2errors=p2errors || MathAbs(vdx-vdx2)>threshold;
p2errors=p2errors || MathAbs(vdy-vdy2)>threshold;
p2errors=p2errors || MathAbs(vd2x-vd2x2)>threshold;
p2errors=p2errors || MathAbs(vd2y-vd2y2)>threshold;
//--- 3D test
CPSpline::PSpline3Calc(p3,v0,vx,vy,vz);
//--- search errors
p3errors=p3errors || MathAbs(vx-vx2)>threshold;
p3errors=p3errors || MathAbs(vy-vy2)>threshold;
p3errors=p3errors || MathAbs(vz-vz2)>threshold;
//--- function call
CPSpline::PSpline3Diff(p3,v0,vx,vdx,vy,vdy,vz,vdz);
//--- search errors
p3errors=p3errors || MathAbs(vx-vx2)>threshold;
p3errors=p3errors || MathAbs(vy-vy2)>threshold;
p3errors=p3errors || MathAbs(vz-vz2)>threshold;
p3errors=p3errors || MathAbs(vdx-vdx2)>threshold;
p3errors=p3errors || MathAbs(vdy-vdy2)>threshold;
p3errors=p3errors || MathAbs(vdz-vdz2)>threshold;
//--- function call
CPSpline::PSpline3Diff2(p3,v0,vx,vdx,vd2x,vy,vdy,vd2y,vz,vdz,vd2z);
//--- search errors
p3errors=p3errors || MathAbs(vx-vx2)>threshold;
p3errors=p3errors || MathAbs(vy-vy2)>threshold;
p3errors=p3errors || MathAbs(vz-vz2)>threshold;
p3errors=p3errors || MathAbs(vdx-vdx2)>threshold;
p3errors=p3errors || MathAbs(vdy-vdy2)>threshold;
p3errors=p3errors || MathAbs(vdz-vdz2)>threshold;
p3errors=p3errors || MathAbs(vd2x-vd2x2)>threshold;
p3errors=p3errors || MathAbs(vd2y-vd2y2)>threshold;
p3errors=p3errors || MathAbs(vd2z-vd2z2)>threshold;
//--- Test tangents for 2D/3D
CPSpline::PSpline2Tangent(p2,v0,vx,vy);
//--- search errors
p2errors=p2errors || MathAbs(vx-vdx2/CApServ::SafePythag2(vdx2,vdy2))>threshold;
p2errors=p2errors || MathAbs(vy-vdy2/CApServ::SafePythag2(vdx2,vdy2))>threshold;
//--- function call
CPSpline::PSpline3Tangent(p3,v0,vx,vy,vz);
//--- search errors
p3errors=p3errors || MathAbs(vx-vdx2/CApServ::SafePythag3(vdx2,vdy2,vdz2))>threshold;
p3errors=p3errors || MathAbs(vy-vdy2/CApServ::SafePythag3(vdx2,vdy2,vdz2))>threshold;
p3errors=p3errors || MathAbs(vz-vdz2/CApServ::SafePythag3(vdx2,vdy2,vdz2))>threshold;
}
//--- Arc length test.
//--- Simple problem with easy solution (points on a straight line with
//--- uniform parameterization).
for(n=2;n<=maxn;n++)
{
//--- allocation
xy.Resize(n,2);
xyz.Resize(n,3);
for(i=0;i<=n-1;i++)
{
xy[i].Set(0,i);
xy[i].Set(1,i);
xyz[i].Set(0,i);
xyz[i].Set(1,i);
xyz[i].Set(2,i);
}
//--- function calls
CPSpline::PSpline2Build(xy,n,1,0,p2);
CPSpline::PSpline3Build(xyz,n,1,0,p3);
a=CMath::RandomReal();
b=CMath::RandomReal();
//--- search errors
p2errors=p2errors || MathAbs(CPSpline::PSpline2ArcLength(p2,a,b)-(b-a)*MathSqrt(2)*(n-1))>nonstrictthreshold;
p3errors=p3errors || MathAbs(CPSpline::PSpline3ArcLength(p3,a,b)-(b-a)*MathSqrt(3)*(n-1))>nonstrictthreshold;
}
//--- report
waserrors=p2errors || p3errors;
//--- check
if(!silent)
{
Print("TESTING SPLINE INTERPOLATION");
//--- Normal tests
Print("2D TEST: ");
//--- check
if(p2errors)
Print("FAILED");
else
Print("OK");
Print("3D TEST: ");
//--- check
if(p3errors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- end
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Unset spline, i.e. initialize it with random garbage |
//+------------------------------------------------------------------+
static void CTestPSplineUnit::UnsetP2(CPSpline2Interpolant &p)
{
//--- create matrix
CMatrixDouble xy;
//--- allocation
xy.Resize(2,2);
//--- initialization
xy[0].Set(0,-1);
xy[0].Set(1,-1);
xy[1].Set(0,1);
xy[1].Set(1,1);
//--- function call
CPSpline::PSpline2Build(xy,2,1,0,p);
}
//+------------------------------------------------------------------+
//| Unset spline, i.e. initialize it with random garbage |
//+------------------------------------------------------------------+
static void CTestPSplineUnit::UnsetP3(CPSpline3Interpolant &p)
{
//--- create matrix
CMatrixDouble xy;
//--- allocation
xy.Resize(2,3);
//--- initialization
xy[0].Set(0,-1);
xy[0].Set(1,-1);
xy[0].Set(2,-1);
xy[1].Set(0,1);
xy[1].Set(1,1);
xy[1].Set(2,1);
//--- function call
CPSpline::PSpline3Build(xy,2,1,0,p);
}
//+------------------------------------------------------------------+
//| Unsets real vector |
//+------------------------------------------------------------------+
static void CTestPSplineUnit::Unset1D(double &x[])
{
//--- allocation
ArrayResize(x,1);
//--- change value
x[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Testing class CSpline2D |
//+------------------------------------------------------------------+
class CTestSpline2DUnit
{
public:
//--- constructor, destructor
CTestSpline2DUnit(void);
~CTestSpline2DUnit(void);
//--- public methods
static bool TestSpline2D(const bool silent);
static void LConst(CSpline2DInterpolant &c,double &lx[],double &ly[],const int m,const int n,const double lstep,double &lc,double &lcx,double &lcy,double &lcxy);
static void TwodNumder(CSpline2DInterpolant &c,const double x,const double y,const double h,double &f,double &fx,double &fy,double &fxy);
static bool TestUnpack(CSpline2DInterpolant &c,double &lx[],double &ly[]);
static bool TestLinTrans(CSpline2DInterpolant &c,const double ax,const double bx,const double ay,const double by);
static void UnsetSpline2D(CSpline2DInterpolant &c);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestSpline2DUnit::CTestSpline2DUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestSpline2DUnit::~CTestSpline2DUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CSpline2D |
//+------------------------------------------------------------------+
static bool CTestSpline2DUnit::TestSpline2D(const bool silent)
{
//--- create variables
bool waserrors;
bool blerrors;
bool bcerrors;
bool dserrors;
bool cperrors;
bool uperrors;
bool lterrors;
bool syerrors;
bool rlerrors;
bool rcerrors;
int pass=0;
int passcount=0;
int jobtype=0;
double lstep=0;
double h=0;
double ax=0;
double ay=0;
double bx=0;
double by=0;
int i=0;
int j=0;
int k=0;
int n=0;
int m=0;
int n2=0;
int m2=0;
double err=0;
double t=0;
double t1=0;
double t2=0;
double l1=0;
double l1x=0;
double l1y=0;
double l1xy=0;
double l2=0;
double l2x=0;
double l2y=0;
double l2xy=0;
double fm=0;
double f1=0;
double f2=0;
double f3=0;
double f4=0;
double v1=0;
double v1x=0;
double v1y=0;
double v1xy=0;
double v2=0;
double v2x=0;
double v2y=0;
double v2xy=0;
double mf=0;
//--- create arrays
double x[];
double y[];
double lx[];
double ly[];
//--- create matrix
CMatrixDouble f;
CMatrixDouble fr;
CMatrixDouble ft;
//--- objects of classes
CSpline2DInterpolant c;
CSpline2DInterpolant c2;
//--- initialization
waserrors=false;
passcount=10;
h=0.00001;
lstep=0.001;
blerrors=false;
bcerrors=false;
dserrors=false;
cperrors=false;
uperrors=false;
lterrors=false;
syerrors=false;
rlerrors=false;
rcerrors=false;
//--- Test: bilinear,bicubic
for(n=2;n<=7;n++)
{
for(m=2;m<=7;m++)
{
//--- allocation
ArrayResize(x,n);
ArrayResize(y,m);
ArrayResize(lx,2*n-2+1);
ArrayResize(ly,2*m-2+1);
f.Resize(m,n);
//--- calculation
ft.Resize(n,m);
for(pass=1;pass<=passcount;pass++)
{
//--- Prepare task:
//--- * X and Y stores grid
//--- * F stores function values
//--- * LX and LY stores twice dense grid (for Lipschitz testing)
ax=-1-CMath::RandomReal();
bx=1+CMath::RandomReal();
ay=-1-CMath::RandomReal();
by=1+CMath::RandomReal();
for(j=0;j<=n-1;j++)
{
x[j]=0.5*(bx+ax)-0.5*(bx-ax)*MathCos(M_PI*(2*j+1)/(2*n));
//--- check
if(j==0)
x[j]=ax;
//--- check
if(j==n-1)
x[j]=bx;
lx[2*j]=x[j];
//--- check
if(j>0)
lx[2*j-1]=0.5*(x[j]+x[j-1]);
}
//--- swap
for(j=0;j<=n-1;j++)
{
k=CMath::RandomInteger(n);
//--- check
if(k!=j)
{
t=x[j];
x[j]=x[k];
x[k]=t;
}
}
//--- calculation
for(i=0;i<=m-1;i++)
{
y[i]=0.5*(by+ay)-0.5*(by-ay)*MathCos(M_PI*(2*i+1)/(2*m));
//--- check
if(i==0)
y[i]=ay;
//--- check
if(i==m-1)
y[i]=by;
ly[2*i]=y[i];
//--- check
if(i>0)
ly[2*i-1]=0.5*(y[i]+y[i-1]);
}
//--- swap
for(i=0;i<=m-1;i++)
{
k=CMath::RandomInteger(m);
//--- check
if(k!=i)
{
t=y[i];
y[i]=y[k];
y[k]=t;
}
}
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
f[i].Set(j,MathExp(0.6*x[j])-MathExp(-(0.3*y[i])+0.08*x[j])+2*MathCos(M_PI*(x[j]+1.2*y[i]))+0.1*MathCos(20*x[j]+15*y[i]));
}
//--- Test bilinear interpolation:
//--- * interpolation at the nodes
//--- * linearity
//--- * continuity
//--- * differentiation in the inner points
CSpline2D::Spline2DBuildBilinear(x,y,f,m,n,c);
//--- search errors
err=0;
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
err=MathMax(err,MathAbs(f[i][j]-CSpline2D::Spline2DCalc(c,x[j],y[i])));
}
//--- search errors
blerrors=blerrors || err>10000*CMath::m_machineepsilon;
err=0;
for(i=0;i<=m-2;i++)
{
for(j=0;j<=n-2;j++)
{
//--- Test for linearity between grid points
//--- (test point - geometric center of the cell)
fm=CSpline2D::Spline2DCalc(c,lx[2*j+1],ly[2*i+1]);
f1=CSpline2D::Spline2DCalc(c,lx[2*j],ly[2*i]);
f2=CSpline2D::Spline2DCalc(c,lx[2*j+2],ly[2*i]);
f3=CSpline2D::Spline2DCalc(c,lx[2*j+2],ly[2*i+2]);
f4=CSpline2D::Spline2DCalc(c,lx[2*j],ly[2*i+2]);
//--- search errors
err=MathMax(err,MathAbs(0.25*(f1+f2+f3+f4)-fm));
}
}
//--- search errors
blerrors=blerrors || err>10000*CMath::m_machineepsilon;
//--- function calls
LConst(c,lx,ly,m,n,lstep,l1,l1x,l1y,l1xy);
LConst(c,lx,ly,m,n,lstep/3,l2,l2x,l2y,l2xy);
//--- search errors
blerrors=blerrors || l2/l1>1.2;
err=0;
for(i=0;i<=m-2;i++)
{
for(j=0;j<=n-2;j++)
{
CSpline2D::Spline2DDiff(c,lx[2*j+1],ly[2*i+1],v1,v1x,v1y,v1xy);
TwodNumder(c,lx[2*j+1],ly[2*i+1],h,v2,v2x,v2y,v2xy);
//--- search errors
err=MathMax(err,MathAbs(v1-v2));
err=MathMax(err,MathAbs(v1x-v2x));
err=MathMax(err,MathAbs(v1y-v2y));
err=MathMax(err,MathAbs(v1xy-v2xy));
}
}
//--- search errors
dserrors=dserrors || err>1.0E-3;
uperrors=uperrors || !TestUnpack(c,lx,ly);
lterrors=lterrors || !TestLinTrans(c,ax,bx,ay,by);
//--- Test bicubic interpolation.
//--- * interpolation at the nodes
//--- * smoothness
//--- * differentiation
CSpline2D::Spline2DBuildBicubic(x,y,f,m,n,c);
//--- search errors
err=0;
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
err=MathMax(err,MathAbs(f[i][j]-CSpline2D::Spline2DCalc(c,x[j],y[i])));
}
//--- search errors
bcerrors=bcerrors || err>10000*CMath::m_machineepsilon;
LConst(c,lx,ly,m,n,lstep,l1,l1x,l1y,l1xy);
LConst(c,lx,ly,m,n,lstep/3,l2,l2x,l2y,l2xy);
//--- search errors
bcerrors=bcerrors || l2/l1>1.2;
bcerrors=bcerrors || l2x/l1x>1.2;
bcerrors=bcerrors || l2y/l1y>1.2;
//--- check
if(l2xy>0.01 && l1xy>0.01)
{
//--- Cross-derivative continuity is tested only when
//--- bigger than 0.01. When the task size is too
//--- small,the d2F/dXdY is nearly zero and Lipschitz
//--- constant ratio is ill-conditioned.
bcerrors=bcerrors || l2xy/l1xy>1.2;
}
err=0;
for(i=0;i<=2*m-2;i++)
{
for(j=0;j<=2*n-2;j++)
{
CSpline2D::Spline2DDiff(c,lx[j],ly[i],v1,v1x,v1y,v1xy);
TwodNumder(c,lx[j],ly[i],h,v2,v2x,v2y,v2xy);
//--- search errors
err=MathMax(err,MathAbs(v1-v2));
err=MathMax(err,MathAbs(v1x-v2x));
err=MathMax(err,MathAbs(v1y-v2y));
err=MathMax(err,MathAbs(v1xy-v2xy));
}
}
//--- search errors
dserrors=dserrors || err>1.0E-3;
uperrors=uperrors || !TestUnpack(c,lx,ly);
lterrors=lterrors || !TestLinTrans(c,ax,bx,ay,by);
//--- Copy/Serialise test
if(CMath::RandomReal()>0.5)
CSpline2D::Spline2DBuildBicubic(x,y,f,m,n,c);
else
CSpline2D::Spline2DBuildBilinear(x,y,f,m,n,c);
//--- function calls
UnsetSpline2D(c2);
CSpline2D::Spline2DCopy(c,c2);
//--- calculation
err=0;
for(i=1;i<=5;i++)
{
t1=ax+(bx-ax)*CMath::RandomReal();
t2=ay+(by-ay)*CMath::RandomReal();
//--- search errors
err=MathMax(err,MathAbs(CSpline2D::Spline2DCalc(c,t1,t2)-CSpline2D::Spline2DCalc(c2,t1,t2)));
}
//--- search errors
cperrors=cperrors || err>10000*CMath::m_machineepsilon;
//--- Special symmetry test
err=0;
for(jobtype=0;jobtype<=1;jobtype++)
{
//--- Prepare
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
ft[j].Set(i,f[i][j]);
}
//--- check
if(jobtype==0)
{
CSpline2D::Spline2DBuildBilinear(x,y,f,m,n,c);
CSpline2D::Spline2DBuildBilinear(y,x,ft,n,m,c2);
}
else
{
CSpline2D::Spline2DBuildBicubic(x,y,f,m,n,c);
CSpline2D::Spline2DBuildBicubic(y,x,ft,n,m,c2);
}
//--- Test
for(i=1;i<=10;i++)
{
t1=ax+(bx-ax)*CMath::RandomReal();
t2=ay+(by-ay)*CMath::RandomReal();
//--- search errors
err=MathMax(err,MathAbs(CSpline2D::Spline2DCalc(c,t1,t2)-CSpline2D::Spline2DCalc(c2,t2,t1)));
}
}
//--- search errors
syerrors=syerrors || err>10000*CMath::m_machineepsilon;
}
}
}
//--- Test resample
for(m=2;m<=6;m++)
{
for(n=2;n<=6;n++)
{
//--- allocation
f.Resize(m,n);
ArrayResize(x,n);
ArrayResize(y,m);
//--- change values
for(j=0;j<=n-1;j++)
x[j]=(double)j/(double)(n-1);
for(i=0;i<=m-1;i++)
y[i]=(double)i/(double)(m-1);
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
f[i].Set(j,MathExp(0.6*x[j])-MathExp(-(0.3*y[i])+0.08*x[j])+2*MathCos(M_PI*(x[j]+1.2*y[i]))+0.1*MathCos(20*x[j]+15*y[i]));
}
//--- calculation
for(m2=2;m2<=6;m2++)
{
for(n2=2;n2<=6;n2++)
{
for(pass=1;pass<=passcount;pass++)
{
for(jobtype=0;jobtype<=1;jobtype++)
{
//--- check
if(jobtype==0)
{
CSpline2D::Spline2DResampleBilinear(f,m,n,fr,m2,n2);
CSpline2D::Spline2DBuildBilinear(x,y,f,m,n,c);
}
//--- check
if(jobtype==1)
{
CSpline2D::Spline2DResampleBicubic(f,m,n,fr,m2,n2);
CSpline2D::Spline2DBuildBicubic(x,y,f,m,n,c);
}
//--- change values
err=0;
mf=0;
//--- calculation
for(i=0;i<=m2-1;i++)
{
for(j=0;j<=n2-1;j++)
{
v1=CSpline2D::Spline2DCalc(c,(double)j/(double)(n2-1),(double)i/(double)(m2-1));
v2=fr[i][j];
//--- search errors
err=MathMax(err,MathAbs(v1-v2));
mf=MathMax(mf,MathAbs(v1));
}
}
//--- check
if(jobtype==0)
rlerrors=rlerrors || err/mf>10000*CMath::m_machineepsilon;
//--- check
if(jobtype==1)
rcerrors=rcerrors || err/mf>10000*CMath::m_machineepsilon;
}
}
}
}
}
}
//--- report
waserrors=(((((((blerrors || bcerrors) || dserrors) || cperrors) || uperrors) || lterrors) || syerrors) || rlerrors) || rcerrors;
//--- check
if(!silent)
{
Print("TESTING 2D INTERPOLATION");
//--- Normal tests
Print("BILINEAR TEST: ");
//--- check
if(blerrors)
Print("FAILED");
else
Print("OK");
Print("BICUBIC TEST: ");
//--- check
if(bcerrors)
Print("FAILED");
else
Print("OK");
Print("DIFFERENTIATION TEST: ");
//--- check
if(dserrors)
Print("FAILED");
else
Print("OK");
Print("COPY/SERIALIZE TEST: ");
//--- check
if(cperrors)
Print("FAILED");
else
Print("OK");
Print("UNPACK TEST: ");
//--- check
if(uperrors)
Print("FAILED");
else
Print("OK");
Print("LIN.TRANS. TEST: ");
//--- check
if(lterrors)
Print("FAILED");
else
Print("OK");
Print("SPECIAL SYMMETRY TEST: ");
//--- check
if(syerrors)
Print("FAILED");
else
Print("OK");
Print("BILINEAR RESAMPLING TEST: ");
//--- check
if(rlerrors)
Print("FAILED");
else
Print("OK");
Print("BICUBIC RESAMPLING TEST: ");
//--- check
if(rcerrors)
Print("FAILED");
else
Print("OK");
//--- Summary
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- end
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Lipschitz constants for spline inself,first and second |
//| derivatives. |
//+------------------------------------------------------------------+
static void CTestSpline2DUnit::LConst(CSpline2DInterpolant &c,double &lx[],
double &ly[],const int m,const int n,
const double lstep,double &lc,
double &lcx,double &lcy,double &lcxy)
{
//--- create variables
int i=0;
int j=0;
double f1=0;
double f2=0;
double f3=0;
double f4=0;
double fx1=0;
double fx2=0;
double fx3=0;
double fx4=0;
double fy1=0;
double fy2=0;
double fy3=0;
double fy4=0;
double fxy1=0;
double fxy2=0;
double fxy3=0;
double fxy4=0;
double s2lstep=0;
//--- initialization
lc=0;
lcx=0;
lcy=0;
lcxy=0;
s2lstep=MathSqrt(2)*lstep;
//--- calculation
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- Calculate
TwodNumder(c,lx[j]-lstep/2,ly[i]-lstep/2,lstep/4,f1,fx1,fy1,fxy1);
TwodNumder(c,lx[j]+lstep/2,ly[i]-lstep/2,lstep/4,f2,fx2,fy2,fxy2);
TwodNumder(c,lx[j]+lstep/2,ly[i]+lstep/2,lstep/4,f3,fx3,fy3,fxy3);
TwodNumder(c,lx[j]-lstep/2,ly[i]+lstep/2,lstep/4,f4,fx4,fy4,fxy4);
//--- Lipschitz constant for the function itself
lc=MathMax(lc,MathAbs((f1-f2)/lstep));
lc=MathMax(lc,MathAbs((f2-f3)/lstep));
lc=MathMax(lc,MathAbs((f3-f4)/lstep));
lc=MathMax(lc,MathAbs((f4-f1)/lstep));
lc=MathMax(lc,MathAbs((f1-f3)/s2lstep));
lc=MathMax(lc,MathAbs((f2-f4)/s2lstep));
//--- Lipschitz constant for the first derivative
lcx=MathMax(lcx,MathAbs((fx1-fx2)/lstep));
lcx=MathMax(lcx,MathAbs((fx2-fx3)/lstep));
lcx=MathMax(lcx,MathAbs((fx3-fx4)/lstep));
lcx=MathMax(lcx,MathAbs((fx4-fx1)/lstep));
lcx=MathMax(lcx,MathAbs((fx1-fx3)/s2lstep));
lcx=MathMax(lcx,MathAbs((fx2-fx4)/s2lstep));
//--- Lipschitz constant for the first derivative
lcy=MathMax(lcy,MathAbs((fy1-fy2)/lstep));
lcy=MathMax(lcy,MathAbs((fy2-fy3)/lstep));
lcy=MathMax(lcy,MathAbs((fy3-fy4)/lstep));
lcy=MathMax(lcy,MathAbs((fy4-fy1)/lstep));
lcy=MathMax(lcy,MathAbs((fy1-fy3)/s2lstep));
lcy=MathMax(lcy,MathAbs((fy2-fy4)/s2lstep));
//--- Lipschitz constant for the cross-derivative
lcxy=MathMax(lcxy,MathAbs((fxy1-fxy2)/lstep));
lcxy=MathMax(lcxy,MathAbs((fxy2-fxy3)/lstep));
lcxy=MathMax(lcxy,MathAbs((fxy3-fxy4)/lstep));
lcxy=MathMax(lcxy,MathAbs((fxy4-fxy1)/lstep));
lcxy=MathMax(lcxy,MathAbs((fxy1-fxy3)/s2lstep));
lcxy=MathMax(lcxy,MathAbs((fxy2-fxy4)/s2lstep));
}
}
}
//+------------------------------------------------------------------+
//| Numerical differentiation. |
//+------------------------------------------------------------------+
static void CTestSpline2DUnit::TwodNumder(CSpline2DInterpolant &c,const double x,
const double y,const double h,
double &f,double &fx,double &fy,
double &fxy)
{
//--- calculation
f=CSpline2D::Spline2DCalc(c,x,y);
fx=(CSpline2D::Spline2DCalc(c,x+h,y)-CSpline2D::Spline2DCalc(c,x-h,y))/(2*h);
fy=(CSpline2D::Spline2DCalc(c,x,y+h)-CSpline2D::Spline2DCalc(c,x,y-h))/(2*h);
fxy=(CSpline2D::Spline2DCalc(c,x+h,y+h)-CSpline2D::Spline2DCalc(c,x-h,y+h)-CSpline2D::Spline2DCalc(c,x+h,y-h)+CSpline2D::Spline2DCalc(c,x-h,y-h))/CMath::Sqr(2*h);
}
//+------------------------------------------------------------------+
//| Unpack test |
//+------------------------------------------------------------------+
static bool CTestSpline2DUnit::TestUnpack(CSpline2DInterpolant &c,double &lx[],
double &ly[])
{
//--- create variables
bool result;
int i=0;
int j=0;
int n=0;
int m=0;
int ci=0;
int cj=0;
int p=0;
double err=0;
double tx=0;
double ty=0;
double v1=0;
double v2=0;
int pass=0;
int passcount=0;
//--- create matrix
CMatrixDouble tbl;
//--- initialization
passcount=20;
err=0;
//--- function call
CSpline2D::Spline2DUnpack(c,m,n,tbl);
//--- calculation
for(i=0;i<=m-2;i++)
{
for(j=0;j<=n-2;j++)
{
for(pass=1;pass<=passcount;pass++)
{
p=(n-1)*i+j;
tx=(0.001+0.999*CMath::RandomReal())*(tbl[p][1]-tbl[p][0]);
ty=(0.001+0.999*CMath::RandomReal())*(tbl[p][3]-tbl[p][2]);
//--- Interpolation properties
v1=0;
for(ci=0;ci<=3;ci++)
{
for(cj=0;cj<=3;cj++)
v1=v1+tbl[p][4+ci*4+cj]*MathPow(tx,ci)*MathPow(ty,cj);
}
v2=CSpline2D::Spline2DCalc(c,tbl[p][0]+tx,tbl[p][2]+ty);
//--- search errors
err=MathMax(err,MathAbs(v1-v2));
//--- Grid correctness
err=MathMax(err,MathAbs(lx[2*j]-tbl[p][0]));
err=MathMax(err,MathAbs(lx[2*(j+1)]-tbl[p][1]));
err=MathMax(err,MathAbs(ly[2*i]-tbl[p][2]));
err=MathMax(err,MathAbs(ly[2*(i+1)]-tbl[p][3]));
}
}
}
//--- get result
result=err<10000*CMath::m_machineepsilon;
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| LinTrans test |
//+------------------------------------------------------------------+
static bool CTestSpline2DUnit::TestLinTrans(CSpline2DInterpolant &c,
const double ax,const double bx,
const double ay,const double by)
{
//--- create variables
bool result;
double err=0;
double a1=0;
double a2=0;
double b1=0;
double b2=0;
double tx=0;
double ty=0;
double vx=0;
double vy=0;
double v1=0;
double v2=0;
int pass=0;
int passcount=0;
int xjob=0;
int yjob=0;
//--- object of class
CSpline2DInterpolant c2;
//--- initialization
passcount=5;
err=0;
//--- calculation
for(xjob=0;xjob<=1;xjob++)
{
for(yjob=0;yjob<=1;yjob++)
{
for(pass=1;pass<=passcount;pass++)
{
//--- Prepare
do
{
a1=2*CMath::RandomReal()-1;
}
while(a1==0.0);
//--- change values
a1=a1*xjob;
b1=2*CMath::RandomReal()-1;
do
{
a2=2*CMath::RandomReal()-1;
}
while(a2==0.0);
//--- change values
a2=a2*yjob;
b2=2*CMath::RandomReal()-1;
//--- Test XY
CSpline2D::Spline2DCopy(c,c2);
CSpline2D::Spline2DLinTransXY(c2,a1,b1,a2,b2);
tx=ax+CMath::RandomReal()*(bx-ax);
ty=ay+CMath::RandomReal()*(by-ay);
//--- check
if(xjob==0)
{
tx=b1;
vx=ax+CMath::RandomReal()*(bx-ax);
}
else
vx=(tx-b1)/a1;
//--- check
if(yjob==0)
{
ty=b2;
vy=ay+CMath::RandomReal()*(by-ay);
}
else
vy=(ty-b2)/a2;
v1=CSpline2D::Spline2DCalc(c,tx,ty);
v2=CSpline2D::Spline2DCalc(c2,vx,vy);
//--- search errors
err=MathMax(err,MathAbs(v1-v2));
//--- Test F
CSpline2D::Spline2DCopy(c,c2);
CSpline2D::Spline2DLinTransF(c2,a1,b1);
//--- change values
tx=ax+CMath::RandomReal()*(bx-ax);
ty=ay+CMath::RandomReal()*(by-ay);
v1=CSpline2D::Spline2DCalc(c,tx,ty);
v2=CSpline2D::Spline2DCalc(c2,tx,ty);
//--- search errors
err=MathMax(err,MathAbs(a1*v1+b1-v2));
}
}
}
//--- get result
result=err<10000*CMath::m_machineepsilon;
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Unset spline,i.e. initialize it with random garbage |
//+------------------------------------------------------------------+
static void CTestSpline2DUnit::UnsetSpline2D(CSpline2DInterpolant &c)
{
//--- create arrays
double x[];
double y[];
//--- create matrix
CMatrixDouble f;
//--- allocation
ArrayResize(x,2);
ArrayResize(y,2);
f.Resize(2,2);
//--- initialization
x[0]=-1;
x[1]=1;
y[0]=-1;
y[1]=1;
f[0].Set(0,0);
f[0].Set(1,0);
f[1].Set(0,0);
f[1].Set(1,0);
//--- function call
CSpline2D::Spline2DBuildBilinear(x,y,f,2,2,c);
}
//+------------------------------------------------------------------+
//| Testing class CSpdGEVD |
//+------------------------------------------------------------------+
class CTestSpdGEVDUnit
{
public:
//--- constructor, destructor
CTestSpdGEVDUnit(void);
~CTestSpdGEVDUnit(void);
//--- public method
static bool TestSpdGEVD(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestSpdGEVDUnit::CTestSpdGEVDUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestSpdGEVDUnit::~CTestSpdGEVDUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CSpdGEVD |
//+------------------------------------------------------------------+
static bool CTestSpdGEVDUnit::TestSpdGEVD(const bool silent)
{
//--- create variables
int pass=0;
int n=0;
int passcount=0;
int maxn=0;
int atask=0;
int btask=0;
bool isuppera;
bool isupperb;
int i=0;
int j=0;
int minij=0;
double v=0;
double v1=0;
double v2=0;
double err=0;
double valerr=0;
double threshold=0;
bool waserrors;
bool wfailed;
bool wnsorted;
int i_=0;
//--- create arrays
double d[];
double t1[];
//--- create matrix
CMatrixDouble a;
CMatrixDouble b;
CMatrixDouble afull;
CMatrixDouble bfull;
CMatrixDouble l;
CMatrixDouble z;
//--- initialization
threshold=10000*CMath::m_machineepsilon;
valerr=0;
wfailed=false;
wnsorted=false;
maxn=20;
passcount=5;
//--- Main cycle
for(n=1;n<=maxn;n++)
{
for(pass=1;pass<=passcount;pass++)
{
for(atask=0;atask<=1;atask++)
{
for(btask=0;btask<=1;btask++)
{
isuppera=atask==0;
isupperb=btask==0;
//--- Initialize A,B,AFull,BFull
ArrayResize(t1,n);
a.Resize(n,n);
b.Resize(n,n);
afull.Resize(n,n);
bfull.Resize(n,n);
l.Resize(n,n);
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a[i].Set(j,2*CMath::RandomReal()-1);
a[j].Set(i,a[i][j]);
afull[i].Set(j,a[i][j]);
afull[j].Set(i,a[i][j]);
}
}
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=i+1;j<=n-1;j++)
{
l[i].Set(j,CMath::RandomReal());
l[j].Set(i,l[i][j]);
}
l[i].Set(i,1.5+CMath::RandomReal());
}
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
minij=MathMin(i,j);
//--- change value
v=0.0;
for(i_=0;i_<=minij;i_++)
{
v+=l[i][i_]*l[i_][j];
}
b[i].Set(j,v);
b[j].Set(i,v);
bfull[i].Set(j,v);
bfull[j].Set(i,v);
}
}
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(isuppera)
{
//--- check
if(j<i)
a[i].Set(j,2*CMath::RandomReal()-1);
}
else
{
//--- check
if(i<j)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- check
if(isupperb)
{
//--- check
if(j<i)
b[i].Set(j,2*CMath::RandomReal()-1);
}
else
{
//--- check
if(i<j)
b[i].Set(j,2*CMath::RandomReal()-1);
}
}
}
//--- Problem 1
if(!CSpdGEVD::SMatrixGEVD(a,n,isuppera,b,isupperb,1,1,d,z))
{
wfailed=true;
continue;
}
//--- calculation
err=0;
for(j=0;j<=n-1;j++)
{
for(i=0;i<=n-1;i++)
{
v1=0.0;
for(i_=0;i_<=n-1;i_++)
v1+=afull[i][i_]*z[i_][j];
v2=0.0;
for(i_=0;i_<=n-1;i_++)
v2+=bfull[i][i_]*z[i_][j];
//--- search errors
err=MathMax(err,MathAbs(v1-d[j]*v2));
}
}
//--- search errors
valerr=MathMax(err,valerr);
//--- Problem 2
if(!CSpdGEVD::SMatrixGEVD(a,n,isuppera,b,isupperb,1,2,d,z))
{
wfailed=true;
continue;
}
//--- calculation
err=0;
for(j=0;j<=n-1;j++)
{
for(i=0;i<=n-1;i++)
{
v1=0.0;
for(i_=0;i_<=n-1;i_++)
v1+=bfull[i][i_]*z[i_][j];
t1[i]=v1;
}
for(i=0;i<=n-1;i++)
{
v2=0.0;
for(i_=0;i_<=n-1;i_++)
v2+=afull[i][i_]*t1[i_];
//--- search errors
err=MathMax(err,MathAbs(v2-d[j]*z[i][j]));
}
}
//--- search errors
valerr=MathMax(err,valerr);
//--- Test problem 3
if(!CSpdGEVD::SMatrixGEVD(a,n,isuppera,b,isupperb,1,3,d,z))
{
wfailed=true;
continue;
}
//--- calculation
err=0;
for(j=0;j<=n-1;j++)
{
for(i=0;i<=n-1;i++)
{
v1=0.0;
for(i_=0;i_<=n-1;i_++)
v1+=afull[i][i_]*z[i_][j];
t1[i]=v1;
}
for(i=0;i<=n-1;i++)
{
v2=0.0;
for(i_=0;i_<=n-1;i_++)
v2+=bfull[i][i_]*t1[i_];
//--- search errors
err=MathMax(err,MathAbs(v2-d[j]*z[i][j]));
}
}
//--- search errors
valerr=MathMax(err,valerr);
}
}
}
}
//--- report
waserrors=(valerr>threshold || wfailed) || wnsorted;
//--- check
if(!silent)
{
Print("TESTING SYMMETRIC GEVD");
Print("Av-lambdav error (generalized): ");
Print("{0,5:E3}",valerr);
Print("Eigen values order: ");
//--- check
if(!wnsorted)
Print("OK");
else
Print("FAILED");
Print("Always converged: ");
//--- check
if(!wfailed)
Print("YES");
else
Print("NO");
Print("Threshold: ");
Print("{0,5:E3}",threshold);
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CInverseUpdate |
//+------------------------------------------------------------------+
class CTestInverseUpdateUnit
{
private:
//--- private methods
static void MakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
static void MatLU(CMatrixDouble &a,const int m,const int n,int &pivots[]);
static void GenerateRandomOrthogonalMatrix(CMatrixDouble &a0,const int n);
static void GenerateRandomMatrixCond(CMatrixDouble &a0,const int n,const double c);
static bool InvMatTr(CMatrixDouble &a,const int n,const bool isupper,const bool isunittriangular);
static bool InvMatLU(CMatrixDouble &a,int &pivots[],const int n);
static bool InvMat(CMatrixDouble &a,const int n);
static double MatrixDiff(CMatrixDouble &a,CMatrixDouble &b,const int m,const int n);
static bool UpdAndInv(CMatrixDouble &a,double &u[],double &v[],const int n);
public:
//--- constructor, destructor
CTestInverseUpdateUnit(void);
~CTestInverseUpdateUnit(void);
//--- public method
static bool TestInverseUpdate(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestInverseUpdateUnit::CTestInverseUpdateUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestInverseUpdateUnit::~CTestInverseUpdateUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CInverseUpdate |
//+------------------------------------------------------------------+
static bool CTestInverseUpdateUnit::TestInverseUpdate(const bool silent)
{
//--- create variables
int n=0;
int maxn=0;
int i=0;
int updrow=0;
int updcol=0;
double val=0;
int pass=0;
int passcount=0;
bool waserrors;
double threshold=0;
double c=0;
//--- create arrays
double u[];
double v[];
//--- create matrix
CMatrixDouble a;
CMatrixDouble inva;
CMatrixDouble b1;
CMatrixDouble b2;
//--- initialization
waserrors=false;
maxn=10;
passcount=100;
threshold=1.0E-6;
//--- process
for(n=1;n<=maxn;n++)
{
//--- allocation
a.Resize(n,n);
b1.Resize(n,n);
b2.Resize(n,n);
ArrayResize(u,n);
ArrayResize(v,n);
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
c=MathExp(CMath::RandomReal()*MathLog(10));
GenerateRandomMatrixCond(a,n,c);
MakeACopy(a,n,n,inva);
//--- check
if(!InvMat(inva,n))
{
waserrors=true;
break;
}
//--- Test simple update
updrow=CMath::RandomInteger(n);
updcol=CMath::RandomInteger(n);
val=0.1*(2*CMath::RandomReal()-1);
//--- change values
for(i=0;i<=n-1;i++)
{
//--- check
if(i==updrow)
u[i]=val;
else
u[i]=0;
//--- check
if(i==updcol)
v[i]=1;
else
v[i]=0;
}
//--- function call
MakeACopy(a,n,n,b1);
//--- check
if(!UpdAndInv(b1,u,v,n))
{
waserrors=true;
break;
}
//--- function calls
MakeACopy(inva,n,n,b2);
CInverseUpdate::RMatrixInvUpdateSimple(b2,n,updrow,updcol,val);
//--- search errors
waserrors=waserrors || MatrixDiff(b1,b2,n,n)>threshold;
//--- Test row update
updrow=CMath::RandomInteger(n);
for(i=0;i<=n-1;i++)
{
//--- check
if(i==updrow)
u[i]=1;
else
u[i]=0;
v[i]=0.1*(2*CMath::RandomReal()-1);
}
//--- function call
MakeACopy(a,n,n,b1);
//--- check
if(!UpdAndInv(b1,u,v,n))
{
waserrors=true;
break;
}
//--- function calls
MakeACopy(inva,n,n,b2);
CInverseUpdate::RMatrixInvUpdateRow(b2,n,updrow,v);
//--- search errors
waserrors=waserrors || MatrixDiff(b1,b2,n,n)>threshold;
//--- Test column update
updcol=CMath::RandomInteger(n);
for(i=0;i<=n-1;i++)
{
//--- check
if(i==updcol)
v[i]=1;
else
v[i]=0;
u[i]=0.1*(2*CMath::RandomReal()-1);
}
//--- function call
MakeACopy(a,n,n,b1);
//--- check
if(!UpdAndInv(b1,u,v,n))
{
waserrors=true;
break;
}
//--- function calls
MakeACopy(inva,n,n,b2);
CInverseUpdate::RMatrixInvUpdateColumn(b2,n,updcol,u);
//--- search errors
waserrors=waserrors || MatrixDiff(b1,b2,n,n)>threshold;
//--- Test full update
for(i=0;i<=n-1;i++)
{
v[i]=0.1*(2*CMath::RandomReal()-1);
u[i]=0.1*(2*CMath::RandomReal()-1);
}
//--- function call
MakeACopy(a,n,n,b1);
//--- check
if(!UpdAndInv(b1,u,v,n))
{
waserrors=true;
break;
}
//--- function calls
MakeACopy(inva,n,n,b2);
CInverseUpdate::RMatrixInvUpdateUV(b2,n,u,v);
//--- search errors
waserrors=waserrors || MatrixDiff(b1,b2,n,n)>threshold;
}
}
//--- report
if(!silent)
{
Print("TESTING INVERSE UPDATE (REAL)");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
static void CTestInverseUpdateUnit::MakeACopy(CMatrixDouble &a,const int m,
const int n,CMatrixDouble &b)
{
//--- create variables
int i=0;
int j=0;
//--- allocation
b.Resize(m,n);
//--- copy
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
b[i].Set(j,a[i][j]);
}
}
//+------------------------------------------------------------------+
//| LU decomposition |
//+------------------------------------------------------------------+
static void CTestInverseUpdateUnit::MatLU(CMatrixDouble &a,const int m,
const int n,int &pivots[])
{
//--- create variables
int i=0;
int j=0;
int jp=0;
double s=0;
int i_=0;
//--- create array
double t1[];
//--- allocation
ArrayResize(pivots,MathMin(m-1,n-1)+1);
ArrayResize(t1,MathMax(m-1,n-1)+1);
//--- check
if(!CAp::Assert(m>=0 && n>=0,"Error in LUDecomposition: incorrect function arguments"))
return;
//--- Quick return if possible
if(m==0 || n==0)
return;
//--- calculation
for(j=0;j<=MathMin(m-1,n-1);j++)
{
//--- Find pivot and test for singularity.
jp=j;
for(i=j+1;i<=m-1;i++)
{
//--- check
if(MathAbs(a[i][j])>MathAbs(a[jp][j]))
jp=i;
}
pivots[j]=jp;
//--- check
if(a[jp][j]!=0.0)
{
//--- Apply the interchange to rows
if(jp!=j)
{
for(i_=0;i_<=n-1;i_++)
t1[i_]=a[j][i_];
for(i_=0;i_<=n-1;i_++)
a[j].Set(i_,a[jp][i_]);
for(i_=0;i_<=n-1;i_++)
a[jp].Set(i_,t1[i_]);
}
//--- Compute elements J+1:M of J-th column.
if(j<m)
{
jp=j+1;
s=1/a[j][j];
for(i_=jp;i_<=m-1;i_++)
a[i_].Set(j,s*a[i_][j]);
}
}
//--- check
if(j<MathMin(m,n)-1)
{
//--- Update trailing submatrix.
jp=j+1;
for(i=j+1;i<=m-1;i++)
{
s=a[i][j];
for(i_=jp;i_<=n-1;i_++)
a[i].Set(i_,a[i][i_]-s*a[j][i_]);
}
}
}
}
//+------------------------------------------------------------------+
//| Generate matrix with given condition number C (2-norm) |
//+------------------------------------------------------------------+
static void CTestInverseUpdateUnit::GenerateRandomOrthogonalMatrix(CMatrixDouble &a0,
const int n)
{
//--- create variables
double t=0;
double lambdav=0;
int s=0;
int i=0;
int j=0;
double u1=0;
double u2=0;
double sm=0;
int i_=0;
//--- create arrays
double w[];
double v[];
//--- create matrix
CMatrixDouble a;
//--- check
if(n<=0)
return;
//--- allocation
ArrayResize(w,n+1);
ArrayResize(v,n+1);
a.Resize(n+1,n+1);
a0.Resize(n,n);
//--- Prepare A
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
//--- check
if(i==j)
a[i].Set(j,1);
else
a[i].Set(j,0);
}
}
//--- Calculate A using Stewart algorithm
for(s=2;s<=n;s++)
{
//--- Prepare v and Lambda=v'*v
do
{
i=1;
while(i<=s)
{
//--- change values
u1=2*CMath::RandomReal()-1;
u2=2*CMath::RandomReal()-1;
sm=u1*u1+u2*u2;
//--- check
if(sm==0.0 || sm>1.0)
continue;
sm=MathSqrt(-(2*MathLog(sm)/sm));
v[i]=u1*sm;
//--- check
if(i+1<=s)
v[i+1]=u2*sm;
i=i+2;
}
//--- change value
lambdav=0.0;
for(i_=1;i_<=s;i_++)
lambdav+=v[i_]*v[i_];
}
while((double)(lambdav)==0.0);
lambdav=2/lambdav;
//--- A * (I - 2 vv'/v'v )=
//--- =A - (2/v'v) * A * v * v'=
//--- =A - (2/v'v) * w * v'
//--- where w=Av
for(i=1;i<=s;i++)
{
t=0.0;
for(i_=1;i_<=s;i_++)
t+=a[i][i_]*v[i_];
w[i]=t;
}
//--- calculation
for(i=1;i<=s;i++)
{
t=w[i]*lambdav;
for(i_=1;i_<=s;i_++)
a[i].Set(i_,a[i][i_]-t*v[i_]);
}
}
//--- copy
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
a0[i-1].Set(j-1,a[i][j]);
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestInverseUpdateUnit::GenerateRandomMatrixCond(CMatrixDouble &a0,
const int n,
const double c)
{
//--- create variables
double l1=0;
double l2=0;
int i=0;
int j=0;
int k=0;
//--- create array
double cc[];
//--- create matrix
CMatrixDouble q1;
CMatrixDouble q2;
//--- function calls
GenerateRandomOrthogonalMatrix(q1,n);
GenerateRandomOrthogonalMatrix(q2,n);
//--- allocation
ArrayResize(cc,n);
//--- change values
l1=0;
l2=MathLog(1/c);
cc[0]=MathExp(l1);
for(i=1;i<=n-2;i++)
cc[i]=MathExp(CMath::RandomReal()*(l2-l1)+l1);
cc[n-1]=MathExp(l2);
//--- allocation
a0.Resize(n,n);
//--- calculation
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
a0[i].Set(j,0);
for(k=0;k<=n-1;k++)
a0[i].Set(j,a0[i][j]+q1[i][k]*cc[k]*q2[j][k]);
}
}
}
//+------------------------------------------------------------------+
//| triangular inverse |
//+------------------------------------------------------------------+
static bool CTestInverseUpdateUnit::InvMatTr(CMatrixDouble &a,const int n,
const bool isupper,
const bool isunittriangular)
{
//--- create variables
bool result;
bool nounit;
int i=0;
int j=0;
double v=0;
double ajj=0;
int i_=0;
//--- create array
double t[];
//--- initialization
result=true;
//--- allocation
ArrayResize(t,n);
//--- Test the input parameters.
nounit=!isunittriangular;
//--- check
if(isupper)
{
//--- Compute inverse of upper triangular matrix.
for(j=0;j<=n-1;j++)
{
//--- check
if(nounit)
{
//--- check
if(a[j][j]==0.0)
{
//--- return result
return(false);
}
a[j].Set(j,1/a[j][j]);
ajj=-a[j][j];
}
else
ajj=-1;
//--- Compute elements 1:j-1 of j-th column.
if(j>0)
{
for(i_=0;i_<=j-1;i_++)
t[i_]=a[i_][j];
for(i=0;i<=j-1;i++)
{
//--- check
if(i<j-1)
{
//--- change value
v=0.0;
for(i_=i+1;i_<=j-1;i_++)
v+=a[i][i_]*t[i_];
}
else
v=0;
//--- check
if(nounit)
a[i].Set(j,v+a[i][i]*t[i]);
else
a[i].Set(j,v+t[i]);
}
//--- change values
for(i_=0;i_<=j-1;i_++)
a[i_].Set(j,ajj*a[i_][j]);
}
}
}
else
{
//--- Compute inverse of lower triangular matrix.
for(j=n-1;j>=0;j--)
{
//--- check
if(nounit)
{
//--- check
if(a[j][j]==0.0)
{
//--- return result
return(false);
}
//--- change values
a[j].Set(j,1/a[j][j]);
ajj=-a[j][j];
}
else
ajj=-1;
//--- check
if(j<n-1)
{
//--- Compute elements j+1:n of j-th column.
for(i_=j+1;i_<=n-1;i_++)
t[i_]=a[i_][j];
for(i=j+1;i<=n-1;i++)
{
//--- check
if(i>j+1)
{
//--- change value
v=0.0;
for(i_=j+1;i_<=i-1;i_++)
v+=a[i][i_]*t[i_];
}
else
v=0;
//--- check
if(nounit)
a[i].Set(j,v+a[i][i]*t[i]);
else
a[i].Set(j,v+t[i]);
}
//--- change values
for(i_=j+1;i_<=n-1;i_++)
a[i_].Set(j,ajj*a[i_][j]);
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| LU inverse |
//+------------------------------------------------------------------+
static bool CTestInverseUpdateUnit::InvMatLU(CMatrixDouble &a,int &pivots[],
const int n)
{
//--- create variables
bool result;
int i=0;
int j=0;
int jp=0;
double v=0;
int i_=0;
//--- create array
double work[];
//--- initialization
result=true;
//--- Quick return if possible
if(n==0)
{
//--- return result
return(result);
}
//--- allocation
ArrayResize(work,n);
//--- Form inv(U)
if(!InvMatTr(a,n,true,false))
{
//--- return result
return(false);
}
//--- Solve the equation inv(A)*L=inv(U) for inv(A).
for(j=n-1;j>=0;j--)
{
//--- Copy current column of L to WORK and replace with zeros.
for(i=j+1;i<=n-1;i++)
{
work[i]=a[i][j];
a[i].Set(j,0);
}
//--- Compute current column of inv(A).
if(j<n-1)
{
for(i=0;i<=n-1;i++)
{
//--- change value
v=0.0;
for(i_=j+1;i_<=n-1;i_++)
v+=a[i][i_]*work[i_];
a[i].Set(j,a[i][j]-v);
}
}
}
//--- Apply column interchanges.
for(j=n-2;j>=0;j--)
{
jp=pivots[j];
//--- check
if(jp!=j)
{
//--- copy
for(i_=0;i_<=n-1;i_++)
work[i_]=a[i_][j];
for(i_=0;i_<=n-1;i_++)
a[i_].Set(j,a[i_][jp]);
for(i_=0;i_<=n-1;i_++)
a[i_].Set(jp,work[i_]);
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Matrix inverse |
//+------------------------------------------------------------------+
static bool CTestInverseUpdateUnit::InvMat(CMatrixDouble &a,const int n)
{
//--- create array
int pivots[];
//--- function call
MatLU(a,n,n,pivots);
//--- return result
return(InvMatLU(a,pivots,n));
}
//+------------------------------------------------------------------+
//| Diff |
//+------------------------------------------------------------------+
static double CTestInverseUpdateUnit::MatrixDiff(CMatrixDouble &a,
CMatrixDouble &b,
const int m,const int n)
{
//--- create variables
double result=0;
int i=0;
int j=0;
//--- calculation
for(i=0;i<=m-1;i++)
{
for(j=0;j<=n-1;j++)
result=MathMax(result,MathAbs(b[i][j]-a[i][j]));
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Update and inverse |
//+------------------------------------------------------------------+
static bool CTestInverseUpdateUnit::UpdAndInv(CMatrixDouble &a,double &u[],
double &v[],const int n)
{
//--- create variables
int i=0;
double r=0;
int i_=0;
//--- create array
int pivots[];
//--- calculation
for(i=0;i<=n-1;i++)
{
r=u[i];
for(i_=0;i_<=n-1;i_++)
a[i].Set(i_,a[i][i_]+r*v[i_]);
}
//--- function call
MatLU(a,n,n,pivots);
//--- return result
return(InvMatLU(a,pivots,n));
}
//+------------------------------------------------------------------+
//| Testing class CSchur |
//+------------------------------------------------------------------+
class CTestSchurUnit
{
private:
//--- private methods
static void FillsParseA(CMatrixDouble &a,const int n,const double sparcity);
static void TestSchurProblem(CMatrixDouble &a,const int n,double &materr,double &orterr,bool &errstruct,bool &wfailed);
public:
//--- constructor, destructor
CTestSchurUnit(void);
~CTestSchurUnit(void);
//--- public method
static bool TestSchur(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestSchurUnit::CTestSchurUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestSchurUnit::~CTestSchurUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing Schur decomposition subroutine |
//+------------------------------------------------------------------+
static bool CTestSchurUnit::TestSchur(const bool silent)
{
//--- create variables
int n=0;
int maxn=0;
int i=0;
int j=0;
int pass=0;
int passcount=0;
bool waserrors;
bool errstruct;
bool wfailed;
double materr=0;
double orterr=0;
double threshold=0;
//--- create matrix
CMatrixDouble a;
//--- initialization
materr=0;
orterr=0;
errstruct=false;
wfailed=false;
waserrors=false;
maxn=70;
passcount=1;
threshold=5*100*CMath::m_machineepsilon;
//--- allocation
a.Resize(maxn,maxn);
//--- zero matrix,several cases
for(i=0;i<=maxn-1;i++)
{
for(j=0;j<=maxn-1;j++)
a[i].Set(j,0);
}
for(n=1;n<=maxn;n++)
{
//--- check
if(n>30 && n%2==0)
continue;
//--- function call
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
}
//--- Dense matrix
for(pass=1;pass<=passcount;pass++)
{
for(n=1;n<=maxn;n++)
{
//--- check
if(n>30 && n%2==0)
continue;
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
a[i].Set(j,2*CMath::RandomReal()-1);
}
//--- function call
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
}
}
//--- Sparse matrices,very sparse matrices,incredible sparse matrices
for(pass=1;pass<=1;pass++)
{
for(n=1;n<=maxn;n++)
{
//--- check
if(n>30 && n%3!=0)
continue;
//--- function calls
FillsParseA(a,n,0.8);
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
FillsParseA(a,n,0.9);
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
FillsParseA(a,n,0.95);
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
FillsParseA(a,n,0.997);
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
}
}
//--- report
waserrors=((materr>threshold || orterr>threshold) || errstruct) || wfailed;
//--- check
if(!silent)
{
Print("TESTING SCHUR DECOMPOSITION");
Print("Schur decomposition error: ");
Print("{0,5:E3}",materr);
Print("Schur orthogonality error: ");
Print("{0,5:E3}",orterr);
Print("T matrix structure: ");
//--- check
if(!errstruct)
Print("OK");
else
Print("FAILED");
Print("Always converged: ");
//--- check
if(!wfailed)
Print("OK");
else
Print("FAILED");
Print("Threshold: ");
Print("{0,5:E3}",threshold);
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestSchurUnit::FillsParseA(CMatrixDouble &a,const int n,
const double sparcity)
{
//--- create variables
int i=0;
int j=0;
//--- change values
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- check
if(CMath::RandomReal()>=sparcity)
a[i].Set(j,2*CMath::RandomReal()-1);
else
a[i].Set(j,0);
}
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestSchurUnit::TestSchurProblem(CMatrixDouble &a,const int n,
double &materr,double &orterr,
bool &errstruct,bool &wfailed)
{
//--- create variables
int i=0;
int j=0;
int k=0;
double v=0;
double locerr=0;
int i_=0;
//--- create arrays
double sr[];
double astc[];
double sastc[];
//--- create matrix
CMatrixDouble s;
CMatrixDouble t;
//--- allocation
ArrayResize(sr,n);
ArrayResize(astc,n);
ArrayResize(sastc,n);
//--- Schur decomposition,convergence test
t.Resize(n,n);
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
t[i].Set(j,a[i][j]);
}
//--- check
if(!CSchur::RMatrixSchur(t,n,s))
{
wfailed=true;
return;
}
//--- decomposition error
locerr=0;
for(j=0;j<=n-1;j++)
{
for(i_=0;i_<=n-1;i_++)
sr[i_]=s[j][i_];
//--- calculation
for(k=0;k<=n-1;k++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=t[k][i_]*sr[i_];
astc[k]=v;
}
//--- calculation
for(k=0;k<=n-1;k++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=s[k][i_]*astc[i_];
sastc[k]=v;
}
//--- search errors
for(k=0;k<=n-1;k++)
locerr=MathMax(locerr,MathAbs(sastc[k]-a[k][j]));
}
//--- search errors
materr=MathMax(materr,locerr);
//--- orthogonality error
locerr=0;
for(i=0;i<=n-1;i++)
{
for(j=0;j<=n-1;j++)
{
//--- change value
v=0.0;
for(i_=0;i_<=n-1;i_++)
v+=s[i_][i]*s[i_][j];
//--- check
if(i!=j)
locerr=MathMax(locerr,MathAbs(v));
else
locerr=MathMax(locerr,MathAbs(v-1));
}
}
//--- search errors
orterr=MathMax(orterr,locerr);
//--- T matrix structure
for(j=0;j<=n-1;j++)
{
for(i=j+2;i<=n-1;i++)
{
//--- check
if(t[i][j]!=0.0)
errstruct=true;
}
}
}
//+------------------------------------------------------------------+
//| Testing class CNlEq |
//+------------------------------------------------------------------+
class CTestNlEqUnit
{
private:
//--- private methods
static void TestFuncHBM(CNlEqState &state);
static void TestFuncHB1(CNlEqState &state);
static void TestFuncSHBM(CNlEqState &state);
public:
//--- constructor, destructor
CTestNlEqUnit(void);
~CTestNlEqUnit(void);
//--- public method
static bool TestNlEq(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestNlEqUnit::CTestNlEqUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestNlEqUnit::~CTestNlEqUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CNlEq |
//+------------------------------------------------------------------+
static bool CTestNlEqUnit::TestNlEq(const bool silent)
{
//--- create variables
bool waserrors;
bool basicserrors;
bool converror;
bool othererrors;
int n=0;
int i=0;
int k=0;
double v=0;
double flast=0;
bool firstrep;
int nfunc=0;
int njac=0;
int itcnt=0;
int pass=0;
int passcount=0;
double epsf=0;
double stpmax=0;
int i_=0;
//--- create arrays
double x[];
double xlast[];
//--- objects of classes
CNlEqState state;
CNlEqReport rep;
//--- initialization
waserrors=false;
basicserrors=false;
converror=false;
othererrors=false;
//--- Basic tests
//--- Test with Himmelblau's function (M):
//--- * ability to find correct result
//--- * ability to work after soft restart (restart after finish)
//--- * ability to work after hard restart (restart in the middle of optimization)
passcount=100;
for(pass=0;pass<=passcount-1;pass++)
{
//--- Ability to find correct result
ArrayResize(x,2);
x[0]=20*CMath::RandomReal()-10;
x[1]=20*CMath::RandomReal()-10;
//--- function call
CNlEq::NlEqCreateLM(2,2,x,state);
epsf=1.0E-9;
//--- function call
CNlEq::NlEqSetCond(state,epsf,0);
//--- cycle
while(CNlEq::NlEqIteration(state))
TestFuncHBM(state);
//--- function call
CNlEq::NlEqResults(state,x,rep);
//--- check
if(rep.m_terminationtype>0)
basicserrors=basicserrors || CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7)>CMath::Sqr(epsf);
else
basicserrors=true;
//--- Ability to work after soft restart
ArrayResize(x,2);
x[0]=20*CMath::RandomReal()-10;
x[1]=20*CMath::RandomReal()-10;
//--- function call
CNlEq::NlEqCreateLM(2,2,x,state);
epsf=1.0E-9;
//--- function call
CNlEq::NlEqSetCond(state,epsf,0);
//--- cycle
while(CNlEq::NlEqIteration(state))
TestFuncHBM(state);
//--- function call
CNlEq::NlEqResults(state,x,rep);
//--- allocation
ArrayResize(x,2);
//--- change values
x[0]=20*CMath::RandomReal()-10;
x[1]=20*CMath::RandomReal()-10;
//--- function call
CNlEq::NlEqRestartFrom(state,x);
//--- cycle
while(CNlEq::NlEqIteration(state))
TestFuncHBM(state);
//--- function call
CNlEq::NlEqResults(state,x,rep);
//--- check
if(rep.m_terminationtype>0)
basicserrors=basicserrors || CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7)>CMath::Sqr(epsf);
else
basicserrors=true;
//--- Ability to work after hard restart:
//--- * stopping condition: small F
//--- * StpMax is so small that we need about 10000 iterations to
//--- find solution (steps are small)
//--- * choose random K significantly less that 9999
//--- * iterate for some time,then break,restart optimization
ArrayResize(x,2);
x[0]=100;
x[1]=100;
//--- function call
CNlEq::NlEqCreateLM(2,2,x,state);
epsf=1.0E-9;
//--- function calls
CNlEq::NlEqSetCond(state,epsf,0);
CNlEq::NlEqSetStpMax(state,0.01);
k=1+CMath::RandomInteger(100);
//--- calculation
for(i=0;i<=k-1;i++)
{
//--- check
if(!CNlEq::NlEqIteration(state))
break;
TestFuncHBM(state);
}
//--- allocation
ArrayResize(x,2);
//--- change values
x[0]=20*CMath::RandomReal()-10;
x[1]=20*CMath::RandomReal()-10;
//--- function call
CNlEq::NlEqRestartFrom(state,x);
//--- cycle
while(CNlEq::NlEqIteration(state))
TestFuncHBM(state);
//--- function call
CNlEq::NlEqResults(state,x,rep);
//--- check
if(rep.m_terminationtype>0)
basicserrors=basicserrors || CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7)>CMath::Sqr(epsf);
else
basicserrors=true;
}
//--- Basic tests
//--- Test with Himmelblau's function (1):
//--- * ability to find correct result
passcount=100;
for(pass=0;pass<=passcount-1;pass++)
{
//--- Ability to find correct result
ArrayResize(x,2);
x[0]=20*CMath::RandomReal()-10;
x[1]=20*CMath::RandomReal()-10;
//--- function call
CNlEq::NlEqCreateLM(2,1,x,state);
epsf=1.0E-9;
//--- function call
CNlEq::NlEqSetCond(state,epsf,0);
//--- cycle
while(CNlEq::NlEqIteration(state))
TestFuncHB1(state);
//--- function call
CNlEq::NlEqResults(state,x,rep);
//--- check
if(rep.m_terminationtype>0)
basicserrors=basicserrors || CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7)>(double)(epsf);
else
basicserrors=true;
}
//--- Basic tests
//--- Ability to detect situation when we can't find minimum
passcount=100;
for(pass=0;pass<=passcount-1;pass++)
{
//--- allocation
ArrayResize(x,2);
x[0]=20*CMath::RandomReal()-10;
x[1]=20*CMath::RandomReal()-10;
//--- function call
CNlEq::NlEqCreateLM(2,3,x,state);
epsf=1.0E-9;
//--- function call
CNlEq::NlEqSetCond(state,epsf,0);
//--- cycle
while(CNlEq::NlEqIteration(state))
TestFuncSHBM(state);
//--- function call
CNlEq::NlEqResults(state,x,rep);
//--- search errors
basicserrors=basicserrors || rep.m_terminationtype!=-4;
}
//--- Test correctness of intermediate reports and final report:
//--- * first report is starting point
//--- * function value decreases on subsequent reports
//--- * function value is correctly reported
//--- * last report is final point
//--- * NFunc and NJac are compared with values counted directly
//--- * IterationsCount is compared with value counter directly
n=2;
ArrayResize(x,n);
ArrayResize(xlast,n);
//--- change values
x[0]=20*CMath::RandomReal()-10;
x[1]=20*CMath::RandomReal()-10;
xlast[0]=CMath::m_maxrealnumber;
xlast[1]=CMath::m_maxrealnumber;
//--- function calls
CNlEq::NlEqCreateLM(n,2,x,state);
CNlEq::NlEqSetCond(state,1.0E-6,0);
CNlEq::NlEqSetXRep(state,true);
//--- change values
firstrep=true;
flast=CMath::m_maxrealnumber;
nfunc=0;
njac=0;
itcnt=0;
//--- cycle
while(CNlEq::NlEqIteration(state))
{
//--- check
if(state.m_xupdated)
{
//--- first report must be starting point
if(firstrep)
{
for(i=0;i<=n-1;i++)
othererrors=othererrors || state.m_x[i]!=x[i];
firstrep=false;
}
//--- function value must decrease
othererrors=othererrors || state.m_f>flast;
//--- check correctness of function value
v=CMath::Sqr(state.m_x[0]*state.m_x[0]+state.m_x[1]-11)+CMath::Sqr(state.m_x[0]+state.m_x[1]*state.m_x[1]-7);
othererrors=othererrors || MathAbs(v-state.m_f)/MathMax(v,1)>100*CMath::m_machineepsilon;
//--- update info and continue
for(i_=0;i_<=n-1;i_++)
xlast[i_]=state.m_x[i_];
flast=state.m_f;
itcnt=itcnt+1;
continue;
}
//--- check
if(state.m_needf)
nfunc=nfunc+1;
//--- check
if(state.m_needfij)
{
nfunc=nfunc+1;
njac=njac+1;
}
//--- function call
TestFuncHBM(state);
}
//--- function call
CNlEq::NlEqResults(state,x,rep);
//--- check
if(rep.m_terminationtype>0)
{
othererrors=(othererrors || xlast[0]!=x[0]) || xlast[1]!=x[1];
v=CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7);
othererrors=othererrors || MathAbs(flast-v)/MathMax(v,1)>100*CMath::m_machineepsilon;
}
else
converror=true;
//--- search errors
othererrors=othererrors || rep.m_nfunc!=nfunc;
othererrors=othererrors || rep.m_njac!=njac;
othererrors=othererrors || rep.m_iterationscount!=itcnt-1;
//--- Test ability to set limit on algorithm steps
ArrayResize(x,2);
ArrayResize(xlast,2);
//--- change values
x[0]=20*CMath::RandomReal()+20;
x[1]=20*CMath::RandomReal()+20;
xlast[0]=x[0];
xlast[1]=x[1];
stpmax=0.1+0.1*CMath::RandomReal();
epsf=1.0E-9;
//--- function calls
CNlEq::NlEqCreateLM(2,3,x,state);
CNlEq::NlEqSetStpMax(state,stpmax);
CNlEq::NlEqSetCond(state,epsf,0);
CNlEq::NlEqSetXRep(state,true);
//--- cycle
while(CNlEq::NlEqIteration(state))
{
//--- check
if(state.m_needf || state.m_needfij)
TestFuncHBM(state);
//--- check
if((state.m_needf || state.m_needfij) || state.m_xupdated)
othererrors=othererrors || MathSqrt(CMath::Sqr(state.m_x[0]-xlast[0])+CMath::Sqr(state.m_x[1]-xlast[1]))>1.00001*stpmax;
//--- check
if(state.m_xupdated)
{
xlast[0]=state.m_x[0];
xlast[1]=state.m_x[1];
}
}
//--- end
waserrors=(basicserrors || converror) || othererrors;
//--- check
if(!silent)
{
Print("TESTING NLEQ SOLVER");
Print("BASIC FUNCTIONALITY: ");
//--- check
if(basicserrors)
Print("FAILED");
else
Print("OK");
Print("CONVERGENCE: ");
//--- check
if(converror)
Print("FAILED");
else
Print("OK");
Print("OTHER PROPERTIES: ");
//--- check
if(othererrors)
Print("FAILED");
else
Print("OK");
//--- check
if(waserrors)
Print("TEST FAILED");
else
Print("TEST PASSED");
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Himmelblau's function |
//| F=(x^2+y-11)^2 + (x+y^2-7)^2 |
//| posed as system of M functions: |
//| f0=x^2+y-11 |
//| f1=x+y^2-7 |
//+------------------------------------------------------------------+
static void CTestNlEqUnit::TestFuncHBM(CNlEqState &state)
{
//--- create variables
double x=0;
double y=0;
//--- check
if(!CAp::Assert(state.m_needf || state.m_needfij,"TestNLEQUnit: internal error!"))
return;
//--- change values
x=state.m_x[0];
y=state.m_x[1];
//--- check
if(state.m_needf)
{
state.m_f=CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7);
return;
}
//--- check
if(state.m_needfij)
{
state.m_fi[0]=x*x+y-11;
state.m_fi[1]=x+y*y-7;
state.m_j[0].Set(0,2*x);
state.m_j[0].Set(1,1);
state.m_j[1].Set(0,1);
state.m_j[1].Set(1,2*y);
//--- exit the function
return;
}
}
//+------------------------------------------------------------------+
//| Himmelblau's function |
//| F=(x^2+y-11)^2 + (x+y^2-7)^2 |
//| posed as system of 1 function |
//+------------------------------------------------------------------+
static void CTestNlEqUnit::TestFuncHB1(CNlEqState &state)
{
//--- create variables
double x=0;
double y=0;
//--- check
if(!CAp::Assert(state.m_needf || state.m_needfij,"TestNLEQUnit: internal error!"))
return;
//--- change values
x=state.m_x[0];
y=state.m_x[1];
//--- check
if(state.m_needf)
{
state.m_f=CMath::Sqr(CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7));
return;
}
//--- check
if(state.m_needfij)
{
state.m_fi[0]=CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7);
state.m_j[0].Set(0,2*(x*x+y-11)*2*x+2*(x+y*y-7));
state.m_j[0].Set(1,2*(x*x+y-11)+2*(x+y*y-7)*2*y);
//--- exit the function
return;
}
}
//+------------------------------------------------------------------+
//| Shifted Himmelblau's function |
//| F=(x^2+y-11)^2 + (x+y^2-7)^2 + 1 |
//| posed as system of M functions: |
//| f0=x^2+y-11 |
//| f1=x+y^2-7 |
//| f2=1 |
//| This function is used to test algorithm on problem which has no |
//| solution. |
//+------------------------------------------------------------------+
static void CTestNlEqUnit::TestFuncSHBM(CNlEqState &state)
{
//--- create variables
double x=0;
double y=0;
//--- check
if(!CAp::Assert(state.m_needf || state.m_needfij,"TestNLEQUnit: internal error!"))
return;
//--- change values
x=state.m_x[0];
y=state.m_x[1];
//--- check
if(state.m_needf)
{
state.m_f=CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7)+1;
return;
}
//--- check
if(state.m_needfij)
{
state.m_fi[0]=x*x+y-11;
state.m_fi[1]=x+y*y-7;
state.m_fi[2]=1;
state.m_j[0].Set(0,2*x);
state.m_j[0].Set(1,1);
state.m_j[1].Set(0,1);
state.m_j[1].Set(1,2*y);
state.m_j[2].Set(0,0);
state.m_j[2].Set(1,0);
//--- exit the function
return;
}
}
//+------------------------------------------------------------------+
//| Testing class CChebyshev |
//+------------------------------------------------------------------+
class CTestChebyshevUnit
{
public:
//--- constructor, destructor
CTestChebyshevUnit(void);
~CTestChebyshevUnit(void);
//--- public method
static bool TestChebyshev(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestChebyshevUnit::CTestChebyshevUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestChebyshevUnit::~CTestChebyshevUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CChebyshev |
//+------------------------------------------------------------------+
static bool CTestChebyshevUnit::TestChebyshev(const bool silent)
{
//--- create variables
double err=0;
double sumerr=0;
double cerr=0;
double ferr=0;
double threshold=0;
double x=0;
double v=0;
int pass=0;
int i=0;
int j=0;
int k=0;
int n=0;
int maxn=0;
bool waserrors;
int i_=0;
//--- create arrays
double c[];
double p1[];
double p2[];
//--- create matrix
CMatrixDouble a;
//--- initialization
err=0;
sumerr=0;
cerr=0;
ferr=0;
threshold=1.0E-9;
waserrors=false;
//--- Testing Chebyshev polynomials of the first kind
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,0,0.00)-1));
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,0,0.33)-1));
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,0,-0.42)-1));
x=0.2;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.2));
x=0.4;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.4));
x=0.6;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.6));
x=0.8;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.8));
x=1.0;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-1.0));
x=0.2;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)+0.92));
x=0.4;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)+0.68));
x=0.6;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)+0.28));
x=0.8;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)-0.28));
x=1.0;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)-1.00));
n=10;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,n,0.2)-0.4284556288));
n=11;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,n,0.2)+0.7996160205));
n=12;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,n,0.2)+0.7483020370));
//--- Testing Chebyshev polynomials of the second kind
n=0;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-1.0000000000));
n=1;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-0.4000000000));
n=2;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.8400000000));
n=3;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.7360000000));
n=4;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-0.5456000000));
n=10;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-0.6128946176));
n=11;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.6770370970));
n=12;
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.8837094564));
//--- Testing Clenshaw summation
maxn=20;
ArrayResize(c,maxn+1);
for(k=1;k<=2;k++)
{
for(pass=1;pass<=10;pass++)
{
x=2*CMath::RandomReal()-1;
v=0;
//--- calculation
for(n=0;n<=maxn;n++)
{
c[n]=2*CMath::RandomReal()-1;
v=v+CChebyshev::ChebyshevCalculate(k,n,x)*c[n];
//--- search errors
sumerr=MathMax(sumerr,MathAbs(v-CChebyshev::ChebyshevSum(c,k,n,x)));
}
}
}
//--- Testing coefficients
CChebyshev::ChebyshevCoefficients(0,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-1));
//--- function call
CChebyshev::ChebyshevCoefficients(1,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-0));
cerr=MathMax(cerr,MathAbs(c[1]-1));
//--- function call
CChebyshev::ChebyshevCoefficients(2,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]+1));
cerr=MathMax(cerr,MathAbs(c[1]-0));
cerr=MathMax(cerr,MathAbs(c[2]-2));
//--- function call
CChebyshev::ChebyshevCoefficients(3,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-0));
cerr=MathMax(cerr,MathAbs(c[1]+3));
cerr=MathMax(cerr,MathAbs(c[2]-0));
cerr=MathMax(cerr,MathAbs(c[3]-4));
//--- function call
CChebyshev::ChebyshevCoefficients(4,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-1));
cerr=MathMax(cerr,MathAbs(c[1]-0));
cerr=MathMax(cerr,MathAbs(c[2]+8));
cerr=MathMax(cerr,MathAbs(c[3]-0));
cerr=MathMax(cerr,MathAbs(c[4]-8));
//--- function call
CChebyshev::ChebyshevCoefficients(9,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-0));
cerr=MathMax(cerr,MathAbs(c[1]-9));
cerr=MathMax(cerr,MathAbs(c[2]-0));
cerr=MathMax(cerr,MathAbs(c[3]+120));
cerr=MathMax(cerr,MathAbs(c[4]-0));
cerr=MathMax(cerr,MathAbs(c[5]-432));
cerr=MathMax(cerr,MathAbs(c[6]-0));
cerr=MathMax(cerr,MathAbs(c[7]+576));
cerr=MathMax(cerr,MathAbs(c[8]-0));
cerr=MathMax(cerr,MathAbs(c[9]-256));
//--- Testing FromChebyshev
maxn=10;
a.Resize(maxn+1,maxn+1);
for(i=0;i<=maxn;i++)
{
for(j=0;j<=maxn;j++)
a[i].Set(j,0);
//--- function call
CChebyshev::ChebyshevCoefficients(i,c);
for(i_=0;i_<=i;i_++)
a[i].Set(i_,c[i_]);
}
//--- allocation
ArrayResize(c,maxn+1);
ArrayResize(p1,maxn+1);
//--- calculation
for(n=0;n<=maxn;n++)
{
for(pass=1;pass<=10;pass++)
{
for(i=0;i<=n;i++)
p1[i]=0;
for(i=0;i<=n;i++)
{
//--- change values
c[i]=2*CMath::RandomReal()-1;
v=c[i];
for(i_=0;i_<=i;i_++)
p1[i_]=p1[i_]+v*a[i][i_];
}
//--- function call
CChebyshev::FromChebyshev(c,n,p2);
for(i=0;i<=n;i++)
ferr=MathMax(ferr,MathAbs(p1[i]-p2[i]));
}
}
//--- Reporting
waserrors=((err>threshold || sumerr>threshold) || cerr>threshold) || ferr>threshold;
//--- check
if(!silent)
{
Print("TESTING CALCULATION OF THE CHEBYSHEV POLYNOMIALS");
Print("Max error against table ");
Print("{0,5:E2}",err);
Print("Summation error ");
Print("{0,5:E2}",sumerr);
Print("Coefficients error ");
Print("{0,5:E2}",cerr);
Print("FrobChebyshev error ");
Print("{0,5:E2}",ferr);
Print("Threshold ");
Print("{0,5:E2}",threshold);
//--- check
if(!waserrors)
Print("TEST PASSED");
else
Print("TEST FAILED");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CHermite |
//+------------------------------------------------------------------+
class CTestHermiteUnit
{
public:
//--- constructor, destructor
CTestHermiteUnit(void);
~CTestHermiteUnit(void);
//--- method
static bool TestHermite(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestHermiteUnit::CTestHermiteUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestHermiteUnit::~CTestHermiteUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CHermite |
//+------------------------------------------------------------------+
static bool CTestHermiteUnit::TestHermite(const bool silent)
{
//--- create variables
double err=0;
double sumerr=0;
double cerr=0;
double threshold=0;
int n=0;
int maxn=0;
int pass=0;
double x=0;
double v=0;
bool waserrors;
//--- create array
double c[];
//--- initialization
err=0;
sumerr=0;
cerr=0;
threshold=1.0E-9;
waserrors=false;
//--- Testing Hermite polynomials
n=0;
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-1));
n=1;
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-2));
n=2;
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-2));
n=3;
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)+4));
n=4;
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)+20));
n=5;
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)+8));
n=6;
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-184));
n=7;
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-464));
n=11;
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-230848));
n=12;
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-280768));
//--- Testing Clenshaw summation
maxn=10;
ArrayResize(c,maxn+1);
for(pass=1;pass<=10;pass++)
{
x=2*CMath::RandomReal()-1;
v=0;
//--- calculation
for(n=0;n<=maxn;n++)
{
c[n]=2*CMath::RandomReal()-1;
v=v+CHermite::HermiteCalculate(n,x)*c[n];
//--- search errors
sumerr=MathMax(sumerr,MathAbs(v-CHermite::HermiteSum(c,n,x)));
}
}
//--- Testing coefficients
CHermite::HermiteCoefficients(0,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-1));
//--- function call
CHermite::HermiteCoefficients(1,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-0));
cerr=MathMax(cerr,MathAbs(c[1]-2));
//--- function call
CHermite::HermiteCoefficients(2,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]+2));
cerr=MathMax(cerr,MathAbs(c[1]-0));
cerr=MathMax(cerr,MathAbs(c[2]-4));
//--- function call
CHermite::HermiteCoefficients(3,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-0));
cerr=MathMax(cerr,MathAbs(c[1]+12));
cerr=MathMax(cerr,MathAbs(c[2]-0));
cerr=MathMax(cerr,MathAbs(c[3]-8));
//--- function call
CHermite::HermiteCoefficients(4,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-12));
cerr=MathMax(cerr,MathAbs(c[1]-0));
cerr=MathMax(cerr,MathAbs(c[2]+48));
cerr=MathMax(cerr,MathAbs(c[3]-0));
cerr=MathMax(cerr,MathAbs(c[4]-16));
//--- function call
CHermite::HermiteCoefficients(5,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-0));
cerr=MathMax(cerr,MathAbs(c[1]-120));
cerr=MathMax(cerr,MathAbs(c[2]-0));
cerr=MathMax(cerr,MathAbs(c[3]+160));
cerr=MathMax(cerr,MathAbs(c[4]-0));
cerr=MathMax(cerr,MathAbs(c[5]-32));
//--- function call
CHermite::HermiteCoefficients(6,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]+120));
cerr=MathMax(cerr,MathAbs(c[1]-0));
cerr=MathMax(cerr,MathAbs(c[2]-720));
cerr=MathMax(cerr,MathAbs(c[3]-0));
cerr=MathMax(cerr,MathAbs(c[4]+480));
cerr=MathMax(cerr,MathAbs(c[5]-0));
cerr=MathMax(cerr,MathAbs(c[6]-64));
//--- Reporting
waserrors=(err>threshold || sumerr>threshold) || cerr>threshold;
//--- check
if(!silent)
{
Print("TESTING CALCULATION OF THE HERMITE POLYNOMIALS");
Print("Max error ");
Print("{0,5:E2}",err);
Print("Summation error ");
Print("{0,5:E2}",sumerr);
Print("Coefficients error ");
Print("{0,5:E2}",cerr);
Print("Threshold ");
Print("{0,5:E2}",threshold);
//--- check
if(!waserrors)
Print("TEST PASSED");
else
Print("TEST FAILED");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CLaguerre |
//+------------------------------------------------------------------+
class CTestLaguerreUnit
{
public:
//--- constructor, destructor
CTestLaguerreUnit(void);
~CTestLaguerreUnit(void);
//--- public method
static bool TestLaguerre(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestLaguerreUnit::CTestLaguerreUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestLaguerreUnit::~CTestLaguerreUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CLaguerre |
//+------------------------------------------------------------------+
static bool CTestLaguerreUnit::TestLaguerre(const bool silent)
{
//--- create variables
double err=0;
double sumerr=0;
double cerr=0;
double threshold=0;
int n=0;
int maxn=0;
int pass=0;
double x=0;
double v=0;
bool waserrors;
//--- create array
double c[];
//--- initialization
err=0;
sumerr=0;
cerr=0;
threshold=1.0E-9;
waserrors=false;
//--- Testing Laguerre polynomials
n=0;
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)-1.0000000000));
n=1;
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)-0.5000000000));
n=2;
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)-0.1250000000));
n=3;
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.1458333333));
n=4;
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.3307291667));
n=5;
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.4455729167));
n=6;
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.5041449653));
n=7;
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.5183392237));
n=8;
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.4983629984));
n=9;
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.4529195204));
n=10;
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.3893744141));
n=11;
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.3139072988));
n=12;
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.2316496389));
//--- Testing Clenshaw summation
maxn=20;
ArrayResize(c,maxn+1);
for(pass=1;pass<=10;pass++)
{
x=2*CMath::RandomReal()-1;
v=0;
//--- calculation
for(n=0;n<=maxn;n++)
{
c[n]=2*CMath::RandomReal()-1;
v=v+CLaguerre::LaguerreCalculate(n,x)*c[n];
//--- search errors
sumerr=MathMax(sumerr,MathAbs(v-CLaguerre::LaguerreSum(c,n,x)));
}
}
//--- Testing coefficients
CLaguerre::LaguerreCoefficients(0,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-1));
//--- function call
CLaguerre::LaguerreCoefficients(1,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-1));
cerr=MathMax(cerr,MathAbs(c[1]+1));
//--- function call
CLaguerre::LaguerreCoefficients(2,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-2.0/2.0));
cerr=MathMax(cerr,MathAbs(c[1]+4.0/2.0));
cerr=MathMax(cerr,MathAbs(c[2]-1.0/2.0));
//--- function call
CLaguerre::LaguerreCoefficients(3,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-6.0/6.0));
cerr=MathMax(cerr,MathAbs(c[1]+18.0/6.0));
cerr=MathMax(cerr,MathAbs(c[2]-9.0/6.0));
cerr=MathMax(cerr,MathAbs(c[3]+1.0/6.0));
//--- function call
CLaguerre::LaguerreCoefficients(4,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-24.0/24.0));
cerr=MathMax(cerr,MathAbs(c[1]+96.0/24.0));
cerr=MathMax(cerr,MathAbs(c[2]-72.0/24.0));
cerr=MathMax(cerr,MathAbs(c[3]+16.0/24.0));
cerr=MathMax(cerr,MathAbs(c[4]-1.0/24.0));
//--- function call
CLaguerre::LaguerreCoefficients(5,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-120.0/120.0));
cerr=MathMax(cerr,MathAbs(c[1]+600.0/120.0));
cerr=MathMax(cerr,MathAbs(c[2]-600.0/120.0));
cerr=MathMax(cerr,MathAbs(c[3]+200.0/120.0));
cerr=MathMax(cerr,MathAbs(c[4]-25.0/120.0));
cerr=MathMax(cerr,MathAbs(c[5]+1.0/120.0));
//--- function call
CLaguerre::LaguerreCoefficients(6,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-720.0/720.0));
cerr=MathMax(cerr,MathAbs(c[1]+4320.0/720.0));
cerr=MathMax(cerr,MathAbs(c[2]-5400.0/720.0));
cerr=MathMax(cerr,MathAbs(c[3]+2400.0/720.0));
cerr=MathMax(cerr,MathAbs(c[4]-450.0/720.0));
cerr=MathMax(cerr,MathAbs(c[5]+36.0/720.0));
cerr=MathMax(cerr,MathAbs(c[6]-1.0/720.0));
//--- Reporting
waserrors=(err>threshold || sumerr>threshold) || cerr>threshold;
//--- check
if(!silent)
{
Print("TESTING CALCULATION OF THE LAGUERRE POLYNOMIALS");
Print("Max error ");
Print("{0,5:E2}",err);
Print("Summation error ");
Print("{0,5:E2}",sumerr);
Print("Coefficients error ");
Print("{0,5:E2}",cerr);
Print("Threshold ");
Print("{0,5:E2}",threshold);
//--- check
if(!waserrors)
Print("TEST PASSED");
else
Print("TEST FAILED");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CLegendre |
//+------------------------------------------------------------------+
class CTestLegendreUnit
{
public:
//--- constructor, destructor
CTestLegendreUnit(void);
~CTestLegendreUnit(void);
//--- public method
static bool TestLegendre(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestLegendreUnit::CTestLegendreUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestLegendreUnit::~CTestLegendreUnit(void)
{
}
//+------------------------------------------------------------------+
//| Testing class CLegendre |
//+------------------------------------------------------------------+
static bool CTestLegendreUnit::TestLegendre(const bool silent)
{
//--- create variables
double err=0;
double sumerr=0;
double cerr=0;
double threshold=0;
int n=0;
int maxn=0;
int i=0;
int pass=0;
double x=0;
double v=0;
double t=0;
bool waserrors;
//--- create array
double c[];
//--- initialization
err=0;
sumerr=0;
cerr=0;
threshold=1.0E-9;
waserrors=false;
//--- Testing Legendre polynomials values
for(n=0;n<=10;n++)
{
//--- function call
CLegendre::LegendreCoefficients(n,c);
for(pass=1;pass<=10;pass++)
{
//--- calculation
x=2*CMath::RandomReal()-1;
v=CLegendre::LegendreCalculate(n,x);
t=1;
for(i=0;i<=n;i++)
{
v=v-c[i]*t;
t=t*x;
}
//--- search errors
err=MathMax(err,MathAbs(v));
}
}
//--- Testing Clenshaw summation
maxn=20;
ArrayResize(c,maxn+1);
for(pass=1;pass<=10;pass++)
{
//--- change values
x=2*CMath::RandomReal()-1;
v=0;
for(n=0;n<=maxn;n++)
{
c[n]=2*CMath::RandomReal()-1;
v=v+CLegendre::LegendreCalculate(n,x)*c[n];
//--- search errors
sumerr=MathMax(sumerr,MathAbs(v-CLegendre::LegendreSum(c,n,x)));
}
}
//--- Testing coefficients
CLegendre::LegendreCoefficients(0,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-1));
//--- calculation
CLegendre::LegendreCoefficients(1,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-0));
cerr=MathMax(cerr,MathAbs(c[1]-1));
//--- calculation
CLegendre::LegendreCoefficients(2,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]+1.0/2.0));
cerr=MathMax(cerr,MathAbs(c[1]-0));
cerr=MathMax(cerr,MathAbs(c[2]-3.0/2.0));
//--- calculation
CLegendre::LegendreCoefficients(3,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-0));
cerr=MathMax(cerr,MathAbs(c[1]+3.0/2.0));
cerr=MathMax(cerr,MathAbs(c[2]-0));
cerr=MathMax(cerr,MathAbs(c[3]-5.0/2.0));
//--- calculation
CLegendre::LegendreCoefficients(4,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-3.0/8.0));
cerr=MathMax(cerr,MathAbs(c[1]-0));
cerr=MathMax(cerr,MathAbs(c[2]+30.0/8.0));
cerr=MathMax(cerr,MathAbs(c[3]-0));
cerr=MathMax(cerr,MathAbs(c[4]-35.0/8.0));
//--- calculation
CLegendre::LegendreCoefficients(9,c);
//--- search errors
cerr=MathMax(cerr,MathAbs(c[0]-0));
cerr=MathMax(cerr,MathAbs(c[1]-315.0/128.0));
cerr=MathMax(cerr,MathAbs(c[2]-0));
cerr=MathMax(cerr,MathAbs(c[3]+4620.0/128.0));
cerr=MathMax(cerr,MathAbs(c[4]-0));
cerr=MathMax(cerr,MathAbs(c[5]-18018.0/128.0));
cerr=MathMax(cerr,MathAbs(c[6]-0));
cerr=MathMax(cerr,MathAbs(c[7]+25740.0/128.0));
cerr=MathMax(cerr,MathAbs(c[8]-0));
cerr=MathMax(cerr,MathAbs(c[9]-12155.0/128.0));
//--- Reporting
waserrors=(err>threshold || sumerr>threshold) || cerr>threshold;
//--- check
if(!silent)
{
Print("TESTING CALCULATION OF THE LEGENDRE POLYNOMIALS");
Print("Max error ");
Print("{0,5:E2}",err);
Print("Summation error ");
Print("{0,5:E2}",sumerr);
Print("Coefficients error ");
Print("{0,5:E2}",cerr);
Print("Threshold ");
Print("{0,5:E2}",threshold);
//--- check
if(!waserrors)
Print("TEST PASSED");
else
Print("TEST FAILED");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary class |
//+------------------------------------------------------------------+
class CRec1
{
public:
//--- class variables
bool m_bfield;
double m_rfield;
int m_ifield;
complex m_cfield;
//--- arrays
bool m_b1field[];
double m_r1field[];
int m_i1field[];
complex m_c1field[];
//--- matrix
CMatrixInt m_b2field;
CMatrixDouble m_r2field;
CMatrixInt m_i2field;
CMatrixComplex m_c2field;
//--- constructor, destructor
CRec1(void);
~CRec1(void);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CRec1::CRec1(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CRec1::~CRec1(void)
{
}
//+------------------------------------------------------------------+
//| The auxiliary class |
//+------------------------------------------------------------------+
class CRec4Serialization
{
public:
//--- arrays
bool m_b[];
int m_i[];
double m_r[];
//--- constructor, destructor
CRec4Serialization(void);
~CRec4Serialization(void);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CRec4Serialization::CRec4Serialization(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CRec4Serialization::~CRec4Serialization(void)
{
}
//+------------------------------------------------------------------+
//| Testing the basic functions |
//+------------------------------------------------------------------+
class CTestAlglibBasicsUnit
{
private:
//--- private methods
static bool TestComplexArithmetics(const bool silent);
static bool TestIEEESpecial(const bool silent);
static bool TestSwapFunctions(const bool silent);
static bool TestSerializationFunctions(const bool silent);
public:
//--- constructor, destructor
CTestAlglibBasicsUnit(void);
~CTestAlglibBasicsUnit(void);
//--- public methods
static void Rec4SerializationAlloc(CSerializer &s,CRec4Serialization &v);
static void Rec4SerializationSerialize(CSerializer &s,CRec4Serialization &v);
static void Rec4SerializationUnserialize(CSerializer &s,CRec4Serialization &v);
static bool TestAlglibBasics(const bool silent);
};
//+------------------------------------------------------------------+
//| Constructor without parameters |
//+------------------------------------------------------------------+
CTestAlglibBasicsUnit::CTestAlglibBasicsUnit(void)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CTestAlglibBasicsUnit::~CTestAlglibBasicsUnit(void)
{
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestAlglibBasicsUnit::Rec4SerializationAlloc(CSerializer &s,
CRec4Serialization &v)
{
//--- create a variable
int i=0;
//--- boolean fields
s.Alloc_Entry();
for(i=0;i<=CAp::Len(v.m_b)-1;i++)
s.Alloc_Entry();
//--- integer fields
s.Alloc_Entry();
for(i=0;i<=CAp::Len(v.m_i)-1;i++)
s.Alloc_Entry();
//--- real fields
s.Alloc_Entry();
for(i=0;i<=CAp::Len(v.m_r)-1;i++)
s.Alloc_Entry();
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestAlglibBasicsUnit::Rec4SerializationSerialize(CSerializer &s,
CRec4Serialization &v)
{
//--- create a variable
int i=0;
//--- boolean fields
s.Serialize_Int(CAp::Len(v.m_b));
for(i=0;i<=CAp::Len(v.m_b)-1;i++)
s.Serialize_Bool(v.m_b[i]);
//--- integer fields
s.Serialize_Int(CAp::Len(v.m_i));
for(i=0;i<=CAp::Len(v.m_i)-1;i++)
s.Serialize_Int(v.m_i[i]);
//--- real fields
s.Serialize_Int(CAp::Len(v.m_r));
for(i=0;i<=CAp::Len(v.m_r)-1;i++)
s.Serialize_Double(v.m_r[i]);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
static void CTestAlglibBasicsUnit::Rec4SerializationUnserialize(CSerializer &s,
CRec4Serialization &v)
{
//--- create variables
int i=0;
int k=0;
bool bv;
int iv=0;
double rv=0;
//--- boolean fields
k=s.Unserialize_Int();
//--- check
if(k>0)
{
//--- allocation
ArrayResize(v.m_b,k);
for(i=0;i<=k-1;i++)
{
bv=s.Unserialize_Bool();
v.m_b[i]=bv;
}
}
//--- integer fields
k=s.Unserialize_Int();
//--- check
if(k>0)
{
//--- allocation
ArrayResize(v.m_i,k);
for(i=0;i<=k-1;i++)
{
iv=s.Unserialize_Int();
v.m_i[i]=iv;
}
}
//--- real fields
k=s.Unserialize_Int();
//--- check
if(k>0)
{
//--- allocation
ArrayResize(v.m_r,k);
for(i=0;i<=k-1;i++)
{
rv=s.Unserialize_Double();
v.m_r[i]=rv;
}
}
}
//+------------------------------------------------------------------+
//| Testing the basic functions |
//+------------------------------------------------------------------+
static bool CTestAlglibBasicsUnit::TestAlglibBasics(const bool silent)
{
//--- create a variable
bool result;
//--- function calls
result=true;
result=result && TestComplexArithmetics(silent);
result=result && TestIEEESpecial(silent);
result=result && TestSwapFunctions(silent);
result=result && TestSerializationFunctions(silent);
//--- check
if(!silent)
Print("");
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Complex arithmetics test |
//+------------------------------------------------------------------+
static bool CTestAlglibBasicsUnit::TestComplexArithmetics(const bool silent)
{
//--- create variables
bool result;
bool absc;
bool addcc;
bool addcr;
bool addrc;
bool subcc;
bool subcr;
bool subrc;
bool mulcc;
bool mulcr;
bool mulrc;
bool divcc;
bool divcr;
bool divrc;
complex ca=0;
complex cb=0;
complex res=0;
double ra=0;
double rb=0;
double threshold=0;
int pass=0;
int passcount=0;
//--- initialization
threshold=100*CMath::m_machineepsilon;
passcount=1000;
result=true;
absc=true;
addcc=true;
addcr=true;
addrc=true;
subcc=true;
subcr=true;
subrc=true;
mulcc=true;
mulcr=true;
mulrc=true;
divcc=true;
divcr=true;
divrc=true;
//--- calculation
for(pass=1;pass<=passcount;pass++)
{
//--- Test AbsC
ca.re=2*CMath::RandomReal()-1;
ca.im=2*CMath::RandomReal()-1;
ra=CMath::AbsComplex(ca);
absc=absc && MathAbs(ra-MathSqrt(CMath::Sqr(ca.re)+CMath::Sqr(ca.im)))<threshold;
//--- test Add
ca.re=2*CMath::RandomReal()-1;
ca.im=2*CMath::RandomReal()-1;
cb.re=2*CMath::RandomReal()-1;
cb.im=2*CMath::RandomReal()-1;
ra=2*CMath::RandomReal()-1;
rb=2*CMath::RandomReal()-1;
res=ca+cb;
addcc=(addcc && MathAbs(res.re-ca.re-cb.re)<threshold) && MathAbs(res.im-ca.im-cb.im)<threshold;
res=ca+rb;
addcr=(addcr && MathAbs(res.re-ca.re-rb)<threshold) && MathAbs(res.im-ca.im)<threshold;
res=cb+ra;
addrc=(addrc && MathAbs(res.re-ra-cb.re)<threshold) && MathAbs(res.im-cb.im)<threshold;
//--- test Sub
ca.re=2*CMath::RandomReal()-1;
ca.im=2*CMath::RandomReal()-1;
cb.re=2*CMath::RandomReal()-1;
cb.im=2*CMath::RandomReal()-1;
ra=2*CMath::RandomReal()-1;
rb=2*CMath::RandomReal()-1;
res=ca-cb;
subcc=(subcc && MathAbs(res.re-(ca.re-cb.re))<threshold) && MathAbs(res.im-(ca.im-cb.im))<threshold;
res=ca-rb;
subcr=(subcr && MathAbs(res.re-(ca.re-rb))<threshold) && MathAbs(res.im-ca.im)<threshold;
res=-cb+ra;
subrc=(subrc && MathAbs(res.re-(ra-cb.re))<threshold) && MathAbs(res.im+cb.im)<threshold;
//--- test Mul
ca.re=2*CMath::RandomReal()-1;
ca.im=2*CMath::RandomReal()-1;
cb.re=2*CMath::RandomReal()-1;
cb.im=2*CMath::RandomReal()-1;
ra=2*CMath::RandomReal()-1;
rb=2*CMath::RandomReal()-1;
res=ca*cb;
mulcc=(mulcc && MathAbs(res.re-(ca.re*cb.re-ca.im*cb.im))<threshold) && MathAbs(res.im-(ca.re*cb.im+ca.im*cb.re))<threshold;
res=ca*rb;
mulcr=(mulcr && MathAbs(res.re-ca.re*rb)<threshold) && MathAbs(res.im-ca.im*rb)<threshold;
res=cb*ra;
mulrc=(mulrc && MathAbs(res.re-ra*cb.re)<threshold) && MathAbs(res.im-ra*cb.im)<threshold;
//--- test Div
ca.re=2*CMath::RandomReal()-1;
ca.im=2*CMath::RandomReal()-1;
do
{
cb.re=2*CMath::RandomReal()-1;
cb.im=2*CMath::RandomReal()-1;
}
while(CMath::AbsComplex(cb)<=0.5);
ra=2*CMath::RandomReal()-1;
do
{
rb=2*CMath::RandomReal()-1;
}
while(MathAbs(rb)<=0.5);
res=ca/cb;
divcc=(divcc && MathAbs((res*cb).re-ca.re)<threshold) && MathAbs((res*cb).im-ca.im)<threshold;
res=ca/rb;
divcr=(divcr && MathAbs(res.re-ca.re/rb)<threshold) && MathAbs(res.im-ca.im/rb)<threshold;
complex cra=ra;
res=cra/cb;
divrc=(divrc && MathAbs((res*cb).re-ra)<threshold) && MathAbs((res*cb).im)<threshold;
}
//--- summary
result=result && absc;
result=result && addcc;
result=result && addcr;
result=result && addrc;
result=result && subcc;
result=result && subcr;
result=result && subrc;
result=result && mulcc;
result=result && mulcr;
result=result && mulrc;
result=result && divcc;
result=result && divcr;
result=result && divrc;
//--- check
if(!silent)
{
//--- check
if(result)
Print("COMPLEX ARITHMETICS: OK");
else
{
Print("COMPLEX ARITHMETICS: FAILED");
Print("* AddCC ");
//--- check
if(addcc)
Print("OK");
else
Print("FAILED");
Print("* AddCR ");
//--- check
if(addcr)
Print("OK");
else
Print("FAILED");
Print("* AddRC ");
//--- check
if(addrc)
Print("OK");
else
Print("FAILED");
Print("* SubCC ");
//--- check
if(subcc)
Print("OK");
else
Print("FAILED");
Print("* SubCR ");
//--- check
if(subcr)
Print("OK");
else
Print("FAILED");
Print("* SubRC ");
//--- check
if(subrc)
Print("OK");
else
Print("FAILED");
Print("* MulCC ");
//--- check
if(mulcc)
Print("OK");
else
Print("FAILED");
Print("* MulCR ");
//--- check
if(mulcr)
Print("OK");
else
Print("FAILED");
Print("* MulRC ");
//--- check
if(mulrc)
Print("OK");
else
Print("FAILED");
Print("* DivCC ");
//--- check
if(divcc)
Print("OK");
else
Print("FAILED");
Print("* DivCR ");
//--- check
if(divcr)
Print("OK");
else
Print("FAILED");
Print("* DivRC ");
//--- check
if(divrc)
Print("OK");
else
Print("FAILED");
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Tests for IEEE special quantities |
//+------------------------------------------------------------------+
static bool CTestAlglibBasicsUnit::TestIEEESpecial(const bool silent)
{
//--- create variables
bool result;
bool oknan;
bool okinf;
bool okother;
double v1=0;
double v2=0;
//--- initialization
result=true;
oknan=true;
okinf=true;
okother=true;
//--- Test classification functions
okother=okother && !CInfOrNaN::IsInfinity(CInfOrNaN::NaN());
okother=okother && CInfOrNaN::IsInfinity(CInfOrNaN::PositiveInfinity());
okother=okother && !CInfOrNaN::IsInfinity(CMath::m_maxrealnumber);
okother=okother && !CInfOrNaN::IsInfinity(1.0);
okother=okother && !CInfOrNaN::IsInfinity(CMath::m_minrealnumber);
okother=okother && !CInfOrNaN::IsInfinity(0.0);
okother=okother && !CInfOrNaN::IsInfinity(-CMath::m_minrealnumber);
okother=okother && !CInfOrNaN::IsInfinity(-1.0);
okother=okother && !CInfOrNaN::IsInfinity(-CMath::m_maxrealnumber);
okother=okother && CInfOrNaN::IsInfinity(CInfOrNaN::NegativeInfinity());
okother=okother && !CInfOrNaN::IsPositiveInfinity(CInfOrNaN::NaN());
okother=okother && CInfOrNaN::IsPositiveInfinity(CInfOrNaN::PositiveInfinity());
okother=okother && !CInfOrNaN::IsPositiveInfinity(CMath::m_maxrealnumber);
okother=okother && !CInfOrNaN::IsPositiveInfinity(1.0);
okother=okother && !CInfOrNaN::IsPositiveInfinity(CMath::m_minrealnumber);
okother=okother && !CInfOrNaN::IsPositiveInfinity(0.0);
okother=okother && !CInfOrNaN::IsPositiveInfinity(-CMath::m_minrealnumber);
okother=okother && !CInfOrNaN::IsPositiveInfinity(-1.0);
okother=okother && !CInfOrNaN::IsPositiveInfinity(-CMath::m_maxrealnumber);
okother=okother && !CInfOrNaN::IsPositiveInfinity(CInfOrNaN::NegativeInfinity());
okother=okother && !CInfOrNaN::IsNegativeInfinity(CInfOrNaN::NaN());
okother=okother && !CInfOrNaN::IsNegativeInfinity(CInfOrNaN::PositiveInfinity());
okother=okother && !CInfOrNaN::IsNegativeInfinity(CMath::m_maxrealnumber);
okother=okother && !CInfOrNaN::IsNegativeInfinity(1.0);
okother=okother && !CInfOrNaN::IsNegativeInfinity(CMath::m_minrealnumber);
okother=okother && !CInfOrNaN::IsNegativeInfinity(0.0);
okother=okother && !CInfOrNaN::IsNegativeInfinity(-CMath::m_minrealnumber);
okother=okother && !CInfOrNaN::IsNegativeInfinity(-1.0);
okother=okother && !CInfOrNaN::IsNegativeInfinity(-CMath::m_maxrealnumber);
okother=okother && CInfOrNaN::IsNegativeInfinity(CInfOrNaN::NegativeInfinity());
okother=okother && CInfOrNaN::IsNaN(CInfOrNaN::NaN());
okother=okother && !CInfOrNaN::IsNaN(CInfOrNaN::PositiveInfinity());
okother=okother && !CInfOrNaN::IsNaN(CMath::m_maxrealnumber);
okother=okother && !CInfOrNaN::IsNaN(1.0);
okother=okother && !CInfOrNaN::IsNaN(CMath::m_minrealnumber);
okother=okother && !CInfOrNaN::IsNaN(0.0);
okother=okother && !CInfOrNaN::IsNaN(-CMath::m_minrealnumber);
okother=okother && !CInfOrNaN::IsNaN(-1.0);
okother=okother && !CInfOrNaN::IsNaN(-CMath::m_maxrealnumber);
okother=okother && !CInfOrNaN::IsNaN(CInfOrNaN::NegativeInfinity());
okother=okother && !CMath::IsFinite(CInfOrNaN::NaN());
okother=okother && !CMath::IsFinite(CInfOrNaN::PositiveInfinity());
okother=okother && CMath::IsFinite(CMath::m_maxrealnumber);
okother=okother && CMath::IsFinite(1.0);
okother=okother && CMath::IsFinite(CMath::m_minrealnumber);
okother=okother && CMath::IsFinite(0.0);
okother=okother && CMath::IsFinite(-CMath::m_minrealnumber);
okother=okother && CMath::IsFinite(-1.0);
okother=okother && CMath::IsFinite(-CMath::m_maxrealnumber);
okother=okother && !CMath::IsFinite(CInfOrNaN::NegativeInfinity());
//--- Test NAN
v1=CInfOrNaN::NaN();
oknan=oknan && CInfOrNaN::IsNaN(v1);
//--- Test INF:
//--- * basic properties
//--- * comparisons involving PosINF on one of the sides
//--- * comparisons involving NegINF on one of the sides
v1=CInfOrNaN::PositiveInfinity();
v2=CInfOrNaN::NegativeInfinity();
okinf=okinf && CInfOrNaN::IsInfinity(CInfOrNaN::PositiveInfinity());
okinf=okinf && CInfOrNaN::IsInfinity(v1);
okinf=okinf && CInfOrNaN::IsInfinity(CInfOrNaN::NegativeInfinity());
okinf=okinf && CInfOrNaN::IsInfinity(v2);
okinf=okinf && CInfOrNaN::IsPositiveInfinity(CInfOrNaN::PositiveInfinity());
okinf=okinf && CInfOrNaN::IsPositiveInfinity(v1);
okinf=okinf && !CInfOrNaN::IsPositiveInfinity(CInfOrNaN::NegativeInfinity());
okinf=okinf && !CInfOrNaN::IsPositiveInfinity(v2);
okinf=okinf && !CInfOrNaN::IsNegativeInfinity(CInfOrNaN::PositiveInfinity());
okinf=okinf && !CInfOrNaN::IsNegativeInfinity(v1);
okinf=okinf && CInfOrNaN::IsNegativeInfinity(CInfOrNaN::NegativeInfinity());
okinf=okinf && CInfOrNaN::IsNegativeInfinity(v2);
okinf=okinf && CInfOrNaN::PositiveInfinity()==CInfOrNaN::PositiveInfinity();
okinf=okinf && CInfOrNaN::PositiveInfinity()==v1;
okinf=okinf && !(CInfOrNaN::PositiveInfinity()==CInfOrNaN::NegativeInfinity());
okinf=okinf && !(CInfOrNaN::PositiveInfinity()==v2);
okinf=okinf && !(CInfOrNaN::PositiveInfinity()==0.0);
okinf=okinf && !(CInfOrNaN::PositiveInfinity()==1.2);
okinf=okinf && !(CInfOrNaN::PositiveInfinity()==-1.2);
okinf=okinf && v1==CInfOrNaN::PositiveInfinity();
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==CInfOrNaN::PositiveInfinity());
okinf=okinf && !(v2==CInfOrNaN::PositiveInfinity());
okinf=okinf && !(0.0==CInfOrNaN::PositiveInfinity());
okinf=okinf && !(1.2==CInfOrNaN::PositiveInfinity());
okinf=okinf && !(-1.2==CInfOrNaN::PositiveInfinity());
okinf=okinf && !(CInfOrNaN::PositiveInfinity()!=CInfOrNaN::PositiveInfinity());
okinf=okinf && !(CInfOrNaN::PositiveInfinity()!=v1);
okinf=okinf && CInfOrNaN::PositiveInfinity()!=CInfOrNaN::NegativeInfinity();
okinf=okinf && CInfOrNaN::PositiveInfinity()!=v2;
okinf=okinf && CInfOrNaN::PositiveInfinity()!=0.0;
okinf=okinf && CInfOrNaN::PositiveInfinity()!=1.2;
okinf=okinf && CInfOrNaN::PositiveInfinity()!=-1.2;
okinf=okinf && !(v1!=CInfOrNaN::PositiveInfinity());
okinf=okinf && CInfOrNaN::NegativeInfinity()!=CInfOrNaN::PositiveInfinity();
okinf=okinf && v2!=CInfOrNaN::PositiveInfinity();
okinf=okinf && 0.0!=CInfOrNaN::PositiveInfinity();
okinf=okinf && 1.2!=CInfOrNaN::PositiveInfinity();
okinf=okinf && -1.2!=CInfOrNaN::PositiveInfinity();
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<CInfOrNaN::PositiveInfinity());
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<v1);
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<CInfOrNaN::NegativeInfinity());
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<v2);
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<0.0);
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<1.2);
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<-1.2);
okinf=okinf && !(v1<CInfOrNaN::PositiveInfinity());
okinf=okinf && CInfOrNaN::NegativeInfinity()<CInfOrNaN::PositiveInfinity();
okinf=okinf && v2<CInfOrNaN::PositiveInfinity();
okinf=okinf && 0.0<CInfOrNaN::PositiveInfinity();
okinf=okinf && 1.2<CInfOrNaN::PositiveInfinity();
okinf=okinf && -1.2<CInfOrNaN::PositiveInfinity();
okinf=okinf && CInfOrNaN::PositiveInfinity()<=CInfOrNaN::PositiveInfinity();
okinf=okinf && CInfOrNaN::PositiveInfinity()<=v1;
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<=CInfOrNaN::NegativeInfinity());
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<=v2);
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<=0.0);
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<=1.2);
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<=-1.2);
okinf=okinf && v1<=CInfOrNaN::PositiveInfinity();
okinf=okinf && CInfOrNaN::NegativeInfinity()<=CInfOrNaN::PositiveInfinity();
okinf=okinf && v2<=CInfOrNaN::PositiveInfinity();
okinf=okinf && 0.0<=CInfOrNaN::PositiveInfinity();
okinf=okinf && 1.2<=CInfOrNaN::PositiveInfinity();
okinf=okinf && -1.2<=CInfOrNaN::PositiveInfinity();
okinf=okinf && !(CInfOrNaN::PositiveInfinity()>CInfOrNaN::PositiveInfinity());
okinf=okinf && !(CInfOrNaN::PositiveInfinity()>v1);
okinf=okinf && CInfOrNaN::PositiveInfinity()>CInfOrNaN::NegativeInfinity();
okinf=okinf && CInfOrNaN::PositiveInfinity()>v2;
okinf=okinf && CInfOrNaN::PositiveInfinity()>0.0;
okinf=okinf && CInfOrNaN::PositiveInfinity()>1.2;
okinf=okinf && CInfOrNaN::PositiveInfinity()>-1.2;
okinf=okinf && !(v1>CInfOrNaN::PositiveInfinity());
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>CInfOrNaN::PositiveInfinity());
okinf=okinf && !(v2>CInfOrNaN::PositiveInfinity());
okinf=okinf && !(0.0>CInfOrNaN::PositiveInfinity());
okinf=okinf && !(1.2>CInfOrNaN::PositiveInfinity());
okinf=okinf && !(-1.2>CInfOrNaN::PositiveInfinity());
okinf=okinf && CInfOrNaN::PositiveInfinity()>=CInfOrNaN::PositiveInfinity();
okinf=okinf && CInfOrNaN::PositiveInfinity()>=v1;
okinf=okinf && CInfOrNaN::PositiveInfinity()>=CInfOrNaN::NegativeInfinity();
okinf=okinf && CInfOrNaN::PositiveInfinity()>=v2;
okinf=okinf && CInfOrNaN::PositiveInfinity()>=0.0;
okinf=okinf && CInfOrNaN::PositiveInfinity()>=1.2;
okinf=okinf && CInfOrNaN::PositiveInfinity()>=-1.2;
okinf=okinf && v1>=CInfOrNaN::PositiveInfinity();
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=CInfOrNaN::PositiveInfinity());
okinf=okinf && !(v2>=CInfOrNaN::PositiveInfinity());
okinf=okinf && !(0.0>=CInfOrNaN::PositiveInfinity());
okinf=okinf && !(1.2>=CInfOrNaN::PositiveInfinity());
okinf=okinf && !(-1.2>=CInfOrNaN::PositiveInfinity());
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==CInfOrNaN::PositiveInfinity());
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==v1);
okinf=okinf && CInfOrNaN::NegativeInfinity()==CInfOrNaN::NegativeInfinity();
okinf=okinf && CInfOrNaN::NegativeInfinity()==v2;
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==0.0);
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==1.2);
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==-1.2);
okinf=okinf && !(v1==CInfOrNaN::NegativeInfinity());
okinf=okinf && CInfOrNaN::NegativeInfinity()==CInfOrNaN::NegativeInfinity();
okinf=okinf && v2==CInfOrNaN::NegativeInfinity();
okinf=okinf && !(0.0==CInfOrNaN::NegativeInfinity());
okinf=okinf && !(1.2==CInfOrNaN::NegativeInfinity());
okinf=okinf && !(-1.2==CInfOrNaN::NegativeInfinity());
okinf=okinf && CInfOrNaN::NegativeInfinity()!=CInfOrNaN::PositiveInfinity();
okinf=okinf && CInfOrNaN::NegativeInfinity()!=v1;
okinf=okinf && !(CInfOrNaN::NegativeInfinity()!=CInfOrNaN::NegativeInfinity());
okinf=okinf && !(CInfOrNaN::NegativeInfinity()!=v2);
okinf=okinf && CInfOrNaN::NegativeInfinity()!=0.0;
okinf=okinf && CInfOrNaN::NegativeInfinity()!=1.2;
okinf=okinf && CInfOrNaN::NegativeInfinity()!=-1.2;
okinf=okinf && v1!=CInfOrNaN::NegativeInfinity();
okinf=okinf && !(CInfOrNaN::NegativeInfinity()!=CInfOrNaN::NegativeInfinity());
okinf=okinf && !(v2!=CInfOrNaN::NegativeInfinity());
okinf=okinf && 0.0!=CInfOrNaN::NegativeInfinity();
okinf=okinf && 1.2!=CInfOrNaN::NegativeInfinity();
okinf=okinf && -1.2!=CInfOrNaN::NegativeInfinity();
okinf=okinf && CInfOrNaN::NegativeInfinity()<CInfOrNaN::PositiveInfinity();
okinf=okinf && CInfOrNaN::NegativeInfinity()<v1;
okinf=okinf && !(CInfOrNaN::NegativeInfinity()<CInfOrNaN::NegativeInfinity());
okinf=okinf && !(CInfOrNaN::NegativeInfinity()<v2);
okinf=okinf && CInfOrNaN::NegativeInfinity()<0.0;
okinf=okinf && CInfOrNaN::NegativeInfinity()<1.2;
okinf=okinf && CInfOrNaN::NegativeInfinity()<-1.2;
okinf=okinf && !(v1<CInfOrNaN::NegativeInfinity());
okinf=okinf && !(CInfOrNaN::NegativeInfinity()<CInfOrNaN::NegativeInfinity());
okinf=okinf && !(v2<CInfOrNaN::NegativeInfinity());
okinf=okinf && !(0.0<CInfOrNaN::NegativeInfinity());
okinf=okinf && !(1.2<CInfOrNaN::NegativeInfinity());
okinf=okinf && !(-1.2<CInfOrNaN::NegativeInfinity());
okinf=okinf && CInfOrNaN::NegativeInfinity()<=CInfOrNaN::PositiveInfinity();
okinf=okinf && CInfOrNaN::NegativeInfinity()<=v1;
okinf=okinf && CInfOrNaN::NegativeInfinity()<=CInfOrNaN::NegativeInfinity();
okinf=okinf && CInfOrNaN::NegativeInfinity()<=v2;
okinf=okinf && CInfOrNaN::NegativeInfinity()<=0.0;
okinf=okinf && CInfOrNaN::NegativeInfinity()<=1.2;
okinf=okinf && CInfOrNaN::NegativeInfinity()<=-1.2;
okinf=okinf && !(v1<=CInfOrNaN::NegativeInfinity());
okinf=okinf && CInfOrNaN::NegativeInfinity()<=CInfOrNaN::NegativeInfinity();
okinf=okinf && v2<=CInfOrNaN::NegativeInfinity();
okinf=okinf && !(0.0<=CInfOrNaN::NegativeInfinity());
okinf=okinf && !(1.2<=CInfOrNaN::NegativeInfinity());
okinf=okinf && !(-1.2<=CInfOrNaN::NegativeInfinity());
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>CInfOrNaN::PositiveInfinity());
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>v1);
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>CInfOrNaN::NegativeInfinity());
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>v2);
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>0.0);
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>1.2);
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>-1.2);
okinf=okinf && v1>CInfOrNaN::NegativeInfinity();
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>CInfOrNaN::NegativeInfinity());
okinf=okinf && !(v2>CInfOrNaN::NegativeInfinity());
okinf=okinf && 0.0>CInfOrNaN::NegativeInfinity();
okinf=okinf && 1.2>CInfOrNaN::NegativeInfinity();
okinf=okinf && -1.2>CInfOrNaN::NegativeInfinity();
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=CInfOrNaN::PositiveInfinity());
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=v1);
okinf=okinf && CInfOrNaN::NegativeInfinity()>=CInfOrNaN::NegativeInfinity();
okinf=okinf && CInfOrNaN::NegativeInfinity()>=v2;
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=0.0);
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=1.2);
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=-1.2);
okinf=okinf && v1>=CInfOrNaN::NegativeInfinity();
okinf=okinf && CInfOrNaN::NegativeInfinity()>=CInfOrNaN::NegativeInfinity();
okinf=okinf && v2>=CInfOrNaN::NegativeInfinity();
okinf=okinf && 0.0>=CInfOrNaN::NegativeInfinity();
okinf=okinf && 1.2>=CInfOrNaN::NegativeInfinity();
okinf=okinf && -1.2>=CInfOrNaN::NegativeInfinity();
//--- summary
result=result && oknan;
result=result && okinf;
result=result && okother;
//--- check
if(!silent)
{
//--- check
if(result)
Print("IEEE SPECIAL VALUES: OK");
else
{
Print("IEEE SPECIAL VALUES: FAILED");
Print("* NAN ");
//--- check
if(oknan)
Print("OK");
else
Print("FAILED");
Print("* INF ");
//--- check
if(okinf)
Print("OK");
else
Print("FAILED");
Print("* FUNCTIONS ");
//--- check
if(okother)
Print("OK");
else
Print("FAILED");
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Tests for swapping functions |
//+------------------------------------------------------------------+
static bool CTestAlglibBasicsUnit::TestSwapFunctions(const bool silent)
{
//--- create variables
bool result;
bool okb1;
bool okb2;
bool oki1;
bool oki2;
bool okr1;
bool okr2;
bool okc1;
bool okc2;
//--- create arrays
bool b11[];
bool b12[];
int i11[];
int i12[];
double r11[];
double r12[];
complex c11[];
complex c12[];
//--- create matrix
CMatrixInt b21;
CMatrixInt b22;
CMatrixInt i21;
CMatrixInt i22;
CMatrixDouble r21;
CMatrixDouble r22;
CMatrixComplex c21;
CMatrixComplex c22;
//--- initialization
result=true;
okb1=true;
okb2=true;
oki1=true;
oki2=true;
okr1=true;
okr2=true;
okc1=true;
okc2=true;
//--- Test B1 swaps
ArrayResize(b11,1);
ArrayResize(b12,2);
//--- change values
b11[0]=true;
b12[0]=false;
b12[1]=true;
//--- function call
CAp::Swap(b11,b12);
//--- check
if(CAp::Len(b11)==2 && CAp::Len(b12)==1)
{
okb1=okb1 && !b11[0];
okb1=okb1 && b11[1];
okb1=okb1 && b12[0];
}
else
okb1=false;
//--- Test I1 swaps
ArrayResize(i11,1);
ArrayResize(i12,2);
//--- change values
i11[0]=1;
i12[0]=2;
i12[1]=3;
//--- function call
CAp::Swap(i11,i12);
//--- check
if(CAp::Len(i11)==2 && CAp::Len(i12)==1)
{
oki1=oki1 && i11[0]==2;
oki1=oki1 && i11[1]==3;
oki1=oki1 && i12[0]==1;
}
else
oki1=false;
//--- Test R1 swaps
ArrayResize(r11,1);
ArrayResize(r12,2);
//--- change values
r11[0]=1.5;
r12[0]=2.5;
r12[1]=3.5;
//--- function call
CAp::Swap(r11,r12);
//--- check
if(CAp::Len(r11)==2 && CAp::Len(r12)==1)
{
okr1=okr1 && r11[0]==2.5;
okr1=okr1 && r11[1]==3.5;
okr1=okr1 && r12[0]==1.5;
}
else
okr1=false;
//--- Test C1 swaps
ArrayResize(c11,1);
ArrayResize(c12,2);
//--- change values
c11[0]=1;
c12[0]=2;
c12[1]=3;
//--- function call
CAp::Swap(c11,c12);
//--- check
if(CAp::Len(c11)==2 && CAp::Len(c12)==1)
{
okc1=okc1 && c11[0]==2;
okc1=okc1 && c11[1]==3;
okc1=okc1 && c12[0]==1;
}
else
okc1=false;
//--- Test B2 swaps
b21.Resize(1,2);
b22.Resize(2,1);
//--- change values
b21[0].Set(0,true);
b21[0].Set(1,false);
b22[0].Set(0,false);
b22[1].Set(0,true);
//--- function call
CAp::Swap(b21,b22);
//--- check
if(((CAp::Rows(b21)==2 && CAp::Cols(b21)==1) && CAp::Rows(b22)==1) && CAp::Cols(b22)==2)
{
okb2=okb2 && !b21[0][0];
okb2=okb2 && b21[1][0];
okb2=okb2 && b22[0][0];
okb2=okb2 && !b22[0][1];
}
else
okb2=false;
//--- Test I2 swaps
i21.Resize(1,2);
i22.Resize(2,1);
//--- change values
i21[0].Set(0,1);
i21[0].Set(1,2);
i22[0].Set(0,3);
i22[1].Set(0,4);
//--- function call
CAp::Swap(i21,i22);
//--- check
if(((CAp::Rows(i21)==2 && CAp::Cols(i21)==1) && CAp::Rows(i22)==1) && CAp::Cols(i22)==2)
{
oki2=oki2 && i21[0][0]==3;
oki2=oki2 && i21[1][0]==4;
oki2=oki2 && i22[0][0]==1;
oki2=oki2 && i22[0][1]==2;
}
else
oki2=false;
//--- Test R2 swaps
r21.Resize(1,2);
r22.Resize(2,1);
//--- change values
r21[0].Set(0,1);
r21[0].Set(1,2);
r22[0].Set(0,3);
r22[1].Set(0,4);
//--- function call
CAp::Swap(r21,r22);
//--- check
if(((CAp::Rows(r21)==2 && CAp::Cols(r21)==1) && CAp::Rows(r22)==1) && CAp::Cols(r22)==2)
{
okr2=okr2 && r21[0][0]==3.0;
okr2=okr2 && r21[1][0]==4.0;
okr2=okr2 && r22[0][0]==1.0;
okr2=okr2 && r22[0][1]==2.0;
}
else
okr2=false;
//--- Test C2 swaps
c21.Resize(1,2);
c22.Resize(2,1);
//--- change values
c21[0].Set(0,1);
c21[0].Set(1,2);
c22[0].Set(0,3);
c22[1].Set(0,4);
//--- function call
CAp::Swap(c21,c22);
//--- check
if(((CAp::Rows(c21)==2 && CAp::Cols(c21)==1) && CAp::Rows(c22)==1) && CAp::Cols(c22)==2)
{
okc2=okc2 && c21[0][0]==3;
okc2=okc2 && c21[1][0]==4;
okc2=okc2 && c22[0][0]==1;
okc2=okc2 && c22[0][1]==2;
}
else
okc2=false;
//--- summary
result=result && okb1;
result=result && okb2;
result=result && oki1;
result=result && oki2;
result=result && okr1;
result=result && okr2;
result=result && okc1;
result=result && okc2;
//--- check
if(!silent)
{
//--- check
if(result)
Print("SWAPPING FUNCTIONS: OK");
else
Print("SWAPPING FUNCTIONS: FAILED");
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Tests for swapping functions |
//+------------------------------------------------------------------+
static bool CTestAlglibBasicsUnit::TestSerializationFunctions(const bool silent)
{
//--- create variables
bool result;
bool okb;
bool oki;
bool okr;
int nb=0;
int ni=0;
int nr=0;
int i=0;
//--- objects of classes
CRec4Serialization r0;
CRec4Serialization r1;
//--- initialization
result=true;
okb=true;
oki=true;
okr=true;
//--- calculation
for(nb=1;nb<=4;nb++)
{
for(ni=1;ni<=4;ni++)
{
for(nr=1;nr<=4;nr++)
{
//--- allocation
ArrayResize(r0.m_b,nb);
for(i=0;i<=nb-1;i++)
r0.m_b[i]=CMath::RandomInteger(2)!=0;
//--- allocation
ArrayResize(r0.m_i,ni);
for(i=0;i<=ni-1;i++)
r0.m_i[i]=CMath::RandomInteger(10)-5;
//--- allocation
ArrayResize(r0.m_r,nr);
for(i=0;i<=nr-1;i++)
r0.m_r[i]=2*CMath::RandomReal()-1;
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
//--- serialization
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CTestAlglibBasicsUnit::Rec4SerializationAlloc(_local_serializer,r0);
_local_serializer.SStart_Str();
CTestAlglibBasicsUnit::Rec4SerializationSerialize(_local_serializer,r0);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//--- unserialization
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CTestAlglibBasicsUnit::Rec4SerializationUnserialize(_local_serializer,r1);
_local_serializer.Stop();
}
//--- check
if((CAp::Len(r0.m_b)==CAp::Len(r1.m_b) && CAp::Len(r0.m_i)==CAp::Len(r1.m_i)) && CAp::Len(r0.m_r)==CAp::Len(r1.m_r))
{
//--- change value
for(i=0;i<=nb-1;i++)
okb=okb && ((r0.m_b[i] && r1.m_b[i]) || (!r0.m_b[i] && !r1.m_b[i]));
for(i=0;i<=ni-1;i++)
oki=oki && r0.m_i[i]==r1.m_i[i];
for(i=0;i<=nr-1;i++)
okr=okr && r0.m_r[i]==r1.m_r[i];
}
else
oki=false;
}
}
}
//--- summary
result=result && okb;
result=result && oki;
result=result && okr;
//--- check
if(!silent)
{
//--- check
if(result)
Print("SERIALIZATION FUNCTIONS: OK");
else
{
Print("SERIALIZATION FUNCTIONS: FAILED");
Print("* BOOLEAN ");
//--- check
if(okb)
Print("OK");
else
Print("FAILED");
Print("* INTEGER ");
//--- check
if(oki)
Print("OK");
else
Print("FAILED");
Print("* REAL ");
//--- check
if(okr)
Print("OK");
else
Print("FAILED");
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+