Files
EA_with_Python/Scripts/UnitTests/Alglib/TestClasses.mqh
T

86598 lines
2.9 MiB
Plaintext

//+------------------------------------------------------------------+
//| TestClasses.mqh |
//| Copyright 2003-2022 Sergey Bochkanov (ALGLIB project) |
//| Copyright 2012-2026, MetaQuotes Ltd. |
//| 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\alglib.mqh>
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
#define CheckErrorDiff(val,refval,tol,s) (MathAbs(val-refval)>tol*MathMax(MathAbs(refval),s))
#define PrintResult(function,check) PrintFormat("%-40s: %10s",function,(check ? "PASSED " : "- FAILED -"));
//+------------------------------------------------------------------+
//| Testing class CHighQualityRand |
//+------------------------------------------------------------------+
class CTestHQRndUnit
{
public:
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);
};
//+------------------------------------------------------------------+
//| Testing class CHighQualityRand |
//+------------------------------------------------------------------+
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
PrintResult("SEED TEST",!seederrors);
//--- check
PrintResult("UNIFORM CONTINUOUS",!urerrors);
//--- check
PrintResult("UNIFORM INTEGER",!uierrors);
//--- check
PrintResult("NORMAL",!normerrors);
//--- check
PrintResult("EXPONENTIAL",!experrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
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; 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; i++)
v1+=CMath::Sqr(x[i]-mean);
//--- change value
v2=0;
for(i=0; i<n; 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 |
//+------------------------------------------------------------------+
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:
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);
};
//+------------------------------------------------------------------+
//| Testing class CTSort |
//+------------------------------------------------------------------+
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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; i++)
waserrors=waserrors||a3[i]!=a0[i];
}
}
//--- report
if(!silent)
{
PrintResult("TESTING TAGSORT",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Unsets 2D array. |
//+------------------------------------------------------------------+
void CTestTSortUnit::Unset2D(CMatrixComplex &a)
{
//--- allocation
a.Resize(1,1);
//--- change value
a.Set(0,0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets 1D array. |
//+------------------------------------------------------------------+
void CTestTSortUnit::Unset1D(double &a[])
{
//--- allocation
ArrayResize(a,1);
//--- change value
a[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Unsets 1D array. |
//+------------------------------------------------------------------+
void CTestTSortUnit::Unset1DI(int &a[])
{
//--- allocation
ArrayResize(a,1);
//--- change value
a[0]=CMath::RandomInteger(3)-1;
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
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; i++)
waserrors=waserrors||asorted[i]!=aoriginal[p1[i]];
//--- change values
for(i=0; i<n; i++)
f[i]=0;
for(i=0; i<n; i++)
f[p1[i]]=f[p1[i]]+1;
//--- search errors
for(i=0; i<n; i++)
waserrors=waserrors||f[i]!=1;
//--- P2 correctness
for(i=0; i<n; i++)
a2[i]=aoriginal[i];
for(i=0; i<n; 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; i++)
waserrors=waserrors||asorted[i]!=a2[i];
}
//+------------------------------------------------------------------+
//| Testing class CNearestNeighbor |
//+------------------------------------------------------------------+
class CTestNearestNeighborUnit
{
public:
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);
};
//+------------------------------------------------------------------+
//| Testing class CNearestNeighbor |
//+------------------------------------------------------------------+
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; j++)
xy.Set(i,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; j++)
{
xy.Set(2*i,j,10*CMath::RandomReal()-5);
xy.Set(2*i+1,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; j++)
xy.Set(i,j,v);
}
for(j=0; j<nx+ny; j++)
xy.Set(smalln-1,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
PrintResult("KD TREES",!kdterrors);
//--- check
PrintResult("TEST",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Unsets 2D array. |
//+------------------------------------------------------------------+
void CTestNearestNeighborUnit::Unset2D(CMatrixComplex &a)
{
//--- allocation
a.Resize(1,1);
//--- change value
a.Set(0,0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets 1D array. |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
bool CTestNearestNeighborUnit::KDTResultsDifferent(CMatrixDouble &refxy,
const int ntotal,
CMatrixDouble &qx,
CMatrixDouble &qxy,
int &qt[],const int n,
const int nx,const int ny)
{
bool result=false;
for(int i=0; i<n; i++)
{
//--- check
if(qt[i]<0 || qt[i]>=ntotal)
{
//--- return result
return(true);
}
//--- search errors
for(int j=0; j<nx ; j++)
{
result=result || qx.Get(i,j)!=refxy[qt[i]][j];
result=result || qxy.Get(i,j)!=refxy[qt[i]][j];
}
for(int j=0; j<ny; j++)
result=result || qxy[i][nx+j]!=refxy[qt[i]][nx+j];
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Returns norm |
//+------------------------------------------------------------------+
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; i++)
result=MathMax(result,MathAbs(x[i]));
//--- return result
return(result);
}
//--- check
if(normtype==1)
{
//--- calculation
result=0;
for(i=0; i<n; i++)
result+=MathAbs(x[i]);
//--- return result
return(result);
}
//--- check
if(normtype==2)
{
//--- calculation
result=0;
for(i=0; i<n; i++)
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 |
//+------------------------------------------------------------------+
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; 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 ; i_++)
ptx[i_]=xy.Get(i,i_);
}
else
{
for(i=0; i<nx ; 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; i++)
tmpb[i]=true;
r=0;
//--- calculation
for(i=0; i<k; i++)
{
tmpb[qtags[i]]=false;
for(i_=0; i_<nx ; i_++)
tmpx[i_]=ptx[i_];
for(i_=0; i_<nx ; i_++)
tmpx[i_]=tmpx[i_]-qx.Get(i,i_);
r=MathMax(r,VNorm(tmpx,nx,normtype));
}
for(i=0; i<n; i++)
{
//--- check
if(tmpb[i])
{
for(i_=0; i_<nx ; i_++)
tmpx[i_]=ptx[i_];
for(i_=0; i_<nx ; i_++)
tmpx[i_]=tmpx[i_]-xy.Get(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; i++)
{
for(i_=0; i_<nx ; i_++)
tmpx[i_]=ptx[i_];
for(i_=0; i_<nx ; 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 ; i++)
qx.Set(1,i,CInfOrNaN::NaN());
for(i=0; i<nx+ny; i++)
qxy.Set(1,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 ; i++)
{
//--- search errors
kdterrors=kdterrors||!CInfOrNaN::IsNaN(qx[1][i]);
}
for(i=0; i<nx+ny; 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 ; i_++)
ptx[i_]=xy.Get(i,i_);
}
else
{
for(i=0; i<nx ; 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; i++)
tmpb[i]=true;
r=0;
//--- calculation
for(i=0; i<k; i++)
{
tmpb[qtags[i]]=false;
for(i_=0; i_<nx ; i_++)
tmpx[i_]=ptx[i_];
for(i_=0; i_<nx ; i_++)
tmpx[i_]=tmpx[i_]-qx.Get(i,i_);
r=MathMax(r,VNorm(tmpx,nx,normtype));
}
//--- calculation
for(i=0; i<n; i++)
{
//--- check
if(tmpb[i])
{
for(i_=0; i_<nx ; i_++)
tmpx[i_]=ptx[i_];
for(i_=0; i_<nx ; i_++)
tmpx[i_]=tmpx[i_]-xy.Get(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; i++)
{
for(i_=0; i_<nx ; i_++)
tmpx[i_]=ptx[i_];
for(i_=0; i_<nx ; 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 ; i_++)
ptx[i_]=xy.Get(i,i_);
}
else
{
for(i=0; i<nx ; 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; i++)
tmpb[i]=true;
for(i=0; i<=kx-1; i++)
tmpb[qtags[i]]=false;
//--- calculation
for(i=0; i<n; i++)
{
for(i_=0; i_<nx ; i_++)
tmpx[i_]=ptx[i_];
for(i_=0; i_<nx ; i_++)
tmpx[i_]=tmpx[i_]-xy.Get(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; i++)
{
for(i_=0; i_<nx ; i_++)
ptx[i_]=xy.Get(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 ; 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 |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<nx+ny; j++)
xy.Set(i,j,CMath::RandomReal());
tags[i]=CMath::RandomInteger(100);
}
//--- Build tree,pass it through CSerializer
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.Alloc_Entry();
_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; k++)
{
for(q=0; q<=CAp::Len(qsizes)-1; q++)
{
for(i_=0; i_<nx ; 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 ; i++)
{
for(j=0; j<nx+ny; j++)
{
//--- check
if(MathAbs(xy0.Get(i,j)-xy1.Get(i,j))>threshold)
{
err=true;
return;
}
}
}
//--- function call
CNearestNeighbor::KDTreeQueryResultsTags(tree0,tags0);
//--- function call
CNearestNeighbor::KDTreeQueryResultsTags(tree1,tags1);
for(i=0; i<k0 ; i++)
{
//--- check
if(tags0[i]!=tags1[i])
{
err=true;
return;
}
}
}
}
}
}
}
//--- Next N
n+=25;
}
}
//+------------------------------------------------------------------+
//| Testing class CAblas |
//+------------------------------------------------------------------+
class CTestAblasUnit
{
public:
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);
};
//+------------------------------------------------------------------+
//| Testing class CAblas |
//+------------------------------------------------------------------+
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");
PrintResult("* TRSM",!trsmerrors);
PrintResult("* SYRK",!syrkerrors);
PrintResult("* GEMM",!gemmerrors);
PrintResult("* TRANS",!transerrors);
PrintResult("* RANK1",!rank1errors);
PrintResult("* MV",!mverrors);
PrintResult("* COPY",!copyerrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
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.Set(ci1+i-1,cj1+j-1,alpha*v);
else
c.Set(ci1+i-1,cj1+j-1,beta*c[ci1+i-1][cj1+j-1]+alpha*v);
}
}
}
//+------------------------------------------------------------------+
//| ?Matrix????TRSM tests |
//| Returns False for passed test,True - for failed |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<m; j++)
refra.Set(i,j,0.2*CMath::RandomReal()-0.1);
}
for(i=0; i<m; i++)
refra.Set(i,i,(2*CMath::RandomInteger(1)-1)*(2*m+CMath::RandomReal()));
for(i=0; i<m; i++)
{
for(j=0; j<m; j++)
{
refra.Set(i+m,j,refra.Get(i,j));
refra.Set(i,j+m,refra.Get(i,j));
refra.Set(i+m,j+m,refra.Get(i,j));
}
}
//--- allocation
refca.Resize(2*m,2*m);
//--- change values
for(i=0; i<m; i++)
{
for(j=0; j<m; j++)
{
refca.SetRe(i,j,0.2*CMath::RandomReal()-0.1);
refca.SetIm(i,j,0.2*CMath::RandomReal()-0.1);
}
}
//--- change values
for(i=0; i<m; i++)
{
refca.SetRe(i,i,(2*CMath::RandomInteger(2)-1)*(2*m+CMath::RandomReal()));
refca.SetIm(i,i,(2*CMath::RandomInteger(2)-1)*(2*m+CMath::RandomReal()));
}
//--- change values
for(i=0; i<m; i++)
{
for(j=0; j<m; j++)
{
refca.Set(i+m,j,refca.Get(i,j));
refca.Set(i,j+m,refca.Get(i,j));
refca.Set(i+m,j+m,refca.Get(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; i++)
{
for(j=0; j<m; j++)
refrxr.Set(i,j,2*CMath::RandomReal()-1);
}
//--- allocation
refrxl.Resize(m,n);
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
refrxl.Set(i,j,2*CMath::RandomReal()-1);
}
//--- allocation
refcxr.Resize(n,m);
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
{
refcxr.SetRe(i,j,2*CMath::RandomReal()-1);
refcxr.SetIm(i,j,2*CMath::RandomReal()-1);
}
}
//--- allocation
refcxl.Resize(m,n);
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
{
refcxl.SetRe(i,j,2*CMath::RandomReal()-1);
refcxl.SetIm(i,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.Set(i,j,refca.Get(i,j));
ra.Set(i,j,refra.Get(i,j));
}
else
{
ca.Set(i,j,CMath::RandomReal());
ra.Set(i,j,CMath::RandomReal());
}
}
}
//--- change values
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
{
//--- check
if(i>=xoffsi && j>=xoffsj)
{
cxr1.Set(i,j,refcxr.Get(i,j));
cxr2.Set(i,j,refcxr.Get(i,j));
rxr1.Set(i,j,refrxr.Get(i,j));
rxr2.Set(i,j,refrxr.Get(i,j));
}
else
{
cxr1.Set(i,j,CMath::RandomReal());
cxr2.Set(i,j,cxr1.Get(i,j));
rxr1.Set(i,j,CMath::RandomReal());
rxr2.Set(i,j,rxr1.Get(i,j));
}
}
}
//--- change values
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(i>=xoffsi && j>=xoffsj)
{
cxl1.Set(i,j,refcxl.Get(i,j));
cxl2.Set(i,j,refcxl.Get(i,j));
rxl1.Set(i,j,refrxl.Get(i,j));
rxl2.Set(i,j,refrxl.Get(i,j));
}
else
{
cxl1.Set(i,j,CMath::RandomReal());
cxl2.Set(i,j,cxl1.Get(i,j));
rxl1.Set(i,j,CMath::RandomReal());
rxl2.Set(i,j,rxl1.Get(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; i++)
{
for(j=0; j<m; j++)
result=result||CMath::AbsComplex(cxr1.Get(i,j)-cxr2.Get(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; i++)
{
for(j=0; j<n; j++)
result=result||CMath::AbsComplex(cxl1.Get(i,j)-cxl2.Get(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; i++)
{
for(j=0; j<m; j++)
result=result||MathAbs(rxr1.Get(i,j)-rxr2.Get(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; i++)
{
for(j=0; j<n; j++)
result=result||MathAbs(rxl1.Get(i,j)-rxl2.Get(i,j))>threshold;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| SYRK tests |
//| Returns False for passed test,True - for failed |
//+------------------------------------------------------------------+
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; i++)
{
refra.Set(i,i,2*CMath::RandomReal()-1);
refca.Set(i,i,2*CMath::RandomReal()-1);
for(j=i+1; j<n; j++)
{
refra.Set(i,j,2*CMath::RandomReal()-1);
refca.SetRe(i,j,2*CMath::RandomReal()-1);
refca.SetIm(i,j,2*CMath::RandomReal()-1);
refra.Set(j,i,refra.Get(i,j));
refca.Set(j,i,CMath::Conj(refca.Get(i,j)));
}
}
//--- change values
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
refra.Set(i+n,j,refra.Get(i,j));
refra.Set(i,j+n,refra.Get(i,j));
refra.Set(i+n,j+n,refra.Get(i,j));
refca.Set(i+n,j,refca.Get(i,j));
refca.Set(i,j+n,refca.Get(i,j));
refca.Set(i+n,j+n,refca.Get(i,j));
}
}
//--- allocation
refrc.Resize(n,k);
refcc.Resize(n,k);
for(i=0; i<n; i++)
{
for(j=0; j<k; j++)
{
refrc.Set(i,j,2*CMath::RandomReal()-1);
refcc.SetRe(i,j,2*CMath::RandomReal()-1);
refcc.SetIm(i,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.Set(i,j,refca.Get(i,j));
ca2.Set(i,j,refca.Get(i,j));
ra1.Set(i,j,refra.Get(i,j));
ra2.Set(i,j,refra.Get(i,j));
}
else
{
ca1.Set(i,j,CMath::RandomReal());
ca2.Set(i,j,ca1.Get(i,j));
ra1.Set(i,j,CMath::RandomReal());
ra2.Set(i,j,ra1.Get(i,j));
}
}
}
//--- change values
for(i=0; i<n; i++)
{
for(j=0; j<k; j++)
{
//--- check
if(i>=xoffsi && j>=xoffsj)
{
rc.Set(i,j,refrc.Get(i,j));
rct.Set(j,i,refrc.Get(i,j));
cc.Set(i,j,refcc.Get(i,j));
cct.Set(j,i,refcc.Get(i,j));
}
else
{
rc.Set(i,j,CMath::RandomReal());
rct.Set(j,i,rc.Get(i,j));
cc.Set(i,j,CMath::RandomReal());
cct.Set(j,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; i++)
{
for(j=0; j<n; j++)
result=result||CMath::AbsComplex(ca1.Get(i,j)-ca2.Get(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; i++)
{
for(j=0; j<n; j++)
result=result||MathAbs(ra1.Get(i,j)-ra2.Get(i,j))>threshold;
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| GEMM tests |
//| Returns False for passed test,True - for failed |
//+------------------------------------------------------------------+
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.Set(i,j,2*CMath::RandomReal()-1);
refrb.Set(i,j,2*CMath::RandomReal()-1);
refrc.Set(i,j,2*CMath::RandomReal()-1);
refca.SetRe(i,j,2*CMath::RandomReal()-1);
refca.SetIm(i,j,2*CMath::RandomReal()-1);
refcb.SetRe(i,j,2*CMath::RandomReal()-1);
refcb.SetIm(i,j,2*CMath::RandomReal()-1);
refcc.SetRe(i,j,2*CMath::RandomReal()-1);
refcc.SetIm(i,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.real=2*CMath::RandomReal()-1;
alphac.imag=2*CMath::RandomReal()-1;
}
else
alphac=0;
//--- check
if(CMath::RandomReal()>0.5)
{
betac.real=2*CMath::RandomReal()-1;
betac.imag=2*CMath::RandomReal()-1;
}
else
betac=0;
//--- copy C
for(i=0; i<=maxn; i++)
{
for(j=0; j<=maxn; j++)
{
rc1.Set(i,j,refrc.Get(i,j));
rc2.Set(i,j,refrc.Get(i,j));
cc1.Set(i,j,refcc.Get(i,j));
cc2.Set(i,j,refcc.Get(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.Get(i,j)-cc2.Get(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.Get(i,j)-rc2.Get(i,j))>threshold;
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| transpose tests |
//| Returns False for passed test,True - for failed |
//+------------------------------------------------------------------+
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.Set(i,j,2*CMath::RandomReal()-1);
refca.SetRe(i,j,2*CMath::RandomReal()-1);
refca.SetIm(i,j,2*CMath::RandomReal()-1);
refrb.Set(i,j,i*v1+j*v2);
refcb.Set(i,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.Get(i,j)-(v1*i+v2*j))>threshold;
else
result=result||MathAbs(refrb.Get(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.Get(i,j)-(v1*i+v2*j))>threshold;
else
result=result||CMath::AbsComplex(refcb.Get(i,j)-refca[aoffsi+j-boffsj][aoffsj+i-boffsi])>threshold;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| rank-1tests |
//| Returns False for passed test,True - for failed |
//+------------------------------------------------------------------+
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.Set(i,j,2*CMath::RandomReal()-1);
refca.SetRe(i,j,2*CMath::RandomReal()-1);
refca.SetIm(i,j,2*CMath::RandomReal()-1);
refrb.Set(i,j,refra.Get(i,j));
refcb.Set(i,j,refca.Get(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].real=2*CMath::RandomReal()-1;
cu[i].imag=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].real=2*CMath::RandomReal()-1;
cv[i].imag=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.Get(i,j)-refcb.Get(i,j))>threshold;
else
result=result||CMath::AbsComplex(refca.Get(i,j)-(refcb.Get(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.Get(i,j)-refrb.Get(i,j))>threshold;
else
result=result||MathAbs(refra.Get(i,j)-(refrb.Get(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 |
//+------------------------------------------------------------------+
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.Set(i,j,2*CMath::RandomReal()-1);
refca.SetRe(i,j,2*CMath::RandomReal()-1);
refca.SetIm(i,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].real=2*CMath::RandomReal()-1;
cx[i].imag=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 |
//+------------------------------------------------------------------+
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.Set(i,j,2*CMath::RandomReal()-1);
ca.SetRe(i,j,2*CMath::RandomReal()-1);
ca.SetIm(i,j,2*CMath::RandomReal()-1);
rb.Set(i,j,1+2*i+3*j);
cb.Set(i,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.Get(i,j)!=1+2*i+3*j;
else
result=result||CMath::AbsComplex(ca[aoffsi+i-boffsi][aoffsj+j-boffsj]-cb.Get(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.Get(i,j)!=1+2*i+3*j;
else
result=result||MathAbs(ra[aoffsi+i-boffsi][aoffsj+j-boffsj]-rb.Get(i,j))>threshold;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
a1.Set(i,j,0);
}
//--- check
if(isupper)
{
for(i=0; i<n; i++)
{
for(j=i; j<n; j++)
a1.Set(i,j,a[i1+i][j1+j]);
}
}
else
{
for(i=0; i<n; i++)
{
for(j=0; j<=i; j++)
a1.Set(i,j,a[i1+i][j1+j]);
}
}
//--- change value
rupper=isupper;
//--- check
if(isunit)
{
for(i=0; i<n; i++)
{
a1.Set(i,i,1);
}
}
//--- allocation
a2.Resize(n,n);
//--- check
if(optype==0)
{
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
a2.Set(i,j,a1.Get(i,j));
}
}
//--- check
if(optype==1)
{
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
a2.Set(i,j,a1[j][i]);
}
//--- change value
rupper=!rupper;
}
//--- check
if(optype==2)
{
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
a2.Set(i,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; i++)
{
i1_=j2;
for(i_=0; i_<n; i_++)
tx[i_]=x[i2+i][i_+i1_];
//--- change values
for(j=0; j<n; j++)
{
vc=0.0;
for(i_=0; i_<n; i_++)
vc+=tx[i_]*a2.Get(i_,j);
x.Set(i2+i,j2+j,vc);
}
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<m; j++)
a1.Set(i,j,0);
}
//--- check
if(isupper)
{
for(i=0; i<m; i++)
{
for(j=i; j<m; j++)
a1.Set(i,j,a[i1+i][j1+j]);
}
}
else
{
for(i=0; i<m; i++)
{
for(j=0; j<=i; j++)
a1.Set(i,j,a[i1+i][j1+j]);
}
}
//--- change value
rupper=isupper;
//--- check
if(isunit)
{
for(i=0; i<m; i++)
a1.Set(i,i,1);
}
//--- allocation
a2.Resize(m,m);
//--- check
if(optype==0)
{
for(i=0; i<m; i++)
{
for(j=0; j<m; j++)
a2.Set(i,j,a1.Get(i,j));
}
}
//--- check
if(optype==1)
{
for(i=0; i<m; i++)
{
for(j=0; j<m; j++)
a2.Set(i,j,a1[j][i]);
}
//--- change value
rupper=!rupper;
}
//--- check
if(optype==2)
{
for(i=0; i<m; i++)
{
for(j=0; j<m; j++)
a2.Set(i,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; j++)
{
i1_=i2;
for(i_=0; i_<m; i_++)
tx[i_]=x[i_+i1_][j2+j];
//--- change values
for(i=0; i<m; i++)
{
vc=0.0;
for(i_=0; i_<m; i_++)
vc+=a2.Get(i,i_)*tx[i_];
x.Set(i2+i,j2+j,vc);
}
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
a1.Set(i,j,0);
}
//--- check
if(isupper)
{
for(i=0; i<n; i++)
{
for(j=i; j<n; j++)
a1.Set(i,j,a[i1+i][j1+j]);
}
}
else
{
for(i=0; i<n; i++)
{
for(j=0; j<=i; j++)
a1.Set(i,j,a[i1+i][j1+j]);
}
}
//--- change value
rupper=isupper;
//--- check
if(isunit)
{
for(i=0; i<n; i++)
a1.Set(i,i,1);
}
//--- allocation
a2.Resize(n,n);
//--- check
if(optype==0)
{
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
a2.Set(i,j,a1.Get(i,j));
}
}
//--- check
if(optype==1)
{
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
a2.Set(i,j,a1[j][i]);
}
//--- change value
rupper=!rupper;
}
//--- function call
InternalRMatrixTrInverse(a2,n,rupper,false);
//--- allocation
ArrayResize(tx,n);
for(i=0; i<m; i++)
{
i1_=j2;
for(i_=0; i_<n; i_++)
tx[i_]=x[i2+i][i_+i1_];
//--- change values
for(j=0; j<n; j++)
{
vr=0.0;
for(i_=0; i_<n; i_++)
vr+=tx[i_]*a2.Get(i_,j);
x.Set(i2+i,j2+j,vr);
}
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<m; j++)
a1.Set(i,j,0);
}
//--- check
if(isupper)
{
for(i=0; i<m; i++)
{
for(j=i; j<m; j++)
a1.Set(i,j,a[i1+i][j1+j]);
}
}
else
{
for(i=0; i<m; i++)
{
for(j=0; j<=i; j++)
a1.Set(i,j,a[i1+i][j1+j]);
}
}
//--- change value
rupper=isupper;
//--- check
if(isunit)
{
for(i=0; i<m; i++)
a1.Set(i,i,1);
}
//--- allocation
a2.Resize(m,m);
//--- check
if(optype==0)
{
for(i=0; i<m; i++)
{
for(j=0; j<m; j++)
a2.Set(i,j,a1.Get(i,j));
}
}
//--- check
if(optype==1)
{
for(i=0; i<m; i++)
{
for(j=0; j<m; j++)
a2.Set(i,j,a1[j][i]);
}
//--- change value
rupper=!rupper;
}
//--- function call
InternalRMatrixTrInverse(a2,m,rupper,false);
//--- allocation
ArrayResize(tx,m);
for(j=0; j<n; j++)
{
i1_=i2;
for(i_=0; i_<m; i_++)
tx[i_]=x[i_+i1_][j2+j];
//--- change values
for(i=0; i<m; i++)
{
vr=0.0;
for(i_=0; i_<m; i_++)
vr+=a2.Get(i,i_)*tx[i_];
x.Set(i2+i,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 |
//+------------------------------------------------------------------+
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; j++)
{
//--- check
if(nounit)
{
//--- check
if(a[j][j]==0)
{
//--- return result
return(false);
}
//--- change values
a.Set(j,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; i_++)
t[i_]=a.Get(i_,j);
for(i=0; i<j; i++)
{
//--- check
if(i+1<j)
{
v=0.0;
for(i_=i+1; i_<j; i_++)
v+=a.Get(i,i_)*t[i_];
}
else
v=0;
//--- check
if(nounit)
a.Set(i,j,v+a[i][i]*t[i]);
else
a.Set(i,j,v+t[i]);
}
//--- change values
for(i_=0; i_<j; i_++)
a.Set(i_,j,ajj*a.Get(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.Set(j,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; i_++)
t[i_]=a.Get(i_,j);
for(i=j+1; i<n; i++)
{
//--- check
if(i>j+1)
{
v=0.0;
for(i_=j+1; i_<i; i_++)
v+=a.Get(i,i_)*t[i_];
}
else
v=0;
//--- check
if(nounit)
a.Set(i,j,v+a[i][i]*t[i]);
else
a.Set(i,j,v+t[i]);
}
//--- change values
for(i_=j+1; i_<n; i_++)
a.Set(i_,j,ajj*a.Get(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 |
//+------------------------------------------------------------------+
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; j++)
{
//--- check
if(nounit)
{
//--- check
if(a[j][j]==0.0)
{
//--- return result
return(false);
}
//--- change values
a.Set(j,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; i_++)
t[i_]=a.Get(i_,j);
for(i=0; i<j; i++)
{
//--- check
if(i<j-1)
{
v=0.0;
for(i_=i+1; i_<j; i_++)
v+=a.Get(i,i_)*t[i_];
}
else
v=0;
//--- check
if(nounit)
a.Set(i,j,v+a[i][i]*t[i]);
else
a.Set(i,j,v+t[i]);
}
//--- change values
for(i_=0; i_<j; i_++)
a.Set(i_,j,ajj*a.Get(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.Set(j,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; i_++)
t[i_]=a.Get(i_,j);
for(i=j+1; i<n; i++)
{
//--- check
if(i>j+1)
{
v=0.0;
for(i_=j+1; i_<i; i_++)
v+=a.Get(i,i_)*t[i_];
}
else
v=0;
//--- check
if(nounit)
a.Set(i,j,v+a[i][i]*t[i]);
else
a.Set(i,j,v+t[i]);
}
//--- change values
for(i_=j+1; i_<n; i_++)
a.Set(i_,j,ajj*a.Get(i_,j));
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Reference SYRK subroutine. |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if((isupper && j>=i) || (!isupper && j<=i))
{
//--- check
if(beta==0.0)
c.Set(i+ic,j+jc,0);
else
c.Set(i+ic,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; i++)
{
for(j=0; j<k; j++)
{
//--- check
if(optypea==0)
ae.Set(i,j,a[ia+i][ja+j]);
//--- check
if(optypea==2)
ae.Set(i,j,CMath::Conj(a[ia+j][ja+i]));
}
}
//--- calculation
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
vc=0;
//--- check
if(k>0)
{
vc=0.0;
for(i_=0; i_<k; i_++)
vc+=ae.Get(i,i_)*CMath::Conj(ae.Get(j,i_));
}
vc=vc*alpha;
//--- check
if(isupper && j>=i)
c.Set(i+ic,jc+j,vc+c[ic+i][jc+j]);
//--- check
if(!isupper && j<=i)
c.Set(i+ic,jc+j,vc+c[ic+i][jc+j]);
}
}
}
//+------------------------------------------------------------------+
//| Reference SYRK subroutine. |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if((isupper && j>=i) || (!isupper && j<=i))
{
//--- check
if(beta==0.0)
c.Set(i+ic,j+jc,0);
else
c.Set(i+ic,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; i++)
{
for(j=0; j<k; j++)
{
//--- check
if(optypea==0)
ae.Set(i,j,a[ia+i][ja+j]);
//--- check
if(optypea==1)
ae.Set(i,j,a[ia+j][ja+i]);
}
}
//--- calculation
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
vr=0;
//--- check
if(k>0)
{
vr=0.0;
for(i_=0; i_<k; i_++)
vr+=ae.Get(i,i_)*ae.Get(j,i_);
}
vr=alpha*vr;
//--- check
if(isupper && j>=i)
c.Set(i+ic,jc+j,vr+c[ic+i][jc+j]);
//--- check
if(!isupper && j<=i)
c.Set(i+ic,jc+j,vr+c[ic+i][jc+j]);
}
}
}
//+------------------------------------------------------------------+
//| Reference GEMM, |
//| ALGLIB subroutine |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<k; j++)
{
//--- check
if(optypea==0)
ae.Set(i,j,a[ia+i][ja+j]);
//--- check
if(optypea==1)
ae.Set(i,j,a[ia+j][ja+i]);
//--- check
if(optypea==2)
ae.Set(i,j,CMath::Conj(a[ia+j][ja+i]));
}
}
//--- allocation
be.Resize(k,n);
//--- change values
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(optypeb==0)
be.Set(i,j,b[ib+i][jb+j]);
//--- check
if(optypeb==1)
be.Set(i,j,b[ib+j][jb+i]);
//--- check
if(optypeb==2)
be.Set(i,j,CMath::Conj(b[ib+j][jb+i]));
}
}
//--- calculation
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
{
//--- change value
vc=0.0;
for(i_=0; i_<k; i_++)
vc+=ae.Get(i,i_)*be.Get(i_,j);
vc=alpha*vc;
//--- check
if(beta!=0)
vc=vc+beta*c[ic+i][jc+j];
c.Set(i+ic,jc+j,vc);
}
}
}
//+------------------------------------------------------------------+
//| Reference GEMM, |
//| ALGLIB subroutine |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<k; j++)
{
//--- check
if(optypea==0)
ae.Set(i,j,a[ia+i][ja+j]);
//--- check
if(optypea==1)
ae.Set(i,j,a[ia+j][ja+i]);
}
}
//--- allocation
be.Resize(k,n);
//--- change values
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(optypeb==0)
be.Set(i,j,b[ib+i][jb+j]);
//--- check
if(optypeb==1)
be.Set(i,j,b[ib+j][jb+i]);
}
}
//--- calculation
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
{
//--- change value
vc=0.0;
for(i_=0; i_<k; i_++)
vc+=ae.Get(i,i_)*be.Get(i_,j);
vc=alpha*vc;
//--- check
if(beta!=0.0)
vc=vc+beta*c[ic+i][jc+j];
c.Set(i+ic,jc+j,vc);
}
}
}
//+------------------------------------------------------------------+
//| Testing class CBaseStat |
//+------------------------------------------------------------------+
class CTestBaseStatUnit
{
public:
static bool TestBaseStat(const bool silent);
};
//+------------------------------------------------------------------+
//| Testing class CBaseStat |
//+------------------------------------------------------------------+
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; 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; 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; 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; 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; i++)
{
for(j=0; j<=kx-1; j++)
mx.Set(i,j,2*CMath::RandomReal()-1);
for(j=0; j<=ky-1; j++)
my.Set(i,j,2*CMath::RandomReal()-1);
}
//--- check
if(ctype==1)
{
cidxx=CMath::RandomInteger(kx);
cidxy=CMath::RandomInteger(ky);
//--- change values
for(i=0; i<n; i++)
{
mx.Set(i,cidxx,0.0);
my.Set(i,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; i++)
{
mx.Set(i,cidxx,v);
my.Set(i,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; i_++)
x[i_]=mx.Get(i_,i);
for(i_=0; i_<n; i_++)
y[i_]=mx.Get(i_,j);
}
//--- search errors
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::Cov2(x,y,n)-cc.Get(i,j))>threshold;
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::PearsonCorr2(x,y,n)-cp.Get(i,j))>threshold;
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::SpearmanCorr2(x,y,n)-cs.Get(i,j))>threshold;
}
}
//--- check
if(ctype!=0 && n>0)
{
for(i=0; i<=kx-1; i++)
{
//--- search errors
covcorrerrors=covcorrerrors||cc.Get(i,cidxx)!=0.0;
covcorrerrors=covcorrerrors||cc.Get(cidxx,i)!=0.0;
covcorrerrors=covcorrerrors||cp.Get(i,cidxx)!=0.0;
covcorrerrors=covcorrerrors||cp.Get(cidxx,i)!=0.0;
covcorrerrors=covcorrerrors||cs.Get(i,cidxx)!=0.0;
covcorrerrors=covcorrerrors||cs.Get(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; i_++)
x[i_]=mx.Get(i_,i);
for(i_=0; i_<n; i_++)
y[i_]=my.Get(i_,j);
}
//--- search errors
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::Cov2(x,y,n)-cc.Get(i,j))>threshold;
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::PearsonCorr2(x,y,n)-cp.Get(i,j))>threshold;
covcorrerrors=covcorrerrors||MathAbs(CBaseStat::SpearmanCorr2(x,y,n)-cs.Get(i,j))>threshold;
}
}
//--- check
if(ctype!=0 && n>0)
{
for(i=0; i<=kx-1; i++)
{
//--- search errors
covcorrerrors=covcorrerrors||cc.Get(i,cidxy)!=0.0;
covcorrerrors=covcorrerrors||cp.Get(i,cidxy)!=0.0;
covcorrerrors=covcorrerrors||cs.Get(i,cidxy)!=0.0;
}
for(j=0; j<=ky-1; j++)
{
//--- search errors
covcorrerrors=covcorrerrors||cc.Get(cidxx,j)!=0.0;
covcorrerrors=covcorrerrors||cp.Get(cidxx,j)!=0.0;
covcorrerrors=covcorrerrors||cs.Get(cidxx,j)!=0.0;
}
}
}
}
}
}
//--- Final report
waserrors=s1errors||covcorrerrors;
//--- check
if(!silent)
{
Print("DESC.STAT TEST");
PrintResult("TOTAL RESULTS",!waserrors);
PrintResult("* 1-SAMPLE FUNCTIONALITY",!s1errors);
PrintResult("* CORRELATION/COVARIATION",!covcorrerrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CBdSS |
//+------------------------------------------------------------------+
class CTestBdSSUnit
{
public:
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);
};
//+------------------------------------------------------------------+
//| Testing class CBdSS |
//+------------------------------------------------------------------+
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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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");
PrintResult("TIES",!tieserrors);
PrintResult("SPLIT-2",!split2errors);
PrintResult("OPTIMAL SPLIT-K",!optimalsplitkerrors);
PrintResult("SPLIT-K",!splitkerrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Unsets 2D array. |
//+------------------------------------------------------------------+
void CTestBdSSUnit::Unset2D(CMatrixComplex &a)
{
//--- allocation
a.Resize(1,1);
//--- change value
a.Set(0,0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets 1D array. |
//+------------------------------------------------------------------+
void CTestBdSSUnit::Unset1D(double &a[])
{
//--- allocation
ArrayResize(a,1);
//--- change value
a[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Unsets 1D array. |
//+------------------------------------------------------------------+
void CTestBdSSUnit::Unset1DI(int &a[])
{
//--- allocation
ArrayResize(a,1);
//--- change value
a[0]=CMath::RandomInteger(3)-1;
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
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; i++)
waserrors=waserrors||asorted[i]!=aoriginal[p1[i]];
//--- change values
for(i=0; i<n; i++)
f[i]=0;
for(i=0; i<n; i++)
f[p1[i]]=f[p1[i]]+1;
//--- search errors
for(i=0; i<n; i++)
waserrors=waserrors||f[i]!=1;
//--- P2 correctness
for(i=0; i<n; i++)
a2[i]=aoriginal[i];
for(i=0; i<n; 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; i++)
waserrors=waserrors||asorted[i]!=a2[i];
}
//+------------------------------------------------------------------+
//| Testing class CDForest |
//+------------------------------------------------------------------+
class CTestDForestUnit
{
public:
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);
};
//+------------------------------------------------------------------+
//| Testing class CDForest |
//+------------------------------------------------------------------+
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");
PrintResult("TOTAL RESULTS",!waserrors);
PrintResult("* PROCESSING FUNCTIONS",!procerrors);
PrintResult("* BASIC TESTS",!basicerrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Processing functions test |
//+------------------------------------------------------------------+
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 ; i++)
{
for(j=0; j<nvars ; j++)
{
//--- check
if(j%2==0)
xy.Set(i,j,2*CMath::RandomReal()-1);
else
xy.Set(i,j,CMath::RandomInteger(2));
}
//--- check
if(nclasses==1)
xy.Set(i,nvars,2*CMath::RandomReal()-1);
else
xy.Set(i,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 ; i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=x1[i];
}
for(i=0; i<nclasses; 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; 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; i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=x1[i];
}
for(i=0; i<nclasses; 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; 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.Alloc_Entry();
_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 ; i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=x1[i];
}
for(i=0; i<nclasses; 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; i++)
allsame=allsame && y1[i]==y2[i];
err=err||!allsame;
//--- Normalization properties
if(nclasses>1)
{
for(i=0; i<nvars ; i++)
x1[i]=2*CMath::RandomReal()-1;
//--- function call
CDForest::DFProcess(df1,x1,y1);
v=0;
//--- search errors
for(i=0; i<nclasses; i++)
{
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 |
//+------------------------------------------------------------------+
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 ; i++)
{
for(j=0; j<nvars ; j++)
xy.Set(i,j,2*CMath::RandomReal()-1);
xy.Set(i,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 ; i++)
{
for(i_=0; i_<nvars ; i_++)
x[i_]=xy.Get(i,i_);
//--- function call
CDForest::DFProcess(df,x,y);
s=0;
for(j=0; j<nclasses; 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.Set(k,j,XY[I,J]
//--- (may be we just can't distinguish two tied values).
//--- definitely error otherwise.
hassame=false;
for(k=0; k<npoints ; k++)
{
//--- check
if(k!=i)
{
for(j=0; j<nvars ; j++)
{
//--- check
if(xy[k][j]==xy.Get(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 |
//+------------------------------------------------------------------+
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 ; i++)
{
xy.Set(i,0,3*CMath::RandomReal());
//--- check
if(xy[i][0]<=1.0)
xy.Set(i,1,0);
else
{
//--- check
if(xy[i][0]<=2.0)
{
//--- check
if(CMath::RandomReal()<xy[i][0]-1)
xy.Set(i,1,1);
else
xy.Set(i,1,0);
}
else
xy.Set(i,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 |
//+------------------------------------------------------------------+
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 ; i++)
{
xy.Set(i,0,2*CMath::RandomReal()-1);
xy.Set(i,1,2*CMath::RandomReal()-1);
//--- check
if(CMath::Sqr(xy[i][0])+CMath::Sqr(xy[i][1])<=0.25)
xy.Set(i,2,0);
else
xy.Set(i,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 |
//+------------------------------------------------------------------+
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 ; i++)
{
xy.Set(i,0,2*CMath::RandomReal()-1);
xy.Set(i,1,2*CMath::RandomReal()-1);
xy.Set(i,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 |
//+------------------------------------------------------------------+
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 ; i++)
{
for(j=0; j<nvars ; j++)
{
xy.Set(i,j,0);
}
xy.Set(i,i,1);
xy.Set(i,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 ; i++)
{
for(i_=0; i_<nvars ; i_++)
x[i_]=xy.Get(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 ; i++)
{
for(i_=0; i_<nvars ; i_++)
{
x[i_]=xy.Get(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 |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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; j++)
xy.Set(i,j,RNormal());
v=0.0;
for(i_=0; i_<n; i_++)
v+=xy.Get(i,i_)*xy.Get(i,i_);
//--- calculation
v=CMath::RandomReal()/MathSqrt(v);
for(i_=0; i_<n; i_++)
xy.Set(i,i_,v*xy.Get(i,i_));
}
//+------------------------------------------------------------------+
//| Unsets DF |
//+------------------------------------------------------------------+
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.Set(0,0,0);
xy.Set(0,1,0);
//--- function call
CDForest::DFBuildInternal(xy,1,1,1,1,1,1,0,info,df,rep);
}
//+------------------------------------------------------------------+
//| Testing class CBlas |
//+------------------------------------------------------------------+
class CTestBlasUnit
{
public:
static bool TestBlas(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);
};
//+------------------------------------------------------------------+
//| Testing class CBlas |
//+------------------------------------------------------------------+
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+=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.Set(i,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.Get(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.Get(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.Set(1,1,2);
a.Set(1,2,3);
a.Set(1,3,1);
a.Set(2,1,4);
a.Set(2,2,-5);
a.Set(2,3,8);
a.Set(3,1,99);
a.Set(3,2,3);
a.Set(3,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.Set(2,3,2);
a.Set(2,4,-1);
a.Set(2,5,-1);
a.Set(3,3,1);
a.Set(3,4,-2);
a.Set(3,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.Set(i,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.Get(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.Set(i,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.Get(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.Set(i,j,CMath::RandomReal());
b.Set(i,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.Set(i,j,2.1*i+3.1*j);
c2.Set(i,j,c1.Get(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");
PrintResult("VectorNorm2",!n2errors);
PrintResult("AbsMax (vector/row/column)",!amaxerrors);
PrintResult("UpperHessenberg1Norm",!hsnerrors);
PrintResult("MatrixVectorMultiply",!mverrors);
PrintResult("InplaceTranspose",!iterrors);
PrintResult("CopyAndTranspose",!cterrors);
PrintResult("MatrixMatrixMultiply",!mmerrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
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.Set(ci1+i-1,cj1+j-1,alpha*v);
else
c.Set(ci1+i-1,cj1+j-1,beta*c[ci1+i-1][cj1+j-1]+alpha*v);
}
}
}
//+------------------------------------------------------------------+
//| Testing class CKMeans |
//+------------------------------------------------------------------+
class CTestKMeansUnit
{
public:
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);
};
//+------------------------------------------------------------------+
//| Testing class CKMeans |
//+------------------------------------------------------------------+
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");
PrintResult("* CONVERGENCE",!converrors);
PrintResult("* SIMPLE TASKS",!simpleerrors);
PrintResult("* COMPLEX TASKS",!complexerrors);
PrintResult("* OTHER PROPERTIES",!othererrors);
PrintResult("* RESTARTS PROPERTIES",!restartserrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Simple test 1: ellipsoid in NF-dimensional space. |
//| compare k-means centers with random centers |
//+------------------------------------------------------------------+
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 ; i++)
{
RSphere(xy,nvars,i);
xy.Set(i,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 ; i++)
{
cclosest=-1;
dclosest=CMath::m_maxrealnumber;
//--- calculation
for(j=0; j<nc; j++)
{
for(i_=0; i_<nvars ; i_++)
tmp[i_]=xy.Get(i,i_);
for(i_=0; i_<nvars ; i_++)
tmp[i_]=tmp[i_]-c.Get(i_,j);
v=0.0;
for(i_=0; i_<nvars ; 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 ; i++)
{
for(i_=0; i_<nvars ; i_++)
tmp[i_]=xy.Get(i,i_);
for(i_=0; i_<nvars ; i_++)
tmp[i_]=tmp[i_]-c[i_][xyc[i]];
//--- change value
v=0.0;
for(i_=0; i_<nvars ; i_++)
v+=tmp[i_]*tmp[i_];
ekmeans=ekmeans+v;
}
//--- calculation
erandom=0;
for(i=0; i<npoints ; i++)
{
dclosest=CMath::m_maxrealnumber;
v=0;
for(j=0; j<nc; j++)
{
for(i_=0; i_<nvars ; i_++)
tmp[i_]=xy.Get(i,i_);
for(i_=0; i_<nvars ; i_++)
tmp[i_]=tmp[i_]-xy.Get(j,i_);
//--- change value
v=0.0;
for(i_=0; i_<nvars ; 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. |
//+------------------------------------------------------------------+
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 ; i++)
{
RSphere(xy,nvars,i);
for(j=0; j<nvars ; j++)
xy.Set(i,j,xy.Get(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 ; i++)
{
for(i_=0; i_<nvars ; i_++)
tmp[i_]=xy.Get(i,i_);
for(i_=0; i_<nvars ; i_++)
tmp[i_]=tmp[i_]-ca[i_][xyca[i]];
//--- change value
v=0.0;
for(i_=0; i_<nvars ; 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 ; i++)
{
for(i_=0; i_<nvars ; i_++)
tmp[i_]=xy.Get(i,i_);
for(i_=0; i_<nvars ; i_++)
tmp[i_]=tmp[i_]-cb[i_][xycb[i]];
//--- change value
v=0.0;
for(i_=0; i_<nvars ; 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 |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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; j++)
xy.Set(i,j,RNormal());
v=0.0;
for(i_=0; i_<n; i_++)
v+=xy.Get(i,i_)*xy.Get(i,i_);
//--- calculation
v=CMath::RandomReal()/MathSqrt(v);
for(i_=0; i_<n; i_++)
xy.Set(i,i_,v*xy.Get(i,i_));
}
//+------------------------------------------------------------------+
//| Testing class CHblas |
//+------------------------------------------------------------------+
class CTestHblasUnit
{
public:
static bool TestHblas(const bool silent);
};
//+------------------------------------------------------------------+
//| Testing class CHblas |
//+------------------------------------------------------------------+
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.SetRe(i,i,2*CMath::RandomReal()-1);
a.SetIm(i,i,0);
//--- change values
for(j=i+1; j<=n; j++)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
a.Set(j,i,CMath::Conj(a.Get(i,j)));
}
}
//--- change values
for(i=1; i<=n; i++)
{
for(j=1; j<=n; j++)
ua.Set(i,j,0);
}
//--- change values
for(i=1; i<=n; i++)
{
for(j=i; j<=n; j++)
ua.Set(i,j,a.Get(i,j));
}
//--- change values
for(i=1; i<=n; i++)
{
for(j=1; j<=n; j++)
la.Set(i,j,0);
}
//--- change values
for(i=1; i<=n; i++)
{
for(j=1; j<=i; j++)
la.Set(i,j,a.Get(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].real=2*CMath::RandomReal()-1;
x[i].imag=2*CMath::RandomReal()-1;
}
alpha.real=2*CMath::RandomReal()-1;
alpha.imag=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.Get(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");
PrintFormat("MV error: %5.3E",mverr);
PrintFormat("Threshold: %5.3E",threshold);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CReflections |
//+------------------------------------------------------------------+
class CTestReflectionsUnit
{
public:
static bool TestReflections(const bool silent);
};
//+------------------------------------------------------------------+
//| Testing class CReflections |
//+------------------------------------------------------------------+
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.Set(i,j,1-tau*v[i]*v[j]);
else
h.Set(i,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.Get(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.Set(i,j,2*CMath::RandomReal()-1);
b.Set(i,j,a.Get(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.Set(i,j,1-tau*v[i]*v[j]);
else
h.Set(i,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.Get(i,i_)*a.Get(i_,j);
c.Set(i,j,tmp);
}
}
//--- change value
err=0;
for(i=1; i<=m; i++)
{
for(j=1; j<=n; j++)
err=MathMax(err,MathAbs(b.Get(i,j)-c.Get(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.Set(i,j,2*CMath::RandomReal()-1);
b.Set(i,j,a.Get(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.Set(i,j,1-tau*v[i]*v[j]);
else
h.Set(i,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.Get(i,i_)*h.Get(i_,j);
c.Set(i,j,tmp);
}
}
err=0;
for(i=1; i<=m; i++)
{
for(j=1; j<=n; j++)
err=MathMax(err,MathAbs(b.Get(i,j)-c.Get(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");
PrintFormat("Pass count is %d",passcount);
PrintFormat("Generate absolute error is %5.3E",meg);
PrintFormat("Apply(Left) absolute error is %5.3E",mel);
PrintFormat("Apply(Right) absolute error is %5.3E",mer);
Print("Overflow crash test passed");
//--- check
PrintResult("TEST SUMMARY",result);
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Testing class CComplexReflections |
//+------------------------------------------------------------------+
class CTestCReflectionsUnit
{
public:
static bool TestCReflections(const bool silent);
};
//+------------------------------------------------------------------+
//| Testing class CComplexReflections |
//+------------------------------------------------------------------+
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].real=2*CMath::RandomReal()-1;
x[i].imag=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.Set(i,j,-tau*v[i]*CMath::Conj(v[j])+1);
else
h.Set(i,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.Get(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.imag));
meg=MathMax(meg,err);
//--- ApplyReflectionFromTheLeft
for(i=1; i<=m; i++)
{
x[i].real=2*CMath::RandomReal()-1;
x[i].imag=2*CMath::RandomReal()-1;
v[i]=x[i];
}
for(i=1; i<=m; i++)
{
for(j=1; j<=n; j++)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
b.Set(i,j,a.Get(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.Set(i,j,-tau*v[i]*CMath::Conj(v[j])+1);
else
h.Set(i,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.Get(i,i_)*a.Get(i_,j);
c.Set(i,j,tmp);
}
}
err=0;
for(i=1; i<=m; i++)
{
for(j=1; j<=n; j++)
err=MathMax(err,CMath::AbsComplex(b.Get(i,j)-c.Get(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.Set(i,j,2*CMath::RandomReal()-1);
b.Set(i,j,a.Get(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.Set(i,j,-tau*v[i]*CMath::Conj(v[j])+1);
else
h.Set(i,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.Get(i,i_)*h.Get(i_,j);
c.Set(i,j,tmp);
}
}
err=0;
for(i=1; i<=m; i++)
{
for(j=1; j<=n; j++)
err=MathMax(err,CMath::AbsComplex(b.Get(i,j)-c.Get(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");
PrintFormat("Generate error: %5.3E",meg);
PrintFormat("Apply(L) error: %5.3E",mel);
PrintFormat("Apply(R) error: %5.3E",mer);
PrintFormat("Threshold: %5.3E",threshold);
PrintResult("Overflow crash test",true);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CSblas |
//+------------------------------------------------------------------+
class CTestSblasUnit
{
public:
static bool TestSblas(const bool silent);
};
//+------------------------------------------------------------------+
//| Testing class CSblas |
//+------------------------------------------------------------------+
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.Set(i,i,2*CMath::RandomReal()-1);
for(j=i+1; j<=n; j++)
{
a.Set(i,j,2*CMath::RandomReal()-1);
a.Set(j,i,a.Get(i,j));
}
}
//--- change values
for(i=1; i<=n; i++)
{
for(j=1; j<=n; j++)
ua.Set(i,j,0);
}
//--- change values
for(i=1; i<=n; i++)
{
for(j=i; j<=n; j++)
ua.Set(i,j,a.Get(i,j));
}
//--- change values
for(i=1; i<=n; i++)
{
for(j=1; j<=n; j++)
la.Set(i,j,0);
}
//--- change values
for(i=1; i<=n; i++)
{
for(j=1; j<=i; j++)
la.Set(i,j,a.Get(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.Get(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");
PrintFormat("MV error: %5.3E",mverr);
PrintFormat("Threshold: %5.3E",threshold);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class COrtFac |
//+------------------------------------------------------------------+
class CTestOrtFacUnit
{
public:
static bool TestOrtFac(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Main unittest subroutine |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
ra.Set(i,j,0);
ca.Set(i,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; i++)
{
for(j=0; j<n; j++)
{
ra.Set(i,j,2*CMath::RandomReal()-1);
ca.SetRe(i,j,2*CMath::RandomReal()-1);
ca.SetIm(i,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.Set(i,j,0);
ca.Set(i,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.Set(i,j,2*CMath::RandomReal()-1);
ca.SetRe(i,j,2*CMath::RandomReal()-1);
ca.SetIm(i,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=matrix<double>::Zeros(mx,mx);
ca=matrix<complex>::Zeros(mx,mx);
//--- function calls
TestRTdProblem(ra,mx,threshold,rtderrors);
TestCTdProblem(ca,mx,threshold,ctderrors);
//--- change values
for(i=0; i<mx; i++)
{
for(j=i; j<mx; j++)
{
ra.Set(i,j,2*CMath::RandomReal()-1);
ca.SetRe(i,j,2*CMath::RandomReal()-1);
ca.SetIm(i,j,2*CMath::RandomReal()-1);
ra.Set(j,i,ra.Get(i,j));
ca.Set(j,i,CMath::Conj(ca.Get(i,j)));
}
}
for(i=0; i<mx; i++)
ca.Set(i,i,((i+1.0)*(i+1.0))/(mx*mx)-0.5); //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.Set(j,i,ra.Get(i,j));
ca.Set(j,i,CMath::Conj(ca.Get(i,j)));
}
}
for(i=0; i<=mx-1; i++)
ca.Set(i,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");
PrintResult("RQR ERRORS",!rqrerrors);
PrintResult("RLQ ERRORS",!rlqerrors);
PrintResult("CQR ERRORS",!cqrerrors);
PrintResult("CLQ ERRORS",!clqerrors);
PrintResult("RBD ERRORS",!rbderrors);
PrintResult("RHESS ERRORS",!rhesserrors);
PrintResult("RTD ERRORS",!rtderrors);
PrintResult("CTD ERRORS",!ctderrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Diff |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
result=MathMax(result,MathAbs(b.Get(i,j)-a.Get(i,j)));
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
b.Set(i,j,a.Get(i,j));
}
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
b.Set(i,j,a.Get(i,j));
}
}
//+------------------------------------------------------------------+
//| Sparse fill |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(CMath::RandomReal()>=sparcity)
a.Set(i,j,2*CMath::RandomReal()-1);
else
a.Set(i,j,0);
}
}
}
//+------------------------------------------------------------------+
//| Sparse fill |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(CMath::RandomReal()>=sparcity)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
}
else
a.Set(i,j,0);
}
}
}
//+------------------------------------------------------------------+
//| Matrix multiplication |
//+------------------------------------------------------------------+
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]=i;
//--- Prepare C
if(beta==0.0)
{
for(i=ci1; i<=ci2; i++)
{
for(j=cj1; j<=cj2; j++)
c.Set(i,j,0);
}
}
else
{
for(i=ci1; i<=ci2; i++)
{
for(i_=cj1; i_<=cj2; i_++)
c.Set(i,i_,beta*c.Get(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.Set(k,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.Set(ci1+l-ai1,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.Set(ci1+l-ai1,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.Set(k,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;
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.Set(i_,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.Set(ci1+l-aj1,cj1+r-bi1,c[ci1+l-aj1][cj1+r-bi1]+alpha*v);
}
}
return;
}
}
}
//+------------------------------------------------------------------+
//| Problem testing |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
v=0.0;
for(i_=0; i_<m; i_++)
v+=q.Get(i,i_)*r.Get(i_,j);
qrerrors=qrerrors||MathAbs(v-a.Get(i,j))>threshold;
}
}
//--- search errors
for(i=0; i<m; i++)
{
for(j=0; j<=MathMin(i,n-1)-1; j++)
qrerrors=qrerrors||r.Get(i,j)!=0.0;
}
for(i=0; i<m; i++)
{
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<m; i_++)
v+=q.Get(i,i_)*q.Get(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; i++)
{
for(j=0; j<k; j++)
qrerrors=qrerrors||MathAbs(q2.Get(i,j)-q.Get(i,j))>10*CMath::m_machineepsilon;
}
}
//+------------------------------------------------------------------+
//| Problem testing |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<m; i_++)
v+=q.Get(i,i_)*r.Get(i_,j);
qrerrors=qrerrors||CMath::AbsComplex(v-a.Get(i,j))>threshold;
}
}
//--- search errors
for(i=0; i<m; i++)
{
for(j=0; j<=MathMin(i,n-1)-1; j++)
qrerrors=qrerrors||r.Get(i,j)!=0;
}
for(i=0; i<m; i++)
{
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<m; i_++)
v+=q.Get(i,i_)*CMath::Conj(q.Get(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; i++)
{
for(j=0; j<k; j++)
qrerrors=qrerrors||CMath::AbsComplex(q2.Get(i,j)-q.Get(i,j))>10*CMath::m_machineepsilon;
}
}
//+------------------------------------------------------------------+
//| Problem testing |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=l.Get(i,i_)*q.Get(i_,j);
lqerrors=lqerrors||MathAbs(v-a.Get(i,j))>=threshold;
}
}
//--- search errors
for(i=0; i<m; i++)
{
for(j=MathMin(i,n-1)+1; j<n; j++)
lqerrors=lqerrors||l.Get(i,j)!=0.0;
}
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=q.Get(i,i_)*q.Get(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; i++)
{
for(j=0; j<n; j++)
lqerrors=lqerrors||MathAbs(q2.Get(i,j)-q.Get(i,j))>10*CMath::m_machineepsilon;
}
}
//+------------------------------------------------------------------+
//| Problem testing |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=l.Get(i,i_)*q.Get(i_,j);
lqerrors=lqerrors||CMath::AbsComplex(v-a.Get(i,j))>=threshold;
}
}
//--- search errors
for(i=0; i<m; i++)
{
for(j=MathMin(i,n-1)+1; j<n; j++)
lqerrors=lqerrors||l.Get(i,j)!=0;
}
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=q.Get(i,i_)*CMath::Conj(q.Get(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; i++)
{
for(j=0; j<n; j++)
lqerrors=lqerrors||CMath::AbsComplex(q2.Get(i,j)-q.Get(i,j))>10*CMath::m_machineepsilon;
}
}
//+------------------------------------------------------------------+
//| Problem testing |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
bd.Set(i,j,0);
}
for(i=0; i<=MathMin(m,n)-1; i++)
bd.Set(i,i,d[i]);
//--- check
if(up)
{
for(i=0; i<=MathMin(m,n)-2; i++)
bd.Set(i,i+1,e[i]);
}
else
{
for(i=0; i<=MathMin(m,n)-2; i++)
bd.Set(i+1,i,e[i]);
}
//--- allocation
r.Resize(m,n);
//--- calculation
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
{
v=0.0;
for(i_=0; i_<m; i_++)
v+=q.Get(i,i_)*bd.Get(i_,j);
r.Set(i,j,v);
}
}
//--- search errors
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
{
v=0.0;
for(i_=0; i_<n; i_++)
v+=r.Get(i,i_)*pt.Get(i_,j);
bderrors=bderrors||MathAbs(v-a.Get(i,j))>threshold;
}
}
//--- Orthogonality test for Q/PT
for(i=0; i<m; i++)
{
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<m; i_++)
v+=q.Get(i_,i)*q.Get(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; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=CAblasF::RDotRR(n,pt,i,pt,j);
//--- 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; i++)
{
for(j=0; j<k; j++)
bderrors=bderrors||MathAbs(r.Get(i,j)-q.Get(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; i++)
{
for(j=0; j<n; j++)
bderrors=bderrors||r.Get(i,j)-pt.Get(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.Set(i,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 |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=q.Get(i_,i)*q.Get(i_,j);
//--- check
if(i==j)
v=v-1;
hesserrors=hesserrors||MathAbs(v)>threshold;
}
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<=i-2; j++)
hesserrors=hesserrors||h.Get(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 |
//+------------------------------------------------------------------+
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
CRowDouble tau;
CRowDouble d;
CRowDouble e;
//--- create matrix
CMatrixDouble ua;
CMatrixDouble la;
CMatrixDouble t;
CMatrixDouble q;
CMatrixDouble t2;
CMatrixDouble t3;
//--- allocation
ua=a.TriU()+0;
la=a.TriL()+0;
t=matrix<double>::Zeros(n,n);
q=matrix<double>::Zeros(n,n);
t2=matrix<double>::Zeros(n,n);
t3=matrix<double>::Zeros(n,n);
//--- Test 2tridiagonal: upper
COrtFac::SMatrixTD(ua,n,true,tau,d,e);
COrtFac::SMatrixTDUnpackQ(ua,n,true,tau,q);
//--- change values
t.Diag(d);
t.Diag(e,1);
t.Diag(e,-1);
//--- calculation
t2=q.Transpose()+0;
t2=t2.MatMul(a)+0;
t3=t2.MatMul(q)+0;
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
tderrors=tderrors||MathAbs(t3.Get(i,j)-t.Get(i,j))>threshold;
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
v=0.0;
for(i_=0; i_<n; i_++)
v+=q.Get(i,i_)*q.Get(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
t.Diag(d);
t.Diag(e,1);
t.Diag(e,-1);
//--- calculation
t2=q.Transpose()+0;
t2=t2.MatMul(a)+0;
t3=t2.MatMul(q)+0;
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
tderrors=tderrors||MathAbs(t3.Get(i,j)-t.Get(i,j))>threshold;
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
v=0.0;
for(i_=0; i_<n; i_++)
v+=q.Get(i,i_)*q.Get(j,i_);
//--- check
if(i==j)
v=v-1;
tderrors=tderrors||MathAbs(v)>threshold;
}
}
}
//+------------------------------------------------------------------+
//| Hermitian problem tester |
//+------------------------------------------------------------------+
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
CRowComplex tau;
CRowDouble d;
CRowDouble e;
//--- create matrix
CMatrixComplex ua;
CMatrixComplex la;
CMatrixComplex t;
CMatrixComplex q;
CMatrixComplex t2;
CMatrixComplex t3;
//--- allocation
ua=a.TriU()+0;
la=a.TriL()+0;
t=matrix<complex>::Full(n,n,0);
q=matrix<complex>::Full(n,n,0);
t2=matrix<complex>::Full(n,n,0);
t3=matrix<complex>::Full(n,n,0);
//--- 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; i++)
t.Set(i,i,d[i]);
for(i=0; i<=n-2; i++)
{
t.Set(i,i+1,e[i]);
t.Set(i+1,i,e[i]);
}
//--- calculation
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=CMath::Conj(q.Get(i_,i))*a.Get(i_,j);
t2.Set(i,j,v);
}
}
//--- calculation
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=t2.Get(i,i_)*q.Get(i_,j);
t3.Set(i,j,v);
}
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
tderrors=tderrors||CMath::AbsComplex(t3.Get(i,j)-t.Get(i,j))>threshold;
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
v=0.0;
for(i_=0; i_<n; i_++)
v+=q.Get(i,i_)*CMath::Conj(q.Get(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; i++)
{
for(j=0; j<n; j++)
t.Set(i,j,0);
}
for(i=0; i<n; i++)
t.Set(i,i,d[i]);
for(i=0; i<=n-2; i++)
{
t.Set(i,i+1,e[i]);
t.Set(i+1,i,e[i]);
}
//--- calculation
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=CMath::Conj(q.Get(i_,i))*a.Get(i_,j);
t2.Set(i,j,v);
}
}
//--- calculation
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=t2.Get(i,i_)*q.Get(i_,j);
t3.Set(i,j,v);
}
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
tderrors=tderrors||CMath::AbsComplex(t3.Get(i,j)-t.Get(i,j))>threshold;
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
v=0.0;
for(i_=0; i_<n; i_++)
v+=q.Get(i,i_)*CMath::Conj(q.Get(j,i_));
//--- check
if(i==j)
v=v-1;
tderrors=tderrors||CMath::AbsComplex(v)>threshold;
}
}
}
//+------------------------------------------------------------------+
//| Testing class CEigenVDetect |
//+------------------------------------------------------------------+
class CTestEVDUnit
{
public:
static bool TestEVD(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Testing symmetric EVD subroutine |
//+------------------------------------------------------------------+
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");
PrintResult("NS ERRORS",!nserrors);
PrintResult("S ERRORS",!serrors);
PrintResult("H ERRORS",!herrors);
PrintResult("TD ERRORS",!tderrors);
PrintResult("SBI ERRORS",!sbierrors);
PrintResult("HBI ERRORS",!hbierrors);
PrintResult("TDBI ERRORS",!tdbierrors);
PrintResult("FAILURE THRESHOLD",!wfailed);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Sparse fill |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(CMath::RandomReal()>=sparcity)
a.Set(i,j,2*CMath::RandomReal()-1);
else
a.Set(i,j,0);
}
}
}
//+------------------------------------------------------------------+
//| Sparse fill |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(CMath::RandomReal()>=sparcity)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
}
else
a.Set(i,j,0);
}
}
}
//+------------------------------------------------------------------+
//| Copies A to AL (lower half) and AU (upper half),filling unused |
//| parts by random garbage. |
//+------------------------------------------------------------------+
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; i++)
{
for(j=i+1; j<n; j++)
{
al.Set(i,j,2*CMath::RandomReal()-1);
al.Set(j,i,a.Get(i,j));
au.Set(i,j,a.Get(i,j));
au.Set(j,i,2*CMath::RandomReal()-1);
}
al.Set(i,i,a[i][i]);
au.Set(i,i,a[i][i]);
}
}
//+------------------------------------------------------------------+
//| Copies A to AL (lower half) and AU (upper half),filling unused |
//| parts by random garbage. |
//+------------------------------------------------------------------+
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; i++)
{
for(j=i+1; j<n; j++)
{
al.Set(i,j,2*CMath::RandomReal()-1);
al.Set(j,i,CMath::Conj(a.Get(i,j)));
au.Set(i,j,a.Get(i,j));
au.Set(j,i,2*CMath::RandomReal()-1);
}
al.Set(i,i,a[i][i]);
au.Set(i,i,a[i][i]);
}
}
//+------------------------------------------------------------------+
//| Unsets 2D array. |
//+------------------------------------------------------------------+
void CTestEVDUnit::Unset2D(CMatrixDouble &a)
{
//--- allocation
a.Resize(1,1);
//--- change value
a.Set(0,0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets 2D array. |
//+------------------------------------------------------------------+
void CTestEVDUnit::CUnset2D(CMatrixComplex &a)
{
//--- allocation
a.Resize(1,1);
//--- change value
a.Set(0,0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets 1D array. |
//+------------------------------------------------------------------+
void CTestEVDUnit::Unset1D(double &a[])
{
//--- allocation
ArrayResize(a,1);
//--- change value
a[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Unsets 1D array. |
//+------------------------------------------------------------------+
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. |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- Calculate V=A.Get(i,j),A=Z*Lambda*Z'
v=0;
for(k=0; k<n; k++)
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; 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. |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- Calculate V=A.Get(i,j),A=Z*Lambda*Z'
v=0;
for(k=0; k<n; k++)
v+=z[i][k]*lambdav[k]*z[j][k];
//--- Compare
result=MathMax(result,MathAbs(v-a.Get(i,j)));
}
}
//--- change value
mx=0;
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
mx=MathMax(mx,MathAbs(a.Get(i,j)));
}
//--- check
if(mx==0.0)
mx=1;
//--- return result
return(result/mx);
}
//+------------------------------------------------------------------+
//| Tests Z*Z' against diag(1...1) |
//| Returns absolute error. |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=z.Get(i_,i)*z.Get(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. |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- Calculate V=A.Get(i,j),A=Z*Lambda*Z'
v=0;
for(k=0; k<n; k++)
v+=z[i][k]*lambdav[k]*CMath::Conj(z[j][k]);
//--- Compare
result=MathMax(result,CMath::AbsComplex(v-a.Get(i,j)));
}
}
//--- change value
mx=0;
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
mx=MathMax(mx,CMath::AbsComplex(a.Get(i,j)));
}
//--- check
if(mx==0.0)
mx=1;
//--- return result
return(result/mx);
}
//+------------------------------------------------------------------+
//| Tests Z*Z' against diag(1...1) |
//| Returns absolute error. |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=z.Get(i_,i)*CMath::Conj(z.Get(i_,j));
//--- check
if(i==j)
v=v-1;
result=MathMax(result,CMath::AbsComplex(v));
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Tests SEVD problem |
//+------------------------------------------------------------------+
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; 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; i++)
serrors=serrors||MathAbs(lambdav[i]-lambdaref[i])>threshold;
}
//+------------------------------------------------------------------+
//| Tests SEVD problem |
//+------------------------------------------------------------------+
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; 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; 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. |
//+------------------------------------------------------------------+
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; 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; 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; 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; 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; k++)
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- Distinct eigenvalues,test vectors
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=z.Get(i_,j)*zref[i_][i1+j];
//--- check
if(v<0.0)
{
for(i_=0; i_<n; i_++)
z.Set(i_,j,-1*z.Get(i_,j));
}
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
serrors=serrors||MathAbs(z.Get(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; k++)
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- Distinct eigenvalues,test vectors
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=z.Get(i_,j)*zref[i_][i1+j];
//--- check
if(v<0.0)
{
for(i_=0; i_<n; i_++)
z.Set(i_,j,-1*z.Get(i_,j));
}
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
serrors=serrors||MathAbs(z.Get(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; k++)
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- Distinct eigenvalues,test vectors
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=z.Get(i_,j)*zref[i_][i1+j];
//--- check
if(v<0.0)
{
for(i_=0; i_<n; i_++)
z.Set(i_,j,-1*z.Get(i_,j));
}
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
serrors=serrors||MathAbs(z.Get(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; k++)
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- Distinct eigenvalues,test vectors
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=z.Get(i_,j)*zref[i_][i1+j];
//--- check
if(v<0.0)
{
for(i_=0; i_<n; i_++)
z.Set(i_,j,-1*z.Get(i_,j));
}
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
serrors=serrors||MathAbs(z.Get(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. |
//+------------------------------------------------------------------+
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; 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; 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; 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; 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; k++)
herrors=herrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- Distinct eigenvalues,test vectors
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=z.Get(i_,j)*CMath::Conj(zref[i_][i1+j]);
v=CMath::Conj(v/CMath::AbsComplex(v));
//--- calculation
for(i_=0; i_<n; i_++)
z.Set(i_,j,v*z.Get(i_,j));
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
herrors=herrors||CMath::AbsComplex(z.Get(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; k++)
herrors=herrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- Distinct eigenvalues,test vectors
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=z.Get(i_,j)*CMath::Conj(zref[i_][i1+j]);
v=CMath::Conj(v/CMath::AbsComplex(v));
//--- calculation
for(i_=0; i_<n; i_++)
z.Set(i_,j,v*z.Get(i_,j));
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
herrors=herrors||CMath::AbsComplex(z.Get(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; k++)
herrors=herrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- Distinct eigenvalues,test vectors
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=z.Get(i_,j)*CMath::Conj(zref[i_][i1+j]);
v=CMath::Conj(v/CMath::AbsComplex(v));
//--- calculation
for(i_=0; i_<n; i_++)
z.Set(i_,j,v*z.Get(i_,j));
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
herrors=herrors||CMath::AbsComplex(z.Get(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; k++)
herrors=herrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- Distinct eigenvalues,test vectors
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=z.Get(i_,j)*CMath::Conj(zref[i_][i1+j]);
v=CMath::Conj(v/CMath::AbsComplex(v));
//--- calculation
for(i_=0; i_<n; i_++)
z.Set(i_,j,v*z.Get(i_,j));
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
herrors=herrors||CMath::AbsComplex(z.Get(i,j)-zref[i][i1+j])>threshold;
}
}
}
//+------------------------------------------------------------------+
//| Tests EVD problem |
//+------------------------------------------------------------------+
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; 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; i++)
{
for(j=0; j<n; j++)
zref.Set(i,j,z.Get(i,j));
}
//--- Test values only variant
for(i=0; i<n; 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; i++)
tderrors=tderrors||MathAbs(lambda2[i]-lambdav[i])>threshold;
//--- Test multiplication variant
for(i=0; i<n; i++)
lambda2[i]=d[i];
for(i=0; i<=n-2; i++)
ee[i]=e[i];
//--- change values
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
a1.Set(i,j,2*CMath::RandomReal()-1);
a2.Set(i,j,a1.Get(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; i++)
tderrors=tderrors||MathAbs(lambda2[i]-lambdav[i])>threshold;
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a2.Get(i,i_)*zref.Get(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.Get(i,j))>threshold && MathAbs(v+a1.Get(i,j))>threshold);
}
}
//--- Test first row variant
for(i=0; i<n; 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; 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. |
//+------------------------------------------------------------------+
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; 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; 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; k++)
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- Test indexes,no vectors
ArrayResize(lambdav,n);
for(i=0; i<n; 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; k++)
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- Test interval,transform vectors
ArrayResize(lambdav,n);
for(i=0; i<n; i++)
lambdav[i]=d[i];
//--- allocation
a1.Resize(n,n);
a2.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
a1.Set(i,j,2*CMath::RandomReal()-1);
a2.Set(i,j,a1.Get(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; k++)
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- allocation
ar.Resize(n,m);
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a2.Get(i,i_)*zref[i_][i1+j];
ar.Set(i,j,v);
}
}
//--- calculation
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a1.Get(i_,j)*ar.Get(i_,j);
//--- check
if(v<0.0)
{
for(i_=0; i_<n; i_++)
ar.Set(i_,j,-1*ar.Get(i_,j));
}
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
serrors=serrors||MathAbs(a1.Get(i,j)-ar.Get(i,j))>threshold;
}
}
//--- Test indexes,transform vectors
ArrayResize(lambdav,n);
for(i=0; i<n; i++)
lambdav[i]=d[i];
//--- allocation
a1.Resize(n,n);
a2.Resize(n,n);
//--- change values
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
a1.Set(i,j,2*CMath::RandomReal()-1);
a2.Set(i,j,a1.Get(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; k++)
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
//--- allocation
ar.Resize(n,m);
//--- calculation
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a2.Get(i,i_)*zref[i_][i1+j];
ar.Set(i,j,v);
}
}
//--- calculation
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a1.Get(i_,j)*ar.Get(i_,j);
//--- check
if(v<0.0)
{
for(i_=0; i_<n; i_++)
ar.Set(i_,j,-1*ar.Get(i_,j));
}
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
serrors=serrors||MathAbs(a1.Get(i,j)-ar.Get(i,j))>threshold;
}
}
//--- Test interval,do not transform vectors
ArrayResize(lambdav,n);
for(i=0; i<n; 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; k++)
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=z.Get(i_,j)*zref[i_][i1+j];
//--- check
if(v<0.0)
{
for(i_=0; i_<n; i_++)
z.Set(i_,j,-1*z.Get(i_,j));
}
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
serrors=serrors||MathAbs(z.Get(i,j)-zref[i][i1+j])>threshold;
}
}
//--- Test indexes,do not transform vectors
ArrayResize(lambdav,n);
for(i=0; i<n; 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; k++)
serrors=serrors||MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
//--- check
if(distvals)
{
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=z.Get(i_,j)*zref[i_][i1+j];
//--- check
if(v<0.0)
{
for(i_=0; i_<n; i_++)
z.Set(i_,j,-1*z.Get(i_,j));
}
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
serrors=serrors||MathAbs(z.Get(i,j)-zref[i][i1+j])>threshold;
}
}
}
//+------------------------------------------------------------------+
//| Non-symmetric problem |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(MathAbs(a.Get(i,j))>mx)
mx=MathAbs(a.Get(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; i_++)
wr0s[i_]=wr0[i_];
for(i_=0; i_<n; i_++)
wi0s[i_]=wi0[i_];
for(i=0; i<n; 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; i_++)
wr1s[i_]=wr1[i_];
for(i_=0; i_<n; i_++)
wi1s[i_]=wi1[i_];
//--- swap
for(i=0; i<n; 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; i++)
{
CAp::SetErrorFlag(nserrors,MathAbs(wr0s[i]-wr1s[i])>threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob));;
CAp::SetErrorFlag(nserrors,MathAbs(wi0s[i]-wi1s[i])>threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob));;
}
//--- Test right vectors
if(needr)
{
k=0;
//--- calculation
while(k<=n-1)
{
//--- check
if(wi1[k]==0.0)
{
for(i_=0; i_<n; i_++)
vec1r[i_]=vr[i_][k];
for(i=0; i<n; i++)
vec1i[i]=0;
curwr=wr1[k];
curwi=0;
}
//--- check
if(wi1[k]>0.0)
{
for(i_=0; i_<n; i_++)
vec1r[i_]=vr[i_][k];
for(i_=0; i_<n; i_++)
vec1i[i_]=vr[i_][k+1];
curwr=wr1[k];
curwi=wi1[k];
}
//--- check
if(wi1[k]<0.0)
{
for(i_=0; i_<n; i_++)
vec1r[i_]=vr[i_][k-1];
for(i_=0; i_<n; i_++)
vec1i[i_]=-vr[i_][k];
curwr=wr1[k];
curwi=wi1[k];
}
//--- calculation
for(i=0; i<n; i++)
{
vt=0.0;
for(i_=0; i_<n; i_++)
vt+=a.Get(i,i_)*vec1r[i_];
vec2r[i]=vt;
vt=0.0;
for(i_=0; i_<n; i_++)
vt+=a.Get(i,i_)*vec1i[i_];
vec2i[i]=vt;
}
//--- change values
for(i_=0; i_<n; i_++)
vec3r[i_]=curwr*vec1r[i_];
for(i_=0; i_<n; i_++)
vec3r[i_]=vec3r[i_]-curwi*vec1i[i_];
for(i_=0; i_<n; i_++)
vec3i[i_]=curwi*vec1r[i_];
for(i_=0; i_<n; i_++)
vec3i[i_]=vec3i[i_]+curwr*vec1i[i_];
//--- search errors
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(nserrors,MathAbs(vec2r[i]-vec3r[i])>threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob));
CAp::SetErrorFlag(nserrors,MathAbs(vec2i[i]-vec3i[i])>threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob));
}
k++;
}
}
//--- Test left vectors
if(needl)
{
k=0;
//--- calculation
while(k<=n-1)
{
//--- check
if(wi1[k]==0.0)
{
for(i_=0; i_<n; i_++)
vec1r[i_]=vl[i_][k];
for(i=0; i<n; i++)
vec1i[i]=0;
curwr=wr1[k];
curwi=0;
}
//--- check
if(wi1[k]>0.0)
{
for(i_=0; i_<n; i_++)
vec1r[i_]=vl[i_][k];
for(i_=0; i_<n; i_++)
vec1i[i_]=vl[i_][k+1];
curwr=wr1[k];
curwi=wi1[k];
}
//--- check
if(wi1[k]<0.0)
{
for(i_=0; i_<n; i_++)
vec1r[i_]=vl[i_][k-1];
for(i_=0; i_<n; i_++)
vec1i[i_]=-vl[i_][k];
curwr=wr1[k];
curwi=wi1[k];
}
//--- calculation
for(j=0; j<n; j++)
{
vt=0.0;
for(i_=0; i_<n; i_++)
vt+=vec1r[i_]*a.Get(i_,j);
vec2r[j]=vt;
vt=0.0;
for(i_=0; i_<n; i_++)
vt+=vec1i[i_]*a.Get(i_,j);
vec2i[j]=-vt;
}
//--- change values
for(i_=0; i_<n; i_++)
vec3r[i_]=curwr*vec1r[i_];
for(i_=0; i_<n; i_++)
vec3r[i_]=vec3r[i_]+curwi*vec1i[i_];
for(i_=0; i_<n; i_++)
vec3i[i_]=curwi*vec1r[i_];
for(i_=0; i_<n; i_++)
vec3i[i_]=vec3i[i_]-curwr*vec1i[i_];
//--- search errors
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(nserrors,MathAbs(vec2r[i]-vec3r[i])>threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob));
CAp::SetErrorFlag(nserrors,MathAbs(vec2i[i]-vec3i[i])>threshold,StringFormat("%s: %d vjob=%d",__FUNCTION__,__LINE__,vjob));
}
k++;
}
}
}
}
//+------------------------------------------------------------------+
//| 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. |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
ra.Set(i,j,0);
ca.Set(i,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; i++)
{
for(j=i+1; j<n; j++)
{
ra.Set(i,j,2*CMath::RandomReal()-1);
ca.SetRe(i,j,2*CMath::RandomReal()-1);
ca.SetIm(i,j,2*CMath::RandomReal()-1);
ra.Set(j,i,ra.Get(i,j));
ca.Set(j,i,CMath::Conj(ca.Get(i,j)));
}
//--- change values
ra.Set(i,i,2*CMath::RandomReal()-1);
ca.Set(i,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; i++)
{
for(j=i+1; j<n; j++)
{
ra.Set(i,j,0.1*(2*CMath::RandomReal()-1)/n);
ca.SetRe(i,j,0.1*(2*CMath::RandomReal()-1)/n);
ca.SetIm(i,j,0.1*(2*CMath::RandomReal()-1)/n);
ra.Set(j,i,ra.Get(i,j));
ca.Set(j,i,CMath::Conj(ca.Get(i,j)));
}
//--- change values
ra.Set(i,i,0.1*(2*CMath::RandomReal()-1)+i);
ca.Set(i,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; i++)
{
for(j=i+1; j<n; j++)
{
ra.Set(j,i,ra.Get(i,j));
ca.Set(j,i,CMath::Conj(ca.Get(i,j)));
}
ca.SetIm(i,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; 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; 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; 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; 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; 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; 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; 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; 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; i++)
{
for(j=0; j<n; j++)
{
ra.Set(i,j,0);
ca.Set(i,j,0);
}
}
//--- function call
TestNSEVDProblem(ra,n,threshold,nserrors,failc,runs);
//--- change values
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
ra.Set(i,j,2*CMath::RandomReal()-1);
ca.SetRe(i,j,2*CMath::RandomReal()-1);
ca.SetIm(i,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
{
public:
//--- class constant
static const int m_maxsvditerations;
//--- public method
static bool TestMatGen(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Initialize constant |
//+------------------------------------------------------------------+
const int CTestMatGenUnit::m_maxsvditerations=60;
//+------------------------------------------------------------------+
//| Testing class CMatGen |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- orthogonality test
vt=0.0;
for(i_=0; i_<n; i_++)
vt+=a.Get(i,i_)*a.Get(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; i_++)
vt+=b.Get(i,i_)*b.Get(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.Get(i,j)==b.Get(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; i++)
{
for(j=0; j<n; j++)
{
//--- orthogonality test
ct=0.0;
for(i_=0; i_<n; i_++)
ct+=ca.Get(i,i_)*CMath::Conj(ca.Get(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; i_++)
ct+=cb.Get(i,i_)*CMath::Conj(cb.Get(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.Get(i,j)==cb.Get(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; i++)
{
for(j=0; j<n; j++)
{
a.Set(i,j,0);
b.Set(i,j,0);
}
a.Set(i,i,1);
b.Set(i,i,1);
}
//--- function call
CMatGen::RMatrixRndOrthogonalFromTheRight(a,n,n);
//--- function call
CMatGen::RMatrixRndOrthogonalFromTheRight(b,n,n);
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- orthogonality test
vt=0.0;
for(i_=0; i_<n; i_++)
vt+=a.Get(i,i_)*a.Get(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; i_++)
vt+=b.Get(i,i_)*b.Get(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.Get(i,j)==b.Get(i,j);
}
}
//--- change values
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
r2.Set(i,j,2*CMath::RandomReal()-1);
r2.Set(i+n,j,r2.Get(i,j));
}
}
//--- function call
CMatGen::RMatrixRndOrthogonalFromTheRight(r2,2*n,n);
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
rerr=rerr||MathAbs(r2[i+n][j]-r2.Get(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; i++)
{
for(j=0; j<n; j++)
{
a.Set(i,j,0);
b.Set(i,j,0);
}
a.Set(i,i,1);
b.Set(i,i,1);
}
//--- function call
CMatGen::RMatrixRndOrthogonalFromTheLeft(a,n,n);
//--- function call
CMatGen::RMatrixRndOrthogonalFromTheLeft(b,n,n);
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- orthogonality test
vt=0.0;
for(i_=0; i_<n; i_++)
vt+=a.Get(i,i_)*a.Get(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; i_++)
vt+=b.Get(i,i_)*b.Get(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.Get(i,j)==b.Get(i,j);
}
}
//--- change values
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
r1.Set(i,j,2*CMath::RandomReal()-1);
r1.Set(i,j+n,r1.Get(i,j));
}
}
//--- function call
CMatGen::RMatrixRndOrthogonalFromTheLeft(r1,n,2*n);
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
rerr=rerr||MathAbs(r1.Get(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; i++)
{
for(j=0; j<n; j++)
{
ca.Set(i,j,0);
cb.Set(i,j,0);
}
ca.Set(i,i,1);
cb.Set(i,i,1);
}
//--- function call
CMatGen::CMatrixRndOrthogonalFromTheRight(ca,n,n);
//--- function call
CMatGen::CMatrixRndOrthogonalFromTheRight(cb,n,n);
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- orthogonality test
ct=0.0;
for(i_=0; i_<n; i_++)
ct+=ca.Get(i,i_)*CMath::Conj(ca.Get(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; i_++)
ct+=cb.Get(i,i_)*CMath::Conj(cb.Get(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.Get(i,j)==cb.Get(i,j);
}
}
//--- change values
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
c2.Set(i,j,2*CMath::RandomReal()-1);
c2.Set(i+n,j,c2.Get(i,j));
}
}
//--- function call
CMatGen::CMatrixRndOrthogonalFromTheRight(c2,2*n,n);
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
cerr=cerr||CMath::AbsComplex(c2[i+n][j]-c2.Get(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; i++)
{
for(j=0; j<n; j++)
{
ca.Set(i,j,0);
cb.Set(i,j,0);
}
ca.Set(i,i,1);
cb.Set(i,i,1);
}
//--- function call
CMatGen::CMatrixRndOrthogonalFromTheLeft(ca,n,n);
//--- function call
CMatGen::CMatrixRndOrthogonalFromTheLeft(cb,n,n);
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- orthogonality test
ct=0.0;
for(i_=0; i_<n; i_++)
ct+=ca.Get(i,i_)*CMath::Conj(ca.Get(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; i_++)
ct+=cb.Get(i,i_)*CMath::Conj(cb.Get(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.Get(i,j)==cb.Get(i,j);
}
}
//--- change values
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
c1.Set(i,j,2*CMath::RandomReal()-1);
c1.Set(i,j+n,c1.Get(i,j));
}
}
//--- function call
CMatGen::CMatrixRndOrthogonalFromTheLeft(c1,n,2*n);
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
cerr=cerr||CMath::AbsComplex(c1.Get(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.Set(i,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; i++)
{
for(j=0; j<n; j++)
spderr=spderr||MathAbs(a.Get(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; i++)
{
for(j=0; j<n; j++)
{
if(a.Get(i,j)==b.Get(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; i++)
{
for(j=0; j<n; j++)
hpderr=hpderr||CMath::AbsComplex(ca.Get(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; i++)
{
for(j=0; j<n; j++)
{
hpderr=hpderr||ca.Get(i,j)==cb.Get(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; i++)
{
for(j=0; j<n; j++)
serr=serr||a.Get(i,j)==b.Get(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; i++)
{
for(j=0; j<n; j++)
herr=herr||CMath::AbsComplex(ca.Get(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; i++)
{
for(j=0; j<n; j++)
herr=herr||ca.Get(i,j)==cb.Get(i,j);
}
}
}
}
//--- report
waserrors=((((rerr||cerr)||serr)||spderr)||herr)||hpderr;
//--- check
if(!silent)
{
Print("TESTING MATRIX GENERATOR");
PrintResult("REAL TEST",!rerr);
PrintResult("COMPLEX TEST",!cerr);
PrintResult("SYMMETRIC TEST",!serr);
PrintResult("HERMITIAN TEST",!herr);
PrintResult("SPD TEST",!spderr);
PrintResult("HPD TEST",!hpderr);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Unsets 2D array. |
//+------------------------------------------------------------------+
void CTestMatGenUnit::Unset2D(CMatrixDouble &a)
{
//--- allocation
a.Resize(1,1);
//--- change value
a.Set(0,0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets 2D array. |
//+------------------------------------------------------------------+
void CTestMatGenUnit::Unset2DC(CMatrixComplex &a)
{
//--- allocation
a.Resize(1,1);
//--- change value
a.Set(0,0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Test whether matrix is SPD |
//+------------------------------------------------------------------+
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; j++)
{
//--- Compute U(J,J) and test for non-positive-definiteness.
v=0.0;
for(i_=0; i_<j; i_++)
v+=a.Get(i_,j)*a.Get(i_,j);
ajj=a[j][j]-v;
//--- check
if(ajj<=0.0)
{
//--- return result
return(false);
}
//--- change values
ajj=MathSqrt(ajj);
a.Set(j,j,ajj);
//--- Compute elements J+1:N of row J.
if(j<n-1)
{
for(i=j+1; i<n; i++)
{
//--- change value
v=0.0;
for(i_=0; i_<j; i_++)
v+=a.Get(i_,i)*a.Get(i_,j);
a.Set(j,i,a[j][i]-v);
}
v=1/ajj;
for(i_=j+1; i_<n; i_++)
a.Set(j,i_,v*a.Get(j,i_));
}
}
}
else
{
//--- Compute the Cholesky factorization A=L*L'.
for(j=0; j<n; j++)
{
//--- Compute L(J,J) and test for non-positive-definiteness.
v=0.0;
for(i_=0; i_<j; i_++)
v+=a.Get(j,i_)*a.Get(j,i_);
ajj=a[j][j]-v;
//--- check
if(ajj<=0.0)
{
//--- return result
return(false);
}
ajj=MathSqrt(ajj);
a.Set(j,j,ajj);
//--- Compute elements J+1:N of column J.
if(j<n-1)
{
for(i=j+1; i<n; i++)
{
//--- change value
v=0.0;
for(i_=0; i_<j; i_++)
v+=a.Get(i,i_)*a.Get(j,i_);
a.Set(i,j,a.Get(i,j)-v);
}
//--- change values
v=1/ajj;
for(i_=j+1; i_<n; i_++)
a.Set(i_,j,v*a.Get(i_,j));
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Tests whether A is HPD |
//+------------------------------------------------------------------+
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; j++)
{
//--- Compute U(J,J) and test for non-positive-definiteness.
v=0.0;
for(i_=0; i_<j; i_++)
v+=CMath::Conj(a.Get(i_,j))*a.Get(i_,j);
ajj=(a[j][j]-v).real;
//--- check
if(ajj<=0.0)
{
a.Set(j,j,ajj);
//--- return result
return(false);
}
ajj=MathSqrt(ajj);
a.Set(j,j,ajj);
//--- Compute elements J+1:N-1 of row J.
if(j<n-1)
{
for(i_=0; i_<j; i_++)
t2[i_]=CMath::Conj(a.Get(i_,j));
for(i_=j+1; i_<n; i_++)
t3[i_]=a.Get(j,i_);
//--- calculation
for(i=j+1; i<n; i++)
{
//--- change value
v=0.0;
for(i_=0; i_<j; i_++)
v+=a.Get(i_,i)*t2[i_];
t3[i]=t3[i]-v;
}
for(i_=j+1; i_<n; i_++)
a.Set(j,i_,t3[i_]);
//--- change values
r=1/ajj;
for(i_=j+1; i_<n; i_++)
a.Set(j,i_,a.Get(j,i_)*r);
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| SVD condition number |
//+------------------------------------------------------------------+
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.Set(i,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 |
//+------------------------------------------------------------------+
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.Set(k,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.Set(i,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.Set(k,j,a[k][j]+f*a[k][i]);
}
}
for(k=i; k<=m; k++)
a.Set(k,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.Set(i,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.Set(i,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.Set(j,k,a[j][k]+s*rv1[k]);
}
}
for(k=l; k<=n; k++)
a.Set(i,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.Set(j,i,a.Get(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.Set(k,j,v[k][j]+s*v[k][i]);
}
}
//--- change values
for(j=l; j<=n; j++)
{
v.Set(i,j,0.0);
v.Set(j,i,0.0);
}
}
//--- change values
v.Set(i,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.Set(i,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.Set(k,j,a[k][j]+f*a[k][i]);
}
}
for(j=i; j<=m; j++)
a.Set(j,i,a[j][i]*g);
}
else
{
//--- change values
for(j=i; j<=m; j++)
a.Set(j,i,0.0);
}
a.Set(i,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.Set(j,nm,y*c+z*s);
a.Set(j,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.Set(j,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.Get(jj,j);
z=v.Get(jj,i);
v.Set(jj,j,x*c+z*s);
v.Set(jj,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.Get(jj,j);
z=a.Get(jj,i);
a.Set(jj,j,y*c+z*s);
a.Set(jj,i,-(y*s)+z*c);
}
}
//--- change values
rv1[l]=0.0;
rv1[k]=f;
w[k]=x;
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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
{
public:
static bool TestTrFac(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Testing class CTrFac |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
ra.Set(i,j,0);
ca.Set(i,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; i++)
{
for(j=0; j<n; j++)
{
ra.Set(i,j,0);
ca.Set(i,j,0);
}
}
//--- change values
for(i=0; i<=MathMin(m,n)-1; i++)
{
ra.Set(i,i,1+10*CMath::RandomReal());
ca.Set(i,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; i++)
{
for(j=0; j<n; j++)
{
cal.Set(i,j,i);
cau.Set(i,j,j);
}
}
//--- change values
for(i=0; i<n; i++)
{
for(i_=0; i_<=i; i_++)
cal.Set(i,i_,ca.Get(i,i_));
for(i_=i; i_<n; i_++)
cau.Set(i,i_,ca.Get(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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(j>i)
hpderr=hpderr||cal.Get(i,j)!=i;
else
{
vc=0.0;
for(i_=0; i_<=j; i_++)
vc+=cal.Get(i,i_)*CMath::Conj(cal.Get(j,i_));
//--- search errors
hpderr=hpderr||CMath::AbsComplex(ca.Get(i,j)-vc)>threshold;
}
}
}
}
else
hpderr=true;
//--- check
if(CTrFac::HPDMatrixCholesky(cau,n,true))
{
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(j<i)
hpderr=hpderr||cau.Get(i,j)!=j;
else
{
vc=0.0;
for(i_=0; i_<=i; i_++)
vc+=CMath::Conj(cau.Get(i_,i))*cau.Get(i_,j);
//--- search errors
hpderr=hpderr||CMath::AbsComplex(ca.Get(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; i++)
{
for(j=0; j<n; j++)
{
ral.Set(i,j,i);
rau.Set(i,j,j);
}
}
//--- change values
for(i=0; i<n; i++)
{
for(i_=0; i_<=i; i_++)
ral.Set(i,i_,ra.Get(i,i_));
for(i_=i; i_<n; i_++)
rau.Set(i,i_,ra.Get(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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(j>i)
spderr=spderr||ral.Get(i,j)!=i;
else
{
vr=0.0;
for(i_=0; i_<=j; i_++)
vr+=ral.Get(i,i_)*ral.Get(j,i_);
//--- search errors
spderr=spderr||MathAbs(ra.Get(i,j)-vr)>threshold;
}
}
}
}
else
spderr=true;
//--- check
if(CTrFac::SPDMatrixCholesky(rau,n,true))
{
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(j<i)
spderr=spderr||rau.Get(i,j)!=j;
else
{
vr=0.0;
for(i_=0; i_<=i; i_++)
vr+=rau.Get(i_,i)*rau.Get(i_,j);
//--- search errors
spderr=spderr||MathAbs(ra.Get(i,j)-vr)>threshold;
}
}
}
}
else
spderr=true;
}
//--- report
waserrors=(((rerr||spderr)||cerr)||hpderr)||properr;
//--- check
if(!silent)
{
Print("TESTING TRIANGULAR FACTORIZATIONS");
PrintResult("* REAL",!rerr);
PrintResult("* SPD",!spderr);
PrintResult("* COMPLEX",!cerr);
PrintResult("* HPD",!hpderr);
PrintResult("* OTHER PROPERTIES",!properr);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
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; i++)
{
for(i_=0; i_<n; i_++)
ca.Set(i,i_,a.Get(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; i++)
cl.Set(i,j,0.0);
//--- change values
cl.Set(j,j,1.0);
for(i=j+1; i<m; i++)
cl.Set(i,j,ca.Get(i,j));
}
//--- allocation
cu.Resize(minmn,n);
//--- change values
for(i=0; i<=minmn-1; i++)
{
for(j=0; j<i; j++)
cu.Set(i,j,0.0);
for(j=i; j<n; j++)
cu.Set(i,j,ca.Get(i,j));
}
//--- allocation
ca2.Resize(m,n);
//--- calculation
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<=minmn-1; i_++)
v+=cl.Get(i,i_)*cu.Get(i_,j);
ca2.Set(i,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; i_++)
ct[i_]=ca2.Get(i,i_);
for(i_=0; i_<n; i_++)
ca2.Set(i,i_,ca2[p[i]][i_]);
for(i_=0; i_<n; i_++)
ca2.Set(p[i],i_,ct[i_]);
}
}
//--- search errors
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
err=err||CMath::AbsComplex(a.Get(i,j)-ca2.Get(i,j))>threshold;
}
//--- LUP test
ca.Resize(m,n);
for(i=0; i<m; i++)
{
for(i_=0; i_<n; i_++)
ca.Set(i,i_,a.Get(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; i++)
cl.Set(i,j,0.0);
for(i=j; i<m; i++)
cl.Set(i,j,ca.Get(i,j));
}
//--- allocation
cu.Resize(minmn,n);
//--- change values
for(i=0; i<=minmn-1; i++)
{
for(j=0; j<i; j++)
cu.Set(i,j,0.0);
cu.Set(i,i,1.0);
for(j=i+1; j<n; j++)
cu.Set(i,j,ca.Get(i,j));
}
//--- allocation
ca2.Resize(m,n);
//--- calculation
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<=minmn-1; i_++)
v+=cl.Get(i,i_)*cu.Get(i_,j);
ca2.Set(i,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; i_++)
ct[i_]=ca2.Get(i_,i);
for(i_=0; i_<m; i_++)
ca2.Set(i_,i,ca2[i_][p[i]]);
for(i_=0; i_<m; i_++)
ca2.Set(i_,p[i],ct[i_]);
}
}
//--- search errors
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
err=err||CMath::AbsComplex(a.Get(i,j)-ca2.Get(i,j))>threshold;
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
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=a;
CMatrixDouble cl;
CMatrixDouble cu;
CMatrixDouble ca2;
//--- initialization
minmn=MathMin(m,n);
//--- PLU test
ca.Resize(m,n);
//--- 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; i++)
cl.Set(i,j,0.0);
cl.Set(j,j,1.0);
for(i=j+1; i<m; i++)
cl.Set(i,j,ca.Get(i,j));
}
//--- allocation
cu.Resize(minmn,n);
//--- change values
for(i=0; i<=minmn-1; i++)
{
for(j=0; j<i; j++)
cu.Set(i,j,0.0);
for(j=i; j<n; j++)
cu.Set(i,j,ca.Get(i,j));
}
//--- allocation
ca2.Resize(m,n);
//--- calculation
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<=minmn-1; i_++)
v+=cl.Get(i,i_)*cu.Get(i_,j);
ca2.Set(i,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; i_++)
ct[i_]=ca2.Get(i,i_);
for(i_=0; i_<n; i_++)
ca2.Set(i,i_,ca2[p[i]][i_]);
for(i_=0; i_<n; i_++)
ca2.Set(p[i],i_,ct[i_]);
}
}
//--- search errors
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
err=err||MathAbs(a.Get(i,j)-ca2.Get(i,j))>threshold;
}
//--- LUP test
ca.Resize(m,n);
for(i=0; i<m; i++)
{
for(i_=0; i_<n; i_++)
ca.Set(i,i_,a.Get(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; i++)
cl.Set(i,j,0.0);
for(i=j; i<m; i++)
cl.Set(i,j,ca.Get(i,j));
}
//--- allocation
cu.Resize(minmn,n);
//--- change values
for(i=0; i<=minmn-1; i++)
{
for(j=0; j<i; j++)
cu.Set(i,j,0.0);
cu.Set(i,i,1.0);
for(j=i+1; j<n; j++)
cu.Set(i,j,ca.Get(i,j));
}
//--- allocation
ca2.Resize(m,n);
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<=minmn-1; i_++)
v+=cl.Get(i,i_)*cu.Get(i_,j);
ca2.Set(i,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; i_++)
ct[i_]=ca2.Get(i_,i);
for(i_=0; i_<m; i_++)
ca2.Set(i_,i,ca2[i_][p[i]]);
for(i_=0; i_<m; i_++)
ca2.Set(i_,p[i],ct[i_]);
}
}
//--- search errors
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
err=err||MathAbs(a.Get(i,j)-ca2.Get(i,j))>threshold;
}
}
//+------------------------------------------------------------------+
//| Testing class CTrLinSolve |
//+------------------------------------------------------------------+
class CTestTrLinSolveUnit
{
public:
static bool TestTrLinSolve(const bool silent);
private:
static void MakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
};
//+------------------------------------------------------------------+
//| Main unittest subroutine |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
aeffective.Set(i,j,0);
aparam.Set(i,j,0);
}
}
//--- Prepare matrices
if(isupper)
{
for(i=0; i<n; i++)
{
for(j=i; j<n; j++)
{
aeffective.Set(i,j,0.9*(2*CMath::RandomReal()-1));
aparam.Set(i,j,aeffective.Get(i,j));
}
//--- change values
aeffective.Set(i,i,(2*CMath::RandomInteger(2)-1)*(0.8+CMath::RandomReal()));
aparam.Set(i,i,aeffective[i][i]);
}
}
else
{
for(i=0; i<n; i++)
{
for(j=0; j<=i; j++)
{
aeffective.Set(i,j,0.9*(2*CMath::RandomReal()-1));
aparam.Set(i,j,aeffective.Get(i,j));
}
//--- change values
aeffective.Set(i,i,(2*CMath::RandomInteger(2)-1)*(0.8+CMath::RandomReal()));
aparam.Set(i,i,aeffective[i][i]);
}
}
//--- check
if(isunit)
{
for(i=0; i<n; i++)
{
aeffective.Set(i,i,1);
aparam.Set(i,i,0);
}
}
//--- check
if(istrans)
{
//--- check
if(isupper)
{
for(i=0; i<n; i++)
{
for(j=i+1; j<n; j++)
{
aeffective.Set(j,i,aeffective.Get(i,j));
aeffective.Set(i,j,0);
}
}
}
else
{
for(i=0; i<n; i++)
{
for(j=i+1; j<n; j++)
{
aeffective.Set(i,j,aeffective[j][i]);
aeffective.Set(j,i,0);
}
}
}
}
//--- Prepare task,solve,compare
for(i=0; i<n; i++)
xe[i]=2*CMath::RandomReal()-1;
for(i=0; i<n; i++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=aeffective.Get(i,i_)*xe[i_];
b[i]=v;
}
//--- function call
CTrLinSolve::RMatrixTrSafeSolve(aparam,n,b,s,isupper,istrans,isunit);
//--- calculation
for(i_=0; i_<n; i_++)
xe[i_]=s*xe[i_];
for(i_=0; i_<n; i_++)
xe[i_]=xe[i_]-b[i_];
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=xe[i_]*xe[i_];
v=MathSqrt(v);
//--- search errors
waserrors=waserrors||v>threshold;
}
}
}
}
}
}
//--- report
if(!silent)
{
Print("TESTING RMatrixTrSafeSolve");
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
b.Set(i,j,a.Get(i,j));
}
}
//+------------------------------------------------------------------+
//| Testing class CSafeSolve |
//+------------------------------------------------------------------+
class CTestSafeSolveUnit
{
public:
static bool TestSafeSolve(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Main unittest subroutine |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(i==j)
{
ca.SetRe(i,j,(2*CMath::RandomInteger(2)-1)*(5+CMath::RandomReal()));
ca.SetIm(i,j,(2*CMath::RandomInteger(2)-1)*(5+CMath::RandomReal()));
}
else
{
ca.SetRe(i,j,0.2*CMath::RandomReal()-0.1);
ca.SetIm(i,j,0.2*CMath::RandomReal()-0.1);
}
}
}
//--- function call
CMatrixMakeACopy(ca,n,n,ctmpa);
for(i=0; i<n; i++)
{
//--- check
if(isupper)
{
j1=0;
j2=i-1;
}
else
{
j1=i+1;
j2=n-1;
}
for(j=j1; j<=j2; j++)
ctmpa.Set(i,j,0);
//--- check
if(isunit)
ctmpa.Set(i,i,1);
}
//--- allocation
cea.Resize(n,n);
for(i=0; i<n; i++)
{
//--- check
if(trans==0)
{
for(i_=0; i_<n; i_++)
cea.Set(i,i_,ctmpa.Get(i,i_)*scalea);
}
//--- check
if(trans==1)
{
for(i_=0; i_<n; i_++)
cea.Set(i_,i,ctmpa.Get(i,i_)*scalea);
}
//--- check
if(trans==2)
{
for(i_=0; i_<n; i_++)
cea.Set(i_,i,CMath::Conj(ctmpa.Get(i,i_))*scalea);
}
}
//--- allocation
ArrayResize(cxe,n);
//--- change values
for(i=0; i<n; i++)
{
cxe[i].real=2*CMath::RandomReal()-1;
cxe[i].imag=2*CMath::RandomReal()-1;
}
//--- allocation
ArrayResize(cxs,n);
for(i=0; i<n; i++)
{
//--- change value
cv=0.0;
for(i_=0; i_<n; i_++)
cv+=cea.Get(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; 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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(i==j)
ra.Set(i,j,(2*CMath::RandomInteger(2)-1)*(5+CMath::RandomReal()));
else
ra.Set(i,j,0.2*CMath::RandomReal()-0.1);
}
}
//--- function call
RMatrixMakeACopy(ra,n,n,rtmpa);
//--- calculation
for(i=0; i<n; i++)
{
//--- check
if(isupper)
{
j1=0;
j2=i-1;
}
else
{
j1=i+1;
j2=n-1;
}
for(j=j1; j<=j2; j++)
rtmpa.Set(i,j,0);
//--- check
if(isunit)
rtmpa.Set(i,i,1);
}
//--- allocation
rea.Resize(n,n);
//--- calculation
for(i=0; i<n; i++)
{
//--- check
if(trans==0)
{
for(i_=0; i_<n; i_++)
rea.Set(i,i_,scalea*rtmpa.Get(i,i_));
}
//--- check
if(trans==1)
{
for(i_=0; i_<n; i_++)
rea.Set(i_,i,scalea*rtmpa.Get(i,i_));
}
}
//--- allocation
ArrayResize(rxe,n);
for(i=0; i<n; i++)
rxe[i]=2*CMath::RandomReal()-1;
//--- allocation
ArrayResize(rxs,n);
for(i=0; i<n; i++)
{
//--- change value
rv=0.0;
for(i_=0; i_<n; i_++)
rv+=rea.Get(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; 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.Set(0,0,1);
ca.Set(0,1,0);
ca.Set(1,0,0);
ca.Set(1,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.Set(0,0,1);
ra.Set(0,1,0);
ra.Set(1,0,0);
ra.Set(1,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.Set(0,0,1);
ca.Set(0,1,0);
ca.Set(1,0,0);
ca.Set(1,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.Set(0,0,1);
ra.Set(0,1,0);
ra.Set(1,0,0);
ra.Set(1,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");
PrintResult("REAL",!rerrors);
PrintResult("COMPLEX",!cerrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
void CTestSafeSolveUnit::RMatrixMakeACopy(CMatrixDouble &a,
const int m,const int n,
CMatrixDouble &b)
{
//--- allocation
b.Resize(m,n);
//--- copy
for(int i=0; i<m; i++)
{
for(int j=0; j<n; j++)
b.Set(i,j,a.Get(i,j));
}
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
void CTestSafeSolveUnit::CMatrixMakeACopy(CMatrixComplex &a,
const int m,const int n,
CMatrixComplex &b)
{
//--- allocation
b.Resize(m,n);
//--- copy
for(int i=0; i<m; i++)
{
for(int j=0; j<n; j++)
b.Set(i,j,a.Get(i,j));
}
}
//+------------------------------------------------------------------+
//| Testing class CRCond |
//+------------------------------------------------------------------+
class CTestRCondUnit
{
public:
//--- class constants
static const double m_threshold50;
static const double m_threshold90;
//--- public method
static bool TestRCond(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Initialize constants |
//+------------------------------------------------------------------+
const double CTestRCondUnit::m_threshold50=0.25;
const double CTestRCondUnit::m_threshold90=0.10;
//+------------------------------------------------------------------+
//| Testing class CRCond |
//+------------------------------------------------------------------+
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");
PrintResult("REAL TRIANGULAR",!rtrerr);
PrintResult("COMPLEX TRIANGULAR",!ctrerr);
PrintResult("REAL",!rerr);
PrintResult("SPD",!spderr);
PrintResult("HPD",!hpderr);
PrintResult("COMPLEX",!cerr);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
void CTestRCondUnit::RMatrixMakeACopy(CMatrixDouble &a,const int m,
const int n,CMatrixDouble &b)
{
//--- allocation
b.Resize(m,n);
//--- copy
for(int i=0; i<m; i++)
{
for(int j=0; j<n; j++)
b.Set(i,j,a.Get(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 |
//+------------------------------------------------------------------+
void CTestRCondUnit::RMatrixDropHalf(CMatrixDouble &a,const int n,
const bool droplower)
{
//--- calculation
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
//--- check
if((droplower && i>j) || (!droplower && i<j))
a.Set(i,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 |
//+------------------------------------------------------------------+
void CTestRCondUnit::CMatrixDropHalf(CMatrixComplex &a,const int n,
const bool droplower)
{
//--- calculation
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
//--- check
if((droplower && i>j) || (!droplower && i<j))
a.Set(i,j,1+2*i+3*j);
}
}
}
//+------------------------------------------------------------------+
//| Generate matrix with given condition number C (2-norm) |
//+------------------------------------------------------------------+
void CTestRCondUnit::RMatrixGenZero(CMatrixDouble &a0,const int n)
{
//--- allocation
a0.Resize(n,n);
//--- make zero
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
a0.Set(i,j,0);
}
}
//+------------------------------------------------------------------+
//| Triangular inverse |
//+------------------------------------------------------------------+
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; j++)
{
//--- check
if(nounit)
{
//--- check
if(a[j][j]==0.0)
{
//--- return result
return(false);
}
a.Set(j,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; i_++)
t[i_]=a.Get(i_,j);
for(i=0; i<j; i++)
{
//--- check
if(i<j-1)
{
//--- change value
v=0.0;
for(i_=i+1; i_<j; i_++)
v+=a.Get(i,i_)*t[i_];
}
else
v=0;
//--- check
if(nounit)
a.Set(i,j,v+a[i][i]*t[i]);
else
a.Set(i,j,v+t[i]);
}
//--- calculation
for(i_=0; i_<j; i_++)
a.Set(i_,j,ajj*a.Get(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.Set(j,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; i_++)
t[i_]=a.Get(i_,j);
for(i=j+1; i<n; i++)
{
//--- check
if(i>j+1)
{
//--- change value
v=0.0;
for(i_=j+1; i_<i; i_++)
v+=a.Get(i,i_)*t[i_];
}
else
v=0;
//--- check
if(nounit)
a.Set(i,j,v+a[i][i]*t[i]);
else
a.Set(i,j,v+t[i]);
}
//--- calculation
for(i_=j+1; i_<n; i_++)
a.Set(i_,j,ajj*a.Get(i_,j));
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| LU inverse |
//+------------------------------------------------------------------+
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; i++)
{
work[i]=a.Get(i,j);
a.Set(i,j,0);
}
//--- Compute current column of inv(A).
if(j<n-1)
{
for(i=0; i<n; i++)
{
//--- change value
v=0.0;
for(i_=j+1; i_<n; i_++)
v+=a.Get(i,i_)*work[i_];
a.Set(i,j,a.Get(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; i_++)
work[i_]=a.Get(i_,j);
for(i_=0; i_<n; i_++)
a.Set(i_,j,a[i_][jp]);
for(i_=0; i_<n; i_++)
a.Set(i_,jp,work[i_]);
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Matrix inverse |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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; k++)
{
//--- change values
v=0;
for(i=0; i<n; i++)
v+=MathAbs(a[i][k]);
nrm1a=MathMax(nrm1a,v);
v=0;
for(i=0; i<n; i++)
v+=MathAbs(a[k][i]);
nrminfa=MathMax(nrminfa,v);
}
//--- norm inv A
nrm1inva=0;
nrminfinva=0;
//--- calculation
for(k=0; k<n; k++)
{
//--- change values
v=0;
for(i=0; i<n; i++)
v+=MathAbs(inva[i][k]);
nrm1inva=MathMax(nrm1inva,v);
v=0;
for(i=0; i<n; i++)
v+=MathAbs(inva[k][i]);
nrminfinva=MathMax(nrminfinva,v);
}
//--- result
rc1=nrm1inva*nrm1a;
rcinf=nrminfinva*nrminfa;
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
b.Set(i,j,a.Get(i,j));
}
}
//+------------------------------------------------------------------+
//| Generate matrix with given condition number C (2-norm) |
//+------------------------------------------------------------------+
void CTestRCondUnit::CMatrixGenZero(CMatrixComplex &a0,const int n)
{
//--- allocation
a0.Resize(n,n);
//--- copy
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
a0.Set(i,j,0);
}
}
//+------------------------------------------------------------------+
//| triangular inverse |
//+------------------------------------------------------------------+
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; j++)
{
//--- check
if(nounit)
{
//--- check
if(a[j][j]==0)
{
//--- return result
return(false);
}
a.Set(j,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; i_++)
t[i_]=a.Get(i_,j);
//--- calculation
for(i=0; i<j; i++)
{
//--- check
if(i<j-1)
{
//--- change value
v=0.0;
for(i_=i+1; i_<j; i_++)
v+=a.Get(i,i_)*t[i_];
}
else
v=0;
//--- check
if(nounit)
a.Set(i,j,v+a[i][i]*t[i]);
else
a.Set(i,j,v+t[i]);
}
for(i_=0; i_<j; i_++)
a.Set(i_,j,ajj*a.Get(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.Set(j,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; i_++)
t[i_]=a.Get(i_,j);
//--- calculation
for(i=j+1; i<n; i++)
{
//--- check
if(i>j+1)
{
//--- change value
v=0.0;
for(i_=j+1; i_<i; i_++)
v+=a.Get(i,i_)*t[i_];
}
else
v=0;
//--- check
if(nounit)
a.Set(i,j,v+a[i][i]*t[i]);
else
a.Set(i,j,v+t[i]);
}
for(i_=j+1; i_<n; i_++)
a.Set(i_,j,ajj*a.Get(i_,j));
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| LU inverse |
//+------------------------------------------------------------------+
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; i++)
{
work[i]=a.Get(i,j);
a.Set(i,j,0);
}
//--- Compute current column of inv(A).
if(j<n-1)
{
for(i=0; i<n; i++)
{
//--- change value
v=0.0;
for(i_=j+1; i_<n; i_++)
v+=a.Get(i,i_)*work[i_];
a.Set(i,j,a.Get(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; i_++)
work[i_]=a.Get(i_,j);
for(i_=0; i_<n; i_++)
a.Set(i_,j,a[i_][jp]);
for(i_=0; i_<n; i_++)
a.Set(i_,jp,work[i_]);
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Matrix inverse |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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; k++)
{
v=0;
for(i=0; i<n; i++)
v+=CMath::AbsComplex(a[i][k]);
nrm1a=MathMax(nrm1a,v);
//--- change value
v=0;
for(i=0; i<n; i++)
v+=CMath::AbsComplex(a[k][i]);
nrminfa=MathMax(nrminfa,v);
}
//--- norm inv A
nrm1inva=0;
nrminfinva=0;
//--- calculation
for(k=0; k<n; k++)
{
v=0;
for(i=0; i<n; i++)
v+=CMath::AbsComplex(inva[i][k]);
nrm1inva=MathMax(nrm1inva,v);
//--- change value
v=0;
for(i=0; i<n; i++)
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 |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,CMath::RandomReal()-0.5);
}
for(i=0; i<n; i++)
a.Set(i,i,1+CMath::RandomReal());
//--- function call
RMatrixMakeACopy(a,n,n,ea);
for(i=0; i<n; 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.Set(i,j,0);
//--- check
if(isunit)
ea.Set(i,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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0.0);
}
a.Set(0,0,1);
a.Set(n-1,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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0.0);
}
//--- change values
for(i=0; i<n; i++)
a.Set(i,i,1);
i=CMath::RandomInteger(n);
a.Set(i,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 |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
a.SetRe(i,j,CMath::RandomReal()-0.5);
a.SetIm(i,j,CMath::RandomReal()-0.5);
}
}
//--- change values
for(i=0; i<n; i++)
{
a.SetRe(i,i,1+CMath::RandomReal());
a.SetIm(i,i,1+CMath::RandomReal());
}
//--- function call
CMatrixMakeACopy(a,n,n,ea);
//--- change values
for(i=0; i<n; i++)
{
//--- check
if(isupper)
{
j1=0;
j2=i-1;
}
else
{
j1=i+1;
j2=n-1;
}
for(j=j1; j<=j2; j++)
ea.Set(i,j,0);
//--- check
if(isunit)
ea.Set(i,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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0.0);
}
a.Set(0,0,1);
a.Set(n-1,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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0.0);
}
//--- change values
for(i=0; i<n; i++)
a.Set(i,i,1);
i=CMath::RandomInteger(n);
a.Set(i,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 |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0.0);
}
a.Set(0,0,1);
a.Set(n-1,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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0.0);
}
//--- change values
for(i=0; i<n; i++)
a.Set(i,i,1);
i=CMath::RandomInteger(n);
a.Set(i,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 |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0.0);
}
a.Set(0,0,1);
a.Set(n-1,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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0.0);
}
//--- change values
for(i=0; i<n; i++)
a.Set(i,i,1);
i=CMath::RandomInteger(n);
a.Set(i,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 |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0.0);
}
a.Set(0,0,1);
a.Set(n-1,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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0.0);
}
//--- change values
for(i=0; i<n; i++)
a.Set(i,i,1);
i=CMath::RandomInteger(n);
a.Set(i,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 |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0.0);
}
a.Set(0,0,1);
a.Set(n-1,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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0.0);
}
//--- change values
for(i=0; i<n; i++)
a.Set(i,i,1);
i=CMath::RandomInteger(n);
a.Set(i,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
{
public:
static bool TestMatInv(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
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");
PrintResult("* REAL TRIANGULAR",!rtrerrors);
PrintResult("* COMPLEX TRIANGULAR",!ctrerrors);
PrintResult("* REAL",!rerrors);
PrintResult("* COMPLEX",!cerrors);
PrintResult("* SPD",!spderrors);
PrintResult("* HPD",!hpderrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
void CTestMatInvUnit::RMatrixMakeACopy(CMatrixDouble &a,
const int m,const int n,
CMatrixDouble &b)
{
//--- allocation
b.Resize(m,n);
//--- copy
for(int i=0; i<m; i++)
{
for(int j=0; j<n; j++)
b.Set(i,j,a.Get(i,j));
}
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
void CTestMatInvUnit::CMatrixMakeACopy(CMatrixComplex &a,
const int m,const int n,
CMatrixComplex &b)
{
//--- allocation
b.Resize(m,n);
//--- copy
for(int i=0; i<m; i++)
{
for(int j=0; j<n; j++)
b.Set(i,j,a.Get(i,j));
}
}
//+------------------------------------------------------------------+
//| Checks whether inverse is correct |
//| Returns True on success. |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*inva.Get(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. |
//+------------------------------------------------------------------+
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; i_++)
a.Set(i_,i,a.Get(i,i_));
for(i_=i+1; i_<n; i_++)
inva.Set(i_,i,inva.Get(i,i_));
}
else
{
//--- change values
for(i_=i+1; i_<n; i_++)
a.Set(i,i_,a.Get(i_,i));
for(i_=i+1; i_<n; i_++)
inva.Set(i,i_,inva.Get(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; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*inva.Get(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. |
//+------------------------------------------------------------------+
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; i_++)
a.Set(i_,i,CMath::Conj(a.Get(i,i_)));
for(i_=i+1; i_<n; i_++)
inva.Set(i_,i,CMath::Conj(inva.Get(i,i_)));
}
else
{
//--- change values
for(i_=i+1; i_<n; i_++)
a.Set(i,i_,CMath::Conj(a.Get(i_,i)));
for(i_=i+1; i_<n; i_++)
inva.Set(i,i_,CMath::Conj(inva.Get(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; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*inva.Get(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. |
//+------------------------------------------------------------------+
bool CTestMatInvUnit::RMatrixCheckInverseSingular(CMatrixDouble &inva,
const int n,
const double threshold,
const int info,
CMatInvReport &rep)
{
bool 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(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
result=result && inva.Get(i,j)==0.0;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Checks whether inverse is correct |
//| Returns True on success. |
//+------------------------------------------------------------------+
bool CTestMatInvUnit::CMatrixCheckInverse(CMatrixComplex &a,
CMatrixComplex &inva,
const int n,
const double threshold,
const int info,
CMatInvReport &rep)
{
//--- create variables
bool result=true;
int i=0;
int j=0;
complex v=0;
int i_=0;
//--- 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; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*inva.Get(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. |
//+------------------------------------------------------------------+
bool CTestMatInvUnit::CMatrixCheckInverseSingular(CMatrixComplex &inva,
const int n,
const double threshold,
const int info,
CMatInvReport &rep)
{
bool 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(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
result=result && inva.Get(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 |
//+------------------------------------------------------------------+
void CTestMatInvUnit::RMatrixDropHalf(CMatrixDouble &a,const int n,
const bool droplower)
{
//--- calculation
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
//--- check
if((droplower && i>j) || (!droplower && i<j))
a.Set(i,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 |
//+------------------------------------------------------------------+
void CTestMatInvUnit::CMatrixDropHalf(CMatrixComplex &a,const int n,
const bool droplower)
{
//--- calculation
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
//--- check
if((droplower && i>j) || (!droplower && i<j))
a.Set(i,j,1+2*i+3*j);
}
}
}
//+------------------------------------------------------------------+
//| Real TR inverse |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(i==j)
a.Set(i,i,1+CMath::RandomReal());
else
a.Set(i,j,0.2*CMath::RandomReal()-0.1);
b.Set(i,j,a.Get(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; i++)
rtrerrors=rtrerrors||a[i][i]!=b[i][i];
}
//--- check
if(isupper)
{
for(i=0; i<n; i++)
{
for(j=0; j<i; j++)
rtrerrors=rtrerrors||a.Get(i,j)!=b.Get(i,j);
}
}
else
{
for(i=0; i<n; i++)
{
for(j=i+1; j<n; j++)
rtrerrors=rtrerrors||a.Get(i,j)!=b.Get(i,j);
}
}
//--- Inverse test
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- check
if((j<i && isupper)||(j>i && !isupper))
{
a.Set(i,j,0);
b.Set(i,j,0);
}
}
}
//--- check
if(isunit)
{
for(i=0; i<n; i++)
{
a.Set(i,i,1);
b.Set(i,i,1);
}
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*b.Get(i_,j);
//--- check
if(j!=i)
rtrerrors=rtrerrors||MathAbs(v)>threshold;
else
rtrerrors=rtrerrors||MathAbs(v-1)>threshold;
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Complex TR inverse |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(i==j)
{
a.SetRe(i,i,1+CMath::RandomReal());
a.SetIm(i,i,1+CMath::RandomReal());
}
else
{
a.SetRe(i,j,0.2*CMath::RandomReal()-0.1);
a.SetIm(i,j,0.2*CMath::RandomReal()-0.1);
}
b.Set(i,j,a.Get(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; i++)
ctrerrors=ctrerrors||a[i][i]!=b[i][i];
}
//--- check
if(isupper)
{
for(i=0; i<n; i++)
{
for(j=0; j<i; j++)
ctrerrors=ctrerrors||a.Get(i,j)!=b.Get(i,j);
}
}
else
{
for(i=0; i<n; i++)
{
for(j=i+1; j<n; j++)
ctrerrors=ctrerrors||a.Get(i,j)!=b.Get(i,j);
}
}
//--- Inverse test
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- check
if((j<i && isupper) || (j>i && !isupper))
{
a.Set(i,j,0);
b.Set(i,j,0);
}
}
}
//--- check
if(isunit)
{
for(i=0; i<n; i++)
{
a.Set(i,i,1);
b.Set(i,i,1);
}
}
//--- search errors
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*b.Get(i_,j);
//--- check
if(j!=i)
ctrerrors=ctrerrors||CMath::AbsComplex(v)>threshold;
else
ctrerrors=ctrerrors||CMath::AbsComplex(v-1)>threshold;
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| Real test |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0);
}
}
//--- check
if(taskkind==1)
{
//--- there is zero column
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CMath::RandomReal()-1);
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0; i_<n; i_++)
a.Set(i_,k,0*a[i_][k]);
}
//--- check
if(taskkind==2)
{
//--- there is zero row
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CMath::RandomReal()-1);
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0; i_<n; i_++)
a.Set(k,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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CMath::RandomReal()-1);
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0; i_<n; i_++)
a.Set(i_,0,a[i_][k]);
}
//--- check
if(taskkind==4)
{
//--- equal rows
if(n<2)
continue;
//--- allocation
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CMath::RandomReal()-1);
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0; i_<n; i_++)
a.Set(0,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 |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0);
}
}
//--- check
if(taskkind==1)
{
//--- there is zero column
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0; i_<n; i_++)
a.Set(i_,k,a[i_][k]*0);
}
//--- check
if(taskkind==2)
{
//--- there is zero row
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0; i_<n; i_++)
a.Set(k,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; i++)
{
for(j=0; j<n; j++)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0; i_<n; i_++)
a.Set(i_,0,a[i_][k]);
}
//--- check
if(taskkind==4)
{
//--- equal rows
if(n<2)
continue;
//--- allocation
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
}
}
//--- change value
k=1+CMath::RandomInteger(n-1);
for(i_=0; i_<n; i_++)
a.Set(0,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 |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0);
}
}
//--- check
if(taskkind==1)
{
//--- there is zero column
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CMath::RandomReal()-1);
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0; i_<n; i_++)
a.Set(i_,k,0*a[i_][k]);
}
//--- check
if(taskkind==2)
{
//--- there is zero row
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CMath::RandomReal()-1);
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0; i_<n; i_++)
a.Set(k,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 |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0);
}
}
//--- check
if(taskkind==1)
{
//--- there is zero column
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0; i_<n; i_++)
a.Set(i_,k,a[i_][k]*0);
for(i_=0; i_<n; i_++)
a.Set(k,i_,a[k][i_]*0);
}
//--- check
if(taskkind==2)
{
//--- there is zero row
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0; i_<n; i_++)
a.Set(k,i_,a[k][i_]*0);
for(i_=0; i_<n; i_++)
a.Set(i_,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 |
//+------------------------------------------------------------------+
void CTestMatInvUnit::Unset2D(CMatrixDouble &x)
{
//--- allocation
x.Resize(1,1);
//--- change value
x.Set(0,0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets real matrix |
//+------------------------------------------------------------------+
void CTestMatInvUnit::Unset1D(double &x[])
{
//--- allocation
ArrayResize(x,1);
//--- change value
x[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Unsets real matrix |
//+------------------------------------------------------------------+
void CTestMatInvUnit::CUnset2D(CMatrixComplex &x)
{
//--- allocation
x.Resize(1,1);
//--- change value
x.Set(0,0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets real vector |
//+------------------------------------------------------------------+
void CTestMatInvUnit::CUnset1D(double &x[])
{
//--- allocation
ArrayResize(x,1);
//--- change value
x[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Unsets report |
//+------------------------------------------------------------------+
void CTestMatInvUnit::UnsetRep(CMatInvReport &r)
{
//--- change values
r.m_r1=-1;
r.m_rinf=-1;
}
//+------------------------------------------------------------------+
//| Testing class CLDA |
//+------------------------------------------------------------------+
class CTestLDAUnit
{
public:
static bool TestLDA(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Testing class CLDA |
//+------------------------------------------------------------------+
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; 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.Get(axis,0)<=0.75;
CLDA::FisherLDA(xy,ns,nf,nc,info,w1);
//--- search errors
for(i=0; i<nf; i++)
lda1errors=lda1errors||w1[i]!=wn[i][0];
}
}
}
}
//--- Final report
waserrors=ldanerrors||lda1errors;
//--- check
if(!silent)
{
Print("LDA TEST");
PrintResult("FISHER LDA-N",!ldanerrors);
PrintResult("FISHER LDA-1",!lda1errors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Generates 'simple' set - a sequence of unit 'balls' at |
//| (0,0),(1,0),(2,0) and so on. |
//+------------------------------------------------------------------+
void CTestLDAUnit::GenSimpleSet(const int nfeatures,const int nclasses,
const int nsamples,const int axis,
CMatrixDouble &xy)
{
//--- check
if(!CAp::Assert(axis>=0 && axis<nfeatures,"GenSimpleSet: wrong Axis!"))
return;
//--- allocation
xy.Resize(nsamples,nfeatures+1);
//--- calculation
for(int i=0; i<=nsamples-1; i++)
{
for(int j=0; j<nfeatures; j++)
xy.Set(i,j,GenerateNormal(0.0,1.0));
//--- change values
int c=i%nclasses;
xy.Set(i,axis,xy[i][axis]+c);
xy.Set(i,nfeatures,c);
}
}
//+------------------------------------------------------------------+
//| Generates 'degenerate' set #1. |
//| NFeatures>=3. |
//+------------------------------------------------------------------+
void CTestLDAUnit::GenDeg1Set(const int nfeatures,const int nclasses,
const int nsamples,int axis,
CMatrixDouble &xy)
{
//--- 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(int i=0; i<=nsamples-1; i++)
{
for(int j=0; j<=nfeatures-2; j++)
xy.Set(i,j,GenerateNormal(0.0,1.0));
//--- change values
xy.Set(i,nfeatures-1,xy[i][nfeatures-2]);
int c=i%nclasses;
xy.Set(i,axis,xy[i][axis]+c);
xy.Set(i,nfeatures,c);
}
}
//+------------------------------------------------------------------+
//| Normal random number |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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; j++)
{
for(i_=0; i_<nf; i_++)
tx[i_]=wn.Get(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; i++)
result=result && jp[i]<=tol*CMath::m_machineepsilon*jp[0];
//--- Test for J optimality
for(i_=0; i_<nf; i_++)
tx[i_]=wn[i_][0];
v=CalcJ(nf,st,sw,tx,p,q);
//--- calculation
for(i=0; i<nf; 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; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<nf; i_++)
v+=wn.Get(i_,j)*wn.Get(i_,j);
//--- change values
v=MathSqrt(v);
result=result && MathAbs(v-1)<=1000*CMath::m_machineepsilon;
v=0;
for(i=0; i<nf; i++)
v+=wn.Get(i,j);
result=result && v>=0.0;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Calculates J |
//+------------------------------------------------------------------+
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; i++)
{
//--- change value
v=0.0;
for(i_=0; i_<nf; i_++)
v+=st.Get(i,i_)*w[i_];
tx[i]=v;
}
//--- change value
v=0.0;
for(i_=0; i_<nf; i_++)
v+=w[i_]*tx[i_];
p=v;
for(i=0; i<nf; i++)
{
//--- change value
v=0.0;
for(i_=0; i_<nf; i_++)
v+=sw.Get(i,i_)*w[i_];
tx[i]=v;
}
//--- change value
v=0.0;
for(i_=0; i_<nf; i_++)
v+=w[i_]*tx[i_];
q=v;
result=p/q;
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Calculates ST/SW |
//+------------------------------------------------------------------+
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 ; i++)
c[i]=(int)MathRound(xy.Get(i,nfeatures));
//--- Calculate class sizes and means
ArrayResize(mu,nfeatures);
muc.Resize(nclasses,nfeatures);
ArrayResize(nc,nclasses);
//--- change values
for(j=0; j<nfeatures; j++)
mu[j]=0;
for(i=0; i<nclasses; i++)
{
nc[i]=0;
for(j=0; j<nfeatures; j++)
muc.Set(i,j,0);
}
//--- calculation
for(i=0; i<npoints ; i++)
{
for(i_=0; i_<nfeatures; i_++)
mu[i_]=mu[i_]+xy.Get(i,i_);
for(i_=0; i_<nfeatures; i_++)
muc.Set(c[i],i_,muc[c[i]][i_]+xy.Get(i,i_));
nc[c[i]]=nc[c[i]]+1;
}
//--- calculation
for(i=0; i<nclasses; i++)
{
v=1.0/(double)nc[i];
for(i_=0; i_<nfeatures; i_++)
muc.Set(i,i_,v*muc.Get(i,i_));
}
//--- change values
v=1.0/(double)npoints;
for(i_=0; i_<nfeatures; i_++)
mu[i_]=v*mu[i_];
//--- Create ST matrix
st.Resize(nfeatures,nfeatures);
//--- change values
for(i=0; i<nfeatures; i++)
{
for(j=0; j<nfeatures; j++)
st.Set(i,j,0);
}
//--- calculation
for(k=0; k<npoints ; k++)
{
for(i_=0; i_<nfeatures; i_++)
tf[i_]=xy[k][i_];
for(i_=0; i_<nfeatures; i_++)
tf[i_]=tf[i_]-mu[i_];
//--- calculation
for(i=0; i<nfeatures; i++)
{
v=tf[i];
for(i_=0; i_<nfeatures; i_++)
st.Set(i,i_,st.Get(i,i_)+v*tf[i_]);
}
}
//--- Create SW matrix
sw.Resize(nfeatures,nfeatures);
for(i=0; i<nfeatures; i++)
{
for(j=0; j<nfeatures; j++)
sw.Set(i,j,0);
}
//--- calculation
for(k=0; k<npoints ; k++)
{
for(i_=0; i_<nfeatures; i_++)
tf[i_]=xy[k][i_];
for(i_=0; i_<nfeatures; i_++)
tf[i_]=tf[i_]-muc[c[k]][i_];
//--- calculation
for(i=0; i<nfeatures; i++)
{
v=tf[i];
for(i_=0; i_<nfeatures; i_++)
sw.Set(i,i_,sw.Get(i,i_)+v*tf[i_]);
}
}
}
//+------------------------------------------------------------------+
//| Testing class CGammaFunc |
//+------------------------------------------------------------------+
class CTestGammaFuncUnit
{
public:
static bool TestGammaFunc(const bool silent);
};
//+------------------------------------------------------------------+
//| Testing class CGammaFunc |
//+------------------------------------------------------------------+
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");
PrintResult("GAMMA",!gammaerrors);
PrintResult("LN GAMMA",!lngammaerrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- end
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CBdSingValueDecompose |
//+------------------------------------------------------------------+
class CTestBdSVDUnit
{
public:
static bool TestBdSVD(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Testing bidiagonal SVD decomposition subroutine |
//+------------------------------------------------------------------+
bool CTestBdSVDUnit::TestBdSVD(const bool silent)
{
//--- create variables
int n=0;
int maxn=15;
int i=0;
int pass=0;
bool waserrors=false;
bool wsorted=true;
bool wfailed=false;
bool failcase;
double materr=0;
double orterr=0;
double threshold=5*100*CMath::m_machineepsilon;
double failthreshold=1.0E-2;
double failr=0;
int failcount=0;
int succcount=0;
//--- create arrays
double d[];
double e[];
//--- create matrix
CMatrixDouble mempty;
//--- 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");
PrintFormat("SVD decomposition error: %5.3E",materr);
PrintFormat("SVD orthogonality error: %5.3E}",orterr);
PrintResult("Singular values order",wsorted);
PrintFormat("Always converged: %s",(!wfailed ? "YES" : "NO"));
if(wfailed)
PrintFormat("Fail ratio: %5.3f",failr);
PrintFormat("Fail matrix test: %s",(!failcase ? "AS EXPECTED" : "CONVERGED (UNEXPECTED)"));
PrintFormat("Threshold: %5.3E",threshold);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
void CTestBdSVDUnit::FillIdentity(CMatrixDouble &a,const int n)
{
//--- allocation
a.Resize(n,n);
//--- change values
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
//--- check
if(i==j)
a.Set(i,j,1);
else
a.Set(i,j,0);
}
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
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; 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 |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
//--- change value
sm=0;
for(k=0; k<n; 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; i++)
{
for(j=0; j<n; j++)
locerr=MathMax(locerr,MathAbs(u.Get(i,j)-c[j][i]));
}
materr=MathMax(materr,locerr);
//--- orthogonality error
locerr=0;
for(i=0; i<n; i++)
{
for(j=i; j<n; j++)
{
//--- change value
sm=0.0;
for(i_=0; i_<n; i_++)
sm+=u.Get(i_,i)*u.Get(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; i_++)
sm+=vt.Get(i,i_)*vt.Get(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; i++)
{
//--- check
if(w[i]>w[i-1])
wsorted=false;
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
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; 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; 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; 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; 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; 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
{
public:
static bool TestSVD(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Testing SVD decomposition subroutine |
//+------------------------------------------------------------------+
bool CTestSVDUnit::TestSVD(const bool silent)
{
//--- create variables
int m=0;
int n=0;
int maxmn=30;
int i=0;
int j=0;
int gpass=0;
int pass=0;
bool waserrors=false;
bool wsorted=true;
bool wfailed=false;
double materr=0;
double orterr=0;
double othererr=0;
double threshold=5*100*CMath::m_machineepsilon;
double failthreshold=5.0E-3;
double failr=0;
int failcount=0;
int succcount=0;
//--- create matrix
CMatrixDouble a;
//--- 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.Set(i,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.Set(i,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.Set(i,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; i++)
{
for(j=0; j<n; j++)
a.Set(i,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");
PrintFormat("SVD decomposition error: %5.3E",materr);
PrintFormat("SVD orthogonality error: %5.3E",orterr);
PrintFormat("SVD with different parameters error: %5.3E",othererr);
PrintResult("Singular values order",wsorted);
PrintFormat("Always converged: %s",(!wfailed ? "YES" : "NO"));
if(wfailed)
PrintFormat("Fail ratio: %5.3f",failr);
PrintFormat("Threshold: %5.3E",threshold);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
void CTestSVDUnit::FillsParseA(CMatrixDouble &a,const int m,
const int n,const double sparcity)
{
//--- change values
for(int i=0; i<m; i++)
{
for(int j=0; j<n; j++)
{
//--- check
if(CMath::RandomReal()>=sparcity)
a.Set(i,j,2*CMath::RandomReal()-1);
else
a.Set(i,j,0);
}
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; 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.Get(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; i_++)
sm+=u.Get(i_,i)*u.Get(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; i_++)
sm+=vt.Get(i,i_)*vt.Get(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 |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<=ucheck-1; j++)
othererr=MathMax(othererr,MathAbs(u.Get(i,j)-u2.Get(i,j)));
}
//--- search errors
for(i=0; i<=vtcheck-1; i++)
{
for(j=0; j<n; j++)
othererr=MathMax(othererr,MathAbs(vt.Get(i,j)-vt2.Get(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:
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);
};
//+------------------------------------------------------------------+
//| Testing class CLinReg |
//+------------------------------------------------------------------+
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; i++)
s[i]=CMath::RandomReal()+1;
//--- 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; 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; i++)
{
//--- check
if(i%2==0)
xy.Set(i,1,5.0);
else
xy.Set(i,1,-5.0);
}
//--- check
if(n%2!=0)
xy.Set(n-1,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; i++)
{
xy.Set(i,0,2*CMath::RandomReal()-1);
xy.Set(i,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; i++)
{
xy.Set(i,0,1.5*CMath::RandomReal()-0.5);
xy.Set(i,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; i++)
xy.Set(i,0,2*CMath::RandomReal()-1);
for(i=0; i<n; i++)
{
for(j=1; j<m; j++)
xy.Set(i,j,xy[i][0]*xy[i][j-1]);
}
sinshift=CMath::RandomReal()*M_PI;
//--- calculation
for(i=0; i<n; i++)
{
xy0[i]=MathSin(sinshift+M_PI*0.5*(xy[i][0]+1));
xy.Set(i,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; i++)
{
for(j=0; j<m; j++)
xy.Set(i,j,0);
xy.Set(i,i%3,1);
xy.Set(i,3+i/3%3,1);
xy.Set(i,6+i/9%3,1);
//--- change value
v=0.0;
for(i_=0; i_<=8; i_++)
v+=xy.Get(i,i_)*ta[i_];
xy0[i]=v;
xy.Set(i,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; i++)
{
for(j=0; j<m; j++)
{
//--- check
if(tasktype==2)
xy.Set(i,j,1+GenerateNormal(0,3));
else
xy.Set(i,j,1+GenerateNormal(0,0.05));
}
//--- change value
v=0.0;
for(i_=0; i_<=8; i_++)
v+=xy.Get(i,i_)*ta[i_];
xy0[i]=v;
xy.Set(i,m,v+noiselevel*GenerateNormal(0,1));
}
}
for(i=0; i<n; 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; i++)
{
x[i]=2*CMath::RandomReal()-1;
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; 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; i++)
{
v=tmpweights[m];
for(j=0; j<m; j++)
v+=xy.Get(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; i++)
{
for(i_=0; i_<m; i_++)
xy2.Set(i,i_,xy.Get(i,i_));
xy2.Set(i,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.Get(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.Set(i,i_,xy[i+1][i_]);
s2[i]=s[i+1];
}
//--- calculation
for(i=0; i<n; i++)
{
//--- Trn
v=0.0;
for(i_=0; i_<m; i_++)
v+=xy.Get(i,i_)*tmpweights[i_];
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; i_++)
v+=xy.Get(i,i_)*w2[i_];
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.Set(i,i_,xy.Get(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; i++)
{
xy.Set(3*i,0,i);
xy.Set(3*i+1,0,i);
xy.Set(3*i+2,0,i);
xy.Set(3*i,1,tasklevel-noiselevel);
xy.Set(3*i+1,1,tasklevel);
xy.Set(3*i+2,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; i++)
{
xy.Set(3*i,0,i);
xy.Set(3*i+1,0,i);
xy.Set(3*i+2,0,i);
xy.Set(3*i,1,-noiselevel);
xy.Set(3*i+1,1,0);
xy.Set(3*i+2,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; i++)
{
for(j=0; j<=m; j++)
xy.Set(i,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; 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");
PrintResult("STRAIGHT LINE REGRESSION",!slerrors);
PrintResult("STRAIGHT LINE REGRESSION CONVERGENCE",!slcerrors);
PrintResult("GENERAL LINEAR REGRESSION",!(gropterrors || grcoverrors || gresterrors || grothererrors || grconverrors));
PrintResult("* OPTIMALITY",!gropterrors);
PrintResult("* COV. MATRIX",!grcoverrors);
PrintResult("* ERROR ESTIMATES",!gresterrors);
PrintResult("* CONVERGENCE",!grconverrors);
PrintResult("* OTHER SUBROUTINES",!grothererrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Task generation. Meaningless task,just random numbers. |
//+------------------------------------------------------------------+
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[])
{
//--- allocation
xy.Resize(n,2);
ArrayResize(s,n);
//--- calculation
for(int i=0; i<n; i++)
{
//--- check
if(randomx)
xy.Set(i,0,xl+(xr-xl)*CMath::RandomReal());
else
xy.Set(i,0,xl+(xr-xl)*i/(n-1));
//--- calculation
xy.Set(i,1,ymin+(ymax-ymin)*CMath::RandomReal());
s[i]=smin+(smax-smin)*CMath::RandomReal();
}
}
//+------------------------------------------------------------------+
//| Task generation. |
//+------------------------------------------------------------------+
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[])
{
//--- allocation
xy.Resize(n,2);
ArrayResize(s,n);
//--- calculation
for(int i=0; i<n; i++)
{
//--- check
if(randomx)
xy.Set(i,0,xl+(xr-xl)*CMath::RandomReal());
else
xy.Set(i,0,xl+(xr-xl)*i/(n-1));
//--- change values
s[i]=smin+(smax-smin)*CMath::RandomReal();
xy.Set(i,1,a+b*xy[i][0]+GenerateNormal(0,s[i]));
}
}
//+------------------------------------------------------------------+
//| Task generation. |
//| y[i] are filled based on A,B,X[I],S[I] |
//+------------------------------------------------------------------+
void CTestLinRegUnit::FillTaskWithY(const double a,const double b,
const int n,CMatrixDouble &xy,
double &s[])
{
//--- change values
for(int i=0; i<n; i++)
xy.Set(i,1,a+b*xy[i][0]+GenerateNormal(0,s[i]));
}
//+------------------------------------------------------------------+
//| Normal random numbers |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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; 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; i++)
v1+=CMath::Sqr(x[i]-mean);
//--- change value
v2=0;
for(i=0; i<n; 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 |
//+------------------------------------------------------------------+
void CTestLinRegUnit::UnsetLR(CLinearModel &lr)
{
int info=0;
//--- create matrix
CMatrixDouble xy;
//--- object of class
CLRReport rep;
//--- allocation
xy.Resize(6,2);
//--- change values
for(int i=0; i<=5; i++)
{
xy.Set(i,0,0);
xy.Set(i,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:
static bool TestXBlas(const bool silent);
};
//+------------------------------------------------------------------+
//| Testing class CXblas |
//+------------------------------------------------------------------+
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; 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; 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; i++)
{
//--- check
if(CMath::RandomReal()>0.2)
{
cx[i].real=2*CMath::RandomReal()-1;
cx[i].imag=2*CMath::RandomReal()-1;
}
else
cx[i]=0;
//--- check
if(CMath::RandomReal()>0.2)
{
cy[i].real=2*CMath::RandomReal()-1;
cy[i].imag=2*CMath::RandomReal()-1;
}
else
cy[i]=0;
}
//--- change value
cv1=0.0;
for(i_=0; i_<n; 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; 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; i++)
ry[i]=ry[0];
for(i=0; i<n; i++)
rx[i]=01;
//--- 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].real=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
cy[0].imag=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
for(i=1; i<n; 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; i++)
cy[i]=cy[0];
for(i=0; i<n; 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");
PrintResult("APPROX.TESTS",!approxerrors);
PrintResult("EXACT TESTS",!exactnesserrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- end
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CDenseSolver |
//+------------------------------------------------------------------+
class CTestDenseSolverUnit
{
public:
static bool TestDenseSolver(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
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");
PrintResult("* REAL",!rerrors);
PrintResult("* COMPLEX",!cerrors);
PrintResult("* SPD",!spderrors);
PrintResult("* HPD",!hpderrors);
PrintResult("* ITERATIVE IMPROVEMENT",!rfserrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Checks whether solver results are correct solution. |
//| Returns True on success. |
//+------------------------------------------------------------------+
bool CTestDenseSolverUnit::RMatrixCheckSolutionM(CMatrixDouble &xe,
const int n,const int m,
const double threshold,
const int info,
CDenseSolverReport &rep,
CMatrixDouble &xs)
{
bool 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(int i=0; i<n; i++)
{
for(int j=0; j<m; j++)
result=result && MathAbs(xe.Get(i,j)-xs.Get(i,j))<=threshold;
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Checks whether solver results are correct solution. |
//| Returns True on success. |
//+------------------------------------------------------------------+
bool CTestDenseSolverUnit::RMatrixCheckSolution(CMatrixDouble &xe,
const int n,
const double threshold,
const int info,
CDenseSolverReport &rep,
double &xs[])
{
//--- create matrix
CMatrixDouble xsm;
//--- allocation
xsm.Resize(n,1);
//--- change values
for(int i_=0; i_<n; i_++)
xsm.Set(i_,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. |
//+------------------------------------------------------------------+
bool CTestDenseSolverUnit::RMatrixCheckSingularM(const int n,
const int m,
const int info,
CDenseSolverReport &rep,
CMatrixDouble &xs)
{
bool 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(int i=0; i<n; i++)
{
for(int j=0; j<m; j++)
result=result && xs.Get(i,j)==0.0;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Checks whether solver results indicate singular matrix. |
//| Returns True on success. |
//+------------------------------------------------------------------+
bool CTestDenseSolverUnit::RMatrixCheckSingular(const int n,
const int info,
CDenseSolverReport &rep,
double &xs[])
{
//--- create matrix
CMatrixDouble xsm;
//--- allocation
xsm.Resize(n,1);
//--- change values
for(int i_=0; i_<n; i_++)
xsm.Set(i_,0,xs[i_]);
//--- return result
return(RMatrixCheckSingularM(n,1,info,rep,xsm));
}
//+------------------------------------------------------------------+
//| Checks whether solver results are correct solution. |
//| Returns True on success. |
//+------------------------------------------------------------------+
bool CTestDenseSolverUnit::CMatrixCheckSolutionM(CMatrixComplex &xe,
const int n,const int m,
const double threshold,
const int info,
CDenseSolverReport &rep,
CMatrixComplex &xs)
{
bool 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(int i=0; i<n; i++)
{
for(int j=0; j<m; j++)
result=result && CMath::AbsComplex(xe.Get(i,j)-xs.Get(i,j))<=threshold;
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Checks whether solver results are correct solution. |
//| Returns True on success. |
//+------------------------------------------------------------------+
bool CTestDenseSolverUnit::CMatrixCheckSolution(CMatrixComplex &xe,
const int n,
const double threshold,
const int info,
CDenseSolverReport &rep,
complex &xs[])
{
//--- create matrix
CMatrixComplex xsm;
//--- allocation
xsm.Resize(n,1);
//--- change values
for(int i_=0; i_<n; i_++)
xsm.Set(i_,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. |
//+------------------------------------------------------------------+
bool CTestDenseSolverUnit::CMatrixCheckSingularM(const int n,
const int m,
const int info,
CDenseSolverReport &rep,
CMatrixComplex &xs)
{
bool 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(int i=0; i<n; i++)
{
for(int j=0; j<m; j++)
result=result && xs.Get(i,j)==0;
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Checks whether solver results indicate singular matrix. |
//| Returns True on success. |
//+------------------------------------------------------------------+
bool CTestDenseSolverUnit::CMatrixCheckSingular(const int n,
const int info,
CDenseSolverReport &rep,
complex &xs[])
{
//--- create matrix
CMatrixComplex xsm;
//--- allocation
xsm.Resize(n,1);
//--- change values
for(int i_=0; i_<n; i_++)
xsm.Set(i_,0,xs[i_]);
//--- return result
return(CMatrixCheckSingularM(n,1,info,rep,xsm));
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
void CTestDenseSolverUnit::RMatrixMakeACopy(CMatrixDouble &a,
const int m,
const int n,
CMatrixDouble &b)
{
//--- allocation
b.Resize(m,n);
//--- copy
for(int i=0; i<m; i++)
{
for(int j=0; j<n; j++)
b.Set(i,j,a.Get(i,j));
}
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
void CTestDenseSolverUnit::CMatrixMakeACopy(CMatrixComplex &a,
const int m,
const int n,
CMatrixComplex &b)
{
//--- allocation
b.Resize(m,n);
//--- copy
for(int i=0; i<m; i++)
{
for(int j=0; j<n; j++)
b.Set(i,j,a.Get(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 |
//+------------------------------------------------------------------+
void CTestDenseSolverUnit::RMatrixDropHalf(CMatrixDouble &a,
const int n,
const bool droplower)
{
//--- calculation
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
//--- check
if((droplower && i>j) || (!droplower && i<j))
a.Set(i,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 |
//+------------------------------------------------------------------+
void CTestDenseSolverUnit::CMatrixDropHalf(CMatrixComplex &a,
const int n,
const bool droplower)
{
//--- calculation
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
//--- check
if((droplower && i>j) || (!droplower && i<j))
a.Set(i,j,1+2*i+3*j);
}
}
}
//+------------------------------------------------------------------+
//| Real test |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<m; j++)
xe.Set(i,j,2*CMath::RandomReal()-1);
}
//--- allocation
b.Resize(n,m);
//--- calculation
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*xe.Get(i_,j);
b.Set(i,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; 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; 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; 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; 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; 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; 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; 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; 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; i++)
{
for(j=n; j<=2*n-1; j++)
atmp.Set(i,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; 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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0);
}
}
//--- check
if(taskkind==1)
{
//--- there is zero column
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CMath::RandomReal()-1);
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0; i_<n; i_++)
a.Set(i_,k,0*a[i_][k]);
}
//--- check
if(taskkind==2)
{
//--- there is zero row
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CMath::RandomReal()-1);
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0; i_<n; i_++)
a.Set(k,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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CMath::RandomReal()-1);
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0; i_<n; i_++)
a.Set(i_,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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CMath::RandomReal()-1);
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0; i_<n; i_++)
a.Set(0,i_,a[k][i_]);
}
//--- allocation
xe.Resize(n,m);
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
xe.Set(i,j,2*CMath::RandomReal()-1);
}
//--- allocation
b.Resize(n,m);
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*xe.Get(i_,j);
b.Set(i,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; 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; 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; 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; i++)
xv[i]=2*CMath::RandomReal()-1;
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CMath::RandomReal()-1);
for(i_=0; i_<n; i_++)
y[i_]=a.Get(i,i_);
//--- function call
CXblas::XDot(y,xv,n,tx,v,verr);
bv[i]=v;
}
//--- change values
for(i_=0; i_<n; i_++)
b.Set(i_,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; i_++)
xv[i_]=x[i_][0];
for(i=0; i<n; i++)
{
for(i_=0; i_<n; i_++)
y[i_]=a.Get(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; i++)
{
for(i_=0; i_<n; i_++)
y[i_]=a.Get(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; i++)
{
for(i_=0; i_<n; i_++)
y[i_]=a.Get(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 |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<m; j++)
xe.Set(i,j,2*CMath::RandomReal()-1);
}
//--- allocation
b.Resize(n,m);
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*xe.Get(i_,j);
b.Set(i,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; 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; 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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0);
}
}
//--- check
if(taskkind==1)
{
//--- there is zero column
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=i; j<n; j++)
{
a.Set(i,j,2*CMath::RandomReal()-1);
a.Set(j,i,a.Get(i,j));
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0; i_<n; i_++)
a.Set(i_,k,0*a[i_][k]);
for(i_=0; i_<n; i_++)
a.Set(k,i_,0*a[k][i_]);
}
//--- check
if(taskkind==2)
{
//--- there is zero row
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=i; j<n; j++)
{
a.Set(i,j,2*CMath::RandomReal()-1);
a.Set(j,i,a.Get(i,j));
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0; i_<n; i_++)
a.Set(k,i_,0*a[k][i_]);
for(i_=0; i_<n; i_++)
a.Set(i_,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; i++)
{
for(j=i; j<n; j++)
{
a.Set(i,j,2*CMath::RandomReal()-1);
a.Set(j,i,a.Get(i,j));
}
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0; i_<n; i_++)
a.Set(i_,0,a[i_][k]);
for(i_=0; i_<n; i_++)
a.Set(0,i_,a[k][i_]);
}
//--- allocation
xe.Resize(n,m);
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
xe.Set(i,j,2*CMath::RandomReal()-1);
}
//--- allocation
b.Resize(n,m);
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*xe.Get(i_,j);
b.Set(i,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; 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; 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 |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<m; j++)
{
xe.SetRe(i,j,2*CMath::RandomReal()-1);
xe.SetIm(i,j,2*CMath::RandomReal()-1);
}
}
//--- allocation
b.Resize(n,m);
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*xe.Get(i_,j);
b.Set(i,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; 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; 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; 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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0);
}
}
//--- check
if(taskkind==1)
{
//--- there is zero column
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0; i_<n; i_++)
a.Set(i_,k,a[i_][k]*0);
}
//--- check
if(taskkind==2)
{
//--- there is zero row
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0; i_<n; i_++)
a.Set(k,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; i++)
{
for(j=0; j<n; j++)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0; i_<n; i_++)
a.Set(i_,0,a[i_][k]);
}
//--- check
if(taskkind==4)
{
//--- equal rows
if(n<2)
continue;
//--- allocation
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
}
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0; i_<n; i_++)
a.Set(0,i_,a[k][i_]);
}
//--- allocation
xe.Resize(n,m);
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
xe.Set(i,j,2*CMath::RandomReal()-1);
}
//--- allocation
b.Resize(n,m);
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*xe.Get(i_,j);
b.Set(i,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; 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; 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; 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; i++)
{
xv[i].real=2*CMath::RandomReal()-1;
xv[i].imag=2*CMath::RandomReal()-1;
}
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
}
for(i_=0; i_<n; i_++)
y[i_]=a.Get(i,i_);
//--- function call
CXblas::XCDot(y,xv,n,tx,v,verr);
bv[i]=v;
}
for(i_=0; i_<n; i_++)
b.Set(i_,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; i_++)
xv[i_]=x[i_][0];
for(i=0; i<n; i++)
{
for(i_=0; i_<n; i_++)
y[i_]=a.Get(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; i++)
{
for(i_=0; i_<n; i_++)
y[i_]=a.Get(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 |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<m; j++)
{
xe.SetRe(i,j,2*CMath::RandomReal()-1);
xe.SetIm(i,j,2*CMath::RandomReal()-1);
}
}
//--- allocation
b.Resize(n,m);
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*xe.Get(i_,j);
b.Set(i,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; 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; 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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,0);
}
}
//--- check
if(taskkind==1)
{
//--- there is zero column
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=i; j<n; j++)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
//--- check
if(i==j)
a.SetIm(i,j,0);
a.Set(j,i,a.Get(i,j));
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0; i_<n; i_++)
a.Set(i_,k,a[i_][k]*0);
for(i_=0; i_<n; i_++)
a.Set(k,i_,a[k][i_]*0);
}
//--- check
if(taskkind==2)
{
//--- there is zero row
a.Resize(n,n);
for(i=0; i<n; i++)
{
for(j=i; j<n; j++)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
//--- check
if(i==j)
a.SetIm(i,j,0);
a.Set(j,i,a.Get(i,j));
}
}
//--- change values
k=CMath::RandomInteger(n);
for(i_=0; i_<n; i_++)
a.Set(k,i_,a[k][i_]*0);
for(i_=0; i_<n; i_++)
a.Set(i_,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; i++)
{
for(j=i; j<n; j++)
{
a.SetRe(i,j,2*CMath::RandomReal()-1);
a.SetIm(i,j,2*CMath::RandomReal()-1);
//--- check
if(i==j)
a.SetIm(i,j,0);
a.Set(j,i,a.Get(i,j));
}
}
//--- change values
k=1+CMath::RandomInteger(n-1);
for(i_=0; i_<n; i_++)
a.Set(i_,0,a[i_][k]);
for(i_=0; i_<n; i_++)
a.Set(0,i_,a[k][i_]);
}
//--- allocation
xe.Resize(n,m);
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
xe.Set(i,j,2*CMath::RandomReal()-1);
}
//--- allocation
b.Resize(n,m);
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*xe.Get(i_,j);
b.Set(i,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; 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; 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 |
//+------------------------------------------------------------------+
void CTestDenseSolverUnit::Unset2D(CMatrixDouble &x)
{
//--- allocation
x.Resize(1,1);
//--- change value
x.Set(0,0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets real vector |
//+------------------------------------------------------------------+
void CTestDenseSolverUnit::Unset1D(double &x[])
{
//--- allocation
ArrayResize(x,1);
//--- change value
x[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Unsets real matrix |
//+------------------------------------------------------------------+
void CTestDenseSolverUnit::CUnset2D(CMatrixComplex &x)
{
//--- allocation
x.Resize(1,1);
//--- change value
x.Set(0,0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets real vector |
//+------------------------------------------------------------------+
void CTestDenseSolverUnit::CUnset1D(complex &x[])
{
//--- allocation
ArrayResize(x,1);
//--- change value
x[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Unsets report |
//+------------------------------------------------------------------+
void CTestDenseSolverUnit::UnsetRep(CDenseSolverReport &r)
{
//--- change values
r.m_r1=-1;
r.m_rinf=-1;
}
//+------------------------------------------------------------------+
//| Unsets report |
//+------------------------------------------------------------------+
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)
{
bool waserrors=false;
//--- check
if(!silent)
{
PrintResult("TESTING LINMIN",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
};
//+------------------------------------------------------------------+
//| Testing class CMinCG |
//+------------------------------------------------------------------+
class CTestMinCGUnit
{
public:
static bool TestMinCG(const bool silent);
static void TestOther(bool &err);
private:
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);
};
//+------------------------------------------------------------------+
//| Testing class CMinCG |
//+------------------------------------------------------------------+
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.Set(0,2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2]));
state.m_g.Set(1,2*state.m_x[1]);
state.m_g.Set(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.Set(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.Set(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; i++)
xe[i]=2*CMath::RandomReal()-1;
//--- change values
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CMath::RandomReal()-1);
a.Set(i,i,a[i][i]+3*MathSign(a[i][i]));
}
for(i=0; i<n; i++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*xe[i_];
b[i]=v;
}
//--- Solve task
for(i=0; i<n; 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; i++)
state.m_g.Set(i,0);
}
for(i=0; i<n; i++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*state.m_x[i_];
//--- check
if(state.m_needf || state.m_needfg)
state.m_f+=CMath::Sqr(v-b[i]);
//--- check
if(state.m_needfg)
{
for(j=0; j<n; j++)
state.m_g.Add(j,2*(v-b[i])*a.Get(i,j));
}
}
}
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- search errors
eqerror=eqerror||rep.m_terminationtype<=0;
for(i=0; i<n; 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; 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; 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; 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; 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");
PrintResult("REFERENCE PROBLEM",!referror);
PrintResult("LIN-1 PROBLEM",!linerror1);
PrintResult("LIN-2 PROBLEM",!linerror2);
PrintResult("LINEAR EQUATIONS",!eqerror);
PrintResult("RESTARTS",!restartserror);
PrintResult("PRECONDITIONING",!precerror);
PrintResult("CONVERGENCE PROPERTIES",!converror);
PrintResult("OTHER PROPERTIES",!othererrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Other properties |
//+------------------------------------------------------------------+
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; 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; i++)
{
state.m_f+=CMath::Sqr((1+i)*state.m_x[i]);
state.m_g.Set(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; i++)
err=err||state.m_x[i]!=x[i];
}
//--- change values
fprev=state.m_f;
for(i_=0; i_<n; i_++)
xlast[i_]=state.m_x[i_];
}
}
//--- function call
CMinCG::MinCGResults(state,x,rep);
//--- search errors
for(i=0; i<n; 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; 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; i++)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f+=CMath::Sqr((1+i)*state.m_x[i]);
//--- check
if(state.m_needfg)
state.m_g.Set(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.Set(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; 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; 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; i++)
{
state.m_f+=a[i]*MathPow(state.m_x[i],4);
state.m_g.Set(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; i++)
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; 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; i++)
{
state.m_f+=a[i]*MathPow(state.m_x[i],4);
state.m_g.Set(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; 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; 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.Set(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 |
//+------------------------------------------------------------------+
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.Set(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.Set(1,2*state.m_x[1]);
state.m_g.Set(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.Set(0,MathSqrt(CMath::m_maxrealnumber));
state.m_g.Set(1,0);
state.m_g.Set(2,0);
}
}
}
//+------------------------------------------------------------------+
//| Calculate test function #2 |
//| Simple variation of #1,much more nonlinear,which makes unlikely |
//| premature convergence of algorithm . |
//+------------------------------------------------------------------+
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.Set(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.Set(1,4*state.m_x[1]*CMath::Sqr(state.m_x[1]));
state.m_g.Set(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.Set(0,MathSqrt(CMath::m_maxrealnumber));
state.m_g.Set(1,0);
state.m_g.Set(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 |
//+------------------------------------------------------------------+
void CTestMinCGUnit::TestFunc3(CMinCGState &state)
{
double 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.Set(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.Set(1,2*(CMath::Sqr(state.m_x[1])+s)*2*state.m_x[1]);
state.m_g.Set(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.Set(0,MathSqrt(CMath::m_maxrealnumber));
state.m_g.Set(1,0);
state.m_g.Set(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. |
//+------------------------------------------------------------------+
void CTestMinCGUnit::CalcIIP2(CMinCGState &state,const int n)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=0;
//--- calculation
for(int i=0; i<n; i++)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f+=CMath::Sqr(i*i+1)*CMath::Sqr(state.m_x[i]);
//--- check
if(state.m_needfg)
state.m_g.Set(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 |
//+------------------------------------------------------------------+
void CTestMinCGUnit::CalcLowRank(CMinCGState &state,const int n,
const int vcnt,double &d[],
CMatrixDouble &v,double &vd[],
double &x0[])
{
//--- create variables
int i=0;
double dx=0;
double t=0;
double t2=0;
//--- change values
state.m_f=0;
//--- calculation
for(i=0; i<n; i++)
{
dx=state.m_x[i]-x0[i];
state.m_f+=0.5*dx*d[i]*dx;
state.m_g.Set(i,d[i]*dx);
}
//--- calculation
for(i=0; i<=vcnt-1; i++)
{
t=(v[i]*(state.m_x-x0)).Sum();
//--- change values
state.m_f+=0.5*t*vd[i]*t;
t2=t*vd[i];
state.m_g+=v[i]*t2;
}
}
//+------------------------------------------------------------------+
//| This function tests preconditioning |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
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; 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; pass++)
{
for(i=0; i<n; 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+=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; 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; pass++)
{
for(i=0; i<n; 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+=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; 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; i++)
{
for(j=0; j<n; j++)
v.Set(i,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; pass++)
{
for(i=0; i<n; 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+=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; pass++)
{
for(i=0; i<n; 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+=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);
ArrayInitialize(x,0);
//--- function calls
CMinCG::MinCGCreate(n,x,state);
ArrayResize(s,n);
for(i=0; i<n; 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; pass++)
{
for(i=0; i<n; 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+=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; pass++)
{
for(i=0; i<n; 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+=rep.m_iterationscount;
//--- search errors
err=err||rep.m_terminationtype<=0;
}
//--- search errors
err=err||cntb2<cntg2;
}
}
}
//+------------------------------------------------------------------+
//| Testing class CMinBLEIC |
//+------------------------------------------------------------------+
class CTestMinBLEICUnit
{
public:
static bool TestMinBLEIC(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Testing class CMinBLEIC |
//+------------------------------------------------------------------+
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");
PrintResult("FEASIBILITY PROPERTIES",!feasibilityerrors);
PrintResult("PRECONDITIONING",!precerrors);
PrintResult("OTHER PROPERTIES",!othererrors);
PrintResult("CONVERGENCE PROPERTIES",!converrors);
PrintResult("INTERNAL ERRORS",!interrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
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). |
//+------------------------------------------------------------------+
void CTestMinBLEICUnit::CheckBounds(double &x[],double &bndl[],
double &bndu[],const int n,
bool &err)
{
//--- calculation
for(int i=0; i<n; 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. |
//+------------------------------------------------------------------+
void CTestMinBLEICUnit::CalcIIP2(CMinBLEICState &state,const int n,
const int fk)
{
//--- check
if(state.m_needfg)
state.m_f=0;
//--- calculation
for(int i=0; i<n; i++)
{
//--- check
if(state.m_needfg)
{
state.m_f+=MathPow(i*i+1,2*fk)*CMath::Sqr(state.m_x[i]);
state.m_g.Set(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). |
//+------------------------------------------------------------------+
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
CRowDouble bl;
CRowDouble bu;
CRowDouble x;
CRowDouble g;
CRowDouble x0;
CRowDouble xc;
CRowDouble xs;
CRowInt ct;
CRowDouble svdw;
//--- create matrix
CMatrixDouble c;
CMatrixDouble svdvt,svdu;
//--- objects of classes
CMinBLEICState state;
CMinBLEICReport rep;
//--- initialization
nmax=5;
double weakepsg=1.0E-4;
double epsx=1.0E-4;
double epsfeas=1.0E-6;
passcount=10;
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;
x=vector<double>::Zeros(n);
g=vector<double>::Zeros(n);
c=matrix<double>::Zeros(1,n+1);
ct.Resize(1);
for(i=0; i<n; i++)
{
x.Set(i,2*CMath::RandomReal()-1);
c.Set(0,i,2*CMath::RandomReal()-1);
v=2*CMath::RandomReal()-1;
c.Add(0,n,c.Get(0,i)*v);
}
ct.Set(0,0);
//--- Create and optimize
if(dkind==0)
{
CMinBLEIC::MinBLEICCreate(n,x,state);
}
if(dkind==1)
{
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
}
CMinBLEIC::MinBLEICSetLC(state,c,ct,1);
CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0);
SetRandomPreconditioner(state,n,preckind);
while(CMinBLEIC::MinBLEICIteration(state))
{
if(state.m_needf || state.m_needfg)
state.m_f=(state.m_x.Pow(p)+0).Sum();
if(state.m_needfg)
state.m_g=state.m_x.Pow(p-1)*p;
}
CMinBLEIC::MinBLEICResults(state,x,rep);
if(rep.m_terminationtype<=0)
{
converr=true;
return;
}
//--- Test feasibility of solution
v=CAblasF::RDotVR(n,x,c,0);
feaserr=feaserr||MathAbs(v-c.Get(0,n))>epsfeas;
//--- 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=CAblasF::RDotRR(n,c,0,c,0);
if(vv!=0.0)
{
//--- Calculate gradient at the result
//--- Project gradient into C
//--- Check projected norm
g=x.Pow(p-1)*p;
v2=CAblasF::RDotRR(n,c,0,c,0);
v=CAblasF::RDotVR(n,g,c,0);
vv=v/v2;
for(i_=0; i_<n; i_++)
g.Add(i_,-vv*c.Get(0,i_));
v3=g.Dot(g);
converr=converr||MathSqrt(v3)>weakepsg;
}
}
}
}
}
//--- 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]
//--- * equality constraints are checked for being well conditioned
//--- * 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.
x=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xs=vector<double>::Zeros(n);
g=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
x.Set(i,2*CMath::RandomReal()-1);
xs.Set(i,2*CMath::RandomReal()-1);
}
do
{
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
c.Set(i,j,2*CMath::RandomReal()-1);
v=CAblasF::RDotVR(n,xs,c,i);
c.Set(i,n,v);
ct.Set(i,0);
}
feaserr=feaserr||!CSingValueDecompose::RMatrixSVD(c,k,n,0,0,0,svdw,svdu,svdvt);
}
while(!(svdw[0]>0.0 && svdw[k-1]>(0.001*svdw[0])));
for(i_=0; i_<n; i_++)
x0.Set(i_,xs[i_]+c.Get(0,i_));
//--- Create and optimize
if(dkind==0)
CMinBLEIC::MinBLEICCreate(n,x,state);
if(dkind==1)
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
CMinBLEIC::MinBLEICSetLC(state,c,ct,k);
CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0);
SetRandomPreconditioner(state,n,preckind);
while(CMinBLEIC::MinBLEICIteration(state))
{
if(state.m_needf || state.m_needfg)
state.m_f=MathPow(state.m_x.ToVector()-x0.ToVector(),2.0).Sum();
if(state.m_needfg)
state.m_g=(state.m_x-x0)*2.0;
}
CMinBLEIC::MinBLEICResults(state,x,rep);
if(rep.m_terminationtype<=0)
{
converr=true;
return;
}
//--- check feasiblity properties
for(i=0; i<k; i++)
{
v=CAblasF::RDotVR(n,x,c,i);
feaserr=feaserr||MathAbs(v-c.Get(i,n))>epsx;
}
//--- Compare with XS
v=MathPow(x.ToVector()-xs.ToVector(),2.0).Sum();
v=MathSqrt(v);
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;
bl=vector<double>::Zeros(n);
bu=vector<double>::Ones(n);
x=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x.Set(i,CMath::RandomReal());
x0.Set(i,3*CMath::RandomReal()-1);
}
//--- Create and optimize
if(dkind==0)
CMinBLEIC::MinBLEICCreate(n,x,state);
if(dkind==1)
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0);
SetRandomPreconditioner(state,n,preckind);
while(CMinBLEIC::MinBLEICIteration(state))
{
if(state.m_needf || state.m_needfg)
state.m_f=MathPow(state.m_x.ToVector()-x0.ToVector(),p).Sum();
if(state.m_needfg)
state.m_g=MathPow(state.m_x.ToVector()-x0.ToVector(),p-1)*p;
feaserr=feaserr||state.m_x.Min()<0.0;
feaserr=feaserr||state.m_x.Max()>1.0;
}
CMinBLEIC::MinBLEICResults(state,x,rep);
if(rep.m_terminationtype<=0)
{
converr=true;
return;
}
//--- * compare solution with analytic one
//--- * check feasibility
v=0.0;
for(i=0; i<n; i++)
{
if(x[i]>0.0 && x[i]<1.0)
v+=CMath::Sqr(p*MathPow(x[i]-x0[i],p-1));
feaserr=feaserr||x[i]<0.0;
feaserr=feaserr||x[i]>1.0;
}
converr=converr||MathSqrt(v)>weakepsg;
}
}
}
}
//--- Same as previous problem, but with minor modifications:
//--- * some bound constraints are 0<=x[i]<=1, some are Ci=x[i]=Ci
//--- * 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;
bl=vector<double>::Zeros(n);
bu=vector<double>::Zeros(n);
x=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
if(CMath::RandomReal()>0.5)
{
bl.Set(i,0);
bu.Set(i,1);
}
else
{
bl.Set(i,CMath::RandomReal());
bu.Set(i,bl[i]);
}
x.Set(i,CMath::RandomReal());
x0.Set(i,3*CMath::RandomReal()-1);
}
//--- Create and optimize
if(dkind==0)
CMinBLEIC::MinBLEICCreate(n,x,state);
if(dkind==1)
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0);
SetRandomPreconditioner(state,n,preckind);
while(CMinBLEIC::MinBLEICIteration(state))
{
if(state.m_needf || state.m_needfg)
state.m_f=MathPow(state.m_x.ToVector()-x0.ToVector(),p).Sum();
if(state.m_needfg)
state.m_g=MathPow(state.m_x.ToVector()-x0.ToVector(),p-1)*p;
for(i=0; i<n; i++)
{
feaserr=feaserr||state.m_x[i]<bl[i];
feaserr=feaserr||state.m_x[i]>bu[i];
}
}
CMinBLEIC::MinBLEICResults(state,x,rep);
if(rep.m_terminationtype<=0)
{
converr=true;
return;
}
//--- * compare solution with analytic one
//--- * check feasibility
v=0.0;
for(i=0; i<n; i++)
{
if(x[i]>bl[i] && x[i]<bu[i])
v+=CMath::Sqr(p*MathPow(x[i]-x0[i],p-1));
feaserr=feaserr||x[i]<bl[i];
feaserr=feaserr||x[i]>bu[i];
}
converr=converr||MathSqrt(v)>weakepsg;
}
}
}
}
//--- 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;
x=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
c=matrix<double>::Zeros(2*n,n+1);
ct.Resize(2*n);
for(i=0; i<n; i++)
{
x.Set(i,CMath::RandomReal());
x0.Set(i,3*CMath::RandomReal()-1);
c.Row(2*i,vector<double>::Zeros(n+1));
c.Row(2*i+1,vector<double>::Zeros(n+1));
c.Set(2*i,i,1);
c.Set(2*i+0,n,0);
ct.Set(2*i,1);
c.Set(2*i+1,i,1);
c.Set(2*i+1,n,1);
ct.Set(2*i+1,-1);
}
//--- Create and optimize
CMinBLEIC::MinBLEICCreate(n,x,state);
CMinBLEIC::MinBLEICSetLC(state,c,ct,2*n);
CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0);
SetRandomPreconditioner(state,n,preckind);
while(CMinBLEIC::MinBLEICIteration(state))
{
if(state.m_needfg)
{
state.m_f=MathPow(state.m_x.ToVector()-x0.ToVector(),p).Sum();
state.m_g=MathPow(state.m_x.ToVector()-x0.ToVector(),p-1)*p;
continue;
}
//--- Unknown protocol specified
interr=true;
return;
}
CMinBLEIC::MinBLEICResults(state,x,rep);
if(rep.m_terminationtype<=0)
{
converr=true;
return;
}
//--- * compare solution with analytic one
//--- * check feasibility
v=0.0;
for(i=0; i<n; i++)
{
if(x[i]>0.02 && x[i]<0.98)
v+=CMath::Sqr(p*MathPow(x[i]-x0[i],p-1));
feaserr=feaserr||x[i]<(0.0-epsfeas);
feaserr=feaserr||x[i]>(1.0+epsfeas);
}
converr=converr||MathSqrt(v)>weakepsg;
}
}
}
//--- Feasibility problem:
//--- * bound constraints 0<=x[i]<=1
//--- * starting point xs with xs[i] in [-1,+2]
//--- * random point xc from [0,1] is used to generate K<=N
//--- random linear equality/inequality constraints of the form
//--- (c,x-xc)=0.0 (or, alternatively, >= or <=), where
//--- c is a random vector.
//--- * 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
//--- * we do not know analytic form of the solution, and, if fact, we do not
//--- check for solution correctness. We just check that algorithm converges
//--- to the feasible points.
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;
x0=vector<double>::Zeros(n);
xc=vector<double>::Zeros(n);
xs=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
bl=vector<double>::Zeros(n);
bu=vector<double>::Ones(n);
for(i=0; i<n; i++)
{
x0.Set(i,3*CMath::RandomReal()-1);
xs.Set(i,3*CMath::RandomReal()-1);
xc.Set(i,0.1+0.8*CMath::RandomReal());
}
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
{
c.Set(i,j,2*CMath::RandomReal()-1);
c.Add(i,n,c.Get(i,j)*xc[j]);
}
ct.Set(i,CMath::RandomInteger(3)-1);
}
//--- Create and optimize
CMinBLEIC::MinBLEICCreate(n,xs,state);
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
CMinBLEIC::MinBLEICSetLC(state,c,ct,k);
CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0);
SetRandomPreconditioner(state,n,preckind);
while(CMinBLEIC::MinBLEICIteration(state))
{
if(state.m_needfg)
{
state.m_f=MathPow(state.m_x.ToVector()-x0.ToVector(),p).Sum();
state.m_g=MathPow(state.m_x.ToVector()-x0.ToVector(),p-1)*p;
continue;
}
//--- Unknown protocol specified
interr=true;
return;
}
CMinBLEIC::MinBLEICResults(state,x,rep);
if(rep.m_terminationtype<=0)
{
converr=true;
return;
}
//--- Check feasibility
for(i=0; i<n; i++)
{
feaserr=feaserr||x[i]<0.0;
feaserr=feaserr||x[i]>1.0;
}
for(i=0; i<k; i++)
{
v=CAblasF::RDotVR(n,x,c,i);
v-=c.Get(i,n);
if(ct[i]==0)
feaserr=feaserr||MathAbs(v)>epsfeas;
if(ct[i]<0)
feaserr=feaserr||v>epsfeas;
if(ct[i]>0)
feaserr=feaserr||v<(-epsfeas);
}
}
}
}
}
//--- 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;
bl=vector<double>::Zeros(n);
bu=vector<double>::Ones(n);
x=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x.Set(i,CMath::RandomReal());
x0.Set(i,3*CMath::RandomReal()-1);
}
i=CMath::RandomInteger(n);
bl.Set(i,1);
bu.Set(i,0);
//--- Create and optimize
CMinBLEIC::MinBLEICCreate(n,x,state);
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0);
SetRandomPreconditioner(state,n,preckind);
while(CMinBLEIC::MinBLEICIteration(state))
{
if(state.m_needfg)
{
state.m_f=MathPow(state.m_x.ToVector()-x0.ToVector(),p).Sum();
state.m_g=MathPow(state.m_x.ToVector()-x0.ToVector(),p-1)*p;
continue;
}
//--- Unknown protocol specified
interr=true;
return;
}
CMinBLEIC::MinBLEICResults(state,x,rep);
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;
x=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k+1,n+1);
ct.Resize(k+1);
ct.Fill(0);
for(i=0; i<n; i++)
x.Set(i,CMath::RandomReal());
for(i=0; i<k; i++)
{
for(j=0; j<=n; j++)
c.Set(i,j,2*CMath::RandomReal()-1);
}
for(i_=0; i_<n; i_++)
c.Set(k,i_,c.Get(0,i_));
c.Set(k,n,c.Get(0,n)+1);
//--- Create and optimize
CMinBLEIC::MinBLEICCreate(n,x,state);
CMinBLEIC::MinBLEICSetLC(state,c,ct,k+1);
CMinBLEIC::MinBLEICSetCond(state,weakepsg,0.0,0.0,0);
SetRandomPreconditioner(state,n,preckind);
while(CMinBLEIC::MinBLEICIteration(state))
{
if(state.m_needfg)
{
state.m_f=(state.m_x.Pow(p)+0).Sum();
state.m_g=state.m_x.Pow(p-1)*p;
continue;
}
//--- Unknown protocol specified
interr=true;
return;
}
CMinBLEIC::MinBLEICResults(state,x,rep);
feaserr=feaserr||rep.m_terminationtype!=-3;
}
}
}
}
}
}
//+------------------------------------------------------------------+
//| This function additional properties. |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
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; 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; i++)
{
state.m_f+=CMath::Sqr((1+i)*state.m_x[i]);
state.m_g.Set(i,2*(1+i)*state.m_x[i]);
}
}
//--- check
if(state.m_xupdated)
{
//--- check
if(fprev==CMath::m_maxrealnumber)
{
for(i=0; i<n; i++)
{
err=err||state.m_x[i]!=x[i];
}
}
//--- change values
fprev=state.m_f;
for(i_=0; i_<n; i_++)
xlast[i_]=state.m_x[i_];
}
}
//--- function call
CMinBLEIC::MinBLEICResults(state,x,rep);
//--- search errors
for(i=0; i<n; 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; 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; i++)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f+=CMath::Sqr((1+i)*state.m_x[i]);
//--- check
if(state.m_needfg)
state.m_g.Set(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.Set(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.Set(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.Set(0,n,0);
//--- change values
for(i=0; i<n; i++)
{
x[i]=CMath::RandomReal()+1;
bl[i]=-100000;
bu[i]=100000;
c.Set(0,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,0.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; i++)
{
state.m_f+=a[i]*MathPow(state.m_x[i],2);
state.m_g.Set(i,2*a[i]*state.m_x[i]);
}
}
}
//--- 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; i++)
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.Set(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) |
//+------------------------------------------------------------------+
void CTestMinBLEICUnit::TestConv(bool &err)
{
//--- create variables
int passcount=10;
int pass=0;
double epsc=1.0E-4;
double epsg=1.0E-8;
double tol=0.001;
//--- create arrays
double bl[];
double bu[];
double x[];
int ct[];
//--- create matrix
CMatrixDouble c;
//--- objects of classes
CMinBLEICState state;
CMinBLEICReport rep;
//--- 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.Set(0,0,1);
c.Set(0,1,1);
c.Set(0,2,1.5);
ct[0]=-1;
c.Set(1,0,1);
c.Set(1,1,-1);
c.Set(1,2,1.5);
ct[1]=-1;
c.Set(2,0,-1);
c.Set(2,1,1);
c.Set(2,2,1.5);
ct[2]=-1;
c.Set(3,0,-1);
c.Set(3,1,-1);
c.Set(3,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.Set(0,1);
state.m_g.Set(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.Set(0,2*(state.m_x[0]+10));
state.m_g.Set(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.Set(0,2*(state.m_x[0]+10));
state.m_g.Set(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) |
//+------------------------------------------------------------------+
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; i++)
{
x[i]=0;
units[i]=1;
}
//--- function call
CMinBLEIC::MinBLEICCreate(n,x,state);
CMinBLEIC::MinBLEICSetCond(state,epsg,0.0,0.0,0);
//--- check
if(ckind==1 || ckind==3)
{
//--- allocation
ArrayResize(bl,n);
ArrayResize(bu,n);
for(i=0; i<n; 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; i++)
c.Set(0,i,2*CMath::RandomReal()-1);
c.Set(0,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; pass++)
{
for(i=0; i<n; 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+=rep.m_inneriterationscount;
//--- search errors
err=err||rep.m_terminationtype<=0;
}
//--- allocation
ArrayResize(diagh,n);
for(i=0; i<n; 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; pass++)
{
for(i=0; i<n; 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+=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; i++)
s[i]=1.0/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; pass++)
{
for(i=0; i<n; 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+=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; pass++)
{
for(i=0; i<n; 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+=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 |
//+------------------------------------------------------------------+
void CTestMinBLEICUnit::SetRandomPreconditioner(CMinBLEICState &state,
const int n,
const int preckind)
{
//--- create array
double p[];
//--- check
if(preckind==1)
{
//--- allocation
ArrayResize(p,n);
for(int i=0; i<n; i++)
p[i]=MathExp(10*CMath::RandomReal()-5);
//--- function call
CMinBLEIC::MinBLEICSetPrecDiag(state,p);
}
else
CMinBLEIC::MinBLEICSetPrecDefault(state);
}
//+------------------------------------------------------------------+
//| Testing class CMarkovCPD |
//+------------------------------------------------------------------+
class CTestMCPDUnit
{
public:
static bool TestMCPD(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Testing class CMarkovCPD |
//+------------------------------------------------------------------+
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");
PrintResult("* SIMPLE",!simpleerrors);
PrintResult("* ENTRY/EXIT",!entryexiterrors);
PrintResult("* EQUALITY CONSTRAINTS",!ecerrors);
PrintResult("* BOUND CONSTRAINTS",!bcerrors);
PrintResult("* LINEAR CONSTRAINTS",!lcerrors);
PrintResult("* OTHER PROPERTIES",!othererrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Simple test with no "entry"/"exit" states |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
void CTestMCPDUnit::TestSimple(bool &err)
{
//--- create variables
int n=0;
double threshold=1.0E-2;
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;
//--- 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; i++)
{
for(j=0; j<n; j++)
pexact.Set(i,j,CMath::RandomReal());
}
for(j=0; j<n; j++)
{
i=CMath::RandomInteger(n);
pexact.Set(i,j,pexact.Get(i,j)+0.1);
}
//--- calculation
for(j=0; j<n; j++)
{
v=0;
for(i=0; i<n; i++)
v+=pexact.Get(i,j);
for(i=0; i<n; i++)
pexact.Set(i,j,pexact.Get(i,j)/v);
}
//--- Initialize solver:
//--- * create object
//--- * add tracks
CMarkovCPD::MCPDCreate(n,s);
for(i=0; i<n; i++)
{
xy.Resize(2,n);
//--- change values
for(j=0; j<n; j++)
xy.Set(0,j,0);
xy.Set(0,i,1);
for(j=0; j<n; j++)
xy.Set(1,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; i++)
{
for(j=0; j<n; j++)
err=err||MathAbs(p.Get(i,j)-pexact.Get(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; i++)
{
for(j=0; j<n; j++)
pexact.Set(i,j,CMath::RandomReal());
}
//--- calculation
for(j=0; j<n; j++)
{
i=CMath::RandomInteger(n);
pexact.Set(i,j,pexact.Get(i,j)+0.1);
}
for(j=0; j<n; j++)
{
v=0;
for(i=0; i<n; i++)
v+=pexact.Get(i,j);
for(i=0; i<n; i++)
pexact.Set(i,j,pexact.Get(i,j)/v);
}
//--- Initialize solver:
//--- * create object
//--- * add tracks
CMarkovCPD::MCPDCreate(n,s);
for(i=0; i<n; i++)
{
//--- allocation
xy.Resize(2,n);
for(j=0; j<n; j++)
xy.Set(0,j,0);
//--- "main" element
xy.Set(0,i,1.0-2*offdiagonal);
for(j=0; j<n; j++)
xy.Set(1,j,(1.0-2*offdiagonal)*pexact[j][i]);
//--- off-diagonal ones
if(i>0)
{
xy.Set(0,i-1,offdiagonal);
for(j=0; j<n; j++)
xy.Set(1,j,xy[1][j]+offdiagonal*pexact[j][i-1]);
}
//--- check
if(i<n-1)
{
xy.Set(0,i+1,offdiagonal);
for(j=0; j<n; j++)
xy.Set(1,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; i++)
{
for(j=0; j<n; j++)
err=err||MathAbs(p.Get(i,j)-pexact.Get(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; i++)
{
for(j=0; j<n; j++)
pexact.Set(i,j,CMath::RandomReal());
}
//--- change values
for(j=0; j<n; j++)
{
i=CMath::RandomInteger(n);
pexact.Set(i,j,pexact.Get(i,j)+0.1);
}
for(j=0; j<n; j++)
{
v=0;
for(i=0; i<n; i++)
v+=pexact.Get(i,j);
for(i=0; i<n; i++)
pexact.Set(i,j,pexact.Get(i,j)/v);
}
//--- Initialize solver:
//--- * create object
//--- * add tracks
CMarkovCPD::MCPDCreate(n,s);
for(i=0; i<n; i++)
{
//--- allocation
xy.Resize(2,n);
for(j=0; j<n; j++)
xy.Set(0,j,0);
//--- "main" element
v0=9*CMath::RandomReal()+1;
xy.Set(0,i,v0*(1.0-2*offdiagonal));
for(j=0; j<n; j++)
xy.Set(1,j,v0*(1.0-2*offdiagonal)*pexact[j][i]);
//--- off-diagonal ones
if(i>0)
{
xy.Set(0,i-1,v0*offdiagonal);
for(j=0; j<n; j++)
xy.Set(1,j,xy[1][j]+v0*offdiagonal*pexact[j][i-1]);
}
//--- check
if(i<n-1)
{
xy.Set(0,i+1,v0*offdiagonal);
for(j=0; j<n; j++)
xy.Set(1,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; i++)
{
for(j=0; j<n; j++)
err=err||MathAbs(p.Get(i,j)-pexact.Get(i,j))>threshold;
}
}
else
err=true;
}
}
//+------------------------------------------------------------------+
//| Test for different combinations of "entry"/"exit" models |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
void CTestMCPDUnit::TestEntryExit(bool &err)
{
//--- create variables
int n=0;
double threshold=1.0E-3;
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;
//--- 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; i++)
{
for(j=0; j<n; j++)
{
pexact.Set(i,j,1+CMath::RandomInteger(5));
//--- check
if(i==entrystate)
pexact.Set(i,j,0.0);
//--- check
if(j==exitstate)
pexact.Set(i,j,0.0);
}
}
//--- calculation
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i=0; i<n; i++)
v+=pexact.Get(i,j);
//--- check
if(v!=0.0)
{
for(i=0; i<n; i++)
pexact.Set(i,j,pexact.Get(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; k++)
{
//--- Generate track whose length is in 2..4
xy.Resize(2+CMath::RandomInteger(3),n);
for(j=0; j<n; j++)
xy.Set(0,j,0.05*CMath::RandomReal());
xy.Set(0,k,1+CMath::RandomReal());
//--- calculation
for(i=1; i<=CAp::Rows(xy)-1; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(j!=entrystate)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=pexact.Get(j,i_)*xy[i-1][i_];
xy.Set(i,j,v);
}
else
xy.Set(i,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; j++)
v+=xy.Get(i,j);
//--- check
if(v>0.0)
{
for(j=0; j<n; j++)
xy.Set(i,j,xy.Get(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; i++)
{
for(j=0; j<n; j++)
err=err||MathAbs(p.Get(i,j)-pexact.Get(i,j))>threshold;
}
}
else
err=true;
}
}
}
}
}
//+------------------------------------------------------------------+
//| Test equality constraints. |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
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.Set(i,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; 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; i++)
{
for(j=0; j<n; j++)
ec.Set(i,j,CInfOrNaN::NaN());
}
for(j=1; j<=n-2; j++)
ec.Set(1+CMath::RandomInteger(n-2),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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(CMath::IsFinite(ec.Get(i,j)))
err=err||p.Get(i,j)!=ec.Get(i,j);
}
}
}
else
err=true;
}
}
}
}
//+------------------------------------------------------------------+
//| Test bound constraints. |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
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;
//--- 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.Set(i,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; 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; i++)
{
for(j=0; j<n; j++)
{
bndl.Set(i,j,CInfOrNaN::NegativeInfinity());
bndu.Set(i,j,CInfOrNaN::PositiveInfinity());
}
}
//--- change values
for(j=1; j<=n-2; j++)
{
i=1+CMath::RandomInteger(n-2);
bndl.Set(i,j,0.5-0.1*CMath::RandomReal());
bndu.Set(i,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; i++)
{
for(j=0; j<n; j++)
{
err=err||p.Get(i,j)<bndl.Get(i,j);
err=err||p.Get(i,j)>bndu.Get(i,j);
}
}
}
else
err=true;
}
}
}
}
//+------------------------------------------------------------------+
//| Test bound constraints. |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
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.Set(i,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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(((i==0 || i==n-1) || j==0) || j==n-1)
c.Set(0,i*n+j,0);
else
{
c.Set(0,i*n+j,CMath::RandomReal());
v+=c[0][i*n+j]*(1.0/(double)n);
}
}
}
//--- change value
c.Set(0,n*n,v);
ct[0]=CMath::RandomInteger(3)-1;
//--- check
if(ct[0]<0)
c.Set(0,n*n,c[0][n*n]+0.1);
//--- check
if(ct[0]>0)
c.Set(0,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; i++)
{
for(j=0; j<n; j++)
v+=p.Get(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.Set(0,i,0.0);
for(i=0; i<n; i++)
c.Set(0,n*i+jc,1.0);
c.Set(0,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.Set(0,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.Set(0,i,0.0);
for(j=0; j<n; j++)
c.Set(0,n*entrystate+j,1.0);
ct[0]=0;
c.Set(0,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.Set(0,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.Set(0,i,0.0);
for(i=0; i<n; i++)
c.Set(0,n*i+exitstate,1.0);
ct[0]=0;
c.Set(0,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.Set(0,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.Set(i,j,CMath::RandomReal());
}
//--- Generate random constraints
c.Resize(k,n*n+1);
ArrayResize(ct,k);
//--- calculation
for(i=0; i<k; i++)
{
//--- Generate constraint and its right part
c.Set(i,n*n,0);
for(j=0; j<=n*n-1; j++)
{
c.Set(i,j,2*CMath::RandomReal()-1);
c.Set(i,n*n,c[i][n*n]+c.Get(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.Set(i,n*n,c[i][n*n]+0.1);
//--- check
if(ct[i]>0)
c.Set(i,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; t++)
{
//--- change values
v=0;
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
v+=p.Get(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 |
//+------------------------------------------------------------------+
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:
static bool TestFbls(const bool silent);
};
//+------------------------------------------------------------------+
//| Testing |
//+------------------------------------------------------------------+
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
CRowDouble tmp1;
CRowDouble tmp2;
double b[];
CRowDouble 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);
x.Resize(n);
ArrayResize(xe,n);
tmp1.Resize(m);
tmp2.Resize(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; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CMath::RandomReal()-1);
}
//--- change values
alpha=CMath::RandomReal()+0.1;
for(i=0; i<n; i++)
{
b[i]=2*CMath::RandomReal()-1;
xe[i]=2*CMath::RandomReal()-1;
x.Set(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; i++)
x.Set(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
tmp2+=x*alpha+0;
tmp2-=b;
//--- change value
v=CAblasF::RDotV2(n,tmp2);
e1=MathSqrt(v);
//--- function calls
double X[];
x.ToArray(X);
CFbls::FblsSolveCGx(a,m,n,alpha,b,X,buf);
x=X;
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
tmp2+=x*alpha+0;
tmp2-=b;
//--- change value
v=CAblasF::RDotV2(n,tmp2);
e2=MathSqrt(v);
//--- search errors
cgerrors=cgerrors||e2>0.001*e1;
//--- Test sparse CG (which relies on reverse communication)
for(i=0; i<n; i++)
x.Set(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
tmp2+=x*alpha+0;
tmp2-=b;
//--- change value
v=CAblasF::RDotV2(n,tmp2);
e1=MathSqrt(v);
//--- function call
x.ToArray(X);
CFbls::FblsCGCreate(X,b,n,cgstate);
x=X;
//--- 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);
cgstate.m_ax+=cgstate.m_x*alpha+0;
//--- change values
v1=CAblasF::RDotV2(m,tmp1);
v2=CAblasF::RDotV2(n,cgstate.m_x);
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
tmp2+=cgstate.m_xk*alpha+0;
tmp2-=b;
//--- change value
v=CAblasF::RDotV2(n,tmp2);
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");
PrintResult("CG ERRORS",!cgerrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CMinLBFGS |
//+------------------------------------------------------------------+
class CTestMinLBFGSUnit
{
public:
static bool TestMinLBFGS(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Testing class CMinLBFGS |
//+------------------------------------------------------------------+
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.Set(0,2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2]));
state.m_g.Set(1,2*state.m_x[1]);
state.m_g.Set(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.Set(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+=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; i++)
xe[i]=2*CMath::RandomReal()-1;
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CMath::RandomReal()-1);
a.Set(i,i,a[i][i]+3*MathSign(a[i][i]));
}
//--- calculation
for(i=0; i<n; i++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(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; 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; i++)
state.m_g.Set(i,0);
}
for(i=0; i<n; i++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*state.m_x[i_];
//--- check
if(state.m_needf || state.m_needfg)
state.m_f+=CMath::Sqr(v-b[i]);
//--- check
if(state.m_needfg)
{
for(j=0; j<n; j++)
state.m_g.Add(j,2*(v-b[i])*a.Get(i,j));
}
}
}
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- search errors
eqerror=eqerror||rep.m_terminationtype<=0;
for(i=0; i<n; 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.Set(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.Set(1,2*state.m_x[1]);
state.m_g.Set(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");
PrintResult("REFERENCE PROBLEM",!referror);
PrintResult("NON-CONVEX PROBLEM",!nonconverror);
PrintResult("LINEAR EQUATIONS",!eqerror);
PrintResult("RESTARTS",!restartserror);
PrintResult("PRECONDITIONER",!precerror);
PrintResult("CONVERGENCE PROPERTIES",!converror);
PrintResult("CRASH TEST",!crashtest);
PrintResult("OTHER PROPERTIES",!othererrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Calculate test function #1 |
//| It may show very interesting behavior when optimized with |
//| 'x[0]>=ln(2)' constraint. |
//+------------------------------------------------------------------+
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.Set(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.Set(1,2*state.m_x[1]);
state.m_g.Set(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.Set(0,MathSqrt(CMath::m_maxrealnumber));
state.m_g.Set(1,0);
state.m_g.Set(2,0);
}
}
}
//+------------------------------------------------------------------+
//| Calculate test function #2 |
//| Simple variation of #1,much more nonlinear,which makes unlikely |
//| premature convergence of algorithm. |
//+------------------------------------------------------------------+
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.Set(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.Set(1,4*state.m_x[1]*CMath::Sqr(state.m_x[1]));
state.m_g.Set(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.Set(0,MathSqrt(CMath::m_maxrealnumber));
state.m_g.Set(1,0);
state.m_g.Set(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 |
//+------------------------------------------------------------------+
void CTestMinLBFGSUnit::TestFunc3(CMinLBFGSState &state)
{
double 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.Set(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.Set(1,2*(CMath::Sqr(state.m_x[1])+s)*2*state.m_x[1]);
state.m_g.Set(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.Set(0,MathSqrt(CMath::m_maxrealnumber));
state.m_g.Set(1,0);
state.m_g.Set(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. |
//+------------------------------------------------------------------+
void CTestMinLBFGSUnit::CalcIIP2(CMinLBFGSState &state,const int n)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f=0;
//--- calculation
for(int i=0; i<n; i++)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f+=CMath::Sqr(i*i+1)*CMath::Sqr(state.m_x[i]);
//--- check
if(state.m_needfg)
state.m_g.Set(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) |
//+------------------------------------------------------------------+
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; 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; pass++)
{
for(i=0; i<n; 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+=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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(i==j)
a.Set(i,i,1);
else
a.Set(i,j,0);
}
}
//--- function call
CMinLBFGS::MinLBFGSSetPrecCholesky(state,a,pkind==0);
//--- change values
cntb2=0;
for(pass=0; pass<k; pass++)
{
for(i=0; i<n; 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+=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; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(i==j)
a.Set(i,i,(i*i+1)*(0.8+0.4*CMath::RandomReal()));
else
{
//--- check
if((pkind==0 && j>i) || (pkind==1 && j<i))
a.Set(i,j,0.1*CMath::RandomReal()-0.05);
else
a.Set(i,j,CInfOrNaN::NaN());
}
}
}
//--- function call
CMinLBFGS::MinLBFGSSetPrecCholesky(state,a,pkind==0);
cntg1=0;
for(pass=0; pass<k; pass++)
{
//--- change values
for(i=0; i<n; 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+=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; 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; pass++)
{
//--- change values
for(i=0; i<n; 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+=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; i++)
x[i]=0;
//--- function call
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
//--- allocation
ArrayResize(s,n);
for(i=0; i<n; 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; pass++)
{
for(i=0; i<n; 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+=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; pass++)
{
for(i=0; i<n; 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+=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) |
//+------------------------------------------------------------------+
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; 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; i++)
{
state.m_f+=CMath::Sqr((1+i)*state.m_x[i]);
state.m_g.Set(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; i++)
err=err||state.m_x[i]!=x[i];
}
//--- change values
fprev=state.m_f;
for(i_=0; i_<n; i_++)
xlast[i_]=state.m_x[i_];
}
}
//--- function call
CMinLBFGS::MinLBFGSResults(state,x,rep);
//--- search errors
for(i=0; i<n; 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; 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; i++)
{
//--- check
if(state.m_needf || state.m_needfg)
state.m_f+=CMath::Sqr((1+i)*state.m_x[i]);
//--- check
if(state.m_needfg)
state.m_g.Set(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.Set(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; 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; 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; i++)
{
state.m_f+=a[i]*MathPow(state.m_x[i],4);
state.m_g.Set(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; i++)
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; 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; i++)
{
state.m_f+=a[i]*MathPow(state.m_x[i],4);
state.m_g.Set(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; 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; 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.Set(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 and CMLPTrain |
//+------------------------------------------------------------------+
class CTestMLPTrainUnit
{
public:
static bool TestMLPTrain(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Testing class CMLPBase and CMLPTrain |
//+------------------------------------------------------------------+
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.Set(0,0,-1);
xy.Set(0,1,-1);
xy.Set(0,2,-1);
xy.Set(1,0,1);
xy.Set(1,1,-1);
xy.Set(1,2,1);
xy.Set(2,0,-1);
xy.Set(2,1,1);
xy.Set(2,2,1);
xy.Set(3,0,1);
xy.Set(3,1,1);
xy.Set(3,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.Set(i,0,2*CMath::RandomReal()-1);
xy.Set(i,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");
PrintResult("INFORMATIONAL FUNCTIONS",!inferrors);
PrintResult("BASIC PROCESSING",!procerrors);
PrintResult("GRADIENT CALCULATION",!graderrors);
PrintResult("HESSIAN CALCULATION",!hesserrors);
PrintResult("TRAINING",!trnerrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
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. |
//+------------------------------------------------------------------+
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.Alloc_Entry();
_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.Alloc_Entry();
_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.Alloc_Entry();
_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.Alloc_Entry();
_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.Alloc_Entry();
_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.Alloc_Entry();
_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.Alloc_Entry();
_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.Alloc_Entry();
_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.Alloc_Entry();
_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.Alloc_Entry();
_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.Alloc_Entry();
_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.Alloc_Entry();
_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 |
//+------------------------------------------------------------------+
void CTestMLPTrainUnit::UnsetNetwork(CMultilayerPerceptron &network)
{
CMLPBase::MLPCreate0(1,1,network);
}
//+------------------------------------------------------------------+
//| Informational functions test |
//+------------------------------------------------------------------+
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.Set(i,j,0);
}
//--- allocation
ArrayResize(x,nin);
//--- change values
for(i=0; i<nin ; i++)
x[i]=2*CMath::RandomReal()-1;
//--- allocation
ArrayResize(y,nout);
//--- change values
for(i=0; i<nout; i++)
y[i]=2*CMath::RandomReal()-1;
for(j=0; j<nin ; j++)
{
//--- function call
CMLPBase::MLPGetInputScaling(network,j,mean,sigma);
neurons.Set(0,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.Set(i,j,neurons.Get(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.Get(i,j)-c,fkind,f,df,d2f);
neurons.Set(i,j,f);
}
}
//--- check
if(nkind==1)
{
s=0;
for(j=0; j<nout; j++)
s=s+MathExp(neurons[nlayers-1][j]);
for(j=0; j<nout; j++)
neurons.Set(nlayers-1,j,MathExp(neurons[nlayers-1][j])/s);
}
//--- calculation
for(j=0; j<nout; j++)
{
//--- function call
CMLPBase::MLPGetOutputScaling(network,j,mean,sigma);
neurons.Set(nlayers-1,j,neurons[nlayers-1][j]*sigma+mean);
}
//--- function call
CMLPBase::MLPProcess(network,x,y);
//--- search errors
for(j=0; j<nout; j++)
err=err||MathAbs(neurons[nlayers-1][j]-y[j])>threshold;
}
//+------------------------------------------------------------------+
//| Processing functions test |
//+------------------------------------------------------------------+
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; i_++)
network.m_weights.Set(i_,0);
zeronet=true;
}
//--- Same inputs leads to same outputs
for(i=0; i<nin ; i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=x1[i];
}
for(i=0; i<nout; 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; 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 ; i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=x1[i];
}
for(i=0; i<nout; 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; 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.Alloc_Entry();
_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 ; i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=x1[i];
}
for(i=0; i<nout; 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; 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 ; i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=2*CMath::RandomReal()-1;
}
for(i=0; i<nout; 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; 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 ; i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=2*CMath::RandomReal()-1;
}
for(i=0; i<nout; 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; 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 ; i++)
{
x1[i]=2*CMath::RandomReal()-1;
x2[i]=2*CMath::RandomReal()-1;
}
for(i=0; i<nout; 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; 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 ; i++)
x1[i]=2*CMath::RandomReal()-1;
//--- function call
CMLPBase::MLPProcess(network,x1,y1);
v=0;
for(i=0; i<nout; i++)
{
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 ; i++)
x1[i]=2*CMath::RandomReal()-1;
//--- function call
CMLPBase::MLPProcess(network,x1,y1);
for(i=0; i<nout; 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 ; i++)
x1[i]=2*CMath::RandomReal()-1;
//--- function call
CMLPBase::MLPProcess(network,x1,y1);
//--- search errors
for(i=0; i<nout; i++)
err=(err||y1[i]<MathMin(a1,a2))||y1[i]>MathMax(a1,a2);
}
}
}
//+------------------------------------------------------------------+
//| Gradient functions test |
//+------------------------------------------------------------------+
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 ; i++)
x[i]=4*CMath::RandomReal()-2;
for(i_=0; i_<nin ; i_++)
xy.Set(0,i_,x[i_]);
//--- check
if(CMLPBase::MLPIsSoftMax(network))
{
for(i=0; i<nout; i++)
y[i]=0;
xy.Set(0,nin,CMath::RandomInteger(nout));
y[(int)MathRound(xy[0][nin])]=1;
}
else
{
for(i=0; i<nout; i++)
y[i]=4*CMath::RandomReal()-2;
i1_=-nin;
for(i_=nin; i_<nin+nout ; i_++)
xy.Set(0,i_,y[i_+i1_]);
}
//--- function calls
CMLPBase::MLPGrad(network,x,y,e,grad2);
CMLPBase::MLPProcess(network,x,y2);
for(i_=0; i_<nout; i_++)
y2[i_]=y2[i_]-y[i_];
//--- change value
v=0.0;
for(i_=0; i_<nout; 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; i++)
{
wprev=network.m_weights[i];
network.m_weights.Set(i,wprev-2*h);
//--- function call
CMLPBase::MLPProcess(network,x,y1);
for(i_=0; i_<nout; i_++)
y1[i_]=y1[i_]-y[i_];
//--- change value
v1=0.0;
for(i_=0; i_<nout; i_++)
v1+=y1[i_]*y1[i_];
v1=v1/2;
network.m_weights.Set(i,wprev-h);
//--- function call
CMLPBase::MLPProcess(network,x,y1);
for(i_=0; i_<nout; i_++)
y1[i_]=y1[i_]-y[i_];
//--- change value
v2=0.0;
for(i_=0; i_<nout; i_++)
v2+=y1[i_]*y1[i_];
v2=v2/2;
network.m_weights.Set(i,wprev+h);
//--- function call
CMLPBase::MLPProcess(network,x,y1);
for(i_=0; i_<nout; i_++)
y1[i_]=y1[i_]-y[i_];
//--- change value
v3=0.0;
for(i_=0; i_<nout; i_++)
v3+=y1[i_]*y1[i_];
v3=v3/2;
network.m_weights.Set(i,wprev+2*h);
//--- function call
CMLPBase::MLPProcess(network,x,y1);
for(i_=0; i_<nout; i_++)
y1[i_]=y1[i_]-y[i_];
//--- change value
v4=0.0;
for(i_=0; i_<nout; i_++)
v4+=y1[i_]*y1[i_];
v4=v4/2;
network.m_weights.Set(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 ; i++)
x[i]=4*CMath::RandomReal()-2;
for(i_=0; i_<nin ; i_++)
xy.Set(0,i_,x[i_]);
//--- check
if(CMLPBase::MLPIsSoftMax(network))
{
for(i=0; i<nout; i++)
y[i]=0;
xy.Set(0,nin,CMath::RandomInteger(nout));
y[(int)MathRound(xy[0][nin])]=1;
}
else
{
for(i=0; i<nout; i++)
y[i]=4*CMath::RandomReal()-2;
i1_=-nin;
for(i_=nin; i_<nin+nout ; i_++)
xy.Set(0,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; i++)
v+=0.5*CMath::Sqr(y2[i]-y[i]);
}
else
{
for(i=0; i<nout; i++)
{
//--- check
if(y[i]!=0.0)
{
//--- check
if(y2[i]==0.0)
v+=y[i]*MathLog(CMath::m_maxrealnumber);
else
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; i++)
{
wprev=network.m_weights[i];
network.m_weights.Set(i,wprev+h);
//--- function call
CMLPBase::MLPProcess(network,x,y2);
network.m_weights.Set(i,wprev-h);
//--- function call
CMLPBase::MLPProcess(network,x,y1);
network.m_weights.Set(i,wprev);
v=0;
//--- check
if(nkind!=1)
{
for(j=0; j<nout; j++)
v+=0.5*(CMath::Sqr(y2[j]-y[j])-CMath::Sqr(y1[j]-y[j]))/(2*h);
}
else
{
for(j=0; j<nout; j++)
{
//--- check
if(y[j]!=0.0)
{
//--- check
if(y2[j]==0.0)
v+=y[j]*MathLog(CMath::m_maxrealnumber);
else
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; i++)
grad1[i]=0;
//--- calculation
e1=0;
for(i=0; i<ssize; i++)
{
for(j=0; j<nin ; j++)
x1[j]=4*CMath::RandomReal()-2;
for(i_=0; i_<nin ; i_++)
xy.Set(i,i_,x1[i_]);
//--- check
if(CMLPBase::MLPIsSoftMax(network))
{
for(j=0; j<nout; j++)
y1[j]=0;
//--- change values
xy.Set(i,nin,CMath::RandomInteger(nout));
y1[(int)MathRound(xy[i][nin])]=1;
}
else
{
//--- change values
for(j=0; j<nout; j++)
y1[j]=4*CMath::RandomReal()-2;
i1_=-nin;
for(i_=nin; i_<nin+nout ; i_++)
xy.Set(i,i_,y1[i_+i1_]);
}
//--- function call
CMLPBase::MLPGrad(network,x1,y1,v,grad2);
//--- change values
e1+=v;
for(i_=0; i_<wcount; 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; 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; i++)
grad1[i]=0;
//--- calculation
e1=0;
for(i=0; i<ssize; i++)
{
for(j=0; j<nin ; j++)
x1[j]=4*CMath::RandomReal()-2;
for(i_=0; i_<nin ; i_++)
xy.Set(i,i_,x1[i_]);
//--- check
if(CMLPBase::MLPIsSoftMax(network))
{
for(j=0; j<nout; j++)
y1[j]=0;
//--- change values
xy.Set(i,nin,CMath::RandomInteger(nout));
y1[(int)MathRound(xy[i][nin])]=1;
}
else
{
//--- change values
for(j=0; j<nout; j++)
y1[j]=4*CMath::RandomReal()-2;
i1_=-nin;
for(i_=nin; i_<nin+nout ; i_++)
xy.Set(i,i_,y1[i_+i1_]);
}
//--- function call
CMLPBase::MLPGradN(network,x1,y1,v,grad2);
//--- change values
e1+=v;
for(i_=0; i_<wcount; 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; 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 |
//+------------------------------------------------------------------+
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;
//--- 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.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; i++)
grad1[i]=0;
//--- change values
for(i=0; i<wcount; i++)
{
for(j=0; j<wcount; j++)
h1.Set(i,j,0);
}
//--- calculation
e1=0;
for(i=0; i<ssize; i++)
{
//--- X,Y
for(j=0; j<nin ; j++)
x1[j]=4*CMath::RandomReal()-2;
for(i_=0; i_<nin ; i_++)
xy.Set(i,i_,x1[i_]);
//--- check
if(CMLPBase::MLPIsSoftMax(network))
{
for(j=0; j<nout; j++)
y1[j]=0;
//--- change values
xy.Set(i,nin,CMath::RandomInteger(nout));
y1[(int)MathRound(xy[i][nin])]=1;
}
else
{
//--- change values
for(j=0; j<nout; j++)
y1[j]=4*CMath::RandomReal()-2;
i1_=-nin;
for(i_=nin; i_<nin+nout ; i_++)
xy.Set(i,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+=v;
for(i_=0; i_<wcount; i_++)
grad1[i_]+=grad2[i_];
//--- H1
for(j=0; j<wcount; j++)
{
wprev=network.m_weights[j];
network.m_weights.Set(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.Set(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; i_++)
grad2[i_]-=8*grad3[i_];
network.m_weights.Set(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; i_++)
grad2[i_]+=8*grad3[i_];
network.m_weights.Set(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; i_++)
grad2[i_]-=grad3[i_];
v=1/(12*h);
for(i_=0; i_<wcount; i_++)
h1.Set(j,i_,h1.Get(j,i_)+v*grad2[i_]);
network.m_weights.Set(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; 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; i++)
{
for(j=0; j<wcount; j++)
{
//--- check
if(MathAbs(h1.Get(i,j))>5.0E-2)
err=err||MathAbs((h1.Get(i,j)-h2.Get(i,j))/h1.Get(i,j))>etol;
else
err=err||MathAbs(h2.Get(i,j)-h1.Get(i,j))>etol;
}
}
}
}
}
//+------------------------------------------------------------------+
//| Testing class CMLPE |
//+------------------------------------------------------------------+
class CTestMLPEUnit
{
public:
static bool TestMLPE(const bool silent);
private:
static bool TestMLPTrainES(void);
static bool TestMLPTrainRegr(void);
static bool TestMLPXORRegr(void);
static bool TestMLPTrainClass(void);
static bool TestMLPXORCls(void);
static bool TestMLPZeroWeights(void);
static bool TestMLPRestarts(void);
static bool TestMLPTrainEns(void);
static bool TestMLPTrainEnsRegr(void);
static bool TestMLPTrainEnsCls(void);
};
//+------------------------------------------------------------------+
//| Testing class CMLPE |
//+------------------------------------------------------------------+
bool CTestMLPEUnit::TestMLPE(const bool silent)
{
//--- create variables
bool waserrors=false;
int info=0;
CMultilayerPerceptronShell network;
CMultilayerPerceptronShell network2;
CMLPReportShell rep;
CMLPCVReportShell cvrep;
CMatrixDouble xy;
CMatrixDouble valxy;
bool trnerrors=false;
bool mlptrainregrerr=false;
bool mlptrainclasserr=false;
bool mlprestartserr=false;
bool mlpxorregrerr=false;
bool mlpxorclserr=false;
bool mlptrainenserrors=false;
//--- Test network training on simple XOR problem
xy.Resize(4,3);
xy.Set(0,0,-1);
xy.Set(0,1,-1);
xy.Set(0,2,-1);
xy.Set(1,0,1);
xy.Set(1,1,-1);
xy.Set(1,2,1);
xy.Set(2,0,-1);
xy.Set(2,1,1);
xy.Set(2,2,1);
xy.Set(3,0,1);
xy.Set(3,1,1);
xy.Set(3,2,-1);
CMLPBase::MLPCreate1(2,2,1,network.GetInnerObj());
CMLPTrain::MLPTrainLM(network.GetInnerObj(),xy,4,0.0001,20,info,rep.GetInnerObj());
trnerrors=trnerrors||CMLPBase::MLPRMSError(network.GetInnerObj(),xy,4)>0.1;
//--- Test early stopping
trnerrors=trnerrors||TestMLPTrainES();
//--- Test for training functions
mlptrainregrerr=TestMLPTrainRegr()||TestMLPZeroWeights();
mlptrainclasserr=TestMLPTrainClass();
mlprestartserr=TestMLPRestarts();
mlpxorregrerr=TestMLPXORRegr();
mlpxorclserr=TestMLPXORCls();
//--- Training for ensembles
mlptrainenserrors=(TestMLPTrainEns()||TestMLPTrainEnsRegr()||TestMLPTrainEnsCls());
//--- Final report
waserrors=(trnerrors||mlptrainregrerr||mlptrainclasserr||mlprestartserr||mlpxorregrerr||mlpxorclserr||mlptrainenserrors);
if(!silent)
{
Print("MLPE TEST");
PrintResult("TRAINING",!trnerrors);
PrintResult("TRAIN -LM -LBFGS FOR REGRESSION",!mlptrainregrerr);
PrintResult("TRAIN -LM -LBFGS FOR CLASSIFIER",!mlptrainclasserr);
PrintResult("PARAMETER RESTARTS IN TRAIN -LBFGS",!mlprestartserr);
PrintResult("TRAINIG WITH TRAINER FOR REGRESSION",!mlpxorregrerr);
PrintResult("TRAINIG WITH TRAINER FOR CLASSIFIER",!mlpxorclserr);
PrintResult("TRAINING ENSEMBLES",!mlptrainenserrors);
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| This function tests MLPTrainES(). |
//| It returns True in case of errors, False when no errors were |
//| detected |
//+------------------------------------------------------------------+
bool CTestMLPEUnit::TestMLPTrainES(void)
{
//--- create variables
int pass=0;
int passcount=0;
CMultilayerPerceptron network;
CMatrixDouble trnxy;
CMatrixDouble valxy;
CRowDouble x;
CRowDouble y;
int n=0;
int i=0;
int j=0;
int nrestarts=0;
int info=0;
CMLPReportShell rep;
//--- First test checks that MLPTrainES() - when training set is equal to the validation
//--- set, MLPTrainES() behaves just like a "normal" training algorithm.
//--- Test sequence:
//--- * generate training set - 100 random points from 2D square [-1,+1]*[-1,+1]
//--- * generate network with 2 inputs, no hidden layers, nonlinear output layer,
//--- use its outputs as target values for the test set
//--- * randomize network
//--- * train with MLPTrainES(), using original set as both training and validation set
//--- * trained network must reproduce training set with good precision
//--- NOTE: it is important to test algorithm on nonlinear network because linear
//--- problems converge too fast. Slow convergence is important to detect
//--- some kinds of bugs.
//--- NOTE: it is important to have NRestarts at least equal to 5, because with just
//--- one restart algorithm fails test about once in several thousands of passes.
passcount=10;
nrestarts=5;
for(pass=1; pass<=passcount; pass++)
{
//--- Create network, generate training/validation sets
CMLPBase::MLPCreateR0(2,1,-2.0,2.0,network);
CMLPBase::MLPRandomize(network);
n=100;
trnxy.Resize(n,3);
valxy.Resize(n,3);
x.Resize(2);
y.Resize(1);
for(i=0; i<n; i++)
{
for(j=0; j<=1; j++)
{
trnxy.Set(i,j,2*CMath::RandomReal()-1);
valxy.Set(i,j,trnxy.Get(i,j));
x.Set(j,trnxy.Get(i,j));
}
CMLPBase::MLPProcess(network,x,y);
trnxy.Set(i,2,y[0]);
valxy.Set(i,2,y[0]);
}
CMLPBase::MLPRandomize(network);
CMLPTrain::MLPTrainES(network,trnxy,n,valxy,n,0.0001,nrestarts,info,rep.GetInnerObj());
if(info<=0)
return(true);
if(MathSqrt(CMLPBase::MLPError(network,valxy,n)/n)>0.01)
return(true);
}
return(false);
}
//+------------------------------------------------------------------+
//| This function tests MLPTrainLM, MLPTrainLBFGS and |
//| MLPTrainNetwork functions for regression. It check that train |
//| functions work correctly. |
//| Test use Create1 with 10 neurons. |
//| Test function is f(x,y)=X^2+cos(3*Pi*y). |
//+------------------------------------------------------------------+
bool CTestMLPEUnit::TestMLPTrainRegr(void)
{
//--- create variables
CMultilayerPerceptronShell net;
CMLPTrainer trainer;
CMLPReport rep;
int info=0;
CMatrixDouble xy;
CSparseMatrix sm;
CRowDouble x;
CRowDouble y;
int n=0;
int sn=0;
int nneurons=0;
double vdecay=0;
double averr=0;
double st=0;
double eps=0;
double traineps=0;
int nneedrest=0;
int trainits=0;
int shift=0;
int i=0;
int j=0;
int vtrain=0;
//--- initialization
eps=0.02;
vdecay=0.001;
nneurons=10;
nneedrest=5;
traineps=1.0E-3;
trainits=0;
sn=5;
n=sn*sn;
st=(double)2/(double)(sn-1);
CSparse::SparseCreate(n,3,n*3,sm);
xy.Resize(n,3);
x.Resize(2);
for(vtrain=0; vtrain<=3; vtrain++)
{
averr=0;
//--- Create a train set(uniformly distributed set of points).
for(i=0; i<sn; i++)
{
for(j=0; j<sn; j++)
{
shift=i*sn+j;
xy.Set(shift,0,i*st-1.0);
xy.Set(shift,1,j*st-1.0);
xy.Set(shift,2,CMath::Sqr(xy.Get(shift,0))+MathCos(3*M_PI*xy.Get(shift,1)));
}
}
//--- Create and train a neural network
CMLPBase::MLPCreate1(2,nneurons,1,net.GetInnerObj());
switch(vtrain)
{
case 0:
CMLPTrain::MLPTrainLM(net.GetInnerObj(),xy,n,vdecay,nneedrest,info,rep);
break;
case 1:
CMLPTrain::MLPTrainLBFGS(net.GetInnerObj(),xy,n,vdecay,nneedrest,traineps,trainits,info,rep);
break;
//--- Train with trainer, using:
//--- * dense matrix;
case 2:
CMLPTrain::MLPCreateTrainer(2,1,trainer);
CMLPTrain::MLPSetDataset(trainer,xy,n);
CMLPTrain::MLPSetDecay(trainer,vdecay);
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
CMLPTrain::MLPTrainNetwork(trainer,net.GetInnerObj(),nneedrest,rep);
break;
//--- * sparse matrix.
case 3:
for(i=0; i<n; i++)
for(j=0; j<=2; j++)
CSparse::SparseSet(sm,i,j,xy.Get(i,j));
CMLPTrain::MLPCreateTrainer(2,1,trainer);
CMLPTrain::MLPSetSparseDataset(trainer,sm,n);
CMLPTrain::MLPSetDecay(trainer,vdecay);
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
CMLPTrain::MLPTrainNetwork(trainer,net.GetInnerObj(),nneedrest,rep);
break;
}
//--- Check that network is trained correctly
for(i=0; i<n; i++)
{
x=xy[i]+0;
CMLPBase::MLPProcess(net.GetInnerObj(),x,y);
//--- Calculate average error
averr=averr+MathAbs(y[0]-xy.Get(i,2));
}
if((averr/n)>eps)
return(true);
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| This function tests MLPTrainNetwork / MLPStartTraining / |
//| MLPContinueTraining functions for regression. It check that train|
//| functions work correctly. |
//| Test use Create1 with 2 neurons. |
//| Test function is XOR(x,y). |
//+------------------------------------------------------------------+
bool CTestMLPEUnit::TestMLPXORRegr(void)
{
//--- create variables
CMultilayerPerceptronShell net;
CMLPTrainer trainer;
CMLPReportShell rep;
CMatrixDouble xy;
CSparseMatrix sm;
CRowDouble x;
CRowDouble y;
int n=0;
int sn=0;
int nneurons=0;
double vdecay=0;
double averr=0;
double eps=0;
int numxp=0;
double traineps=0;
int nneedrest=0;
int trainits=0;
int shift=0;
int i=0;
int j=0;
int vtrain=0;
int xp=0;
int nfailures=0;
eps=0.01;
numxp=15;
vdecay=0.0001;
nneurons=4;
nneedrest=1;
traineps=1.0E-4;
trainits=0;
sn=2;
n=sn*sn;
CSparse::SparseCreate(n,3,n*3,sm);
xy.Resize(n,3);
x.Resize(2);
nfailures=0;
for(xp=1; xp<=numxp; xp++)
{
for(vtrain=0; vtrain<=3; vtrain++)
{
//--- Create a train set(uniformly distributed set of points).
for(i=0; i<sn; i++)
{
for(j=0; j<sn; j++)
{
shift=i*sn+j;
xy.Set(shift,0,i);
xy.Set(shift,1,j);
if(xy.Get(shift,0)==xy.Get(shift,1))
xy.Set(shift,2,0);
else
xy.Set(shift,2,1);
}
}
//--- Create and train a neural network
CMLPBase::MLPCreate1(2,nneurons,1,net.GetInnerObj());
//--- Train with trainer, using:
//--- * dense matrix;
switch(vtrain)
{
case 0:
CMLPTrain::MLPCreateTrainer(2,1,trainer);
CMLPTrain::MLPSetDataset(trainer,xy,n);
CMLPTrain::MLPSetDecay(trainer,vdecay);
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
CMLPTrain::MLPTrainNetwork(trainer,net.GetInnerObj(),nneedrest,rep.GetInnerObj());
break;
case 1:
CMLPTrain::MLPCreateTrainer(2,1,trainer);
CMLPTrain::MLPSetDataset(trainer,xy,n);
CMLPTrain::MLPSetDecay(trainer,vdecay);
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
CMLPTrain::MLPStartTraining(trainer,net.GetInnerObj(),true);
while(CMLPTrain::MLPContinueTraining(trainer,net.GetInnerObj()))
{
}
break;
//--- * sparse matrix.
case 2:
for(i=0; i<n; i++)
for(j=0; j<=2; j++)
CSparse::SparseSet(sm,i,j,xy.Get(i,j));
CMLPTrain::MLPCreateTrainer(2,1,trainer);
CMLPTrain::MLPSetSparseDataset(trainer,sm,n);
CMLPTrain::MLPSetDecay(trainer,vdecay);
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
CMLPTrain::MLPTrainNetwork(trainer,net.GetInnerObj(),nneedrest,rep.GetInnerObj());
break;
case 3:
for(i=0; i<n; i++)
for(j=0; j<=2; j++)
CSparse::SparseSet(sm,i,j,xy.Get(i,j));
CMLPTrain::MLPCreateTrainer(2,1,trainer);
CMLPTrain::MLPSetSparseDataset(trainer,sm,n);
CMLPTrain::MLPSetDecay(trainer,vdecay);
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
CMLPTrain::MLPStartTraining(trainer,net.GetInnerObj(),true);
while(CMLPTrain::MLPContinueTraining(trainer,net.GetInnerObj()))
{
}
break;
}
//--- Check that network is trained correctly
averr=0;
for(i=0; i<n; i++)
{
x=xy[i]+0;
CMLPBase::MLPProcess(net.GetInnerObj(),x,y);
//--- Calculate average error
averr=averr+MathAbs(y[0]-xy.Get(i,2));
}
if((averr/n)>eps)
nfailures++;
}
}
if(nfailures>=2)
{
Print("testmlptrainunit.ap:580");
return(true);
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| This function tests MLPTrainLM, MLPTrainLBFGS and |
//| MLPTrainNetwork functions for classification problems. It check |
//| that train functions work correctly when is used CreateC1 |
//| function. Here the network tries to distinguish positive from|
//| negative numbers. |
//+------------------------------------------------------------------+
bool CTestMLPEUnit::TestMLPTrainClass(void)
{
//--- create variables
CMultilayerPerceptron net;
CMLPTrainer trainer;
CMLPReportShell rep;
int info=0;
CMatrixDouble xy;
CSparseMatrix sm;
CRowDouble x;
CRowDouble y;
int n=0;
double vdecay=0;
double traineps=0;
int nneedrest=0;
int trainits=0;
double tmp=0;
double mnc=0;
double mxc=0;
int nxp=0;
int i=0;
int rndind=0;
int vtrain=0;
int xp=0;
//--- initialization
mnc=10;
mxc=11;
nxp=15;
vdecay=0.0001;
nneedrest=10;
traineps=1.0E-4;
trainits=0;
n=100;
CSparse::SparseCreate(n,2,n*2,sm);
x.Resize(1);
xy.Resize(n,2);
for(xp=1; xp<=nxp; xp++)
{
for(vtrain=0; vtrain<=3; vtrain++)
{
//--- Initialization:
//--- * create negative part of the set;
for(i=0; i<=n/2-1; i++)
{
xy.Set(i,0,-(1*((mxc-mnc)*CMath::RandomReal()+mnc)));
xy.Set(i,1,0);
}
//--- * create positive part of the set;
for(i=n/2; i<n; i++)
{
xy.Set(i,0,(mxc-mnc)*CMath::RandomReal()+mnc);
xy.Set(i,1,1);
}
//--- * mix two parts.
for(i=0; i<n; i++)
{
do
{
rndind=CMath::RandomInteger(n);
}
while(rndind==i);
tmp=xy.Get(i,0);
xy.Set(i,0,xy.Get(rndind,0));
xy.Set(rndind,0,tmp);
tmp=xy.Get(i,1);
xy.Set(i,1,xy.Get(rndind,1));
xy.Set(rndind,1,tmp);
}
//--- Create and train a neural network
CMLPBase::MLPCreateC0(1,2,net);
switch(vtrain)
{
case 0:
CMLPTrain::MLPTrainLM(net,xy,n,vdecay,nneedrest,info,rep.GetInnerObj());
break;
case 1:
CMLPTrain::MLPTrainLBFGS(net,xy,n,vdecay,nneedrest,traineps,trainits,info,rep.GetInnerObj());
break;
//--- Train with trainer, using:
//--- * dense matrix;
case 2:
CMLPTrain::MLPCreateTrainerCls(1,2,trainer);
CMLPTrain::MLPSetDataset(trainer,xy,n);
CMLPTrain::MLPSetDecay(trainer,vdecay);
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
CMLPTrain::MLPTrainNetwork(trainer,net,nneedrest,rep.GetInnerObj());
break;
//--- * sparse matrix.
case 3:
for(i=0; i<n; i++)
{
CSparse::SparseSet(sm,i,0,xy.Get(i,0));
CSparse::SparseSet(sm,i,1,xy.Get(i,1));
}
CMLPTrain::MLPCreateTrainerCls(1,2,trainer);
CMLPTrain::MLPSetSparseDataset(trainer,sm,n);
CMLPTrain::MLPSetDecay(trainer,vdecay);
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
CMLPTrain::MLPTrainNetwork(trainer,net,nneedrest,rep.GetInnerObj());
break;
}
//--- Test on training set
for(i=0; i<n; i++)
{
x.Set(0,xy.Get(i,0));
CMLPBase::MLPProcess(net,x,y);
//--- Negative number has to be negative and
//--- positive number has to be positive.
if((x[0]<0.0 && y[0]<0.95 && y[1]>0.05) || (x[0]>=0.0 && y[0]>0.05 && y[1]<0.95))
{
Print("testmlptrainunit.ap:730");
return(true);
}
}
//--- Test on random set
for(i=0; i<n; i++)
{
x.Set(0,MathPow(-1,CMath::RandomInteger(2))*((mxc-mnc)*CMath::RandomReal()+mnc));
CMLPBase::MLPProcess(net,x,y);
if((x[0]<0.0 && y[0]<0.95 && y[1]>0.05) || (x[0]>=0.0 && y[0]>0.05 && y[1]<0.95))
{
Print("testmlptrainunit.ap:745");
return(true);
}
}
}
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| This function tests MLPTrainNetwork / MLPStartTraining / |
//| MLPContinueTraining functions for classification problems. It |
//| check that train functions work correctly when is used CreateC1 |
//| function. Here the network tries to distinguish positive from |
//| negative numbers. |
//+------------------------------------------------------------------+
bool CTestMLPEUnit::TestMLPXORCls(void)
{
//--- create variables
CMultilayerPerceptron net;
CMLPTrainer trainer;
CMLPReportShell rep;
CMatrixDouble xy;
CSparseMatrix sm;
CRowDouble x;
CRowDouble y;
int n=0;
int nin=0;
int nout=0;
int wcount=0;
double e=0;
double ebest=0;
double v=0;
CRowDouble wbest;
double vdecay=0;
double traineps=0;
int nneurons=0;
int nneedrest=0;
int trainits=0;
int nxp=0;
int i=0;
int vtrain=0;
int xp=0;
int i_=0;
//--- initialization
nxp=15;
nneurons=4;
vdecay=0.001;
nneedrest=3;
traineps=1.0E-4;
trainits=0;
n=4;
CSparse::SparseCreate(n,3,n*3,sm);
x.Resize(2);
xy.Resize(n,3);
//--- Initialization:
xy.Set(0,0,0);
xy.Set(0,1,0);
xy.Set(0,2,0);
xy.Set(1,0,0);
xy.Set(1,1,1);
xy.Set(1,2,1);
xy.Set(2,0,1);
xy.Set(2,1,0);
xy.Set(2,2,1);
xy.Set(3,0,1);
xy.Set(3,1,1);
xy.Set(3,2,0);
//--- Create a neural network
CMLPBase::MLPCreateC1(2,nneurons,2,net);
CMLPBase::MLPProperties(net,nin,nout,wcount);
wbest.Resize(wcount);
//--- Test
for(xp=1; xp<=nxp; xp++)
{
for(vtrain=0; vtrain<=3; vtrain++)
{
//--- Train with trainer, using:
//--- * dense matrix;
switch(vtrain)
{
case 0:
CMLPTrain::MLPCreateTrainerCls(2,2,trainer);
CMLPTrain::MLPSetDataset(trainer,xy,n);
CMLPTrain::MLPSetDecay(trainer,vdecay);
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
CMLPTrain::MLPTrainNetwork(trainer,net,nneedrest,rep.GetInnerObj());
break;
case 1:
CMLPTrain::MLPCreateTrainerCls(2,2,trainer);
CMLPTrain::MLPSetDataset(trainer,xy,n);
CMLPTrain::MLPSetDecay(trainer,vdecay);
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
ebest=CMath::m_maxrealnumber;
for(i=1; i<=nneedrest; i++)
{
CMLPTrain::MLPStartTraining(trainer,net,true);
while(CMLPTrain::MLPContinueTraining(trainer,net))
{
}
v=CAblasF::RDotV2(wcount,net.m_weights);
e=CMLPBase::MLPError(net,xy,n)+0.5*vdecay*v;
//--- Compare with the best answer.
if(e<ebest)
{
wbest=net.m_weights;
ebest=e;
}
}
//--- The best result
net.m_weights=wbest;
break;
//--- * sparse matrix.
case 2:
for(i=0; i<n; i++)
{
CSparse::SparseSet(sm,i,0,xy.Get(i,0));
CSparse::SparseSet(sm,i,1,xy.Get(i,1));
CSparse::SparseSet(sm,i,2,xy.Get(i,2));
}
CMLPTrain::MLPCreateTrainerCls(2,2,trainer);
CMLPTrain::MLPSetSparseDataset(trainer,sm,n);
CMLPTrain::MLPSetDecay(trainer,vdecay);
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
CMLPTrain::MLPTrainNetwork(trainer,net,nneedrest,rep.GetInnerObj());
break;
case 3:
for(i=0; i<n; i++)
{
CSparse::SparseSet(sm,i,0,xy.Get(i,0));
CSparse::SparseSet(sm,i,1,xy.Get(i,1));
CSparse::SparseSet(sm,i,2,xy.Get(i,2));
}
CMLPTrain::MLPCreateTrainerCls(2,2,trainer);
CMLPTrain::MLPSetSparseDataset(trainer,sm,n);
CMLPTrain::MLPSetDecay(trainer,vdecay);
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
ebest=CMath::m_maxrealnumber;
for(i=1; i<=nneedrest; i++)
{
CMLPTrain::MLPStartTraining(trainer,net,true);
while(CMLPTrain::MLPContinueTraining(trainer,net))
{
}
v=CAblasF::RDotV2(wcount,net.m_weights);
e=CMLPBase::MLPError(net,xy,n)+0.5*vdecay*v;
//--- Compare with the best answer.
if(e<ebest)
{
wbest=net.m_weights;
ebest=e;
}
}
//--- The best result
net.m_weights=wbest;
break;
}
//--- Test on training set
for(i=0; i<n; i++)
{
x=xy[i]+0;
CMLPBase::MLPProcess(net,x,y);
if((x[0]==x[1] && y[0]<0.95 && y[1]>0.05) || (x[0]!=x[1] && y[0]>0.05 && y[1]<0.95))
return(true);
}
}
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| The test check, that all weights are zero after training with|
//| trainer using empty dataset(either zero size or is't used |
//| MLPSetDataSet function). Test on regression and classification |
//| problems given by dense or sparse matrix. |
//| NOTE: Result of the function is written in MLPTrainRegrErr |
//| variable in unit test. |
//+------------------------------------------------------------------+
bool CTestMLPEUnit::TestMLPZeroWeights(void)
{
//--- create variables
CMLPTrainer trainer;
CMultilayerPerceptron net;
CMLPReportShell rep;
int nin=0;
int nout=0;
int wcount=0;
int mxnin=0;
int mxnout=0;
double vdecay=0;
double traineps=0;
int trainits=0;
int nneedrest=0;
CMatrixDouble dds;
CSparseMatrix sds;
bool iscls;
bool issparse;
int c=0;
int n=0;
int xp=0;
int nxp=0;
//--- initialization
mxnin=10;
mxnout=10;
vdecay=1.0E-3;
nneedrest=1;
traineps=1.0E-3;
trainits=0;
CSparse::SparseCreate(1,1,0,sds);
CSparse::SparseConvertToCRS(sds);
nxp=10;
for(xp=1; xp<=nxp; xp++)
{
c=CMath::RandomInteger(2);
iscls=c==1;
c=CMath::RandomInteger(2);
issparse=c==1;
//--- Create trainer and network
if(!iscls)
{
//--- Regression
nin=CMath::RandomInteger(mxnin)+1;
nout=CMath::RandomInteger(mxnout)+1;
CMLPTrain::MLPCreateTrainer(nin,nout,trainer);
CMLPBase::MLPCreate0(nin,nout,net);
}
else
{
//--- Classification
nin=CMath::RandomInteger(mxnin)+1;
nout=CMath::RandomInteger(mxnout)+2;
CMLPTrain::MLPCreateTrainerCls(nin,nout,trainer);
CMLPBase::MLPCreateC0(nin,nout,net);
}
n=CMath::RandomInteger(2)-1;
if(n==0)
{
if(!issparse)
CMLPTrain::MLPSetDataset(trainer,dds,n);
else
CMLPTrain::MLPSetSparseDataset(trainer,sds,n);
}
CMLPTrain::MLPSetDecay(trainer,vdecay);
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
c=CMath::RandomInteger(2);
if(c==0)
{
CMLPTrain::MLPStartTraining(trainer,net,true);
while(CMLPTrain::MLPContinueTraining(trainer,net))
{
}
}
if(c==1)
{
CMLPTrain::MLPTrainNetwork(trainer,net,nneedrest,rep.GetInnerObj());
}
//--- Check weights
CMLPBase::MLPProperties(net,nin,nout,wcount);
for(c=0; c<wcount; c++)
{
if(net.m_weights[c]!=0.0)
return(true);
}
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| This function tests that increasing numbers of restarts lead to |
//| statistical improvement quality of solution. |
//| Neural network created by Create1(10 neurons) and trained by |
//| MLPTrainLBFGS. |
//| TEST's DISCRIPTION: |
//| Net0 - network trained with one restart (denoted as R1) |
//| Net1 - network trained with more than one restart (denoted |
//| as Rn) |
//| We must refuse hypothesis that R1 equivalent to Rn. |
//| Here Mean = N/2, Sigma = Sqrt(N)/2. |
//| _ |
//| | 0 - R1 worse than Rn; |
//| ri = | |
//| |_1 - Rn same or worse then R1. |
//| If Sum(ri)<Mean-5*Sigma then hypothesis is refused and test is |
//| passed. In another case if Mean-5*Sigma<=Sum(ri)<=Mean+5*Sigma |
//| then hypothesis is't refused and test is broken; and if |
//| Mean + 5 * Sigma < Sum(ri) then test broken too hard! |
//+------------------------------------------------------------------+
bool CTestMLPEUnit::TestMLPRestarts(void)
{
//--- create variables
CMultilayerPerceptron net0;
CMultilayerPerceptron net1;
CMLPTrainer trainer;
CMLPReportShell rep;
int info=0;
CSparseMatrix sm;
CMatrixDouble xy;
CRowDouble x;
CRowDouble y;
int n=0;
int nneurons=0;
double vdecay=0;
int wcount0=0;
int wcount1=0;
int nin=0;
int nout=0;
double avval=0;
double e0=0;
double e1=0;
double mean=0;
double numsigma=0;
int numxp=0;
double traineps=0;
int nneedrest=0;
int trainits=0;
int i=0;
int vtrain=0;
int xp=0;
int i_=0;
vdecay=0.001;
nneurons=4;
nneedrest=3;
traineps=0.00;
trainits=2;
n=20;
numxp=400;
xy.Resize(n,2);
x.Resize(1);
CSparse::SparseCreate(n,2,n*2,sm);
mean=numxp/2.0;
numsigma=5.0*MathSqrt(numxp)/2.0;
for(vtrain=0; vtrain<=2; vtrain++)
{
avval=0;
for(xp=1; xp<=numxp; xp++)
{
//--- Create a train set
for(i=0; i<n; i++)
{
xy.Set(i,0,2*CMath::RandomReal()-1);
xy.Set(i,1,2*CMath::RandomReal()-1);
}
//--- Create and train a neural network
CMLPBase::MLPCreate1(1,nneurons,1,net0);
CMLPBase::MLPCreate1(1,nneurons,1,net1);
switch(vtrain)
{
case 0:
CMLPTrain::MLPTrainLBFGS(net0,xy,n,vdecay,1,traineps,trainits,info,rep.GetInnerObj());
CMLPTrain::MLPTrainLBFGS(net1,xy,n,vdecay,nneedrest,traineps,trainits,info,rep.GetInnerObj());
break;
case 1:
CMLPTrain::MLPCreateTrainer(1,1,trainer);
CMLPTrain::MLPSetDataset(trainer,xy,n);
CMLPTrain::MLPSetDecay(trainer,vdecay);
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
CMLPTrain::MLPTrainNetwork(trainer,net0,1,rep.GetInnerObj());
CMLPTrain::MLPTrainNetwork(trainer,net1,nneedrest,rep.GetInnerObj());
break;
case 2:
for(i=0; i<n; i++)
{
CSparse::SparseSet(sm,i,0,xy.Get(i,0));
CSparse::SparseSet(sm,i,1,xy.Get(i,1));
}
CMLPTrain::MLPCreateTrainer(1,1,trainer);
CMLPTrain::MLPSetSparseDataset(trainer,sm,n);
CMLPTrain::MLPSetDecay(trainer,vdecay);
CMLPTrain::MLPSetCond(trainer,traineps,trainits);
CMLPTrain::MLPTrainNetwork(trainer,net0,1,rep.GetInnerObj());
CMLPTrain::MLPTrainNetwork(trainer,net1,nneedrest,rep.GetInnerObj());
break;
}
//--- Calculate errors for...
//--- ...for Net0, trained with 1 restart.
CMLPBase::MLPProperties(net0,nin,nout,wcount0);
e0=CAblasF::RDotV2(wcount0,net0.m_weights);
e0=CMLPBase::MLPErrorN(net0,xy,n)+0.5*vdecay*e0;
//--- ...for Net1, trained with NNeedRest>1 restarts.
CMLPBase::MLPProperties(net1,nin,nout,wcount1);
e1=CAblasF::RDotV2(wcount1,net1.m_weights);
e1=CMLPBase::MLPErrorN(net1,xy,n)+0.5*vdecay*e1;
if(e0<=e1)
avval++;
}
if((double)(mean-numsigma)<(double)(avval))
return(true);
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| The function tests functions for training ensembles : |
//| MLPEBaggingLM, MLPEBaggingLBFGS. |
//+------------------------------------------------------------------+
bool CTestMLPEUnit::TestMLPTrainEns(void)
{
//--- create variables
CMLPEnsembleShell ensemble;
CMLPReportShell rep;
CMLPCVReportShell oobrep;
int info=0;
CMatrixDouble xy;
int nin=0;
int nout=0;
int npoints=0;
CMatrixDouble hf;
int nhid=0;
int algtype=0;
int tasktype=0;
int pass=0;
double e=0;
int nless=0;
int nall=0;
int nclasses=0;
int i=0;
int j=0;
int k=0;
CHighQualityRandStateShell rs;
CHighQualityRand::HQRndRandomize(rs.GetInnerObj());
//--- network training must reduce error
//--- test on random regression task
nin=3;
nout=2;
nhid=5;
npoints=100;
nless=0;
nall=0;
for(pass=1; pass<=10; pass++)
{
for(algtype=0; algtype<=1; algtype++)
{
for(tasktype=0; tasktype<=1; tasktype++)
{
if(tasktype==0)
{
hf.Resize(nout,nin+1);
for(i=0; i<nout; i++)
for(j=0; j<=nin; j++)
hf.Set(i,j,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
//---
xy.Resize(npoints,nin+nout);
for(i=0; i<npoints; i++)
{
for(j=0; j<nin; j++)
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
for(j=0; j<nout; j++)
xy.Set(i,nin+j,CHighQualityRand::HQRndNormal(rs.GetInnerObj())+CAblasF::RDotRR(nin,hf,j,xy,i));
}
CMLPE::MLPECreate1(nin,nhid,nout,1+CMath::RandomInteger(3),ensemble.GetInnerObj());
}
else
{
xy.Resize(npoints,nin+1);
nclasses=2+CMath::RandomInteger(2);
//--- check
if(!CAp::Assert(nclasses<=nin,__FUNCTION__+": bug in test"))
return(true);
for(i=0; i<npoints; i++)
{
for(j=0; j<nin; j++)
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
k=0;
for(j=0; j<nclasses; j++)
{
if(xy.Get(i,j)>xy.Get(i,k))
k=j;
}
if(CHighQualityRand::HQRndNormal(rs.GetInnerObj())>0.0)
xy.Set(i,nin,k);
else
xy.Set(i,nin,CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),nclasses));
}
CMLPE::MLPECreateC1(nin,nhid,nclasses,1+CMath::RandomInteger(3),ensemble.GetInnerObj());
}
e=CMLPE::MLPERMSError(ensemble.GetInnerObj(),xy,npoints);
if(algtype==0)
CMLPTrain::MLPEBaggingLM(ensemble.GetInnerObj(),xy,npoints,0.001,1,info,rep.GetInnerObj(),oobrep.GetInnerObj());
else
CMLPTrain::MLPEBaggingLBFGS(ensemble.GetInnerObj(),xy,npoints,0.001,1,0.01,0,info,rep.GetInnerObj(),oobrep.GetInnerObj());
if(info<0)
return(true);
if(CMLPE::MLPERMSError(ensemble.GetInnerObj(),xy,npoints)<e)
nless++;
nall++;
}
}
}
if((double)(nall-nless)>(double)(0.3*nall))
{
Print("testmlptrainunit.ap:1636");
return(true);
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| Testing for functions MLPETrainES and MLPTrainEnsembleES on |
//| regression problems. Returns TRUE for errors, FALSE for success. |
//+------------------------------------------------------------------+
bool CTestMLPEUnit::TestMLPTrainEnsRegr(void)
{
//--- return result
CMLPTrainer trainer;
CMLPEnsembleShell netens;
CMLPReportShell rep;
CModelErrors repx;
int info=0;
CSparseMatrix xytrainsp;
CMatrixDouble xytrain;
CMatrixDouble xytest;
int nin=0;
int nout=0;
int nneurons=0;
CRowDouble x;
CRowDouble y;
double decay=0;
double wstep=0;
int maxits=0;
int nneedrest=0;
int enssize=0;
double mnval=0;
double mxval=0;
int ntrain=0;
int ntest=0;
double avgerr=0;
int issparse=0;
int withtrainer=0;
double eps=0;
int xp=0;
int i=0;
int j=0;
int i_=0;
//--- This test checks ability to train ensemble on simple regression
//--- problem "f(x0,x1,x2,...) = x0 + x1 + x2 + ...".
eps=5.0E-2;
mnval=-1;
mxval=1;
ntrain=40;
ntest=20;
decay=1.0E-3;
wstep=1.0E-3;
maxits=20;
nneedrest=1;
nneurons=20;
nout=1;
enssize=100;
for(xp=1; xp<=2; xp++)
{
nin=CMath::RandomInteger(3)+1;
CApServ::RVectorSetLengthAtLeast(x,nin);
CMLPTrain::MLPCreateTrainer(nin,nout,trainer);
CMLPTrain::MLPSetDecay(trainer,decay);
CMLPTrain::MLPSetCond(trainer,wstep,maxits);
CApServ::RMatrixSetLengthAtLeast(xytrain,ntrain,nin+nout);
CApServ::RMatrixSetLengthAtLeast(xytest,ntest,nin+nout);
withtrainer=CMath::RandomInteger(2);
issparse=0;
if(withtrainer==0)
issparse=0;
if(withtrainer==1)
issparse=CMath::RandomInteger(2);
//--- Training set
for(i=0; i<ntrain; i++)
{
for(j=0; j<nin; j++)
xytrain.Set(i,j,(mxval-mnval)*CMath::RandomReal()+mnval);
xytrain.Set(i,nin,0);
for(j=0; j<nin; j++)
xytrain.Add(i,nin,xytrain.Get(i,j));
}
if(withtrainer==1)
{
//--- Dense matrix
if(issparse==0)
CMLPTrain::MLPSetDataset(trainer,xytrain,ntrain);
//--- Sparse matrix
if(issparse==1)
{
CSparse::SparseCreate(ntrain,nin+nout,ntrain*(nin+nout),xytrainsp);
//--- Just copy dense matrix to sparse matrix(using SparseGet() is too expensive).
for(i=0; i<ntrain; i++)
{
for(j=0; j<nin+nout; j++)
CSparse::SparseSet(xytrainsp,i,j,xytrain.Get(i,j));
}
CSparse::SparseConvertToCRS(xytrainsp);
CMLPTrain::MLPSetSparseDataset(trainer,xytrainsp,ntrain);
}
}
//--- Test set
for(i=0; i<ntest; i++)
{
for(j=0; j<nin; j++)
xytest.Set(i,j,(mxval-mnval)*CMath::RandomReal()+mnval);
xytest.Set(i,nin,0);
for(j=0; j<nin; j++)
xytest.Add(i,nin,xytest.Get(i,j));
}
//--- Create ensemble
CMLPE::MLPECreate1(nin,nneurons,nout,enssize,netens.GetInnerObj());
//--- Train ensembles:
//--- * without trainer;
if(withtrainer==0)
CMLPTrain::MLPETrainES(netens.GetInnerObj(),xytrain,ntrain,decay,nneedrest,info,rep.GetInnerObj());
//--- * with trainer.
if(withtrainer==1)
CMLPTrain::MLPTrainEnsembleES(trainer,netens.GetInnerObj(),nneedrest,rep.GetInnerObj());
//--- Test that Rep contains correct error values
CMLPE::MLPEAllErrorsX(netens.GetInnerObj(),xytrain,xytrainsp,ntrain,0,netens.GetInnerObj().m_network.m_dummyidx,0,ntrain,0,repx);
if(CheckErrorDiff(rep.GetInnerObj().m_RelCLSError,repx.m_RelCLSError,1.0E-4,1.0E-2) ||
CheckErrorDiff(rep.GetInnerObj().m_AvgCE,repx.m_AvgCE,1.0E-4,1.0E-2) ||
CheckErrorDiff(rep.GetInnerObj().m_RMSError,repx.m_RMSError,1.0E-4,1.0E-2) ||
CheckErrorDiff(rep.GetInnerObj().m_AvgError,repx.m_AvgError,1.0E-4,1.0E-2) ||
CheckErrorDiff(rep.GetInnerObj().m_AvgRelError,repx.m_AvgRelError,1.0E-4,1.0E-2))
{
PrintFormat("%s: diff error",__FUNCTION__);
return(true);
}
//--- Test that network fits data well. Calculate average error:
//--- * on training dataset;
//--- * on test dataset. (here we reduce the accuracy
//--- requirements - average error is compared with 2*Eps).
avgerr=0;
for(i=0; i<ntrain; i++)
{
if(issparse==0)
{
for(i_=0; i_<nin; i_++)
x.Set(i_,xytrain.Get(i,i_));
}
if(issparse==1)
CSparse::SparseGetRow(xytrainsp,i,x);
CMLPE::MLPEProcess(netens.GetInnerObj(),x,y);
avgerr+=MathAbs(y[0]-xytrain.Get(i,nin));
}
avgerr=avgerr/ntrain;
if((double)(avgerr)>eps)
{
Print("testmlptrainunit.ap:1817");
return(true);
}
avgerr=0;
for(i=0; i<ntest; i++)
{
for(i_=0; i_<nin; i_++)
x.Set(i_,xytest.Get(i,i_));
CMLPE::MLPEProcess(netens.GetInnerObj(),x,y);
avgerr+=MathAbs(y[0]-xytest.Get(i,nin));
}
avgerr=avgerr/ntest;
if((double)(avgerr)>(double)(2*eps))
{
Print("testmlptrainunit.ap:1826");
return(true);
}
}
//--- Catch bug in implementation of MLPTrainEnsembleX:
//--- test ensemble training on empty dataset.
//--- Unfixed version should crash with violation of array
//--- bounds (at least in C#).
nin=2;
nout=2;
nneurons=3;
enssize=3;
nneedrest=2;
wstep=0.001;
maxits=2;
CMLPTrain::MLPCreateTrainer(nin,nout,trainer);
CMLPTrain::MLPSetCond(trainer,wstep,maxits);
CMLPE::MLPECreate1(nin,nneurons,nout,enssize,netens.GetInnerObj());
CMLPTrain::MLPTrainEnsembleES(trainer,netens.GetInnerObj(),nneedrest,rep.GetInnerObj());
return(false);
}
//+------------------------------------------------------------------+
//| Testing for functions MLPETrainES and MLPTrainEnsembleES on |
//| classification problems. |
//+------------------------------------------------------------------+
bool CTestMLPEUnit::TestMLPTrainEnsCls(void)
{
//--- create variables
CMLPTrainer trainer;
CMLPEnsembleShell netens;
CMLPReportShell rep;
int info=0;
CSparseMatrix xytrainsp;
CMatrixDouble xytrain;
CMatrixDouble xytest;
int nin=0;
int nout=0;
CRowDouble x;
CRowDouble y;
double decay=0;
double wstep=0;
int maxits=0;
int nneedrest=0;
int enssize=0;
int val=0;
int ntrain=0;
int ntest=0;
double avgerr=0;
double eps=0;
double delta=0;
int issparse=0;
int withtrainer=0;
int xp=0;
int nxp=0;
int i=0;
int j=0;
int i_=0;
eps=5.0E-2;
delta=0.1;
ntrain=90;
ntest=90;
nin=3;
nout=3;
CApServ::RVectorSetLengthAtLeast(x,nin);
CApServ::RMatrixSetLengthAtLeast(xytrain,ntrain,nin+1);
CApServ::RMatrixSetLengthAtLeast(xytest,ntest,nin+1);
decay=1.0E-3;
wstep=1.0E-3;
maxits=100;
nneedrest=1;
CMLPTrain::MLPCreateTrainerCls(nin,nout,trainer);
CMLPTrain::MLPSetDecay(trainer,decay);
CMLPTrain::MLPSetCond(trainer,wstep,maxits);
nxp=5;
for(xp=1; xp<=nxp; xp++)
{
enssize=(int)MathRound(MathPow(10,CMath::RandomInteger(2)+1));
withtrainer=CMath::RandomInteger(2);
issparse=0;
if(withtrainer==0)
issparse=0;
if(withtrainer==1)
issparse=CMath::RandomInteger(2);
for(i=0; i<ntrain; i++)
{
val=i%nin;
for(j=0; j<nin; j++)
xytrain.Set(i,j,delta*(CMath::RandomReal()-1));
xytrain.Add(i,val,1);
xytrain.Set(i,nin,val);
}
//--- Set dense dataset in trainer
if(issparse==0)
CMLPTrain::MLPSetDataset(trainer,xytrain,ntrain);
//--- * Sparse dataset(create it with using dense dataset).
if(issparse==1)
{
CSparse::SparseCreate(ntrain,nin+1,ntrain*(nin+1),xytrainsp);
for(i=0; i<ntrain; i++)
{
for(j=0; j<nin; j++)
CSparse::SparseSet(xytrainsp,i,j,xytrain.Get(i,j));
CSparse::SparseSet(xytrainsp,i,nin,xytrain.Get(i,nin));
}
CSparse::SparseConvertToCRS(xytrainsp);
//--- Set sparse dataset in trainer
CMLPTrain::MLPSetSparseDataset(trainer,xytrainsp,ntrain);
}
//--- Create test set
for(i=0; i<ntest; i++)
{
val=CMath::RandomInteger(nin);
for(j=0; j<nin; j++)
xytest.Set(i,j,delta*(CMath::RandomReal()-1));
xytest.Add(i,val,1);
xytest.Set(i,nin,val);
}
//--- Create ensemble
CMLPE::MLPECreateC0(nin,nout,enssize,netens.GetInnerObj());
//--- Train ensembles:
//--- * without trainer;
if(withtrainer==0)
CMLPTrain::MLPETrainES(netens.GetInnerObj(),xytrain,ntrain,decay,nneedrest,info,rep.GetInnerObj());
//--- * with trainer.
if(withtrainer==1)
CMLPTrain::MLPTrainEnsembleES(trainer,netens.GetInnerObj(),nneedrest,rep.GetInnerObj());
//--- Calculate average error:
//--- * on training dataset;
avgerr=0;
for(i=0; i<ntrain; i++)
{
if(issparse==0)
{
for(i_=0; i_<nin; i_++)
x.Set(i_,xytrain.Get(i,i_));
}
if(issparse==1)
CSparse::SparseGetRow(xytrainsp,i,x);
CMLPE::MLPEProcess(netens.GetInnerObj(),x,y);
for(j=0; j<nout; j++)
{
if((double)(j)!=xytrain.Get(i,nin))
avgerr+=y[j];
else
avgerr+=(1-y[j]);
}
}
avgerr=avgerr/(ntrain*nout);
if((double)(avgerr)>eps)
return(true);
//--- * on test dataset.
avgerr=0;
for(i=0; i<ntest; i++)
{
for(i_=0; i_<nin; i_++)
x.Set(i_,xytest.Get(i,i_));
CMLPE::MLPEProcess(netens.GetInnerObj(),x,y);
for(j=0; j<nout; j++)
{
if((double)(j)!=(double)(xytest.Get(i,nin)))
avgerr+=y[j];
else
avgerr+=(1-y[j]);
}
}
avgerr=avgerr/(ntest*nout);
if((double)(avgerr)>eps)
return(true);
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| Testing class CPCAnalysis |
//+------------------------------------------------------------------+
class CTestPCAUnit
{
public:
static bool TestPCA(const bool silent);
private:
static void CalculateMV(double &x[],const int n,double &mean,double &means,double &stddev,double &stddevs);
};
//+------------------------------------------------------------------+
//| Testing class CPCAnalysis |
//+------------------------------------------------------------------+
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; j++)
means[j]=1.5*CMath::RandomReal()-0.75;
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
x.Set(i,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; i++)
{
for(j=0; j<m; j++)
{
//--- change value
t=0.0;
for(i_=0; i_<m; i_++)
t+=v.Get(i_,i)*v.Get(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; k++)
{
for(i=0; i<n; i++)
{
//--- change value
t=0.0;
for(i_=0; i_<m; i_++)
t+=x.Get(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; i++)
{
//--- change values
t=0.0;
for(i_=0; i_<m; i_++)
t+=x.Get(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; i_++)
t3[i_]=t2[i_];
for(i_=0; i_<n; i_++)
t3[i_]=t3[i_]+h*x[i_][k/2];
//--- change value
t=0;
for(j=0; j<m; 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; 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; i++)
{
for(j=0; j<m; j++)
{
//--- change value
t=0.0;
for(i_=0; i_<m; i_++)
t+=v.Get(i_,i)*v.Get(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");
PrintResult("TOTAL RESULTS",!waserrors);
PrintResult("* CONVERGENCE",!pcaconverrors);
PrintResult("* ORTOGONALITY",!pcaorterrors);
PrintResult("* VARIANCE REPORT",!pcavarerrors);
PrintResult("* OPTIMALITY",!pcaopterrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Moments estimates and their errors |
//+------------------------------------------------------------------+
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; 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; i++)
v1+=CMath::Sqr(x[i]-mean);
v2=0;
for(i=0; i<n; 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
{
public:
static bool TestODESolver(const bool silent);
private:
static void Unset2D(CMatrixDouble &x);
static void Unset1D(double &x[]);
static void UnsetRep(CODESolverReport &rep);
};
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
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; i++)
xg[i]=xg[i-1]+CMath::RandomReal();
//--- change values
v=2*M_PI/(xg[m-1]-xg[0]);
for(i_=0; i_<m; i_++)
xg[i_]=v*xg[i_];
//--- check
if(CMath::RandomReal()>0.5)
{
for(i_=0; i_<m; 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; 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; 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; 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");
PrintResult("* RK CASH-KARP",!rkckerrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Unsets real matrix |
//+------------------------------------------------------------------+
void CTestODESolverUnit::Unset2D(CMatrixDouble &x)
{
//--- allocation
x.Resize(1,1);
//--- change value
x.Set(0,0,2*CMath::RandomReal()-1);
}
//+------------------------------------------------------------------+
//| Unsets real vector |
//+------------------------------------------------------------------+
void CTestODESolverUnit::Unset1D(double &x[])
{
//--- allocation
ArrayResize(x,1);
//--- change value
x[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Unsets report |
//+------------------------------------------------------------------+
void CTestODESolverUnit::UnsetRep(CODESolverReport &rep)
{
rep.m_nfev=0;
}
//+------------------------------------------------------------------+
//| Testing class CFastFourierTransform |
//+------------------------------------------------------------------+
class CTestFFTUnit
{
public:
static bool TestFFT(const bool silent);
private:
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[]);
};
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
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; i++)
{
a1[i].real=2*CMath::RandomReal()-1;
a1[i].imag=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; 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; i++)
{
r1[i]=2*CMath::RandomReal()-1;
r2[i]=r1[i];
}
//--- function call
CFastFourierTransform::FFTR1D(r2,n,a1);
for(i_=0; i_<n; i_++)
r2[i_]=0*r2[i_];
//--- function call
CFastFourierTransform::FFTR1DInv(a1,n,r2);
//--- search errors
for(i=0; i<n; 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; i++)
{
a1[i].real=2*CMath::RandomReal()-1;
a1[i].imag=2*CMath::RandomReal()-1;
a2[i]=a1[i];
}
//--- function calls
CFastFourierTransform::FFTC1D(a1,n);
RefFFTC1D(a2,n);
//--- search errors
for(i=0; i<n; i++)
referr=MathMax(referr,CMath::AbsComplex(a1[i]-a2[i]));
//--- Complex inverse FFT
ArrayResize(a1,n);
ArrayResize(a2,n);
for(i=0; i<n; i++)
{
a1[i].real=2*CMath::RandomReal()-1;
a1[i].imag=2*CMath::RandomReal()-1;
a2[i]=a1[i];
}
//--- function calls
CFastFourierTransform::FFTC1DInv(a1,n);
RefFFTC1DInv(a2,n);
//--- search errors
for(i=0; i<n; 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; 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; 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].imag=2*CMath::RandomReal()-1;
//--- check
if(n%2==0)
a3[(int)MathFloor((double)n/2.0)].imag=2*CMath::RandomReal()-1;
for(i=0; i<n; i++)
r1[i]=0;
//--- function call
CFastFourierTransform::FFTR1DInv(a3,n,r1);
//--- search errors
for(i=0; i<n; 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; i++)
{
r1[i]=2*CMath::RandomReal()-1;
r2[i]=r1[i];
}
//--- function call
CFtBase::FtComplexFFTPlan(n/2,1,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].real));
reinterr=MathMax(reinterr,MathAbs(r1[1]-a2[n/2].real));
for(i=1; i<=n/2-1; i++)
{
reinterr=MathMax(reinterr,MathAbs(r1[2*i+0]-a2[i].real));
reinterr=MathMax(reinterr,MathAbs(r1[2*i+1]-a2[i].imag));
}
//--- Real backward FFT
ArrayResize(r1,n);
for(i=0; i<n; 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].real=r1[2*i+0];
a2[i].imag=r1[2*i+1];
}
a2[(int)MathFloor((double)n/2.0)]=r1[1];
//--- function call
CFtBase::FtComplexFFTPlan(n/2,1,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; 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");
PrintResult("FINAL RESULT",!waserrors);
PrintResult("* BI-DIRECTIONAL COMPLEX TEST",!bidierrors);
PrintResult("* AGAINST REFERENCE COMPLEX FFT",!referrors);
PrintResult("* BI-DIRECTIONAL REAL TEST",!bidirerrors);
PrintResult("* AGAINST REFERENCE REAL FFT",!refrerrors);
PrintResult("* INTERNAL EVEN FFT",!reinterrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Reference FFT |
//+------------------------------------------------------------------+
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; i++)
{
buf[2*i+0]=a[i].real;
buf[2*i+1]=a[i].imag;
}
//--- function call
RefInternalCFFT(buf,n,false);
//--- copy
for(i=0; i<n; i++)
{
a[i].real=buf[2*i+0];
a[i].imag=buf[2*i+1];
}
}
//+------------------------------------------------------------------+
//| Reference inverse FFT |
//+------------------------------------------------------------------+
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; i++)
{
buf[2*i+0]=a[i].real;
buf[2*i+1]=a[i].imag;
}
//--- function call
RefInternalCFFT(buf,n,true);
//--- copy
for(i=0; i<n; i++)
{
a[i].real=buf[2*i+0];
a[i].imag=buf[2*i+1];
}
}
//+------------------------------------------------------------------+
//| Internal complex FFT stub. |
//| Uses straightforward formula with O(N^2) complexity. |
//+------------------------------------------------------------------+
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 real=0;
double imag=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++)
{
real=a[2*k];
imag=a[2*k+1];
c=MathCos(-(2*M_PI*k*i/nn));
s=MathSin(-(2*M_PI*k*i/nn));
hre=hre+c*real-s*imag;
him=him+c*imag+s*real;
}
//--- 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++)
{
real=a[2*i];
imag=a[2*i+1];
c=MathCos(2*M_PI*k*i/nn);
s=MathSin(2*M_PI*k*i/nn);
hre=hre+c*real-s*imag;
him=him+c*imag+s*real;
}
//--- 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. |
//+------------------------------------------------------------------+
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].real=tmp[2*i+0];
f[i].imag=tmp[2*i+1];
}
}
//+------------------------------------------------------------------+
//| Testing class CConv |
//+------------------------------------------------------------------+
class CTestConvUnit
{
public:
static bool TestConv(const bool silent);
private:
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[]);
};
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
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; i++)
{
ca[i].real=2*CMath::RandomReal()-1;
ca[i].imag=2*CMath::RandomReal()-1;
}
//--- allocation
ArrayResize(cb,n);
for(i=0; i<n; i++)
{
cb[i].real=2*CMath::RandomReal()-1;
cb[i].imag=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; i++)
referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i]));
}
//--- Real convolution
ArrayResize(ra,m);
for(i=0; i<m; i++)
ra[i]=2*CMath::RandomReal()-1;
//--- allocation
ArrayResize(rb,n);
for(i=0; i<n; 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; 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; i++)
{
ca[i].real=2*CMath::RandomReal()-1;
ca[i].imag=2*CMath::RandomReal()-1;
}
//--- allocation
ArrayResize(cb,n);
for(i=0; i<n; i++)
{
cb[i].real=2*CMath::RandomReal()-1;
cb[i].imag=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; 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; i++)
inverr=MathMax(inverr,CMath::AbsComplex(cr1[i]-ca[i]));
//--- Real circilar and non-circular
ArrayResize(ra,m);
for(i=0; i<m; i++)
ra[i]=2*CMath::RandomReal()-1;
//--- allocation
ArrayResize(rb,n);
for(i=0; i<n; 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; 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; 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");
PrintResult("FINAL RESULT",!waserrors);
PrintResult("* AGAINST REFERENCE COMPLEX CONV",!referrors);
PrintResult("* AGAINST REFERENCE REAL CONV",!refrerrors);
PrintResult("* COMPLEX INVERSE",!inverrors);
PrintResult("* REAL INVERSE",!invrerrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
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; i++)
{
v=a[i];
i1_=-i;
for(i_=i; i_<=i+n-1; i_++)
r[i_]=r[i_]+v*b[i_+i1_];
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
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; 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 |
//+------------------------------------------------------------------+
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; i++)
{
v=a[i];
i1_=-i;
for(i_=i; i_<=i+n-1; i_++)
r[i_]=r[i_]+v*b[i_+i1_];
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
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; 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
{
public:
static bool TestCorr(const bool silent);
private:
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[]);
};
//+------------------------------------------------------------------+
//| Testing class CCorr |
//+------------------------------------------------------------------+
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; i++)
{
ca[i].real=2*CMath::RandomReal()-1;
ca[i].imag=2*CMath::RandomReal()-1;
}
//--- allocation
ArrayResize(cb,n);
for(i=0; i<n; i++)
{
cb[i].real=2*CMath::RandomReal()-1;
cb[i].imag=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; i++)
referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i]));
//--- Real correlation
ArrayResize(ra,m);
for(i=0; i<m; i++)
ra[i]=2*CMath::RandomReal()-1;
//--- allocation
ArrayResize(rb,n);
for(i=0; i<n; 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; 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");
PrintResult("FINAL RESULT",!waserrors);
PrintResult("* AGAINST REFERENCE COMPLEX CORR",!referrors);
PrintResult("* AGAINST REFERENCE REAL CORR",!refrerrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
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; 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; i++)
{
v=0;
for(j=0; j<m; j++)
{
//--- check
if(i+j>=n)
break;
v+=CMath::Conj(pattern[j])*s[i+j];
}
r[i]=v;
}
//--- calculation
for(i=1; i<m; i++)
{
v=0;
for(j=i; j<m; j++)
v+=CMath::Conj(pattern[j])*s[j-i];
r[m+n-1-i]=v;
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
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; i++)
{
//--- change value
v=0;
for(j=0; j<m; j++)
v+=CMath::Conj(pattern[j])*signal[(i+j)%n];
r[i]=v;
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
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; 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; i++)
{
v=0;
for(j=0; j<m; j++)
{
//--- check
if(i+j>=n)
break;
v+=pattern[j]*s[i+j];
}
r[i]=v;
}
//--- calculation
for(i=1; i<m; i++)
{
v=0;
for(j=i; j<m; j++)
v+=pattern[j]*s[-i+j];
r[m+n-1-i]=v;
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
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; i++)
{
v=0;
for(j=0; j<m; j++)
v+=pattern[j]*signal[(i+j)%n];
r[i]=v;
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
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; i++)
{
v=a[i];
i1_=-i;
for(i_=i; i_<=i+n-1; i_++)
r[i_]=r[i_]+v*b[i_+i1_];
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
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; 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 |
//+------------------------------------------------------------------+
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; i++)
{
v=a[i];
i1_=-i;
for(i_=i; i_<=i+n-1; i_++)
r[i_]=r[i_]+v*b[i_+i1_];
}
}
//+------------------------------------------------------------------+
//| Reference implementation |
//+------------------------------------------------------------------+
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; 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
{
public:
static bool TestFHT(const bool silent);
private:
static void RefFHTR1D(double &a[],const int n);
static void RefFHTR1DInv(double &a[],const int n);
};
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
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; 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; 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; 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; i++)
referr=MathMax(referr,MathAbs(r1[i]-r2[i]));
//--- inverse FHT
ArrayResize(r1,n);
ArrayResize(r2,n);
for(i=0; i<n; 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; 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");
PrintResult("FINAL RESULT",!waserrors);
PrintResult("* BI-DIRECTIONAL TEST",!bidierrors);
PrintResult("* AGAINST REFERENCE FHT",!referrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Reference FHT |
//+------------------------------------------------------------------+
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; i++)
{
v=0;
for(j=0; j<n; j++)
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; i++)
a[i]=buf[i];
}
//+------------------------------------------------------------------+
//| Reference inverse FHT |
//+------------------------------------------------------------------+
void CTestFHTUnit::RefFHTR1DInv(double &a[],const int n)
{
//--- check
if(!CAp::Assert(n>0,"RefFHTR1DInv: incorrect N!"))
return;
//--- function call
RefFHTR1D(a,n);
//--- change values
for(int i=0; i<n; i++)
a[i]=a[i]/n;
}
//+------------------------------------------------------------------+
//| Testing class CGaussQ |
//+------------------------------------------------------------------+
class CTestGQUnit
{
public:
static bool TestGQ(const bool silent);
private:
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[]);
};
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
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; 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; 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]=1.0/3.0;
//--- 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; 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; 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; 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; 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; 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");
PrintResult("FINAL RESULT",!waserrors);
PrintResult("* SPECIAL CASES (LEGENDRE/JACOBI/..)",!specerrors);
PrintResult("* RECURRENCE-BASED",!recerrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
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 |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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; 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; 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 |
//+------------------------------------------------------------------+
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; 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; 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 |
//+------------------------------------------------------------------+
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; 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; 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; 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 |
//+------------------------------------------------------------------+
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; 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; 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
{
public:
static bool TestGKQ(const bool silent);
private:
static double MapKind(const int k);
};
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
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; 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; 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; 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");
PrintResult("FINAL RESULT",!waserrors);
PrintResult("* PRE-CALCULATED TABLE",!intblerrors);
PrintResult("* CALCULATED AGAINST THE TABLE",!vstblerrors);
PrintResult("* GENERAL PROPERTIES",!generrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
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 |
//+------------------------------------------------------------------+
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:
static bool TestAutoGK(const bool silent);
};
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
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");
PrintResult("INTEGRATION WITH GIVEN ACCURACY",!(simpleerrors || sngenderrors));
PrintResult("* SIMPLE PROBLEMS",!simpleerrors);
PrintResult("* SINGULAR PROBLEMS (ENDS OF INTERVAL)",!sngenderrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CIDWInt |
//+------------------------------------------------------------------+
class CTestIDWIntUnit
{
public:
static bool TestIDWInt(const bool silent);
private:
static void TestContinuity(CIDWModel &model,int nx,int ny,CRowDouble &x0,CRowDouble &x1,int nsteps,int d,bool &err);
static void TestCommon(bool &err);
static void TestMSTAB(bool &err);
};
//+------------------------------------------------------------------+
//| Testing IDW interpolation |
//+------------------------------------------------------------------+
bool CTestIDWIntUnit::TestIDWInt(const bool silent)
{
bool commonerrors=false;
bool mstaberrors=false;
TestCommon(commonerrors);
TestMSTAB(mstaberrors);
bool waserrors=commonerrors||mstaberrors;
if(!silent)
{
Print("TESTING INVERSE DISTANCE WEIGHTING");
PrintResult("* common properties",!commonerrors);
PrintResult("* MSTAB-specific tests",!mstaberrors);
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing continuity properties: C0 (D=0) or C1 (D=1) continuity. |
//| Error flag is modified on failure, unchanged on success. |
//+------------------------------------------------------------------+
void CTestIDWIntUnit::TestContinuity(CIDWModel &model,int nx,
int ny,CRowDouble &x0,CRowDouble &x1,
int nsteps,
int d,bool &err)
{
//--- create variables
int i=0;
int j=0;
int cidx=0;
double t=0;
CRowDouble xc;
CRowDouble yc;
CMatrixDouble yy;
double lc1=0;
double lc2=0;
//--- check
if(!CAp::Assert(nsteps>=10,"TestContinuity: NSteps is too small"))
{
err=true;
return;
}
if(!CAp::Assert(d==0 || d==1,"TestContinuity: incorrect D"))
{
err=true;
return;
}
//--- Compute sequence of function values
xc.Resize(nx);
yy.Resize(nsteps,ny);
for(i=0; i<nsteps; i++)
{
t=(double)i/(double)(nsteps-1);
for(j=0; j<nx; j++)
xc.Set(j,x0[j]*t+x1[j]*(1-t));
CIDWInt::IDWCalcBuf(model,xc,yc);
for(j=0; j<ny; j++)
yy.Set(i,j,yc[j]);
}
//--- Evaluate all differentiability levels (C0, C1) requested by user
for(cidx=0; cidx<=d; cidx++)
{
//--- Compute Lipschitz constant for original and increased steps
lc1=0;
lc2=0;
for(i=0; i<=nsteps-3; i++)
{
for(j=0; j<ny; j++)
{
lc1=MathMax(lc1,MathAbs(yy.Get(i,j)-yy[i+1][ j]));
lc2=MathMax(lc2,MathAbs(yy.Get(i,j)-yy[i+2][j])/2);
}
}
if(lc2>1.0E-4 && lc1>(1.750*lc2))
{
Print("testidwunit.ap:67");
err=true;
return;
}
//--- Differentiate function, repeat one more time
for(i=0; i<=nsteps-2; i++)
{
for(j=0; j<ny; j++)
yy.Set(i,j,yy.Get(i+1,j)-yy.Get(i,j));
}
nsteps --;
}
}
//+------------------------------------------------------------------+
//| Test MSTAB; Err is set to True on failure, unchanged otherwise. |
//+------------------------------------------------------------------+
void CTestIDWIntUnit::TestCommon(bool &err)
{
//--- create variables
int pass=0;
int algotype=0;
int i=0;
int i0=0;
int i1=0;
int j=0;
int k=0;
int n=0;
CMatrixDouble xy;
double v=0;
double vv=0;
bool initdone;
CIDWModelShell model;
CIDWModelShell model1;
CIDWBuilder builder;
CIDWReport rep;
CIDWCalcBuffer buffer;
double shepardp=0;
double rbase=0;
double tol=0;
double mindistinf=0;
double refrms=0;
double refavg=0;
double refmax=0;
double refr2=0;
double refrss=0;
double reftss=0;
int nx=0;
int ny=0;
CHighQualityRandStateShell rs;
double x0=0;
double x1=0;
double x2=0;
int continuitytesting=0;
CRowDouble x;
CRowDouble xx;
CRowDouble y;
CRowDouble meany;
CHighQualityRand::HQRndRandomize(rs.GetInnerObj());
tol=1.0E-10;
mindistinf=0.05;
//--- Try all algorithms
for(algotype=0; (algotype<=2 && !err); algotype++)
{
//--- Test empty dataset
for(nx=1; (nx<=5 && !err); nx++)
{
for(ny=1; (ny<=5 && !err); ny++)
{
initdone=false;
CIDWInt::IDWBuilderCreate(nx,ny,builder);
if(algotype==0)
{
initdone=true;
shepardp=1+(nx+1)*CHighQualityRand::HQRndUniformR(rs.GetInnerObj());
CIDWInt::IDWBuilderSetAlgoTextBookShepard(builder,shepardp);
}
if(algotype==1)
{
initdone=true;
rbase=MathPow(2.0,4*CHighQualityRand::HQRndUniformR(rs.GetInnerObj())-2);
CIDWInt::IDWBuilderSetAlgoTextBookModShepard(builder,rbase);
}
if(algotype==2)
{
initdone=true;
rbase=MathPow(2.0,4*CHighQualityRand::HQRndUniformR(rs.GetInnerObj())-2);
CIDWInt::IDWBuilderSetAlgoMSTAB(builder,rbase);
}
//--- check
if(!CAp::Assert(initdone,__FUNCTION__+": unexpected AlgoType"))
{
err=true;
return;
}
if(CHighQualityRand::HQRndNormal(rs.GetInnerObj())>0.0)
{
//--- Fit and store result directly into the variable
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
}
else
{
//--- Fit, store result to temporary, pass through the CSerializer
CIDWInt::IDWFit(builder,model1.GetInnerObj(),rep);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
_local_serializer.Alloc_Start();
CIDWInt::IDWAlloc(_local_serializer,model1.GetInnerObj());
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CIDWInt::IDWSerialize(_local_serializer,model1.GetInnerObj());
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CIDWInt::IDWUnserialize(_local_serializer,model.GetInnerObj());
_local_serializer.Stop();
}
}
CIDWInt::IDWCreateCalcBuffer(model.GetInnerObj(),buffer);
//--- Test report
if(rep.m_rmserror!=0.0)
{
err=true;
Print("testidwunit.ap:169");
return;
}
if(rep.m_avgerror!=0.0)
{
err=true;
Print("testidwunit.ap:170");
return;
}
if(rep.m_maxerror!=0.0)
{
err=true;
Print("testidwunit.ap:171");
return;
}
if(rep.m_r2!=1.0)
{
err=true;
Print("testidwunit.ap:172");
return;
}
//--- Test simplified evaluation
x0=CHighQualityRand::HQRndNormal(rs.GetInnerObj());
x1=CHighQualityRand::HQRndNormal(rs.GetInnerObj());
x2=CHighQualityRand::HQRndNormal(rs.GetInnerObj());
if(nx==1 && ny==1 && CIDWInt::IDWCalc1(model.GetInnerObj(),x0)!=0.0)
{
err=true;
Print("testidwunit.ap:181");
return;
}
if(nx==2 && ny==1 && CIDWInt::IDWCalc2(model.GetInnerObj(),x0,x1)!=0.0)
{
err=true;
Print("testidwunit.ap:183");
return;
}
if(nx==3 && ny==1 && CIDWInt::IDWCalc3(model.GetInnerObj(),x0,x1,x2)!=0.0)
{
err=true;
Print("testidwunit.ap:185");
return;
}
//--- Test generic evaluation
x.Resize(nx);
for(i=0; i<nx; i++)
x.Set(i,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
y.Resize(0);
CIDWInt::IDWCalc(model.GetInnerObj(),x,y);
if(CAp::SetErrorFlag(err,CAp::Len(y)!=ny,"testidwunit.ap:195"))
return;
for(i=0; i<ny; i++)
if(CAp::SetErrorFlag(err,y[i]!=0.0,"testidwunit.ap:199"))
return;
y.Resize(0);
CIDWInt::IDWCalcBuf(model.GetInnerObj(),x,y);
if(CAp::SetErrorFlag(err,CAp::Len(y)!=ny,"testidwunit.ap:202"))
return;
for(i=0; i<ny; i++)
if(CAp::SetErrorFlag(err,y[i]!=0.0,"testidwunit.ap:206"))
return;
y.Resize(0);
CIDWInt::IDWTsCalcBuf(model.GetInnerObj(),buffer,x,y);
if(CAp::SetErrorFlag(err,CAp::Len(y)!=ny,"testidwunit.ap:209"))
return;
for(i=0; i<ny; i++)
if(CAp::SetErrorFlag(err,y[i]!=0.0,"testidwunit.ap:213"))
return;
}
}
//--- Generate random dataset with distinct points, test interpolation
//--- properties (target function is reproduced almost exactly, the model
//--- is continuous)
for(pass=1; pass<=20; pass++)
{
n=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),25);
nx=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),4);
ny=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),4);
x.Resize(nx);
xx.Resize(nx);
y.Resize(ny);
//--- Generate dataset with distinct points
xy.Resize(n,nx+ny);
meany=vector<double>::Zeros(ny);
i=0;
while(i<n)
{
//--- Generate random point
for(j=0; j<nx ; j++)
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
//--- Test distance between newly generated point and other ones.
//--- Repeat point generation if it is too close to some other point.
v=CMath::m_maxrealnumber;
for(i0=0; i0<i; i0++)
{
vv=0;
for(j=0; j<nx ; j++)
vv=MathMax(vv,MathAbs(xy.Get(i,j)-xy.Get(i0,j)));
v=MathMin(v,vv);
}
if(v<mindistinf)
continue;
//--- Point is accepted
for(j=0; j<ny; j++)
{
xy.Set(i,nx+j,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
meany.Add(j,xy.Get(i,nx+j)/n);
}
i++;
}
//--- Build IDW model
initdone=false;
CIDWInt::IDWBuilderCreate(nx,ny,builder);
if(algotype==0)
{
initdone=true;
shepardp=1+(nx+1)*CHighQualityRand::HQRndUniformR(rs.GetInnerObj());
CIDWInt::IDWBuilderSetAlgoTextBookShepard(builder,shepardp);
}
if(algotype==1)
{
initdone=true;
rbase=MathPow(2.0,4*CHighQualityRand::HQRndUniformR(rs.GetInnerObj())-2);
CIDWInt::IDWBuilderSetAlgoTextBookModShepard(builder,rbase);
}
if(algotype==2)
{
initdone=true;
rbase=MathPow(2.0,4*CHighQualityRand::HQRndUniformR(rs.GetInnerObj())-2);
CIDWInt::IDWBuilderSetAlgoMSTAB(builder,rbase);
}
//--- check
if(!CAp::Assert(initdone,"TestCommon: unexpected AlgoType"))
return;
CIDWInt::IDWBuilderSetPoints(builder,xy,n);
if(CHighQualityRand::HQRndNormal(rs.GetInnerObj())>0.0)
{
//--- Fit and store result directly into the variable
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
}
else
{
//--- Fit, store result to temporary, pass through the CSerializer
CIDWInt::IDWFit(builder,model1.GetInnerObj(),rep);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
_local_serializer.Alloc_Start();
CIDWInt::IDWAlloc(_local_serializer,model1.GetInnerObj());
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CIDWInt::IDWSerialize(_local_serializer,model1.GetInnerObj());
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CIDWInt::IDWUnserialize(_local_serializer,model.GetInnerObj());
_local_serializer.Stop();
}
}
CIDWInt::IDWCreateCalcBuffer(model.GetInnerObj(),buffer);
//--- Test error metrics
//--- NOTE: we expect that dataset is reproduced exactly
if(CAp::SetErrorFlag(err,MathAbs(rep.m_rmserror)>tol,"testidwunit.ap:320") ||
CAp::SetErrorFlag(err,MathAbs(rep.m_avgerror)>tol,"testidwunit.ap:321") ||
CAp::SetErrorFlag(err,MathAbs(rep.m_maxerror)>tol,"testidwunit.ap:322") ||
CAp::SetErrorFlag(err,MathAbs(rep.m_r2)<(1.0-tol),"testidwunit.ap:323"))
return;
//--- Test that dataset is actually exactly reproduced
for(i=0; i<n; i++)
{
//--- Test generic evaluation
x=xy[i]+0;
y.Resize(0);
CIDWInt::IDWCalc(model.GetInnerObj(),x,y);
if(CAp::SetErrorFlag(err,CAp::Len(y)!=ny,"testidwunit.ap:337"))
return;
for(j=0; j<ny; j++)
if(CAp::SetErrorFlag(err,MathAbs(y[j]-xy.Get(i,nx+j))>tol,"testidwunit.ap:339"))
return;
k=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),2*ny+1);
y.Resize(k);
CIDWInt::IDWCalcBuf(model.GetInnerObj(),x,y);
if(CAp::SetErrorFlag(err,CAp::Len(y)!=MathMax(ny,k),"testidwunit.ap:346"))
return;
for(j=0; j<ny; j++)
if(CAp::SetErrorFlag(err,MathAbs(y[j]-xy.Get(i,nx+j))>tol,"testidwunit.ap:348"))
return;
k=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),2*ny+1);
y.Resize(k);
CIDWInt::IDWTsCalcBuf(model.GetInnerObj(),buffer,x,y);
if(CAp::SetErrorFlag(err,CAp::Len(y)!=MathMax(ny,k),"testidwunit.ap:355"))
return;
for(j=0; j<ny; j++)
if(CAp::SetErrorFlag(err,MathAbs(y[j]-xy.Get(i,nx+j))>tol,"testidwunit.ap:357"))
return;
//--- Specialized 1, 2, 3-dimensional cases
if(ny==1)
{
if(nx==1 && CAp::SetErrorFlag(err,MathAbs(CIDWInt::IDWCalc1(model.GetInnerObj(),x[0])-xy.Get(i,nx))>tol,"testidwunit.ap:366"))
return;
if(nx==2 && CAp::SetErrorFlag(err,MathAbs(CIDWInt::IDWCalc2(model.GetInnerObj(),x[0],x[1])-xy.Get(i,nx))>tol,"testidwunit.ap:368"))
return;
if(nx==3 && CAp::SetErrorFlag(err,MathAbs(CIDWInt::IDWCalc3(model.GetInnerObj(),x[0],x[1],x[2])-xy.Get(i,nx))>tol,"testidwunit.ap:370"))
return;
}
}
//--- Test continuity properties:
//--- * continuity is guaranteed for original Shepard's method, MSTAB and MSMOOTH
//--- * modified Shepard method does not guarantee continuity of the model, but
//--- we can be sure that model is continuous along line connecting two nearest
//--- points
for(k=0; k<=1; k++)
{
i0=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),n);
x=xy[i0]+0;
i1=-1;
v=CMath::m_maxrealnumber;
for(i=0; i<n; i++)
{
vv=0;
for(j=0; j<nx; j++)
vv+=CMath::Sqr(x[j]-xy.Get(i,j));
if(vv<v && vv>0.0)
{
i1=i;
xx=xy[i]+0;
v=vv;
}
}
if(i1<0)
{
i1=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),n);
xx=xy[i1]+0;
}
continuitytesting=1;
if(algotype==0)
continuitytesting=0;
if(algotype==1)
continuitytesting=-1;
if(continuitytesting>=0)
TestContinuity(model.GetInnerObj(),nx,ny,x,xx,10000,continuitytesting,err);
if(err)
return;
}
//--- Test evaluation at remote points
x.Resize(nx);
for(j=0; j<nx; j++)
x.Set(j,1.0E20*(2*CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),2)-1));
CIDWInt::IDWCalc(model.GetInnerObj(),x,y);
for(j=0; j<ny; j++)
if(CAp::SetErrorFlag(err,MathAbs(y[j]-meany[j])>tol,"testidwunit.ap:424"))
return;
}
//--- Generate random dataset with NONDISTINCT points, test approximation
//--- properties and error reports.
for(pass=1; pass<=20; pass++)
{
n=2*(1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),10));
nx=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),4);
ny=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),4);
x.Resize(nx);
xx.Resize(nx);
y.Resize(ny);
meany=vector<double>::Zeros(ny);
//--- Generate dataset with nondistinct points, each point is repeated;
//--- compute reference values of the error metrics
xy.Resize(n,nx+ny);
refrms=0;
refavg=0;
refmax=0;
refrss=0;
reftss=0;
i=0;
while(i<=n/2-1)
{
//--- Generate two copies of the same point
for(j=0; j<nx+ny; j++)
{
v=CHighQualityRand::HQRndNormal(rs.GetInnerObj());
xy.Set(2*i+0,j,v);
xy.Set(2*i+1,j,v);
}
//--- Test distance between newly generated point and other ones.
//--- Repeat point generation if it is too close to some other point.
v=CMath::m_maxrealnumber;
for(i0=0; i0<2*i; i0++)
{
vv=0;
for(j=0; j<nx; j++)
vv=MathMax(vv,MathAbs(xy.Get(2*i+0,j)-xy.Get(i0,j)));
v=MathMin(v,vv);
}
if(v<mindistinf)
continue;
//--- Update meanY
for(j=0; j<ny; j++)
meany.Add(j,(xy.Get(2*i,nx+j)+xy.Get(2*i+1,nx+j))/n);
//--- Apply perturbation to the target value
for(j=0; j<ny; j++)
{
v=MathPow(2,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
xy.Add(2*i,nx+j,v);
xy.Add(2*i+1,nx+j,- v);
v=MathAbs(v);
refrms=refrms+2*v*v;
refavg=refavg+2*v;
refmax=MathMax(refmax,v);
refrss=refrss+2*v*v;
}
//--- Next I
i++;
}
for(i=0; i<n; i++)
{
for(j=0; j<ny; j++)
reftss+=CMath::Sqr(xy.Get(i,nx+j)-meany[j]);
}
refrms=MathSqrt(refrms/(n*ny));
refavg=refavg/(n*ny);
refr2=1.0-refrss/CApServ::Coalesce(reftss,1);
//--- Build IDW model
initdone=false;
CIDWInt::IDWBuilderCreate(nx,ny,builder);
if(algotype==0)
{
initdone=true;
shepardp=nx*(1+CHighQualityRand::HQRndUniformR(rs.GetInnerObj()));
CIDWInt::IDWBuilderSetAlgoTextBookShepard(builder,shepardp);
}
if(algotype==1)
{
initdone=true;
rbase=MathPow(2.0,4*CHighQualityRand::HQRndUniformR(rs.GetInnerObj())-2);
CIDWInt::IDWBuilderSetAlgoTextBookModShepard(builder,rbase);
}
if(algotype==2)
{
initdone=true;
rbase=MathPow(2.0,4*CHighQualityRand::HQRndUniformR(rs.GetInnerObj())-2);
CIDWInt::IDWBuilderSetAlgoMSTAB(builder,rbase);
}
//--- check
if(!CAp::Assert(initdone,"TestCommon: unexpected AlgoType"))
return;
CIDWInt::IDWBuilderSetPoints(builder,xy,n);
if(CHighQualityRand::HQRndNormal(rs.GetInnerObj())>0.0)
{
//--- Fit and store result directly into the variable
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
}
else
{
//--- Fit, store result to temporary, pass through the CSerializer
CIDWInt::IDWFit(builder,model1.GetInnerObj(),rep);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
_local_serializer.Alloc_Start();
CIDWInt::IDWAlloc(_local_serializer,model1.GetInnerObj());
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CIDWInt::IDWSerialize(_local_serializer,model1.GetInnerObj());
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CIDWInt::IDWUnserialize(_local_serializer,model.GetInnerObj());
_local_serializer.Stop();
}
}
CIDWInt::IDWCreateCalcBuffer(model.GetInnerObj(),buffer);
//--- Test error metrics
if(CAp::SetErrorFlag(err,MathAbs(rep.m_rmserror-refrms)>tol,"testidwunit.ap:562") ||
CAp::SetErrorFlag(err,MathAbs(rep.m_avgerror-refavg)>tol,"testidwunit.ap:563") ||
CAp::SetErrorFlag(err,MathAbs(rep.m_maxerror-refmax)>tol,"testidwunit.ap:564") ||
CAp::SetErrorFlag(err,MathAbs(rep.m_r2-refr2)>tol,"testidwunit.ap:565"))
return;
//--- Test ability to reproduce mean over non-distinct points
//--- NOTE: we do not test all evaluation functions, just IDWCalc()
for(i=0; i<n/2; i++)
{
x=xy[2*i]+0;
CIDWInt::IDWCalc(model.GetInnerObj(),x,y);
for(j=0; j<ny; j++)
if(CAp::SetErrorFlag(err,MathAbs(y[j]-0.5*(xy.Get(2*i+0,nx+j)+xy.Get(2*i+1,nx+j)))>tol,"testidwunit.ap:578"))
return;
}
//--- Test continuity properties:
//--- * continuity is guaranteed for original Shepard's method, MSTAB and MSMOOTH
//--- * modified Shepard method does not guarantee continuity of the model, but
//--- we can be sure that model is continuous along line connecting two nearest
//--- points
for(k=0; k<=1; k++)
{
i0=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),n);
x=xy[i0]+0;
i1=-1;
v=CMath::m_maxrealnumber;
for(i=0; i<n; i++)
{
vv=0;
for(j=0; j<nx ; j++)
vv+=CMath::Sqr(x[j]-xy.Get(i,j));
if(vv<v && vv>0.0)
{
i1=i;
xx=xy[i]+0;
v=vv;
}
}
if(i1<0)
{
i1=CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),n);
xx=xy[i1]+0;
}
continuitytesting=1;
if(algotype==0)
continuitytesting=0;
if(algotype==1)
continuitytesting=-1;
if(continuitytesting>=0)
TestContinuity(model.GetInnerObj(),nx,ny,x,xx,10000,continuitytesting,err);
if(err)
return;
}
}
//--- Test correct handling of the prior term
n=10;
for(pass=1; pass<=20; pass++)
{
nx=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),4);
ny=1+CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),4);
x.Resize(nx);
y.Resize(ny);
xy.Resize(n,nx+ny);
meany=vector<double>::Zeros(ny);
for(i=0; i<n; i++)
{
for(j=0; j<nx; j++)
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
for(j=0; j<ny; j++)
{
xy.Set(i,nx+j,CHighQualityRand::HQRndNormal(rs.GetInnerObj()));
meany.Add(j,xy.Get(i,nx+j)/n);
}
}
initdone=false;
CIDWInt::IDWBuilderCreate(nx,ny,builder);
if(algotype==0)
{
initdone=true;
shepardp=1+(nx+1)*CHighQualityRand::HQRndUniformR(rs.GetInnerObj());
CIDWInt::IDWBuilderSetAlgoTextBookShepard(builder,shepardp);
}
if(algotype==1)
{
initdone=true;
rbase=MathPow(2.0,4*CHighQualityRand::HQRndUniformR(rs.GetInnerObj())-2);
CIDWInt::IDWBuilderSetAlgoTextBookModShepard(builder,rbase);
}
if(algotype==2)
{
initdone=true;
rbase=MathPow(2.0,4*CHighQualityRand::HQRndUniformR(rs.GetInnerObj())-2);
CIDWInt::IDWBuilderSetAlgoMSTAB(builder,rbase);
}
//--- check
if(!CAp::Assert(initdone,"TestCommon: unexpected AlgoType (prior test)"))
return;
CIDWInt::IDWBuilderSetPoints(builder,xy,n);
//--- Zero prior (not tested with textbook Shepard method)
if(algotype!=0)
{
CIDWInt::IDWBuilderSetZeroTerm(builder);
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
for(j=0; j<nx; j++)
x.Set(j,1.0E20*(2*CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),2)-1));
CIDWInt::IDWCalc(model.GetInnerObj(),x,y);
for(j=0; j<ny; j++)
if(CAp::SetErrorFlag(err,MathAbs(y[j])>tol,"testidwunit.ap:684"))
return;
}
//--- Mean prior
CIDWInt::IDWBuilderSetConstTerm(builder);
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
for(j=0; j<nx; j++)
x.Set(j,1.0E20*(2*CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),2)-1));
CIDWInt::IDWCalc(model.GetInnerObj(),x,y);
for(j=0; j<ny; j++)
if(CAp::SetErrorFlag(err,MathAbs(y[j]-meany[j])>tol,"testidwunit.ap:696"))
return;
//--- User-specified prior (not tested with textbook Shepard method)
if(algotype!=0)
{
v=CHighQualityRand::HQRndNormal(rs.GetInnerObj());
CIDWInt::IDWBuilderSetUserTerm(builder,v);
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
for(j=0; j<nx; j++)
x.Set(j,1.0E20*(2*CHighQualityRand::HQRndUniformI(rs.GetInnerObj(),2)-1));
CIDWInt::IDWCalc(model.GetInnerObj(),x,y);
for(j=0; j<ny; j++)
if(CAp::SetErrorFlag(err,MathAbs(y[j]-v)>tol,"testidwunit.ap:710"))
return;
}
}
}
}
//+------------------------------------------------------------------+
//| Test MSTAB; |
//| Err is set to True on failure, unchanged otherwise. |
//+------------------------------------------------------------------+
void CTestIDWIntUnit::TestMSTAB(bool &err)
{
CMatrixDouble xy;
int i=0;
int n=0;
double v=0;
double vv=0;
double x0=0;
double x1=0;
double rbase=0;
CIDWModelShell model;
CIDWBuilder builder;
CIDWReport rep;
CHighQualityRandStateShell rs;
CHighQualityRand::HQRndRandomize(rs.GetInnerObj());
//--- Basic test #1: nonzero derivative
//--- * XY = [[-1,-1],[0,0,],[1,1]]
//--- * RBase>=2
//--- * derivative at x=0 must be positive and bigger than 0.1
xy.Resize(3,2);
for(i=0; i<=2; i++)
{
xy.Set(i,0,i-1);
xy.Set(i,1,i-1);
}
CIDWInt::IDWBuilderCreate(1,1,builder);
rbase=MathPow(2.0,1.0+CHighQualityRand::HQRndUniformR(rs.GetInnerObj()));
CIDWInt::IDWBuilderSetAlgoMSTAB(builder,rbase);
CIDWInt::IDWBuilderSetPoints(builder,xy,3);
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
v=0.01;
if(CAp::SetErrorFlag(err,((CIDWInt::IDWCalc1(model.GetInnerObj(),v)-CIDWInt::IDWCalc1(model.GetInnerObj(),-v))/(2*v))<0.1,"testidwunit.ap:758"))
return;
//--- Basic test #2: good smoothness
//--- * 2D task, dataset is composed from 3 parallel lines
//--- along y=-0.1, y=0, y=+0.1, with outer lines having
//--- constant zero target value, inner line having constant
//--- target equal to 1
//--- * RBase=1 is used
//--- * we test that function value does not change significantly
//--- along the line
n=100;
xy.Resize(3*n,3);
for(i=0; i<n; i++)
{
xy.Set(3*i,0,(double)i/(double)(n-1));
xy.Set(3*i,1,-0.1);
xy.Set(3*i,2,0);
xy.Set(3*i+1,0,(double)i/(double)(n-1));
xy.Set(3*i+1,1,0);
xy.Set(3*i+1,2,1);
xy.Set(3*i+2,0,(double)i/(double)(n-1));
xy.Set(3*i+2,1,0.1);
xy.Set(3*i+2,2,0);
}
rbase=1.0;
CIDWInt::IDWBuilderCreate(2,1,builder);
CIDWInt::IDWBuilderSetAlgoMSTAB(builder,rbase);
CIDWInt::IDWBuilderSetPoints(builder,xy,3*n);
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
v=0;
for(i=0; i<=1000; i++)
{
v=MathMax(v,MathAbs(CIDWInt::IDWCalc2(model.GetInnerObj(),CHighQualityRand::HQRndUniformR(rs.GetInnerObj()),-0.1)));
v=MathMax(v,MathAbs(CIDWInt::IDWCalc2(model.GetInnerObj(),CHighQualityRand::HQRndUniformR(rs.GetInnerObj()),0.1)));
v=MathMax(v,MathAbs(CIDWInt::IDWCalc2(model.GetInnerObj(),CHighQualityRand::HQRndUniformR(rs.GetInnerObj()),0)-1));
}
CAp::SetErrorFlag(err,v>0.001,"testidwunit.ap:796");
//--- Continuity when moving away from the dataset
xy.Resize(1,2);
xy.Set(0,0,0);
xy.Set(0,1,1);
rbase=1.0;
CIDWInt::IDWBuilderCreate(1,1,builder);
CIDWInt::IDWBuilderSetAlgoMSTAB(builder,rbase);
CIDWInt::IDWBuilderSetPoints(builder,xy,1);
CIDWInt::IDWBuilderSetZeroTerm(builder);
CIDWInt::IDWFit(builder,model.GetInnerObj(),rep);
CAp::SetErrorFlag(err,CIDWInt::IDWCalc1(model.GetInnerObj(),100000)!=0.0,"testidwunit.ap:810");
v=0;
for(i=0; i<=500; i++)
{
x0=1.2*rbase*((double)i/(double)500);
x1=1.2*rbase*((double)(i+1)/(double)500);
v=MathMax(v,MathAbs((CIDWInt::IDWCalc1(model.GetInnerObj(),x1)-CIDWInt::IDWCalc1(model.GetInnerObj(),x0))/(x1-x0)));
}
vv=0;
for(i=0; i<=1000; i++)
{
x0=1.2*rbase*((double)i/1000.0);
x1=1.2*rbase*((double)(i+1)/1000.0);
vv=MathMax(vv,MathAbs((CIDWInt::IDWCalc1(model.GetInnerObj(),x1)-CIDWInt::IDWCalc1(model.GetInnerObj(),x0))/(x1-x0)));
}
CAp::SetErrorFlag(err,(vv/v)>1.333,"testidwunit.ap:825");
}
//+------------------------------------------------------------------+
//| Testing class CRatInt |
//+------------------------------------------------------------------+
class CTestRatIntUnit
{
public:
static bool TestRatInt(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Testing class CRatInt |
//+------------------------------------------------------------------+
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; 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; j++)
{
w[j]=1;
for(k=0; k<n; 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; 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; 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; 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; 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; i++)
x[i]=MathArctan((b-a)*i/(n-1)+a);
for(i=0; i<n; 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; 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; i++)
x[i]=(b-a)*i/(n-1)+a;
for(i=0; i<n; i++)
y[i]=2*CMath::RandomReal()-1;
s=1;
//--- check
if(d==0)
{
for(i=0; i<n; 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; 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");
PrintResult("BASIC BARYCENTRIC FUNCTIONS",!bcerrors);
PrintResult("FLOATER-HORMANN",!nperrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- end
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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; i++)
s1=s1+CMath::Sqr(w[i]*(c-y[i]));
//--- calculation
s2=0;
s3=0;
for(i=0; i<n; 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
{
public:
static bool TestPolInt(const bool silent);
private:
static double InternalPolInt(double &x[],double &cf[],int n,const double t);
static void BRCunSet(CBarycentricInterpolant &b);
};
//+------------------------------------------------------------------+
//| Unit test |
//+------------------------------------------------------------------+
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; i++)
interrors=interrors||p.m_w[i]==0.0;
}
//--- report
waserrors=interrors;
//--- check
if(!silent)
{
Print("TESTING POLYNOMIAL INTERPOLATION");
//--- Normal tests
PrintResult("INTERPOLATION TEST",!interrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- end
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
double CTestPolIntUnit::InternalPolInt(double &x[],double &cf[],
int n,const double t)
{
//--- create array
double f[];
//--- copy
ArrayCopy(f,cf);
//--- calculation
n=n-1;
for(int j=0; j<n; j++)
{
for(int 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 |
//+------------------------------------------------------------------+
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
{
public:
static bool TestSpline1D(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Testing class CSpline1D |
//+------------------------------------------------------------------+
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; 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; 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; 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; 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; 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; 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; 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 ; 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 ; 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 ; 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 ; 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; 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; 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; 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; 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; 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; 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; 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; k++)
{
for(i_=0; i_<n; 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; 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; 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; 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
PrintResult("LINEAR SPLINE TEST",!lserrors);
PrintResult("CUBIC SPLINE TEST",!cserrors);
PrintResult("CATMULL-ROM SPLINE TEST",!crserrors);
PrintResult("HERMITE SPLINE TEST",!hserrors);
PrintResult("AKIMA SPLINE TEST",!aserrors);
PrintResult("DIFFERENTIATION TEST",!dserrors);
PrintResult("COPY/SERIALIZATION TEST",!cperrors);
PrintResult("UNPACK TEST",!uperrors);
PrintResult("LIN.TRANS. TEST",!lterrors);
PrintResult("INTEGRATION TEST",!ierrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- end
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Lipschitz constants for spline inself,first and second |
//| derivatives. |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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; i++)
s1=s1+CMath::Sqr(w[i]*(c-y[i]));
s2=0;
s3=0;
//--- calculation
for(i=0; i<n; 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:
static bool TestMinLM(const bool silent);
private:
static void TestU(bool &errorflag,bool &statefieldsconsistencyflag);
static void TestBC(bool &errorflag);
static void TestLC(bool &errorflag);
static void TestOther(bool &errorflag,bool &statefieldsconsistencyflag);
static void TestOptGuard(bool &waserrors);
static bool RKindVSStateCheck(int rkind,CMinLMState &state);
static void AXMB(CMinLMState &state,CMatrixDouble &a,CRowDouble &b,int n);
static void TryReproduceFixedBugs(bool &err);
static void TestFunc1(int n,int m,CMatrixDouble &c,CRowDouble &x,double &f,bool needf,CRowDouble &fi,bool needfi,CMatrixDouble &jac,bool needjac);
};
//+------------------------------------------------------------------+
//| Testing class CMinLM |
//+------------------------------------------------------------------+
bool CTestMinLMUnit::TestMinLM(const bool silent)
{
//--- create variables
bool result=false;
bool waserrors=false;
bool uerrors=false;
bool bcerrors=false;
bool lcerrors=false;
bool scerror=false;
bool othererrors=false;
bool optguarderr=false;
//--- Various tests
TestOther(othererrors,scerror);
//--- Tests sorted by constraint types
TestU(uerrors,scerror);
TestBC(bcerrors);
TestLC(lcerrors);
//--- Try to reproduce previously fixed bugs
TryReproduceFixedBugs(othererrors);
//--- Test for MinLMGradientCheck
optguarderr=false;
TestOptGuard(optguarderr);
//--- end
waserrors=(uerrors||bcerrors||lcerrors||scerror||othererrors||optguarderr);
if(!silent)
{
Print("TESTING LEVENBERG-MARQUARDT OPTIMIZATION");
Print("PROBLEM TYPES:");
PrintResult("* UNCONSTRAINED",!uerrors);
PrintResult("* BOX CONSTRAINED",!bcerrors);
PrintResult("* LINEARLY CONSTRAINED",!lcerrors);
PrintResult("STATE FIELDS CONSISTENCY",!scerror);
PrintResult("OTHER PROPERTIES",!othererrors);
PrintResult("OPTGUARD",!optguarderr);
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| Test for unconstrained problems. |
//| On failure sets error flag, leaves it unchanged on success. |
//+------------------------------------------------------------------+
void CTestMinLMUnit::TestU(bool &errorflag,
bool &statefieldsconsistencyflag)
{
int n=0;
int m=0;
int i=0;
CRowDouble x;
CRowDouble xe;
CRowDouble b;
double h=0;
int rkind=0;
CMatrixDouble a;
double v=0;
double s=0;
double eps=0;
double epsx=0;
int maxits=0;
int ckind=0;
CMinLMState state;
CMinLMReport rep;
int i_=0;
//--- 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.
x=vector<double>::Zeros(3);
n=3;
m=3;
h=0.0001;
for(rkind=-2; rkind<=5; rkind++)
{
x.Set(0,100*CMath::RandomReal()-50);
x.Set(1,100*CMath::RandomReal()-50);
x.Set(2,100*CMath::RandomReal()-50);
switch(rkind)
{
case -2:
CMinLM::MinLMCreateV(n,m,x,h,state);
CMinLM::MinLMSetAccType(state,1);
break;
case -1:
CMinLM::MinLMCreateV(n,m,x,h,state);
CMinLM::MinLMSetAccType(state,0);
break;
case 0:
CMinLM::MinLMCreateFJ(n,m,x,state);
break;
case 1:
CMinLM::MinLMCreateFGJ(n,m,x,state);
break;
case 2:
CMinLM::MinLMCreateFGH(n,x,state);
break;
case 3:
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,0);
break;
case 4:
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,1);
break;
case 5:
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,2);
break;
}
while(CMinLM::MinLMIteration(state))
{
//--- (x-2)^2 + y^2 + (z-x)^2
if(state.m_needfi)
{
state.m_fi.Set(0,state.m_x[0]-2);
state.m_fi.Set(1,state.m_x[1]);
state.m_fi.Set(2,state.m_x[2]-state.m_x[0]);
}
if(state.m_needfij)
{
state.m_fi.Set(0,state.m_x[0]-2);
state.m_fi.Set(1,state.m_x[1]);
state.m_fi.Set(2,state.m_x[2]-state.m_x[0]);
state.m_j.Set(0,0,1);
state.m_j.Set(0,1,0);
state.m_j.Set(0,2,0);
state.m_j.Set(1,0,0);
state.m_j.Set(1,1,1);
state.m_j.Set(1,2,0);
state.m_j.Set(2,0,-1);
state.m_j.Set(2,1,0);
state.m_j.Set(2,2,1);
}
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]);
if(state.m_needfg || state.m_needfgh)
{
state.m_g.Set(0,2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2]));
state.m_g.Set(1,2*state.m_x[1]);
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
}
if(state.m_needfgh)
{
state.m_h.Set(0,0,4);
state.m_h.Set(0,1,0);
state.m_h.Set(0,2,-2);
state.m_h.Set(1,0,0);
state.m_h.Set(1,1,2);
state.m_h.Set(1,2,0);
state.m_h.Set(2,0,-2);
state.m_h.Set(2,1,0);
state.m_h.Set(2,2,2);
}
CAp::SetErrorFlag(statefieldsconsistencyflag,!RKindVSStateCheck(rkind,state),"testminlmunit.ap:212");
}
CMinLM::MinLMResults(state,x,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:215");
CAp::SetErrorFlag(errorflag,MathAbs(x[0]-2)>0.001,"testminlmunit.ap:216");
CAp::SetErrorFlag(errorflag,MathAbs(x[1])>0.001,"testminlmunit.ap:217");
CAp::SetErrorFlag(errorflag,MathAbs(x[2]-2)>0.001,"testminlmunit.ap:218");
}
//--- 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++)
{
x.Resize(1);
n=1;
m=1;
h=0.00001;
x.Set(0,100*CMath::RandomReal()-50);
switch(rkind)
{
case -2:
CMinLM::MinLMCreateV(n,m,x,h,state);
CMinLM::MinLMSetAccType(state,1);
break;
case -1:
CMinLM::MinLMCreateV(n,m,x,h,state);
CMinLM::MinLMSetAccType(state,0);
break;
case 0:
CMinLM::MinLMCreateFJ(n,m,x,state);
break;
case 1:
CMinLM::MinLMCreateFGJ(n,m,x,state);
break;
case 2:
CMinLM::MinLMCreateFGH(n,x,state);
break;
case 3:
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,0);
break;
case 4:
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,1);
break;
case 5:
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,2);
break;
}
while(CMinLM::MinLMIteration(state))
{
if(state.m_needfi)
state.m_fi.Set(0,MathSin(state.m_x[0]));
if(state.m_needfij)
{
state.m_fi.Set(0,MathSin(state.m_x[0]));
state.m_j.Set(0,0,MathCos(state.m_x[0]));
}
if(state.m_needf || state.m_needfg || state.m_needfgh)
state.m_f=CMath::Sqr(MathSin(state.m_x[0]));
if(state.m_needfg || state.m_needfgh)
state.m_g.Set(0,2*MathSin(state.m_x[0])*MathCos(state.m_x[0]));
if(state.m_needfgh)
state.m_h.Set(0,0,2*(MathCos(state.m_x[0])*MathCos(state.m_x[0])-MathSin(state.m_x[0])*MathSin(state.m_x[0])));
CAp::SetErrorFlag(statefieldsconsistencyflag,!RKindVSStateCheck(rkind,state),"testminlmunit.ap:281");
}
CMinLM::MinLMResults(state,x,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:284");
CAp::SetErrorFlag(errorflag,MathAbs(x[0]/M_PI-(int)MathRound(x[0]/M_PI))>0.001,"testminlmunit.ap:285");
}
//--- Linear equations: test normal optimization and optimization with restarts
for(n=1; n<=10; n++)
{
//--- Prepare task
h=0.00001;
CMatGen::RMatrixRndCond(n,100,a);
x.Resize(n);
xe.Resize(n);
b.Resize(n);
for(i=0; i<n; i++)
xe.Set(i,2*CMath::RandomReal()-1);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xe,a,i);
b.Set(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; i++)
x.Set(i,2*CMath::RandomReal()-1);
switch(rkind)
{
case -2:
CMinLM::MinLMCreateV(n,n,x,h,state);
CMinLM::MinLMSetAccType(state,1);
break;
case -1:
CMinLM::MinLMCreateV(n,n,x,h,state);
CMinLM::MinLMSetAccType(state,0);
break;
case 0:
CMinLM::MinLMCreateFJ(n,n,x,state);
break;
case 1:
CMinLM::MinLMCreateFGJ(n,n,x,state);
break;
case 2:
CMinLM::MinLMCreateFGH(n,x,state);
break;
case 3:
CMinLM::MinLMCreateVJ(n,n,x,state);
CMinLM::MinLMSetAccType(state,0);
break;
case 4:
CMinLM::MinLMCreateVJ(n,n,x,state);
CMinLM::MinLMSetAccType(state,1);
break;
case 5:
CMinLM::MinLMCreateVJ(n,n,x,state);
CMinLM::MinLMSetAccType(state,2);
break;
}
while(CMinLM::MinLMIteration(state))
{
AXMB(state,a,b,n);
CAp::SetErrorFlag(statefieldsconsistencyflag,!RKindVSStateCheck(rkind,state),"testminlmunit.ap:357");
}
CMinLM::MinLMResults(state,x,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:360");
if(errorflag)
return;
//---
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,MathAbs(x[i]-xe[i])>0.001,"testminlmunit.ap:364");
//--- Now we try to restart algorithm from new point
for(i=0; i<n; i++)
x.Set(i,2*CMath::RandomReal()-1);
CMinLM::MinLMRestartFrom(state,x);
while(CMinLM::MinLMIteration(state))
{
AXMB(state,a,b,n);
CAp::SetErrorFlag(statefieldsconsistencyflag,!RKindVSStateCheck(rkind,state),"testminlmunit.ap:375");
}
CMinLM::MinLMResults(state,x,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:378");
if(errorflag)
return;
//---
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,MathAbs(x[i]-xe[i])>0.001,"testminlmunit.ap:382");
}
}
//--- 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<=1; ckind++)
{
eps=0;
epsx=0;
maxits=0;
if(ckind==0)
{
epsx=1.0E-6;
eps=1.0E-4;
}
if(ckind==1)
{
maxits=2;
eps=0.05;
}
x.Resize(3);
n=3;
m=3;
for(i=0; i<=2; i++)
x.Set(i,6);
if(!CAp::Assert(rkind!=2))
return;
switch(rkind)
{
case 0:
CMinLM::MinLMCreateFJ(n,m,x,state);
break;
case 1:
CMinLM::MinLMCreateFGJ(n,m,x,state);
break;
case 3:
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,0);
break;
case 4:
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,1);
break;
case 5:
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,2);
break;
}
CMinLM::MinLMSetCond(state,epsx,maxits);
while(CMinLM::MinLMIteration(state))
{
if(state.m_needfi || state.m_needfij)
{
state.m_fi.Set(0,s*(MathExp(state.m_x[0])-2));
state.m_fi.Set(1,CMath::Sqr(state.m_x[1])+1);
state.m_fi.Set(2,state.m_x[2]-state.m_x[0]);
}
if(state.m_needfij)
{
state.m_j.Set(0,0,s*MathExp(state.m_x[0]));
state.m_j.Set(0,1,0);
state.m_j.Set(0,2,0);
state.m_j.Set(1,0,0);
state.m_j.Set(1,1,2*state.m_x[1]);
state.m_j.Set(1,2,0);
state.m_j.Set(2,0,-1);
state.m_j.Set(2,1,0);
state.m_j.Set(2,2,1);
}
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]);
}
if(state.m_needfg || state.m_needfgh)
{
state.m_g.Set(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.Set(1,2*(CMath::Sqr(state.m_x[1])+1)*2*state.m_x[1]);
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
}
if(state.m_needfgh)
{
state.m_h.Set(0,0,s*(4*CMath::Sqr(MathExp(state.m_x[0]))-4*MathExp(state.m_x[0]))+2);
state.m_h.Set(0,1,0);
state.m_h.Set(0,2,-2);
state.m_h.Set(1,0,0);
state.m_h.Set(1,1,12*CMath::Sqr(state.m_x[1])+4);
state.m_h.Set(1,2,0);
state.m_h.Set(2,0,-2);
state.m_h.Set(2,1,0);
state.m_h.Set(2,2,2);
}
CAp::SetErrorFlag(statefieldsconsistencyflag,!RKindVSStateCheck(rkind,state),"testminlmunit.ap:486");
}
CMinLM::MinLMResults(state,x,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:489");
if(errorflag)
return;
//---
if(ckind==0)
{
CAp::SetErrorFlag(errorflag,MathAbs(x[0]-MathLog(2))>eps,"testminlmunit.ap:494");
CAp::SetErrorFlag(errorflag,MathAbs(x[1])>eps,"testminlmunit.ap:495");
CAp::SetErrorFlag(errorflag,MathAbs(x[2]-MathLog(2))>eps,"testminlmunit.ap:496");
CAp::SetErrorFlag(errorflag,rep.m_terminationtype!=2,"testminlmunit.ap:497");
}
if(ckind==1)
{
CAp::SetErrorFlag(errorflag,rep.m_terminationtype!=5,"testminlmunit.ap:501");
CAp::SetErrorFlag(errorflag,rep.m_iterationscount!=maxits,"testminlmunit.ap:502");
}
}
}
}
//+------------------------------------------------------------------+
//| Test for box constrained problems. |
//| On failure sets error flag, leaves it unchanged on success. |
//+------------------------------------------------------------------+
void CTestMinLMUnit::TestBC(bool &errorflag)
{
CMinLMState state;
CMinLMReport rep;
CRowDouble bl;
CRowDouble bu;
int n=0;
int m=0;
CRowDouble x0;
CRowDouble x;
CRowDouble xe;
CRowDouble x1;
CRowDouble d;
CMatrixDouble c;
int i=0;
int j=0;
double v=0;
double h=0;
int tmpkind=0;
CHighQualityRandState rs;
double epsx=0;
double tolf=0;
double f0=0;
double f1=0;
CHighQualityRand::HQRndRandomize(rs);
//--- Reference box 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++)
{
bl.Resize(n);
bu.Resize(n);
xe.Resize(n);
x.Resize(n);
for(i=0; i<n; i++)
{
bl.Set(i,0);
bu.Set(i,1);
xe.Set(i,3*CMath::RandomReal()-1);
x.Set(i,CMath::RandomReal());
}
switch(tmpkind)
{
case 0:
CMinLM::MinLMCreateFGH(n,x,state);
break;
case 1:
CMinLM::MinLMCreateV(n,n,x,1.0E-5,state);
break;
}
CMinLM::MinLMSetCond(state,1.0E-6,0);
CMinLM::MinLMSetBC(state,bl,bu);
while(CMinLM::MinLMIteration(state))
{
if(state.m_needfi)
state.m_fi=MathPow(state.m_x.ToVector()-xe.ToVector(),2.0);
if(state.m_needf || state.m_needfg || state.m_needfgh)
state.m_f=MathPow(state.m_x.ToVector()-xe.ToVector(),4.0).Sum();
if(state.m_needfg || state.m_needfgh)
state.m_g=MathPow(state.m_x.ToVector()-xe.ToVector(),3.0)*4.0;
if(state.m_needfgh)
{
state.m_h=matrix<double>::Zeros(n,n);
state.m_h.Diag(MathPow(state.m_x.ToVector()-xe.ToVector(),2.0)*12);
}
}
CMinLM::MinLMResults(state,x,rep);
if(rep.m_terminationtype>0)
{
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,MathAbs(x[i]-CApServ::BoundVal(xe[i],bl[i],bu[i]))>(double)(5.0E-2),"testminlmunit.ap:594");
}
else
CAp::SetErrorFlag(errorflag,true,"testminlmunit.ap:597");
}
}
//--- Minimize
//--- [ [ ]2 ]
//--- SUM_i[ SUM_j[ power(x_j,3)*c_ij ] ]
//--- [ [ ] ]
//--- subject to non-negativity constraints on x_j
epsx=1.0E-9;
tolf=1.0E-10;
for(tmpkind=0; tmpkind<=1; tmpkind++)
{
for(n=1; n<=20; n++)
{
m=n+CHighQualityRand::HQRndUniformI(rs,n);
bl.Resize(n);
bu.Resize(n);
x0.Resize(n);
for(i=0; i<n; i++)
{
bl.Set(i,0);
bu.Set(i,AL_POSINF);
x0.Set(i,1+CHighQualityRand::HQRndUniformR(rs));
}
c.Resize(m,n+1);
for(i=0; i<m; i++)
{
for(j=0; j<=n; j++)
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
}
if(tmpkind==0)
CMinLM::MinLMCreateV(n,m,x0,10*epsx,state);
if(tmpkind==1)
CMinLM::MinLMCreateVJ(n,m,x0,state);
CMinLM::MinLMSetCond(state,epsx,0);
CMinLM::MinLMSetBC(state,bl,bu);
while(CMinLM::MinLMIteration(state))
{
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(errorflag,state.m_x[i]<bl[i],"testminlmunit.ap:639");
CAp::SetErrorFlag(errorflag,state.m_x[i]>bu[i],"testminlmunit.ap:640");
}
if(state.m_needfi)
{
TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,false);
continue;
}
if(state.m_needfij)
{
TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,true);
continue;
}
//--- check
if(!CAp::Assert(false,"MinLM test: integrity check failed"))
return;
}
CMinLM::MinLMResults(state,x,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:655");
if(errorflag)
return;
//---
TestFunc1(n,m,c,x,f0,true,state.m_fi,false,state.m_j,false);
x1.Resize(n);
h=0.001;
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(errorflag,x[i]<bl[i],"testminlmunit.ap:664");
CAp::SetErrorFlag(errorflag,x[i]>bu[i],"testminlmunit.ap:665");
if((x[i]+h)>=bl[i])
{
x1=x;
x1.Set(i,x[i]+h);
TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false);
CAp::SetErrorFlag(errorflag,f1<(double)(f0*(1-tolf)),"testminlmunit.ap:674");
}
if((x[i]-h)>=bl[i])
{
x1=x;
x1.Set(i,x[i]-h);
TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false);
CAp::SetErrorFlag(errorflag,f1<(double)(f0*(1-tolf)),"testminlmunit.ap:684");
}
}
}
}
//--- Minimize
//--- [ [ ]2 ]
//--- SUM_i[ SUM_j[ power(x_j,3)*c_ij ] ]
//--- [ [ ] ]
//--- subject to random box constraints on x_j
epsx=1.0E-9;
tolf=1.0E-10;
for(tmpkind=0; tmpkind<=1; tmpkind++)
{
for(n=1; n<=20; n++)
{
m=n+CHighQualityRand::HQRndUniformI(rs,n);
bl.Resize(n);
bu.Resize(n);
x0.Resize(n);
for(i=0; i<n; i++)
{
bl.Set(i,CHighQualityRand::HQRndNormal(rs));
bu.Set(i,bl[i]+CHighQualityRand::HQRndUniformR(rs));
x0.Set(i,1+CHighQualityRand::HQRndUniformR(rs));
}
c.Resize(m,n+1);
for(i=0; i<m; i++)
{
for(j=0; j<=n; j++)
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
}
if(tmpkind==0)
CMinLM::MinLMCreateV(n,m,x0,10*epsx,state);
if(tmpkind==1)
CMinLM::MinLMCreateVJ(n,m,x0,state);
CMinLM::MinLMSetCond(state,epsx,0);
CMinLM::MinLMSetBC(state,bl,bu);
while(CMinLM::MinLMIteration(state))
{
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(errorflag,state.m_x[i]<bl[i],"testminlmunit.ap:728");
CAp::SetErrorFlag(errorflag,state.m_x[i]>bu[i],"testminlmunit.ap:729");
}
if(state.m_needfi)
{
TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,false);
continue;
}
if(state.m_needfij)
{
TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,true);
continue;
}
//--- check
if(!CAp::Assert(false,"minlm test: integrity check failed"))
return;
}
CMinLM::MinLMResults(state,x,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:744");
if(errorflag)
return;
//---
TestFunc1(n,m,c,x,f0,true,state.m_fi,false,state.m_j,false);
x1.Resize(n);
h=0.001;
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(errorflag,x[i]<bl[i],"testminlmunit.ap:753");
CAp::SetErrorFlag(errorflag,x[i]>bu[i],"testminlmunit.ap:754");
if((x[i]+h)>=bl[i] && (x[i]+h)<=bu[i])
{
x1=x;
x1.Set(i,x[i]+h);
TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false);
CAp::SetErrorFlag(errorflag,f1<(f0*(1-tolf)),"testminlmunit.ap:763");
}
if((x[i]-h)>=bl[i] && (x[i]-h)<=bu[i])
{
x1=x;
x1.Set(i,x[i]-h);
TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false);
CAp::SetErrorFlag(errorflag,f1<(f0*(1-tolf)),"testminlmunit.ap:773");
}
}
}
}
}
//+------------------------------------------------------------------+
//| Test for linearly constrained problems. |
//| On failure sets error flag, leaves it unchanged on success. |
//+------------------------------------------------------------------+
void CTestMinLMUnit::TestLC(bool &errorflag)
{
CMinLMState state;
CMinLMReport rep;
CRowDouble bl;
CRowDouble bu;
int n=0;
int m=0;
int m1=0;
int m2=0;
CRowDouble x0;
CRowDouble x;
CRowDouble xe;
CRowDouble x1;
CRowDouble x12;
CRowDouble d;
CMatrixDouble rawc;
CRowInt rawct;
int rawccnt=0;
CMatrixDouble c;
CMatrixDouble c12;
CMatrixDouble z;
int trialidx=0;
int i=0;
int j=0;
double v=0;
double h=0;
int optkind=0;
CHighQualityRandState rs;
double epsx=0;
double xtol=0;
double tolf=0;
double f0=0;
double f1=0;
bool bflag=false;
int i_=0;
CHighQualityRand::HQRndRandomize(rs);
//--- Minimize
//--- [ [ ]2 ]
//--- SUM_i[ SUM_j[ (0.1*x_j+power(x_j,3))*c_ij ] ]
//--- [ [ ] ]
//--- subject to mix of box and linear inequality constraints on x_j
//--- We check correctness of solution by sampling a few random points
//--- around one returned by optimizer, and comparing function value
//--- with target. Sampling is performed with respect to inequality
//--- constraints.
epsx=1.0E-12;
xtol=1.0E-8;
tolf=1.0E-10;
for(optkind=0; optkind<=1; optkind++)
{
for(n=5; n<=20; n++)
{
//--- Generate problem
m=n+CHighQualityRand::HQRndUniformI(rs,n);
bl.Resize(n);
bu.Resize(n);
x0.Resize(n);
for(i=0; i<n; i++)
{
bl.Set(i,CHighQualityRand::HQRndNormal(rs));
bu.Set(i,bl[i]+CHighQualityRand::HQRndUniformR(rs));
x0.Set(i,bl[i]+(bu[i]-bl[i])*CHighQualityRand::HQRndUniformR(rs));
}
c.Resize(m,n+1);
for(i=0; i<m; i++)
{
for(j=0; j<=n; j++)
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
}
//--- check
if(!CAp::Assert(n>=5))
return;
rawccnt=3;
rawc.Resize(rawccnt,n+1);
rawct.Resize(rawccnt);
for(i=0; i<rawccnt; i++)
{
v=0;
for(j=0; j<n; j++)
{
rawc.Set(i,j,CHighQualityRand::HQRndNormal(rs));
v+=x0[j]*rawc.Get(i,j);
}
rawc.Set(i,n,v);
rawct.Set(i,2*CHighQualityRand::HQRndUniformI(rs,2)-1);
}
//--- Solve
if(optkind==0)
CMinLM::MinLMCreateV(n,m,x0,10*epsx,state);
if(optkind==1)
CMinLM::MinLMCreateVJ(n,m,x0,state);
CMinLM::MinLMSetCond(state,epsx,0);
CMinLM::MinLMSetBC(state,bl,bu);
CMinLM::MinLMSetLC(state,rawc,rawct,rawccnt);
while(CMinLM::MinLMIteration(state))
{
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(errorflag,state.m_x[i]<bl[i],"testminlmunit.ap:878");
CAp::SetErrorFlag(errorflag,state.m_x[i]>bu[i],"testminlmunit.ap:879");
}
if(state.m_needfi)
{
TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,false);
continue;
}
if(state.m_needfij)
{
TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,true);
continue;
}
//--- check
if(!CAp::Assert(false,"MinLM test: integrity check failed"))
return;
}
CMinLM::MinLMResults(state,x,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:894");
if(errorflag)
return;
//--- Test feasibility w.r.t. box and linear inequality constraints
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(errorflag,x[i]<bl[i],"testminlmunit.ap:903");
CAp::SetErrorFlag(errorflag,x[i]>bu[i],"testminlmunit.ap:904");
}
for(i=0; i<=rawccnt-1; i++)
{
v=CAblasF::RDotVR(n,x,rawc,i);
v-=rawc.Get(i,n);
if(rawct[i]>0)
CAp::SetErrorFlag(errorflag,v<(-xtol),"testminlmunit.ap:911");
if(rawct[i]<0)
CAp::SetErrorFlag(errorflag,v>xtol,"testminlmunit.ap:913");
}
//--- Make several random trial steps and:
//--- 0) generate small random trial step
//--- 1) if step is infeasible, skip to next trial
//--- 2) compare function value in the trial point against one in other points
TestFunc1(n,m,c,x,f0,true,state.m_fi,false,state.m_j,false);
x1.Resize(n);
for(trialidx=0; trialidx<=10*n; trialidx++)
{
h=0.001;
for(i=0; i<n; i++)
{
do
{
x1.Set(i,x[i]+(CHighQualityRand::HQRndUniformR(rs)*2-1)*h);
}
while(!(x1[i]>=bl[i] && x1[i]<=bu[i]));
}
bflag=false;
for(i=0; i<rawccnt; i++)
{
//--- check
if(!CAp::Assert(rawct[i]!=0))
return;
v=CAblasF::RDotVR(n,x1,rawc,i)-rawc.Get(i,n);
bflag=bflag||(rawct[i]>0 && v<0.0);
bflag=bflag||(rawct[i]<0 && v>0.0);
}
if(bflag)
continue;
//---
TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false);
CAp::SetErrorFlag(errorflag,f1<(double)(f0*(1-tolf)),"testminlmunit.ap:950");
}
}
}
//--- Minimize
//--- [ [ ]2 ]
//--- SUM_i[ SUM_j[ (0.1*x_j+power(x_j,3))*c_ij ] ]
//--- [ [ ] ]
//--- subject to linear EQUALITY constraints on x_j.
//--- We check correctness of solution by sampling a few random points
//--- around one returned by optimizer, and comparing function value
//--- with target. Sampling is performed with respect to equality
//--- constraints. In order to simplify algorithm we use orthogonal
//--- equality constraints.
//--- NOTE: we solve problem using VJ mode (analytic Jacobian) because
//--- roundoff errors from numerical differentiation sometimes
//--- prevent us from converging with good precision.
epsx=1.0E-12;
xtol=1.0E-8;
tolf=1.0E-10;
optkind=1;
for(n=10; n<=20; n++)
{
//--- Generate problem
m=n+CHighQualityRand::HQRndUniformI(rs,n);
x0.Resize(n);
for(i=0; i<n; i++)
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
c.Resize(m,n+1);
for(i=0; i<m; i++)
{
for(j=0; j<=n; j++)
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
}
//--- check
if(!CAp::Assert(n>=5))
return;
rawccnt=1+CHighQualityRand::HQRndUniformI(rs,5);
CMatGen::RMatrixRndOrthogonal(n,z);
rawc.Resize(rawccnt,n+1);
rawct.Resize(rawccnt);
rawct.Fill(0);
for(i=0; i<rawccnt; i++)
{
v=0;
for(j=0; j<n; j++)
{
rawc.Set(i,j,z.Get(i,j));
v+=x0[j]*rawc.Get(i,j);
}
rawc.Set(i,n,v);
}
//--- Solve
if(optkind==0)
CMinLM::MinLMCreateV(n,m,x0,1.0E-12,state);
if(optkind==1)
CMinLM::MinLMCreateVJ(n,m,x0,state);
CMinLM::MinLMSetCond(state,epsx,0);
CMinLM::MinLMSetLC(state,rawc,rawct,rawccnt);
while(CMinLM::MinLMIteration(state))
{
if(state.m_needfi)
{
TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,false);
continue;
}
if(state.m_needfij)
{
TestFunc1(n,m,c,state.m_x,v,false,state.m_fi,true,state.m_j,true);
continue;
}
//--- check
if(!CAp::Assert(false,"MinLM Test: integrity check failed"))
return;
}
CMinLM::MinLMResults(state,x,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:1031");
if(errorflag)
return;
//--- Test feasibility w.r.t. linear equality constraints
for(i=0; i<rawccnt; i++)
{
v=CAblasF::RDotVR(n,x,rawc,i)-rawc.Get(i,n);
CAp::SetErrorFlag(errorflag,MathAbs(v)>xtol,"testminlmunit.ap:1042");
}
//--- Make several random trial steps and:
//--- 0) generate small random trial step
//--- 1) project it onto equality constrained subspace
//--- 2) compare function value in the trial point against one in other points
TestFunc1(n,m,c,x,f0,true,state.m_fi,false,state.m_j,false);
x1.Resize(n);
for(trialidx=0; trialidx<=10*n; trialidx++)
{
h=0.001;
for(i=0; i<n; i++)
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
for(i=0; i<rawccnt; i++)
{
v=CAblasF::RDotVR(n,x1,rawc,i);
for(i_=0; i_<n; i_++)
x1.Add(i_,- v*rawc.Get(i,i_));
}
v=x1.Dot(x1);
//--- check
if(!CAp::Assert(v>0.0))
return;
v=h/MathSqrt(v);
x1*=v;
x1+=x;
TestFunc1(n,m,c,x1,f1,true,state.m_fi,false,state.m_j,false);
CAp::SetErrorFlag(errorflag,f1<(f0*(1-tolf)),"testminlmunit.ap:1072");
}
}
//--- Minimize
//--- [ [ ]2 ] [ [ ]2 ]
//--- SUM_i[ SUM_j[ (0.1*x_j+power(x0_j,3))*c0_ij ] ] + SUM_i[ SUM_j[ (0.1*x_j+power(x1_j,3))*c1_ij ] ]
//--- [ [ ] ] [ [ ] ]
//--- for two sets of unknowns (x0_j and x1_j) and two sets of
//--- coefficients (c0_ij and c1_ij, M1*N and M2*N matrices) subject
//--- to equality constraint
//--- x0_j=x1_j for all j
//--- Such optimization problem arises when we fit same model to
//--- two distinct datasets and want to share SOME of coefficients
//--- between fits. If we share ALL coefficients, it is equal to
//--- fitting one model to combination of two datasets.
//--- Our test checks that such "combined" 2N-dimensional problem
//--- solved with general linear constraints which "glue" two datasets
//--- together returns same answer as N-dimensional problem on (M1+M2)-point
//--- dataset.
//--- NOTE: we solve problem using VJ mode (analytic Jacobian) because
//--- roundoff errors from numerical differentiation prevent us
//--- from converging with good precision.
epsx=1.0E-12;
for(n=5; n<=20; n++)
{
//--- Generate problems
m1=n+CHighQualityRand::HQRndUniformI(rs,n);
m2=n+CHighQualityRand::HQRndUniformI(rs,n);
x0.Resize(n);
x12.Resize(2*n);
for(i=0; i<n; i++)
{
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
x12.Set(2*i+0,CHighQualityRand::HQRndNormal(rs));
x12.Set(2*i+1,CHighQualityRand::HQRndNormal(rs));
}
c.Resize(m1+m2,n+1);
c12=matrix<double>::Zeros(m1+m2,2*n+1);
for(i=0; i<=m1+m2-1; i++)
{
for(j=0; j<n; j++)
{
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
if(i<m1)
c12.Set(i,j,c.Get(i,j));
else
c12.Set(i,n+j,c.Get(i,j));
}
c.Set(i,n,CHighQualityRand::HQRndNormal(rs));
c12.Set(i,2*n,c.Get(i,n));
}
rawccnt=n;
rawc=matrix<double>::Identity(rawccnt,2*n+1);
rawct.Resize(rawccnt);
rawct.Fill(0);
rawc.Diag(vector<double>::Full(n,-1),n);
//--- Solve N-dimensional "combined" problem, store result to X1
CMinLM::MinLMCreateVJ(n,m1+m2,x0,state);
CMinLM::MinLMSetCond(state,epsx,0);
while(CMinLM::MinLMIteration(state))
{
if(state.m_needfi)
{
TestFunc1(n,m1+m2,c,state.m_x,v,false,state.m_fi,true,state.m_j,false);
continue;
}
if(state.m_needfij)
{
TestFunc1(n,m1+m2,c,state.m_x,v,false,state.m_fi,true,state.m_j,true);
continue;
}
//--- check
if(!CAp::Assert(false,"MinLM Test: integrity check failed"))
return;
}
CMinLM::MinLMResults(state,x,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:1170");
if(errorflag)
return;
//---
//--- Solve N-dimensional "glued" problem, store result to X12
CMinLM::MinLMCreateVJ(2*n,m1+m2,x12,state);
CMinLM::MinLMSetCond(state,epsx,0);
CMinLM::MinLMSetLC(state,rawc,rawct,rawccnt);
while(CMinLM::MinLMIteration(state))
{
if(state.m_needfi)
{
TestFunc1(2*n,m1+m2,c12,state.m_x,v,false,state.m_fi,true,state.m_j,false);
continue;
}
if(state.m_needfij)
{
TestFunc1(2*n,m1+m2,c12,state.m_x,v,false,state.m_fi,true,state.m_j,true);
continue;
}
//--- check
if(!CAp::Assert(false,"minlm test: integrity check failed"))
return;
}
CMinLM::MinLMResults(state,x12,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:1195");
if(errorflag)
return;
//---
//--- Compare solutions
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(errorflag,MathAbs(x[i]-x12[i])>1.0E-3,"testminlmunit.ap:1204");
CAp::SetErrorFlag(errorflag,MathAbs(x[i]-x12[n+i])>1.0E-3,"testminlmunit.ap:1205");
CAp::SetErrorFlag(errorflag,MathAbs(x12[i]-x12[n+i])>1.0E-6,"testminlmunit.ap:1206");
}
}
}
//+------------------------------------------------------------------+
//| Test other properties |
//| On failure sets error flag, leaves it unchanged on success. |
//+------------------------------------------------------------------+
void CTestMinLMUnit::TestOther(bool &errorflag,bool &statefieldsconsistencyflag)
{
CMinLMState state;
CMinLMReport rep;
int n=0;
int m=0;
double v=0;
double s=0;
CRowDouble x;
CRowDouble xlast;
int i=0;
int j=0;
int rkind=0;
double fprev=0;
double xprev=0;
double stpmax=0;
int stopcallidx=0;
int callidx=0;
int maxits=0;
bool terminationrequested=false;
int pass=0;
int spoilcnt=0;
double mx=0;
int i_=0;
//--- 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;
x.Resize(n);
xlast.Resize(n);
for(i=0; i<n; i++)
x.Set(i,6+CMath::RandomReal());
switch(rkind)
{
case 0:
CMinLM::MinLMCreateFJ(n,m,x,state);
break;
case 1:
CMinLM::MinLMCreateFGJ(n,m,x,state);
break;
case 2:
CMinLM::MinLMCreateFGH(n,x,state);
break;
case 3:
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,0);
break;
case 4:
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,1);
break;
case 5:
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetAccType(state,2);
break;
}
CMinLM::MinLMSetCond(state,0,4);
CMinLM::MinLMSetXRep(state,true);
fprev=CMath::m_maxrealnumber;
while(CMinLM::MinLMIteration(state))
{
if(state.m_needfi || state.m_needfij)
{
state.m_fi.Set(0,MathSqrt(s)*(MathExp(state.m_x[0])-2));
state.m_fi.Set(1,state.m_x[1]);
state.m_fi.Set(2,state.m_x[2]-state.m_x[0]);
}
if(state.m_needfij)
{
state.m_j.Set(0,0,MathSqrt(s)*MathExp(state.m_x[0]));
state.m_j.Set(0,1,0);
state.m_j.Set(0,2,0);
state.m_j.Set(1,0,0);
state.m_j.Set(1,1,1);
state.m_j.Set(1,2,0);
state.m_j.Set(2,0,-1);
state.m_j.Set(2,1,0);
state.m_j.Set(2,2,1);
}
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]);
if(state.m_needfg || state.m_needfgh)
{
state.m_g.Set(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.Set(1,2*state.m_x[1]);
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
}
if(state.m_needfgh)
{
state.m_h.Set(0,0,s*(4*CMath::Sqr(MathExp(state.m_x[0]))-4*MathExp(state.m_x[0]))+2);
state.m_h.Set(0,1,0);
state.m_h.Set(0,2,-2);
state.m_h.Set(1,0,0);
state.m_h.Set(1,1,2);
state.m_h.Set(1,2,0);
state.m_h.Set(2,0,-2);
state.m_h.Set(2,1,0);
state.m_h.Set(2,2,2);
}
CAp::SetErrorFlag(statefieldsconsistencyflag,!RKindVSStateCheck(rkind,state),"testminlmunit.ap:1320");
if(state.m_xupdated)
{
CAp::SetErrorFlag(errorflag,state.m_f>fprev,"testminlmunit.ap:1323");
if(fprev==CMath::m_maxrealnumber)
{
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,state.m_x[i]!=x[i],"testminlmunit.ap:1326");
}
fprev=state.m_f;
xlast=state.m_x;
}
}
CMinLM::MinLMResults(state,x,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminlmunit.ap:1332");
if(errorflag)
return;
//---
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,x[i]!=xlast[i],"testminlmunit.ap:1336");
}
n=1;
x=vector<double>::Zeros(n);
x.Set(0,100);
stpmax=0.05+0.05*CMath::RandomReal();
CMinLM::MinLMCreateFGH(n,x,state);
CMinLM::MinLMSetCond(state,1.0E-12,0);
CMinLM::MinLMSetStpMax(state,stpmax);
CMinLM::MinLMSetXRep(state,true);
xprev=x[0];
while(CMinLM::MinLMIteration(state))
{
if((state.m_needf || state.m_needfg) || state.m_needfgh)
state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]);
if(state.m_needfg || state.m_needfgh)
state.m_g.Set(0,MathExp(state.m_x[0])-MathExp(-state.m_x[0]));
if(state.m_needfgh)
state.m_h.Set(0,0,MathExp(state.m_x[0])+MathExp(-state.m_x[0]));
CAp::SetErrorFlag(errorflag,MathAbs(state.m_x[0]-xprev)>((1+MathSqrt(CMath::m_machineepsilon))*stpmax),"testminlmunit.ap:1355");
if(state.m_xupdated)
xprev=state.m_x[0];
}
//--- Check algorithm ability to handle request for termination:
//--- * to terminate with correct return code = 8
//---*to return point which was "current" at the moment of termination
for(pass=1; pass<=50; pass++)
{
n=3;
m=3;
s=100;
x=vector<double>::Zeros(n);
xlast=vector<double>::Zeros(n);
for(i=0; i<n; i++)
x.Set(i,6+CMath::RandomReal());
stopcallidx=CMath::RandomInteger(20);
maxits=25;
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetCond(state,0,maxits);
CMinLM::MinLMSetXRep(state,true);
callidx=0;
terminationrequested=false;
xlast=x;
while(CMinLM::MinLMIteration(state))
{
if(state.m_needfi || state.m_needfij)
{
state.m_fi.Set(0,MathSqrt(s)*(MathExp(state.m_x[0])-2));
state.m_fi.Set(1,state.m_x[1]);
state.m_fi.Set(2,state.m_x[2]-state.m_x[0]);
if(state.m_needfij)
{
state.m_j.Set(0,0,MathSqrt(s)*MathExp(state.m_x[0]));
state.m_j.Set(0,1,0);
state.m_j.Set(0,2,0);
state.m_j.Set(1,0,0);
state.m_j.Set(1,1,1);
state.m_j.Set(1,2,0);
state.m_j.Set(2,0,-1);
state.m_j.Set(2,1,0);
state.m_j.Set(2,2,1);
}
if(callidx==stopcallidx)
{
CMinLM::MinLMRequestTermination(state);
terminationrequested=true;
}
callidx++;
continue;
}
if(state.m_xupdated)
{
if(!terminationrequested)
xlast=state.m_x;
continue;
}
//--- check
if(!CAp::Assert(false))
return;
}
CMinLM::MinLMResults(state,x,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype!=8,"testminlmunit.ap:1419");
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,x[i]!=(double)(xlast[i]),"testminlmunit.ap:1421");
}
//--- Test ability to detect NAN/INF values.
//--- We use Rosenbrock's function which is modified to return
//--- NAN/INF (randomly) when near solution. Algorithm should set
//--- appropriate error code (-8) on exit.
n=10;
m=2*(n-1);
s=10;
x=vector<double>::Zeros(n);
CMinLM::MinLMCreateVJ(n,m,x,state);
CMinLM::MinLMSetCond(state,1.0E-12,0);
spoilcnt=0;
while(CMinLM::MinLMIteration(state))
{
if(state.m_needfij)
{
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
state.m_j.Set(i,j,0);
}
for(i=0; i<n-1; i++)
{
state.m_fi.Set(2*i+0,s*(state.m_x[i+1]-CMath::Sqr(state.m_x[i])));
state.m_j.Set(2*i+0,i,-(s*2*state.m_x[i]));
state.m_j.Set(2*i+0,i+1,s);
state.m_fi.Set(2*i+1,1-state.m_x[i]);
state.m_j.Set(2*i+1,i,-1);
}
mx=MathAbs(state.m_x.ToVector()-1.0).Max();
if(mx<1.0E-2)
{
i=CMath::RandomInteger(3);
v=AL_NaN;
if(i==1)
v=AL_POSINF;
if(i==2)
v=AL_NEGINF;
if(CMath::RandomReal()>0.5)
state.m_fi.Set(CMath::RandomInteger(m),v);
else
state.m_j.Set(CMath::RandomInteger(m),CMath::RandomInteger(n),v);
spoilcnt++;
}
continue;
}
if(state.m_needfi)
{
for(i=0; i<n-1; i++)
{
state.m_fi.Set(2*i+0,s*(state.m_x[i+1]-CMath::Sqr(state.m_x[i])));
state.m_fi.Set(2*i+1,1-state.m_x[i]);
}
mx=MathAbs(state.m_x.ToVector()-1.0).Max();
if(mx<1.0E-2)
{
i=CMath::RandomInteger(3);
v=AL_NaN;
if(i==1)
v=AL_POSINF;
if(i==2)
v=AL_NEGINF;
state.m_fi.Set(CMath::RandomInteger(m),v);
spoilcnt++;
}
continue;
}
//--- check
if(!CAp::Assert(false))
return;
}
CMinLM::MinLMResults(state,x,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype!=-8,"testminlmunit.ap:1513");
CAp::SetErrorFlag(errorflag,spoilcnt!=1,"testminlmunit.ap:1514");
}
//+------------------------------------------------------------------+
//| This function tests OptGuard |
//| On failure sets error flag. |
//+------------------------------------------------------------------+
void CTestMinLMUnit::TestOptGuard(bool &waserrors)
{
CHighQualityRandState rs;
double v=0;
CMinLMState state;
CMinLMReport rep;
COptGuardReport ogrep;
int i=0;
int j=0;
int n=0;
CMatrixDouble a;
CMatrixDouble a1;
CRowDouble s;
CRowDouble x0;
CRowDouble x1;
CRowDouble bndl;
CRowDouble bndu;
double diffstep=0;
int defecttype=0;
int funcidx=0;
int varidx=0;
int skind=0;
CMatrixDouble jactrue;
CMatrixDouble jacdefect;
CHighQualityRand::HQRndRandomize(rs);
//--- Check that gradient verification is disabled by default:
//--- gradient checking for bad problem must return nothing
n=10;
x0.Resize(n);
for(i=0; i<n; i++)
x0.Set(i,1.0+0.1*i);
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
CMatGen::SPDMatrixRndCond(n,1.0E3,a1);
CMinLM::MinLMCreateVJ(n,1,x0,state);
CMinLM::MinLMSetCond(state,1.0E-9,10);
while(CMinLM::MinLMIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,state.m_x,a,i);
state.m_fi.Add(0,0.5*state.m_x[i]*v);
}
state.m_j.Row(0,vector<double>::Zeros(n));
continue;
}
//--- check
if(!CAp::Assert(false))
return;
}
CMinLM::MinLMResults(state,x1,rep);
CMinLM::MinLMOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminlmunit.ap:1575");
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminlmunit.ap:1576");
if(waserrors)
return;
CAp::SetErrorFlag(waserrors,CAp::Len(ogrep.m_badgradxbase)!=0,"testminlmunit.ap:1579");
CAp::SetErrorFlag(waserrors,CAp::Rows(ogrep.m_badgraduser)!=0,"testminlmunit.ap:1580");
CAp::SetErrorFlag(waserrors,CAp::Cols(ogrep.m_badgraduser)!=0,"testminlmunit.ap:1581");
CAp::SetErrorFlag(waserrors,CAp::Rows(ogrep.m_badgradnum)!=0,"testminlmunit.ap:1582");
CAp::SetErrorFlag(waserrors,CAp::Cols(ogrep.m_badgradnum)!=0,"testminlmunit.ap:1583");
if(waserrors)
return;
CAp::SetErrorFlag(waserrors,ogrep.m_badgradsuspected,"testminlmunit.ap:1586");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=-1,"testminlmunit.ap:1587");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=-1,"testminlmunit.ap:1588");
//--- Test gradient checking functionality, try various
//--- defect types:
//--- * accidental zeroing of some gradient component
//--- * accidental addition of 1.0 to some component
//--- * accidental multiplication by 2.0
//--- Try distorting both target and constraints.
diffstep=0.001;
n=10;
for(skind=0; skind<=1; skind++)
{
for(funcidx=0; funcidx<=1; funcidx++)
{
for(defecttype=-1; defecttype<=2; defecttype++)
{
varidx=CHighQualityRand::HQRndUniformI(rs,n);
x0.Resize(n);
s.Resize(n);
bndl.Resize(n);
bndu.Resize(n);
for(i=0; i<n; i++)
{
s.Set(i,MathPow(10,skind*(30*CHighQualityRand::HQRndUniformR(rs)-15)));
x0.Set(i,(1.0+0.1*i)*s[i]);
j=CHighQualityRand::HQRndUniformI(rs,3);
bndl.Set(i,-(100*s[i]));
bndu.Set(i,100*s[i]);
if(j==1)
bndl.Set(i,x0[i]);
if(j==2)
bndu.Set(i,x0[i]);
}
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
CMatGen::SPDMatrixRndCond(n,1.0E3,a1);
CMinLM::MinLMCreateVJ(n,2,x0,state);
CMinLM::MinLMOptGuardGradient(state,diffstep);
CMinLM::MinLMSetCond(state,1.0E-9,10);
CMinLM::MinLMSetScale(state,s);
CMinLM::MinLMSetBC(state,bndl,bndu);
while(CMinLM::MinLMIteration(state))
{
if(state.m_needfi || state.m_needfij)
{
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(waserrors,state.m_x[i]<bndl[i],"testminlmunit.ap:1637");
CAp::SetErrorFlag(waserrors,state.m_x[i]>bndu[i],"testminlmunit.ap:1638");
}
state.m_fi.Set(0,0);
vector<double> xs=state.m_x/s;
for(i=0; i<n; i++)
{
v=xs.Dot(a[i]+0);
state.m_fi.Add(0,0.5*(state.m_x[i]/s[i])*v);
if(state.m_needfij)
state.m_j.Set(0,i,v);
}
state.m_fi.Set(1,0);
for(i=0; i<n; i++)
{
v=xs.Dot(a1[i]+0);
state.m_fi.Add(1,0.5*(state.m_x[i]/s[i])*v);
if(state.m_needfij)
state.m_j.Set(1,i,v);
}
if(state.m_needfij)
{
if(defecttype==0)
state.m_j.Set(funcidx,varidx,0);
if(defecttype==1)
state.m_j.Add(funcidx,varidx,1);
if(defecttype==2)
state.m_j.Mul(funcidx,varidx,2);
}
if(state.m_needfij)
{
state.m_j.Row(0,state.m_j[0]/s.ToVector());
state.m_j.Row(1,state.m_j[1]/s.ToVector());
}
continue;
}
//--- check
if(!CAp::Assert(false))
return;
}
CMinLM::MinLMResults(state,x1,rep);
CMinLM::MinLMOptGuardResults(state,ogrep);
//--- Check that something is returned
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminlmunit.ap:1695");
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminlmunit.ap:1696");
if(waserrors)
return;
//---
//--- Compute reference values for true and spoiled Jacobian at X0
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(ogrep.m_badgradxbase,n),"testminlmunit.ap:1703");
if(waserrors)
return;
//---
jactrue.Resize(2,n);
jacdefect.Resize(2,n);
vector<double> bs=ogrep.m_badgradxbase/s;
for(i=0; i<n; i++)
{
v=bs.Dot(a[i]+0);
jactrue.Set(0,i,v);
jacdefect.Set(0,i,v);
}
for(i=0; i<n; i++)
{
v=bs.Dot(a1[i]+0);
jactrue.Set(1,i,v);
jacdefect.Set(1,i,v);
}
if(defecttype==0)
jacdefect.Set(funcidx,varidx,0);
if(defecttype==1)
jacdefect.Add(funcidx,varidx,1);
if(defecttype==2)
jacdefect.Mul(funcidx,varidx,2);
jactrue.Row(0,jactrue[0]/s.ToVector());
jactrue.Row(1,jactrue[1]/s.ToVector());
jacdefect.Row(0,jacdefect[0]/s.ToVector());
jacdefect.Row(1,jacdefect[1]/s.ToVector());
}
//--- Check OptGuard report
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteMatrix(ogrep.m_badgraduser,2,n),"testminlmunit.ap:1741");
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteMatrix(ogrep.m_badgradnum,2,n),"testminlmunit.ap:1742");
if(waserrors)
return;
//---
if(defecttype>=0)
{
CAp::SetErrorFlag(waserrors,!ogrep.m_badgradsuspected,"testminlmunit.ap:1747");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=funcidx,"testminlmunit.ap:1748");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=varidx,"testminlmunit.ap:1749");
}
else
{
CAp::SetErrorFlag(waserrors,ogrep.m_badgradsuspected,"testminlmunit.ap:1753");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=-1,"testminlmunit.ap:1754");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=-1,"testminlmunit.ap:1755");
}
for(i=0; i<=1; i++)
{
for(j=0; j<n; j++)
{
CAp::SetErrorFlag(waserrors,MathAbs(jactrue.Get(i,j)-ogrep.m_badgradnum.Get(i,j))>(0.01/s[j]),"testminlmunit.ap:1760");
CAp::SetErrorFlag(waserrors,MathAbs(jacdefect.Get(i,j)-ogrep.m_badgraduser.Get(i,j))>(0.01/s[j]),"testminlmunit.ap:1761");
}
}
}
}
}
//+------------------------------------------------------------------+
//| 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 |
//+------------------------------------------------------------------+
bool CTestMinLMUnit::RKindVSStateCheck(int rkind,CMinLMState &state)
{
int nset=0;
//--- check
if(state.m_needfi)
nset++;
if(state.m_needf)
nset++;
if(state.m_needfg)
nset++;
if(state.m_needfij)
nset++;
if(state.m_needfgh)
nset++;
if(state.m_xupdated)
nset++;
//--- check
if(nset!=1)
return(false);
switch(rkind)
{
case -2:
return (state.m_needfi || state.m_xupdated);
case -1:
return (state.m_needfi || state.m_xupdated);
case 0:
return (state.m_needf || state.m_needfij || state.m_xupdated);
case 1:
return (state.m_needf || state.m_needfij || state.m_needfg || state.m_xupdated);
case 2:
return (state.m_needf || state.m_needfg || state.m_needfgh || state.m_xupdated);
case 3:
return (state.m_needfi || state.m_needfij || state.m_xupdated);
case 4:
return (state.m_needfi || state.m_needfij || state.m_xupdated);
case 5:
return (state.m_needfi || state.m_needfij || state.m_xupdated);
}
return(false);
}
//+------------------------------------------------------------------+
//| Calculates FI / F / G / H for problem min( || Ax - b ||) |
//+------------------------------------------------------------------+
void CTestMinLMUnit::AXMB(CMinLMState &state,CMatrixDouble &a,
CRowDouble &b,int n)
{
//--- create variables
double v=0;
if(state.m_needf || state.m_needfg || state.m_needfgh)
state.m_f=0;
if(state.m_needfg || state.m_needfgh)
state.m_g=vector<double>::Zeros(n);
if(state.m_needfgh)
state.m_h=matrix<double>::Zeros(n,n);
for(int i=0; i<n; i++)
{
v=state.m_x.Dot(a[i]+0);
if(state.m_needf || state.m_needfg || state.m_needfgh)
state.m_f+=CMath::Sqr(v-b[i]);
if(state.m_needfg || state.m_needfgh)
state.m_g+=a[i]*2*(v-b[i]);
if(state.m_needfgh)
for(int j=0; j<n; j++)
for(int k=0; k<n; k++)
state.m_h.Add(j,k,2*a.Get(i,j)*a.Get(i,k));
if(state.m_needfi)
state.m_fi.Set(i,v-b[i]);
if(state.m_needfij)
{
state.m_fi.Set(i,v-b[i]);
state.m_j.Row(i,a[i]+0);
}
}
}
//+------------------------------------------------------------------+
//| This function tries to reproduce previously fixed bugs; |
//| in case of bug being present sets Err to True; |
//| leaves it unchanged otherwise. |
//+------------------------------------------------------------------+
void CTestMinLMUnit::TryReproduceFixedBugs(bool &err)
{
CMinLMState s;
CMinLMReport rep;
CRowDouble bl=vector<double>::Full(2,-1);
CRowDouble bu=vector<double>::Full(2,1);;
CRowDouble x=vector<double>::Full(2,2);
//--- Reproduce bug reported by ISS:
//--- when solving bound constrained problem with numerical differentiation
//--- and starting from infeasible point, we won't Stop at the feasible point
CMinLM::MinLMCreateV(2,2,x,0.001,s);
CMinLM::MinLMSetBC(s,bl,bu);
while(CMinLM::MinLMIteration(s))
{
if(s.m_needfi)
s.m_fi=s.m_x.Pow(2)+0;
}
CMinLM::MinLMResults(s,x,rep);
CAp::SetErrorFlag(err,(x[0]<bl[0] || x[0]>bu[0] || x[1]<bl[1] || x[1]>bu[1]),"testminlmunit.ap:1923");
}
//+------------------------------------------------------------------+
//| Test function 1: F(N, M, C, X) = SUM(f_i ^ 2) |
//| f_i = SUM((power(x_j, 3) + alpha*x_j) * c_ij) |
//+------------------------------------------------------------------+
void CTestMinLMUnit::TestFunc1(int n,int m,CMatrixDouble &c,
CRowDouble &x,double &f,
bool needf,CRowDouble &fi,
bool needfi,CMatrixDouble &jac,
bool needjac)
{
//--- create variables
double v=0;
double alpha=0.01;
if(needf)
f=0;
for(int i=0; i<m; i++)
{
v=c.Get(i,n);
for(int j=0; j<n; j++)
{
v+=(alpha*x[j]+MathPow(x[j],3))*c.Get(i,j);
if(needjac)
jac.Set(i,j,(alpha+3*MathPow(x[j],2))*c.Get(i,j));
}
if(needfi)
fi.Set(i,v);
if(needf)
f+=v*v;
}
}
//+------------------------------------------------------------------+
//| Testing class CLSFit |
//+------------------------------------------------------------------+
class CTestLSFitUnit
{
public:
static bool TestLSFit(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Testing class CLSFit |
//+------------------------------------------------------------------+
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");
PrintResult("POLYNOMIAL LEAST SQUARES",!polfiterrors);
PrintResult("RATIONAL LEAST SQUARES",!ratfiterrors);
PrintResult("SPLINE LEAST SQUARES",!splfiterrors);
PrintResult("LINEAR LEAST SQUARES",!llserrors);
PrintResult("NON-LINEAR LEAST SQUARES",!nlserrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- end
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Unit test |
//+------------------------------------------------------------------+
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; 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; 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; 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; 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; 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; 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; i++)
{
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; 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 |
//+------------------------------------------------------------------+
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; 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; 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; 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; 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; 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; 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; 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; i++)
{
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; 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 |
//+------------------------------------------------------------------+
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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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
CIntComp::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
CIntComp::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; 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
CIntComp::Spline1DFitPenalizedW(x,y,w,n,m,rho,info,c,rep);
CIntComp::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; i++)
{
x[i]=(double)i/(double)(n-1);
y[i]=CMath::RandomReal();
w[i]=0.1+CMath::RandomReal();
}
//--- function call
CIntComp::Spline1DFitPenalized(x,y,n,m,rho,info,c,rep);
//--- check
if(info>0)
{
//--- search errors
for(i=0; i<n; i++)
fiterrors=fiterrors||MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i]))>nonstrictthreshold;
}
else
fiterrors=true;
//--- function call
CIntComp::Spline1DFitPenalizedW(x,y,w,n,m,rho,info,c,rep);
//--- check
if(info>0)
{
//--- search errors
for(i=0; i<n; 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
CIntComp::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 |
//+------------------------------------------------------------------+
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; 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.Set(i,0,1);
//--- check
if(m>1)
a.Set(i,1,x[i]);
for(j=2; j<m; j++)
a.Set(i,j,2*x[i]*a.Get(i,j-1)-a.Get(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; i++)
{
for(j=0; j<m; j++)
{
a2.Set(i,2*j+0,a.Get(i,j));
a2.Set(i,2*j+1,a.Get(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; 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; 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; 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; i++)
c2[i]=2*CMath::RandomReal()-1;
//--- function calls
CLSFit::LSFitCreateWF(a,y,w,c2,n,m,m,diffstep,state);
CLSFit::LSFitSetCond(state,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; i++)
nlserrors=nlserrors||MathAbs(c[i]-c2[i])>100*nlthreshold;
}
//--- change values
for(i=0; i<m; 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,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; i++)
nlserrors=nlserrors||MathAbs(c[i]-c2[i])>100*nlthreshold;
}
//--- change values
for(i=0; i<m; i++)
c2[i]=2*CMath::RandomReal()-1;
//--- function calls
CLSFit::LSFitCreateWFGH(a,y,w,c2,n,m,m,state);
CLSFit::LSFitSetCond(state,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; 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; i++)
c2[i]=2*CMath::RandomReal()-1;
//--- function calls
CLSFit::LSFitCreateF(a,y,c2,n,m,m,diffstep,state);
CLSFit::LSFitSetCond(state,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; i++)
nlserrors=nlserrors||MathAbs(c[i]-c2[i])>100*nlthreshold;
}
//--- change values
for(i=0; i<m; 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,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; i++)
nlserrors=nlserrors||MathAbs(c[i]-c2[i])>100*nlthreshold;
}
//--- change values
for(i=0; i<m; i++)
c2[i]=2*CMath::RandomReal()-1;
//--- function calls
CLSFit::LSFitCreateFGH(a,y,c2,n,m,m,state);
CLSFit::LSFitSetCond(state,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; 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; 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; j++)
{
//--- check
if(i==j)
{
a.Set(i,i,0.1+0.9*CMath::RandomReal());
v1=MathMin(v1,a[i][i]);
v2=MathMax(v2,a[i][i]);
}
else
a.Set(i,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; 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; 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.Set(i,0,1);
//--- check
if(m>1)
a.Set(i,1,x[i]);
for(j=2; j<m; j++)
a.Set(i,j,2*x[i]*a[i][j-1]-a[i][j-2]);
}
//--- allocation
cm.Resize(k,m+1);
for(i=0; i<k; i++)
{
for(j=0; j<=m; j++)
cm.Set(i,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; 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; 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; i++)
{
a.Set(i,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,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.Set(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.Set(0,0,1);
y[0]=v-v2;
a.Set(1,0,1);
y[1]=v-v1;
a.Set(2,0,1);
y[2]=v+v1;
a.Set(3,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,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.Set(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|
//+------------------------------------------------------------------+
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; i++)
{
//--- change value
v=0.0;
for(i_=0; i_<m; i_++)
v+=cmatrix.Get(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; 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; i_++)
c2[i_]=c[i_];
for(i=0; i<m; i++)
{
//--- check
if(i==j)
deltac[i]=delta;
else
deltac[i]=0;
}
//--- check
if(k==0)
{
for(i_=0; i_<m; i_++)
deltaproj[i_]=deltac[i_];
}
else
{
for(i=0; i<m; i++)
deltaproj[i]=0;
for(i=k; i<m; i++)
{
//--- change value
v=0.0;
for(i_=0; i_<m; i_++)
v+=vt.Get(i,i_)*deltac[i_];
for(i_=0; i_<m; i_++)
deltaproj[i_]=deltaproj[i_]+v*vt.Get(i,i_);
}
}
//--- now we have DeltaProj such that if C is feasible,
//--- then C+DeltaProj is feasible too
for(i_=0; i_<m; i_++)
c2[i_]=c[i_];
for(i_=0; i_<m; i_++)
c2[i_]=c2[i_]+deltaproj[i_];
s2=GetGLSError(n,m,y,w,fmatrix,c2);
for(i_=0; i_<m; i_++)
c2[i_]=c[i_];
for(i_=0; i_<m; 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 |
//+------------------------------------------------------------------+
double CTestLSFitUnit::GetGLSError(const int n,const int m,double &y[],
double &w[],CMatrixDouble &fmatrix,double &c[])
{
//--- create variables
double result=0;
double v=0;
//--- calculation
for(int i=0; i<n; i++)
{
//--- change value
v=0.0;
for(int i_=0; i_<m; i_++)
v+=fmatrix.Get(i,i_)*c[i_];
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. |
//+------------------------------------------------------------------+
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; 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; 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; i_++)
v+=state.m_x[i_]*state.m_c[i_];
state.m_f=v;
//--- copy
for(i_=0; i_<m; i_++)
state.m_g.Set(i_,state.m_x[i_]);
continue;
}
//--- check
if(state.m_needfgh)
{
//--- change value
v=0.0;
for(i_=0; i_<m; i_++)
v+=state.m_x[i_]*state.m_c[i_];
state.m_f=v;
//--- copy
for(i_=0; i_<m; i_++)
state.m_g.Set(i_,state.m_x[i_]);
for(i=0; i<m; i++)
{
for(j=0; j<m; j++)
state.m_h.Set(i,j,0);
}
continue;
}
}
}
//+------------------------------------------------------------------+
//| Testing class CPSpline |
//+------------------------------------------------------------------+
class CTestPSplineUnit
{
public:
static bool TestPSpline(const bool silent);
private:
static void UnsetP2(CPSpline2Interpolant &p);
static void UnsetP3(CPSpline3Interpolant &p);
static void Unset1D(double &x[]);
};
//+------------------------------------------------------------------+
//| Testing class CPSpline |
//+------------------------------------------------------------------+
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; i_++)
xy.Set(i_,0,x[i_]);
for(i_=0; i_<n; i_++)
xyz.Set(i_,0,x[i_]);
//--- function call
CApServ::TaskGenInt1DEquidist(-1,1,n,t2,y);
//--- change values
for(i_=0; i_<n; i_++)
xy.Set(i_,1,y[i_]);
for(i_=0; i_<n; i_++)
xyz.Set(i_,1,y[i_]);
//--- function call
CApServ::TaskGenInt1DEquidist(-1,1,n,t2,z);
//--- change values
for(i_=0; i_<n; i_++)
xyz.Set(i_,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; 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; 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; 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; i_++)
xy.Set(i_,0,x[i_]);
for(i_=0; i_<n; i_++)
xyz.Set(i_,0,x[i_]);
//--- function call
CApServ::TaskGenInt1DEquidist(0,1,n,t,y);
//--- change values
for(i_=0; i_<n; i_++)
xy.Set(i_,1,y[i_]);
for(i_=0; i_<n; i_++)
xyz.Set(i_,1,y[i_]);
//--- function call
CApServ::TaskGenInt1DEquidist(0,1,n,t,z);
//--- change values
for(i_=0; i_<n; i_++)
xyz.Set(i_,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; i++)
{
xy.Set(i,0,i);
xy.Set(i,1,i);
xyz.Set(i,0,i);
xyz.Set(i,1,i);
xyz.Set(i,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
PrintResult("2D TEST",!p2errors);
PrintResult("3D TEST",!p3errors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- end
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Unset spline, i.e. initialize it with random garbage |
//+------------------------------------------------------------------+
void CTestPSplineUnit::UnsetP2(CPSpline2Interpolant &p)
{
//--- create matrix
CMatrixDouble xy;
//--- allocation
xy.Resize(2,2);
//--- initialization
xy.Set(0,0,-1);
xy.Set(0,1,-1);
xy.Set(1,0,1);
xy.Set(1,1,1);
//--- function call
CPSpline::PSpline2Build(xy,2,1,0,p);
}
//+------------------------------------------------------------------+
//| Unset spline, i.e. initialize it with random garbage |
//+------------------------------------------------------------------+
void CTestPSplineUnit::UnsetP3(CPSpline3Interpolant &p)
{
//--- create matrix
CMatrixDouble xy;
//--- allocation
xy.Resize(2,3);
//--- initialization
xy.Set(0,0,-1);
xy.Set(0,1,-1);
xy.Set(0,2,-1);
xy.Set(1,0,1);
xy.Set(1,1,1);
xy.Set(1,2,1);
//--- function call
CPSpline::PSpline3Build(xy,2,1,0,p);
}
//+------------------------------------------------------------------+
//| Unsets real vector |
//+------------------------------------------------------------------+
void CTestPSplineUnit::Unset1D(double &x[])
{
//--- allocation
ArrayResize(x,1);
//--- change value
x[0]=2*CMath::RandomReal()-1;
}
//+------------------------------------------------------------------+
//| Testing class CSpline2D |
//+------------------------------------------------------------------+
class CTestSpline2DUnit
{
public:
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);
};
//+------------------------------------------------------------------+
//| Testing class CSpline2D |
//+------------------------------------------------------------------+
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; 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; 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; 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; i++)
{
k=CMath::RandomInteger(m);
//--- check
if(k!=i)
{
t=y[i];
y[i]=y[k];
y[k]=t;
}
}
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
f.Set(i,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; i++)
{
for(j=0; j<n; j++)
err=MathMax(err,MathAbs(f.Get(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; i++)
{
for(j=0; j<n; j++)
err=MathMax(err,MathAbs(f.Get(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; i++)
{
for(j=0; j<n; j++)
ft.Set(j,i,f.Get(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; j++)
x[j]=(double)j/(double)(n-1);
for(i=0; i<m; i++)
y[i]=(double)i/(double)(m-1);
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
f.Set(i,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 ; j++)
{
v1=CSpline2D::Spline2DCalc(c,(double)j/(double)(n2-1),(double)i/(double)(m2-1));
v2=fr.Get(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
PrintResult("BILINEAR TEST",!blerrors);
PrintResult("BICUBIC TEST",!bcerrors);
PrintResult("DIFFERENTIATION TEST",!dserrors);
PrintResult("COPY/SERIALIZE TEST",!cperrors);
PrintResult("UNPACK TEST",!uperrors);
PrintResult("LIN.TRANS. TEST",!lterrors);
PrintResult("SPECIAL SYMMETRY TEST",!syerrors);
PrintResult("BILINEAR RESAMPLING TEST",!rlerrors);
PrintResult("BICUBIC RESAMPLING TEST",!rcerrors);
//--- Summary
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- end
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Lipschitz constants for spline inself,first and second |
//| derivatives. |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; 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. |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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+=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 |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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.Set(0,0,0);
f.Set(0,1,0);
f.Set(1,0,0);
f.Set(1,1,0);
//--- function call
CSpline2D::Spline2DBuildBilinear(x,y,f,2,2,c);
}
//+------------------------------------------------------------------+
//| Testing class CSpdGEVD |
//+------------------------------------------------------------------+
class CTestSpdGEVDUnit
{
public:
static bool TestSpdGEVD(const bool silent);
};
//+------------------------------------------------------------------+
//| Testing class CSpdGEVD |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
a.Set(i,j,2*CMath::RandomReal()-1);
a.Set(j,i,a.Get(i,j));
afull.Set(i,j,a.Get(i,j));
afull.Set(j,i,a.Get(i,j));
}
}
//--- change values
for(i=0; i<n; i++)
{
for(j=i+1; j<n; j++)
{
l.Set(i,j,CMath::RandomReal());
l.Set(j,i,l.Get(i,j));
}
l.Set(i,i,1.5+CMath::RandomReal());
}
//--- calculation
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
minij=MathMin(i,j);
//--- change value
v=0.0;
for(i_=0; i_<=minij; i_++)
{
v+=l.Get(i,i_)*l.Get(i_,j);
}
b.Set(i,j,v);
b.Set(j,i,v);
bfull.Set(i,j,v);
bfull.Set(j,i,v);
}
}
//--- calculation
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
//--- check
if(isuppera)
{
//--- check
if(j<i)
a.Set(i,j,2*CMath::RandomReal()-1);
}
else
{
//--- check
if(i<j)
a.Set(i,j,2*CMath::RandomReal()-1);
}
//--- check
if(isupperb)
{
//--- check
if(j<i)
b.Set(i,j,2*CMath::RandomReal()-1);
}
else
{
//--- check
if(i<j)
b.Set(i,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; j++)
{
for(i=0; i<n; i++)
{
v1=0.0;
for(i_=0; i_<n; i_++)
v1+=afull.Get(i,i_)*z.Get(i_,j);
v2=0.0;
for(i_=0; i_<n; i_++)
v2+=bfull.Get(i,i_)*z.Get(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; j++)
{
for(i=0; i<n; i++)
{
v1=0.0;
for(i_=0; i_<n; i_++)
v1+=bfull.Get(i,i_)*z.Get(i_,j);
t1[i]=v1;
}
for(i=0; i<n; i++)
{
v2=0.0;
for(i_=0; i_<n; i_++)
v2+=afull.Get(i,i_)*t1[i_];
//--- search errors
err=MathMax(err,MathAbs(v2-d[j]*z.Get(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; j++)
{
for(i=0; i<n; i++)
{
v1=0.0;
for(i_=0; i_<n; i_++)
v1+=afull.Get(i,i_)*z.Get(i_,j);
t1[i]=v1;
}
for(i=0; i<n; i++)
{
v2=0.0;
for(i_=0; i_<n; i_++)
v2+=bfull.Get(i,i_)*t1[i_];
//--- search errors
err=MathMax(err,MathAbs(v2-d[j]*z.Get(i,j)));
}
}
//--- search errors
valerr=MathMax(err,valerr);
}
}
}
}
//--- report
waserrors=(valerr>threshold||wfailed)||wnsorted;
//--- check
if(!silent)
{
Print("TESTING SYMMETRIC GEVD");
PrintFormat("Av-lambdav error (generalized): %5.3E",valerr);
PrintResult("Eigen values order",!wnsorted);
PrintFormat("Always converged: " + (!wfailed ? "YES" : "NO"));
PrintFormat("Threshold: %5.3E",threshold);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CInverseUpdate |
//+------------------------------------------------------------------+
class CTestInverseUpdateUnit
{
public:
static bool TestInverseUpdate(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Testing class CInverseUpdate |
//+------------------------------------------------------------------+
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; 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; 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; 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; 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
PrintResult("TEST",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
void CTestInverseUpdateUnit::MakeACopy(CMatrixDouble &a,const int m,
const int n,CMatrixDouble &b)
{
//--- allocation
b.Resize(m,n);
//--- copy
for(int i=0; i<m; i++)
{
for(int j=0; j<n; j++)
b.Set(i,j,a.Get(i,j));
}
}
//+------------------------------------------------------------------+
//| LU decomposition |
//+------------------------------------------------------------------+
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; i++)
{
//--- check
if(MathAbs(a.Get(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; i_++)
t1[i_]=a.Get(j,i_);
for(i_=0; i_<n; i_++)
a.Set(j,i_,a[jp][i_]);
for(i_=0; i_<n; i_++)
a.Set(jp,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; i_++)
a.Set(i_,j,s*a.Get(i_,j));
}
}
//--- check
if(j<MathMin(m,n)-1)
{
//--- Update trailing submatrix.
jp=j+1;
for(i=j+1; i<m; i++)
{
s=a.Get(i,j);
for(i_=jp; i_<n; i_++)
a.Set(i,i_,a.Get(i,i_)-s*a.Get(j,i_));
}
}
}
}
//+------------------------------------------------------------------+
//| Generate matrix with given condition number C (2-norm) |
//+------------------------------------------------------------------+
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.Set(i,j,1);
else
a.Set(i,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.Get(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.Set(i,i_,a.Get(i,i_)-t*v[i_]);
}
}
//--- copy
for(i=1; i<=n; i++)
{
for(j=1; j<=n; j++)
a0.Set(i-1,j-1,a.Get(i,j));
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
{
a0.Set(i,j,0);
for(k=0; k<n; k++)
a0.Set(i,j,a0.Get(i,j)+q1[i][k]*cc[k]*q2[j][k]);
}
}
}
//+------------------------------------------------------------------+
//| triangular inverse |
//+------------------------------------------------------------------+
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; j++)
{
//--- check
if(nounit)
{
//--- check
if(a[j][j]==0.0)
{
//--- return result
return(false);
}
a.Set(j,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; i_++)
t[i_]=a.Get(i_,j);
for(i=0; i<j; i++)
{
//--- check
if(i<j-1)
{
//--- change value
v=0.0;
for(i_=i+1; i_<j; i_++)
v+=a.Get(i,i_)*t[i_];
}
else
v=0;
//--- check
if(nounit)
a.Set(i,j,v+a[i][i]*t[i]);
else
a.Set(i,j,v+t[i]);
}
//--- change values
for(i_=0; i_<j; i_++)
a.Set(i_,j,ajj*a.Get(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.Set(j,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; i_++)
t[i_]=a.Get(i_,j);
for(i=j+1; i<n; i++)
{
//--- check
if(i>j+1)
{
//--- change value
v=0.0;
for(i_=j+1; i_<i; i_++)
v+=a.Get(i,i_)*t[i_];
}
else
v=0;
//--- check
if(nounit)
a.Set(i,j,v+a[i][i]*t[i]);
else
a.Set(i,j,v+t[i]);
}
//--- change values
for(i_=j+1; i_<n; i_++)
a.Set(i_,j,ajj*a.Get(i_,j));
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| LU inverse |
//+------------------------------------------------------------------+
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; i++)
{
work[i]=a.Get(i,j);
a.Set(i,j,0);
}
//--- Compute current column of inv(A).
if(j<n-1)
{
for(i=0; i<n; i++)
{
//--- change value
v=0.0;
for(i_=j+1; i_<n; i_++)
v+=a.Get(i,i_)*work[i_];
a.Set(i,j,a.Get(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; i_++)
work[i_]=a.Get(i_,j);
for(i_=0; i_<n; i_++)
a.Set(i_,j,a[i_][jp]);
for(i_=0; i_<n; i_++)
a.Set(i_,jp,work[i_]);
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Matrix inverse |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
double CTestInverseUpdateUnit::MatrixDiff(CMatrixDouble &a,
CMatrixDouble &b,
const int m,const int n)
{
double result=0;
//--- calculation
for(int i=0; i<m; i++)
{
for(int j=0; j<n; j++)
result=MathMax(result,MathAbs(b.Get(i,j)-a.Get(i,j)));
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Update and inverse |
//+------------------------------------------------------------------+
bool CTestInverseUpdateUnit::UpdAndInv(CMatrixDouble &a,double &u[],
double &v[],const int n)
{
double r=0;
//--- create array
int pivots[];
//--- calculation
for(int i=0; i<n; i++)
{
r=u[i];
for(int i_=0; i_<n; i_++)
a.Set(i,i_,a.Get(i,i_)+r*v[i_]);
}
//--- function call
MatLU(a,n,n,pivots);
//--- return result
return(InvMatLU(a,pivots,n));
}
//+------------------------------------------------------------------+
//| Testing class CSchur |
//+------------------------------------------------------------------+
class CTestSchurUnit
{
public:
static bool TestSchur(const bool silent);
private:
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);
};
//+------------------------------------------------------------------+
//| Testing Schur decomposition subroutine |
//+------------------------------------------------------------------+
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.Set(i,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; i++)
{
for(j=0; j<n; j++)
a.Set(i,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");
PrintFormat("Schur decomposition error: %5.3E",materr);
PrintFormat("Schur orthogonality error: %5.3E",orterr);
PrintResult("T matrix structure",!errstruct);
PrintFormat("Always converged: %s",(!wfailed ? "OK" : "FAILED"));
PrintFormat("Threshold: %5.3E",threshold);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
Print("");
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
void CTestSchurUnit::FillsParseA(CMatrixDouble &a,const int n,
const double sparcity)
{
//--- change values
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
//--- check
if(CMath::RandomReal()>=sparcity)
a.Set(i,j,2*CMath::RandomReal()-1);
else
a.Set(i,j,0);
}
}
}
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
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; i++)
{
for(j=0; j<n; j++)
t.Set(i,j,a.Get(i,j));
}
//--- check
if(!CSchur::RMatrixSchur(t,n,s))
{
wfailed=true;
return;
}
//--- decomposition error
locerr=0;
for(j=0; j<n; j++)
{
for(i_=0; i_<n; i_++)
sr[i_]=s.Get(j,i_);
//--- calculation
for(k=0; k<n; k++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=t[k][i_]*sr[i_];
astc[k]=v;
}
//--- calculation
for(k=0; k<n; k++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=s[k][i_]*astc[i_];
sastc[k]=v;
}
//--- search errors
for(k=0; k<n; 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; i++)
{
for(j=0; j<n; j++)
{
//--- change value
v=0.0;
for(i_=0; i_<n; i_++)
v+=s.Get(i_,i)*s.Get(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; j++)
{
for(i=j+2; i<n; i++)
{
//--- check
if(t.Get(i,j)!=0.0)
errstruct=true;
}
}
}
//+------------------------------------------------------------------+
//| Testing class CNlEq |
//+------------------------------------------------------------------+
class CTestNlEqUnit
{
public:
static bool TestNlEq(const bool silent);
private:
static void TestFuncHBM(CNlEqState &state);
static void TestFuncHB1(CNlEqState &state);
static void TestFuncSHBM(CNlEqState &state);
};
//+------------------------------------------------------------------+
//| Testing class CNlEq |
//+------------------------------------------------------------------+
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; 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; 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; 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");
PrintResult("BASIC FUNCTIONALITY",!basicserrors);
PrintResult("CONVERGENCE",!converror);
PrintResult("OTHER PROPERTIES",!othererrors);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
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 |
//+------------------------------------------------------------------+
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.Set(0,0,2*x);
state.m_j.Set(0,1,1);
state.m_j.Set(1,0,1);
state.m_j.Set(1,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 |
//+------------------------------------------------------------------+
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.Set(0,0,2*(x*x+y-11)*2*x+2*(x+y*y-7));
state.m_j.Set(0,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. |
//+------------------------------------------------------------------+
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.Set(0,0,2*x);
state.m_j.Set(0,1,1);
state.m_j.Set(1,0,1);
state.m_j.Set(1,1,2*y);
state.m_j.Set(2,0,0);
state.m_j.Set(2,1,0);
//--- exit the function
return;
}
}
//+------------------------------------------------------------------+
//| Testing class CChebyshev |
//+------------------------------------------------------------------+
class CTestChebyshevUnit
{
public:
static bool TestChebyshev(const bool silent);
};
//+------------------------------------------------------------------+
//| Testing class CChebyshev |
//+------------------------------------------------------------------+
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+=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.Set(i,j,0);
//--- function call
CChebyshev::ChebyshevCoefficients(i,c);
for(i_=0; i_<=i; i_++)
a.Set(i,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.Get(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");
PrintFormat("Max error against table %5.2E",err);
PrintFormat("Summation error %5.2E",sumerr);
PrintFormat("Coefficients error %5.2E",cerr);
PrintFormat("FrobChebyshev error %5.2E",ferr);
PrintFormat("Threshold %5.2E",threshold);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CHermite |
//+------------------------------------------------------------------+
class CTestHermiteUnit
{
public:
static bool TestHermite(const bool silent);
};
//+------------------------------------------------------------------+
//| Testing class CHermite |
//+------------------------------------------------------------------+
bool CTestHermiteUnit::TestHermite(const bool silent)
{
//--- create variables
double err=0;
double sumerr=0;
double cerr=0;
double threshold=1.0E-9;
int n=0;
int maxn=0;
int pass=0;
double x=0;
double v=0;
bool waserrors=false;
//--- create array
double c[];
//--- 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+=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");
PrintFormat("Max error %5.2E}",err);
PrintFormat("Summation error %5.2E",sumerr);
PrintFormat("Coefficients error %5.2E",cerr);
PrintFormat("Threshold %5.2E",threshold);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CLaguerre |
//+------------------------------------------------------------------+
class CTestLaguerreUnit
{
public:
static bool TestLaguerre(const bool silent);
};
//+------------------------------------------------------------------+
//| Testing class CLaguerre |
//+------------------------------------------------------------------+
bool CTestLaguerreUnit::TestLaguerre(const bool silent)
{
//--- create variables
double err=0;
double sumerr=0;
double cerr=0;
double threshold=1.0E-9;
int n=0;
int maxn=0;
int pass=0;
double x=0;
double v=0;
bool waserrors=false;
//--- create array
double c[];
//--- 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+=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");
PrintFormat("Max error %5.2E",err);
PrintFormat("Summation error %5.2E",sumerr);
PrintFormat("Coefficients error %5.2E",cerr);
PrintFormat("Threshold %5.2E",threshold);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
}
//--- return result
return(!waserrors);
}
//+------------------------------------------------------------------+
//| Testing class CLegendre |
//+------------------------------------------------------------------+
class CTestLegendreUnit
{
public:
static bool TestLegendre(const bool silent);
};
//+------------------------------------------------------------------+
//| Testing class CLegendre |
//+------------------------------------------------------------------+
bool CTestLegendreUnit::TestLegendre(const bool silent)
{
//--- create variables
double err=0;
double sumerr=0;
double cerr=0;
double threshold=1.0E-9;
int n=0;
int maxn=0;
int i=0;
int pass=0;
double x=0;
double v=0;
double t=0;
bool waserrors=false;
//--- create array
double c[];
//--- 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+=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");
PrintFormat("Max error %5.2E",err);
PrintFormat("Summation error %5.2E",sumerr);
PrintFormat("Coefficients error %5.2E",cerr);
PrintFormat("Threshold %5.2E",threshold);
//--- check
PrintResult("TEST SUMMARY",!waserrors);
}
//--- 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) {}
};
//+------------------------------------------------------------------+
//| The auxiliary class |
//+------------------------------------------------------------------+
class CRec4Serialization
{
public:
//--- arrays
bool m_b[];
int m_i[];
double m_r[];
//--- constructor, destructor
CRec4Serialization(void) {}
~CRec4Serialization(void) {}
};
//+------------------------------------------------------------------+
//| Testing the basic functions |
//+------------------------------------------------------------------+
class CTestAlglibBasicsUnit
{
public:
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);
private:
static bool TestComplexArithmetics(const bool silent);
static bool TestIEEESpecial(const bool silent);
static bool TestSwapFunctions(const bool silent);
static bool TestSerializationFunctions(const bool silent);
};
//+------------------------------------------------------------------+
//| The auxiliary function |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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 |
//+------------------------------------------------------------------+
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; 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; 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; i++)
{
rv=s.Unserialize_Double();
v.m_r[i]=rv;
}
}
}
//+------------------------------------------------------------------+
//| Testing the basic functions |
//+------------------------------------------------------------------+
bool CTestAlglibBasicsUnit::TestAlglibBasics(const bool silent)
{
//--- create a variable
bool result=true;
//--- function calls
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 |
//+------------------------------------------------------------------+
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.real=2*CMath::RandomReal()-1;
ca.imag=2*CMath::RandomReal()-1;
ra=CMath::AbsComplex(ca);
absc=absc && MathAbs(ra-MathSqrt(CMath::Sqr(ca.real)+CMath::Sqr(ca.imag)))<threshold;
//--- test Add
ca.real=2*CMath::RandomReal()-1;
ca.imag=2*CMath::RandomReal()-1;
cb.real=2*CMath::RandomReal()-1;
cb.imag=2*CMath::RandomReal()-1;
ra=2*CMath::RandomReal()-1;
rb=2*CMath::RandomReal()-1;
res=ca+cb;
addcc=(addcc && MathAbs(res.real-ca.real-cb.real)<threshold) && MathAbs(res.imag-ca.imag-cb.imag)<threshold;
res=ca+rb;
addcr=(addcr && MathAbs(res.real-ca.real-rb)<threshold) && MathAbs(res.imag-ca.imag)<threshold;
res=cb+ra;
addrc=(addrc && MathAbs(res.real-ra-cb.real)<threshold) && MathAbs(res.imag-cb.imag)<threshold;
//--- test Sub
ca.real=2*CMath::RandomReal()-1;
ca.imag=2*CMath::RandomReal()-1;
cb.real=2*CMath::RandomReal()-1;
cb.imag=2*CMath::RandomReal()-1;
ra=2*CMath::RandomReal()-1;
rb=2*CMath::RandomReal()-1;
res=ca-cb;
subcc=(subcc && MathAbs(res.real-(ca.real-cb.real))<threshold) && MathAbs(res.imag-(ca.imag-cb.imag))<threshold;
res=ca-rb;
subcr=(subcr && MathAbs(res.real-(ca.real-rb))<threshold) && MathAbs(res.imag-ca.imag)<threshold;
res=-cb+ra;
subrc=(subrc && MathAbs(res.real-(ra-cb.real))<threshold) && MathAbs(res.imag+cb.imag)<threshold;
//--- test Mul
ca.real=2*CMath::RandomReal()-1;
ca.imag=2*CMath::RandomReal()-1;
cb.real=2*CMath::RandomReal()-1;
cb.imag=2*CMath::RandomReal()-1;
ra=2*CMath::RandomReal()-1;
rb=2*CMath::RandomReal()-1;
res=ca*cb;
mulcc=(mulcc && MathAbs(res.real-(ca.real*cb.real-ca.imag*cb.imag))<threshold) && MathAbs(res.imag-(ca.real*cb.imag+ca.imag*cb.real))<threshold;
res=ca*rb;
mulcr=(mulcr && MathAbs(res.real-ca.real*rb)<threshold) && MathAbs(res.imag-ca.imag*rb)<threshold;
res=cb*ra;
mulrc=(mulrc && MathAbs(res.real-ra*cb.real)<threshold) && MathAbs(res.imag-ra*cb.imag)<threshold;
//--- test Div
ca.real=2*CMath::RandomReal()-1;
ca.imag=2*CMath::RandomReal()-1;
do
{
cb.real=2*CMath::RandomReal()-1;
cb.imag=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).real-ca.real)<threshold) && MathAbs((res*cb).imag-ca.imag)<threshold;
res=ca/rb;
divcr=(divcr && MathAbs(res.real-ca.real/rb)<threshold) && MathAbs(res.imag-ca.imag/rb)<threshold;
complex cra=ra;
res=cra/cb;
divrc=(divrc && MathAbs((res*cb).real-ra)<threshold) && MathAbs((res*cb).imag)<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)
{
PrintResult("COMPLEX ARITHMETICS",true);
}
else
{
PrintResult("COMPLEX ARITHMETICS",false);
PrintResult("* AddCC ",addcc);
PrintResult("* AddCR ",addcr);
PrintResult("* AddRC ",addrc);
PrintResult("* SubCC ",subcc);
PrintResult("* SubCR ",subcr);
PrintResult("* SubRC ",subrc);
PrintResult("* MulCC ",mulcc);
PrintResult("* MulCR ",mulcr);
PrintResult("* MulRC ",mulrc);
PrintResult("* DivCC ",divcc);
PrintResult("* DivCR ",divcr);
PrintResult("* DivRC ",divrc);
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Tests for IEEE special quantities |
//+------------------------------------------------------------------+
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)
{
PrintResult("IEEE SPECIAL VALUES",true);
}
else
{
PrintResult("IEEE SPECIAL VALUES",false);
PrintResult("* NAN",oknan);
PrintResult("* INF",okinf);
PrintResult("* FUNCTIONS",okother);
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Tests for swapping functions |
//+------------------------------------------------------------------+
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.Set(0,0,true);
b21.Set(0,1,false);
b22.Set(0,0,false);
b22.Set(1,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.Set(0,0,1);
i21.Set(0,1,2);
i22.Set(0,0,3);
i22.Set(1,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.Set(0,0,1);
r21.Set(0,1,2);
r22.Set(0,0,3);
r22.Set(1,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.Set(0,0,1);
c21.Set(0,1,2);
c22.Set(0,0,3);
c22.Set(1,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
PrintResult("SWAPPING FUNCTIONS",result);
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Tests for swapping functions |
//+------------------------------------------------------------------+
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.Alloc_Entry();
_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)
{
PrintResult("SERIALIZATION FUNCTIONS",true);
}
else
{
PrintResult("SERIALIZATION FUNCTIONS",false);
PrintResult("* BOOLEAN ",okb);
PrintResult("* INTEGER ",oki);
PrintResult("* REAL ",okr);
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Test sparse matrix |
//+------------------------------------------------------------------+
class CSparseGenerator
{
public:
int m_m;
int m_matkind;
int m_n;
int m_triangle;
RCommState m_rcs;
CMatrixDouble m_bufa;
CHighQualityRandState m_rs;
CSparseGenerator(void) {}
~CSparseGenerator(void) {}
void Copy(const CSparseGenerator &obj);
//--- overloading
void operator=(const CSparseGenerator &obj) { Copy(obj); }
};
//+------------------------------------------------------------------+
//| Copy |
//+------------------------------------------------------------------+
void CSparseGenerator::Copy(const CSparseGenerator &obj)
{
m_m=obj.m_m;
m_matkind=obj.m_matkind;
m_n=obj.m_n;
m_triangle=obj.m_triangle;
m_rcs=obj.m_rcs;
m_bufa=obj.m_bufa;
m_rs=obj.m_rs;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestSparseUnit
{
public:
static const int m_maxtype;
static bool TestSparse(bool silent);
static bool SKSTest(void);
static void CRSTest(bool errorflag);
static void TestSerialize(bool errorflag);
static bool BasicFuncTest(void);
static bool TestLevel2Unsymmetric(void);
static bool TestLevel2Triangular(void);
static bool TestLevel3Unsymmetric(void);
static bool TestLevel2Symmetric(void);
static bool TestLevel3Symmetric(void);
static bool TestSymmetricPerm(void);
static bool BasicFuncRandomTest(void);
static bool LinearFunctionsTest(void);
static bool LinearFunctionSSTest(void);
static bool LinearFunctionSMMTest(void);
static bool LinearFunctionsSMMTest(void);
static bool BasicCopyFuncTest(bool silent);
static bool CopyFuncTest(bool silent);
private:
static void InitGenerator(int m,int n,int matkind,int triangle,CSparseGenerator &g);
static bool GenerateNext(CSparseGenerator &g,CMatrixDouble &da,CSparseMatrix &sa);
static void CreateRandom(int m,int n,int pkind,int ckind,int p0,int p1,CMatrixDouble &da,CSparseMatrix &sa);
static bool EnumerateTest(void);
static bool RewriteExistingTest(void);
static void TestGetRow(bool &err);
static bool TestConvertSM(void);
static bool TestGCMatrixType(void);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
const int CTestSparseUnit::m_maxtype=2;
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestSparseUnit::TestSparse(bool silent)
{
bool result=false;
bool waserrors=false;
bool basicerrors=false;
bool linearerrors=false;
bool basicrnderrors=false;
bool level2unsymmetricerrors=false;
bool level2symmetricerrors=false;
bool level2triangularerrors=false;
bool level3unsymmetricerrors=false;
bool level3symmetricerrors=false;
bool symmetricpermerrors=false;
bool linearserrors=false;
bool linearmmerrors=false;
bool linearsmmerrors=false;
bool getrowerrors=false;
bool serializeerrors=false;
bool copyerrors=false;
bool basiccopyerrors=false;
bool enumerateerrors=false;
bool rewriteexistingerr=false;
bool skserrors=false;
bool crserrors=false;
skserrors=SKSTest();
CRSTest(crserrors);
basicerrors=BasicFuncTest()||TestGCMatrixType();
basicrnderrors=BasicFuncRandomTest();
linearerrors=LinearFunctionsTest();
level2unsymmetricerrors=TestLevel2Unsymmetric();
level2symmetricerrors=TestLevel2Symmetric();
level2triangularerrors=TestLevel2Triangular();
level3unsymmetricerrors=TestLevel3Unsymmetric();
level3symmetricerrors=TestLevel3Symmetric();
symmetricpermerrors=TestSymmetricPerm();
linearserrors=LinearFunctionSSTest();
linearmmerrors=LinearFunctionSMMTest();
linearsmmerrors=LinearFunctionsSMMTest();
copyerrors=CopyFuncTest(true)||TestConvertSM();
basiccopyerrors=BasicCopyFuncTest(true);
enumerateerrors=EnumerateTest();
rewriteexistingerr=RewriteExistingTest();
TestGetRow(getrowerrors);
TestSerialize(serializeerrors);
//--- report
waserrors=(skserrors||crserrors||getrowerrors||serializeerrors ||
basicerrors||linearerrors||basicrnderrors||level2unsymmetricerrors ||
level2symmetricerrors||level2triangularerrors||level3unsymmetricerrors ||
level3symmetricerrors||symmetricpermerrors||linearserrors||linearmmerrors ||
linearsmmerrors||copyerrors||basiccopyerrors||enumerateerrors ||
rewriteexistingerr);
if(!silent)
{
Print("TESTING SPARSE");
Print("STORAGE FORMAT SPECIFICS:");
PrintResult("* SKS",!skserrors);
PrintResult("* CRS",!crserrors);
Print("OPERATIONS:");
PrintResult("* GETROW",!getrowerrors);
PrintResult("* SERIALIZE",!serializeerrors);
Print("BLAS:");
PrintResult("* LEVEL 2 GENERAL",!level2unsymmetricerrors);
PrintResult("* LEVEL 2 SYMMETRIC",!level2symmetricerrors);
PrintResult("* LEVEL 2 TRIANGULAR",!level2triangularerrors);
PrintResult("* LEVEL 3 GENERAL",!level3unsymmetricerrors);
PrintResult("* LEVEL 3 SYMMETRIC",!level3symmetricerrors);
PrintResult("* PERMUTATIONS (SYMMETRIC)",!symmetricpermerrors);
PrintResult("BASIC TEST",!basicerrors);
PrintResult("COPY TEST",!copyerrors);
PrintResult("BASIC_COPY TEST",!basiccopyerrors);
PrintResult("BASIC_RND TEST",!basicrnderrors);
PrintResult("LINEAR TEST",!linearerrors);
PrintResult("LINEAR TEST FOR SYMMETRIC MATRICES",!linearserrors);
PrintResult("LINEAR MxM TEST",!linearmmerrors);
PrintResult("LINEAR MxM TEST FOR SYMMETRIC MATRICES",!linearsmmerrors);
PrintResult("ENUMERATE TEST",!enumerateerrors);
PrintResult("REWRITE EXISTING TEST",!rewriteexistingerr);
//---
PrintResult("TEST SUMMARY",!waserrors);
Print("\n\n");
}
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing basic SKS functional. |
//| Returns True on errors, False on success. |
//+------------------------------------------------------------------+
bool CTestSparseUnit::SKSTest(void)
{
bool result=false;
CSparseMatrix s0;
CSparseMatrix s1;
CSparseMatrix s2;
CSparseMatrix s3;
CSparseMatrix s4;
CSparseMatrix s5;
CSparseMatrix s6;
int n=0;
int nz=0;
double pnz=0;
int i=0;
int j=0;
int t0=0;
int t1=0;
CMatrixDouble a;
CMatrixInt wasenumerated;
CRowInt d;
CRowInt u;
CHighQualityRandState rs;
double v0=0;
double v1=0;
int uppercnt=0;
int lowercnt=0;
int bw=0;
result=false;
CHighQualityRand::HQRndRandomize(rs);
for(n=1; n<=20; n++)
{
nz=n*n-n;
while(true)
{
//--- Generate N*N matrix where probability of non-diagonal element
//--- being non-zero is PNZ. We also generate D and U - subdiagonal
//--- and superdiagonal profile sizes.
//--- Create matrix with either general SKS or banded SKS constructor function
if(n>1)
pnz=(double)nz/(double)(n*n-n);
else
pnz=1.0;
d.Resize(n);
u.Resize(n);
d.Fill(0);
u.Fill(0);
a=matrix<double>::Zeros(n,n);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
//--- Test SparseCreateSKS() functionality
for(i=0; i<n; i++)
for(j=0; j<n; j++)
{
if(i==j||CHighQualityRand::HQRndUniformR(rs)<=pnz)
{
a.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
if(j<i)
d.Set(i,MathMax(d[i],i-j));
else
u.Set(j,MathMax(u[j],j-i));
}
else
a.Set(i,j,0.0);
}
CSparse::SparseCreateSKS(n,n,d,u,s0);
}
else
{
//--- Test SparseCreateSKSBand() functionality
bw=CHighQualityRand::HQRndUniformI(rs,n+1);
for(i=0; i<n; i++)
{
d.Set(i,MathMin(bw,i));
u.Set(i,MathMin(bw,i));
}
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
if(MathAbs(i-j)<=bw && CHighQualityRand::HQRndUniformR(rs)<=pnz)
a.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
else
a.Set(i,j,0.0);
}
}
CSparse::SparseCreateSKSBand(n,n,bw,s0);
}
for(i=0; i<n; i++)
for(j=0; j<n; j++)
if(a.Get(i,j)!=0.0)
{
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CAp::SetErrorFlag(result,!CSparse::SparseRewriteExisting(s0,i,j,a.Get(i,j)),"testsparseunit.ap:456");
else
CSparse::SparseSet(s0,i,j,a.Get(i,j));
}
uppercnt=0;
lowercnt=0;
for(i=0; i<n; i++)
{
uppercnt=uppercnt+u[i];
lowercnt=lowercnt+d[i];
}
//--- Check correctness of SparseExists()
for(i=0; i<n; i++)
for(j=0; j<n; j++)
{
if(i>=j && i-j>d[i])
CAp::SetErrorFlag(result,CSparse::SparseExists(s0,i,j),"testsparseunit.ap:475");
if(i>=j && i-j<=d[i])
CAp::SetErrorFlag(result,!CSparse::SparseExists(s0,i,j),"testsparseunit.ap:477");
if(i<=j && j-i>u[j])
CAp::SetErrorFlag(result,CSparse::SparseExists(s0,i,j),"testsparseunit.ap:479");
if(i<=j && j-i<=u[j])
CAp::SetErrorFlag(result,!CSparse::SparseExists(s0,i,j),"testsparseunit.ap:481");
}
//--- Try to call SparseRewriteExisting() for out-of-band elements, make sure that it returns False.
for(i=0; i<n; i++)
for(j=0; j<n; j++)
if((i>=j && i-j>d[i]) || (i<=j && j-i>u[j]))
CAp::SetErrorFlag(result,CSparse::SparseRewriteExisting(s0,i,j,1.0),"testsparseunit.ap:490");
//--- Convert to several different formats, check their contents with SparseGet().
CSparse::SparseCopy(s0,s1);
CSparse::SparseConvertToCRS(s1);
CSparse::SparseCopyToCRS(s0,s2);
CSparse::SparseCopyToCRSBuf(s0,s3);
CSparse::SparseCopyToHash(s0,s4);
CSparse::SparseCopyToHashBuf(s0,s5);
CSparse::SparseCopy(s0,s6);
CSparse::SparseConvertToHash(s6);
CAp::SetErrorFlag(result,CSparse::SparseGetNRows(s0)!=n,"testsparseunit.ap:503");
CAp::SetErrorFlag(result,CSparse::SparseGetNCols(s0)!=n,"testsparseunit.ap:504");
CAp::SetErrorFlag(result,CSparse::SparseGetMatrixType(s0)!=2,"testsparseunit.ap:505");
CAp::SetErrorFlag(result,!CSparse::SparseIsSKS(s0),"testsparseunit.ap:506");
CAp::SetErrorFlag(result,CSparse::SparseIsCRS(s0),"testsparseunit.ap:507");
CAp::SetErrorFlag(result,CSparse::SparseIsHash(s0),"testsparseunit.ap:508");
CAp::SetErrorFlag(result,CSparse::SparseIsSKS(s1),"testsparseunit.ap:509");
CAp::SetErrorFlag(result,!CSparse::SparseIsCRS(s1),"testsparseunit.ap:510");
CAp::SetErrorFlag(result,CSparse::SparseIsHash(s1),"testsparseunit.ap:511");
for(i=0; i<n; i++)
{
v1=a.Get(i,i);
v0=CSparse::SparseGetDiagonal(s0,i);
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:516");
}
for(i=0; i<n; i++)
for(j=0; j<n; j++)
{
v1=a.Get(i,j);
v0=CSparse::SparseGet(s0,i,j);
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:523");
v0=CSparse::SparseGet(s1,i,j);
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:525");
v0=CSparse::SparseGet(s2,i,j);
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:527");
v0=CSparse::SparseGet(s3,i,j);
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:529");
v0=CSparse::SparseGet(s4,i,j);
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:531");
v0=CSparse::SparseGet(s5,i,j);
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:533");
v0=CSparse::SparseGet(s6,i,j);
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:535");
}
//--- Check enumeration capabilities:
//--- * each element returned by SparseEnumerate() is returned only once
//--- * each non-zero element of A was enumerated
wasenumerated.Resize(n,n);
wasenumerated.Fill((int)false);
t0=0;
t1=0;
while(CSparse::SparseEnumerate(s0,t0,t1,i,j,v0))
{
CAp::SetErrorFlag(result,(bool)wasenumerated.Get(i,j),"testsparseunit.ap:551");
wasenumerated.Set(i,j,(int)true);
}
for(i=0; i<n; i++)
for(j=0; j<n; j++)
if(a.Get(i,j)!=0.0)
CAp::SetErrorFlag(result,!wasenumerated.Get(i,j),"testsparseunit.ap:557");
//--- Check UpperCnt()/LowerCnt()
CAp::SetErrorFlag(result,CSparse::SparseGetUpperCount(s0)!=uppercnt,"testsparseunit.ap:562");
CAp::SetErrorFlag(result,CSparse::SparseGetLowerCount(s0)!=lowercnt,"testsparseunit.ap:563");
//--- Check in-place transposition
CSparse::SparseCopy(s0,s1);
CSparse::SparseTransposeSKS(s1);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
{
v0=CSparse::SparseGet(s0,i,j);
v1=CSparse::SparseGet(s1,j,i);
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:575");
}
//--- One more check - matrix is initially created in some other format
//--- (CRS or Hash) and converted to SKS later.
CSparse::SparseCreate(n,n,0,s0);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
if(a.Get(i,j)!=0.0)
CSparse::SparseSet(s0,i,j,a.Get(i,j));
//---
CSparse::SparseCopy(s0,s1);
CSparse::SparseConvertToSKS(s1);
CSparse::SparseCopyToSKS(s0,s2);
CSparse::SparseCopyToSKSBuf(s0,s3);
CAp::SetErrorFlag(result,!CSparse::SparseIsSKS(s1),"testsparseunit.ap:591");
CAp::SetErrorFlag(result,CSparse::SparseIsCRS(s1),"testsparseunit.ap:592");
CAp::SetErrorFlag(result,CSparse::SparseIsHash(s1),"testsparseunit.ap:593");
CAp::SetErrorFlag(result,!CSparse::SparseIsSKS(s2),"testsparseunit.ap:594");
CAp::SetErrorFlag(result,CSparse::SparseIsCRS(s2),"testsparseunit.ap:595");
CAp::SetErrorFlag(result,CSparse::SparseIsHash(s2),"testsparseunit.ap:596");
CAp::SetErrorFlag(result,!CSparse::SparseIsSKS(s3),"testsparseunit.ap:597");
CAp::SetErrorFlag(result,CSparse::SparseIsCRS(s3),"testsparseunit.ap:598");
CAp::SetErrorFlag(result,CSparse::SparseIsHash(s3),"testsparseunit.ap:599");
for(i=0; i<n; i++)
for(j=0; j<n; j++)
{
v1=a.Get(i,j);
v0=CSparse::SparseGet(s1,i,j);
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:605");
v0=CSparse::SparseGet(s2,i,j);
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:607");
v0=CSparse::SparseGet(s3,i,j);
CAp::SetErrorFlag(result,v0!=v1,"testsparseunit.ap:609");
}
//--- Increase problem sparcity and try one more time.
//--- Stop after testing NZ=0.
if(nz==0)
break;
nz=(int)MathMin((int)MathRound(nz*0.95),nz-1);
}
}
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing CRS-specific functionality. |
//| On failure sets ErrorFlag, on success does not touch it. |
//+------------------------------------------------------------------+
void CTestSparseUnit::CRSTest(bool errorflag)
{
//--- create variables
int n=0;
int m=0;
int nz=0;
double pnz=0;
CHighQualityRandState rs;
CSparseMatrix s0;
CSparseMatrix s1;
CSparseMatrix s2;
CSparseMatrix s3;
CSparseMatrix s4;
int i=0;
int j=0;
CHighQualityRand::HQRndRandomize(rs);
for(n=1; n<=10; n++)
{
for(m=1; m<=10; m++)
{
nz=n*m;
while(true)
{
//--- Generate N*N matrix where probability of non-diagonal element
//--- being non-zero is PNZ. We also generate D and U - subdiagonal
//--- and superdiagonal profile sizes.
//--- Create matrix with either general SKS or banded SKS constructor function
if(n>1)
pnz=(double)nz/(double)(m*n);
else
pnz=1.0;
//--- Generate random matrix in HASH format (testing SparseExists()
//--- during process), copy it to CRS format, compare with original
CSparse::SparseCreate(m,n,(int)MathRound(nz*CHighQualityRand::HQRndUniformR(rs)),s0);
for(i=0; i<m; i++)
for(j=0; j<n; j++)
{
CAp::SetErrorFlag(errorflag,CSparse::SparseExists(s0,i,j),"testsparseunit.ap:676");
if(CHighQualityRand::HQRndUniformR(rs)<=pnz)
{
CSparse::SparseSet(s0,i,j,CHighQualityRand::HQRndNormal(rs));
CAp::SetErrorFlag(errorflag,!CSparse::SparseExists(s0,i,j),"testsparseunit.ap:680");
}
}
CSparse::SparseCopyToCRS(s0,s1);
CAp::SetErrorFlag(errorflag,!CSparse::SparseIsCRS(s1),"testsparseunit.ap:684");
for(i=0; i<m; i++)
for(j=0; j<n; j++)
{
CAp::SetErrorFlag(errorflag,CSparse::SparseGet(s0,i,j)!=CSparse::SparseGet(s1,i,j),"testsparseunit.ap:688");
CAp::SetErrorFlag(errorflag,CSparse::SparseExists(s0,i,j) && !CSparse::SparseExists(s1,i,j),"testsparseunit.ap:689");
CAp::SetErrorFlag(errorflag,CSparse::SparseExists(s1,i,j) && !CSparse::SparseExists(s0,i,j),"testsparseunit.ap:690");
}
if(errorflag)
return;
//--- Check transposition
CSparse::SparseCopyToCRS(s1,s2);
CSparse::SparseTransposeCRS(s2);
CAp::SetErrorFlag(errorflag,!CSparse::SparseIsCRS(s2),"testsparseunit.ap:700");
CAp::SetErrorFlag(errorflag,CSparse::SparseGetNRows(s1)!=CSparse::SparseGetNCols(s2),"testsparseunit.ap:701");
CAp::SetErrorFlag(errorflag,CSparse::SparseGetNCols(s1)!=CSparse::SparseGetNRows(s2),"testsparseunit.ap:702");
if(errorflag)
return;
//---
for(i=0; i<m; i++)
for(j=0; j<n; j++)
CAp::SetErrorFlag(errorflag,CSparse::SparseGet(s1,i,j)!=CSparse::SparseGet(s2,j,i),"testsparseunit.ap:707");
if(errorflag)
return;
//--- Check transposition
CSparse::SparseCopyTransposeCRS(s1,s3);
CAp::SetErrorFlag(errorflag,!CSparse::SparseIsCRS(s3),"testsparseunit.ap:715");
CAp::SetErrorFlag(errorflag,CSparse::SparseGetNRows(s1)!=CSparse::SparseGetNCols(s3),"testsparseunit.ap:716");
CAp::SetErrorFlag(errorflag,CSparse::SparseGetNCols(s1)!=CSparse::SparseGetNRows(s3),"testsparseunit.ap:717");
if(errorflag)
return;
//---
for(i=0; i<m; i++)
for(j=0; j<n; j++)
CAp::SetErrorFlag(errorflag,CSparse::SparseGet(s1,i,j)!=CSparse::SparseGet(s3,j,i),"testsparseunit.ap:722");
if(errorflag)
return;
//--- Check transposition
CSparse::SparseCopyTransposeCRS(s1,s4);
CAp::SetErrorFlag(errorflag,!CSparse::SparseIsCRS(s4),"testsparseunit.ap:730");
CAp::SetErrorFlag(errorflag,CSparse::SparseGetNRows(s1)!=CSparse::SparseGetNCols(s4),"testsparseunit.ap:731");
CAp::SetErrorFlag(errorflag,CSparse::SparseGetNCols(s1)!=CSparse::SparseGetNRows(s4),"testsparseunit.ap:732");
if(errorflag)
return;
for(i=0; i<m; i++)
for(j=0; j<n; j++)
CAp::SetErrorFlag(errorflag,CSparse::SparseGet(s1,i,j)!=CSparse::SparseGet(s4,j,i),"testsparseunit.ap:737");
if(errorflag)
return;
//--- Increase problem sparcity and try one more time.
//--- Stop after testing NZ=0.
if(nz==0)
break;
nz=MathMin((int)MathRound(nz*0.95),nz-1);
}
}
}
}
//+------------------------------------------------------------------+
//| Function for testing serialization. |
//| On failure sets ErrorFlag, on success does not touch it. |
//+------------------------------------------------------------------+
void CTestSparseUnit::TestSerialize(bool errorflag)
{
//--- create variables
int n=0;
int m=0;
int mtype=0;
int nz=0;
double pnz=0;
CHighQualityRandState rs;
CSparseMatrix s0;
CSparseMatrix s1;
CSparseMatrix s2;
CSparseMatrix s3;
CSparseMatrix s4;
int i=0;
int j=0;
CHighQualityRand::HQRndRandomize(rs);
for(n=1; n<=10; n++)
{
for(m=1; m<=10; m++)
{
for(mtype=0; mtype<=m_maxtype; mtype++)
{
if(mtype==2 && m!=n)
continue;
nz=n*m;
while(true)
{
//--- Generate N*N matrix where probability of non-diagonal element
//--- being non-zero is PNZ.
if(n>1)
pnz=(double)nz/(double)(m*n);
else
pnz=0.5;
//--- Generate random matrix in HASH format (testing SparseExists()
//--- during process), convert it to HASH/CRS/SKS format
CSparse::SparseCreate(m,n,(int)MathRound(nz*CHighQualityRand::HQRndUniformR(rs)),s0);
for(i=0; i<m; i++)
for(j=0; j<n; j++)
{
CAp::SetErrorFlag(errorflag,CSparse::SparseExists(s0,i,j),"testsparseunit.ap:797");
if(CHighQualityRand::HQRndUniformR(rs)<=pnz)
{
CSparse::SparseSet(s0,i,j,CHighQualityRand::HQRndNormal(rs));
CAp::SetErrorFlag(errorflag,!CSparse::SparseExists(s0,i,j),"testsparseunit.ap:801");
}
}
CSparse::SparseConvertTo(s0,mtype);
CAp::SetErrorFlag(errorflag,CSparse::SparseGetMatrixType(s0)!=mtype,"testsparseunit.ap:805");
//--- Check copy-through-serialization
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CSparse::SparseAlloc(_local_serializer,s0);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CSparse::SparseSerialize(_local_serializer,s0);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CSparse::SparseUnserialize(_local_serializer,s1);
_local_serializer.Stop();
}
CAp::SetErrorFlag(errorflag,CSparse::SparseGetMatrixType(s1)!=mtype,"testsparseunit.ap:811");
CAp::SetErrorFlag(errorflag,CSparse::SparseGetNRows(s0)!=CSparse::SparseGetNRows(s1),"testsparseunit.ap:812");
CAp::SetErrorFlag(errorflag,CSparse::SparseGetNCols(s0)!=CSparse::SparseGetNCols(s1),"testsparseunit.ap:813");
if(errorflag)
return;
for(i=0; i<m; i++)
for(j=0; j<n; j++)
CAp::SetErrorFlag(errorflag,CSparse::SparseGet(s0,i,j)!=(double)(CSparse::SparseGet(s1,i,j)),"testsparseunit.ap:818");
if(errorflag)
return;
//--- Increase problem sparcity and try one more time.
//--- Stop after testing NZ=0.
if(nz==0)
break;
nz=MathMin((int)MathRound(nz*0.95),nz-1);
}
}
}
}
}
//+------------------------------------------------------------------+
//| Function for testing basic functional |
//+------------------------------------------------------------------+
bool CTestSparseUnit::BasicFuncTest(void)
{
//--- create variables
bool result=false;
CSparseMatrix s;
int n=0;
int m=0;
int i=0;
int j=0;
int i1=0;
int j1=0;
int uppercnt=0;
int lowercnt=0;
CMatrixDouble a;
n=10;
m=10;
for(i=1; i<m; i++)
{
for(j=1; j<n; j++)
{
CSparse::SparseCreate(i,j,1,s);
a=matrix<double>::Zeros(i,j);
//--- Checking for Matrix with hash table type
uppercnt=0;
lowercnt=0;
for(i1=0; i1<i; i1++)
for(j1=0; j1<j; j1++)
{
if(j1>i1)
uppercnt++;
if(j1<i1)
lowercnt++;
a.Set(i1,j1,i1+j1+(double)((i+j)*(m+n))/2.0+1);
CSparse::SparseSet(s,i1,j1,i1+j1+(double)((i+j)*(m+n))/(double)2);
CSparse::SparseAdd(s,i1,j1,1);
if(a.Get(i1,j1)!=CSparse::SparseGet(s,i1,j1))
{
result=true;
return(result);
}
}
for(i1=0; i1<MathMin(i,j); i1++)
if(a.Get(i1,i1)!=CSparse::SparseGetDiagonal(s,i1))
{
result=true;
return(result);
}
CAp::SetErrorFlag(result,CSparse::SparseGetUpperCount(s)!=uppercnt,"testsparseunit.ap:887");
CAp::SetErrorFlag(result,CSparse::SparseGetLowerCount(s)!=lowercnt,"testsparseunit.ap:888");
//--- Checking for Matrix with CRS type
CSparse::SparseConvertToCRS(s);
for(i1=0; i1<i; i1++)
for(j1=0; j1<j; j1++)
if(a.Get(i1,j1)!=CSparse::SparseGet(s,i1,j1))
{
result=true;
return(result);
}
for(i1=0; i1<MathMin(i,j); i1++)
if(a.Get(i1,i1)!=CSparse::SparseGetDiagonal(s,i1))
{
result=true;
return(result);
}
CAp::SetErrorFlag(result,CSparse::SparseGetUpperCount(s)!=uppercnt,"testsparseunit.ap:907");
CAp::SetErrorFlag(result,CSparse::SparseGetLowerCount(s)!=lowercnt,"testsparseunit.ap:908");
}
}
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing Level 2 unsymmetric linear algebra functions|
//| Additionally it tests SparseGet() for several matrix formats. |
//| Returns True on failure. |
//+------------------------------------------------------------------+
bool CTestSparseUnit::TestLevel2Unsymmetric(void)
{
//--- create variables
bool result=false;
int m=0;
int n=0;
CRowDouble x0;
CRowDouble x1;
CRowDouble y0;
CRowDouble y1;
int i=0;
int j=0;
CMatrixDouble a;
CSparseMatrix s0;
CSparseMatrix sa;
double eps=0;
double v=0;
int ix=0;
int iy=0;
double alpha=0;
double beta=0;
int ops=0;
int opm=0;
int opn=0;
int alphakind=0;
int betakind=0;
CSparseGenerator g;
CHighQualityRandState rs;
int i_=0;
eps=10000*CMath::m_machineepsilon;
CHighQualityRand::HQRndRandomize(rs);
//--- Test linear algebra functions
for(m=1; m<=20; m++)
{
for(n=1; n<=20; n++)
{
InitGenerator(m,n,0,0,g);
while(GenerateNext(g,a,sa))
{
//--- Convert SA to desired storage format:
//--- * to CRS if M<>N
//--- * with 50% probability to CRS or SKS, if M=N
if(m!=n || CHighQualityRand::HQRndUniformR(rs)<0.5)
CSparse::SparseCopyToCRS(sa,s0);
else
CSparse::SparseCopyToSKS(sa,s0);
//--- Test SparseGet() for SA and S0 against matrix returned in A
for(i=0; i<m; i++)
for(j=0; j<n; j++)
{
CAp::SetErrorFlag(result,MathAbs(CSparse::SparseGet(sa,i,j)-a.Get(i,j))>eps,"testsparseunit.ap:966");
CAp::SetErrorFlag(result,MathAbs(CSparse::SparseGet(s0,i,j)-a.Get(i,j))>eps,"testsparseunit.ap:967");
}
//--- Test SparseMV
x0=vector<double>::Zeros(n);
x1=vector<double>::Zeros(n);
for(j=0; j<n; j++)
x0.Set(j,CHighQualityRand::HQRndUniformR(rs)-0.5);
x1=x0;
CSparse::SparseMV(s0,x0,y0);
CAp::SetErrorFlag(result,CAp::Len(y0)<m,"testsparseunit.ap:981");
if(result)
return(result);
for(i=0; i<m; i++)
{
v=CAblasF::RDotVR(n,x1,a,i);
CAp::SetErrorFlag(result,MathAbs(v-y0[i])>eps,"testsparseunit.ap:987");
}
//--- Test SparseMTV
x0=vector<double>::Zeros(m);
for(j=0; j<m; j++)
x0.Set(j,CHighQualityRand::HQRndUniformR(rs)-0.5);
x1=x0;
CSparse::SparseMTV(s0,x0,y0);
CAp::SetErrorFlag(result,CAp::Len(y0)<n,"testsparseunit.ap:1001");
if(result)
return(result);
for(j=0; j<n; j++)
{
v=x1.DotC(a,j);
CAp::SetErrorFlag(result,MathAbs(v-y0[j])>eps,"testsparseunit.ap:1007");
}
//--- Sparse GEMV
for(ops=0; ops<=1; ops++)
{
for(alphakind=0; alphakind<=1; alphakind++)
{
for(betakind=0; betakind<=1; betakind++)
{
//--- Prepare inputs for testing
ix=CHighQualityRand::HQRndUniformI(rs,50);
iy=CHighQualityRand::HQRndUniformI(rs,50);
alpha=alphakind*CHighQualityRand::HQRndNormal(rs);
beta=betakind*CHighQualityRand::HQRndNormal(rs);
if(ops==0)
{
opm=m;
opn=n;
}
else
{
opm=n;
opn=m;
}
x0=vector<double>::Zeros(ix+opn);
for(j=0; j<ix+opn; j++)
x0.Set(j,CHighQualityRand::HQRndUniformR(rs)-0.5);
x1=x0;
y0=vector<double>::Zeros(iy+opm);
for(j=0; j<iy+opm; j++)
y0.Set(j,CHighQualityRand::HQRndUniformR(rs)-0.5);
y1=y0;
//--- Prepare reference result in Y1
for(i=0; i<opm; i++)
{
v=0;
for(j=0; j<=opn-1; j++)
{
if(ops==0)
v+=a.Get(i,j)*x1[ix+j];
else
v+=a.Get(j,i)*x1[ix+j];
}
y1.Set(iy+i,alpha*v+beta*y1[iy+i]);
}
//--- Test
CSparse::SparseGemV(s0,alpha,ops,x0,ix,beta,y0,iy);
for(j=0; j<iy+opm; j++)
CAp::SetErrorFlag(result,MathAbs(y0[j]-y1[j])>eps,"testsparseunit.ap:1068");
}
}
}
//--- Test SparseMV2
if(m==n)
{
x0=vector<double>::Zeros(n);
for(j=0; j<n; j++)
x0.Set(j,CHighQualityRand::HQRndUniformR(rs)-0.5);
x1=x0;
CSparse::SparseMV2(s0,x0,y0,y1);
CAp::SetErrorFlag(result,CAp::Len(y0)<n,"testsparseunit.ap:1084");
CAp::SetErrorFlag(result,CAp::Len(y1)<n,"testsparseunit.ap:1085");
if(result)
return(result);
for(j=0; j<n; j++)
{
v=x1.DotR(a,j);
CAp::SetErrorFlag(result,MathAbs(v-y0[j])>eps,"testsparseunit.ap:1091");
v=x1.DotC(a,j);
CAp::SetErrorFlag(result,MathAbs(v-y1[j])>eps,"testsparseunit.ap:1093");
}
}
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing Level 3 unsymmetric linear algebra functions|
//| Additionally it tests SparseGet() for several matrix formats. |
//| Returns True on failure. |
//+------------------------------------------------------------------+
bool CTestSparseUnit::TestLevel3Unsymmetric(void)
{
//--- create variables
bool result=false;
int m=0;
int n=0;
int k=0;
CMatrixDouble x0;
CMatrixDouble x1;
CMatrixDouble y0;
CMatrixDouble y1;
int i=0;
int j=0;
CMatrixDouble a;
CSparseMatrix s0;
CSparseMatrix sa;
double eps=0;
double v=0;
CSparseGenerator g;
CHighQualityRandState rs;
int i_=0;
eps=10000*CMath::m_machineepsilon;
CHighQualityRand::HQRndRandomize(rs);
//--- Test linear algebra functions
for(m=1; m<=20; m++)
{
for(n=1; n<=20; n++)
{
InitGenerator(m,n,0,0,g);
while(GenerateNext(g,a,sa))
{
//--- Choose matrix width K
k=1+CHighQualityRand::HQRndUniformI(rs,20);
//--- Convert SA to desired storage format:
//--- * to CRS if M<>N
//--- * with 50% probability to CRS or SKS, if M=N
if(m!=n || CHighQualityRand::HQRndUniformR(rs)<0.5)
CSparse::SparseCopyToCRS(sa,s0);
else
CSparse::SparseCopyToSKS(sa,s0);
//--- Test SparseGet() for SA and S0 against matrix returned in A
for(i=0; i<m; i++)
for(j=0; j<n; j++)
{
CAp::SetErrorFlag(result,CSparse::SparseGet(sa,i,j)!=a.Get(i,j),"testsparseunit.ap:1156");
CAp::SetErrorFlag(result,CSparse::SparseGet(s0,i,j)!=a.Get(i,j),"testsparseunit.ap:1157");
}
//--- Test SparseMV
x0=matrix<double>::Zeros(n,k);
for(i=0; i<n; i++)
for(j=0; j<k; j++)
x0.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
x1=x0;
CSparse::SparseMM(s0,x0,k,y0);
CAp::SetErrorFlag(result,CAp::Rows(y0)<m,"testsparseunit.ap:1172");
CAp::SetErrorFlag(result,CAp::Cols(y0)<k,"testsparseunit.ap:1173");
if(result)
return(result);
for(i=0; i<m; i++)
for(j=0; j<k; j++)
{
v=0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*x1.Get(i_,j);
CAp::SetErrorFlag(result,MathAbs(v-y0.Get(i,j))>eps,"testsparseunit.ap:1180");
}
//--- Test SparseMTM
x0=matrix<double>::Zeros(m,k);
x1=matrix<double>::Zeros(m,k);
for(i=0; i<m; i++)
for(j=0; j<k; j++)
x0.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
x1=x0;
CSparse::SparseMTM(s0,x0,k,y0);
CAp::SetErrorFlag(result,CAp::Rows(y0)<n,"testsparseunit.ap:1195");
CAp::SetErrorFlag(result,CAp::Cols(y0)<k,"testsparseunit.ap:1196");
if(result)
return(result);
for(i=0; i<n; i++)
for(j=0; j<k; j++)
{
v=0.0;
for(i_=0; i_<m; i_++)
v+=a.Get(i_,i)*x1.Get(i_,j);
CAp::SetErrorFlag(result,MathAbs(v-y0.Get(i,j))>eps,"testsparseunit.ap:1203");
}
//--- Test SparseMM2
if(m==n)
{
x0=matrix<double>::Zeros(n,k);
for(i=0; i<n; i++)
for(j=0; j<k; j++)
x0.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
x1=x0;
CSparse::SparseMM2(s0,x0,k,y0,y1);
CAp::SetErrorFlag(result,CAp::Rows(y0)<n,"testsparseunit.ap:1220");
CAp::SetErrorFlag(result,CAp::Cols(y0)<k,"testsparseunit.ap:1221");
CAp::SetErrorFlag(result,CAp::Rows(y1)<n,"testsparseunit.ap:1222");
CAp::SetErrorFlag(result,CAp::Cols(y1)<k,"testsparseunit.ap:1223");
if(result)
return(result);
for(i=0; i<n; i++)
for(j=0; j<k; j++)
{
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*x1.Get(i_,j);
CAp::SetErrorFlag(result,MathAbs(v-y0.Get(i,j))>eps,"testsparseunit.ap:1230");
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i_,i)*x1.Get(i_,j);
CAp::SetErrorFlag(result,MathAbs(v-y1.Get(i,j))>eps,"testsparseunit.ap:1232");
}
}
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing Level 2 symmetric linear algebra functions. |
//| Additionally it tests SparseGet() for several matrix formats. |
//| Returns True on failure. |
//+------------------------------------------------------------------+
bool CTestSparseUnit::TestLevel2Symmetric(void)
{
//--- create variables
bool result=false;
int n=0;
CRowDouble x0;
CRowDouble x1;
CRowDouble y0;
CRowDouble y1;
int i=0;
int j=0;
CMatrixDouble a;
CSparseMatrix s0;
CSparseMatrix s1;
CSparseMatrix sa;
double eps=0;
double v=0;
double va=0;
double vb=0;
CSparseGenerator g;
CHighQualityRandState rs;
bool isupper=false;
int triangletype=0;
int i_=0;
eps=10000*CMath::m_machineepsilon;
result=false;
CHighQualityRand::HQRndRandomize(rs);
//--- Test linear algebra functions
for(n=1; n<=20; n++)
{
for(triangletype=-1; triangletype<=1; triangletype++)
{
isupper=(CHighQualityRand::HQRndUniformR(rs)>0.5);
if(triangletype<0)
isupper=false;
if(triangletype>0)
isupper=true;
InitGenerator(n,n,0,triangletype,g);
while(GenerateNext(g,a,sa))
{
//--- Convert SA to desired storage format:
//--- * S0 stores unmodified copy
//--- * S1 stores copy with unmodified triangle corresponding
//--- to IsUpper and another triangle being spoiled by random
//--- trash
CSparse::SparseCopyToHash(sa,s1);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
if((j<i && isupper) || (j>i && !isupper))
CSparse::SparseSet(s1,i,j,CHighQualityRand::HQRndUniformR(rs));
if(CHighQualityRand::HQRndUniformR(rs)<0.5)
{
CSparse::SparseCopyToCRS(sa,s0);
CSparse::SparseConvertToCRS(s1);
}
else
{
CSparse::SparseCopyToSKS(sa,s0);
CSparse::SparseConvertToSKS(s1);
}
//--- Test SparseGet() for SA and S0 against matrix returned in A
for(i=0; i<n; i++)
for(j=0; j<n; j++)
{
CAp::SetErrorFlag(result,MathAbs(CSparse::SparseGet(sa,i,j)-a.Get(i,j))>eps,"testsparseunit.ap:1311");
CAp::SetErrorFlag(result,MathAbs(CSparse::SparseGet(s0,i,j)-a.Get(i,j))>eps,"testsparseunit.ap:1312");
CAp::SetErrorFlag(result,(j<i && triangletype==1) && CSparse::SparseGet(s0,i,j)!=0.0,"testsparseunit.ap:1313");
CAp::SetErrorFlag(result,(j>i && triangletype==-1) && CSparse::SparseGet(s0,i,j)!=0.0,"testsparseunit.ap:1314");
}
//--- Before we proceed with testing, update empty triangle of A
//--- with its copy from another part of the matrix.
for(i=0; i<n; i++)
for(j=0; j<n; j++)
if((j<i && isupper) || (j>i && !isupper))
a.Set(i,j,a.Get(j,i));
//--- Test SparseSMV
x0=vector<double>::Zeros(n);
for(j=0; j<n; j++)
x0.Set(j,CHighQualityRand::HQRndUniformR(rs)-0.5);
x1=x0;
CSparse::SparseSMV(s0,isupper,x0,y0);
CAp::SetErrorFlag(result,CAp::Len(y0)<n,"testsparseunit.ap:1337");
if(result)
return(result);
for(i=0; i<n; i++)
{
v=x1.DotR(a,i);
CAp::SetErrorFlag(result,MathAbs(v-y0[i])>eps,"testsparseunit.ap:1343");
}
CSparse::SparseSMV(s1,isupper,x0,y1);
CAp::SetErrorFlag(result,CAp::Len(y1)<n,"testsparseunit.ap:1346");
if(result)
return(result);
for(i=0; i<n; i++)
{
v=x1.DotR(a,i);
CAp::SetErrorFlag(result,MathAbs(v-y1[i])>eps,"testsparseunit.ap:1352");
}
//--- Test SparseVSMV
x0=vector<double>::Zeros(n);
for(j=0; j<n; j++)
x0.Set(j,CHighQualityRand::HQRndUniformR(rs)-0.5);
x1=x0;
vb=0.0;
for(i=0; i<n; i++)
for(j=0; j<n; j++)
vb+=x1[i]*a.Get(i,j)*x1[j];
va=CSparse::SparseVSMV(s0,isupper,x0);
CAp::SetErrorFlag(result,MathAbs(va-vb)>eps,"testsparseunit.ap:1370");
va=CSparse::SparseVSMV(s1,isupper,x0);
CAp::SetErrorFlag(result,MathAbs(va-vb)>eps,"testsparseunit.ap:1372");
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing Level 2 symmetric linear algebra functions. |
//| Additionally it tests SparseGet() for several matrix formats. |
//| Returns True on failure. |
//+------------------------------------------------------------------+
bool CTestSparseUnit::TestLevel3Symmetric(void)
{
//--- create variables
bool result=false;
int n=0;
int k=0;
CMatrixDouble x0;
CMatrixDouble x1;
CMatrixDouble y0;
CMatrixDouble y1;
int i=0;
int j=0;
CMatrixDouble a;
CSparseMatrix s0;
CSparseMatrix s1;
CSparseMatrix sa;
double eps=0;
double v=0;
CSparseGenerator g;
CHighQualityRandState rs;
bool isupper=false;
int triangletype=0;
int i_=0;
eps=10000*CMath::m_machineepsilon;
CHighQualityRand::HQRndRandomize(rs);
//--- Test linear algebra functions
for(n=1; n<=20; n++)
{
for(triangletype=-1; triangletype<=1; triangletype++)
{
isupper=CHighQualityRand::HQRndUniformR(rs)>0.5;
if(triangletype<0)
isupper=false;
if(triangletype>0)
isupper=true;
InitGenerator(n,n,0,triangletype,g);
while(GenerateNext(g,a,sa))
{
//--- Choose matrix width K
k=1+CHighQualityRand::HQRndUniformI(rs,20);
//--- Convert SA to desired storage format:
//--- * S0 stores unmodified copy
//--- * S1 stores copy with unmodified triangle corresponding
//--- to IsUpper and another triangle being spoiled by random
//--- trash
CSparse::SparseCopyToHash(sa,s1);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
if((j<i && isupper) || (j>i && !isupper))
CSparse::SparseSet(s1,i,j,CHighQualityRand::HQRndUniformR(rs));
if(CHighQualityRand::HQRndUniformR(rs)<0.5)
{
CSparse::SparseCopyToCRS(sa,s0);
CSparse::SparseConvertToCRS(s1);
}
else
{
CSparse::SparseCopyToSKS(sa,s0);
CSparse::SparseConvertToSKS(s1);
}
//--- Test SparseGet() for SA and S0 against matrix returned in A
for(i=0; i<n; i++)
for(j=0; j<n; j++)
{
CAp::SetErrorFlag(result,(double)(MathAbs(CSparse::SparseGet(sa,i,j)-a.Get(i,j)))>eps,"testsparseunit.ap:1453");
CAp::SetErrorFlag(result,(double)(MathAbs(CSparse::SparseGet(s0,i,j)-a.Get(i,j)))>eps,"testsparseunit.ap:1454");
CAp::SetErrorFlag(result,(j<i && triangletype==1) && CSparse::SparseGet(s0,i,j)!=0.0,"testsparseunit.ap:1455");
CAp::SetErrorFlag(result,(j>i && triangletype==-1) && CSparse::SparseGet(s0,i,j)!=0.0,"testsparseunit.ap:1456");
}
//--- Before we proceed with testing, update empty triangle of A
//--- with its copy from another part of the matrix.
for(i=0; i<n; i++)
for(j=0; j<n; j++)
if((j<i && isupper) || (j>i && !isupper))
a.Set(i,j,a.Get(j,i));
//--- Test SparseSMM
x0=matrix<double>::Zeros(n,k);
for(i=0; i<n; i++)
for(j=0; j<k; j++)
x0.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
x1=x0;
CSparse::SparseSMM(s0,isupper,x0,k,y0);
CAp::SetErrorFlag(result,CAp::Rows(y0)<n,"testsparseunit.ap:1480");
CAp::SetErrorFlag(result,CAp::Cols(y0)<k,"testsparseunit.ap:1481");
if(result)
return(result);
//---
for(i=0; i<n; i++)
for(j=0; j<k; j++)
{
v=0.0;
for(i_=0; i_<n; i_++)
v+=a.Get(i,i_)*x1.Get(i_,j);
CAp::SetErrorFlag(result,MathAbs(v-y0.Get(i,j))>eps,"testsparseunit.ap:1488");
}
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing sparse symmetric permutations |
//+------------------------------------------------------------------+
bool CTestSparseUnit::TestSymmetricPerm(void)
{
//--- create variables
bool result=false;
int n=0;
int functype=0;
int i=0;
int j=0;
CHighQualityRandState rs;
bool isupper=false;
double eps=0;
double nzprob=0;
CMatrixDouble da;
CMatrixDouble db;
CSparseMatrix sa;
CSparseMatrix sb;
CRowInt ptbl;
CRowInt pprod;
eps=10*CMath::m_machineepsilon;
CHighQualityRand::HQRndRandomize(rs);
//--- Try various N and fill factors
for(n=1; n<=20; n++)
{
nzprob=1.0;
while((double)(nzprob)>=(double)(0.1/(n*n)))
{
//--- Generate matrix with desired fill factor, randomly select one triangle,
//--- generate random permutation (in table and product form)
CSparse::SparseCreate(n,n,0,sa);
da=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
if(CHighQualityRand::HQRndUniformR(rs)<(double)(nzprob))
{
da.Set(i,j,CHighQualityRand::HQRndNormal(rs));
CSparse::SparseSet(sa,i,j,da.Get(i,j));
}
}
}
CSparse::SparseConvertToCRS(sa);
isupper=CHighQualityRand::HQRndNormal(rs)>0.0;
for(i=0; i<n; i++)
for(j=0; j<=i; j++)
{
if(isupper)
da.Set(i,j,da.Get(j,i));
else
da.Set(j,i,da.Get(i,j));
}
ptbl.Resize(n);
pprod.Resize(n);
for(i=0; i<n; i++)
ptbl.Set(i,i);
for(i=0; i<n; i++)
{
pprod.Set(i,i+CHighQualityRand::HQRndUniformI(rs,n-i));
j=ptbl[i];
ptbl.Set(i,ptbl[pprod[i]]);
ptbl.Set(pprod[i],j);
}
db=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
db.Set(ptbl[i],ptbl[j],da.Get(i,j));
//--- Select function to test, run and test
functype=CHighQualityRand::HQRndUniformI(rs,2);
if(functype==0)
CSparse::SparseSymmPermTblBuf(sa,isupper,ptbl,sb);
if(functype==1)
CSparse::SparseSymmPermTbl(sa,isupper,ptbl,sb);
CAp::SetErrorFlag(result,!CSparse::SparseIsCRS(sb),"testsparseunit.ap:1582");
CAp::SetErrorFlag(result,CSparse::SparseGetNRows(sb)!=n,"testsparseunit.ap:1583");
CAp::SetErrorFlag(result,CSparse::SparseGetNCols(sb)!=n,"testsparseunit.ap:1584");
if(result)
return(result);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
{
CAp::SetErrorFlag(result,(isupper && j>=i) && MathAbs(db.Get(i,j)-CSparse::SparseGet(sb,i,j))>eps,"testsparseunit.ap:1590");
CAp::SetErrorFlag(result,(isupper && j<i) && CSparse::SparseGet(sb,i,j)!=0.0,"testsparseunit.ap:1591");
}
//--- Increase sparsity
nzprob*=0.75;
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing Level 2 triangular linear algebra functions.|
//| Returns True on failure. |
//+------------------------------------------------------------------+
bool CTestSparseUnit::TestLevel2Triangular(void)
{
//--- create variables
bool result=false;
int n=0;
CRowDouble x0;
CRowDouble x1;
CRowDouble y0;
CRowDouble y1;
CRowDouble ey;
int i=0;
int j=0;
int i1=0;
int j1=0;
CMatrixDouble a;
CMatrixDouble ea;
CSparseMatrix s0;
CSparseMatrix s1;
CSparseMatrix sa;
double eps=0;
double v=0;
CSparseGenerator g;
CHighQualityRandState rs;
bool isupper=false;
bool isunit=false;
int optype=0;
int triangletype=0;
int i_=0;
eps=10000*CMath::m_machineepsilon;
CHighQualityRand::HQRndRandomize(rs);
//--- Test sparseTRMV
for(n=1; n<=20; n++)
{
for(triangletype=-1; triangletype<=1; triangletype++)
{
isupper=CHighQualityRand::HQRndUniformR(rs)>0.5;
if(triangletype<0)
isupper=false;
if(triangletype>0)
isupper=true;
InitGenerator(n,n,0,triangletype,g);
while(GenerateNext(g,a,sa))
{
//--- Settings (IsUpper was already set, handle the rest)
isunit=CHighQualityRand::HQRndUniformR(rs)<0.5;
optype=CHighQualityRand::HQRndUniformI(rs,2);
//--- Convert SA to desired storage format:
//--- * S0 stores unmodified copy
//--- * S1 stores copy with unmodified triangle corresponding
//--- to IsUpper and another triangle being spoiled by random
//--- trash
CSparse::SparseCopyToHash(sa,s1);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
if((j<i && isupper) || (j>i && !isupper))
CSparse::SparseSet(s1,i,j,CHighQualityRand::HQRndUniformR(rs));
if(CHighQualityRand::HQRndUniformR(rs)<0.5)
{
CSparse::SparseCopyToCRS(sa,s0);
CSparse::SparseConvertToCRS(s1);
}
else
{
CSparse::SparseCopyToSKS(sa,s0);
CSparse::SparseConvertToSKS(s1);
}
//--- Generate "effective A"
ea=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
if((j>=i && isupper) || (j<=i && !isupper))
{
i1=i;
j1=j;
if(optype==1)
CApServ::Swap(i1,j1);
ea.Set(i1,j1,a.Get(i,j));
if(isunit && i1==j1)
ea.Set(i1,j1,1.0);
}
//--- Test SparseTRMV
x0=vector<double>::Zeros(n);
for(j=0; j<n; j++)
x0.Set(j,CHighQualityRand::HQRndUniformR(rs)-0.5);
x1=x0;
CSparse::SparseTRMV(s0,isupper,isunit,optype,x0,y0);
CAp::SetErrorFlag(result,CAp::Len(y0)<n,"testsparseunit.ap:1706");
if(result)
return(result);
//---
for(i=0; i<n; i++)
{
v=x1.DotR(ea,i);
CAp::SetErrorFlag(result,MathAbs(v-y0[i])>eps,"testsparseunit.ap:1712");
}
//---
CSparse::SparseTRMV(s0,isupper,isunit,optype,x0,y1);
CAp::SetErrorFlag(result,CAp::Len(y1)<n,"testsparseunit.ap:1715");
if(result)
return(result);
for(i=0; i<n; i++)
{
v=x1.DotR(ea,i);
CAp::SetErrorFlag(result,MathAbs(v-y1[i])>eps,"testsparseunit.ap:1721");
}
}
}
}
//--- Test sparseTRSV
for(n=1; n<=20; n++)
{
for(triangletype=-1; triangletype<=1; triangletype++)
{
isupper=CHighQualityRand::HQRndUniformR(rs)>0.5;
if(triangletype==-1)
isupper=false;
if(triangletype==1)
isupper=true;
InitGenerator(n,n,1,triangletype,g);
while(GenerateNext(g,a,sa))
{
//--- Settings (IsUpper was already set, handle the rest)
isunit=CHighQualityRand::HQRndUniformR(rs)<0.5;
optype=CHighQualityRand::HQRndUniformI(rs,2);
//--- Convert SA to desired storage format:
//--- * S0 stores unmodified copy
//--- * S1 stores copy with unmodified triangle corresponding
//--- to IsUpper and another triangle being spoiled by random
//--- trash
CSparse::SparseCopyToHash(sa,s1);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
if((j<i && isupper) || (j>i && !isupper))
CSparse::SparseSet(s1,i,j,CHighQualityRand::HQRndUniformR(rs));
if(CHighQualityRand::HQRndUniformR(rs)<0.5)
{
CSparse::SparseCopyToCRS(sa,s0);
CSparse::SparseConvertToCRS(s1);
}
else
{
CSparse::SparseCopyToSKS(sa,s0);
CSparse::SparseConvertToSKS(s1);
}
//--- Generate "effective A" and EY = inv(EA)*x0
ea=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
if((j>=i && isupper) || (j<=i && !isupper))
{
i1=i;
j1=j;
if(optype==1)
CApServ::Swap(i1,j1);
ea.Set(i1,j1,a.Get(i,j));
if(isunit && i1==j1)
ea.Set(i1,j1,1.0);
}
ey=vector<double>::Zeros(n);
for(i=0; i<n; i++)
ey.Set(i,CHighQualityRand::HQRndUniformR(rs)-0.5);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
v=ey.DotR(ea,i);
x0.Set(i,v);
}
x1=x0;
//--- Test SparseTRSV
CSparse::SparseTRSV(s0,isupper,isunit,optype,x0);
CAp::SetErrorFlag(result,CAp::Len(x0)<n,"testsparseunit.ap:1805");
if(result)
return(result);
//---
for(i=0; i<n; i++)
CAp::SetErrorFlag(result,MathAbs(ey[i]-x0[i])>eps,"testsparseunit.ap:1809");
CSparse::SparseTRSV(s1,isupper,isunit,optype,x1);
CAp::SetErrorFlag(result,CAp::Len(x1)<n,"testsparseunit.ap:1811");
if(result)
return(result);
//---
for(i=0; i<n; i++)
CAp::SetErrorFlag(result,MathAbs(ey[i]-x1[i])>eps,"testsparseunit.ap:1815");
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing basic functional |
//+------------------------------------------------------------------+
bool CTestSparseUnit::BasicFuncRandomTest(void)
{
//--- create variables
bool result=false;
CSparseMatrix s;
int n=0;
int m=0;
int i=0;
int j=0;
int i1=0;
int j1=0;
CMatrixDouble a;
int mfigure=0;
int temp=0;
n=20;
m=20;
mfigure=10;
for(i=1; i<m; i++)
{
for(j=1; j<n; j++)
{
CSparse::SparseCreate(i,j,0,s);
a=matrix<double>::Zeros(i,j);
//--- Checking for Matrix with hash table type
for(i1=0; i1<i; i1++)
for(j1=0; j1<j; j1++)
{
temp=2*CMath::RandomInteger(mfigure)-mfigure;
a.Set(i1,j1,temp);
if(CMath::RandomInteger(2)==0)
{
CSparse::SparseSet(s,i1,j1,temp);
CSparse::SparseSet(s,i1,j1,temp);
}
else
{
CSparse::SparseAdd(s,i1,j1,temp);
CSparse::SparseAdd(s,i1,j1,0);
}
if(a.Get(i1,j1)!=CSparse::SparseGet(s,i1,j1))
{
result=true;
return(result);
}
}
//--- Nulling all elements
for(i1=0; i1<i; i1++)
for(j1=0; j1<j; j1++)
if(CMath::RandomInteger(2)==0)
CSparse::SparseSet(s,i1,j1,0);
else
CSparse::SparseAdd(s,i1,j1,-(1*CSparse::SparseGet(s,i1,j1)));
//--- Again initialization of the matrix and check new values
for(i1=0; i1<i; i1++)
for(j1=0; j1<j; j1++)
{
temp=2*CMath::RandomInteger(mfigure)-mfigure;
a.Set(i1,j1,temp);
if(CMath::RandomInteger(2)==0)
CSparse::SparseSet(s,i1,j1,temp);
else
CSparse::SparseAdd(s,i1,j1,temp);
if((double)(a.Get(i1,j1))!=(double)(CSparse::SparseGet(s,i1,j1)))
{
result=true;
return(result);
}
}
//--- Checking for Matrix with CRS type
CSparse::SparseConvertToCRS(s);
for(i1=0; i1<i; i1++)
for(j1=0; j1<j; j1++)
if((double)(a.Get(i1,j1))!=(double)(CSparse::SparseGet(s,i1,j1)))
{
result=true;
return(result);
}
}
}
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing multyplication matrix with vector |
//+------------------------------------------------------------------+
bool CTestSparseUnit::LinearFunctionsTest(void)
{
//--- create variables
bool result=false;
CSparseMatrix s;
int n=0;
int m=0;
int i=0;
int j=0;
int i1=0;
int j1=0;
double lb=0;
double rb=0;
CMatrixDouble a;
CRowDouble x0;
CRowDouble x1;
CRowDouble ty;
CRowDouble tyt;
CRowDouble y;
CRowDouble yt;
CRowDouble y0;
CRowDouble yt0;
double eps=0;
//--- Accuracy
eps=1000*CMath::m_machineepsilon;
//--- Size of the matrix (m*n)
n=10;
m=10;
//--- Left and right borders, limiting matrix values
lb=-10;
rb=10;
//--- Test linear algebra functions for:
//--- a) sparse matrix converted to CRS from Hash-Table
//--- b) sparse matrix initially created as CRS
for(i=1; i<m; i++)
{
for(j=1; j<n; j++)
{
//--- Prepare test problem
CreateRandom(i,j,-1,-1,-1,-1,a,s);
//--- Initialize temporaries
ty=vector<double>::Zeros(i);
tyt=vector<double>::Zeros(j);
x0=vector<double>::Zeros(j);
x1=vector<double>::Zeros(i);
for(i1=0; i1<j; i1++)
x0.Set(i1,(rb-lb)*CMath::RandomReal()+lb);
for(i1=0; i1<i; i1++)
x1.Set(i1,(rb-lb)*CMath::RandomReal()+lb);
//--- Consider two cases: square matrix, and non-square matrix
if(i!=j)
{
//--- Searching true result
ty=x0.MatMul(a.Transpose()+0)+0;
tyt=x1.MatMul(a)+0;
//--- Multiplication
CSparse::SparseMV(s,x0,y);
CSparse::SparseMTV(s,x1,yt);
//--- Check for MV-result
for(i1=0; i1<i; i1++)
if(MathAbs(y[i1]-ty[i1])>=eps)
{
result=true;
return(result);
}
//--- Check for MTV-result
for(i1=0; i1<j; i1++)
if(MathAbs(yt[i1]-tyt[i1])>=eps)
{
result=true;
return(result);
}
}
else
{
//--- Searching true result
for(i1=0; i1<i; i1++)
for(j1=0; j1<j; j1++)
{
ty.Add(i1,a.Get(i1,j1)*x0[j1]);
tyt.Add(j1,a.Get(i1,j1)*x0[i1]);
}
CSparse::SparseMV(s,x0,y);
CSparse::SparseMTV(s,x0,yt);
CSparse::SparseMV2(s,x0,y0,yt0);
//--- Check for MV2-result
for(i1=0; i1<i; i1++)
if(MathAbs(y0[i1]-ty[i1])>=eps || MathAbs(yt0[i1]-tyt[i1])>=eps)
{
result=true;
return(result);
}
//--- Check for MV- and MTV-result by help MV2
for(i1=0; i1<i; i1++)
if(MathAbs(y0[i1]-y[i1])>eps || MathAbs(yt0[i1]-yt[i1])>eps)
{
result=true;
return(result);
}
}
}
}
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing multyplication for simmetric matrix with |
//| vector |
//+------------------------------------------------------------------+
bool CTestSparseUnit::LinearFunctionSSTest(void)
{
//--- create variables
bool result=false;
CSparseMatrix s;
int m=0;
int i=0;
int i1=0;
int j1=0;
double lb=0;
double rb=0;
CMatrixDouble a;
CRowDouble x0;
CRowDouble x1;
CRowDouble ty;
CRowDouble tyt;
CRowDouble y;
CRowDouble yt;
double eps=0;
//--- Accuracy
eps=1000*CMath::m_machineepsilon;
//--- Size of the matrix (m*m)
m=10;
//--- Left and right borders, limiting matrix values
lb=-10;
rb=10;
//--- Test linear algebra functions for:
//--- a) sparse matrix converted to CRS from Hash-Table
//--- b) sparse matrix initially created as CRS
for(i=1; i<m; i++)
{
//--- Prepare test problem
CreateRandom(i,i,-1,-1,-1,-1,a,s);
//--- Initialize temporaries
ty=vector<double>::Zeros(i);
tyt=vector<double>::Zeros(i);
x0=vector<double>::Zeros(i);
x1=vector<double>::Zeros(i);
for(i1=0; i1<i; i1++)
{
x0.Set(i1,(rb-lb)*CMath::RandomReal()+lb);
x1.Set(i1,(rb-lb)*CMath::RandomReal()+lb);
}
//--- Searching true result for upper and lower triangles
//--- of the matrix
for(i1=0; i1<i; i1++)
for(j1=i1; j1<i; j1++)
{
ty.Add(i1,a.Get(i1,j1)*x0[j1]);
if(i1!=j1)
ty.Add(j1,a.Get(i1,j1)*x0[i1]);
}
for(i1=0; i1<i; i1++)
for(j1=0; j1<=i1; j1++)
{
tyt.Add(i1,a.Get(i1,j1)*x1[j1]);
if(i1!=j1)
tyt.Add(j1,a.Get(i1,j1)*x1[i1]);
}
//--- Multiplication
CSparse::SparseSMV(s,true,x0,y);
CSparse::SparseSMV(s,false,x1,yt);
//--- Check for SMV-result
for(i1=0; i1<i; i1++)
if((double)(MathAbs(y[i1]-ty[i1]))>=eps || (double)(MathAbs(yt[i1]-tyt[i1]))>=eps)
{
result=true;
return(result);
}
}
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing multyplication sparse matrix with nerrow |
//| dense matrix |
//+------------------------------------------------------------------+
bool CTestSparseUnit::LinearFunctionSMMTest(void)
{
//--- create variables
bool result=false;
CSparseMatrix s;
int n=0;
int m=0;
int kmax=0;
int i=0;
int j=0;
int k=0;
int i1=0;
int j1=0;
int k1=0;
double lb=0;
double rb=0;
CMatrixDouble a;
CMatrixDouble x0;
CMatrixDouble x1;
CMatrixDouble ty;
CMatrixDouble tyt;
CMatrixDouble y;
CMatrixDouble yt;
CMatrixDouble y0;
CMatrixDouble yt0;
double eps=0;
//--- Accuracy
eps=1000*CMath::m_machineepsilon;
//--- Size of the matrix (m*n)
n=32;
m=32;
kmax=32;
//--- Left and right borders, limiting matrix values
lb=-10;
rb=10;
//--- Test linear algebra functions for:
//--- a) sparse matrix converted to CRS from Hash-Table
//--- b) sparse matrix initially created as CRS
for(i=1; i<m; i++)
{
for(j=1; j<n; j++)
{
//--- Prepare test problem
CreateRandom(i,j,-1,-1,-1,-1,a,s);
x0=matrix<double>::Zeros(j,kmax);
x1=matrix<double>::Zeros(i,kmax);
for(i1=0; i1<j; i1++)
for(j1=0; j1<=kmax-1; j1++)
x0.Set(i1,j1,(rb-lb)*CMath::RandomReal()+lb);
for(i1=0; i1<i; i1++)
for(j1=0; j1<=kmax-1; j1++)
x1.Set(i1,j1,(rb-lb)*CMath::RandomReal()+lb);
ty=matrix<double>::Zeros(i,kmax);
tyt=matrix<double>::Zeros(j,kmax);
if(i!=j)
{
for(i1=0; i1<i; i1++)
for(k1=0; k1<=kmax-1; k1++)
for(j1=0; j1<j; j1++)
{
ty.Add(i1,k1,a.Get(i1,j1)*x0.Get(j1,k1));
tyt.Add(j1,k1,a.Get(i1,j1)*x1.Get(i1,k1));
}
}
else
{
for(i1=0; i1<i; i1++)
for(k1=0; k1<=kmax-1; k1++)
for(j1=0; j1<j; j1++)
{
ty.Add(i1,k1,a.Get(i1,j1)*x0.Get(j1,k1));
tyt.Add(j1,k1,a.Get(i1,j1)*x0.Get(i1,k1));
}
}
for(k=1; k<=kmax; k++)
{
//--- Consider two cases: square matrix, and non-square matrix
if(i!=j)
{
//--- Multiplication
CSparse::SparseMM(s,x0,k,y);
CSparse::SparseMTM(s,x1,k,yt);
//--- Check for MM-result
for(i1=0; i1<i; i1++)
for(j1=0; j1<k; j1++)
if(MathAbs(y.Get(i1,j1)-ty.Get(i1,j1))>=eps)
{
result=true;
return(result);
}
//--- Check for MTM-result
for(i1=0; i1<j; i1++)
for(j1=0; j1<k; j1++)
if(MathAbs(yt.Get(i1,j1)-tyt.Get(i1,j1))>=eps)
{
result=true;
return(result);
}
}
else
{
CSparse::SparseMM(s,x0,k,y);
CSparse::SparseMTM(s,x0,k,yt);
CSparse::SparseMM2(s,x0,k,y0,yt0);
//--- Check for MM2-result
for(i1=0; i1<i; i1++)
for(j1=0; j1<k; j1++)
if(MathAbs(y0.Get(i1,j1)-ty.Get(i1,j1))>=eps || MathAbs(yt0.Get(i1,j1)-tyt.Get(i1,j1))>=eps)
{
result=true;
return(result);
}
//--- Check for MV- and MTM-result by help MV2
for(i1=0; i1<i; i1++)
for(j1=0; j1<k; j1++)
if(MathAbs(y0.Get(i1,j1)-y.Get(i1,j1))>eps || MathAbs(yt0.Get(i1,j1)-yt.Get(i1,j1))>eps)
{
result=true;
return(result);
}
}
}
}
}
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing multyplication for simmetric sparse matrix |
//| with narrow dense matrix |
//+------------------------------------------------------------------+
bool CTestSparseUnit::LinearFunctionsSMMTest(void)
{
bool result=false;
CSparseMatrix s;
int m=0;
int k=0;
int i=0;
int j=0;
int i1=0;
int j1=0;
int k1=0;
double lb=0;
double rb=0;
CMatrixDouble a;
CMatrixDouble x0;
CMatrixDouble x1;
CMatrixDouble ty;
CMatrixDouble tyt;
CMatrixDouble y;
CMatrixDouble yt;
double eps=0;
//--- Accuracy
eps=1000*CMath::m_machineepsilon;
//--- Size of the matrix (m*m)
m=32;
k=32;
//--- Left and right borders, limiting matrix values
lb=-10;
rb=10;
//--- Test linear algebra functions for:
//--- a) sparse matrix converted to CRS from Hash-Table
//--- b) sparse matrix initially created as CRS
for(i=1; i<m; i++)
{
for(j=1; j<k; j++)
{
//--- Prepare test problem
CreateRandom(i,i,-1,-1,-1,-1,a,s);
//--- Initialize temporaries
ty=matrix<double>::Zeros(i,j);
tyt=matrix<double>::Zeros(i,j);
x0=matrix<double>::Zeros(i,j);
x1=matrix<double>::Zeros(i,j);
for(i1=0; i1<i; i1++)
for(j1=0; j1<j; j1++)
{
x0.Set(i1,j1,(rb-lb)*CMath::RandomReal()+lb);
x1.Set(i1,j1,(rb-lb)*CMath::RandomReal()+lb);
}
//--- Searching true result for upper and lower triangles
//--- of the matrix
for(k1=0; k1<j; k1++)
for(i1=0; i1<i; i1++)
for(j1=i1; j1<i; j1++)
{
ty.Add(i1,k1,a.Get(i1,j1)*x0.Get(j1,k1));
if(i1!=j1)
ty.Add(j1,k1,a.Get(i1,j1)*x0.Get(i1,k1));
}
for(k1=0; k1<j; k1++)
for(i1=0; i1<i; i1++)
for(j1=0; j1<=i1; j1++)
{
tyt.Add(i1,k1,a.Get(i1,j1)*x1.Get(j1,k1));
if(i1!=j1)
tyt.Add(j1,k1,a.Get(i1,j1)*x1.Get(i1,k1));
}
//--- Multiplication
CSparse::SparseSMM(s,true,x0,j,y);
CSparse::SparseSMM(s,false,x1,j,yt);
//--- Check for SMM-result
for(k1=0; k1<j; k1++)
for(i1=0; i1<i; i1++)
if(MathAbs(y.Get(i1,k1)-ty.Get(i1,k1))>=eps || MathAbs(yt.Get(i1,k1)-tyt.Get(i1,k1))>=eps)
{
result=true;
return(result);
}
}
}
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| Function for basic test SparseCopy |
//+------------------------------------------------------------------+
bool CTestSparseUnit::BasicCopyFuncTest(bool silent)
{
//--- create variables
bool result=false;
CSparseMatrix s;
CSparseMatrix ss;
CSparseMatrix sss;
int n=0;
int m=0;
CRowInt ner;
int i=0;
int j=0;
int i1=0;
int j1=0;
CMatrixDouble a;
double a0=0;
double a1=0;
n=30;
m=30;
for(i=1; i<m; i++)
{
for(j=1; j<n; j++)
{
CSparse::SparseCreate(i,j,1,s);
a=matrix<double>::Zeros(i,j);
ner.Resize(i);
for(i1=0; i1<i; i1++)
{
if(i1<=j-3)
ner.Set(i1,2);
else
{
if(j-3<i1 && i1<=j-2)
ner.Set(i1,1);
else
ner.Set(i1,0);
}
}
CSparse::SparseCreateCRS(i,j,ner,sss);
//--- Checking for Matrix with hash table type
for(i1=0; i1<i; i1++)
{
for(j1=0; j1<j; j1++)
{
if(j1>i1 && j1<=i1+2)
{
a.Set(i1,j1,i1+j1+1);
CSparse::SparseSet(s,i1,j1,a.Get(i1,j1));
CSparse::SparseAdd(s,i1,j1,0);
CSparse::SparseSet(sss,i1,j1,a.Get(i1,j1));
}
else
{
a.Set(i1,j1,0);
CSparse::SparseSet(s,i1,j1,a.Get(i1,j1));
CSparse::SparseAdd(s,i1,j1,0);
}
//--- Check for SparseCreate
CSparse::SparseCopy(s,ss);
a0=CSparse::SparseGet(s,i1,j1);
a1=CSparse::SparseGet(ss,i1,j1);
if(a0!=a1)
{
if(!silent)
{
Print("BasicCopyFuncTest::Report::SparseGet");
PrintFormat("S::[%d,%d]=%.5f",i1,j1,a0);
PrintFormat("SS::[%d,%d]=%.5f",i1,j1,a1);
Print(" TEST FAILED");
}
result=true;
return(result);
}
}
}
//--- Check for SparseCreateCRS
for(i1=0; i1<i; i1++)
{
for(j1=0; j1<j; j1++)
{
CSparse::SparseCopy(sss,ss);
a0=CSparse::SparseGet(sss,i1,j1);
a1=CSparse::SparseGet(ss,i1,j1);
if(a0!=a1)
{
if(!silent)
{
Print("BasicCopyFuncTest::Report::SparseGet\n");
PrintFormat("S::[%d,%d]=%.5f",i1,j1,a0);
PrintFormat("SS::[%d,%d]=%.5f",i1,j1,a1);
Print(" TEST FAILED");
}
result=true;
return(result);
}
}
}
//--- Check for Matrix with CRS type
CSparse::SparseConvertToCRS(s);
CSparse::SparseCopy(s,ss);
for(i1=0; i1<i; i1++)
for(j1=0; j1<j; j1++)
{
a0=CSparse::SparseGet(s,i1,j1);
a1=CSparse::SparseGet(ss,i1,j1);
if(a0!=a1)
{
if(!silent)
{
Print("BasicCopyFuncTest::Report::SparseGet");
PrintFormat("S::[%d,%d]=%.5f",i1,j1,a0);
PrintFormat("SS::[%d,%d]=%.5f",i1,j1,a1);
Print(" TEST FAILED.\n");
}
result=true;
return(result);
}
}
}
}
if(!silent)
Print(" TEST IS PASSED");
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing SparseCopy |
//+------------------------------------------------------------------+
bool CTestSparseUnit::CopyFuncTest(bool silent)
{
//--- create variables
bool result=false;
CSparseMatrix s;
CSparseMatrix ss;
int n=0;
int m=0;
int mtype=0;
int i=0;
int j=0;
int i1=0;
int j1=0;
double lb=0;
double rb=0;
CMatrixDouble a;
CRowDouble x0;
CRowDouble x1;
CRowDouble ty;
CRowDouble tyt;
CRowDouble y;
CRowDouble yt;
CRowDouble y0;
CRowDouble yt0;
CRowDouble cpy;
CRowDouble cpyt;
CRowDouble cpy0;
CRowDouble cpyt0;
double eps=0;
double a0=0;
double a1=0;
//--- Accuracy
eps=1000*CMath::m_machineepsilon;
//--- Size of the matrix (m*n)
n=30;
m=30;
//--- Left and right borders, limiting matrix values
lb=-10;
rb=10;
//--- Test linear algebra functions for:
//--- a) sparse matrix converted to CRS from Hash-Table
//--- b) sparse matrix initially created as CRS
for(i=1; i<m; i++)
{
for(j=1; j<n; j++)
{
for(mtype=0; mtype<=1; mtype++)
{
//--- Prepare test problem
CreateRandom(i,j,-1,mtype,-1,-1,a,s);
CSparse::SparseCopy(s,ss);
//--- Initialize temporaries
ty=vector<double>::Zeros(i);
tyt=vector<double>::Zeros(j);
x0=vector<double>::Zeros(j);
x1=vector<double>::Zeros(i);
for(i1=0; i1<j; i1++)
x0.Set(i1,(rb-lb)*CMath::RandomReal()+lb);
for(i1=0; i1<i; i1++)
x1.Set(i1,(rb-lb)*CMath::RandomReal()+lb);
//--- Consider two cases: square matrix, and non-square matrix
if(i!=j)
{
//--- Searching true result
for(i1=0; i1<i; i1++)
for(j1=0; j1<j; j1++)
{
ty.Add(i1,a.Get(i1,j1)*x0[j1]);
tyt.Add(j1,a.Get(i1,j1)*x1[i1]);
}
//--- Multiplication
CSparse::SparseMV(s,x0,y);
CSparse::SparseMTV(s,x1,yt);
CSparse::SparseMV(ss,x0,cpy);
CSparse::SparseMTV(ss,x1,cpyt);
//--- Check for MV-result
for(i1=0; i1<i; i1++)
{
if(MathAbs(y[i1]-ty[i1])>=eps || MathAbs(cpy[i1]-ty[i1])>=eps || (cpy[i1]-y[i1])!=0.0)
{
if(!silent)
{
Print("CopyFuncTest::Report::RES_MV");
PrintFormat("Y[%d]=%.5f; tY[%d]=%.5f",i1,y[i1],i1,ty[i1]);
PrintFormat("cpY[%d]=%.5f;",i1,cpy[i1]);
Print(" TEST FAILED.\n");
}
result=true;
return(result);
}
}
//--- Check for MTV-result
for(i1=0; i1<j; i1++)
if(((double)(MathAbs(yt[i1]-tyt[i1]))>=eps || (double)(MathAbs(cpyt[i1]-tyt[i1]))>=eps) || (double)(cpyt[i1]-yt[i1])!=0.0)
{
if(!silent)
{
Print("CopyFuncTest::Report::RES_MTV");
PrintFormat("Yt[%d]=%.5f; tYt[%d]=%.5f",i1,yt[i1],i1,tyt[i1]);
PrintFormat("cpYt[%d]=%.5f;",i1,cpyt[i1]);
Print(" TEST FAILED.");
}
result=true;
return(result);
}
CSparse::SparseCopy(s,ss);
for(i1=0; i1<i; i1++)
for(j1=0; j1<j; j1++)
{
a0=CSparse::SparseGet(s,i1,j1);
a1=CSparse::SparseGet(ss,i1,j1);
if(a0!=a1)
{
if(!silent)
{
Print("CopyFuncTest::Report::SparseGet");
PrintFormat("S::[%d,%d]=%.5f",i1,j1,a0);
PrintFormat("SS::[%d,%d]=%.5f",i1,j1,a1);
Print(" TEST FAILED.");
}
result=true;
return(result);
}
}
}
else
{
//--- Searching true result
for(i1=0; i1<i; i1++)
for(j1=0; j1<j; j1++)
{
ty.Add(i1,a.Get(i1,j1)*x0[j1]);
tyt.Add(j1,a.Get(i1,j1)*x0[i1]);
}
//--- Multiplication
CSparse::SparseMV(s,x0,y);
CSparse::SparseMTV(s,x0,yt);
CSparse::SparseMV2(s,x0,y0,yt0);
CSparse::SparseMV(ss,x0,cpy);
CSparse::SparseMTV(ss,x0,cpyt);
CSparse::SparseMV2(ss,x0,cpy0,cpyt0);
//--- Check for MV2-result
for(i1=0; i1<i; i1++)
if(MathAbs(y0[i1]-ty[i1])>=eps || MathAbs(yt0[i1]-tyt[i1])>=eps || MathAbs(cpy0[i1]-ty[i1])>=eps || MathAbs(cpyt0[i1]-tyt[i1])>=eps || (cpy0[i1]-y0[i1])!=0.0 || (cpyt0[i1]-yt0[i1])!=0.0)
{
if(!silent)
{
Print("CopyFuncTest::Report::RES_MV2");
PrintFormat("Y0[%d]=%.5f; tY[%d]=%.5f",i1,y0[i1],i1,ty[i1]);
PrintFormat("Yt0[%d]=%.5f; tYt[%d]=%.5f",i1,yt0[i1],i1,tyt[i1]);
PrintFormat("cpY0[%d]=%.5f;",i1,cpy0[i1]);
PrintFormat("cpYt0[%d]=%.5f;",i1,cpyt0[i1]);
Print(" TEST FAILED.");
}
result=true;
return(result);
}
//--- Check for MV- and MTV-result by help MV2
for(i1=0; i1<i; i1++)
if(MathAbs(y0[i1]-y[i1])>eps || MathAbs(yt0[i1]-yt[i1])>eps || MathAbs(cpy0[i1]-cpy[i1])>eps || MathAbs(cpyt0[i1]-cpyt[i1])>eps)
{
if(!silent)
{
Print("CopyFuncTest::Report::RES_MV_MVT");
PrintFormat("Y0[%d]=%.5f; Y[%d]=%.5f",i1,y0[i1],i1,y[i1]);
PrintFormat("Yt0[%d]=%.5f; Yt[%d]=%.5f",i1,yt0[i1],i1,yt[i1]);
PrintFormat("cpY0[%d]=%.5f; cpY[%d]=%.5f",i1,cpy0[i1],i1,cpy[i1]);
PrintFormat("cpYt0[%d]=%.5f; cpYt[%d]=%.5f",i1,cpyt0[i1],i1,cpyt[i1]);
Print(" TEST FAILED.");
}
result=true;
return(result);
}
CSparse::SparseCopy(s,ss);
for(i1=0; i1<i; i1++)
for(j1=0; j1<j; j1++)
{
a0=CSparse::SparseGet(s,i1,j1);
a1=CSparse::SparseGet(ss,i1,j1);
if(a0!=a1)
{
if(!silent)
{
Print("CopyFuncTest::Report::SparseGet");
PrintFormat("S::[%d,%d]=%.5f",i1,j1,a0);
PrintFormat("SS::[%d,%d]=%.5f",i1,j1,a1);
Print(" TEST FAILED.");
}
result=true;
return(result);
}
}
}
}
}
}
if(!silent)
Print(" TEST IS PASSED.\n");
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| This function initializes sparse matrix generator, which is used |
//| to generate a set of matrices with sequentially increasing |
//| sparsity. |
//| PARAMETERS: |
//| M, N - matrix size. If M=0, then matrix is square N*N. |
//| N and M must be small enough to store N*M dense |
//| matrix. |
//| MatKind - matrix properties: |
//| * 0 - general sparse (no structure) |
//| * 1 - general sparse, but diagonal is |
//| always present and non-zero |
//| * 2 - diagonally dominant, SPD |
//| Triangle - triangle being returned: |
//| * +1 - upper triangle |
//| * -1 - lower triangle |
//| * 0 - full matrix is returned |
//| OUTPUT PARAMETERS: |
//| G - generator |
//| A - matrix A in dense format |
//| S - matrix A in sparse format (hash-table storage) |
//+------------------------------------------------------------------+
void CTestSparseUnit::InitGenerator(int m,int n,int matkind,int triangle,
CSparseGenerator &g)
{
g.m_n=n;
g.m_m=m;
g.m_matkind=matkind;
g.m_triangle=triangle;
CHighQualityRand::HQRndRandomize(g.m_rs);
g.m_rcs.ia.Resize(5+1);
g.m_rcs.ra=vector<double>::Zeros(1+1);
g.m_rcs.stage=-1;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestSparseUnit::GenerateNext(CSparseGenerator &g,
CMatrixDouble &da,
CSparseMatrix &sa)
{
//--- create variables
bool result=false;
int n=0;
int m=0;
int nz=0;
int nzd=0;
double pnz=0;
int i=0;
int j=0;
double v=0;
int label=-1;
da.Resize(0,0);
//--- Reverse communication preparations
//--- I know it looks ugly, but it works the same way
//--- anywhere from C++ to Python.
//--- This code initializes locals by:
//--- * random values determined during code
//--- generation - on first subroutine call
//--- * values from previous call - on subsequent calls
if(g.m_rcs.stage>=0)
{
n=g.m_rcs.ia[0];
m=g.m_rcs.ia[1];
nz=g.m_rcs.ia[2];
nzd=g.m_rcs.ia[3];
i=g.m_rcs.ia[4];
j=g.m_rcs.ia[5];
pnz=g.m_rcs.ra[0];
v=g.m_rcs.ra[1];
}
else
{
n=359;
m=-58;
nz=-919;
nzd=-909;
i=81;
j=255;
pnz=74;
v=-788;
}
switch(g.m_rcs.stage)
{
case 0:
label=0;
break;
case 1:
label=1;
break;
default:
//--- Routine body
n=g.m_n;
if(g.m_m==0)
m=n;
else
m=g.m_m;
if(!CAp::Assert(m>0 && n>0,__FUNCTION__+": incorrect N/M"))
return(false);
//--- Generate general sparse matrix
if(g.m_matkind!=0)
{
label=2;
break;
}
nz=n*m;
label=4;
break;
}
while(label>=0)
switch(label)
{
case 4:
//--- Generate dense N*N matrix where probability of element
//--- being non-zero is PNZ.
pnz=(double)nz/(double)(n*m);
g.m_bufa=matrix<double>::Zeros(m,n);
for(i=0; i<m; i++)
for(j=0; j<n; j++)
if((double)(CHighQualityRand::HQRndUniformR(g.m_rs))<=pnz)
g.m_bufa.Set(i,j,CHighQualityRand::HQRndUniformR(g.m_rs)-0.5);
//--- Output matrix and RComm
da=matrix<double>::Zeros(m,n);
CSparse::SparseCreate(m,n,(int)MathRound(pnz*m*n),sa);
for(i=0; i<m; i++)
for(j=0; j<n; j++)
if((j<=i && g.m_triangle<=0) || (j>=i && g.m_triangle>=0))
{
da.Set(i,j,g.m_bufa.Get(i,j));
CSparse::SparseSet(sa,i,j,g.m_bufa.Get(i,j));
}
g.m_rcs.stage=0;
label=-1;
break;
case 0:
//--- Increase problem sparcity and try one more time.
//--- Stop after testing NZ=0.
if(nz==0)
{
label=5;
break;
}
nz=nz/2;
label=4;
break;
case 5:
result=false;
return(result);
case 2:
//--- Generate general sparse matrix with non-zero diagonal
if(g.m_matkind!=1)
{
label=6;
break;
}
if(!CAp::Assert(n==m,__FUNCTION__+": non-square matrix for MatKind=1"))
return(false);
nz=n*n-n;
case 8:
//--- Generate dense N*N matrix where probability of non-diagonal element
//--- being non-zero is PNZ.
if(n>1)
pnz=(double)nz/(double)(n*n-n);
else
pnz=1;
g.m_bufa=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
{
if(i==j)
{
do
{
g.m_bufa.Set(i,i,CHighQualityRand::HQRndUniformR(g.m_rs)-0.5);
}
while(g.m_bufa.Get(i,i)==0.0);
g.m_bufa.Add(i,i,1.5*MathSign(g.m_bufa.Get(i,i)));
continue;
}
if(CHighQualityRand::HQRndUniformR(g.m_rs)<=pnz)
g.m_bufa.Set(i,j,CHighQualityRand::HQRndUniformR(g.m_rs)-0.5);
}
//--- Output matrix and RComm
da=matrix<double>::Zeros(n,n);
CSparse::SparseCreate(n,n,(int)MathRound(pnz*(n*n-n)+n),sa);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
if((j<=i && g.m_triangle<=0) || (j>=i && g.m_triangle>=0))
{
da.Set(i,j,g.m_bufa.Get(i,j));
CSparse::SparseSet(sa,i,j,g.m_bufa.Get(i,j));
}
g.m_rcs.stage=1;
label=-1;
break;
case 1:
//--- Increase problem sparcity and try one more time.
//--- Stop after testing NZ=0.
if(nz==0)
{
label=9;
break;
}
nz=nz/2;
label=8;
break;
case 9:
result=false;
return(result);
case 6:
result=false;
return(result);
}
//--- Saving state
result=true;
g.m_rcs.ia.Set(0,n);
g.m_rcs.ia.Set(1,m);
g.m_rcs.ia.Set(2,nz);
g.m_rcs.ia.Set(3,nzd);
g.m_rcs.ia.Set(4,i);
g.m_rcs.ia.Set(5,j);
g.m_rcs.ra.Set(0,pnz);
g.m_rcs.ra.Set(1,v);
return(result);
}
//+------------------------------------------------------------------+
//| This function creates random sparse matrix with some prescribed |
//| pattern. |
//| INPUT PARAMETERS: |
//| M - number of rows |
//| N - number of columns |
//| PKind - sparsity pattern: |
//| *-1 = pattern is chosen at random as well as P0/P1 |
//| * 0 = matrix with up to P0 non-zero elements at |
//| random locations (however, actual number of |
//| non-zero elements can be less than P0, and in|
//| fact can be zero) |
//| * 1 = band matrix with P0 non-zero elements below |
//| diagonal and P1 non-zero element above |
//| diagonal |
//| * 2 = matrix with random number of contiguous |
//| non-zero elements in the each row |
//| CKind - creation type: |
//| *-1 = CKind is chosen at random |
//| * 0 = matrix is created in Hash-Table format and |
//| converted to CRS representation |
//| * 1 = matrix is created in CRS format |
//| * 2 = matrix is created in Hash-Table format and |
//| converted to SKS representation |
//| OUTPUT PARAMETERS: |
//| DA - dense representation of A, array[M,N] |
//| SA - sparse representation of A, in CRS format |
//+------------------------------------------------------------------+
void CTestSparseUnit::CreateRandom(int m,int n,int pkind,int ckind,
int p0,int p1,CMatrixDouble &da,
CSparseMatrix &sa)
{
//--- create variables
int maxpkind=0;
int maxckind=0;
int i=0;
int j=0;
int k=0;
double v=0;
CRowInt c0;
CRowInt c1;
CRowInt rowsizes;
maxpkind=2;
maxckind=2;
if(!CAp::Assert(m>=1,__FUNCTION__+": incorrect parameters"))
return;
if(!CAp::Assert(n>=1,__FUNCTION__+": incorrect parameters"))
return;
if(!CAp::Assert(pkind>=-1 && pkind<=maxpkind,__FUNCTION__+": incorrect parameters"))
return;
if(!CAp::Assert(ckind>=-1 && ckind<=maxckind,__FUNCTION__+": incorrect parameters"))
return;
if(!CAp::Assert(!(ckind==2 && m!=n),__FUNCTION__+": incorrect parameters"))
return;
if(pkind==-1)
{
pkind=CMath::RandomInteger(maxpkind+1);
if(pkind==0)
p0=CMath::RandomInteger(m*n);
if(pkind==1)
{
p0=CMath::RandomInteger(MathMin(m,n));
p1=CMath::RandomInteger(MathMin(m,n));
}
}
if(ckind==-1)
{
do
{
ckind=CMath::RandomInteger(maxckind+1);
}
while(ckind==2 && m!=n);
}
if(pkind==0)
{
//--- Matrix with elements at random locations
da=matrix<double>::Zeros(m,n);
if(ckind==0 || ckind==2)
{
//--- Create matrix in Hash format, convert to CRS or SKS
CSparse::SparseCreate(m,n,1,sa);
for(k=0; k<=p0-1; k++)
{
i=CMath::RandomInteger(m);
j=CMath::RandomInteger(n);
v=(double)(CMath::RandomInteger(17)-8)/8.0;
if(CMath::RandomReal()>0.5)
{
da.Set(i,j,v);
CSparse::SparseSet(sa,i,j,v);
}
else
{
da.Add(i,j,v);
CSparse::SparseAdd(sa,i,j,v);
}
}
if(ckind!=2)
CSparse::SparseConvertToCRS(sa);
else
CSparse::SparseConvertToSKS(sa);
return;
}
if(ckind==1)
{
//--- Create matrix in CRS format
for(k=0; k<=p0-1; k++)
{
i=CMath::RandomInteger(m);
j=CMath::RandomInteger(n);
v=(double)(CMath::RandomInteger(17)-8)/8.0;
da.Set(i,j,v);
}
rowsizes.Resize(m);
for(i=0; i<m; i++)
{
rowsizes.Set(i,0);
for(j=0; j<n; j++)
if(da.Get(i,j)!=0.0)
rowsizes.Add(i,1);
}
CSparse::SparseCreateCRS(m,n,rowsizes,sa);
for(i=0; i<m; i++)
for(j=0; j<n; j++)
if(da.Get(i,j)!=0.0)
CSparse::SparseSet(sa,i,j,da.Get(i,j));
return;
}
CAp::Assert(false,__FUNCTION__+": internal error");
return;
}
if(pkind==1)
{
//--- Band matrix
da=matrix<double>::Zeros(m,n);
rowsizes.Resize(m);
for(i=0; i<m; i++)
{
for(j=MathMax(i-p0,0); j<=MathMin(i+p1,n-1); j++)
{
do
{
da.Set(i,j,(double)(CMath::RandomInteger(17)-8)/8.0);
}
while(da.Get(i,j)==0.0);
}
rowsizes.Set(i,MathMax(MathMin(i+p1,n-1)-MathMax(i-p0,0)+1,0));
}
if(ckind==0)
CSparse::SparseCreate(m,n,1,sa);
if(ckind==1)
CSparse::SparseCreateCRS(m,n,rowsizes,sa);
if(ckind==2)
CSparse::SparseCreate(m,n,1,sa);
for(i=0; i<m; i++)
for(j=0; j<n; j++)
if(da.Get(i,j)!=0.0)
CSparse::SparseSet(sa,i,j,da.Get(i,j));
if(ckind!=2)
CSparse::SparseConvertToCRS(sa);
else
CSparse::SparseConvertToSKS(sa);
return;
}
if(pkind==2)
{
//--- Matrix with one contiguous sequence of non-zero elements per row
da=matrix<double>::Zeros(m,n);
rowsizes.Resize(m);
c0.Resize(m);
c1.Resize(m);
for(i=0; i<m; i++)
{
c0.Set(i,CMath::RandomInteger(n));
c1.Set(i,c0[i]+CMath::RandomInteger(n-c0[i]+1));
rowsizes.Set(i,c1[i]-c0[i]);
}
for(i=0; i<m; i++)
for(j=c0[i]; j<=c1[i]-1; j++)
{
do
{
da.Set(i,j,(double)(CMath::RandomInteger(17)-8)/8.0);
}
while(da.Get(i,j)==0.0);
}
if(ckind==0)
CSparse::SparseCreate(m,n,1,sa);
if(ckind==1)
CSparse::SparseCreateCRS(m,n,rowsizes,sa);
if(ckind==2)
CSparse::SparseCreate(m,n,1,sa);
for(i=0; i<m; i++)
for(j=0; j<n; j++)
if(da.Get(i,j)!=0.0)
CSparse::SparseSet(sa,i,j,da.Get(i,j));
if(ckind!=2)
CSparse::SparseConvertToCRS(sa);
else
CSparse::SparseConvertToSKS(sa);
return;
}
}
//+------------------------------------------------------------------+
//| This function does test for SparseEnumerate function. |
//+------------------------------------------------------------------+
bool CTestSparseUnit::EnumerateTest(void)
{
//--- create variables
bool result=false;
CSparseMatrix spa;
CMatrixDouble a;
CMatrixInt ta;
int m=0;
int n=0;
double r=0;
double v=0;
int ne=0;
int t0=0;
int t1=0;
int counter=0;
int c=0;
int hashcrs=0;
int i=0;
int j=0;
r=10.5;
for(m=1; m<=30; m++)
{
for(n=1; n<=30; n++)
{
ne=0;
//--- Create matrix with non-zero elements inside the region:
//--- 0<=I<S.M and 0<=J<S.N
a=matrix<double>::Zeros(m,n);
ta.Resize(m,n);
ta.Fill((int)false);
//---
for(i=0; i<m; i++)
for(j=0; j<n; j++)
{
c=CMath::RandomInteger(2);
if(c!=0)
{
do
{
a.Set(i,j,r*(2*CMath::RandomReal()-1));
}
while(a.Get(i,j)==0.0);
//--- Number of non-zero elements
ne++;
}
}
for(hashcrs=0; hashcrs<=1; hashcrs++)
{
CSparse::SparseCreate(m,n,m*n,spa);
for(i=0; i<m; i++)
for(j=0; j<n; j++)
CSparse::SparseSet(spa,i,j,a.Get(i,j));
if(hashcrs==1)
CSparse::SparseConvertToCRS(spa);
t0=0;
t1=0;
counter=0;
while(CSparse::SparseEnumerate(spa,t0,t1,i,j,v))
{
ta.Set(i,j,(int)true);
counter++;
if(v!=a.Get(i,j))
{
CAp::SetErrorFlag(result,true,"testsparseunit.ap:3112");
return(result);
}
}
//--- Check that all non-zero elements was enumerated
for(i=0; i<m; i++)
for(j=0; j<n; j++)
if(ta.Get(i,j) && a.Get(i,j)==0.0)
{
CAp::SetErrorFlag(result,true,"testsparseunit.ap:3124");
return(result);
}
if(ne!=counter)
{
CAp::SetErrorFlag(result,true,"testsparseunit.ap:3130");
return(result);
}
}
}
}
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| This function does test for SparseRewriteExisting function. |
//+------------------------------------------------------------------+
bool CTestSparseUnit::RewriteExistingTest(void)
{
//--- create variables
bool result=false;
CSparseMatrix spa;
double spaval=0;
CMatrixDouble a;
CMatrixInt ta;
int m=0;
int n=0;
int c=0;
int ne=0;
int nr=0;
double r=0;
double v=0;
int hashcrs=0;
int i=0;
int j=0;
r=20.0;
for(m=1; m<=30; m++)
{
for(n=1; n<=30; n++)
{
ta.Resize(m,n);
for(hashcrs=0; hashcrs<=1; hashcrs++)
{
a=matrix<double>::Zeros(m,n);
ta.Fill((int)false);
v=r*(2*CMath::RandomReal()-1);
//--- Creating and filling of the matrix
ne=0;
CSparse::SparseCreate(m,n,m*n,spa);
for(i=0; i<m; i++)
for(j=0; j<n; j++)
{
c=CMath::RandomInteger(2);
if(c==1)
{
do
{
a.Set(i,j,r*(2*CMath::RandomReal()-1));
}
while(a.Get(i,j)==0.0);
CSparse::SparseSet(spa,i,j,a.Get(i,j));
ne++;
}
}
if(hashcrs==1)
CSparse::SparseConvertToCRS(spa);
//--- Rewrite some elements
nr=0;
for(i=0; i<m; i++)
for(j=0; j<n; j++)
{
c=CMath::RandomInteger(2);
if(c==1)
{
ta.Set(i,j,(int)CSparse::SparseRewriteExisting(spa,i,j,v));
if(ta.Get(i,j))
{
a.Set(i,j,v);
nr++;
}
}
}
//--- Now we have to be sure, that all changes had made correctly
for(i=0; i<m; i++)
for(j=0; j<n; j++)
if(ta.Get(i,j))
{
spaval=CSparse::SparseGet(spa,i,j);
nr--;
if(spaval!=v || spaval!=a.Get(i,j))
{
result=true;
return(result);
}
}
if(nr!=0)
{
result=true;
return(result);
}
//--- Rewrite all elements
for(i=0; i<m; i++)
for(j=0; j<n; j++)
{
ta.Set(i,j,(int)CSparse::SparseRewriteExisting(spa,i,j,v));
if(ta.Get(i,j))
a.Set(i,j,v);
}
for(i=0; i<m; i++)
for(j=0; j<n; j++)
if(ta.Get(i,j))
ne--;
if(ne!=0)
{
result=true;
return(result);
}
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
{
spaval=CSparse::SparseGet(spa,i,j);
if(ta.Get(i,j))
{
if(spaval!=v || spaval!=a.Get(i,j))
{
result=true;
return(result);
}
}
else
{
if(spaval!=0.0 || spaval!=a.Get(i,j))
{
result=true;
return(result);
}
}
}
}
}
}
}
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| Test for SparseGetRow/GetCompressedRow function. It creates |
//| random dense and sparse matrices; then get every row from sparse |
//| matrix and compares it with every row in dense matrix. |
//| On failure sets error flag, on success leaves it unchanged. |
//+------------------------------------------------------------------+
void CTestSparseUnit::TestGetRow(bool &err)
{
//--- create variables
CSparseMatrix s;
CMatrixDouble a;
int m=0;
int n=0;
int msize=0;
int nsize=0;
int nz=0;
CRowDouble vals;
CRowDouble mrow;
CRowInt colidx;
CRowInt wasreturned;
int mtype=0;
int i=0;
int j=0;
msize=15;
nsize=15;
for(mtype=1; mtype<=2; mtype++)
{
for(m=1; m<=msize; m++)
{
for(n=1; n<=nsize; n++)
{
//--- Skip nonrectangular SKS matrices - not supported
if(mtype==2 && m!=n)
continue;
//--- Create "reference" and sparse matrices
a=matrix<double>::Zeros(m,n);
CSparse::SparseCreate(m,n,1,s);
for(i=0; i<m; i++)
for(j=0; j<n; j++)
if(CMath::RandomInteger(5)==3)
{
a.Set(i,j,2*CMath::RandomReal()-1);
CSparse::SparseSet(s,i,j,a.Get(i,j));
}
//--- Choose matrix type to test
if(mtype==1)
CSparse::SparseConvertToCRS(s);
else
CSparse::SparseConvertToSKS(s);
//--- Test SparseGetRow()
for(i=0; i<m; i++)
{
CSparse::SparseGetRow(s,i,mrow);
for(j=0; j<n; j++)
if(mrow[j]!=a.Get(i,j) || mrow[j]!=CSparse::SparseGet(s,i,j))
{
CAp::SetErrorFlag(err,true,"testsparseunit.ap:3355");
return;
}
}
//--- Test SparseGetCompressedRow()
wasreturned.Resize(n);
for(i=0; i<m; i++)
{
CSparse::SparseGetCompressedRow(s,i,colidx,vals,nz);
if(nz<0 || nz>n)
{
CAp::SetErrorFlag(err,true,"testsparseunit.ap:3369");
return;
}
wasreturned.Fill((int)false);
for(j=0; j<=nz-1; j++)
{
if(colidx[j]<0 || colidx[j]>n)
{
CAp::SetErrorFlag(err,true,"testsparseunit.ap:3378");
return;
}
CAp::SetErrorFlag(err,j>0 && colidx[j]<=colidx[j-1],"testsparseunit.ap:3381");
CAp::SetErrorFlag(err,vals[j]!=a.Get(i,colidx[j]) || vals[j]!=CSparse::SparseGet(s,i,colidx[j]),"testsparseunit.ap:3382");
wasreturned.Set(colidx[j],true);
}
for(j=0; j<n; j++)
CAp::SetErrorFlag(err,a.Get(i,j)!=0.0 && !wasreturned[j],"testsparseunit.ap:3386");
}
}
}
}
}
//+------------------------------------------------------------------+
//| Test for SparseConvert functions(isn't tested ConvertToCRS |
//| function). The function create random dense and sparse matrices |
//| in CRS format. Then convert sparse matrix to some format by |
//| CONVERT_TO/COPY_TO functions, then it does some modification in |
//| matrices and compares that marices are identical. |
//| NOTE: |
//| Result of the function assigned to variable CopyErrors in unit |
//| test. |
//+------------------------------------------------------------------+
bool CTestSparseUnit::TestConvertSM(void)
{
//--- create variables
bool result=false;
CSparseMatrix s;
CSparseMatrix cs;
CMatrixDouble a;
int m=0;
int n=0;
int msize=0;
int nsize=0;
CRowInt ner;
double tmp=0;
int i=0;
int j=0;
int vartf=0;
msize=15;
nsize=15;
for(m=1; m<=msize; m++)
{
for(n=1; n<=nsize; n++)
{
for(vartf=0; vartf<=2; vartf++)
{
a=matrix<double>::Zeros(m,n);
ner.Resize(m);
ner.Fill(0);
for(i=0; i<m; i++)
for(j=0; j<n; j++)
if(CMath::RandomInteger(5)==3)
{
ner.Add(i,1);
do
{
a.Set(i,j,2*CMath::RandomReal()-1);
}
while(a.Get(i,j)==0.0);
}
//--- Create sparse matrix
CSparse::SparseCreateCRS(m,n,ner,s);
for(i=0; i<m; i++)
for(j=0; j<n; j++)
if(a.Get(i,j)!=0.0)
{
a.Set(i,j,2*CMath::RandomReal()-1);
CSparse::SparseSet(s,i,j,a.Get(i,j));
}
//--- Set matrix type(we have to be sure that all formats
//--- converted correctly)
i=CMath::RandomInteger(2);
if(i==0)
CSparse::SparseConvertToHash(s);
if(i==1)
CSparse::SparseConvertToCRS(s);
//--- Start test
if(vartf==0)
{
CSparse::SparseConvertToHash(s);
CSparse::SparseCopy(s,cs);
}
if(vartf==1)
CSparse::SparseCopyToHash(s,cs);
if(vartf==2)
CSparse::SparseCopyToCRS(s,cs);
//--- Change some elements in row
if(vartf!=2)
for(i=0; i<m; i++)
{
tmp=2*CMath::RandomReal()-1;
j=CMath::RandomInteger(n);
a.Set(i,j,tmp);
CSparse::SparseSet(cs,i,j,tmp);
tmp=2*CMath::RandomReal()-1;
j=CMath::RandomInteger(n);
a.Add(i,j,tmp);
CSparse::SparseAdd(cs,i,j,tmp);
}
//--- Check that A is identical to S
for(i=0; i<m; i++)
for(j=0; j<n; j++)
if(a.Get(i,j)!=(double)(CSparse::SparseGet(cs,i,j)))
{
result=true;
return(result);
}
}
}
}
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| Test for check/get type functions. The function create sparse |
//| matrix, converts it to desired type then check this type. |
//| NOTE: |
//| Result of the function assigned to variable BasicErrors in unit|
//| test. |
//+------------------------------------------------------------------+
bool CTestSparseUnit::TestGCMatrixType(void)
{
//--- create variables
bool result=false;
CSparseMatrix s;
CSparseMatrix cs;
int m=0;
int n=0;
int msize=0;
int nsize=0;
msize=5;
nsize=5;
for(m=1; m<=msize; m++)
{
for(n=1; n<=nsize; n++)
{
CSparse::SparseCreate(m,n,1,s);
CSparse::SparseConvertToCRS(s);
if((CSparse::SparseIsHash(s) || !CSparse::SparseIsCRS(s)) || CSparse::SparseGetMatrixType(s)!=1)
{
result=true;
return(result);
}
CSparse::SparseConvertToHash(s);
if((!CSparse::SparseIsHash(s) || CSparse::SparseIsCRS(s)) || CSparse::SparseGetMatrixType(s)!=0)
{
result=true;
return(result);
}
CSparse::SparseCopyToCRS(s,cs);
if((CSparse::SparseIsHash(cs) || !CSparse::SparseIsCRS(cs)) || CSparse::SparseGetMatrixType(cs)!=1)
{
result=true;
return(result);
}
CSparse::SparseCopyToHash(cs,s);
if((!CSparse::SparseIsHash(s) || CSparse::SparseIsCRS(s)) || CSparse::SparseGetMatrixType(s)!=0)
{
result=true;
return(result);
}
}
}
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CAblasFPlayground
{
public:
double m_v0;
bool m_bx0[];
bool m_bx1[];
CRowInt m_ix0;
CRowInt m_ix1;
CRowDouble m_x0;
CRowDouble m_x1;
CRowDouble m_x2;
CRowDouble m_x3;
CMatrixDouble m_a0;
CMatrixDouble m_a1;
CAblasFPlayground() {}
~CAblasFPlayground() {}
void Copy(CAblasFPlayground &obj);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CAblasFPlayground::Copy(CAblasFPlayground &obj)
{
m_v0=obj.m_v0;
ArrayCopy(m_bx0,obj.m_bx0);
ArrayCopy(m_bx1,obj.m_bx1);
m_ix0=obj.m_ix0;
m_ix1=obj.m_ix1;
m_x0=obj.m_x0;
m_x1=obj.m_x1;
m_x2=obj.m_x2;
m_x3=obj.m_x3;
m_a0=obj.m_a0;
m_a1=obj.m_a1;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestAblasFUnit
{
public:
static bool TestAblasF(bool silent);
static double RefRDotV(int n,CRowDouble &x,CRowDouble &y);
static double RefRDotVR(int n,CRowDouble &x,CMatrixDouble &a,int i);
static double RefRDotRR(int n,CMatrixDouble &a,int ia,CMatrixDouble &b,int ib);
static double RefRDotV2(int n,CRowDouble &x);
static void RefRAddV(int n,double alpha,CRowDouble &y,CRowDouble &x);
static void RefRAddVX(int n,double alpha,CRowDouble &y,int offsy,CRowDouble &x,int offsx);
static void RefRAddVC(int n,double alpha,CRowDouble &y,CMatrixDouble &x,int colidx);
static void RefRAddVR(int n,double alpha,CRowDouble &y,CMatrixDouble &x,int rowidx);
static void RefRAddRV(int n,double alpha,CMatrixDouble &y,int ridx,CRowDouble &x);
static void RefRAddRR(int n,double alpha,CMatrixDouble &y,int ridxsrc,CMatrixDouble &x,int ridxdst);
static void RefRSetV(int n,double v,CRowDouble &x);
static void RefRSetVX(int n,double v,CRowDouble &x,int offsx);
static void RefISetV(int n,int v,CRowInt &x);
static void RefBSetV(int n,bool v,bool &x[]);
static void RefRSetM(int m,int n,double v,CMatrixDouble &a);
static void RefRSetR(int n,double v,CMatrixDouble &a,int i);
static void RefRSetC(int n,double v,CMatrixDouble &a,int j);
static void RefRCopyV(int n,CRowDouble &x,CRowDouble &y);
static void RefBCopyV(int n,bool &x[],bool &y[]);
static void RefRCopyVX(int n,CRowDouble &x,int offsx,CRowDouble &y,int offsy);
static void RefICopyV(int n,CRowInt &x,CRowInt &y);
static void RefICopyVX(int n,CRowInt &x,int offsx,CRowInt &y,int offsy);
static void RefRCopyVR(int n,CRowDouble &x,CMatrixDouble &a,int i);
static void RefRCopyRV(int n,CMatrixDouble &a,int i,CRowDouble &x);
static void RefRCopyRR(int n,CMatrixDouble &a,int i,CMatrixDouble &b,int k);
static void RefRCopyVC(int n,CRowDouble &x,CMatrixDouble &a,int j);
static void RefRCopyCV(int n,CMatrixDouble &a,int j,CRowDouble &x);
static void RefRCopyMulV(int n,double v,CRowDouble &x,CRowDouble &y);
static void RefRCopyMulVR(int n,double v,CRowDouble &x,CMatrixDouble &y,int ridx);
static void RefRCopyMulVC(int n,double v,CRowDouble &x,CMatrixDouble &y,int cidx);
static void RefRMulV(int n,double v,CRowDouble &x);
static void RefRMulR(int n,double v,CMatrixDouble &x,int rowidx);
static void RefRSqrtV(int n,CRowDouble &x);
static void RefRSqrtR(int n,CMatrixDouble &x,int rowidx);
static void RefRMulVX(int n,double v,CRowDouble &x,int offsx);
static void RefRMergeMulV(int n,CRowDouble &y,CRowDouble &x);
static void RefRMergeMulVR(int n,CRowDouble &y,CMatrixDouble &x,int rowidx);
static void RefRMergeMulRV(int n,CMatrixDouble &y,int rowidx,CRowDouble &x);
static void RefRMergeDivV(int n,CRowDouble &y,CRowDouble &x);
static void RefRMergeDivVR(int n,CRowDouble &y,CMatrixDouble &x,int rowidx);
static void RefRMergeDivRV(int n,CMatrixDouble &y,int rowidx,CRowDouble &x);
static void RefRMergeMaxV(int n,CRowDouble &y,CRowDouble &x);
static void RefRMergeMaxVR(int n,CRowDouble &y,CMatrixDouble &x,int rowidx);
static void RefRMergeMaxRV(int n,CMatrixDouble &y,int rowidx,CRowDouble &x);
static void RefRMergeMinV(int n,CRowDouble &y,CRowDouble &x);
static void RefRMergeMinVR(int n,CRowDouble &y,CMatrixDouble &x,int rowidx);
static void RefRMergeMinRV(int n,CMatrixDouble &y,int rowidx,CRowDouble &x);
static double RefRMaxV(int n,CRowDouble &x);
static double RefRMaxAbsV(int n,CRowDouble &x);
static double RefRMaxR(int n,CMatrixDouble &x,int rowidx);
static double RefRMaxAbsR(int n,CMatrixDouble &x,int rowidx);
static void RefRGemVX(int m,int n,double alpha,CMatrixDouble &a,int ia,int ja,int opa,CRowDouble &x,int ix,double beta,CRowDouble &y,int iy);
static void RefTrsVX(int n,CMatrixDouble &a,int ia,int ja,bool isupper,bool isunit,int optype,CRowDouble &x,int ix);
private:
static bool TestXDot(int maxn,double tol);
static bool TestXSet(int maxn,double tol);
static bool TestXAdd(int maxn,double tol);
static bool TestXMulAdd(int maxn,double tol);
static bool TestXMul(int maxn,double tol);
static bool TestXSqrt(int maxn,double tol);
static bool TestXMax(int maxn,double tol);
static bool TestXMerge(int maxn,double tol);
static bool TestXCopy(int maxn,double tol);
static bool TestXGemV(int maxn,double tol);
static bool TestXGer(int maxn,double tol);
static bool TestXTrsV(int maxn,double tol);
static void InitPlayground(int minlen,int iseed,CAblasFPlayground &s);
static double ComparePlaygrounds(CAblasFPlayground &s0,CAblasFPlayground &s1);
static int PseudoRandomInit1(CRowDouble &x,int iseed);
static int PseudoRandomInit1I(CRowInt &x,int iseed);
static int PseudoRandomInit1B(bool &x[],int iseed);
static int PseudoRandomInit2(CMatrixDouble &x,int iseed);
static double rmx3(double r0,double r1,double r2);
static double rcmp1(CRowDouble &x,CRowDouble &y);
static double icmp1(CRowInt &x,CRowInt &y);
static double bcmp1(bool &x[],bool &y[]);
static double rcmp2(CMatrixDouble &x,CMatrixDouble &y);
static void RefRMulAddV(int n,CRowDouble &y,CRowDouble &z,CRowDouble &x);
static void RefRNegMulAddV(int n,CRowDouble &y,CRowDouble &z,CRowDouble &x);
static void RefRCopyMulAddV(int n,CRowDouble &y,CRowDouble &z,CRowDouble &x,CRowDouble &r);
static void RefRCopyNegMulAddV(int n,CRowDouble &y,CRowDouble &z,CRowDouble &x,CRowDouble &r);
static void RefGerX(int m,int n,CMatrixDouble &a,int ia,int ja,double alpha,CRowDouble &u,int iu,CRowDouble &v,int iv);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestAblasFUnit::TestAblasF(bool silent)
{
//--- create variables
bool result;
int maxn=100;
double tol=10000*CMath::m_machineepsilon;
bool waserrors;
bool xdoterrors;
bool xseterrors;
bool xadderrors;
bool xmulerrors;
bool xmaxerrors;
bool xsqrterrors;
bool xmergeerrors;
bool xcopyerrors;
bool xmuladderrors;
bool xgemverrors;
bool xgererrors;
bool xtrsverrors;
//--- check
xdoterrors=TestXDot(maxn,tol);
xseterrors=TestXSet(maxn,tol);
xadderrors=TestXAdd(maxn,tol);
xmulerrors=TestXMul(maxn,tol);
xsqrterrors=TestXSqrt(maxn,tol);
xmaxerrors=TestXMax(maxn,tol);
xmergeerrors=TestXMerge(maxn,tol);
xcopyerrors=TestXCopy(maxn,tol);
xmuladderrors=TestXMulAdd(maxn,tol);
xgemverrors=TestXGemV(maxn,tol);
xgererrors=TestXGer(maxn,tol);
xtrsverrors=TestXTrsV(maxn,tol);
waserrors=((((((((((xdoterrors||xseterrors)||xadderrors)||xmulerrors)||xsqrterrors)||xmaxerrors)||xmergeerrors)||xcopyerrors)||xmuladderrors)||xgemverrors)||xgererrors)||xtrsverrors;
//--- report
if(!silent)
{
Print("TESTING ABLASF");
PrintResult("xDotXX ",!xdoterrors);
PrintResult("xSetXX",!xseterrors);
PrintResult("xAddXX",!xadderrors);
PrintResult("xMulXX",!xmulerrors);
PrintResult("xSqrtXX",!xsqrterrors);
PrintResult("xMaxXX",!xmaxerrors);
PrintResult("xMergeXX",!xmergeerrors);
PrintResult("xMulAddXX",!xmuladderrors);
PrintResult("xCopyXX",!xcopyerrors);
PrintResult("xGEMVx",!xgemverrors);
PrintResult("xGERx",!xgererrors);
PrintResult("xTRSVx",!xtrsverrors);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CTestAblasFUnit::RefRDotV(int n,CRowDouble &x,CRowDouble &y)
{
double result=0;
for(int i=0; i<n; i++)
result+=x[i]*y[i];
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CTestAblasFUnit::RefRDotVR(int n,CRowDouble &x,
CMatrixDouble &a,int i)
{
double result=0;
for(int j=0; j<n; j++)
result+=x[j]*a.Get(i,j);
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CTestAblasFUnit::RefRDotRR(int n,CMatrixDouble &a,int ia,
CMatrixDouble &b,int ib)
{
double result=0;
for(int j=0; j<n; j++)
result+=a.Get(ia,j)*b.Get(ib,j);
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CTestAblasFUnit::RefRDotV2(int n,CRowDouble &x)
{
//--- create variables
double result=0;
double v=0;
for(int i=0; i<n; i++)
{
v=x[i];
result+=v*v;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRAddV(int n,double alpha,CRowDouble &y,
CRowDouble &x)
{
for(int i=0; i<n; i++)
x.Add(i,alpha*y[i]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRAddVX(int n,double alpha,
CRowDouble &y,int offsy,
CRowDouble &x,int offsx)
{
for(int i=0; i<n; i++)
x.Add(offsx+i,alpha*y[offsy+i]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRAddVC(int n,double alpha,CRowDouble &y,
CMatrixDouble &x,int colidx)
{
for(int i=0; i<n; i++)
x.Add(i,colidx,alpha*y[i]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRAddVR(int n,double alpha,CRowDouble &y,
CMatrixDouble &x,int rowidx)
{
for(int i=0; i<n; i++)
x.Add(rowidx,i,alpha*y[i]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRAddRV(int n,double alpha,CMatrixDouble &y,
int ridx,CRowDouble &x)
{
for(int i=0; i<n; i++)
x.Add(i,alpha*y.Get(ridx,i));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRAddRR(int n,double alpha,
CMatrixDouble &y,int ridxsrc,
CMatrixDouble &x,int ridxdst)
{
for(int i=0; i<n; i++)
x.Add(ridxdst,i,alpha*y.Get(ridxsrc,i));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRSetV(int n,double v,CRowDouble &x)
{
for(int j=0; j<n; j++)
x.Set(j,v);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRSetVX(int n,double v,
CRowDouble &x,int offsx)
{
for(int j=0; j<n; j++)
x.Set(offsx+j,v);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefISetV(int n,int v,CRowInt &x)
{
for(int j=0; j<n; j++)
x.Set(j,v);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefBSetV(int n,bool v,bool &x[])
{
for(int j=0; j<n; j++)
x[j]=v;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRSetM(int m,int n,double v,CMatrixDouble &a)
{
for(int i=0; i<m; i++)
for(int j=0; j<n; j++)
a.Set(i,j,v);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRSetR(int n,double v,
CMatrixDouble &a,int i)
{
for(int j=0; j<n; j++)
a.Set(i,j,v);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRSetC(int n,double v,
CMatrixDouble &a,int j)
{
for(int i=0; i<n; i++)
a.Set(i,j,v);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRCopyV(int n,CRowDouble &x,CRowDouble &y)
{
for(int j=0; j<n; j++)
y.Set(j,x[j]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefBCopyV(int n,bool &x[],bool &y[])
{
for(int j=0; j<n; j++)
y[j]=x[j];
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRCopyVX(int n,CRowDouble &x,int offsx,
CRowDouble &y,int offsy)
{
for(int j=0; j<n; j++)
y.Set(offsy+j,x[offsx+j]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefICopyV(int n,CRowInt &x,CRowInt &y)
{
for(int j=0; j<n; j++)
y.Set(j,x[j]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefICopyVX(int n,CRowInt &x,int offsx,
CRowInt &y,int offsy)
{
for(int j=0; j<n; j++)
y.Set(offsy+j,x[offsx+j]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRCopyVR(int n,CRowDouble &x,
CMatrixDouble &a,int i)
{
for(int j=0; j<n; j++)
a.Set(i,j,x[j]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRCopyRV(int n,CMatrixDouble &a,int i,
CRowDouble &x)
{
for(int j=0; j<n; j++)
x.Set(j,a.Get(i,j));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRCopyRR(int n,CMatrixDouble &a,int i,
CMatrixDouble &b,int k)
{
for(int j=0; j<n; j++)
b.Set(k,j,a.Get(i,j));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRCopyVC(int n,CRowDouble &x,
CMatrixDouble &a,int j)
{
for(int i=0; i<n; i++)
a.Set(i,j,x[i]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRCopyCV(int n,CMatrixDouble &a,int j,
CRowDouble &x)
{
for(int i=0; i<n; i++)
x.Set(i,a.Get(i,j));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRCopyMulV(int n,double v,CRowDouble &x,
CRowDouble &y)
{
for(int i=0; i<n; i++)
y.Set(i,v*x[i]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRCopyMulVR(int n,double v,CRowDouble &x,
CMatrixDouble &y,int ridx)
{
for(int i=0; i<n; i++)
y.Set(ridx,i,v*x[i]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRCopyMulVC(int n,double v,CRowDouble &x,
CMatrixDouble &y,int cidx)
{
for(int i=0; i<n; i++)
y.Set(i,cidx,v*x[i]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRMulV(int n,double v,CRowDouble &x)
{
for(int i=0; i<n; i++)
x.Mul(i,v);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRMulR(int n,double v,CMatrixDouble &x,
int rowidx)
{
for(int i=0; i<n; i++)
x.Mul(rowidx,i,v);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRSqrtV(int n,CRowDouble &x)
{
for(int i=0; i<n; i++)
x.Set(i,MathSqrt(x[i]));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRSqrtR(int n,CMatrixDouble &x,int rowidx)
{
for(int i=0; i<n; i++)
x.Set(rowidx,i,MathSqrt(x.Get(rowidx,i)));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRMulVX(int n,double v,
CRowDouble &x,int offsx)
{
for(int i=0; i<n; i++)
x.Mul(offsx+i,v);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRMergeMulV(int n,CRowDouble &y,CRowDouble &x)
{
for(int i=0; i<n; i++)
x.Mul(i,y[i]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRMergeMulVR(int n,CRowDouble &y,
CMatrixDouble &x,int rowidx)
{
for(int i=0; i<n; i++)
x.Mul(rowidx,i,y[i]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRMergeMulRV(int n,CMatrixDouble &y,int rowidx,
CRowDouble &x)
{
for(int i=0; i<n; i++)
x.Mul(i,y.Get(rowidx,i));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRMergeDivV(int n,CRowDouble &y,CRowDouble &x)
{
for(int i=0; i<n; i++)
x.Mul(i,1.0/y[i]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRMergeDivVR(int n,CRowDouble &y,
CMatrixDouble &x,int rowidx)
{
for(int i=0; i<n; i++)
x.Mul(rowidx,i,1.0/y[i]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRMergeDivRV(int n,CMatrixDouble &y,int rowidx,
CRowDouble &x)
{
for(int i=0; i<n; i++)
x.Mul(i,1.0/y.Get(rowidx,i));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRMergeMaxV(int n,CRowDouble &y,CRowDouble &x)
{
for(int i=0; i<n; i++)
x.Set(i,MathMax(x[i],y[i]));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRMergeMaxVR(int n,CRowDouble &y,
CMatrixDouble &x,int rowidx)
{
for(int i=0; i<n; i++)
x.Set(rowidx,i,MathMax(x.Get(rowidx,i),y[i]));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRMergeMaxRV(int n,CMatrixDouble &y,int rowidx,
CRowDouble &x)
{
for(int i=0; i<n; i++)
x.Set(i,MathMax(x[i],y.Get(rowidx,i)));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRMergeMinV(int n,CRowDouble &y,CRowDouble &x)
{
for(int i=0; i<n; i++)
x.Set(i,MathMin(x[i],y[i]));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRMergeMinVR(int n,CRowDouble &y,
CMatrixDouble &x,int rowidx)
{
for(int i=0; i<n; i++)
x.Set(rowidx,i,MathMin(x.Get(rowidx,i),y[i]));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRMergeMinRV(int n,CMatrixDouble &y,int rowidx,
CRowDouble &x)
{
for(int i=0; i<n; i++)
x.Set(i,MathMin(x[i],y.Get(rowidx,i)));
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CTestAblasFUnit::RefRMaxV(int n,CRowDouble &x)
{
//--- create variables
double result=0;
double v=0;
//--- check
if(n<=0)
{
result=0;
return(result);
}
result=x[0];
for(int i=0; i<n; i++)
{
v=x[i];
if(v>result)
result=v;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CTestAblasFUnit::RefRMaxAbsV(int n,CRowDouble &x)
{
//--- create variables
double result=0;
double v=0;
for(int i=0; i<n; i++)
{
v=MathAbs(x[i]);
if(v>result)
result=v;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CTestAblasFUnit::RefRMaxR(int n,CMatrixDouble &x,int rowidx)
{
//--- create variables
double result=0;
double v=0;
//--- check
if(n<=0)
{
result=0;
return(result);
}
result=x.Get(rowidx,0);
for(int i=0; i<n; i++)
{
v=x.Get(rowidx,i);
if(v>result)
result=v;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CTestAblasFUnit::RefRMaxAbsR(int n,CMatrixDouble &x,int rowidx)
{
//--- create variables
double result=0;
double v=0;
for(int i=0; i<n; i++)
{
v=MathAbs(x.Get(rowidx,i));
if(v>result)
result=v;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRGemVX(int m,int n,double alpha,
CMatrixDouble &a,int ia,
int ja,int opa,
CRowDouble &x,int ix,double beta,
CRowDouble &y,int iy)
{
//--- create variables
int i=0;
double v=0;
int i_=0;
int i1_=0;
//--- Quick exit for M=0, N=0 or Alpha=0.
//--- After this block we have M>0, N>0, Alpha<>0.
if(m<=0)
return;
if(n<=0 || alpha==0.0)
{
if(beta!=0.0)
{
for(i=0; i<m; i++)
y.Mul(iy+i,beta);
}
else
{
for(i=0; i<m; i++)
y.Set(iy+i,0.0);
}
return;
}
//--- Generic code
if(opa==0)
{
//--- y = A*x
for(i=0; i<m; i++)
{
i1_=(ix)-(ja);
v=0.0;
for(i_=ja; i_<ja+n; i_++)
v+=a.Get(ia+i,i_)*x[i_+i1_];
if(beta==0.0)
y.Set(iy+i,alpha*v);
else
y.Set(iy+i,alpha*v+beta*y[iy+i]);
}
return;
}
if(opa==1)
{
//--- Prepare output array
if(beta==0.0)
{
for(i=0; i<m; i++)
y.Set(iy+i,0);
}
else
{
for(i=0; i<m; i++)
y.Mul(iy+i,beta);
}
//--- y += A^T*x
for(i=0; i<n; i++)
{
v=alpha*x[ix+i];
i1_=(ja)-(iy);
for(i_=iy; i_<iy+m; i_++)
y.Add(i_,v*a.Get(ia+i,i_+i1_));
}
return;
}
}
//+------------------------------------------------------------------+
//| Reference TRSV |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefTrsVX(int n,CMatrixDouble &a,int ia,int ja,
bool isupper,bool isunit,int optype,
CRowDouble &x,int ix)
{
//--- create variables
int i=0;
int j=0;
double v=0;
//--- Quick exit
if(n<=0)
return;
//--- Generic code
if(optype==0 && isupper)
{
for(i=n-1; i>=0; i--)
{
v=x[ix+i];
for(j=i+1; j<n; j++)
v-=a.Get(ia+i,ja+j)*x[ix+j];
if(!isunit)
v/=a.Get(ia+i,ja+i);
x.Set(ix+i,v);
}
return;
}
if(optype==0 && !isupper)
{
for(i=0; i<n; i++)
{
v=x[ix+i];
for(j=0; j<i; j++)
v-=a.Get(ia+i,ja+j)*x[ix+j];
if(!isunit)
v/=a.Get(ia+i,ja+i);
x.Set(ix+i,v);
}
return;
}
if(optype==1 && isupper)
{
for(i=0; i<n; i++)
{
v=x[ix+i];
if(!isunit)
v/=a.Get(ia+i,ja+i);
x.Set(ix+i,v);
if(v==0)
continue;
for(j=i+1; j<n; j++)
x.Add(ix+j,- v*a.Get(ia+i,ja+j));
}
return;
}
if(optype==1 && !isupper)
{
for(i=n-1; i>=0; i--)
{
v=x[ix+i];
if(!isunit)
v/=a.Get(ia+i,ja+i);
x.Set(ix+i,v);
if(v==0)
continue;
for(j=0; j<i; j++)
x.Add(ix+j,- v*a.Get(ia+i,ja+j));
}
return;
}
//--- check
CAp::Assert(false,"RMatrixTRSV: unexpected operation type");
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestAblasFUnit::TestXDot(int maxn,double tol)
{
//--- create variables
bool result=false;
int n=0;
int iseed=CMath::RandomInteger(10000);
int ridx=0;
int ridx2=0;
CAblasFPlayground s0;
CAblasFPlayground s1;
for(n=0; n<=maxn; n++)
{
//--- Prepare two identical playground structures
InitPlayground(n,iseed,s0);
s1=s0;
iseed ++;
//--- Compute each rDot version twice - reference vs library.
//--- Compare playground snapshots - should be identical.
ridx=CMath::RandomInteger(MathMax(n,1));
ridx2=CMath::RandomInteger(MathMax(n,1));
CAp::SetErrorFlag(result,(MathAbs(CAblasF::RDotV(n,s0.m_x0,s0.m_x1)-RefRDotV(n,s1.m_x0,s1.m_x1)))>tol,"testablasfunit.ap:156");
CAp::SetErrorFlag(result,(MathAbs(CAblasF::RDotVR(n,s0.m_x0,s0.m_a0,ridx)-RefRDotVR(n,s1.m_x0,s1.m_a0,ridx)))>tol,"testablasfunit.ap:157");
CAp::SetErrorFlag(result,(MathAbs(CAblasF::RDotRR(n,s0.m_a0,ridx,s0.m_a1,ridx2)-RefRDotRR(n,s1.m_a0,ridx,s1.m_a1,ridx2)))>tol,"testablasfunit.ap:158");
CAp::SetErrorFlag(result,(MathAbs(CAblasF::RDotV2(n,s0.m_x0)-RefRDotV2(n,s1.m_x0)))>tol,"testablasfunit.ap:159");
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestAblasFUnit::TestXSet(int maxn,double tol)
{
//--- create variables
bool result=false;
int n=0;
int iseed=CMath::RandomInteger(10000);
double alpha=0;
int ialpha=0;
bool balpha;
int ridx=0;
int cidx=0;
int m0=0;
int m1=0;
int offsx=0;
CAblasFPlayground s0;
CAblasFPlayground s1;
for(n=0; n<=maxn; n++)
{
//--- Prepare two identical playground structures
//--- Compute each xSetXX version twice - reference vs library.
//--- Compare playground snapshots - should be identical.
m0=CMath::RandomInteger(MathMax(n,1));
m1=CMath::RandomInteger(MathMax(n,1));
ridx=CMath::RandomInteger(MathMax(n,1));
cidx=CMath::RandomInteger(MathMax(n,1));
offsx=CMath::RandomInteger(n/2+1);
alpha=2*CMath::RandomReal()-1;
ialpha=CMath::RandomInteger(21)-10;
balpha=CMath::RandomReal()>0.5;
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RSetV(n,alpha,s0.m_x0);
RefRSetV(n,alpha,s1.m_x0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:197");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RSetR(n,alpha,s0.m_a0,ridx);
RefRSetR(n,alpha,s1.m_a0,ridx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:203");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RSetC(n,alpha,s0.m_a0,cidx);
RefRSetC(n,alpha,s1.m_a0,cidx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:209");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RSetM(m0,m1,alpha,s0.m_a0);
RefRSetM(m0,m1,alpha,s1.m_a0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:215");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::ISetV(n,ialpha,s0.m_ix0);
RefISetV(n,ialpha,s1.m_ix0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:221");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::BSetV(n,balpha,s0.m_bx0);
RefBSetV(n,balpha,s1.m_bx0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:227");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RSetVX(n/2,alpha,s0.m_x0,offsx);
RefRSetVX(n/2,alpha,s1.m_x0,offsx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:233");
//--- Increment seed
iseed ++;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestAblasFUnit::TestXAdd(int maxn,double tol)
{
//--- create variables
bool result=false;
int n=0;
int iseed=CMath::RandomInteger(10000);
double alpha=0;
int ridx=0;
int ridx2=0;
int cidx=0;
int offsx=0;
int offsy=0;
CAblasFPlayground s0;
CAblasFPlayground s1;
for(n=0; n<=maxn; n++)
{
//--- Prepare two identical playground structures
//--- Compute each xAddXX version twice - reference vs library.
//--- Compare playground snapshots - should be identical.
ridx=CMath::RandomInteger(MathMax(n,1));
ridx2=CMath::RandomInteger(MathMax(n,1));
cidx=CMath::RandomInteger(MathMax(n,1));
offsx=CMath::RandomInteger(n/2+1);
offsy=CMath::RandomInteger(n/2+1);
alpha=2*CMath::RandomReal()-1;
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RAddV(n,alpha,s0.m_x0,s0.m_x1);
RefRAddV(n,alpha,s1.m_x0,s1.m_x1);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:271");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RAddVR(n,alpha,s0.m_x0,s0.m_a0,cidx);
RefRAddVR(n,alpha,s1.m_x0,s1.m_a0,cidx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:277");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RAddVC(n,alpha,s0.m_x0,s0.m_a0,cidx);
RefRAddVC(n,alpha,s1.m_x0,s1.m_a0,cidx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:283");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RAddRV(n,alpha,s0.m_a0,ridx,s0.m_x0);
RefRAddRV(n,alpha,s1.m_a0,ridx,s1.m_x0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:289");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RAddRR(n,alpha,s0.m_a0,ridx,s0.m_a1,ridx2);
RefRAddRR(n,alpha,s1.m_a0,ridx,s1.m_a1,ridx2);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:295");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RAddVX(n/2,alpha,s0.m_x0,offsx,s0.m_x1,offsy);
RefRAddVX(n/2,alpha,s1.m_x0,offsx,s1.m_x1,offsy);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:301");
//--- Increment seed
iseed++;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestAblasFUnit::TestXMulAdd(int maxn,double tol)
{
//--- create variables
bool result=false;
int n=0;
int iseed=CMath::RandomInteger(10000);
CAblasFPlayground s0;
CAblasFPlayground s1;
for(n=0; n<=maxn; n++)
{
//--- Prepare two identical playground structures
//--- Compute each xMulAddXX version twice - reference vs library.
//--- Compare playground snapshots - should be identical.
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RMulAddV(n,s0.m_x0,s0.m_x1,s0.m_x2);
RefRMulAddV(n,s1.m_x0,s1.m_x1,s1.m_x2);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:329");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RNegMulAddV(n,s0.m_x0,s0.m_x1,s0.m_x2);
RefRNegMulAddV(n,s1.m_x0,s1.m_x1,s1.m_x2);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:335");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RCopyMulAddV(n,s0.m_x0,s0.m_x1,s0.m_x2,s0.m_x3);
RefRCopyMulAddV(n,s1.m_x0,s1.m_x1,s1.m_x2,s1.m_x3);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:341");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RCopyNegMulAddV(n,s0.m_x0,s0.m_x1,s0.m_x2,s0.m_x3);
RefRCopyNegMulAddV(n,s1.m_x0,s1.m_x1,s1.m_x2,s1.m_x3);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:347");
//--- Increment seed
iseed++;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestAblasFUnit::TestXMul(int maxn,double tol)
{
//--- create variables
bool result=false;
int n=0;
int iseed=CMath::RandomInteger(10000);
double alpha=0;
int ridx=0;
int offsx=0;
CAblasFPlayground s0;
CAblasFPlayground s1;
for(n=0; n<=maxn; n++)
{
//--- Prepare two identical playground structures
//--- Compute each xAddXX version twice - reference vs library.
//--- Compare playground snapshots - should be identical.
ridx=CMath::RandomInteger(MathMax(n,1));
offsx=CMath::RandomInteger(n/2+1);
alpha=2*CMath::RandomReal()-1;
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RMulV(n,alpha,s0.m_x0);
RefRMulV(n,alpha,s1.m_x0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:382");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RMulR(n,alpha,s0.m_a0,ridx);
RefRMulR(n,alpha,s1.m_a0,ridx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:388");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RMulVX(n/2,alpha,s0.m_x0,offsx);
RefRMulVX(n/2,alpha,s1.m_x0,offsx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:394");
//--- Increment seed
iseed++;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestAblasFUnit::TestXSqrt(int maxn,double tol)
{
//--- create variables
bool result=false;
int n=0;
int iseed=CMath::RandomInteger(10000);
int ridx=0;
CAblasFPlayground s0;
CAblasFPlayground s1;
for(n=0; n<=maxn; n++)
{
//--- Prepare two identical playground structures
//--- Compute each xAddXX version twice - reference vs library.
//--- Compare playground snapshots - should be identical.
ridx=CMath::RandomInteger(MathMax(n,1));
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RSqrtV(n,s0.m_x0);
RefRSqrtV(n,s1.m_x0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:425");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RSqrtR(n,s0.m_a0,ridx);
RefRSqrtR(n,s1.m_a0,ridx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:431");
//--- Increment seed
iseed++;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestAblasFUnit::TestXMax(int maxn,double tol)
{
//--- create variables
bool result=false;
int n=0;
int iseed=CMath::RandomInteger(10000);
int ridx=0;
CAblasFPlayground s0;
CAblasFPlayground s1;
for(n=0; n<=maxn; n++)
{
//--- Prepare two identical playground structures
//--- Compute each xAddXX version twice - reference vs library.
//--- Compare playground snapshots - should be identical.
ridx=CMath::RandomInteger(MathMax(n,1));
InitPlayground(n,iseed,s0);
s1=s0;
s0.m_v0=CAblasF::RMaxV(n,s0.m_x0);
s1.m_v0=RefRMaxV(n,s1.m_x0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:462");
//---
InitPlayground(n,iseed,s0);
s1=s0;
s0.m_v0=CAblasF::RMaxAbsV(n,s0.m_x0);
s1.m_v0=RefRMaxAbsV(n,s1.m_x0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:468");
//---
InitPlayground(n,iseed,s0);
s1=s0;
s0.m_v0=CAblasF::RMaxR(n,s0.m_a0,ridx);
s1.m_v0=RefRMaxR(n,s1.m_a0,ridx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:474");
//---
InitPlayground(n,iseed,s0);
s1=s0;
s0.m_v0=CAblasF::RMaxAbsR(n,s0.m_a0,ridx);
s1.m_v0=RefRMaxAbsR(n,s1.m_a0,ridx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:480");
//--- Increment seed
iseed++;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestAblasFUnit::TestXMerge(int maxn,double tol)
{
//--- create variables
bool result=false;
int n=0;
int i=0;
int iseed=CMath::RandomInteger(10000);
int ridx=0;
CAblasFPlayground s0;
CAblasFPlayground s1;
for(n=0; n<=maxn; n++)
{
//--- Prepare two identical playground structures
//--- Compute each xAddXX version twice - reference vs library.
//--- Compare playground snapshots - should be identical.
ridx=CMath::RandomInteger(MathMax(n,1));
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RMergeMulV(n,s0.m_x0,s0.m_x1);
RefRMergeMulV(n,s1.m_x0,s1.m_x1);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:511");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RMergeMulVR(n,s0.m_x0,s0.m_a0,ridx);
RefRMergeMulVR(n,s1.m_x0,s1.m_a0,ridx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:517");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RMergeMulRV(n,s0.m_a0,ridx,s0.m_x0);
RefRMergeMulRV(n,s1.m_a0,ridx,s1.m_x0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:523");
//---
InitPlayground(n,iseed,s0);
s1=s0;
for(i=0; i<n; i++)
{
s0.m_x0.Add(i,0.01*CApServ::PosSign(s0.m_x0[i]));
s1.m_x0.Add(i,0.01*CApServ::PosSign(s1.m_x0[i]));
}
CAblasF::RMergeDivV(n,s0.m_x0,s0.m_x1);
RefRMergeDivV(n,s1.m_x0,s1.m_x1);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:534");
//---
InitPlayground(n,iseed,s0);
s1=s0;
for(i=0; i<n; i++)
{
s0.m_x0.Add(i,0.01*CApServ::PosSign(s0.m_x0[i]));
s1.m_x0.Add(i,0.01*CApServ::PosSign(s1.m_x0[i]));
}
CAblasF::RMergeDivVR(n,s0.m_x0,s0.m_a0,ridx);
RefRMergeDivVR(n,s1.m_x0,s1.m_a0,ridx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:545");
//---
InitPlayground(n,iseed,s0);
s1=s0;
for(i=0; i<n; i++)
{
s0.m_a0.Add(ridx,i,0.01*CApServ::PosSign(s0.m_a0.Get(ridx,i)));
s1.m_a0.Add(ridx,i,0.01*CApServ::PosSign(s1.m_a0.Get(ridx,i)));
}
CAblasF::RMergeDivRV(n,s0.m_a0,ridx,s0.m_x0);
RefRMergeDivRV(n,s1.m_a0,ridx,s1.m_x0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:556");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RMergeMaxV(n,s0.m_x0,s0.m_x1);
RefRMergeMaxV(n,s1.m_x0,s1.m_x1);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:562");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RMergeMaxVR(n,s0.m_x0,s0.m_a0,ridx);
RefRMergeMaxVR(n,s1.m_x0,s1.m_a0,ridx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:568");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RMergeMaxRV(n,s0.m_a0,ridx,s0.m_x0);
RefRMergeMaxRV(n,s1.m_a0,ridx,s1.m_x0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:574");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RMergeMinV(n,s0.m_x0,s0.m_x1);
RefRMergeMinV(n,s1.m_x0,s1.m_x1);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:580");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RMergeMinVR(n,s0.m_x0,s0.m_a0,ridx);
RefRMergeMinVR(n,s1.m_x0,s1.m_a0,ridx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:586");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RMergeMinRV(n,s0.m_a0,ridx,s0.m_x0);
RefRMergeMinRV(n,s1.m_a0,ridx,s1.m_x0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:592");
//--- Increment seed
iseed++;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestAblasFUnit::TestXCopy(int maxn,double tol)
{
//--- create variables
bool result=false;
int n=0;
int iseed=CMath::RandomInteger(10000);
double alpha=0;
int ridx=0;
int ridx2=0;
int cidx=0;
int offsx=0;
int offsy=0;
CAblasFPlayground s0;
CAblasFPlayground s1;
for(n=0; n<=maxn; n++)
{
//--- Prepare two identical playground structures
//--- Compute each xAddXX version twice - reference vs library.
//--- Compare playground snapshots - should be identical.
ridx=CMath::RandomInteger(MathMax(n,1));
ridx2=CMath::RandomInteger(MathMax(n,1));
cidx=CMath::RandomInteger(MathMax(n,1));
offsx=CMath::RandomInteger(n/2+1);
offsy=CMath::RandomInteger(n/2+1);
alpha=2*CMath::RandomReal()-1;
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RCopyV(n,s0.m_x0,s0.m_x1);
RefRCopyV(n,s1.m_x0,s1.m_x1);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:630");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::ICopyV(n,s0.m_ix0,s0.m_ix1);
RefICopyV(n,s1.m_ix0,s1.m_ix1);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:636");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::BCopyV(n,s0.m_bx0,s0.m_bx1);
RefBCopyV(n,s1.m_bx0,s1.m_bx1);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:642");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RCopyVR(n,s0.m_x0,s0.m_a0,ridx);
RefRCopyVR(n,s1.m_x0,s1.m_a0,ridx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:648");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RCopyRV(n,s0.m_a0,ridx,s0.m_x0);
RefRCopyRV(n,s1.m_a0,ridx,s1.m_x0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:654");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RCopyRR(n,s0.m_a0,ridx,s0.m_a1,ridx2);
RefRCopyRR(n,s1.m_a0,ridx,s1.m_a1,ridx2);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:660");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RCopyVC(n,s0.m_x0,s0.m_a0,cidx);
RefRCopyVC(n,s1.m_x0,s1.m_a0,cidx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:666");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RCopyCV(n,s0.m_a0,cidx,s0.m_x0);
RefRCopyCV(n,s1.m_a0,cidx,s1.m_x0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:672");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RCopyVX(n/2,s0.m_x0,offsx,s0.m_x1,offsy);
RefRCopyVX(n/2,s1.m_x0,offsx,s1.m_x1,offsy);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:678");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::ICopyVX(n/2,s0.m_ix0,offsx,s0.m_ix1,offsy);
RefICopyVX(n/2,s1.m_ix0,offsx,s1.m_ix1,offsy);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:684");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RCopyMulV(n,alpha,s0.m_x0,s0.m_x1);
RefRCopyMulV(n,alpha,s1.m_x0,s1.m_x1);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:690");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RCopyMulVR(n,alpha,s0.m_x0,s0.m_a0,ridx);
RefRCopyMulVR(n,alpha,s1.m_x0,s1.m_a0,ridx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:696");
//---
InitPlayground(n,iseed,s0);
s1=s0;
CAblasF::RCopyMulVC(n,alpha,s0.m_x0,s0.m_a0,cidx);
RefRCopyMulVC(n,alpha,s1.m_x0,s1.m_a0,cidx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:702");
//--- Increment seed
iseed++;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestAblasFUnit::TestXGemV(int maxn,double tol)
{
//--- create variables
bool result=false;
int n=0;
int m=0;
int iseed=CMath::RandomInteger(10000);
double alpha=0;
double beta=0;
int offs0=0;
int offs1=0;
int offsx=0;
int offsy=0;
int padding=0;
int opa=0;
CAblasFPlayground s0;
CAblasFPlayground s1;
for(n=0; n<=maxn; n++)
{
//--- Prepare two identical playground structures
//--- Compute each GEMV version twice - reference vs library.
//--- Compare playground snapshots - should be identical.
padding=CMath::RandomInteger(10);
m=CMath::RandomInteger(n+1);
offs0=CMath::RandomInteger(MathMax(n,1));
offs1=CMath::RandomInteger(MathMax(n,1));
offsx=CMath::RandomInteger(MathMax(n,1));
offsy=CMath::RandomInteger(MathMax(n,1));
alpha=(2*CMath::RandomReal()-1)*CMath::RandomInteger(2);
beta=(2*CMath::RandomReal()-1)*CMath::RandomInteger(2);
opa=CMath::RandomInteger(2);
//---
InitPlayground(2*n+padding,iseed,s0);
InitPlayground(2*n+padding,iseed,s1);
CAblasF::RGemV(m,n,alpha,s0.m_a0,opa,s0.m_x0,beta,s0.m_x1);
RefRGemVX(m,n,alpha,s1.m_a0,0,0,opa,s1.m_x0,0,beta,s1.m_x1,0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:745");
//---
InitPlayground(2*n+padding,iseed,s0);
InitPlayground(2*n+padding,iseed,s1);
CAblasF::RGemVX(m,n,alpha,s0.m_a0,offs0,offs1,opa,s0.m_x0,offsx,beta,s0.m_x1,offsy);
RefRGemVX(m,n,alpha,s1.m_a0,offs0,offs1,opa,s1.m_x0,offsx,beta,s1.m_x1,offsy);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:751");
//--- Increment seed
iseed++;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestAblasFUnit::TestXGer(int maxn,double tol)
{
//--- create variables
bool result=false;
int n=0;
int m=0;
int iseed=CMath::RandomInteger(10000);
double alpha=0;
int padding=0;
CAblasFPlayground s0;
CAblasFPlayground s1;
for(n=0; n<=maxn; n++)
{
//--- Prepare two identical playground structures
//--- Compute each GER version twice - reference vs library.
//--- Compare playground snapshots - should be identical.
padding=CMath::RandomInteger(10);
m=CMath::RandomInteger(n+1);
alpha=(2*CMath::RandomReal()-1)*CMath::RandomInteger(2);
//---
InitPlayground(2*n+padding,iseed,s0);
InitPlayground(2*n+padding,iseed,s1);
CAblasF::RGer(m,n,alpha,s0.m_x0,s0.m_x1,s0.m_a0);
RefGerX(m,n,s1.m_a0,0,0,alpha,s1.m_x0,0,s1.m_x1,0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:786");
//---
InitPlayground(2*n+padding,iseed,s0);
InitPlayground(2*n+padding,iseed,s1);
CAblasF::RGer(n,m,alpha,s0.m_x0,s0.m_x1,s0.m_a0);
RefGerX(n,m,s1.m_a0,0,0,alpha,s1.m_x0,0,s1.m_x1,0);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:792");
//--- Increment seed
iseed++;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestAblasFUnit::TestXTrsV(int maxn,double tol)
{
//--- create variables
bool result=false;
int n=0;
int iseed=CMath::RandomInteger(10000);
int offs0=0;
int offs1=0;
int offsx=0;
int padding=0;
int opa=0;
bool isupper;
bool isunit;
CAblasFPlayground s0;
CAblasFPlayground s1;
for(n=0; n<=maxn; n++)
{
//--- Prepare two identical playground structures
//--- Compute each GEMV version twice - reference vs library.
//--- Compare playground snapshots - should be identical.
padding=CMath::RandomInteger(10);
offs0=CMath::RandomInteger(MathMax(n,1));
offs1=CMath::RandomInteger(MathMax(n,1));
offsx=CMath::RandomInteger(MathMax(n,1));
opa=CMath::RandomInteger(2);
isupper=CMath::RandomReal()>0.5;
isunit=CMath::RandomReal()>0.5;
InitPlayground(2*n+padding,iseed,s0);
InitPlayground(2*n+padding,iseed,s1);
CAblasF::RTrsVX(n,s0.m_a0,offs0,offs1,isupper,isunit,opa,s0.m_x0,offsx);
RefTrsVX(n,s1.m_a0,offs0,offs1,isupper,isunit,opa,s1.m_x0,offsx);
CAp::SetErrorFlag(result,ComparePlaygrounds(s0,s1)>tol,"testablasfunit.ap:833");
//--- Increment seed
iseed++;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::InitPlayground(int minlen,int iseed,CAblasFPlayground &s)
{
int maxpad=10;
int k=iseed+iseed*iseed*iseed;
minlen=MathMax(minlen,1);
s.m_x0.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))));
k++;
s.m_x1.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))));
k++;
s.m_x2.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))));
k++;
s.m_x3.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))));
k++;
s.m_ix0.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))));
k++;
s.m_ix1.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))));
k++;
ArrayResize(s.m_bx0,minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))));
k++;
ArrayResize(s.m_bx1,minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))));
k++;
s.m_a0.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))),minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k+1))));
k+=2;
s.m_a1.Resize(minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k))),minlen+(int)MathRound(maxpad*CMath::Sqr(MathSin(k+1))));
k+=2;
s.m_v0=0;
k=PseudoRandomInit1(s.m_x0,k);
k=PseudoRandomInit1(s.m_x1,k);
k=PseudoRandomInit1(s.m_x2,k);
k=PseudoRandomInit1(s.m_x3,k);
k=PseudoRandomInit1I(s.m_ix0,k);
k=PseudoRandomInit1I(s.m_ix1,k);
k=PseudoRandomInit1B(s.m_bx0,k);
k=PseudoRandomInit1B(s.m_bx1,k);
k=PseudoRandomInit2(s.m_a0,k);
k=PseudoRandomInit2(s.m_a1,k);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CTestAblasFUnit::ComparePlaygrounds(CAblasFPlayground &s0,
CAblasFPlayground &s1)
{
double result=0;
result=MathMax(result,MathAbs(s0.m_v0-s1.m_v0)/rmx3(MathAbs(s0.m_v0),MathAbs(s1.m_v0),1));
result=MathMax(result,rcmp1(s0.m_x0,s1.m_x0));
result=MathMax(result,rcmp1(s0.m_x1,s1.m_x1));
result=MathMax(result,icmp1(s0.m_ix0,s1.m_ix0));
result=MathMax(result,icmp1(s0.m_ix1,s1.m_ix1));
result=MathMax(result,bcmp1(s0.m_bx0,s1.m_bx0));
result=MathMax(result,bcmp1(s0.m_bx1,s1.m_bx1));
result=MathMax(result,rcmp1(s0.m_x2,s1.m_x2));
result=MathMax(result,rcmp1(s0.m_x3,s1.m_x3));
result=MathMax(result,rcmp2(s0.m_a0,s1.m_a0));
result=MathMax(result,rcmp2(s0.m_a1,s1.m_a1));
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int CTestAblasFUnit::PseudoRandomInit1(CRowDouble &x,int iseed)
{
for(int i=0; i<CAp::Len(x); i++)
{
x.Set(i,MathSin(iseed+MathSin(i)));
iseed++;
}
//--- return result
return(iseed);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int CTestAblasFUnit::PseudoRandomInit1I(CRowInt &x,int iseed)
{
for(int i=0; i<CAp::Len(x); i++)
{
x.Set(i,(int)MathRound(100*MathSin(iseed+MathSin(i))));
iseed++;
}
//--- return result
return(iseed);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int CTestAblasFUnit::PseudoRandomInit1B(bool &x[],int iseed)
{
for(int i=0; i<CAp::Len(x); i++)
{
x[i]=(double)(MathSin(iseed+MathSin(i)))>0.0;
iseed++;
}
//--- return result
return(iseed);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int CTestAblasFUnit::PseudoRandomInit2(CMatrixDouble &x,int iseed)
{
//--- create variables
int i=0;
int j=0;
CRowDouble xr;
CRowDouble xc;
xr=vector<double>::Zeros(CAp::Rows(x));
for(i=0; i<CAp::Rows(x); i++)
xr.Set(i,MathSin(13+2*i+iseed));
iseed++;
xc=vector<double>::Zeros(CAp::Cols(x));
for(j=0; j<CAp::Cols(x); j++)
xc.Set(j,MathSin(17+3*j+iseed));
iseed++;
for(i=0; i<CAp::Rows(x); i++)
{
for(j=0; j<CAp::Cols(x); j++)
{
x.Set(i,j,xr[i]+xc[j]);
while(x.Get(i,j)>1.0)
x.Add(i,j,- 1);
while(x.Get(i,j)<(-1.0))
x.Add(i,j,1);
}
}
//--- return result
return(iseed);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CTestAblasFUnit::rmx3(double r0,double r1,double r2)
{
double result=r0;
if(r1>result)
result=r1;
if(r2>result)
result=r2;
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CTestAblasFUnit::rcmp1(CRowDouble &x,CRowDouble &y)
{
double result=0;
int i=0;
double mx=0;
//--- check
if(!CAp::Assert(CAp::Len(x)==CAp::Len(y),"rcmp1: sizes do not match"))
return(EMPTY_VALUE);
result=0;
mx=1;
for(i=0; i<CAp::Len(x); i++)
{
if(CInfOrNaN::IsNaN(x[i]) && CInfOrNaN::IsNaN(y[i]))
continue;
result=MathMax(result,MathAbs(x[i]-y[i]));
mx=MathMax(mx,MathAbs(x[i]));
mx=MathMax(mx,MathAbs(y[i]));
}
result=result/mx;
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CTestAblasFUnit::icmp1(CRowInt &x,CRowInt &y)
{
double result=0;
//--- check
if(!CAp::Assert(CAp::Len(x)==CAp::Len(y),"rcmp1: sizes do not match"))
return(result);
for(int i=0; i<CAp::Len(x) ; i++)
result=MathMax(result,MathAbs(x[i]-y[i]));
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CTestAblasFUnit::bcmp1(bool &x[],bool &y[])
{
double result=0;
//--- check
if(!CAp::Assert(CAp::Len(x)==CAp::Len(y),"rcmp1: sizes do not match"))
return(result);
for(int i=0; i<CAp::Len(x); i++)
{
if((x[i] && !y[i]) || (y[i] && !x[i]))
result=1;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
double CTestAblasFUnit::rcmp2(CMatrixDouble &x,CMatrixDouble &y)
{
//--- create variables
double result=0;
double mx=0;
//--- check
if(!CAp::Assert(CAp::Rows(x)==CAp::Rows(y),"rcmp2: rows do not match"))
return(result);
//--- check
if(!CAp::Assert(CAp::Cols(x)==CAp::Cols(y),"rcmp2: cols do not match"))
return(result);
mx=1;
for(int i=0; i<CAp::Rows(x); i++)
{
for(int j=0; j<CAp::Cols(x); j++)
{
result=MathMax(result,MathAbs(x.Get(i,j)-y.Get(i,j)));
mx=MathMax(mx,MathAbs(x.Get(i,j)));
mx=MathMax(mx,MathAbs(y.Get(i,j)));
}
}
result=result/mx;
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRMulAddV(int n,CRowDouble &y,
CRowDouble &z,CRowDouble &x)
{
for(int i=0; i<n; i++)
x.Add(i,y[i]*z[i]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRNegMulAddV(int n,CRowDouble &y,
CRowDouble &z,CRowDouble &x)
{
for(int i=0; i<n; i++)
x.Add(i,- y[i]*z[i]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRCopyMulAddV(int n,CRowDouble &y,CRowDouble &z,
CRowDouble &x,CRowDouble &r)
{
for(int i=0; i<n; i++)
r.Set(i,x[i]+y[i]*z[i]);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefRCopyNegMulAddV(int n,CRowDouble &y,
CRowDouble &z,
CRowDouble &x,
CRowDouble &r)
{
for(int i=0; i<n; i++)
r.Set(i,x[i]-y[i]*z[i]);
}
//+------------------------------------------------------------------+
//| Reference code |
//+------------------------------------------------------------------+
void CTestAblasFUnit::RefGerX(int m,int n,CMatrixDouble &a,int ia,
int ja,double alpha,
CRowDouble &u,int iu,
CRowDouble &v,int iv)
{
double s=0;
if((m<=0 || n<=0) || alpha==0.0)
return;
for(int i=0; i<m; i++)
{
s=alpha*u[iu+i];
for(int j=0; j<n; j++)
a.Add(ia+i,ja+j,s*v[iv+j]);
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestPolynomialSolverUnit
{
public:
static bool TestPolynomialSolver(bool silent);
};
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
bool CTestPolynomialSolverUnit::TestPolynomialSolver(bool silent)
{
//--- create variables
bool result;
bool waserrors=false;
CRowDouble a;
CRowComplex x;
double eps=0;
int n=0;
CPolynomialSolverReport rep;
//--- Basic tests
eps=1.0E-6;
n=1;
a=vector<double>::Zeros(n+1);
a.Set(0,2);
a.Set(1,3);
CPolynomialSolver::PolynomialSolve(a,n,x,rep);
CAp::SetErrorFlag(waserrors,(double)(MathAbs(x[0].real+2.0/3.0))>eps,"testpolynomialsolverunit.ap:33");
CAp::SetErrorFlag(waserrors,(double)(x[0].imag)!=0.0,"testpolynomialsolverunit.ap:34");
CAp::SetErrorFlag(waserrors,(double)(rep.m_maxerr)>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:35");
n=2;
a=vector<double>::Zeros(n+1);
a.Set(0,1);
a.Set(1,-2);
a.Set(2,1);
CPolynomialSolver::PolynomialSolve(a,n,x,rep);
CAp::SetErrorFlag(waserrors,CMath::AbsComplex(x[0]-1)>eps,"testpolynomialsolverunit.ap:43");
CAp::SetErrorFlag(waserrors,CMath::AbsComplex(x[1]-1)>eps,"testpolynomialsolverunit.ap:44");
CAp::SetErrorFlag(waserrors,rep.m_maxerr>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:45");
n=2;
a=vector<double>::Zeros(n+1);
a.Set(0,2);
a.Set(1,-3);
a.Set(2,1);
CPolynomialSolver::PolynomialSolve(a,n,x,rep);
if(x[0].real<x[1].real)
{
CAp::SetErrorFlag(waserrors,MathAbs(x[0].real-1)>eps,"testpolynomialsolverunit.ap:55");
CAp::SetErrorFlag(waserrors,MathAbs(x[1].real-2)>eps,"testpolynomialsolverunit.ap:56");
}
else
{
CAp::SetErrorFlag(waserrors,MathAbs(x[0].real-2)>eps,"testpolynomialsolverunit.ap:60");
CAp::SetErrorFlag(waserrors,MathAbs(x[1].real-1)>eps,"testpolynomialsolverunit.ap:61");
}
CAp::SetErrorFlag(waserrors,x[0].imag!=0.0,"testpolynomialsolverunit.ap:63");
CAp::SetErrorFlag(waserrors,x[1].imag!=0.0,"testpolynomialsolverunit.ap:64");
CAp::SetErrorFlag(waserrors,rep.m_maxerr>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:65");
n=2;
a=vector<double>::Zeros(n+1);
a.Set(0,1);
a.Set(1,0);
a.Set(2,1);
CPolynomialSolver::PolynomialSolve(a,n,x,rep);
CAp::SetErrorFlag(waserrors,CMath::AbsComplex(x[0]*x[0]+1)>eps,"testpolynomialsolverunit.ap:73");
CAp::SetErrorFlag(waserrors,rep.m_maxerr>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:74");
n=4;
a=vector<double>::Zeros(n+1);
a.Set(4,1);
CPolynomialSolver::PolynomialSolve(a,n,x,rep);
CAp::SetErrorFlag(waserrors,x[0]!=0,"testpolynomialsolverunit.ap:84");
CAp::SetErrorFlag(waserrors,x[1]!=0,"testpolynomialsolverunit.ap:85");
CAp::SetErrorFlag(waserrors,x[2]!=0,"testpolynomialsolverunit.ap:86");
CAp::SetErrorFlag(waserrors,x[3]!=0,"testpolynomialsolverunit.ap:87");
CAp::SetErrorFlag(waserrors,rep.m_maxerr>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:88");
n=2;
a=vector<double>::Zeros(n+1);
a.Set(1,3);
a.Set(2,2);
CPolynomialSolver::PolynomialSolve(a,n,x,rep);
if(x[0].real>x[1].real)
{
CAp::SetErrorFlag(waserrors,x[0]!=0,"testpolynomialsolverunit.ap:98");
CAp::SetErrorFlag(waserrors,MathAbs(x[1].real+3.0/2.0)>eps,"testpolynomialsolverunit.ap:99");
CAp::SetErrorFlag(waserrors,x[1].imag!=0.0,"testpolynomialsolverunit.ap:100");
}
else
{
CAp::SetErrorFlag(waserrors,x[1]!=0,"testpolynomialsolverunit.ap:104");
CAp::SetErrorFlag(waserrors,MathAbs(x[0].real+3.0/2.0)>eps,"testpolynomialsolverunit.ap:105");
CAp::SetErrorFlag(waserrors,x[0].imag!=0.0,"testpolynomialsolverunit.ap:106");
}
CAp::SetErrorFlag(waserrors,rep.m_maxerr>(100.0*CMath::m_machineepsilon),"testpolynomialsolverunit.ap:108");
if(!silent)
PrintResult("TESTING POLYNOMIAL SOLVER",!waserrors);
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestDirectSparseSolversUnit
{
public:
static bool TestDirectSparseSolvers(bool silent);
private:
static void TestSKS(bool &errorflag);
static void TestCholesky(bool &errorflag);
static void TestGen(bool &errorflag);
};
//+------------------------------------------------------------------+
//| Test |
//+------------------------------------------------------------------+
bool CTestDirectSparseSolversUnit::TestDirectSparseSolvers(bool silent)
{
//--- create variables
bool result;
bool rskserrors;
bool rcholerrors;
bool rgenerrors;
bool waserrors;
//--- initialization
rskserrors=false;
rcholerrors=false;
rgenerrors=false;
TestSKS(rskserrors);
TestCholesky(rcholerrors);
TestGen(rgenerrors);
waserrors=(rskserrors||rcholerrors)||rgenerrors;
if(!silent)
{
Print("TESTING DIRECT SPARSE SOLVERS:");
PrintResult("* SPD-SKS (real)",!rskserrors);
PrintResult("* SPD-CRS (real)",!rcholerrors);
PrintResult("* GENERAL (real)",!rgenerrors);
PrintResult("TEST SUMMARY",!waserrors);
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| SPD SKS test |
//+------------------------------------------------------------------+
void CTestDirectSparseSolversUnit::TestSKS(bool &errorflag)
{
//--- create variables
int pass=0;
int passcount=0;
int maxn=0;
double threshold=0;
int bw=0;
bool isupper;
int n=0;
int i=0;
int j=0;
CMatrixDouble a;
CRowDouble xe;
CRowDouble b;
CRowDouble xs;
CSparseMatrix sa;
CSparseMatrix sa2;
CSparseSolverReport rep;
CHighQualityRandState rs;
//--- initialization
passcount=10;
maxn=30;
threshold=1.0E-6;
CHighQualityRand::HQRndRandomize(rs);
//--- Well conditioned SPD problems solved with SKS solver
for(n=1; n<=maxn; n++)
{
for(pass=1; pass<=passcount; pass++)
{
//--- 1. generate random well conditioned matrix A.
//--- 2. generate random solution vector xe
//--- 3. generate right part b=A*xe
isupper=CHighQualityRand::HQRndNormal(rs)>0.5;
bw=CHighQualityRand::HQRndUniformI(rs,n+1);
a=matrix<double>::Zeros(n,n);
CSparse::SparseCreate(n,n,0,sa);
for(i=0; i<n; i++)
{
a.Set(i,i,1+CHighQualityRand::HQRndUniformR(rs));
CSparse::SparseSet(sa,i,i,a.Get(i,i));
for(j=i+1; j<=MathMin(i+bw,n-1); j++)
{
a.Set(i,j,(CHighQualityRand::HQRndUniformR(rs)-0.5)/(double)n);
a.Set(j,i,a.Get(i,j));
if(isupper)
CSparse::SparseSet(sa,i,j,a.Get(i,j));
else
CSparse::SparseSet(sa,j,i,a.Get(i,j));
}
}
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
if((j<i && isupper) || (j>i && !isupper))
if(CHighQualityRand::HQRndUniformR(rs)<0.25)
CSparse::SparseSet(sa,i,j,CHighQualityRand::HQRndNormal(rs));
}
CSparse::SparseConvertTo(sa,CHighQualityRand::HQRndUniformI(rs,3));
xe.Resize(n);
for(i=0; i<n; i++)
xe.Set(i,CHighQualityRand::HQRndNormal(rs));
b=vector<double>::Zeros(n);
CAblas::RMatrixGemVect(n,n,1.0,a,0,0,0,xe,0,0.0,b,0);
//--- Test SKS solver
CSparse::SparseCopyToSKS(sa,sa2);
CDirectSparseSolvers::SparseSPDSolveSKS(sa2,isupper,b,xs,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testdirectsparsesolversunit.ap:88");
CAp::SetErrorFlag(errorflag,CAp::Len(xs)!=n,"testdirectsparsesolversunit.ap:89");
if(errorflag)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,MathAbs(xe[i]-xs[i])>threshold,"testdirectsparsesolversunit.ap:93");
//--- Test solver #2
CSparse::SparseCopyToSKS(sa,sa2);
if(!CTrFac::SparseCholeskySkyLine(sa2,n,isupper))
{
CAp::SetErrorFlag(errorflag,true,"testdirectsparsesolversunit.ap:102");
return;
}
CDirectSparseSolvers::SparseSPDCholeskySolve(sa2,isupper,b,xs,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testdirectsparsesolversunit.ap:107");
CAp::SetErrorFlag(errorflag,CAp::Len(xs)!=n,"testdirectsparsesolversunit.ap:108");
if(errorflag)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,MathAbs(xe[i]-xs[i])>threshold,"testdirectsparsesolversunit.ap:112");
}
}
}
//+------------------------------------------------------------------+
//| SPD SKS test |
//+------------------------------------------------------------------+
void CTestDirectSparseSolversUnit::TestCholesky(bool &errorflag)
{
//--- create variables
int pass=0;
int passcount=0;
int maxn=0;
double threshold=0;
int bw=0;
bool isupper;
int n=0;
int i=0;
int j=0;
CMatrixDouble a;
CRowDouble xe;
CRowDouble b;
CRowDouble xs;
CSparseMatrix sa;
CSparseMatrix sa2;
CSparseSolverReport rep;
CHighQualityRandState rs;
//--- initialization
passcount=10;
maxn=30;
threshold=1.0E-6;
CHighQualityRand::HQRndRandomize(rs);
//--- Well conditioned SPD problems solved with Cholesky solver
for(n=1; n<=maxn; n++)
{
for(pass=1; pass<=passcount; pass++)
{
//--- 1. generate random well conditioned matrix A.
//--- 2. generate random solution vector xe
//--- 3. generate right part b=A*xe
isupper=CHighQualityRand::HQRndNormal(rs)>0.5;
bw=CHighQualityRand::HQRndUniformI(rs,n+1);
a=matrix<double>::Zeros(n,n);
CSparse::SparseCreate(n,n,0,sa);
for(i=0; i<n; i++)
{
a.Set(i,i,1+CHighQualityRand::HQRndUniformR(rs));
CSparse::SparseSet(sa,i,i,a.Get(i,i));
for(j=i+1; j<=MathMin(i+bw,n-1); j++)
{
a.Set(i,j,(CHighQualityRand::HQRndUniformR(rs)-0.5)/(double)n);
a.Set(j,i,a.Get(i,j));
if(isupper)
CSparse::SparseSet(sa,i,j,a.Get(i,j));
else
CSparse::SparseSet(sa,j,i,a.Get(i,j));
}
}
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
if((j<i && isupper) || (j>i && !isupper))
if(CHighQualityRand::HQRndUniformR(rs)<0.25)
CSparse::SparseSet(sa,i,j,CHighQualityRand::HQRndNormal(rs));
}
CSparse::SparseConvertTo(sa,CHighQualityRand::HQRndUniformI(rs,3));
xe=vector<double>::Zeros(n);
for(i=0; i<n; i++)
xe.Set(i,CHighQualityRand::HQRndNormal(rs));
b=vector<double>::Zeros(n);
CAblas::RMatrixGemVect(n,n,1.0,a,0,0,0,xe,0,0.0,b,0);
//--- Test CRS/AMD solver
CSparse::SparseCopy(sa,sa2);
CDirectSparseSolvers::SparseSPDSolve(sa2,isupper,b,xs,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testdirectsparsesolversunit.ap:196");
CAp::SetErrorFlag(errorflag,CAp::Len(xs)!=n,"testdirectsparsesolversunit.ap:197");
if(errorflag)
return;
for(i=0; i<n; i++)
if(CAp::SetErrorFlag(errorflag,MathAbs(xe[i]-xs[i])>threshold,"testdirectsparsesolversunit.ap:201"))
return;
}
}
}
//+------------------------------------------------------------------+
//| General linear test |
//+------------------------------------------------------------------+
void CTestDirectSparseSolversUnit::TestGen(bool &errorflag)
{
//--- create variables
int pass=0;
int passcount=0;
int maxn=0;
double threshold=0;
int n=0;
int noffdiag=0;
int i=0;
int j=0;
int k=0;
CMatrixDouble a;
CRowDouble xe;
CRowDouble b;
CRowDouble xs;
CRowDouble xs2;
CRowInt pivp;
CRowInt pivq;
CSparseMatrix sa;
CSparseMatrix sa2;
CSparseSolverReport rep;
CHighQualityRandState rs;
//--- initialization
passcount=10;
maxn=30;
threshold=1.0E-6;
CHighQualityRand::HQRndRandomize(rs);
//--- Well conditioned general linear problems solved with LU solver
for(n=1; n<=maxn; n++)
{
for(pass=1; pass<=passcount; pass++)
{
//--- Select number of off-diagonal entries, we want to try matrices
//--- from dense to sparse
noffdiag=n*(n-1);
while(true)
{
//--- 1. generate random well conditioned matrix A.
//--- 2. apply row/col permutation
//--- 3. generate random solution vector xe
//--- 4. generate right part b=A*xe
a=matrix<double>::Zeros(n,n);
for(k=0; k<noffdiag; k++)
{
i=CHighQualityRand::HQRndUniformI(rs,n);
j=CHighQualityRand::HQRndUniformI(rs,n);
a.Set(i,j,0.01*CHighQualityRand::HQRndNormal(rs)/(double)n);
}
for(i=0; i<n; i++)
a.Set(i,i,1.0+MathPow(2,CHighQualityRand::HQRndNormal(rs)));
for(i=0; i<n; i++)
CApServ::SwapRows(a,i,i+CHighQualityRand::HQRndUniformI(rs,n-i),n);
for(i=0; i<n; i++)
CApServ::SwapCols(a,i,i+CHighQualityRand::HQRndUniformI(rs,n-i),n);
CSparse::SparseCreate(n,n,0,sa);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
CSparse::SparseSet(sa,i,j,a.Get(i,j));
CSparse::SparseConvertTo(sa,CHighQualityRand::HQRndUniformI(rs,3));
xe=vector<double>::Zeros(n);
for(i=0; i<n; i++)
xe.Set(i,CHighQualityRand::HQRndNormal(rs));
b=vector<double>::Zeros(n);
CAblas::RMatrixGemVect(n,n,1.0,a,0,0,0,xe,0,0.0,b,0);
//--- Test solver #1
CDirectSparseSolvers::SparseSolve(sa,b,xs,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testdirectsparsesolversunit.ap:285");
CAp::SetErrorFlag(errorflag,CAp::Len(xs)!=n,"testdirectsparsesolversunit.ap:286");
if(errorflag)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,MathAbs(xe[i]-xs[i])>threshold,"testdirectsparsesolversunit.ap:290");
//--- Test solver #2
CSparse::SparseCopyToCRS(sa,sa2);
CTrFac::SparseLU(sa2,0,pivp,pivq);
CDirectSparseSolvers::SparseLUSolve(sa2,pivp,pivq,b,xs2,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testdirectsparsesolversunit.ap:300");
CAp::SetErrorFlag(errorflag,CAp::Len(xs2)!=n,"testdirectsparsesolversunit.ap:301");
if(errorflag)
return;
for(i=0; i<n; i++)
if(CAp::SetErrorFlag(errorflag,MathAbs(xe[i]-xs2[i])>threshold,"testdirectsparsesolversunit.ap:305"))
return;
//--- Update fill factor
if(noffdiag==0)
break;
noffdiag=noffdiag/2;
}
}
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestIterativeSparseUnit
{
public:
static bool TestIterativeSparse(bool silent);
private:
static void TestGMRES(int maxn,bool &err);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestIterativeSparseUnit::TestIterativeSparse(bool silent)
{
//--- create variables
bool result;
int maxn=50;
bool gmreserrors;
bool waserrors;
//--- Prepare error flags
gmreserrors=false;
//--- Run tests
TestGMRES(maxn,gmreserrors);
//--- report
waserrors=gmreserrors;
if(!silent)
{
Print("TESTING ITERATIVE SPARSE SOLVERS:");
PrintResult("* GMRES",!gmreserrors);
//--- Were errors?
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| Test GMRES solver. |
//| on failure sets error flag, on success does not touch it |
//+------------------------------------------------------------------+
void CTestIterativeSparseUnit::TestGMRES(int maxn,bool &err)
{
//--- create variables
CSparseMatrix a;
CSparseMatrix crsa;
CRowDouble xref;
CRowDouble x;
CRowDouble b;
CRowDouble b2;
CRowDouble ax;
CRowDouble xr;
CRowDouble x0;
CRowDouble repfirst;
CRowDouble replast;
CRowDouble tmp;
CMatrixDouble z;
int nnz=0;
int i=0;
int j=0;
int k=0;
int n=0;
int gmresk=0;
CSparseSolverState solver;
CHighQualityRandStateShell rs;
double epsf=0;
bool isupper;
CSparseSolverReport rep;
double v=0;
double tol=0;
double rprev=0;
int requesttype=0;
int nmv=0;
int nreports=0;
CAlglib::HQRndRandomize(rs);
epsf=MathPow(10,-4-CAlglib::HQRndUniformI(rs,3));
//--- Test that GMRES is a Krylov subspace method, i.e. its iterate belongs to span(b,A*b,A*A*b,...)
for(n=1; n<=maxn; n++)
{
//--- Generate sparse matrix and RHS.
CSparse::SparseCreate(n,n,0,a);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
CSparse::SparseSet(a,i,j,CAlglib::HQRndNormal(rs));
}
CSparse::SparseCopyToCRS(a,crsa);
CAlglib::HQRndNormalV(rs,n,xref);
//--- Test using nonsymmetric solve
for(gmresk=1; gmresk<MathMin(6,n); gmresk++)
{
CSparse::SparseMV(crsa,xref,b);
CIterativeSparse::SparseSolverCreate(n,solver);
CIterativeSparse::SparseSolverSetAlgoGMRES(solver,gmresk);
CIterativeSparse::SparseSolverSetCond(solver,0,gmresk);
CIterativeSparse::SparseSolverSolve(solver,a,b);
CIterativeSparse::SparseSolverResults(solver,x,rep);
CAp::SetErrorFlag(err,CAp::Len(x)!=n,"testiterativesparseunit.ap:107");
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testiterativesparseunit.ap:108");
if(err)
return;
//--- Test that the solution belongs to the Krylov subspace
z=matrix<double>::Zeros(gmresk+1,n);
CAblasF::RCopyAllocV(n,b,xr);
for(k=0; k<gmresk; k++)
{
CAblasF::RCopyVR(n,xr,z,k);
CSparse::SparseMV(crsa,xr,ax);
CAblasF::RCopyV(n,ax,xr);
}
CAblasF::RCopyVR(n,x,z,gmresk);
COrtFac::RMatrixLQ(z,gmresk+1,n,tmp);
CAp::SetErrorFlag(err,MathAbs(z.Get(gmresk,gmresk))>1.0E-6,"testiterativesparseunit.ap:125");
//--- Additional test - R2 field is correct
CSparse::SparseMV(crsa,x,ax);
CAblasF::RAddV(n,-1.0,b,ax);
if(CAp::SetErrorFlag(err,MathAbs(rep.m_r2-CAblasF::RDotV2(n,ax))>1.0E-6,"testiterativesparseunit.ap:132"))
return;
}
}
//--- Randomly generated sparse problem, possibly degenerate.
//--- Iterate until convergence.
//--- Try various sparsity patterns.
for(n=1; n<=maxn; n++)
{
nnz=n*n;
while(true)
{
//--- Generate sparse matrix and RHS.
//--- The matrix is regularized by placing large elements on its diagonal
CSparse::SparseCreate(n,n,0,a);
for(k=0; k<nnz; k++)
CSparse::SparseSet(a,CAlglib::HQRndUniformI(rs,n),CAlglib::HQRndUniformI(rs,n),CAlglib::HQRndNormal(rs));
for(i=0; i<n; i++)
CSparse::SparseSet(a,i,i,(2*CAlglib::HQRndUniformI(rs,2)-1)*(1+3*((double)nnz/(double)n)+CMath::Sqr(CAlglib::HQRndNormal(rs))));
k=CAlglib::HQRndUniformI(rs,3);
if(k==1)
CSparse::SparseConvertToCRS(a);
if(k==2)
CSparse::SparseConvertToSKS(a);
CSparse::SparseCopyToCRS(a,crsa);
CAlglib::HQRndNormalV(rs,n,xref);
//--- Symmetric solve:
//--- * solver object API
//--- * easy access function
//--- We do not test out-of-core API because it is used internally by the
//--- SparseSolve/SparseSolveSymmetric pair of functions.
gmresk=5+CAlglib::HQRndUniformI(rs,2*n+1);
isupper=CAlglib::HQRndNormal(rs)>0.0;
CSparse::SparseSMV(crsa,isupper,xref,b);
CIterativeSparse::SparseSolverCreate(n,solver);
CIterativeSparse::SparseSolverSetAlgoGMRES(solver,gmresk);
CIterativeSparse::SparseSolverSetCond(solver,epsf,0);
CIterativeSparse::SparseSolverSolveSymmetric(solver,a,isupper,b);
CIterativeSparse::SparseSolverResults(solver,x,rep);
CAp::SetErrorFlag(err,CAp::Len(x)!=n,"testiterativesparseunit.ap:180");
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:181");
if(err)
{
int r=CSparse::SparseGetNRows(a);
int c=CSparse::SparseGetNCols(a);
matrix m=matrix<double>::Zeros(r,c);
CRowDouble sr;
for(int row=0; row<r; row++)
{
CAlglib::SparseGetRow(a,row,sr);
m.Row(sr.ToVector()+0,row);
}
Print(m);
Print(xref.ToVector());
Print(isupper);
Print(gmresk);
return;
}
CSparse::SparseSMV(crsa,isupper,x,b2);
v=0;
for(i=0; i<n; i++)
v+=CMath::Sqr(b[i]-b2[i]);
v=MathSqrt(v)/CApServ::Coalesce(MathSqrt(CAblasF::RDotV2(n,b)),1.0);
CAp::SetErrorFlag(err,v>(1.1*epsf),"testiterativesparseunit.ap:189");
CIterativeSparse::SparseSolveSymmetricGMRES(a,isupper,b,gmresk,epsf,0,x,rep);
CAp::SetErrorFlag(err,CAp::Len(x)!=n,"testiterativesparseunit.ap:193");
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:194");
if(err)
return;
CSparse::SparseSMV(crsa,isupper,x,b2);
v=0;
for(i=0; i<n; i++)
v+=CMath::Sqr(b[i]-b2[i]);
v=MathSqrt(v)/CApServ::Coalesce(MathSqrt(CAblasF::RDotV2(n,b)),1.0);
CAp::SetErrorFlag(err,v>(1.1*epsf),"testiterativesparseunit.ap:202");
//--- Nonsymmetric solve
gmresk=5+CAlglib::HQRndUniformI(rs,2*n+1);
CSparse::SparseMV(crsa,xref,b);
CIterativeSparse::SparseSolverCreate(n,solver);
CIterativeSparse::SparseSolverSetAlgoGMRES(solver,gmresk);
CIterativeSparse::SparseSolverSetCond(solver,epsf,0);
CIterativeSparse::SparseSolverSolve(solver,a,b);
CIterativeSparse::SparseSolverResults(solver,x,rep);
CAp::SetErrorFlag(err,CAp::Len(x)!=n,"testiterativesparseunit.ap:215");
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:216");
if(err)
return;
CSparse::SparseMV(crsa,x,b2);
v=0;
for(i=0; i<n; i++)
v+=CMath::Sqr(b[i]-b2[i]);
v=MathSqrt(v)/CApServ::Coalesce(MathSqrt(CAblasF::RDotV2(n,b)),1.0);
CAp::SetErrorFlag(err,v>(1.1*epsf),"testiterativesparseunit.ap:224");
CIterativeSparse::SparseSolveGMRES(a,b,gmresk,epsf,0,x,rep);
CAp::SetErrorFlag(err,CAp::Len(x)!=n,"testiterativesparseunit.ap:228");
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:229");
if(err)
return;
CSparse::SparseMV(crsa,x,b2);
v=0;
for(i=0; i<n; i++)
v+=CMath::Sqr(b[i]-b2[i]);
v=MathSqrt(v)/CApServ::Coalesce(MathSqrt(CAblasF::RDotV2(n,b)),1.0);
CAp::SetErrorFlag(err,v>(1.1*epsf),"testiterativesparseunit.ap:237");
//--- Increase sparsity
if(nnz==0)
break;
nnz/=2;
}
}
//--- Check that initial point is actually used and improves the situation:
//--- * generate big random problem
//--- * solve it with GMRES(1), EpsF=0.1
//--- * restart using previous solution as initial point
//--- Restarted solution must be achieved in 1 iteration
//--- To be exact, we perform three tests:
//--- * initial solution needs more than 1 iteration
//--- * attempt to solve second time without starting point again needs more than 1 iteration
//--- * solution with starting point needs 1 iteration
n=100;
nnz=10*n;
epsf=0.001;
gmresk=10;
CSparse::SparseCreate(n,n,0,a);
for(k=0; k<nnz; k++)
CSparse::SparseSet(a,CAlglib::HQRndUniformI(rs,n),CAlglib::HQRndUniformI(rs,n),0.1*CAlglib::HQRndNormal(rs));
for(k=0; k<n; k++)
CSparse::SparseSet(a,k,k,(2*CAlglib::HQRndUniformI(rs,2)-1)*((double)nnz/(double)n+CMath::Sqr(CAlglib::HQRndNormal(rs))));
CSparse::SparseCopyToCRS(a,crsa);
CAlglib::HQRndNormalV(rs,n,xref);
CSparse::SparseMV(crsa,xref,b);
CIterativeSparse::SparseSolverCreate(n,solver);
CIterativeSparse::SparseSolverSetAlgoGMRES(solver,gmresk);
CIterativeSparse::SparseSolverSetCond(solver,epsf,0);
CIterativeSparse::SparseSolverSolve(solver,a,b);
CIterativeSparse::SparseSolverResults(solver,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:278");
CAp::SetErrorFlag(err,rep.m_iterationscount<=1,"testiterativesparseunit.ap:279");
if(err)
return;
CIterativeSparse::SparseSolverSolve(solver,a,b);
CIterativeSparse::SparseSolverResults(solver,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:285");
CAp::SetErrorFlag(err,rep.m_iterationscount<=1,"testiterativesparseunit.ap:286");
if(err)
return;
CIterativeSparse::SparseSolverSetStartingPoint(solver,x);
CIterativeSparse::SparseSolverSolve(solver,a,b);
CIterativeSparse::SparseSolverResults(solver,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:293");
CAp::SetErrorFlag(err,rep.m_iterationscount>1,"testiterativesparseunit.ap:294");
if(err)
return;
//--- Check termination request handling by the out-of-core API.
//--- Solve long-running problem, at each OOC call randomly decide whether
//--- we want to terminate the process or not.
n=100;
nnz=10*n;
epsf=1.0E-50;
gmresk=1;
CSparse::SparseCreate(n,n,0,a);
for(k=0; k<nnz; k++)
CSparse::SparseSet(a,CAlglib::HQRndUniformI(rs,n),CAlglib::HQRndUniformI(rs,n),0.1*CAlglib::HQRndNormal(rs));
for(k=0; k<n; k++)
CSparse::SparseSet(a,k,k,1+CMath::Sqr(CAlglib::HQRndNormal(rs)));
CSparse::SparseCopyToCRS(a,crsa);
CAlglib::HQRndNormalV(rs,n,xref);
CSparse::SparseMV(crsa,xref,b);
CIterativeSparse::SparseSolverCreate(n,solver);
CIterativeSparse::SparseSolverSetAlgoGMRES(solver,gmresk);
CIterativeSparse::SparseSolverSetCond(solver,epsf,0);
CIterativeSparse::SparseSolverOOCStart(solver,b);
while(CIterativeSparse::SparseSolverOOCContinue(solver))
{
if(!CAp::Assert(solver.m_requesttype==0,"SparseSolverSolve: integrity check 7372 failed"))
return;
CSparse::SparseMV(crsa,solver.m_x,solver.m_ax);
if(CAlglib::HQRndNormal(rs)<0.0)
CIterativeSparse::SparseSolverRequestTermination(solver);
}
CIterativeSparse::SparseSolverOOCStop(solver,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype!=8,"testiterativesparseunit.ap:329");
//--- Check OOC API, check reports:
//--- * OOC API works correctly
//--- * solver reports a sequence of decreased residuals
//--- * first report is equal to the initial point, last report is equal to the final point
//--- * when XRep is activated, it does not crash SparseSolve() or SparseSolveSymmetric()
n=100;
nnz=10*n;
epsf=1.0E-3;
gmresk=5;
tol=MathSqrt(CMath::m_machineepsilon);
CSparse::SparseCreate(n,n,0,a);
for(k=0; k<nnz; k++)
CSparse::SparseSet(a,CAlglib::HQRndUniformI(rs,n),CAlglib::HQRndUniformI(rs,n),0.1*CAlglib::HQRndNormal(rs));
for(k=0; k<n; k++)
CSparse::SparseSet(a,k,k,(double)nnz/(double)n+CMath::Sqr(CAlglib::HQRndNormal(rs)));
CSparse::SparseCopyToCRS(a,crsa);
CAlglib::HQRndNormalV(rs,n,xref);
CAlglib::HQRndNormalV(rs,n,x0);
CSparse::SparseMV(crsa,xref,b);
CIterativeSparse::SparseSolverCreate(n,solver);
CIterativeSparse::SparseSolverSetAlgoGMRES(solver,gmresk);
CIterativeSparse::SparseSolverSetCond(solver,epsf,0);
CIterativeSparse::SparseSolverSetXRep(solver,true);
CIterativeSparse::SparseSolverSetStartingPoint(solver,x0);
CIterativeSparse::SparseSolverOOCStart(solver,b);
rprev=CMath::m_maxrealnumber;
nmv=0;
nreports=0;
while(CIterativeSparse::SparseSolverOOCContinue(solver))
{
CIterativeSparse::SparseSolverOOCGetRequestInfo(solver,requesttype);
if(requesttype==-1)
{
CIterativeSparse::SparseSolverOOCGetRequestData(solver,xr);
CIterativeSparse::SparseSolverOOCGetRequestData1(solver,v);
if(CAp::Len(repfirst)==0)
CAblasF::RCopyAllocV(n,xr,repfirst);
CAblasF::RCopyAllocV(n,xr,replast);
CSparse::SparseMV(crsa,xr,ax);
CAblasF::RAddV(n,-1.0,b,ax);
CAp::SetErrorFlag(err,MathAbs(CAblasF::RDotV2(n,ax)-v)>(tol*(1+CAblasF::RDotV2(n,ax))),"testiterativesparseunit.ap:376");
CAp::SetErrorFlag(err,v>(rprev+tol),"testiterativesparseunit.ap:377");
rprev=v;
nreports++;
continue;
}
//--- check
if(!CAp::Assert(requesttype==0,"SparseSolverSolve: integrity check 5364 failed"))
return;
CIterativeSparse::SparseSolverOOCGetRequestData(solver,xr);
CSparse::SparseMV(crsa,xr,ax);
nmv++;
CIterativeSparse::SparseSolverOOCSendResult(solver,ax);
}
CIterativeSparse::SparseSolverOOCStop(solver,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testiterativesparseunit.ap:389");
CAp::SetErrorFlag(err,rep.m_nmv!=nmv,"testiterativesparseunit.ap:390");
CAp::SetErrorFlag(err,rep.m_iterationscount<(nreports-2)*gmresk,"testiterativesparseunit.ap:391");
CAp::SetErrorFlag(err,CAp::Len(repfirst)!=n,"testiterativesparseunit.ap:392");
CAp::SetErrorFlag(err,CAp::Len(replast)!=n,"testiterativesparseunit.ap:393");
if(err)
return;
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(err,MathAbs(repfirst[i]-x0[i])>(100.0*CMath::m_machineepsilon),"testiterativesparseunit.ap:398");
CAp::SetErrorFlag(err,MathAbs(replast[i]-x[i])>(double)(100*CMath::m_machineepsilon),"testiterativesparseunit.ap:399");
}
CIterativeSparse::SparseSolverSolve(solver,a,b);
CIterativeSparse::SparseSolverResults(solver,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:403");
CIterativeSparse::SparseSolverSolveSymmetric(solver,a,CAlglib::HQRndNormal(rs)>0.0,b);
CIterativeSparse::SparseSolverResults(solver,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype!=1,"testiterativesparseunit.ap:406");
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestLinCGUnit
{
public:
static const double m_e0;
static const double m_maxcond;
static bool TestLinCG(bool silent);
private:
static bool ComplexTest(bool silent);
static bool ComplexRes(bool silent);
static bool BasicTestX(bool silent);
static bool TestRCorrectness(bool silent);
static bool BasicTestIters(bool silent);
static bool KrylovSubspaceTest(bool silent);
static bool SparseTest(bool silent);
static bool PrecondTest(bool silent);
static void GramShmidtOrtnorm(CMatrixDouble &a,int n,int k,double eps,CMatrixDouble &b,int &k2);
static bool FromBasis(CRowDouble &x,CMatrixDouble &basis,int n,int k,double eps);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
const double CTestLinCGUnit::m_e0=1.0E-6;
const double CTestLinCGUnit::m_maxcond=30;
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestLinCGUnit::TestLinCG(bool silent)
{
//--- create variables
bool result;
bool basictestxerrors;
bool basictestiterserr;
bool complexreserrors;
bool complexerrors;
bool rcorrectness;
bool krylovsubspaceerr;
bool sparseerrors;
bool preconderrors;
bool waserrors;
//--- Tests
basictestxerrors=BasicTestX(silent);
basictestiterserr=BasicTestIters(silent);
complexreserrors=ComplexRes(silent);
complexerrors=ComplexTest(silent);
rcorrectness=TestRCorrectness(silent);
krylovsubspaceerr=KrylovSubspaceTest(silent);
sparseerrors=SparseTest(silent);
preconderrors=PrecondTest(silent);
//--- report
waserrors=((((((basictestxerrors||complexreserrors)||complexerrors)||rcorrectness)||basictestiterserr)||krylovsubspaceerr)||sparseerrors)||preconderrors;
if(!silent)
{
Print("TESTING LinCG");
PrintResult("BasicTestX",!basictestxerrors);
PrintResult("BasicTestIters",!basictestiterserr);
PrintResult("ComplexResTest",!complexreserrors);
PrintResult("ComplexTest",!complexerrors);
PrintResult("R2 correctness",!rcorrectness);
PrintResult("KrylovSubSpaceTest",!krylovsubspaceerr);
PrintResult("SparseTest",!sparseerrors);
PrintResult("PrecondTest",!preconderrors);
//--- was errors?
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing LinCGIteration function(custom option), |
//| which to solve Ax=b (here A is random positive definite matrix |
//| NxN, b is random vector). It uses the default stopping criterion |
//| (RNorm<FEps=10^-6). If algorithm does more iterations than size |
//| of the problem, then some errors are possible. |
//| The test verifies the following propirties: |
//| 1. (A*pk,pm)=0 for any m<>k; |
//| 2. (rk,rm)=0 for any m<>k; |
//| 3. (rk,pm)=0 for any m<>k; |
//| INPUT: |
//| Silent - if true then function output report |
//+------------------------------------------------------------------+
bool CTestLinCGUnit::ComplexTest(bool silent)
{
//--- create variables
bool result=false;
double mx=100;
int n=5;
CLinCGState state;
CLinCGReport rep;
CMatrixDouble a;
CRowDouble b;
double c=0;
CRowDouble x0;
CRowDouble residual;
double normofresidual=0;
double sclr=0;
double na=0;
double nv0=0;
double nv1=0;
int sz=0;
int i=0;
int j=0;
int k=0;
int l=0;
double tmp=0;
CMatrixDouble mtx;
CMatrixDouble mtp;
CMatrixDouble mtr;
double getrnorm=0;
int numofit=0;
for(sz=1; sz<=n; sz++)
{
//--- Generate:
//--- * random A with norm NA (equal to 1.0),
//--- * random right part B whose elements are uniformly distributed in [-MX,+MX]
//--- * random starting point X0 whose elements are uniformly distributed in [-MX,+MX]
c=15+15*CMath::RandomReal();
CMatGen::SPDMatrixRndCond(sz,c,a);
na=1;
b=vector<double>::Zeros(sz);
x0=vector<double>::Zeros(sz);
for(i=0; i<sz; i++)
{
b.Set(i,mx*(2*CMath::RandomReal()-1));
x0.Set(i,mx*(2*CMath::RandomReal()-1));
}
mtx=matrix<double>::Zeros(sz+1,sz);
//--- Start optimization, record its progress for further analysis
//--- NOTE: we set update frequency of R to 2 in order to test that R is updated correctly
CLinCG::LinCGCreate(sz,state);
CLinCG::LinCGSetXRep(state,true);
CLinCG::LinCGSetB(state,b);
CLinCG::LinCGSetStartingPoint(state,x0);
CLinCG::LinCGSetCond(state,0,sz);
CLinCG::LinCGSetRUpdateFreq(state,2);
numofit=0;
getrnorm=CMath::m_maxrealnumber;
while(CLinCG::LinCGIteration(state))
{
if(state.m_needmv)
{
for(i=0; i<sz; i++)
state.m_mv.Set(i,CAblasF::RDotVR(sz,state.m_x,a,i));
}
if(state.m_needvmv)
{
state.m_vmv=0;
for(i=0; i<sz; i++)
{
state.m_mv.Set(i,CAblasF::RDotVR(sz,state.m_x,a,i));
state.m_vmv+=state.m_mv[i]*state.m_x[i];
}
}
if(state.m_needprec)
state.m_pv=state.m_x;
if(state.m_xupdated)
{
//--- Save current point to MtX, it will be used later for additional tests
if(numofit>=CAp::Rows(mtx))
{
CAp::SetErrorFlag(result,true,"testlincgunit.ap:208");
return(result);
}
mtx.Row(numofit,state.m_x);
getrnorm=state.m_r2;
numofit++;
}
}
CLinCG::LinCGResult(state,x0,rep);
if(getrnorm!=rep.m_r2)
{
if(!silent)
{
PrintFormat("IterationsCount=%d;\nNMV=&d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype);
PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::GetRNorm<>Rep.R2!(%0.2E<>%0.2E)\n",sz,c,getrnorm,rep.m_r2);
}
CAp::SetErrorFlag(result,true,"testlincgunit.ap:227");
return(result);
}
//--- Calculate residual, check result
residual=vector<double>::Zeros(sz);
for(i=0; i<sz; i++)
{
tmp=0;
for(j=0; j<sz; j++)
{
tmp=tmp+a.Get(i,j)*x0[j];
}
residual.Set(i,b[i]-tmp);
}
normofresidual=0;
for(i=0; i<sz; i++)
{
if(MathAbs(residual[i])>m_e0)
{
if(!silent)
{
PrintFormat("IterationsCount=%d;\nNMV=%d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype);
PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::Discripancy[%d]>E0!(%0.2E>%0.2E)\n",sz,c,i,residual[i],m_e0);
}
CAp::SetErrorFlag(result,true,"testlincgunit.ap:254");
return(result);
}
normofresidual+=residual[i]*residual[i];
}
if(MathAbs(normofresidual-rep.m_r2)>m_e0)
{
if(!silent)
{
PrintFormat("IterationsCount=%d;\nNMV=%d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype);
PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::||NormOfResidual-Rep.R2||>E0!(%0.2E>%0.2E)\n",sz,c,MathAbs(normofresidual - rep.m_r2),m_e0);
PrintFormat("NormOfResidual=%0.2E; Rep.R2=%0.2E\n",normofresidual,rep.m_r2);
}
CAp::SetErrorFlag(result,true,"testlincgunit.ap:270");
return(result);
}
//--- Check algorithm properties (conjugacy/orthogonality).
//--- Here we use MtX which was filled during algorithm progress towards solution.
//--- NOTE: this test is skipped when algorithm converged in less than SZ iterations.
if(sz>1 && rep.m_iterationscount==sz)
{
mtp=matrix<double>::Zeros(sz,sz);
mtr=matrix<double>::Zeros(sz,sz);
for(i=0; i<sz; i++)
for(j=0; j<sz; j++)
{
mtp.Set(i,j,mtx.Get(i+1,j)-mtx.Get(i,j));
tmp=CAblasF::RDotRR(sz,a,j,mtx,i);
mtr.Set(i,j,b[j]-tmp);
}
//--- (Api,pj)=0?
sclr=0;
nv0=0;
nv1=0;
for(i=0; i<sz; i++)
{
for(j=0; j<sz; j++)
{
if(i==j)
continue;
for(k=0; k<sz; k++)
{
tmp=CAblasF::RDotRR(sz,a,k,mtp,i);
sclr+=tmp*mtp.Get(j,k);
nv0+=mtp.Get(i,k)*mtp.Get(i,k);
nv1+=mtp.Get(j,k)*mtp.Get(j,k);
}
nv0=MathSqrt(nv0);
nv1=MathSqrt(nv1);
if(MathAbs(sclr)>(m_e0*na*MathMax(nv0,1)*MathMax(nv1,1)))
{
if(!silent)
{
PrintFormat("IterationsCount=%d;\nNMV=%d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype);
PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::(Ap%d,p%d)!=0\n{{Sclr=%0.15f; NA=%0.15f NV0=%0.15f NV1=%0.15f;}}\n",sz,c,i,j,sclr,na,nv0,nv1);
}
CAp::SetErrorFlag(result,true,"testlincgunit.ap:327");
return(result);
}
}
}
//--- (ri,pj)=0?
for(i=1; i<sz; i++)
{
for(j=0; j<i; j++)
{
sclr=0;
nv0=0;
nv1=0;
for(k=0; k<sz; k++)
{
sclr+=mtr.Get(i,k)*mtp.Get(j,k);
nv0+=mtr.Get(i,k)*mtr.Get(i,k);
nv1=nv1+mtp.Get(j,k)*mtp.Get(j,k);
}
nv0=MathSqrt(nv0);
nv1=MathSqrt(nv1);
if(MathAbs(sclr)>(m_e0*MathMax(nv0,1)*MathMax(nv1,1)))
{
if(!silent)
{
PrintFormat("IterationsCount=%d;\nNMV=%d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype);
PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::(r%d,p%d)!=0\n{{Sclr=%0.15f; NV0=%0.15f NV1=%0.15f;}}\n",sz,c,i,j,sclr,nv0,nv1);
}
CAp::SetErrorFlag(result,true,"testlincgunit.ap:360");
return(result);
}
}
}
//--- (ri,rj)=0?
for(i=0; i<sz; i++)
{
for(j=i+1; j<sz; j++)
{
sclr=0;
nv0=0;
nv1=0;
for(k=0; k<sz; k++)
{
sclr+=mtr.Get(i,k)*mtr.Get(j,k);
nv0+=mtr.Get(i,k)*mtr.Get(i,k);
nv1+=mtr.Get(j,k)*mtr.Get(j,k);
}
nv0=MathSqrt(nv0);
nv1=MathSqrt(nv1);
if(MathAbs(sclr)>(m_e0*MathMax(nv0,1)*MathMax(nv1,1)))
{
if(!silent)
{
PrintFormat("IterationsCount=%d;\nNMV=%d;\nTerminationType=%d;\n",rep.m_iterationscount,rep.m_nmv,rep.m_terminationtype);
PrintFormat("Size=%d;\nCond=%0.5f;\nComplexTest::Fail::(rm,rk)!=0\n{{Sclr=%0.15f; NV0=%0.15f NV1=%0.15f;}}\n",sz,c,sclr,nv0,nv1);
}
CAp::SetErrorFlag(result,true,"testlincgunit.ap:392");
return(result);
}
}
}
}
}
if(!silent)
PrintResult("ComplexTest",true);
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| This function prepare problem with a known solution 'Xs' |
//| (A*Xs-b=0). There b is A*Xs. After, function check algorithm |
//| result and 'Xs'. |
//| There used two stopping criterions: |
//| 1. achieved the required precision(StCrit=0); |
//| 2. execution of the required number of iterations(StCrit=1). |
//+------------------------------------------------------------------+
bool CTestLinCGUnit::ComplexRes(bool silent)
{
//--- create variables
int sz=5;
double mx=100;
int nxp=100;
CLinCGState s;
CLinCGReport rep;
CMatrixDouble a;
CRowDouble b;
CRowDouble xs;
CRowDouble x0;
double err=0;
int n=0;
double c=0;
int i=0;
int j=0;
int stcrit=0;
double tmp=0;
double eps=0;
int xp=0;
for(xp=0; xp<nxp; xp++)
{
for(n=1; n<=sz; n++)
{
for(stcrit=0; stcrit<=1; stcrit++)
{
//--- Generate:
//--- * random A with norm NA (equal to 1.0),
//--- * random solution XS whose elements are uniformly distributed in [-MX,+MX]
//--- * random starting point X0 whose elements are uniformly distributed in [-MX,+MX]
//--- * B = A*Xs
c=(m_maxcond-1)*CMath::RandomReal()+1;
CMatGen::SPDMatrixRndCond(n,c,a);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xs=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,mx*(2*CMath::RandomReal()-1));
xs.Set(i,mx*(2*CMath::RandomReal()-1));
}
eps=0;
for(i=0; i<n; i++)
{
b.Set(i,CAblasF::RDotVR(n,xs,a,i));
eps+=b[i]*b[i];
}
eps=1.0E-6*MathSqrt(eps);
//--- Solve with different stopping criteria
CLinCG::LinCGCreate(n,s);
CLinCG::LinCGSetB(s,b);
CLinCG::LinCGSetStartingPoint(s,x0);
CLinCG::LinCGSetXRep(s,true);
if(stcrit==0)
CLinCG::LinCGSetCond(s,1.0E-6,0);
else
CLinCG::LinCGSetCond(s,0,n);
//---
while(CLinCG::LinCGIteration(s))
{
if(s.m_needmv)
{
for(i=0; i<n; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
}
if(s.m_needvmv)
{
s.m_vmv=0;
for(i=0; i<n; i++)
{
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
s.m_vmv+=s.m_mv[i]*s.m_x[i];
}
}
if(s.m_needprec)
s.m_pv=s.m_x;
}
CLinCG::LinCGResult(s,x0,rep);
//--- Check result
err=0.0;
for(i=0; i<n; i++)
{
tmp=CAblasF::RDotVR(n,s.m_x,a,i);
err+=CMath::Sqr(b[i]-tmp);
}
err=MathSqrt(err);
if(err>eps)
{
if(!silent)
{
PrintResult("ComplexRes",false);
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
PrintFormat("NMV=%d\n",rep.m_nmv);
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
Print("X and Xs...\n");
for(j=0; j<n; j++)
PrintFormat("x[%d]=%0.10f; xs[%d]=%0.10f\n",j,x0[j],j,xs[j]);
}
return(true);
}
}
}
}
//--- test has been passed
if(!silent)
PrintResult("ComplexRes",true);
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| This function check, that XUpdated return State.X=X0 at zero |
//| iteration and State.X=X(algorithm result) at last. |
//+------------------------------------------------------------------+
bool CTestLinCGUnit::BasicTestX(bool silent)
{
//--- create variables
int sz=5;
double mx=100;
CLinCGState s;
CLinCGReport rep;
CMatrixDouble a;
CRowDouble b;
CRowDouble x0;
CRowDouble x00;
CRowDouble x01;
int n=0;
double c=0;
int i=0;
int j=0;
int iters=0;
for(n=1; n<=sz; n++)
{
//--- Generate:
//--- * random A with norm NA (equal to 1.0),
//--- * random right part B whose elements are uniformly distributed in [-MX,+MX]
//--- * random starting point X0 whose elements are uniformly distributed in [-MX,+MX]
c=(m_maxcond-1)*CMath::RandomReal()+1;
CMatGen::SPDMatrixRndCond(n,c,a);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
x00=vector<double>::Zeros(n);
x01=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,mx*(2*CMath::RandomReal()-1));
b.Set(i,mx*(2*CMath::RandomReal()-1));
}
//--- Solve, save first and last reported points to x00 and x01
CLinCG::LinCGCreate(n,s);
CLinCG::LinCGSetB(s,b);
CLinCG::LinCGSetStartingPoint(s,x0);
CLinCG::LinCGSetXRep(s,true);
CLinCG::LinCGSetCond(s,0,n);
iters=0;
//---
while(CLinCG::LinCGIteration(s))
{
if(s.m_needmv)
{
for(i=0; i<n; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
}
if(s.m_needvmv)
{
s.m_vmv=0;
for(i=0; i<n; i++)
{
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
s.m_vmv+=s.m_mv[i]*s.m_x[i];
}
}
if(s.m_needprec)
s.m_pv=s.m_x;
if(s.m_xupdated)
{
if(iters==0)
x00=s.m_x;
if(iters==n)
x01=s.m_x;
iters++;
}
}
//--- Check first and last points
for(i=0; i<n; i++)
{
if(x00[i]!=x0[i])
{
if(!silent)
{
PrintResult("BasicTestX",false);
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
PrintFormat("NMV=%d\n",rep.m_nmv);
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
for(j=0; j<n; j++)
PrintFormat("x0=%0.5f; x00=%0.5f;\n",x0[j],x00[j]);
}
return(true);
}
}
CLinCG::LinCGResult(s,x0,rep);
for(i=0; i<n; i++)
{
if(x01[i]!=x0[i])
{
if(!silent)
{
PrintResult("BasicTestX",false);
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
PrintFormat("NMV=%d\n",rep.m_nmv);
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
for(j=0; j<n; j++)
PrintFormat("x0=%0.5f; x01=%0.5f;\n",x0[j],x01[j]);
}
return(true);
}
}
}
//--- test has been passed
if(!silent)
PrintResult("BasicTestIters",true);
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| This function checks that XUpdated returns correct State.R2. It |
//| creates large badly conditioned problem (N=50), which should be |
//| large enough and ill-conditioned enough to cause periodic |
//| recalculation of R. |
//+------------------------------------------------------------------+
bool CTestLinCGUnit::TestRCorrectness(bool silent)
{
//--- create variables
double rtol=1.0E6*CMath::m_machineepsilon;
int n=50;
int maxits=n/2;
double c=10000;
CLinCGState s;
CLinCGReport rep;
CMatrixDouble a;
CRowDouble b;
int i=0;
int j=0;
double r2=0;
double v=0;
//--- initialization
CMatGen::SPDMatrixRndCond(n,c,a);
b=vector<double>::Zeros(n);
for(i=0; i<n; i++)
b.Set(i,2*CMath::RandomReal()-1);
CLinCG::LinCGCreate(n,s);
CLinCG::LinCGSetB(s,b);
CLinCG::LinCGSetXRep(s,true);
CLinCG::LinCGSetCond(s,0,maxits);
while(CLinCG::LinCGIteration(s))
{
if(s.m_needmv)
{
for(i=0; i<n; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
}
if(s.m_needvmv)
{
s.m_vmv=0;
for(i=0; i<n; i++)
{
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
s.m_vmv+=s.m_mv[i]*s.m_x[i];
}
}
if(s.m_needprec)
s.m_pv=s.m_x;
if(s.m_xupdated)
{
//--- calculate R2, compare with value returned in state.R2
r2=0;
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,s.m_x,a,i);
r2=r2+CMath::Sqr(v-b[i]);
}
if(MathAbs(r2-s.m_r2)>rtol)
return(true);
}
}
CLinCG::LinCGResult(s,b,rep);
if(rep.m_iterationscount!=maxits)
return(true);
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| This function check, that number of iterations are't more than |
//| MaxIts. |
//+------------------------------------------------------------------+
bool CTestLinCGUnit::BasicTestIters(bool silent)
{
//--- create variables
int sz=5;
double mx=100;
CLinCGState s;
CLinCGReport rep;
CMatrixDouble a;
CRowDouble b;
CRowDouble x0;
int n=0;
double c=0;
int i=0;
int j=0;
int iters=0;
for(n=1; n<=sz; n++)
{
//--- Generate:
//--- * random A with norm NA (equal to 1.0),
//--- * random right part B whose elements are uniformly distributed in [-MX,+MX]
//--- * random starting point X0 whose elements are uniformly distributed in [-MX,+MX]
c=(m_maxcond-1)*CMath::RandomReal()+1;
CMatGen::SPDMatrixRndCond(n,c,a);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,mx*(2*CMath::RandomReal()-1));
b.Set(i,mx*(2*CMath::RandomReal()-1));
}
//--- Solve
CLinCG::LinCGCreate(n,s);
CLinCG::LinCGSetB(s,b);
CLinCG::LinCGSetStartingPoint(s,x0);
CLinCG::LinCGSetXRep(s,true);
CLinCG::LinCGSetCond(s,0,n);
iters=0;
//---
while(CLinCG::LinCGIteration(s))
{
if(s.m_needmv)
{
for(i=0; i<n; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
}
if(s.m_needvmv)
{
s.m_vmv=0;
for(i=0; i<n; i++)
{
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
s.m_vmv+=s.m_mv[i]*s.m_x[i];
}
}
if(s.m_needprec)
s.m_pv=s.m_x;
if(s.m_xupdated)
iters++;
}
CLinCG::LinCGResult(s,x0,rep);
//--- Check
if(iters!=rep.m_iterationscount+1 || iters>n+1)
{
if(!silent)
{
PrintResult("BasicTestIters",false);
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
PrintFormat("NMV=%d\n",rep.m_nmv);
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
PrintFormat("Iters=%d\n",iters);
}
return(true);
}
//--- Restart problem
c=(m_maxcond-1)*CMath::RandomReal()+1;
CMatGen::SPDMatrixRndCond(n,c,a);
for(i=0; i<n; i++)
{
x0.Set(i,mx*(2*CMath::RandomReal()-1));
b.Set(i,mx*(2*CMath::RandomReal()-1));
}
CLinCG::LinCGSetStartingPoint(s,x0);
CLinCG::LinCGRestart(s);
CLinCG::LinCGSetB(s,b);
iters=0;
//---
while(CLinCG::LinCGIteration(s))
{
if(s.m_needmv)
{
for(i=0; i<n; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
}
if(s.m_needvmv)
{
s.m_vmv=0;
for(i=0; i<n; i++)
{
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
s.m_vmv+=s.m_mv[i]*s.m_x[i];
}
}
if(s.m_needprec)
s.m_pv=s.m_x;
if(s.m_xupdated)
iters++;
}
CLinCG::LinCGResult(s,x0,rep);
//--- check
if(iters!=rep.m_iterationscount+1 || iters>n+1)
{
if(!silent)
{
PrintResult("BasicTestIters",false);
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
PrintFormat("NMV=%d\n",rep.m_nmv);
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
PrintFormat("Iters=%d\n",iters);
}
return(true);
}
}
//--- test has been passed
if(!silent)
PrintResult("BasicTestIters",true);
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| This function check, that programmed method is Krylov subspace |
//| methed. |
//+------------------------------------------------------------------+
bool CTestLinCGUnit::KrylovSubspaceTest(bool silent)
{
//--- create variables
bool result=false;
double eps=1.0E-6;
int maxits=3;
int sz=5;
double mx=100;
CLinCGState s;
CLinCGReport rep;
CMatrixDouble a;
CRowDouble b;
CRowDouble x0;
CMatrixDouble ksr;
CRowDouble r0;
CRowDouble tarray;
CMatrixDouble mtx;
int n=0;
double c=0;
int i=0;
int j=0;
int l=0;
int m=0;
double tmp=0;
double normr0=0;
int numofit=0;
int k2=0;
//--- initialization
for(n=1; n<=sz; n++)
{
//--- Generate:
//--- * random A with unit norm
//--- * cond(A) in [0.5*MaxCond, 1.0*MaxCond]
//--- * random x0 and b such that |A*x0-b| is large enough for algorithm to make at least one iteration.
//--- IMPORTANT: it is very important to have cond(A) both (1) not very large and
//--- (2) not very small. Large cond(A) will make our problem ill-conditioned,
//--- thus analytic properties won't hold. Small cond(A), from the other side,
//--- will give us rapid convergence of the algorithm - in fact, too rapid.
//--- Krylov basis will be dominated by numerical noise and test may fail.
c=m_maxcond*(0.5*CMath::RandomReal()+0.5);
CMatGen::SPDMatrixRndCond(n,c,a);
mtx=matrix<double>::Zeros(n+1,n);
ksr=matrix<double>::Zeros(n,n);
r0=vector<double>::Zeros(n);
tarray=vector<double>::Zeros(n);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
//---
do
{
for(i=0; i<n; i++)
{
x0.Set(i,mx*(2*CMath::RandomReal()-1));
b.Set(i,mx*(2*CMath::RandomReal()-1));
}
normr0=0;
for(i=0; i<n; i++)
{
tmp=CAblasF::RDotVR(n,x0,a,i);
r0.Set(i,b[i]-tmp);
normr0+=r0[i]*r0[i];
}
}
while(MathSqrt(normr0)<=eps);
//--- Fill KSR by {r0, A*r0, A^2*r0, ... }
for(i=0; i<n; i++)
{
ksr.Row(i,r0);
for(j=0; j<i; j++)
{
for(l=0; l<n; l++)
tarray.Set(l,CAblasF::RDotRR(n,a,l,ksr,i));
ksr.Row(i,tarray);
}
}
//--- Solve system, record intermediate points for futher analysis.
//--- NOTE: we set update frequency of R to 2 in order to test that R is updated correctly
CLinCG::LinCGCreate(n,s);
CLinCG::LinCGSetB(s,b);
CLinCG::LinCGSetStartingPoint(s,x0);
CLinCG::LinCGSetXRep(s,true);
CLinCG::LinCGSetCond(s,0,n);
CLinCG::LinCGSetRUpdateFreq(s,2);
numofit=0;
//---
while(CLinCG::LinCGIteration(s))
{
if(s.m_needmv)
{
for(i=0; i<n; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
}
if(s.m_needvmv)
{
s.m_vmv=0;
for(i=0; i<n; i++)
{
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
s.m_vmv+=s.m_mv[i]*s.m_x[i];
}
}
if(s.m_needprec)
s.m_pv=s.m_x;
if(s.m_xupdated)
{
mtx.Row(numofit,s.m_x);
numofit++;
}
}
//--- Check that I-th step S_i=X[I+1]-X[i] belongs to I-th Krylov subspace.
//--- Checks are done for first K2 steps, with K2 small enough to avoid
//--- numerical errors.
if(n<=maxits)
k2=n;
else
k2=maxits;
//---
for(i=0; i<k2; i++)
{
for(j=0; j<n; j++)
tarray.Set(j,mtx.Get(i+1,j)-mtx.Get(i,j));
if(!FromBasis(tarray,ksr,n,i+1,m_e0))
{
if(!silent)
{
PrintResult("KrylovSubspaceTest",false);
PrintFormat("Size=%d; Iters=%d;\n",n,i);
}
CAp::SetErrorFlag(result,true,"testlincgunit.ap:1129");
return(result);
}
}
}
if(!silent)
PrintResult("KrylovSubspaceTest",true);
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing LinCgSolveSparse. This function prepare |
//| problem with a known solution 'Xs'(A * Xs - b = 0). There b is |
//| A * Xs. After, function calculate result by LinCGSolveSparse and |
//| compares it with 'Xs'. |
//+------------------------------------------------------------------+
bool CTestLinCGUnit::SparseTest(bool silent)
{
//--- create variables
int sz=5;
double mx=100;
CLinCGState s;
CLinCGReport rep;
CMatrixDouble a;
CRowDouble b;
CRowDouble xs;
CRowDouble x0;
CRowDouble x1;
CSparseMatrix uppera;
CSparseMatrix lowera;
//--- initialization
double err=0;
int n=0;
double c=0;
int i=0;
int j=0;
double eps=0;
for(n=1; n<=sz; n++)
{
//--- Generate:
//--- * random A with unit norm
//--- * random X0 (starting point) and XS (known solution)
//--- Copy dense A to sparse SA
c=(m_maxcond-1)*CMath::RandomReal()+1;
CMatGen::SPDMatrixRndCond(n,c,a);
b=vector<double>::Zeros(n);
xs=vector<double>::Zeros(n);
for(i=0; i<n; i++)
xs.Set(i,mx*(2*CMath::RandomReal()-1));
eps=0;
for(i=0; i<n; i++)
{
b.Set(i,CAblasF::RDotVR(n,xs,a,i));
eps+=b[i]*b[i];
}
eps=1.0E-6*MathSqrt(eps);
CSparse::SparseCreate(n,n,0,uppera);
CSparse::SparseCreate(n,n,0,lowera);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
if(j>=i)
CSparse::SparseSet(uppera,i,j,a.Get(i,j));
if(j<=i)
CSparse::SparseSet(lowera,i,j,a.Get(i,j));
}
}
CSparse::SparseConvertToCRS(uppera);
CSparse::SparseConvertToCRS(lowera);
//--- Test upper triangle
CLinCG::LinCGCreate(n,s);
CLinCG::LinCGSetCond(s,0,n);
CLinCG::LinCGSolveSparse(s,uppera,true,b);
CLinCG::LinCGResult(s,x0,rep);
err=0;
for(i=0; i<n; i++)
err+=CMath::Sqr(x0[i]-xs[i]);
err=MathSqrt(err);
if(err>eps)
return(true);
//--- Test lower triangle
CLinCG::LinCGCreate(n,s);
CLinCG::LinCGSetCond(s,0,n);
CLinCG::LinCGSolveSparse(s,lowera,false,b);
CLinCG::LinCGResult(s,x1,rep);
err=0;
for(i=0; i<n; i++)
err+=CMath::Sqr(x1[i]-xs[i]);
err=MathSqrt(err);
if(err>eps)
return(true);
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| Function for testing the preconditioned conjugate gradient method|
//+------------------------------------------------------------------+
bool CTestLinCGUnit::PrecondTest(bool silent)
{
//--- create variables
bool result;
CLinCGState s;
CLinCGReport rep;
CMatrixDouble a;
CMatrixDouble ta;
CSparseMatrix sa;
CRowDouble m;
CMatrixDouble mtx;
CMatrixDouble mtprex;
CRowDouble de;
CRowDouble rde;
CRowDouble b;
CRowDouble tb;
CRowDouble d;
CRowDouble xe;
CRowDouble x0;
CRowDouble tx0;
CRowDouble err;
//--- initialization
int n=0;
int sz=5;
int numofit=0;
double c=0;
int i=0;
int j=0;
int k=0;
double eps=0;
bool bflag;
//--- Test 1.
//--- Preconditioned CG for A*x=b with preconditioner M=E*E' is algebraically
//--- equivalent to non-preconditioned CG for (inv(E)*A*inv(E'))*z = inv(E)*b
//--- with z=E'*x.
//--- We test it by generating random preconditioner, running algorithm twice -
//--- one time for original problem with preconditioner , another one for
//--- modified problem without preconditioner.
for(n=1; n<=sz; n++)
{
//--- Generate:
//--- * random A with unit norm
//--- * random positive definite diagonal preconditioner M
//--- * dE=sqrt(M)
//--- * rdE=dE^(-1)
//--- * tA = rdE*A*rdE
//--- * random x0 and b - for original preconditioned problem
//--- * tx0 and tb - for modified problem
c=(m_maxcond-1)*CMath::RandomReal()+1;
CMatGen::SPDMatrixRndCond(n,c,a);
ta=matrix<double>::Zeros(n,n);
mtx=matrix<double>::Zeros(n+1,n);
mtprex=matrix<double>::Zeros(n+1,n);
m=vector<double>::Zeros(n);
de=vector<double>::Zeros(n);
rde=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
m.Set(i,CMath::RandomReal()+0.5);
de.Set(i,MathSqrt(m[i]));
rde.Set(i,1/de[i]);
}
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
ta.Set(i,j,rde[i]*a.Get(i,j)*rde[j]);
}
b=vector<double>::Zeros(n);
tb=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
tx0=vector<double>::Zeros(n);
err=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
b.Set(i,2*CMath::RandomReal()-1);
}
eps=1.0E-5;
tx0=de*x0+0;
tb=rde*b+0;
//--- Solve two problems, intermediate points are saved to MtX and MtPreX
CLinCG::LinCGCreate(n,s);
CLinCG::LinCGSetB(s,b);
CLinCG::LinCGSetStartingPoint(s,x0);
CLinCG::LinCGSetXRep(s,true);
CLinCG::LinCGSetCond(s,0,n);
numofit=0;
//---
while(CLinCG::LinCGIteration(s))
{
if(s.m_needmv)
{
for(i=0; i<n; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
}
if(s.m_needvmv)
{
s.m_vmv=0;
for(i=0; i<n; i++)
{
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
s.m_vmv+=s.m_mv[i]*s.m_x[i];
}
}
if(s.m_needprec)
s.m_pv=s.m_x/m+0;
if(s.m_xupdated)
{
if(numofit>=CAp::Rows(mtx))
{
result=true;
return(result);
}
mtx.Row(numofit,s.m_x);
numofit=numofit+1;
}
}
CLinCG::LinCGSetStartingPoint(s,tx0);
CLinCG::LinCGSetB(s,tb);
CLinCG::LinCGRestart(s);
numofit=0;
//---
while(CLinCG::LinCGIteration(s))
{
if(s.m_needmv)
{
for(i=0; i<n; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,ta,i));
}
if(s.m_needvmv)
{
s.m_vmv=0;
for(i=0; i<n; i++)
{
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,ta,i));
s.m_vmv+=s.m_mv[i]*s.m_x[i];
}
}
if(s.m_needprec)
s.m_pv=s.m_x;
if(s.m_xupdated)
{
if(numofit>=CAp::Rows(mtprex))
{
result=true;
return(result);
}
mtprex.Row(numofit,s.m_x);
numofit=numofit+1;
}
}
//--- Compare results - sequence of points generated when solving original problem with
//--- points generated by modified problem.
for(i=0; i<numofit; i++)
{
for(j=0; j<n; j++)
{
if(MathAbs(mtx.Get(i,j)-rde[j]*mtprex.Get(i,j))>eps)
{
if(!silent)
{
PrintResult("PrecondTest",false);
PrintFormat("Size=%d\n",n);
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
PrintFormat("NMV=%d\n",rep.m_nmv);
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
Print("X and X^...\n");
for(k=0; k<n; k++)
PrintFormat("I=%d; mtx[%d]=%0.10f; mtx^[%d]=%0.10f\n",i,k,mtx.Get(i,k),k,mtprex.Get(i,k));
}
result=true;
return(result);
}
}
}
}
//--- Test 2.
//--- We test automatic diagonal preconditioning used by SolveSparse.
//--- In order to do so we:
//--- 1. generate 20*20 matrix A0 with condition number equal to 1.0E1
//--- 2. generate random "exact" solution xe and right part b=A0*xe
//--- 3. generate random ill-conditioned diagonal scaling matrix D with
//--- condition number equal to 1.0E50:
//--- 4. transform A*x=b into badly scaled problem:
//--- A0*x0=b0
//--- A0*D*(inv(D)*x0)=b0
//--- (D*A0*D)*(inv(D)*x0)=(D*b0)
//--- finally we got new problem A*x=b with A=D*A0*D, b=D*b0, x=inv(D)*x0
//--- Then we solve A*x=b:
//--- 1. with default preconditioner
//--- 2. with explicitly activayed diagonal preconditioning
//--- 3. with unit preconditioner.
//--- 1st and 2nd solutions must be close to xe, 3rd solution must be very
//--- far from the true one.
n=20;
CMatGen::SPDMatrixRndCond(n,1.0E1,ta);
xe=vector<double>::Zeros(n);
for(i=0; i<n; i++)
xe.Set(i,CApServ::RandomNormal());
b=vector<double>::Zeros(n);
d=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CAblasF::RDotVR(n,xe,ta,i));
d.Set(i,MathPow(10,100*CMath::RandomReal()-50));
}
a=matrix<double>::Zeros(n,n);
CSparse::SparseCreate(n,n,n*n,sa);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
a.Set(i,j,d[i]*ta.Get(i,j)*d[j]);
CSparse::SparseSet(sa,i,j,a.Get(i,j));
}
b.Mul(i,d[i]);
xe.Mul(i,1.0/d[i]);
}
CSparse::SparseConvertToCRS(sa);
CLinCG::LinCGCreate(n,s);
CLinCG::LinCGSetCond(s,0,2*n);
CLinCG::LinCGSolveSparse(s,sa,true,b);
CLinCG::LinCGResult(s,x0,rep);
if(rep.m_terminationtype<=0)
{
result=true;
return(result);
}
for(i=0; i<n; i++)
{
if(MathAbs(xe[i]-x0[i])>(5.0E-2/d[i]))
{
result=true;
return(result);
}
}
CLinCG::LinCGSetPrecUnit(s);
CLinCG::LinCGSolveSparse(s,sa,true,b);
CLinCG::LinCGResult(s,x0,rep);
if(rep.m_terminationtype>0)
{
bflag=false;
for(i=0; i<n; i++)
bflag=bflag||MathAbs(xe[i]-x0[i])>(5.0E-2/d[i]);
if(!bflag)
{
result=true;
return(result);
}
}
CLinCG::LinCGSetPrecDiag(s);
CLinCG::LinCGSolveSparse(s,sa,true,b);
CLinCG::LinCGResult(s,x0,rep);
if(rep.m_terminationtype<=0)
{
result=true;
return(result);
}
for(i=0; i<n; i++)
{
if(MathAbs(xe[i]-x0[i])>(5.0E-2/d[i]))
{
result=true;
return(result);
}
}
//--- test has been passed
if(!silent)
PrintResult("PrecondTest",true);
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| Orthogonalization by Gram - Shmidt method. |
//+------------------------------------------------------------------+
void CTestLinCGUnit::GramShmidtOrtnorm(CMatrixDouble &a,
int n,
int k,
double eps,
CMatrixDouble &b,
int &k2)
{
//--- create variables
double scaling=0;
double tmp=0;
double e=0;
int i=0;
int j=0;
int l=0;
int m=0;
double sc=0;
//--- initialization
k2=0;
k2=0;
scaling=0;
b=matrix<double>::Zeros(k,n);
for(i=0; i<k; i++)
{
tmp=CAblasF::RDotRR(n,a,i,a,i);
if(tmp>scaling)
scaling=tmp;
}
scaling=MathSqrt(scaling);
e=eps*scaling;
for(i=0; i<k; i++)
{
tmp=CAblasF::RDotRR(n,a,i,a,i);
b.Row(k2,a,i);
tmp=MathSqrt(tmp);
if(tmp<=e)
continue;
for(j=0; j<k2; j++)
{
sc=CAblasF::RDotRR(n,b,k2,b,j);
b.Row(k2,b[k2]-b[j]*sc);
}
tmp=CAblasF::RDotRR(n,b,k2,b,k2);
tmp=MathSqrt(tmp);
if(tmp<=e)
continue;
else
b.Row(k2,b[k2]/tmp);
k2++;
}
}
//+------------------------------------------------------------------+
//| Checks that a vector belongs to the basis. |
//+------------------------------------------------------------------+
bool CTestLinCGUnit::FromBasis(CRowDouble &x,CMatrixDouble &basis,
int n,int k,double eps)
{
//--- create variables
double normx=0;
CMatrixDouble ortnormbasis;
int k2=0;
int i=0;
int j=0;
double alpha=0;
CRowDouble alphas;
alphas=vector<double>::Zeros(k);
//--- calculating NORM for X
normx=CAblasF::RDotV2(n,x);
normx=MathSqrt(normx);
//--- Gram-Shmidt method
GramShmidtOrtnorm(basis,n,k,MathPow(CMath::m_machineepsilon,0.75),ortnormbasis,k2);
for(i=0; i<k2; i++)
{
alpha=CAblasF::RDotVR(n,x,ortnormbasis,i);
alphas.Set(i,alpha);
}
//--- check
for(i=0; i<n; i++)
{
alpha=0;
for(j=0; j<k2; j++)
alpha+=alphas[j]*ortnormbasis.Get(j,i);
if(MathAbs(x[i]-alpha)>(normx*eps))
return(false);
}
//--- return result
return(true);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestNormEstimatorUnit
{
public:
static bool TestNormEstimator(bool silent);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestNormEstimatorUnit::TestNormEstimator(bool silent)
{
//--- create variables
bool result;
double tol=0.01;
int maxmn=5;
bool waserrors=false;
int m=0;
int n=0;
int pass=0;
int passcount=0;
CMatrixDouble a;
CRowInt rowsizes;
CSparseMatrix s;
double snorm=0;
double enorm=0;
double enorm2=0;
int nbetter=0;
double sigma=0;
int i=0;
int j=0;
CNormEstimatorState e;
CNormEstimatorState e2;
//--- First test: algorithm must correctly determine matrix norm
for(m=1; m<=maxmn; m++)
{
for(n=1; n<=maxmn; n++)
{
//--- Create estimator with quite large NStart and NIts.
//--- It should guarantee that we converge to the correct solution.
CNormEstimator::NormEstimatorCreate(m,n,15,15,e);
//--- Try with zero A first
CSparse::SparseCreate(m,n,1,s);
CSparse::SparseConvertToCRS(s);
CNormEstimator::NormEstimatorEstimateSparse(e,s);
CNormEstimator::NormEstimatorResults(e,enorm);
waserrors=waserrors||enorm!=0.0;
//--- Choose random norm, try with non-zero matrix
//--- with specified norm.
snorm=MathExp(10*CMath::RandomReal()-5);
CSparse::SparseCreate(m,n,1,s);
if(m>=n)
{
//--- Generate random orthogonal M*M matrix,
//--- use N leading columns as columns of A
CMatGen::RMatrixRndOrthogonal(m,a);
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
CSparse::SparseSet(s,i,j,snorm*a.Get(i,j));
}
}
else
{
//--- Generate random orthogonal N*N matrix,
//--- use M leading rows as rows of A
//
CMatGen::RMatrixRndOrthogonal(n,a);
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
CSparse::SparseSet(s,i,j,snorm*a.Get(i,j));
}
}
CSparse::SparseConvertToCRS(s);
CNormEstimator::NormEstimatorEstimateSparse(e,s);
CNormEstimator::NormEstimatorResults(e,enorm);
waserrors=(waserrors||enorm>(snorm*(1+tol)))||enorm<(snorm*(1-tol));
}
}
//--- NStart=10 should give statistically better results than NStart=1.
//--- In order to test it we perform PassCount attempts to solve random
//--- problem by means of two estimators: one with NStart=10 and another
//--- one with NStart=1. Every time we compare two estimates and choose
//--- better one.
//--- Random variable NBetter is a number of cases when NStart=10 was better.
//--- Under null hypothesis (no difference) it is binomially distributed
//--- with mean PassCount/2 and variance PassCount/4. However, we expect
//--- to have significant deviation to the right, in the area of larger
//--- values.
//--- NOTE: we use fixed N because this test is independent of problem size.
n=3;
CNormEstimator::NormEstimatorCreate(n,n,1,1,e);
CNormEstimator::NormEstimatorCreate(n,n,10,1,e2);
CNormEstimator::NormEstimatorSetSeed(e,0);
CNormEstimator::NormEstimatorSetSeed(e2,0);
nbetter=0;
passcount=2000;
sigma=5.0;
for(pass=1; pass<=passcount; pass++)
{
snorm=MathPow(10.0,2*CMath::RandomReal()-1);
CSparse::SparseCreate(n,n,1,s);
CMatGen::RMatrixRndCond(n,2.0,a);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
CSparse::SparseSet(s,i,j,snorm*a.Get(i,j));
}
CSparse::SparseConvertToCRS(s);
CNormEstimator::NormEstimatorEstimateSparse(e,s);
CNormEstimator::NormEstimatorResults(e,enorm);
CNormEstimator::NormEstimatorEstimateSparse(e2,s);
CNormEstimator::NormEstimatorResults(e2,enorm2);
if(MathAbs(enorm2-snorm)<MathAbs(enorm-snorm))
nbetter++;
}
waserrors=waserrors||nbetter<(0.5*passcount+sigma*MathSqrt(0.25*passcount));
//--- Same as previous one (for NStart), but tests dependence on NIts.
n=3;
CNormEstimator::NormEstimatorCreate(n,n,1,1,e);
CNormEstimator::NormEstimatorCreate(n,n,1,10,e2);
CNormEstimator::NormEstimatorSetSeed(e,0);
CNormEstimator::NormEstimatorSetSeed(e2,0);
nbetter=0;
passcount=2000;
sigma=5.0;
for(pass=1; pass<=passcount; pass++)
{
snorm=MathPow(10.0,2*CMath::RandomReal()-1);
CSparse::SparseCreate(n,n,1,s);
CMatGen::RMatrixRndCond(n,2.0,a);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
CSparse::SparseSet(s,i,j,snorm*a.Get(i,j));
}
CSparse::SparseConvertToCRS(s);
CNormEstimator::NormEstimatorEstimateSparse(e,s);
CNormEstimator::NormEstimatorResults(e,enorm);
CNormEstimator::NormEstimatorEstimateSparse(e2,s);
CNormEstimator::NormEstimatorResults(e2,enorm2);
if(MathAbs(enorm2-snorm)<MathAbs(enorm-snorm))
nbetter++;
}
waserrors=waserrors||nbetter<(0.5*passcount+sigma*MathSqrt(0.25*passcount));
//--- report
if(!silent)
{
Print("TESTING NORM ESTIMATOR");
PrintResult("TEST",!waserrors);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestLinLSQRUnit
{
public:
static const double m_e0;
static const double m_tolort;
static const double m_emergencye0;
static bool TestLinLSQR(bool silent);
private:
static bool SVDTest(bool silent);
static bool MWCRankSVDTest(bool silent);
static bool MWICRankSVDTest(bool silent);
static bool BiDiagonalTest(bool silent);
static bool ZeroMatrixTest(bool silent);
static bool ReportCorrectnessTest(bool silent);
static bool StopPingCriteriaTest(bool silent);
static bool AnalyticTest(bool silent);
static void TestTerminationRequests(bool &err);
static bool IsItGoodSolution(CMatrixDouble &a,CRowDouble &b,int m,int n,double lambdav,CRowDouble &x,double epserr,double epsort);
static bool PreconditionerTest(void);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
const double CTestLinLSQRUnit::m_e0=1.0E-6;
const double CTestLinLSQRUnit::m_tolort=1.0E-4;
const double CTestLinLSQRUnit::m_emergencye0=1.0E-12;
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestLinLSQRUnit::TestLinLSQR(bool silent)
{
//--- create variables
bool result;
bool svdtesterrors;
bool mwcranksvderr;
bool mwicranksvderr;
bool bidiagonalerr;
bool zeromatrixerr;
bool reportcorrectnesserr;
bool stoppingcriteriaerr;
bool analytictesterrors;
bool prectesterrors;
bool termreqerrors;
bool waserrors;
//--- initialization
termreqerrors=false;
svdtesterrors=SVDTest(silent);
mwcranksvderr=MWCRankSVDTest(silent);
mwicranksvderr=MWICRankSVDTest(silent);
bidiagonalerr=BiDiagonalTest(silent);
zeromatrixerr=ZeroMatrixTest(silent);
reportcorrectnesserr=ReportCorrectnessTest(silent);
stoppingcriteriaerr=StopPingCriteriaTest(silent);
analytictesterrors=AnalyticTest(silent);
prectesterrors=PreconditionerTest();
TestTerminationRequests(termreqerrors);
//--- report
waserrors=(svdtesterrors||mwcranksvderr||mwicranksvderr||bidiagonalerr||zeromatrixerr||reportcorrectnesserr ||
stoppingcriteriaerr||analytictesterrors||prectesterrors||termreqerrors);
if(!silent)
{
Print("TESTING LinLSQR");
Print("Different matrix types:");
PrintResult("* general M*N",!svdtesterrors);
PrintResult("* well conditioned M*N",!mwcranksvderr);
PrintResult("* rank deficient M*N",!mwicranksvderr);
PrintResult("* sparse bidiagonal",!bidiagonalerr);
PrintResult("* zero",!zeromatrixerr);
Print("Other properties:");
PrintResult("* report correctness",!reportcorrectnesserr);
PrintResult("* stopping criteria",!stoppingcriteriaerr);
PrintResult("* analytic properties",!analytictesterrors)
PrintResult("* preconditioner test",!prectesterrors);
PrintResult("* termination requests",!termreqerrors);
//--- was errors?
//
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| This function generates random MxN problem, solves it with|
//| LSQR and compares with results obtained from SVD solver. Matrix A|
//| is generated as MxN matrix with uniformly distributed random |
//| entries, i.e. it has no special properties (like conditioning or |
//| separation of singular values). |
//| We apply random amount regularization to our problem (from zero |
//| to large) in order to test regularizer. Default stopping |
//| criteria are used. Preconditioning is turned off because it |
//| skews results for rank-deficient problems. |
//| INPUT: |
//| Silent - if true then function output report |
//+------------------------------------------------------------------+
bool CTestLinLSQRUnit::SVDTest(bool silent)
{
//--- create variables
int szm=5;
int szn=5;
CLinLSQRState s;
CLinLSQRReport rep;
CSparseMatrix spa;
CMatrixDouble a;
CRowDouble b;
CRowDouble x0;
int n=0;
int m=0;
double lambdai=0;
int i=0;
int j=0;
for(m=1; m<=szm; m++)
{
for(n=1; n<=szn; n++)
{
//--- Prepare MxN matrix A, right part B, lambda
lambdai=CMath::RandomReal();
a=matrix<double>::Zeros(m,n);
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CMath::RandomReal()-1);
}
CSparse::SparseCreate(m,n,1,spa);
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
CSparse::SparseSet(spa,i,j,a.Get(i,j));
}
CSparse::SparseConvertToCRS(spa);
b=vector<double>::Zeros(m);
for(i=0; i<m; i++)
b.Set(i,2*CMath::RandomReal()-1);
//--- Solve by calling LinLSQRIteration
CLinLSQR::LinLSQRCreate(m,n,s);
CLinLSQR::LinLSQRSetB(s,b);
CLinLSQR::LinLSQRSetLambdaI(s,lambdai);
CLinLSQR::LinLSQRSetPrecUnit(s);
while(CLinLSQR::LinLSQRIteration(s))
{
if(s.m_needmv)
for(i=0; i<m; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
if(s.m_needmtv)
{
for(i=0; i<n; i++)
{
s.m_mtv.Set(i,0);
for(j=0; j<m; j++)
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
}
}
}
CLinLSQR::LinLSQRResults(s,x0,rep);
if(!IsItGoodSolution(a,b,m,n,lambdai,x0,m_e0,m_tolort))
return(true);
//--- test LinLSQRRestart and LinLSQRSolveSparse
CLinLSQR::LinLSQRRestart(s);
CLinLSQR::LinLSQRSolveSparse(s,spa,b);
CLinLSQR::LinLSQRResults(s,x0,rep);
if(!IsItGoodSolution(a,b,m,n,lambdai,x0,m_e0,m_tolort))
return(true);
}
}
if(!silent)
PrintResult("SVDTest",true);
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| The test checks that algorithm can solve MxN (with N<=M) |
//| well-conditioned problems - and can do so within exactly N |
//| iterations. We use moderate condition numbers, from 1.0 to 10.0, |
//| because larger condition number may require several additional |
//| iterations to converge. |
//| We try different scalings of the A and B. |
//| INPUT: |
//| Silent - if true then function does not outputs results |
//| to console |
//+------------------------------------------------------------------+
bool CTestLinLSQRUnit::MWCRankSVDTest(bool silent)
{
//--- create variables
CLinLSQRState s;
CLinLSQRReport rep;
CMatrixDouble a;
CRowDouble b;
double bnorm=0;
CRowDouble x0;
int szm=5;
int n=0;
int m=0;
int ns0=0;
int ns1=0;
int nlambdai=0;
double s0=0;
double s1=0;
double lambdai=0;
double c=0;
int i=0;
int j=0;
for(m=1; m<=szm; m++)
{
for(n=1; n<=m; n++)
{
for(nlambdai=0; nlambdai<=3; nlambdai++)
{
for(ns0=-1; ns0<=1; ns0++)
{
for(ns1=-1; ns1<=1; ns1++)
{
//--- Generate problem:
//--- * scale factors s0, s1
//--- * MxN well conditioned A (with condition number C in [1,10] and norm s0)
//--- * regularization coefficient LambdaI
//--- * right part b, with |b|=s1
s0=MathPow(10,10*ns0);
s1=MathPow(10,10*ns1);
lambdai=0;
switch(nlambdai)
{
case 0:
lambdai=0;
break;
case 1:
lambdai=s0/1000;
break;
case 2:
lambdai=s0;
break;
case 3:
lambdai=s0*1000;
break;
}
c=(10-1)*CMath::RandomReal()+1;
CMatGen::RMatrixRndCond(m,c,a);
for(j=0; j<n; j++)
a.Col(j,a.Col(j)*s0);
b=vector<double>::Zeros(m);
do
{
bnorm=0;
for(i=0; i<m; i++)
{
b.Set(i,2*CMath::RandomReal()-1);
bnorm+=b[i]*b[i];
}
bnorm=MathSqrt(bnorm);
}
while(bnorm<=m_e0);
b*=s1/bnorm;
//--- Solve by LSQR method
CLinLSQR::LinLSQRCreate(m,n,s);
CLinLSQR::LinLSQRSetB(s,b);
CLinLSQR::LinLSQRSetCond(s,0,0,n);
CLinLSQR::LinLSQRSetLambdaI(s,lambdai);
while(CLinLSQR::LinLSQRIteration(s))
{
if(s.m_needmv)
for(i=0; i<m; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
if(s.m_needmtv)
{
for(i=0; i<n; i++)
{
s.m_mtv.Set(i,0);
for(j=0; j<m; j++)
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
}
}
}
CLinLSQR::LinLSQRResults(s,x0,rep);
if(!IsItGoodSolution(a,b,m,n,lambdai,x0,m_e0,m_tolort))
return(true);
}
}
}
}
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| The test checks that algorithm can find a solution with minimum |
//| norm for a singular rectangular problem. System matrix has |
//| special property - singular values are either zero or well |
//| separated from zero. |
//| INPUT: |
//| Silent - if true then function output report |
//+------------------------------------------------------------------+
bool CTestLinLSQRUnit::MWICRankSVDTest(bool silent)
{
//--- create variables
CLinLSQRState s;
CLinLSQRReport rep;
CSparseMatrix spa;
CRowDouble b;
double bnorm=0;
CRowDouble x0;
int szm=5;
int n=0;
int m=0;
int nz=0;
int ns0=0;
int ns1=0;
int nlambdai=0;
double s0=0;
double s1=0;
double lambdai=0;
int i=0;
int j=0;
CMatrixDouble a;
for(m=1; m<=szm; m++)
{
for(n=1; n<=m; n++)
{
for(nlambdai=0; nlambdai<=2; nlambdai++)
{
for(ns0=-1; ns0<=1; ns0++)
{
for(ns1=-1; ns1<=1; ns1++)
{
for(nz=0; nz<n; nz++)
{
//--- Generate problem:
//--- * scale coefficients s0, s1
//--- * regularization coefficient LambdaI
//--- * MxN matrix A, norm(A)=s0, with NZ zero singular values and N-NZ nonzero ones
//--- * right part b with norm(b)=s1
s0=MathPow(10,10*ns0);
s1=MathPow(10,10*ns1);
lambdai=0;
switch(nlambdai)
{
case 0:
lambdai=0;
break;
case 1:
lambdai=s0;
break;
case 2:
lambdai=s0*1000;
break;
}
a=matrix<double>::Zeros(m,n);
for(i=0; i<n-nz; i++)
a.Set(i,i,s0*(0.1+0.9*CMath::RandomReal()));
CMatGen::RMatrixRndOrthogonalFromTheLeft(a,m,n);
CMatGen::RMatrixRndOrthogonalFromTheRight(a,m,n);
b=vector<double>::Zeros(m);
do
{
bnorm=0;
for(i=0; i<m; i++)
{
b.Set(i,2*CMath::RandomReal()-1);
bnorm+=b[i]*b[i];
}
bnorm=MathSqrt(bnorm);
}
while(bnorm<=m_e0);
b*=s1/bnorm;
//--- Solve by LSQR method
CLinLSQR::LinLSQRCreate(m,n,s);
CLinLSQR::LinLSQRSetB(s,b);
CLinLSQR::LinLSQRSetCond(s,m_emergencye0,m_emergencye0,n);
CLinLSQR::LinLSQRSetLambdaI(s,lambdai);
while(CLinLSQR::LinLSQRIteration(s))
{
if(s.m_needmv)
{
for(i=0; i<m; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
}
if(s.m_needmtv)
{
for(i=0; i<n; i++)
{
s.m_mtv.Set(i,0);
for(j=0; j<m; j++)
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
}
}
}
CLinLSQR::LinLSQRResults(s,x0,rep);
//--- Check
if(!IsItGoodSolution(a,b,m,n,lambdai,x0,m_e0,m_tolort))
return(true);
}
}
}
}
}
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| The test does check, that algorithm can find a solution with |
//| minimum norm, if a problem has bidiagonal matrix on diagonals of |
//| a lot of zeros. This problem has to lead to case when State. |
//| Alpha and State.Beta are zero, and we we can be sure that the |
//| algorithm correctly handles it. |
//| We do not use iteration count as stopping condition, because |
//| problem can be degenerate and we may need more than N iterations |
//| to converge. |
//| INPUT: |
//| Silent - if true then function output report |
//+------------------------------------------------------------------+
bool CTestLinLSQRUnit::BiDiagonalTest(bool silent)
{
//--- create variables
CLinLSQRState s;
CLinLSQRReport rep;
CMatrixDouble a;
CRowDouble b;
double bnorm=0;
CRowDouble x0;
int sz=5;
int n=0;
int m=0;
int minmn=0;
int ns0=0;
int ns1=0;
double s0=0;
double s1=0;
int i=0;
int j=0;
int p=0;
int diag=0;
double pz=0;
for(m=1; m<=sz; m++)
{
for(n=1; n<=sz; n++)
{
minmn=MathMin(m,n);
for(p=0; p<=2; p++)
{
for(ns0=-1; ns0<=1; ns0++)
{
for(ns1=-1; ns1<=1; ns1++)
{
for(diag=0; diag<=1; diag++)
{
//--- Generate problem:
//--- * scaling coefficients s0, s1
//--- * bidiagonal A, with probability of having zero element at diagonal equal to PZ
s0=MathPow(10,10*ns0);
s1=MathPow(10,10*ns1);
pz=(p+1)*0.25;
a=matrix<double>::Zeros(m,n);
for(i=0; i<minmn; i++)
{
if(CMath::RandomReal()>=pz)
a.Set(i,i,2*CMath::RandomReal()-1);
}
for(i=1; i<minmn; i++)
{
if(CMath::RandomReal()>=pz)
{
if(diag==0)
a.Set(i-1,i,2*CMath::RandomReal()-1);
if(diag==1)
a.Set(i,i-1,2*CMath::RandomReal()-1);
}
}
a*=s0;
b=vector<double>::Zeros(m);
do
{
bnorm=0;
for(i=0; i<m; i++)
{
b.Set(i,2*CMath::RandomReal()-1);
bnorm+=b[i]*b[i];
}
bnorm=MathSqrt(bnorm);
}
while(bnorm<=m_e0);
b*=s1/bnorm;
//--- LSQR solution
CLinLSQR::LinLSQRCreate(m,n,s);
CLinLSQR::LinLSQRSetB(s,b);
CLinLSQR::LinLSQRSetCond(s,m_e0,m_e0,0);
while(CLinLSQR::LinLSQRIteration(s))
{
if(s.m_needmv)
{
for(i=0; i<m; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
}
if(s.m_needmtv)
{
for(i=0; i<n; i++)
{
s.m_mtv.Set(i,0);
for(j=0; j<m; j++)
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
}
}
}
CLinLSQR::LinLSQRResults(s,x0,rep);
//--- Check
if(!IsItGoodSolution(a,b,m,n,0.0,x0,m_e0,m_tolort))
return(true);
}
}
}
}
}
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| The test does check, that algorithm correctly solves a problem in|
//| cases: |
//| 1. A=0, B<>0; |
//| 2. A<>0, B=0; |
//| 3. A=0, B=0. |
//| If some part is not zero then it filled with ones. |
//| INPUT: |
//| Silent - if true then function output report |
//+------------------------------------------------------------------+
bool CTestLinLSQRUnit::ZeroMatrixTest(bool silent)
{
//--- create variables
CLinLSQRState s;
CLinLSQRReport rep;
CMatrixDouble a;
CRowDouble b;
CRowDouble x0;
int sz=5;
int n=0;
int m=0;
int i=0;
int j=0;
int nzeropart=0;
for(m=1; m<=sz; m++)
{
for(n=1; n<=sz; n++)
{
for(nzeropart=0; nzeropart<=2; nzeropart++)
{
//--- Initialize A, b
if(nzeropart==0 || nzeropart==2)
a=matrix<double>::Zeros(m,n);
else
a=matrix<double>::Ones(m,n);
if(nzeropart==1 || nzeropart==2)
b=vector<double>::Zeros(m);
else
b=vector<double>::Ones(m);
//--- LSQR
CLinLSQR::LinLSQRCreate(m,n,s);
CLinLSQR::LinLSQRSetB(s,b);
CLinLSQR::LinLSQRSetCond(s,0,0,n);
while(CLinLSQR::LinLSQRIteration(s))
{
if(s.m_needmv)
for(i=0; i<m; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
if(s.m_needmtv)
{
for(i=0; i<n; i++)
{
s.m_mtv.Set(i,0);
for(j=0; j<m; j++)
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
}
}
}
CLinLSQR::LinLSQRResults(s,x0,rep);
//--- Check
if(!IsItGoodSolution(a,b,m,n,0.0,x0,m_e0,m_tolort))
return(true);
}
}
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| The test does check, that algorithm correctly displays a progress|
//| report. |
//| INPUT: |
//| Silent - if true then function output report |
//+------------------------------------------------------------------+
bool CTestLinLSQRUnit::ReportCorrectnessTest(bool silent)
{
//--- create variables
bool result=false;
double eps=0.001;
int sz=5;
double mn=-100;
double mx=100;
double c=100;
CLinLSQRState s;
CLinLSQRReport rep;
CMatrixDouble a;
CMatrixDouble u;
CMatrixDouble v;
CRowDouble w;
CRowDouble b;
CRowDouble x0;
CRowDouble firstx;
CRowDouble lastx;
double rnorm=0;
double tnorm=0;
int n=0;
int m=0;
int lambdai=0;
int i=0;
int j=0;
int its=0;
double tmp=0;
for(m=1; m<=sz; m++)
{
for(n=1; n<=m; n++)
{
for(lambdai=0; lambdai<=1; lambdai++)
{
its=-1;
//--- initialize matrix A
CMatGen::SPDMatrixRndCond(m+n,c,a);
for(i=m; i<m+n; i++)
{
for(j=0; j<n; j++)
{
if(i-m==j)
a.Set(i,j,lambdai);
else
a.Set(i,j,0);
}
}
//--- initialize b
b=vector<double>::Zeros(m+n);
rnorm=0;
for(i=0; i<m+n; i++)
{
if(i<m)
{
b.Set(i,(mx-mn)*CMath::RandomReal()+mn);
rnorm+=b[i]*b[i];
}
else
b.Set(i,0);
}
//--- initialize FirstX and LastX
firstx=vector<double>::Zeros(n);
lastx=vector<double>::Zeros(n);
//--- calculating by LSQR method
CLinLSQR::LinLSQRCreate(m,n,s);
CLinLSQR::LinLSQRSetB(s,b);
CLinLSQR::LinLSQRSetCond(s,0,0,n);
CLinLSQR::LinLSQRSetLambdaI(s,lambdai);
CLinLSQR::LinLSQRSetXRep(s,true);
CAp::SetErrorFlag(result,CLinLSQR::LinLSQRPeekIterationsCount(s)!=0,"testlinlsqrunit.ap:811");
while(CLinLSQR::LinLSQRIteration(s))
{
if(s.m_needmv)
{
for(i=0; i<m; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
}
if(s.m_needmtv)
{
for(i=0; i<n; i++)
{
s.m_mtv.Set(i,0);
for(j=0; j<m; j++)
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
}
}
if(s.m_xupdated)
{
tnorm=0;
for(i=0; i<=m+n-1; i++)
{
tmp=CAblasF::RDotVR(n,s.m_x,a,i);
tnorm+=CMath::Sqr(b[i]-tmp);
}
//--- check, that RNorm is't more than S.R2
//--- and difference between S.R2 and TNorm
//--- is't more than 'eps'(here S.R2=||rk||,
//--- calculated by the algorithm for LSQR, and
//--- TNorm=||A*S.m_x-b||, calculated by test function).
if(s.m_r2>(rnorm+1000*CMath::m_machineepsilon*MathMax(rnorm,1)))
{
CAp::SetErrorFlag(result,true,"testlinlsqrunit.ap:853");
return(result);
}
if(MathAbs(s.m_r2-tnorm)>eps)
{
CAp::SetErrorFlag(result,true,"testlinlsqrunit.ap:858");
return(result);
}
rnorm=s.m_r2;
its++;
//get X value from first iteration
//--- and from last iteration.
if(its==0)
{
for(i=0; i<n; i++)
firstx.Set(i,s.m_x[i]);
}
if(its==n)
{
for(i=0; i<n; i++)
lastx.Set(i,s.m_x[i]);
}
//--- check iterations counter
CAp::SetErrorFlag(result,CLinLSQR::LinLSQRPeekIterationsCount(s)!=its,"testlinlsqrunit.ap:878");
}
}
CLinLSQR::LinLSQRResults(s,x0,rep);
CAp::SetErrorFlag(result,CLinLSQR::LinLSQRPeekIterationsCount(s)!=n,"testlinlsqrunit.ap:882");
//--- check, that FirstX is equal to zero and LastX
//--- is equal to x0.
for(i=0; i<n; i++)
{
if(firstx[i]!=0.0 || lastx[i]!=x0[i])
{
if(!silent)
{
PrintResult("ReportCorrectnessTest",false);
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
PrintFormat("NMV=%d\n",rep.m_nmv);
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
Print("X and LastX...");
for(j=0; j<n; j++)
PrintFormat("x[%d]=%0.10E; LastX[%d]=%0.10E\n",j,x0[j],j,lastx[j]);
}
CAp::SetErrorFlag(result,true,"testlinlsqrunit.ap:902");
return(result);
}
}
}
}
}
if(!silent)
PrintResult("ReportCorrectnessTest",true);
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| The test does check, that correctly executed Stop criteria by |
//| algorithm. |
//| INPUT: |
//| Silent - if true then function output report |
//+------------------------------------------------------------------+
bool CTestLinLSQRUnit::StopPingCriteriaTest(bool silent)
{
//--- create variables
bool result;
int sz=5;
double mn=-100;
double mx=100;
double c=100;
CLinLSQRState s;
CLinLSQRReport rep;
CMatrixDouble a;
CMatrixDouble u;
CMatrixDouble v;
CRowDouble w;
CRowDouble b;
double bnorm=0;
CRowDouble x0;
int n=0;
int k0=0;
int k1=0;
CRowDouble ark;
double anorm=0;
double arknorm=0;
double rknorm=0;
CRowDouble rk;
int maxits=0;
int i=0;
int j=0;
double tmp=0;
double eps=0;
double epsmod=0;
int i_=0;
for(n=1; n<=sz; n++)
{
//--- Initialize A, unit b
CMatGen::SPDMatrixRndCond(n,c,a);
b=vector<double>::Zeros(n);
bnorm=0;
for(i=0; i<n; i++)
{
b.Set(i,(mx-mn)*CMath::RandomReal()+mn);
bnorm+=b[i]*b[i];
}
bnorm=MathSqrt(bnorm);
//--- Test MaxIts
//--- NOTE: we do not check TerminationType because algorithm may terminate for
//--- several reasons. The only thing which is guaranteed is that stopping condition
//--- MaxIts holds.
maxits=1+CMath::RandomInteger(n);
CLinLSQR::LinLSQRCreate(n,n,s);
CLinLSQR::LinLSQRSetB(s,b);
CLinLSQR::LinLSQRSetCond(s,0,0,maxits);
while(CLinLSQR::LinLSQRIteration(s))
{
if(s.m_needmv)
{
for(i=0; i<n; i++)
{
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
}
}
if(s.m_needmtv)
{
for(i=0; i<n; i++)
{
s.m_mtv.Set(i,0);
for(j=0; j<n; j++)
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
}
}
}
CLinLSQR::LinLSQRResults(s,x0,rep);
if(rep.m_iterationscount>maxits || rep.m_terminationtype<=0)
{
if(!silent)
{
PrintResult("StoppingCriteriaTestn",false);
PrintFormat("N=%d\n",n);
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
PrintFormat("NMV=%d\n",rep.m_nmv);
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
}
result=true;
return(result);
}
//--- Test EpsB.
//--- Set EpsB=eps, check that |r|<epsMod*|b|, where epsMod=1.1*eps.
//--- This modified epsilon is used to avoid influence of the numerical errors.
//--- NOTE: we do not check TerminationType because algorithm may terminate for
//--- several reasons. The only thing which is guaranteed is that stopping condition
//--- EpsB holds.
eps=MathPow(10,-(2+CMath::RandomInteger(3)));
epsmod=1.1*eps;
rk=vector<double>::Zeros(n);
CLinLSQR::LinLSQRCreate(n,n,s);
CLinLSQR::LinLSQRSetB(s,b);
CLinLSQR::LinLSQRSetCond(s,0,eps,0);
while(CLinLSQR::LinLSQRIteration(s))
{
if(s.m_needmv)
{
for(i=0; i<n; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
}
if(s.m_needmtv)
{
for(i=0; i<n; i++)
{
s.m_mtv.Set(i,0);
for(j=0; j<n; j++)
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
}
}
}
CLinLSQR::LinLSQRResults(s,x0,rep);
rknorm=0;
for(i=0; i<n; i++)
{
tmp=CAblasF::RDotVR(n,x0,a,i);
rknorm+=CMath::Sqr(tmp-b[i]);
}
rknorm=MathSqrt(rknorm);
if(rknorm>(epsmod*bnorm) || rep.m_terminationtype<=0)
{
if(!silent)
{
PrintResult("StoppingCriteriaTest",false);
PrintFormat("rkNorm=%0.2E\n",rknorm);
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
PrintFormat("NMV=%d\n",rep.m_nmv);
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
}
result=true;
return(result);
}
}
//--- Test EpsA.
//--- Generate well conditioned underdetermined system with nonzero residual
//--- at the solution. Such system can be generated by generating random
//--- orthogonal matrix (N>=2) and using first N-1 columns as rectangular
//--- system matrix, and sum of all columns with random non-zero coefficients
//--- as right part.
for(n=2; n<=sz; n++)
{
for(k0=-1; k0<=1; k0++)
{
for(k1=-1; k1<=1; k1++)
{
//--- Initialize A with non-unit norm 10^(10*K0), b with non-unit norm 10^(10*K1)
anorm=MathPow(10,10*k0);
CMatGen::RMatrixRndOrthogonal(n,a);
a*=anorm;
b=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
tmp=1+CMath::RandomReal();
for(i_=0; i_<n; i_++)
b.Add(i_,tmp*a.Get(i_,i));
}
tmp=CAblasF::RDotV2(n,b);
tmp=MathPow(10,10*k1)/MathSqrt(tmp);
b*=tmp;
//--- Test EpsA
//--- NOTE: it is guaranteed that algorithm will terminate with correct
//--- TerminationType because other stopping criteria (EpsB) won't be satisfied
//--- on such system.
eps=MathPow(10,-(2+CMath::RandomInteger(3)));
epsmod=1.1*eps;
CLinLSQR::LinLSQRCreate(n,n-1,s);
CLinLSQR::LinLSQRSetB(s,b);
CLinLSQR::LinLSQRSetCond(s,eps,0,0);
while(CLinLSQR::LinLSQRIteration(s))
{
if(s.m_needmv)
{
for(i=0; i<n; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n-1,s.m_x,a,i));
}
if(s.m_needmtv)
{
for(i=0; i<n-1; i++)
{
s.m_mtv.Set(i,0);
for(j=0; j<n; j++)
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
}
}
}
CLinLSQR::LinLSQRResults(s,x0,rep);
//--- Check condition
rk=b;
ark=vector<double>::Zeros(n-1);
rknorm=0;
for(i=0; i<n; i++)
{
rk.Add(i,-CAblasF::RDotVR(n-1,x0,a,i));
rknorm+=CMath::Sqr(rk[i]);
}
rknorm=MathSqrt(rknorm);
arknorm=0;
for(i=0; i<n-1; i++)
{
for(j=0; j<n; j++)
ark.Add(i,a.Get(j,i)*rk[j]);
arknorm+=CMath::Sqr(ark[i]);
}
arknorm=MathSqrt(arknorm);
if((arknorm/(anorm*rknorm))>(epsmod) || rep.m_terminationtype!=4)
{
if(!silent)
{
PrintResult("StoppingCriteriaTestn",false);
PrintFormat("N=%d\n",n);
PrintFormat("IterationsCount=%d\n",rep.m_iterationscount);
PrintFormat("NMV=%d\n",rep.m_nmv);
PrintFormat("TerminationType=%d\n",rep.m_terminationtype);
}
result=true;
return(result);
}
}
}
}
if(!silent)
PrintResult("StoppingCriteriaTest",true);
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| This test compares LSQR for original system A*x=b against |
//| CG for a modified system (A'*A)x = A*b. Both algorithms should |
//| give same sequences of trial points (under exact arithmetics, or|
//| for very good conditioned systems). |
//| INPUT: |
//| Silent - if true then function does not output report |
//+------------------------------------------------------------------+
bool CTestLinLSQRUnit::AnalyticTest(bool silent)
{
//--- create variables
bool result=false;
CLinLSQRState s;
CLinLSQRReport rep;
CMatrixDouble a;
CMatrixDouble xk;
CMatrixDouble ap;
CMatrixDouble r;
CRowDouble b;
CRowDouble tmp;
int n=0;
int m=0;
int i=0;
int j=0;
int k=0;
int smallk=4;
int pointsstored=0;
double v=0;
double tol=1.0E-7;
int i_=0;
//--- Set:
//--- * SmallK - number of steps to check, must be small number in order
//--- to reduce influence of the rounding errors
//--- * Tol - error tolerance for orthogonality/conjugacy criteria
for(m=smallk; m<=smallk+5; m++)
{
for(n=smallk; n<=m; n++)
{
//--- Prepare problem:
//--- * MxN matrix A, Mx1 vector B
//--- * A is filled with random values from [-1,+1]
//--- * diagonal elements are filled with large positive values
//--- (should make system better conditioned)
a=matrix<double>::Zeros(m,n);
b=vector<double>::Zeros(m);
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CMath::RandomReal()-1);
b.Set(i,2*CMath::RandomReal()-1);
}
for(i=0; i<n; i++)
a.Set(i,i,10*(1+CMath::RandomReal()));
//--- Solve with LSQR, save trial points into XK[] array
xk=matrix<double>::Zeros(smallk+1,n);
CLinLSQR::LinLSQRCreate(m,n,s);
CLinLSQR::LinLSQRSetB(s,b);
CLinLSQR::LinLSQRSetCond(s,0,0,smallk);
CLinLSQR::LinLSQRSetXRep(s,true);
pointsstored=0;
while(CLinLSQR::LinLSQRIteration(s))
{
if(s.m_needmv)
{
for(i=0; i<m; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
}
if(s.m_needmtv)
{
for(i=0; i<n; i++)
{
s.m_mtv.Set(i,0);
for(j=0; j<m; j++)
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
}
}
if(s.m_xupdated)
{
//--- check
if(!CAp::Assert(pointsstored<CAp::Rows(xk),"LinLSQR test: internal error"))
return(false);
xk.Row(pointsstored,s.m_x);
pointsstored++;
}
}
if(pointsstored<3)
{
//--- At least two iterations should be performed
//--- (our task is not that easy to solve)
result=true;
return(result);
}
//--- We have recorded sequence of points generated by LSQR,
//--- and now we want to make a comparion against linear CG.
//--- Of course, we could just perform CG solution of (A'*A)*x = A'*b,
//--- but it will need a CG solver, and we do not want to reference one
//--- just to perform testing.
//--- However, we can do better - we can check that sequence of steps
//--- satisfies orthogonality/conjugacy conditions, which are stated
//--- as follows:
//--- * (r[i]^T)*r[j]=0 for i<>j
//--- * (p[i]^T)*A'*A*p[j]=0 for i<>j
//--- where r[i]=(A'*A)*x[i]-A'*b is I-th residual , p[i] is I-th step.
//--- In order to test these criteria we generate two matrices:
//--- * (PointsStored-1)*M matrix AP (matrix of A*p products)
//--- * (PointsStored-1)*N matrix R (matrix of residuals)
//--- Then, we check that each matrix has orthogonal rows.
ap=matrix<double>::Zeros(pointsstored-1,m);
r=matrix<double>::Zeros(pointsstored-1,n);
tmp=vector<double>::Zeros(m);
for(k=0; k<pointsstored-1; k++)
{
//--- Calculate K-th row of AP
for(i=0; i<m; i++)
{
ap.Set(k,i,0.0);
for(j=0; j<n; j++)
ap.Add(k,i,a.Get(i,j)*(xk.Get(k+1,j)-xk.Get(k,j)));
}
//--- Calculate K-th row of R
for(i=0; i<m; i++)
{
v=CAblasF::RDotRR(n,a,i,xk,k);
tmp.Set(i,v-b[i]);
}
for(j=0; j<n; j++)
{
v=0.0;
for(i_=0; i_<m; i_++)
v+=a.Get(i_,j)*tmp[i_];
r.Set(k,j,v);
}
}
for(i=0; i<pointsstored-1; i++)
{
for(j=0; j<pointsstored-1; j++)
{
if(i!=j)
{
v=CAblasF::RDotRR(m,ap,i,ap,j);
result=result||MathAbs(v)>tol;
v=CAblasF::RDotRR(n,r,i,r,j);
result=result||MathAbs(v)>tol;
}
}
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| This test checks behavior of the termination requests. Sets error|
//| flag on failure, leaves it unchanged on success. |
//+------------------------------------------------------------------+
void CTestLinLSQRUnit::TestTerminationRequests(bool &err)
{
//--- create variables
CLinLSQRState s;
CLinLSQRReport rep;
CHighQualityRandState rs;
int pass=0;
CMatrixDouble a;
CRowDouble b;
CRowDouble x1;
int n=0;
int m=0;
int callsleft=0;
int reportsafterrequest=0;
bool firstpointreported;
int i=0;
int j=0;
CHighQualityRand::HQRndRandomize(rs);
for(pass=1; pass<=50; pass++)
{
//--- Prepare problem
callsleft=1+CHighQualityRand::HQRndUniformI(rs,10);
n=callsleft+100+CHighQualityRand::HQRndUniformI(rs,50);
m=n+CHighQualityRand::HQRndUniformI(rs,50);
a=matrix<double>::Zeros(m,n);
b=vector<double>::Zeros(m);
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
b.Set(i,CHighQualityRand::HQRndNormal(rs));
}
//--- Solve with LSQR, terminate after specified amount of A*x/A^T*x requests.
CLinLSQR::LinLSQRCreate(m,n,s);
CLinLSQR::LinLSQRSetB(s,b);
CLinLSQR::LinLSQRSetCond(s,0,0,n);
CLinLSQR::LinLSQRSetXRep(s,true);
firstpointreported=false;
reportsafterrequest=0;
while(CLinLSQR::LinLSQRIteration(s))
{
if(s.m_needmv)
{
for(i=0; i<m; i++)
s.m_mv.Set(i,CAblasF::RDotVR(n,s.m_x,a,i));
if(firstpointreported)
callsleft--;
if(callsleft==0)
CLinLSQR::LinLSQRRequestTermination(s);
}
if(s.m_needmtv)
{
for(i=0; i<n; i++)
{
s.m_mtv.Set(i,0);
for(j=0; j<m; j++)
s.m_mtv.Add(i,a.Get(j,i)*s.m_x[j]);
}
if(firstpointreported)
callsleft--;
if(callsleft==0)
CLinLSQR::LinLSQRRequestTermination(s);
}
if(s.m_xupdated)
{
firstpointreported=true;
if(callsleft<=0)
reportsafterrequest++;
}
}
CLinLSQR::LinLSQRResults(s,x1,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype!=8,"testlinlsqrunit.ap:1460");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(x1,n),"testlinlsqrunit.ap:1461");
CAp::SetErrorFlag(err,reportsafterrequest!=1,"testlinlsqrunit.ap:1462");
}
}
//+------------------------------------------------------------------+
//| This function compares solution calculated by LSQR with one |
//| calculated by SVD solver. Following comparisons are performed: |
//| 1. either: |
//| 1.a) residual norm |Rk| for LSQR solution is at most |
//| epsErr*|B| |
//| 1.b) |A^T*Rk|/(|A|*|Rk|)<=EpsOrt |
//| 2. norm(LSQR_solution) is at most 1.2*norm(SVD_solution) |
//| Test(1) verifies that LSQR found good solution, test(2) verifies |
//| that LSQR finds solution with close-to-minimum norm. We use |
//| factor as large as 1.2 to test deviation from SVD solution |
//| because LSQR is not very good at solving degenerate problems. |
//| INPUT PARAMETERS: |
//| A - array[M,N], system matrix |
//| B - right part |
//| M, N - problem size |
//| LambdaV - regularization value for the problem, >=0 |
//| X - array[N], solution found by LSQR |
//| EpsErr - tolerance for |A*x-b| |
//| EpsOrt - tolerance for |A^T*Rk|/(|A|*|Rk|) |
//| RESULT |
//| True, for solution which passess all the tests |
//+------------------------------------------------------------------+
bool CTestLinLSQRUnit::IsItGoodSolution(CMatrixDouble &a,
CRowDouble &b,int m,int n,
double lambdav,
CRowDouble &x,
double epserr,double epsort)
{
//--- create variables
bool result;
CMatrixDouble svda;
CMatrixDouble u;
CMatrixDouble vt;
CRowDouble w;
CRowDouble svdx;
CRowDouble tmparr;
CRowDouble r;
int i=0;
int j=0;
int minmn=0;
bool svdresult;
double v=0;
double rnorm=0;
double bnorm=0;
double anorm=0;
double atrnorm=0;
double xnorm=0;
double svdxnorm=0;
bool clause1holds;
bool clause2holds;
int i_=0;
//--- Solve regularized problem with SVD solver
svda=a;
svda.Resize(m+n,n);
for(i=m; i<=m+n-1; i++)
{
for(j=0; j<n; j++)
{
if(i-m==j)
svda.Set(i,j,lambdav);
else
svda.Set(i,j,0);
}
}
svdresult=CSingValueDecompose::RMatrixSVD(svda,m+n,n,1,1,0,w,u,vt);
//--- check
if(!CAp::Assert(svdresult,"LINLSQR: internal error in unit test (SVD failed)"))
return(false);
minmn=MathMin(m,n);
svdx=vector<double>::Zeros(n);
tmparr=vector<double>::Zeros(minmn);
for(i=0; i<minmn; i++)
{
for(j=0; j<m; j++)
tmparr.Add(i,u.Get(j,i)*b[j]);
if(w[i]<=(MathSqrt(CMath::m_machineepsilon)*w[0]))
tmparr.Set(i,0);
else
tmparr.Mul(i,1.0/w[i]);
}
for(i=0; i<n; i++)
{
svdx.Set(i,0);
for(j=0; j<minmn; j++)
svdx.Add(i,vt.Get(j,i)*tmparr[j]);
}
//--- Calculate residual, perform checks 1.a and 1.b:
//--- * first, we check 1.a
//--- * in case 1.a fails we check 1.b
r=vector<double>::Zeros(m+n);
for(i=0; i<m+n; i++)
{
v=CAblasF::RDotVR(n,x,svda,i);
r.Set(i,v);
if(i<m)
r.Add(i,-b[i]);
}
v=CAblasF::RDotV2(m+n,r);
rnorm=MathSqrt(v);
v=CAblasF::RDotV2(m,b);
bnorm=MathSqrt(v);
if(rnorm<=(epserr*bnorm))
{
//--- 1.a is true, no further checks is needed
clause1holds=true;
}
else
{
//--- 1.a is false, we have to check 1.b
//--- In order to do so, we calculate ||A|| and ||A^T*Rk||. We do
//--- not store product of A and Rk to some array, all we need is
//--- just one component of product at time, stored in V.
anorm=0;
atrnorm=0;
for(i=0; i<n; i++)
{
v=0;
for(j=0; j<m+n; j++)
{
v+=svda.Get(j,i)*r[j];
anorm+=CMath::Sqr(svda.Get(j,i));
}
atrnorm+=CMath::Sqr(v);
}
anorm=MathSqrt(anorm);
atrnorm=MathSqrt(atrnorm);
clause1holds=(anorm*rnorm)==0.0||(atrnorm/(anorm*rnorm))<=(epsort);
}
//--- Check (2).
//--- Here we assume that Result=True when we enter this block.
v=CAblasF::RDotV2(n,x);
xnorm=MathSqrt(v);
v=CAblasF::RDotV2(n,svdx);
svdxnorm=MathSqrt(v);
clause2holds=xnorm<=(1.2*svdxnorm);
//--- End
result=clause1holds && clause2holds;
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing preconditioned LSQR method. |
//+------------------------------------------------------------------+
bool CTestLinLSQRUnit::PreconditionerTest(void)
{
//--- create variables
bool result;
CLinLSQRState s;
CLinLSQRReport rep;
CMatrixDouble a;
CMatrixDouble ta;
CSparseMatrix sa;
CRowDouble b;
CRowDouble d;
CRowDouble xe;
CRowDouble x0;
bool bflag;
int i=0;
int j=0;
int n=0;
//--- Test 1.
//--- We test automatic diagonal preconditioning used by SolveSparse.
//--- In order to do so we:
//--- 1. generate 20*20 matrix A0 with condition number equal to 1.0E1
//--- 2. generate random "exact" solution xe and right part b=A0*xe
//--- 3. generate random ill-conditioned diagonal scaling matrix D with
//--- condition number equal to 1.0E50:
//--- 4. transform A*x=b into badly scaled problem:
//--- A0*x0=b0
//--- (A0*D)*(inv(D)*x0)=b0
//--- finally we got new problem A*x=b with A=A0*D, b=b0, x=inv(D)*x0
//--- Then we solve A*x=b:
//--- 1. with default preconditioner
//--- 2. with explicitly activayed diagonal preconditioning
//--- 3. with unit preconditioner.
//--- 1st and 2nd solutions must be close to xe, 3rd solution must be very
//--- far from the true one.
n=20;
CMatGen::RMatrixRndCond(n,1.0E1,ta);
xe=vector<double>::Zeros(n);
for(i=0; i<n; i++)
xe.Set(i,CApServ::RandomNormal());
b=vector<double>::Zeros(n);
for(i=0; i<n; i++)
b.Set(i,CAblasF::RDotVR(n,xe,ta,i));
d=vector<double>::Zeros(n);
for(i=0; i<n; i++)
d.Set(i,MathPow(10,100*CMath::RandomReal()-50));
a=matrix<double>::Zeros(n,n);
CSparse::SparseCreate(n,n,n*n,sa);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
a.Set(i,j,ta.Get(i,j)*d[j]);
CSparse::SparseSet(sa,i,j,a.Get(i,j));
}
xe.Set(i,xe[i]/d[i]);
}
CSparse::SparseConvertToCRS(sa);
CLinLSQR::LinLSQRCreate(n,n,s);
CLinLSQR::LinLSQRSetCond(s,0,0,2*n);
CLinLSQR::LinLSQRSolveSparse(s,sa,b);
CLinLSQR::LinLSQRResults(s,x0,rep);
if(rep.m_terminationtype<=0)
{
result=true;
return(result);
}
for(i=0; i<n; i++)
{
if(MathAbs(xe[i]-x0[i])>(5.0E-2/d[i]))
{
result=true;
return(result);
}
}
CLinLSQR::LinLSQRSetPrecUnit(s);
CLinLSQR::LinLSQRSolveSparse(s,sa,b);
CLinLSQR::LinLSQRResults(s,x0,rep);
if(rep.m_terminationtype>0)
{
bflag=false;
for(i=0; i<n; i++)
bflag=bflag||MathAbs(xe[i]-x0[i])>(5.0E-2/d[i]);
if(!bflag)
{
result=true;
return(result);
}
}
CLinLSQR::LinLSQRSetPrecDiag(s);
CLinLSQR::LinLSQRSolveSparse(s,sa,b);
CLinLSQR::LinLSQRResults(s,x0,rep);
if(rep.m_terminationtype<=0)
{
result=true;
return(result);
}
for(i=0; i<n; i++)
{
if(MathAbs(xe[i]-x0[i])>(5.0E-2/d[i]))
{
result=true;
return(result);
}
}
//--- test has been passed
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestOptservUnit
{
public:
static bool TestOptserv(bool silent);
private:
static void TestPrec(bool &waserrors);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestOptservUnit::TestOptserv(bool silent)
{
//--- create variables
bool result;
bool precerrors;
bool waserrors;
//--- initialization
precerrors=false;
TestPrec(precerrors);
//--- report
waserrors=precerrors;
if(!silent)
{
Print("TESTING OPTSERV");
Print("TESTS:");
PrintResult("* PRECONDITIONERS",!precerrors);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| This function checks preconditioning functions |
//| On failure sets error flag. |
//+------------------------------------------------------------------+
void CTestOptservUnit::TestPrec(bool &waserrors)
{
//--- create variables
int n=0;
int k=0;
int i=0;
int j=0;
int i0=0;
int j0=0;
int j1=0;
double v=0;
double rho=0;
double theta=0;
double tolg=0;
CMatrixDouble va;
CRowDouble vc;
CRowDouble vd;
CRowDouble vb;
CRowDouble s0;
CRowDouble s1;
CRowDouble s2;
CRowDouble g;
CPrecBufLBFGS buf;
CPrecBufLowRank lowrankbuf;
CRowDouble norms;
CMatrixDouble sk;
CMatrixDouble yk;
CMatrixDouble bk;
CRowDouble bksk;
CRowDouble tmp;
CMatInvReport rep;
CHighQualityRandState rs;
int i_=0;
CHighQualityRand::HQRndRandomize(rs);
//--- Test for inexact L-BFGS preconditioner.
//--- We generate QP problem 0.5*x'*H*x, with random H=D+V'*C*V.
//--- Different K's, from 0 to N, are tried. We test preconditioner
//--- code which uses compact L-BFGS update against reference implementation
//--- which uses non-compact BFGS scheme.
//--- For each K we perform two tests: first for KxN non-zero matrix V,
//--- second one for NxN matrix V with last N-K rows set to zero. Last test
//--- checks algorithm's ability to handle zero updates.
tolg=1.0E-9;
for(n=1; n<=10; n++)
{
for(k=0; k<=n; k++)
{
//--- Prepare problem:
//--- * VD, VC, VA, with VC/VA reordered by ascending of VC[i]*norm(VA[i,...])^2
//--- * trial vector S (copies are stored to S0,S1,S2)
vd=vector<double>::Zeros(n);
s0=vector<double>::Zeros(n);
s1=vector<double>::Zeros(n);
s2=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
vd.Set(i,MathExp(CHighQualityRand::HQRndNormal(rs)));
s0.Set(i,CHighQualityRand::HQRndNormal(rs));
s1.Set(i,s0[i]);
s2.Set(i,s0[i]);
}
CMatGen::RMatrixRndCond(n,1.0E2,va);
CApServ::RVectorSetLengthAtLeast(vc,n);
for(i=0; i<k; i++)
vc.Set(i,MathExp(CHighQualityRand::HQRndNormal(rs)));
for(i=k; i<n; i++)
{
vc.Set(i,0);
for(j=0; j<n; j++)
va.Set(i,j,0.0);
}
norms=vector<double>::Zeros(k);
for(i=0; i<k; i++)
{
v=CAblasF::RDotRR(n,va,i,va,i);
norms.Set(i,v*vc[i]);
}
for(i=0; i<k; i++)
{
for(j=0; j<=k-2; j++)
{
if(norms[j]>norms[j+1])
{
//--- Swap elements J and J+1
norms.Swap(j,j+1);
vc.Swap(j,j+1);
va.SwapRows(j,j+1);
}
}
}
//--- Generate reference model and apply it to S2:
//--- * generate approximate Hessian Bk
//--- * calculate inv(Bk)
//--- * calculate inv(Bk)*S2, store to S2
CApServ::RMatrixSetLengthAtLeast(sk,k,n);
CApServ::RMatrixSetLengthAtLeast(yk,k,n);
bk=matrix<double>::Zeros(n,n);
bksk=vector<double>::Zeros(n);
tmp=vector<double>::Zeros(n);
for(i=0; i<k; i++)
{
sk.Row(i,va[i]+0);
v=CAblasF::RDotRR(n,sk,i,va,i)*vc[i];
for(j=0; j<n; j++)
yk.Set(i,j,vd[j]*sk.Get(i,j)+va.Get(i,j)*v);
}
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
if(i==j)
bk.Set(i,i,vd[i]);
else
bk.Set(j,i,0.0);
}
}
for(i=0; i<k; i++)
{
theta=0.0;
for(j0=0; j0<n; j0++)
{
bksk.Set(j0,0);
for(j1=0; j1<n; j1++)
{
theta+=sk.Get(i,j0)*bk.Get(j0,j1)*sk.Get(i,j1);
bksk.Add(j0,bk.Get(j0,j1)*sk.Get(i,j1));
}
}
theta=1/theta;
rho=CAblasF::RDotRR(n,sk,i,yk,i);
rho=1/rho;
for(j0=0; j0<n; j0++)
{
for(j1=0; j1<n; j1++)
bk.Add(j0,j1,rho*yk.Get(i,j0)*yk.Get(i,j1));
}
for(j0=0; j0<n; j0++)
{
for(j1=0; j1<n; j1++)
bk.Add(j0,j1,-theta*bksk[j0]*bksk[j1]);
}
}
CMatInv::RMatrixInverse(bk,n,j0,rep);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,s2,bk,i);
tmp.Set(i,v);
}
s2=tmp;
//--- First test for non-zero V:
//--- * apply preconditioner to X0
//--- * compare reference model against implementation being tested
COptServ::InexactLBFGSPreconditioner(s0,n,vd,vc,va,k,buf);
for(i=0; i<n; i++)
CAp::SetErrorFlag(waserrors,MathAbs(s2[i]-s0[i])>tolg,"testoptservunit.ap:236");
//--- Second test - N-K zero rows appended to V and rows are
//--- randomly reordered. Doing so should not change result,
//--- algorithm must be able to order rows according to second derivative
//--- and skip zero updates.
for(i=0; i<n; i++)
{
i0=i+CHighQualityRand::HQRndUniformI(rs,n-i);
vc.Swap(i,i0);
va.SwapRows(i,i0);
}
COptServ::InexactLBFGSPreconditioner(s1,n,vd,vc,va,n,buf);
for(i=0; i<n; i++)
CAp::SetErrorFlag(waserrors,MathAbs(s2[i]-s1[i])>tolg,"testoptservunit.ap:259");
}
}
//--- Test for exact low-rank preconditioner.
//--- We generate QP problem 0.5*x'*H*x, with random H=D+V'*C*V.
//--- Different K's, from 0 to N, are tried. We test preconditioner
//--- code which uses Woodbury update against reference implementation
//--- which performs straightforward matrix inversion.
//--- For each K we perform two tests: first for KxN non-zero matrix V,
//--- second one for NxN matrix V with randomly appended N-K zero rows.
//--- Last test checks algorithm's ability to handle zero updates.
tolg=1.0E-9;
for(n=1; n<=10; n++)
{
for(k=0; k<=n; k++)
{
//--- Prepare problem:
//--- * VD, VC, VA
//--- * trial vector S (copies are stored to S0,S1,S2)
vd=vector<double>::Zeros(n);
s0=vector<double>::Zeros(n);
s1=vector<double>::Zeros(n);
s2=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
vd.Set(i,MathExp(CHighQualityRand::HQRndNormal(rs)));
s0.Set(i,CHighQualityRand::HQRndNormal(rs));
s1.Set(i,s0[i]);
s2.Set(i,s0[i]);
}
CMatGen::RMatrixRndCond(n,1.0E2,va);
CApServ::RVectorSetLengthAtLeast(vc,n);
for(i=0; i<k; i++)
vc.Set(i,MathExp(CHighQualityRand::HQRndNormal(rs)));
for(i=k; i<n; i++)
{
vc.Set(i,0);
for(j=0; j<n; j++)
va.Set(j,i,0.0);
}
//--- Generate reference model and apply it to S2
bk=matrix<double>::Zeros(n,n);
tmp=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
if(i==j)
v=vd[i];
else
v=0.0;
for(j1=0; j1<k; j1++)
{
v+=va.Get(j1,i)*vc[j1]*va.Get(j1,j);
}
bk.Set(j,i,v);
}
}
CMatInv::RMatrixInverse(bk,n,j,rep);
//--- check
if(!CAp::Assert(j>0))
{
waserrors=true;
return;
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,s2,bk,i);
tmp.Set(i,v);
}
s2=tmp;
//--- First test for non-zero V:
//--- * apply preconditioner to X0
//--- * compare reference model against implementation being tested
COptServ::PrepareLowRankPreconditioner(vd,vc,va,n,k,lowrankbuf);
COptServ::ApplyLowRankPreconditioner(s0,lowrankbuf);
for(i=0; i<n; i++)
CAp::SetErrorFlag(waserrors,MathAbs(s2[i]-s0[i])>tolg,"testoptservunit.ap:341");
//--- Second test - N-K zero rows appended to V and rows are
//--- randomly reordered. Doing so should not change result,
//--- algorithm must be able to order rows according to second derivative
//--- and skip zero updates.
//
for(i=0; i<n; i++)
{
i0=i+CHighQualityRand::HQRndUniformI(rs,n-i);
vc.Swap(i,i0);
va.SwapRows(i,i0);
}
COptServ::PrepareLowRankPreconditioner(vd,vc,va,n,n,lowrankbuf);
COptServ::ApplyLowRankPreconditioner(s1,lowrankbuf);
for(i=0; i<n; i++)
CAp::SetErrorFlag(waserrors,MathAbs(s2[i]-s1[i])>tolg,"testoptservunit.ap:365");
}
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestCQModelsUnit
{
public:
static bool TestCQModels(bool silent);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestCQModelsUnit::TestCQModels(bool silent)
{
//--- create variables
bool result;
bool eval0errors;
bool eval1errors;
bool eval2errors;
bool newton0errors;
bool newton1errors;
bool newton2errors;
bool waserrors;
CConvexQuadraticModel s;
int nkind=0;
int kmax=0;
int n=0;
int k=0;
int i=0;
int pass=0;
int j=0;
double alpha=0;
double theta=0;
double tau=0;
double v=0;
double v2=0;
double h=0;
double f0=0;
double mkind=0;
double xtadx2=0;
double noise=0;
CMatrixDouble a;
CMatrixDouble q;
CRowDouble b;
CRowDouble r;
CRowDouble x;
CRowDouble x0;
CRowDouble xc;
CRowDouble d;
CRowDouble ge;
CRowDouble gt;
CRowDouble tmp0;
CRowDouble adx;
CRowDouble adxe;
bool activeset[];
int i_=0;
waserrors=false;
//--- Eval0 test: unconstrained model evaluation
eval0errors=false;
for(n=1; n<=5; n++)
{
for(k=0; k<=2*n; k++)
{
//--- Allocate place
a=matrix<double>::Zeros(n,n);
b=vector<double>::Zeros(n);
x=vector<double>::Zeros(n);
d=vector<double>::Zeros(n);
ge=vector<double>::Zeros(n);
gt=vector<double>::Zeros(n);
tmp0=vector<double>::Zeros(n);
if(k>0)
{
q=matrix<double>::Zeros(k,n);
r=vector<double>::Zeros(k);
}
//--- Generate problem
alpha=CMath::RandomReal()+1.0;
theta=CMath::RandomReal()+1.0;
tau=CMath::RandomReal()*CMath::RandomInteger(2);
for(i=0; i<n; i++)
{
a.Set(i,i,10*(1+CMath::RandomReal()));
b.Set(i,2*CMath::RandomReal()-1);
d.Set(i,CMath::RandomReal()+1);
for(j=i+1; j<n; j++)
{
v=0.1*CMath::RandomReal()-0.05;
a.Set(i,j,v);
a.Set(j,i,v);
}
for(j=0; j<k; j++)
q.Set(j,i,2*CMath::RandomReal()-1);
}
for(i=0; i<k; i++)
r.Set(i,2*CMath::RandomReal()-1);
//--- Build model
CCQModels::CQMInit(n,s);
CCQModels::CQMSetA(s,a,CMath::RandomReal()>0.5,alpha);
CCQModels::CQMSetB(s,b);
CCQModels::CQMSetQ(s,q,r,k,theta);
CCQModels::CQMSetD(s,d,tau);
//--- Evaluate and compare:
//--- * X - random point
//---*GE - "exact" gradient
//--- * XTADX2 - x'*(alpha*A+tau*D)*x/2
//--- * ADXE - (alpha*A+tau*D)*x
//--- * V - model value at X
for(i=0; i<n; i++)
{
x.Set(i,2*CMath::RandomReal()-1);
ge.Set(i,0.0);
}
v=0.0;
xtadx2=0.0;
adxe=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
v+=x[i]*b[i];
ge.Add(i,b[i]);
v+=0.5*CMath::Sqr(x[i])*tau*d[i];
ge.Add(i,x[i]*tau*d[i]);
adxe.Add(i,x[i]*tau*d[i]);
xtadx2+=0.5*CMath::Sqr(x[i])*tau*d[i];
for(j=0; j<n; j++)
{
v+=0.5*alpha*x[i]*a.Get(i,j)*x[j];
ge.Add(i,alpha*a.Get(i,j)*x[j]);
adxe.Add(i,alpha*a.Get(i,j)*x[j]);
xtadx2+=0.5*alpha*x[i]*a.Get(i,j)*x[j];
}
}
for(i=0; i<k; i++)
{
v2=CAblasF::RDotVR(n,x,q,i);
v+=0.5*theta*CMath::Sqr(v2-r[i]);
for(j=0; j<n; j++)
ge.Add(j,theta*(v2-r[i])*q.Get(i,j));
}
v2=CCQModels::CQMEval(s,x);
CAp::SetErrorFlag(eval0errors,MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
CCQModels::CQMEvalX(s,x,v2,noise);
CAp::SetErrorFlag(eval0errors,MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
CAp::SetErrorFlag(eval0errors,(noise<0.0 || noise>(10000.0*CMath::m_machineepsilon)),StringFormat("%s: %d",__FUNCTION__,__LINE__));
v2=CCQModels::CQMXTADX2(s,x,tmp0);
CAp::SetErrorFlag(eval0errors,MathAbs(xtadx2-v2)>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
CCQModels::CQMGradUnconstrained(s,x,gt);
for(i=0; i<n; i++)
CAp::SetErrorFlag(eval0errors,MathAbs(ge[i]-gt[i])>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
CCQModels::CQMADX(s,x,adx);
for(i=0; i<n; i++)
CAp::SetErrorFlag(eval0errors,MathAbs(adx[i]-adxe[i])>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
}
}
waserrors=waserrors||eval0errors;
//--- Eval1 test: constrained model evaluation
eval1errors=false;
for(n=1; n<=5; n++)
{
for(k=0; k<=2*n; k++)
{
//--- Allocate place
a=matrix<double>::Zeros(n,n);
b=vector<double>::Zeros(n);
x=vector<double>::Zeros(n);
xc=vector<double>::Zeros(n);
ArrayResize(activeset,n);
if(k>0)
{
q=matrix<double>::Zeros(k,n);
r=vector<double>::Zeros(k);
}
//--- Generate problem
alpha=CMath::RandomReal()+1.0;
theta=CMath::RandomReal()+1.0;
for(i=0; i<n; i++)
{
a.Set(i,i,10*(1+CMath::RandomReal()));
b.Set(i,2*CMath::RandomReal()-1);
xc.Set(i,2*CMath::RandomReal()-1);
activeset[i]=CMath::RandomReal()>0.5;
for(j=i+1; j<n; j++)
{
v=0.1*CMath::RandomReal()-0.05;
a.Set(i,j,v);
a.Set(j,i,v);
}
for(j=0; j<k; j++)
q.Set(j,i,2*CMath::RandomReal()-1);
}
for(i=0; i<k; i++)
r.Set(i,2*CMath::RandomReal()-1);
//--- Build model, evaluate at random point X, compare
CCQModels::CQMInit(n,s);
CCQModels::CQMSetA(s,a,CMath::RandomReal()>0.5,alpha);
CCQModels::CQMSetB(s,b);
CCQModels::CQMSetQ(s,q,r,k,theta);
CCQModels::CQMSetActiveSet(s,xc,activeset);
for(i=0; i<n; i++)
{
x.Set(i,2*CMath::RandomReal()-1);
if(!activeset[i])
xc.Set(i,x[i]);
}
v=0.0;
for(i=0; i<n; i++)
{
v+=xc[i]*b[i];
for(j=0; j<n; j++)
v+=0.5*alpha*xc[i]*a.Get(i,j)*xc[j];
}
for(i=0; i<k; i++)
{
v2=CAblasF::RDotVR(n,xc,q,i);
v+=0.5*theta*CMath::Sqr(v2-r[i]);
}
CAp::SetErrorFlag(eval1errors,MathAbs(v-CCQModels::CQMEval(s,xc))>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
CAp::SetErrorFlag(eval1errors,MathAbs(v-CCQModels::CQMDebugConstrainedEvalT(s,x))>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
CAp::SetErrorFlag(eval1errors,MathAbs(v-CCQModels::CQMDebugConstrainedEvalE(s,x))>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
}
}
waserrors=waserrors||eval1errors;
//--- Eval2 test: we generate empty problem and apply sequence of random transformations,
//--- re-evaluating and re-checking model after each modification.
//--- The purpose of such test is to ensure that our caching strategy works correctly.
eval2errors=false;
for(n=1; n<=5; n++)
{
kmax=2*n;
a=matrix<double>::Zeros(n,n);
b=vector<double>::Zeros(n);
d=vector<double>::Ones(n);
x=vector<double>::Zeros(n);
xc=vector<double>::Zeros(n);
q=matrix< double>::Zeros(kmax,n);
r=vector< double>::Zeros(kmax);
ArrayResize(activeset,n);
tmp0=vector<double>::Zeros(n);
alpha=0.0;
theta=0.0;
k=0;
tau=1.0+CMath::RandomReal();
for(i=0; i<n; i++)
{
activeset[i]=false;
xc.Set(i,2*CMath::RandomReal()-1);
}
CCQModels::CQMInit(n,s);
CCQModels::CQMSetD(s,d,tau);
for(pass=1; pass<=100; pass++)
{
//--- Select random modification type, apply modification.
//--- MKind is a random integer in [0,7] - number of specific
//--- modification to apply.
mkind=CMath::RandomInteger(8);
if(mkind==0.0)
{
//--- Set non-zero D
tau=1.0+CMath::RandomReal();
for(i=0; i<n; i++)
d.Set(i,2*CMath::RandomReal()+1);
CCQModels::CQMSetD(s,d,tau);
}
else
{
if(mkind==1.0)
{
//--- Set zero D.
//--- In case Alpha=0, set non-zero A.
if(alpha==0.0)
{
alpha=1.0+CMath::RandomReal();
for(i=0; i<n; i++)
{
for(j=i+1; j<n; j++)
{
a.Set(i,j,0.2*CMath::RandomReal()-0.1);
a.Set(j,i,a.Get(i,j));
}
}
for(i=0; i<n; i++)
a.Set(i,i,4+2*CMath::RandomReal());
CCQModels::CQMSetA(s,a,CMath::RandomReal()>0.5,alpha);
}
tau=0.0;
d.Fill(0.0);
CCQModels::CQMSetD(s,d,0.0);
}
else
{
if(mkind==2.0)
{
//--- Set non-zero A
alpha=1.0+CMath::RandomReal();
for(i=0; i<n; i++)
{
for(j=i+1; j<n; j++)
{
a.Set(i,j,0.2*CMath::RandomReal()-0.1);
a.Set(j,i,a.Get(i,j));
}
}
for(i=0; i<n; i++)
a.Set(i,i,4+2*CMath::RandomReal());
CCQModels::CQMSetA(s,a,CMath::RandomReal()>0.5,alpha);
}
else
{
if(mkind==3.0)
{
//--- Set zero A.
//--- In case Tau=0, set non-zero D.
if(tau==0.0)
{
tau=1.0+CMath::RandomReal();
for(i=0; i<n; i++)
d.Set(i,2*CMath::RandomReal()+1);
CCQModels::CQMSetD(s,d,tau);
}
alpha=0.0;
a=matrix<double>::Zeros(n,n);
CCQModels::CQMSetA(s,a,CMath::RandomReal()>0.5,alpha);
}
else
{
if(mkind==4.0)
{
//--- Set B.
for(i=0; i<n; i++)
b.Set(i,2*CMath::RandomReal()-1);
CCQModels::CQMSetB(s,b);
}
else
{
if(mkind==5.0)
{
//--- Set Q.
k=CMath::RandomInteger(kmax+1);
theta=1.0+CMath::RandomReal();
for(i=0; i<k; i++)
{
r.Set(i,2*CMath::RandomReal()-1);
for(j=0; j<n; j++)
q.Set(i,j,2*CMath::RandomReal()-1);
}
CCQModels::CQMSetQ(s,q,r,k,theta);
}
else
{
if(mkind==6.0)
{
//--- Set active set
for(i=0; i<n; i++)
{
activeset[i]=CMath::RandomReal()>0.5;
xc.Set(i,2*CMath::RandomReal()-1);
}
CCQModels::CQMSetActiveSet(s,xc,activeset);
}
else
{
if(mkind==7.0)
{
//--- Rewrite main diagonal
if(alpha==0.0)
alpha=1.0;
for(i=0; i<n; i++)
{
tmp0.Set(i,1+CMath::RandomReal());
a.Set(i,i,tmp0[i]/alpha);
}
CCQModels::CQMRewriteDenseDiagonal(s,tmp0);
}
}
}
}
}
}
}
}
//--- generate random point with respect to constraints,
//--- test model at this point
for(i=0; i<n; i++)
{
x.Set(i,2*CMath::RandomReal()-1);
if(activeset[i])
x.Set(i,xc[i]);
}
v=CAblasF::RDotV(n,x,b);
if(tau>0.0)
{
for(i=0; i<n; i++)
v+=0.5*tau*d[i]*CMath::Sqr(x[i]);
}
if(alpha>0.0)
{
for(i=0; i<n; i++)
for(j=0; j<n; j++)
v+=0.5*alpha*x[i]*a.Get(i,j)*x[j];
}
if(theta>0.0)
{
for(i=0; i<k; i++)
{
v2=CAblasF::RDotVR(n,x,q,i);
v+=0.5*theta*CMath::Sqr(v2-r[i]);
}
}
v2=CCQModels::CQMEval(s,x);
CAp::SetErrorFlag(eval2errors,MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
v2=CCQModels::CQMDebugConstrainedEvalT(s,x);
CAp::SetErrorFlag(eval2errors,MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
v2=CCQModels::CQMDebugConstrainedEvalE(s,x);
CAp::SetErrorFlag(eval2errors,MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon),StringFormat("%s: %d",__FUNCTION__,__LINE__));
}
}
waserrors=waserrors||eval2errors;
//--- Newton0 test: unconstrained optimization
newton0errors=false;
for(n=1; n<=5; n++)
{
for(k=0; k<=2*n; k++)
{
//--- Allocate place
a=matrix<double>::Zeros(n,n);
b=vector<double>::Zeros(n);
x=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
if(k>0)
{
q=matrix<double>::Zeros(k,n);
r=vector<double>::Zeros(k);
}
//--- Generate problem with known solution x0:
//--- min f(x),
//--- f(x) = 0.5*(x-x0)'*A*(x-x0)
//--- = 0.5*x'*A*x + (-x0'*A)*x + 0.5*x0'*A*x0'
alpha=CMath::RandomReal()+1.0;
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
a.Set(i,i,10*(1+CMath::RandomReal()));
for(j=i+1; j<n; j++)
{
v=0.1*CMath::RandomReal()-0.05;
a.Set(i,j,v);
a.Set(j,i,v);
}
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,x0,a,i);
b.Set(i,-(alpha*v));
}
theta=CMath::RandomReal()+1.0;
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
q.Set(i,j,2*CMath::RandomReal()-1);
v=CAblasF::RDotVR(n,x0,q,i);
r.Set(i,v);
}
//--- Build model, evaluate at random point X, compare
CCQModels::CQMInit(n,s);
CCQModels::CQMSetA(s,a,CMath::RandomReal()>0.5,alpha);
CCQModels::CQMSetB(s,b);
CCQModels::CQMSetQ(s,q,r,k,theta);
CCQModels::CQMConstrainedOptimum(s,x);
for(i=0; i<n; i++)
newton0errors=newton0errors||MathAbs(x[i]-x0[i])>(1.0E6*CMath::m_machineepsilon);
}
}
waserrors=waserrors||newton0errors;
//--- Newton1 test: constrained optimization
newton1errors=false;
h=1.0E-3;
for(n=1; n<=5; n++)
{
for(k=0; k<=2*n; k++)
{
//--- Allocate place
a=matrix<double>::Zeros(n,n);
b=vector<double>::Zeros(n);
x=vector<double>::Zeros(n);
xc=vector<double>::Zeros(n);
ArrayResize(activeset,n);
if(k>0)
{
q=matrix<double>::Zeros(k,n);
r=vector<double>::Zeros(k);
}
//--- Generate test problem with unknown solution.
alpha=CMath::RandomReal()+1.0;
for(i=0; i<n; i++)
{
a.Set(i,i,10*(1+CMath::RandomReal()));
b.Set(i,2*CMath::RandomReal()-1);
xc.Set(i,2*CMath::RandomReal()-1);
activeset[i]=CMath::RandomReal()>0.5;
for(j=i+1; j<n; j++)
{
v=0.1*CMath::RandomReal()-0.05;
a.Set(i,j,v);
a.Set(j,i,v);
}
}
theta=CMath::RandomReal()+1.0;
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
q.Set(i,j,2*CMath::RandomReal()-1);
r.Set(i,2*CMath::RandomReal()-1);
}
//--- Build model, find solution
CCQModels::CQMInit(n,s);
CCQModels::CQMSetA(s,a,CMath::RandomReal()>0.5,alpha);
CCQModels::CQMSetB(s,b);
CCQModels::CQMSetQ(s,q,r,k,theta);
CCQModels::CQMSetActiveSet(s,xc,activeset);
if(CCQModels::CQMConstrainedOptimum(s,x))
{
//--- Check that constraints are satisfied,
//--- and that solution is true optimum
f0=CCQModels::CQMEval(s,x);
for(i=0; i<n; i++)
{
newton1errors=newton1errors||(activeset[i] && x[i]!=xc[i]);
if(!activeset[i])
{
v=x[i];
x.Set(i,v+h);
v2=CCQModels::CQMEval(s,x);
newton1errors=newton1errors||v2<f0;
x.Set(i,v-h);
v2=CCQModels::CQMEval(s,x);
newton1errors=newton1errors||v2<f0;
x.Set(i,v);
}
}
}
else
newton1errors=true;
}
}
waserrors=waserrors||newton1errors;
//--- Newton2 test: we test ability to work with diagonal matrices, including
//--- very large ones (up to 100.000 elements). This test checks that:
//--- a) we can work with Alpha=0, i.e. when we have strictly diagonal A
//--- b) diagonal problems are handled efficiently, i.e. algorithm will
//--- successfully solve problem with N=100.000
//--- Test problem:
//--- * diagonal term D and rank-K term Q
//--- * known solution X0,
//--- * about 50% of constraints are active and equal to components of X0
newton2errors=false;
for(nkind=0; nkind<=5; nkind++)
{
for(k=0; k<=5; k++)
{
n=(int)MathRound(MathPow(n,nkind));
//--- generate problem
d=vector<double>::Zeros(n);
b=vector<double>::Zeros(n);
x=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
ArrayResize(activeset,n);
if(k>0)
{
q=matrix<double>::Zeros(k,n);
r=vector<double>::Zeros(k);
}
tau=1+CMath::RandomReal();
theta=1+CMath::RandomReal();
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
d.Set(i,1+CMath::RandomReal());
b.Set(i,-(x0[i]*tau*d[i]));
activeset[i]=CMath::RandomReal()>0.5;
}
for(i=0; i<k; i++)
{
v=0.0;
for(j=0; j<n; j++)
{
q.Set(i,j,2*CMath::RandomReal()-1);
v+=q.Get(i,j)*x0[j];
}
r.Set(i,v);
}
//--- Solve, test
CCQModels::CQMInit(n,s);
CCQModels::CQMSetB(s,b);
CCQModels::CQMSetD(s,d,tau);
CCQModels::CQMSetQ(s,q,r,k,theta);
CCQModels::CQMSetActiveSet(s,x0,activeset);
if(CCQModels::CQMConstrainedOptimum(s,x))
{
//--- Check that constraints are satisfied,
//--- and that solution is true optimum
f0=CCQModels::CQMEval(s,x);
for(i=0; i<n; i++)
{
newton2errors=newton2errors||(activeset[i] && x[i]!=x0[i]);
newton2errors=newton2errors||(!activeset[i] && MathAbs(x[i]-x0[i])>(1000.0*CMath::m_machineepsilon));
}
//--- Check that constrained evaluation at some point gives correct results
for(i=0; i<n; i++)
{
if(activeset[i])
x.Set(i,x0[i]);
else
x.Set(i,2*CMath::RandomReal()-1);
}
v=0.0;
for(i=0; i<n; i++)
v+=0.5*tau*d[i]*CMath::Sqr(x[i])+x[i]*b[i];
for(i=0; i<k; i++)
{
v2=CAblasF::RDotVR(n,x,q,i);
v+=0.5*theta*CMath::Sqr(v2-r[i]);
}
v2=CCQModels::CQMEval(s,x);
newton2errors=(newton2errors||!MathIsValidNumber(v2))||MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon);
v2=CCQModels::CQMDebugConstrainedEvalT(s,x);
newton2errors=(newton2errors||!MathIsValidNumber(v2))||MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon);
v2=CCQModels::CQMDebugConstrainedEvalE(s,x);
newton2errors=(newton2errors||!MathIsValidNumber(v2))||MathAbs(v-v2)>(10000.0*CMath::m_machineepsilon);
}
else
newton2errors=true;
}
}
waserrors=waserrors||newton2errors;
//--- report
if(!silent)
{
Print("TESTING CONVEX QUADRATIC MODELS");
PrintResult("Eval0 test",!eval0errors);
PrintResult("Eval1 test",!eval1errors);
PrintResult("Eval2 test",!eval2errors);
PrintResult("Newton0 test",!newton0errors);
PrintResult("Newton1 test",!newton1errors);
PrintResult("Newton2 test",!newton2errors);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestSNNLSUnit
{
public:
static bool TestSNNLS(bool silent);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestSNNLSUnit::TestSNNLS(bool silent)
{
//--- create variables
bool result;
bool test0errors=false;
bool test1errors=false;
bool test2errors=false;
bool testnewtonerrors=false;
bool waserrors=false;
double eps=0;
int i=0;
int j=0;
int k=0;
double v=0;
int ns=0;
int nd=0;
int nr=0;
CMatrixDouble densea;
CMatrixDouble effectivea;
bool isconstrained[];
CRowDouble g;
CRowDouble b;
CRowDouble x;
CRowDouble xs;
CSNNLSSolver s;
CHighQualityRandState rs;
double rho=0;
double xtol=0;
int nmax=10;
int i_=0;
CHighQualityRand::HQRndRandomize(rs);
//--- Test 2 (comes first because it is very basic):
//--- * NS=0
//--- * ND in [1,NMAX]
//--- * NR=ND
//--- * DenseA is diagonal with positive entries
//--- * B is random
//--- * random constraints
//--- Exact solution is known and can be tested
eps=1.0E-12;
for(nd=1; nd<=nmax; nd++)
{
//--- Generate problem
ns=0;
nr=nd;
densea=matrix<double>::Zeros(nd,nd);
b=vector<double>::Zeros(nd);
ArrayResize(isconstrained,nd);
for(i=0; i<nd; i++)
{
densea.Set(i,i,1+CHighQualityRand::HQRndUniformI(rs,2));
b.Set(i,(1+CHighQualityRand::HQRndUniformI(rs,2))*(2*CHighQualityRand::HQRndUniformI(rs,2)-1));
isconstrained[i]=CHighQualityRand::HQRndUniformR(rs)>0.5;
}
//--- Solve with SNNLS solver
CSNNLS::SNNLSInit(0,0,0,s);
CSNNLS::SNNLSSetProblem(s,densea,b,0,nd,nd);
for(i=0; i<nd; i++)
{
if(!isconstrained[i])
CSNNLS::SNNLSDropNNC(s,i);
}
CSNNLS::SNNLSSolve(s,x);
//--- Check
for(i=0; i<nd; i++)
{
if(isconstrained[i])
{
CAp::SetErrorFlag(test2errors,MathAbs(x[i]-MathMax(b[i]/densea.Get(i,i),0.0))>eps,"testsnnlsunit.ap:86");
CAp::SetErrorFlag(test2errors,x[i]<0.0,"testsnnlsunit.ap:87");
}
else
CAp::SetErrorFlag(test2errors,MathAbs(x[i]-b[i]/densea.Get(i,i))>eps,"testsnnlsunit.ap:90");
}
}
//--- Test 0:
//--- * NS in [0,NMAX]
//--- * ND in [0,NMAX]
//--- * NR in [NS,NS+ND+NMAX]
//--- * NS+ND>0, NR>0
//--- * about 50% of variables are constrained
//--- * we check that constrained gradient is small at the solution
eps=1.0E-5;
for(ns=0; ns<=nmax; ns++)
{
for(nd=0; nd<=nmax; nd++)
{
for(nr=ns; nr<=ns+nd+nmax; nr++)
{
//--- Skip NS+ND=0, NR=0
if(ns+nd==0)
continue;
if(nr==0)
continue;
//--- Generate problem:
//--- * DenseA, array[NR,ND]
//--- * EffectiveA, array[NR,NS+ND]
//--- * B, array[NR]
//--- * IsConstrained, array[NS+ND]
if(nd>0)
{
densea=matrix<double>::Zeros(nr,nd);
for(i=0; i<nr; i++)
{
for(j=0; j<nd; j++)
densea.Set(i,j,2*CMath::RandomReal()-1);
}
}
effectivea=matrix<double>::Zeros(nr,ns+nd);
for(i=0; i<=ns-1; i++)
effectivea.Set(i,i,1.0);
for(i=0; i<nr; i++)
{
for(j=0; j<nd; j++)
effectivea.Set(i,ns+j,densea.Get(i,j));
}
b=vector<double>::Zeros(nr);
for(i=0; i<nr; i++)
b.Set(i,2*CMath::RandomReal()-1);
ArrayResize(isconstrained,ns+nd);
for(i=0; i<ns+nd; i++)
isconstrained[i]=CMath::RandomReal()>0.5;
//--- Solve with SNNLS solver
CSNNLS::SNNLSInit(0,0,0,s);
CSNNLS::SNNLSSetProblem(s,densea,b,ns,nd,nr);
for(i=0; i<ns+nd; i++)
{
if(!isconstrained[i])
CSNNLS::SNNLSDropNNC(s,i);
}
CSNNLS::SNNLSSolve(s,x);
//--- Check non-negativity
for(i=0; i<ns+nd; i++)
CAp::SetErrorFlag(test0errors,isconstrained[i] && x[i]<0.0,"testsnnlsunit.ap:160");
//--- Calculate gradient A'*A*x-b'*A.
//--- Check projected gradient (each component must be less than Eps).
g=vector<double>::Zeros(ns+nd);
for(i=0; i<ns+nd; i++)
{
v=0.0;
for(i_=0; i_<nr; i_++)
v+=b[i_]*effectivea.Get(i_,i);
g.Set(i,-v);
}
for(i=0; i<nr; i++)
{
v=0.0;
for(i_=0; i_<ns+nd; i_++)
v+=effectivea.Get(i,i_)*x[i_];
for(i_=0; i_<ns+nd; i_++)
g.Add(i_,v*effectivea.Get(i,i_));
}
for(i=0; i<ns+nd; i++)
{
if(!isconstrained[i] || x[i]>0.0)
CAp::SetErrorFlag(test0errors,MathAbs(g[i])>eps,"testsnnlsunit.ap:179");
else
CAp::SetErrorFlag(test0errors,g[i]<(-eps),"testsnnlsunit.ap:181");
}
}
}
}
//--- Test 1: ability of the solver to take very short steps.
//--- We solve problem similar to one solver in test 0, but with
//--- progressively decreased magnitude of variables. We generate
//--- problem with already-known solution and compare results against it.
xtol=1.0E-7;
for(ns=0; ns<=nmax; ns++)
{
for(nd=0; nd<=nmax; nd++)
{
for(nr=ns; nr<=ns+nd+nmax; nr++)
{
for(k=0; k<=20; k++)
{
//--- Skip NS+ND=0, NR=0
//--- Skip degenerate problems (NR<NS+ND) - important for this particular test.
if(ns+nd==0)
continue;
if(nr==0)
continue;
if(nr<ns+nd)
continue;
//--- Generate problem:
//--- * DenseA, array[NR,ND]
//--- * EffectiveA, array[NR,NS+ND]
//--- * B, array[NR]
//--- * IsConstrained, array[NS+ND]
rho=MathPow(10,-k);
if(nd>0)
{
densea=matrix<double>::Zeros(nr,nd);
for(i=0; i<nr; i++)
{
for(j=0; j<nd; j++)
densea.Set(i,j,2*CHighQualityRand::HQRndUniformR(rs)-1);
}
}
effectivea=matrix<double>::Zeros(nr,ns+nd);
for(i=0; i<=ns-1; i++)
effectivea.Set(i,i,1.0);
for(i=0; i<nr; i++)
{
for(j=0; j<nd; j++)
effectivea.Set(i,ns+j,densea.Get(i,j));
}
xs=vector<double>::Zeros(ns+nd);
ArrayResize(isconstrained,ns+nd);
for(i=0; i<ns+nd; i++)
{
xs.Set(i,rho*(CHighQualityRand::HQRndUniformR(rs)-0.5));
isconstrained[i]=(xs[i])>0.0;
}
b=vector<double>::Zeros(nr);
for(i=0; i<nr; i++)
{
v=CAblasF::RDotVR(ns+nd,xs,effectivea,i);
b.Set(i,v);
}
//--- Solve with SNNLS solver
CSNNLS::SNNLSInit(0,0,0,s);
CSNNLS::SNNLSSetProblem(s,densea,b,ns,nd,nr);
for(i=0; i<=ns+nd-1; i++)
{
if(!isconstrained[i])
CSNNLS::SNNLSDropNNC(s,i);
}
CSNNLS::SNNLSSolve(s,x);
//--- Check non-negativity
for(i=0; i<ns+nd; i++)
CAp::SetErrorFlag(test1errors,isconstrained[i] && x[i]<0.0,"testsnnlsunit.ap:263");
//--- Compare with true solution
for(i=0; i<ns+nd; i++)
CAp::SetErrorFlag(test1errors,(double)(MathAbs(xs[i]-x[i]))>(double)(rho*xtol),"testsnnlsunit.ap:269");
}
}
}
}
//--- Test for Newton phase:
//--- * NS in [0,NMAX]
//--- * ND in [0,NMAX]
//--- * NR in [NS,NS+ND+NMAX]
//--- * NS+ND>0, NR>0
//--- * all variables are unconstrained
//--- * S.DebugMaxNewton is set to 1, S.RefinementIts is set to 1,
//--- i.e. algorithm is terminated after one Newton iteration, and no
//--- iterative refinement is used.
//--- * we test that gradient is small at solution, i.e. one Newton iteration
//--- on unconstrained problem is enough to find solution. In case of buggy
//--- Newton solver one iteration won't move us to the solution - it may
//--- decrease function value, but won't find exact solution.
//--- This test is intended to catch subtle bugs in the Newton solver which
//--- do NOT prevent algorithm from converging to the solution, but slow it
//--- down (convergence becomes linear or even slower).
eps=1.0E-4;
for(ns=0; ns<=nmax; ns++)
{
for(nd=0; nd<=nmax; nd++)
{
for(nr=ns; nr<=ns+nd+nmax; nr++)
{
//--- Skip NS+ND=0, NR=0
if(ns+nd==0)
continue;
if(nr==0)
continue;
//--- Generate problem:
//--- * DenseA, array[NR,ND]
//--- * EffectiveA, array[NR,NS+ND]
//--- * B, array[NR]
//--- * IsConstrained, array[NS+ND]
if(nd>0)
{
densea=matrix<double>::Zeros(nr,nd);
for(i=0; i<nr; i++)
{
for(j=0; j<nd; j++)
densea.Set(i,j,CHighQualityRand::HQRndNormal(rs));
}
}
effectivea=matrix<double>::Zeros(nr,ns+nd);
for(i=0; i<=ns-1; i++)
effectivea.Set(i,i,1.0);
for(i=0; i<nr; i++)
{
for(j=0; j<nd; j++)
effectivea.Set(i,ns+j,densea.Get(i,j));
}
b=vector<double>::Zeros(nr);
for(i=0; i<nr; i++)
b.Set(i,CHighQualityRand::HQRndNormal(rs));
//--- Solve with SNNLS solver
CSNNLS::SNNLSInit(0,0,0,s);
CSNNLS::SNNLSSetProblem(s,densea,b,ns,nd,nr);
for(i=0; i<ns+nd; i++)
CSNNLS::SNNLSDropNNC(s,i);
s.m_debugmaxinnerits=1;
CSNNLS::SNNLSSolve(s,x);
//--- Calculate gradient A'*A*x-b'*A.
//--- Check projected gradient (each component must be less than Eps).
g=vector<double>::Zeros(ns+nd);
for(i=0; i<ns+nd; i++)
{
v=0.0;
for(i_=0; i_<nr; i_++)
v+=b[i_]*effectivea.Get(i_,i);
g.Set(i,-v);
}
for(i=0; i<nr; i++)
{
v=0.0;
for(i_=0; i_<ns+nd; i_++)
v+=effectivea.Get(i,i_)*x[i_];
for(i_=0; i_<ns+nd; i_++)
g.Add(i_,v*effectivea.Get(i,i_));
}
for(i=0; i<ns+nd; i++)
CAp::SetErrorFlag(testnewtonerrors,MathAbs(g[i])>eps,"testsnnlsunit.ap:358");
}
}
}
//--- report
waserrors=((test0errors||test1errors)||test2errors)||testnewtonerrors;
if(!silent)
{
Print("TESTING SPECIAL NNLS SOLVER");
PrintResult("TEST 0",!test0errors);
PrintResult("TEST 1",!test1errors);
PrintResult("TEST 2",!test2errors);
PrintResult("NEWTON PHASE",!testnewtonerrors);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestSActiveSetsUnit
{
public:
static bool TestSActiveSets(bool silent);
private:
static void CTestSActiveSetsUnit::TestSpecProperties(bool &err);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestSActiveSetsUnit::TestSActiveSets(bool silent)
{
//--- create variables
bool result;
bool waserrors;
bool specerr=false;
//--- initialization
TestSpecProperties(specerr);
//--- report
waserrors=specerr;
if(!silent)
{
Print("TESTING ACTIVE SETS");
PrintResult("* SPECIAL PROPERTIES",!specerr);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| This function tests special properties. |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
void CTestSActiveSetsUnit::TestSpecProperties(bool &err)
{
//--- create variables
int i=0;
int j=0;
int n=0;
int nec=0;
int nic=0;
double v=0;
double vv=0;
CSActiveSet state;
CHighQualityRandState rs;
CRowDouble bl;
CRowDouble bu;
CRowDouble x;
CRowDouble s;
CMatrixDouble c;
CRowInt ct;
int scaletype=0;
int pass=0;
int distortidx=0;
double distortmag=0;
int i_=0;
CHighQualityRand::HQRndRandomize(rs);
//--- N-dimensional problem with Ne equality and Ni inequality constraints.
//--- Check that SActiveSet object uses efficient algorithm
//--- to determine initial point: it avoids expensive (N+Ni)-dimensional
//--- QP subproblem when initial point is feasible w.r.t. constraints.
//--- In order to do so we try to find initial point for a problem with
//--- 2 equality constraints and 1000000 inequality constraints (+box
//--- constraints). Inefficient algorithm will simply fail to allocate
//--- enough memory, so we do not have to perform any checks here.
n=5;
nec=2;
nic=1000000;
x=vector<double>::Zeros(n);
bl=vector<double>::Zeros(n);
bu=vector<double>::Zeros(n);
for(scaletype=0; scaletype<=1; scaletype++)
{
//--- Generate problem
for(i=0; i<n; i++)
{
x.Set(i,CHighQualityRand::HQRndUniformR(rs));
bl.Set(i,x[i]-CHighQualityRand::HQRndUniformR(rs)*CHighQualityRand::HQRndUniformI(rs,2));
bu.Set(i,x[i]+CHighQualityRand::HQRndUniformR(rs)*CHighQualityRand::HQRndUniformI(rs,2));
}
c=matrix<double>::Zeros(nec+nic,n+1);
ct.Resize(nec+nic);
for(i=0; i<nec+nic; i++)
{
v=0;
for(j=0; j<n; j++)
{
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
v+=c.Get(i,j)*x[j];
}
c.Set(i,n,v);
if(i<nec)
ct.Set(i,0);
else
{
c.Add(i,n,0.1);
ct.Set(i,-1);
}
}
//--- Apply scaling (if needed), then randomly multiply C by
//--- some large/small value in order to distort magnitude of
//--- linear constraints.
//--- Correct SActiveSet implementation must be able to renormalize
//--- constraints prior to checking feasibility of initial point.
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
s.Set(i,1);
if(scaletype!=0)
s.Set(i,MathPow(10,40*CHighQualityRand::HQRndUniformR(rs)-20));
x.Mul(i,s[i]);
bl.Mul(i,s[i]);
bu.Mul(i,s[i]);
for(j=0; j<nec+nic; j++)
c.Mul(j,i,1.0/s[i]);
}
for(i=0; i<nec+nic; i++)
{
v=MathPow(10,40*CHighQualityRand::HQRndUniformR(rs)-20);
for(i_=0; i_<=n; i_++)
c.Mul(i,i_,v);
}
//--- Solve.
//--- Simply being able to survive SASStartOptimization() call is enough for this test
CSActiveSets::SASInit(n,state);
CSActiveSets::SASSetScale(state,s);
CSActiveSets::SASSetBC(state,bl,bu);
CSActiveSets::SASSetLC(state,c,ct,nec+nic);
CSActiveSets::SASStartOptimization(state,x);
}
//--- Test that algorithm can correctly distinguish between feasible
//--- and non-feasible initial points. In order to do so we try to
//--- call SASStartOptimization() for moderately sized problem and
//--- examine State.FeasInitPt flag after call is done.
//--- This test is performed for equality-only constraints
for(pass=1; pass<=10; pass++)
{
n=50+CHighQualityRand::HQRndUniformI(rs,10);
nec=10+CHighQualityRand::HQRndUniformI(rs,10);
nic=0;
x=vector<double>::Zeros(n);
for(scaletype=0; scaletype<=1; scaletype++)
{
//--- Generate problem, prepare distortion which introduces infeasibility
for(i=0; i<n; i++)
x.Set(i,CHighQualityRand::HQRndUniformR(rs));
c=matrix<double>::Zeros(nec+nic,n+1);
ct.Resize(nec+nic);
for(i=0; i<nec+nic; i++)
{
v=0;
vv=0;
for(j=0; j<n; j++)
{
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
vv+=CMath::Sqr(c.Get(i,j));
v+=c.Get(i,j)*x[j];
}
c.Set(i,n,v);
vv=CApServ::Coalesce(vv,1);
vv=1/MathSqrt(vv);
for(i_=0; i_<=n; i_++)
c.Mul(i,i_,vv);
ct.Set(i,0);
}
distortidx=CHighQualityRand::HQRndUniformI(rs,n);
distortmag=0;
for(i=0; i<nec+nic; i++)
distortmag=MathMax(distortmag,MathAbs(c.Get(i,distortidx)));
//--- check
if(!CAp::Assert(distortmag>0.0))
{
err=true;
return;
}
distortmag=1.0E7*CMath::m_machineepsilon/distortmag;
//--- Apply scaling (if needed), then randomly multiply C by
//--- some large/small value in order to distort magnitude of
//--- linear constraints.
//--- Correct SActiveSet implementation must be able to renormalize
//--- constraints prior to checking feasibility of initial point.
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
s.Set(i,1);
if(scaletype!=0)
s.Set(i,MathPow(10,40*CHighQualityRand::HQRndUniformR(rs)-20));
x.Mul(i,s[i]);
for(j=0; j<nec+nic; j++)
c.Mul(j,i,1.0/s[i]);
}
for(i=0; i<nec+nic; i++)
{
v=MathPow(10,40*CHighQualityRand::HQRndUniformR(rs)-20);
for(i_=0; i_<=n; i_++)
c.Mul(i,i_,v);
}
//--- Solve for feasible initial X and check State.FeasInitPt flag
CSActiveSets::SASInit(n,state);
CSActiveSets::SASSetScale(state,s);
CSActiveSets::SASSetLC(state,c,ct,nec+nic);
CSActiveSets::SASStartOptimization(state,x);
CAp::SetErrorFlag(err,!state.m_feasinitpt,"testsactivesetsunit.ap:225");
//--- Solve for infeasible initial X and check State.FeasInitPt flag
x.Add(distortidx,s[distortidx]*distortmag);
CSActiveSets::SASInit(n,state);
CSActiveSets::SASSetScale(state,s);
CSActiveSets::SASSetLC(state,c,ct,nec+nic);
CSActiveSets::SASStartOptimization(state,x);
CAp::SetErrorFlag(err,state.m_feasinitpt,"testsactivesetsunit.ap:235");
}
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestMinQPUnit
{
public:
static bool TestMinQP(bool silent);
static bool SimpleTest(void);
static bool FuncTest1(void);
static bool FuncTest2(void);
static bool ConsoleTest(void);
static bool QuickQPTests(void);
static bool BLEICTests(void);
private:
static void BCQPTest(bool &waserrors);
static bool ECQPTest(void);
static void ICQPTest(bool &err);
static void GeneralLCQPTest(bool &errorflag);
static bool SpecialICQPTests(void);
static void DenseAULTests(bool &errorflag);
static void IPMTests(bool &errorflag);
static void SpecTests(bool &errorflag);
static double ProjectedAntiGradNorm(int n,CRowDouble &x,CRowDouble &g,CRowDouble &bndl,CRowDouble &bndu);
static void TestBCGradAndFeasibility(CMatrixDouble &a,CRowDouble &b,CRowDouble &bndl,CRowDouble &bndu,int n,CRowDouble &x,double eps,bool &errorflag);
static int SetRandomAlgoAllModern(CMinQPState &s,double &bctol,double &lctol);
static void SetRandomAlgoNonConvex(CMinQPState &s);
static void SetRandomAlgoSemiDefinite(CMinQPState &s,double &bctol,double &lctol);
static void SetRandomAlgoBC(CMinQPState &s);
static void SetRandomAlgoConvexLC(CMinQPState &s);
static void SetRandomAlgoNonConvexLC(CMinQPState &s);
static void DenseToSparse(CMatrixDouble &a,int n,CSparseMatrix &s);
static void RandomlySplitLC(CMatrixDouble &rawc,CRowInt &rawct,int rawccnt,int n,CSparseMatrix &sparsec,CRowInt &sparsect,int &sparseccnt,CMatrixDouble &densec,CRowInt &densect,int &denseccnt,CHighQualityRandState &rs);
static void RandomlySplitAndSetLCLegacy(CMatrixDouble &rawc,CRowInt &rawct,int rawccnt,int n,CMinQPState &state,CHighQualityRandState &rs);
static void RandomlySplitAndSetLC2(CMatrixDouble &rawc,CRowDouble &rawcl,CRowDouble &rawcu,int rawccnt,int n,CMinQPState &state,CHighQualityRandState &rs);
static void RandomlySelectConvertAndSetQuadraticTerm(CMatrixDouble &a,int n,CMinQPState &state,CHighQualityRandState &rs);
static double GetConstraintRCond(CMatrixDouble &c,int k,int n);
static double QuadraticTarget(CMatrixDouble &a,CRowDouble &b,int n,CRowDouble &x);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestMinQPUnit::TestMinQP(bool silent)
{
//--- create variables
bool result;
bool simpleerrors;
bool func1errors;
bool func2errors;
bool bcqperrors;
bool ecqperrors;
bool icqperrors;
bool lcqperrors;
bool quickqperrors;
bool bleicerrors;
bool denseaulerrors;
bool ipmerrors;
bool specerrors;
bool waserrors;
//--- The VERY basic tests for Cholesky and BLEIC
simpleerrors=SimpleTest();
func1errors=FuncTest1();
func2errors=FuncTest2();
//--- Solver-specific tests
denseaulerrors=false;
ipmerrors=false;
quickqperrors=QuickQPTests();
bleicerrors=BLEICTests();
DenseAULTests(denseaulerrors);
IPMTests(ipmerrors);
icqperrors=false;
lcqperrors=false;
bcqperrors=false;
BCQPTest(bcqperrors);
ecqperrors=ECQPTest();
ICQPTest(icqperrors);
icqperrors=icqperrors||SpecialICQPTests();
GeneralLCQPTest(lcqperrors);
specerrors=false;
SpecTests(specerrors);
//--- report
waserrors=((((((((((simpleerrors||func1errors)||func2errors)||bcqperrors)||ecqperrors)||icqperrors)||lcqperrors)||quickqperrors)||bleicerrors)||denseaulerrors)||ipmerrors)||specerrors;
if(!silent)
{
Print("TESTING MinQP");
Print("BASIC TESTS:");
PrintResult("* SimpleTest",!simpleerrors);
PrintResult("* Func1Test",!func1errors);
PrintResult("* Func2Test",!func2errors);
Print("GENERIC QP TESTS:");
PrintResult("* box constrained",!bcqperrors);
PrintResult("* linearly constrained",!(ecqperrors || icqperrors || lcqperrors));
Print("SOLVER-SPECIFIC TESTS:");
PrintResult("* QuickQP solver tests",!quickqperrors);
PrintResult("* BLEIC solver tests",!bleicerrors);
PrintResult("* DENSE-AUL solver tests",!denseaulerrors);
PrintResult("* IPM solver tests",!ipmerrors);
PrintResult("SPECIAL PROPERTIES",!specerrors);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| Function to test: 'MinQPCreate', 'MinQPSetQuadraticTerm', |
//| 'MinQPSetBC', 'MinQPSetOrigin', 'MinQPSetStartingPoint', |
//| 'MinQPOptimize', 'MinQPResults'. |
//| Test problem: |
//| A = diag(aii), aii>0 (random) |
//| b = 0 random bounds (either no bounds, one bound, two bounds |
//| a<b, two bounds a=b) random start point dimension - |
//| from 1 to 5. |
//| Returns True on success, False on failure. |
//+------------------------------------------------------------------+
bool CTestMinQPUnit::SimpleTest()
{
//--- create variables
double eps=0.001;
int msn=5;
double maxstb=10;
int nexp=1000;
double maxn=10;
double maxnb=1000;
double minnb=-1000;
CMinQPState state;
int sn=0;
int i=0;
int j=0;
int k=0;
int infd=0;
CMatrixDouble a;
CRowDouble ub;
CRowDouble db;
CRowDouble x;
CRowDouble tx;
CRowDouble stx;
CRowDouble xori;
CMinQPReport rep;
for(sn=1; sn<=msn; sn++)
{
tx=vector<double>::Zeros(sn);
xori=vector<double>::Zeros(sn);
stx=vector<double>::Zeros(sn);
db=vector<double>::Zeros(sn);
ub=vector<double>::Zeros(sn);
a=matrix<double>::Zeros(sn,sn);
for(i=0; i<=nexp; i++)
{
//--- create diagonal matrix
for(k=0; k<sn; k++)
a.Set(k,k,maxn*CMath::RandomReal()+1);
CMinQP::MinQPCreate(sn,state);
SetRandomAlgoBC(state);
CMinQP::MinQPSetQuadraticTerm(state,a,false);
for(j=0; j<sn; j++)
{
infd=CMath::RandomInteger(5);
if(infd==0)
{
db.Set(j,AL_NEGINF);
ub.Set(j,AL_POSINF);
}
else
{
if(infd==1)
{
db.Set(j,AL_NEGINF);
ub.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
}
else
{
if(infd==2)
{
db.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
ub.Set(j,AL_POSINF);
}
else
{
if(infd==3)
{
db.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
ub.Set(j,db[j]+maxstb*CMath::RandomReal()+0.01);
}
else
{
db.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
ub.Set(j,db[j]);
}
}
}
}
}
CMinQP::MinQPSetBC(state,db,ub);
//--- initialization for shifting
//--- initial value for 'XORi'
//--- and searching true results
for(j=0; j<sn; j++)
{
xori.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
tx.Set(j,CApServ::BoundVal(xori[j],db[j],ub[j]));
}
CMinQP::MinQPSetOrigin(state,xori);
//--- initialization for starting point
for(j=0; j<sn; j++)
stx.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
CMinQP::MinQPSetStartingPoint(state,stx);
//--- optimize and get result
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x,rep);
for(j=0; j<sn; j++)
{
if(MathAbs(tx[j]-x[j])>eps || x[j]<db[j] || x[j]>ub[j])
return(true);
}
}
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| Function to test: 'MinQPCreate', 'MinQPSetLinearTerm', |
//| 'MinQPSetQuadraticTerm','MinQPSetOrigin','MinQPSetStartingPoint',|
//| 'MinQPOptimize', 'MinQPResults'. |
//| Test problem: |
//| A = positive-definite matrix, obtained by 'SPDMatrixRndCond' |
//| function |
//| b <> 0 without bounds random start point dimension-from 1 to 5.|
//+------------------------------------------------------------------+
bool CTestMinQPUnit::FuncTest1(void)
{
//--- create variables
double eps=0.001;
int msn=5;
int c2=1000;
int nexp=1000;
CMinQPState state;
int sn=0;
int i=0;
int j=0;
int k=0;
CMatrixDouble a;
CRowDouble ub;
CRowDouble db;
CRowDouble x;
CRowDouble tx;
CRowDouble stx;
CRowDouble xori;
CRowDouble xoric;
CMinQPReport rep;
CRowDouble b;
for(sn=1; sn<=msn; sn++)
{
b=vector<double>::Zeros(sn);
tx=vector<double>::Zeros(sn);
xori=vector<double>::Zeros(sn);
xoric=vector<double>::Zeros(sn);
stx=vector<double>::Zeros(sn);
for(i=0; i<=nexp; i++)
{
//--- create simmetric matrix 'A'
CMatGen::SPDMatrixRndCond(sn,MathExp(CMath::RandomReal()*MathLog(c2)),a);
CMinQP::MinQPCreate(sn,state);
SetRandomAlgoBC(state);
CMinQP::MinQPSetQuadraticTerm(state,a,false);
for(j=0; j<sn; j++)
xoric.Set(j,2*CMath::RandomReal()-1);
//--- create linear part
for(j=0; j<sn; j++)
{
b.Set(j,0);
for(k=0; k<sn; k++)
b.Add(j,-xoric[k]*a.Get(k,j));
}
CMinQP::MinQPSetLinearTerm(state,b);
//--- initialization for shifting
//--- initial value for 'XORi'
//--- and searching true results
for(j=0; j<sn; j++)
{
xori.Set(j,2*CMath::RandomReal()-1);
tx.Set(j,xori[j]+xoric[j]);
}
CMinQP::MinQPSetOrigin(state,xori);
//--- initialization for starting point
for(j=0; j<sn; j++)
stx.Set(j,2*CMath::RandomReal()-1);
CMinQP::MinQPSetStartingPoint(state,stx);
//--- optimize and get result
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x,rep);
for(j=0; j<sn; j++)
{
if(MathAbs(tx[j]-x[j])>eps)
return(true);
}
}
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| Function to test: 'MinQPCreate', 'MinQPSetLinearTerm', |
//| 'MinQPSetQuadraticTerm', 'MinQPSetBC', 'MinQPSetOrigin', |
//| 'MinQPSetStartingPoint', 'MinQPOptimize', 'MinQPResults'. |
//| Test problem: |
//| A = positive-definite matrix, obtained by 'SPDMatrixRndCond' |
//| function |
//| b <> 0 boundary constraints random start point dimension - |
//| from 1 to 5. |
//+------------------------------------------------------------------+
bool CTestMinQPUnit::FuncTest2(void)
{
//--- create variables
CMinQPState state;
double eps=0.001;
int msn=5;
int nexp=1000;
int c2=1000;
double maxstb=10;
double maxnb=1000;
double minnb=-1000;
int sn=0;
int i=0;
int j=0;
int k=0;
int infd=0;
double anti=0;
CMatrixDouble a;
CRowDouble ub;
CRowDouble db;
CRowDouble x;
CRowDouble tmpx;
CRowDouble stx;
CRowDouble xori;
CRowDouble xoric;
CMinQPReport rep;
CRowDouble b;
CRowDouble g;
CRowDouble c;
CRowDouble y0;
CRowDouble y1;
for(sn=1; sn<=msn; sn++)
{
tmpx=vector<double>::Zeros(sn);
b=vector<double>::Zeros(sn);
c=vector<double>::Zeros(sn);
g=vector<double>::Zeros(sn);
xori=vector<double>::Zeros(sn);
xoric=vector<double>::Zeros(sn);
stx=vector<double>::Zeros(sn);
db=vector<double>::Zeros(sn);
ub=vector<double>::Zeros(sn);
y0=vector<double>::Zeros(sn);
y1=vector<double>::Zeros(sn);
for(i=0; i<=nexp; i++)
{
//--- create simmetric matrix 'A'
CMatGen::SPDMatrixRndCond(sn,MathExp(CMath::RandomReal()*MathLog(c2)),a);
CMinQP::MinQPCreate(sn,state);
SetRandomAlgoBC(state);
CMinQP::MinQPSetQuadraticTerm(state,a,false);
for(j=0; j<sn; j++)
xoric.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
//--- create linear part
for(j=0; j<sn; j++)
{
b.Set(j,0);
for(k=0; k<sn; k++)
b.Add(j,-xoric[k]*a.Get(k,j));
}
CMinQP::MinQPSetLinearTerm(state,b);
for(j=0; j<sn; j++)
{
infd=CMath::RandomInteger(4);
if(infd==0)
{
db.Set(j,AL_NEGINF);
ub.Set(j,AL_POSINF);
}
else
{
if(infd==1)
{
db.Set(j,AL_NEGINF);
ub.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
}
else
{
if(infd==2)
{
db.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
ub.Set(j,AL_POSINF);
}
else
{
db.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
ub.Set(j,db[j]+maxstb*CMath::RandomReal()+0.01);
}
}
}
}
CMinQP::MinQPSetBC(state,db,ub);
//--- initialization for shifting
//--- initial value for 'XORi'
//--- and searching true results
for(j=0; j<sn; j++)
xori.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
CMinQP::MinQPSetOrigin(state,xori);
for(j=0; j<sn; j++)
{
c.Set(j,0);
for(k=0; k<sn; k++)
c.Add(j,-xori[k]*a.Get(k,j));
}
//--- initialization for starting point
for(j=0; j<sn; j++)
stx.Set(j,(maxnb-minnb)*CMath::RandomReal()+minnb);
CMinQP::MinQPSetStartingPoint(state,stx);
//--- optimize and get result
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x,rep);
CAblas::RMatrixMVect(sn,sn,a,0,0,0,x,0,y0,0);
for(j=0; j<sn; j++)
g.Set(j,y0[j]+c[j]+b[j]);
anti=ProjectedAntiGradNorm(sn,x,g,db,ub);
for(j=0; j<sn; j++)
{
if(MathAbs(anti)>eps)
return(true);
}
}
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| ConsoleTest. |
//+------------------------------------------------------------------+
bool CTestMinQPUnit::ConsoleTest(void)
{
//--- create variables
double eps=0.001;
int msn=2;
CMinQPState state;
int nexp=0;
int sn=0;
int i=0;
int j=0;
int k=0;
CMatrixDouble a;
CRowDouble ub;
CRowDouble db;
CRowDouble x;
CRowDouble stx;
CRowDouble xori;
CRowDouble xoric;
CMinQPReport rep;
CRowDouble b;
CRowDouble g;
CRowDouble y0;
CRowDouble y1;
double c=0;
double anti=0;
for(sn=2; sn<=msn; sn++)
{
b=vector<double>::Zeros(sn);
g=vector<double>::Zeros(sn);
xori=vector<double>::Zeros(sn);
xoric=vector<double>::Zeros(sn);
stx=vector<double>::Zeros(sn);
db=vector<double>::Zeros(sn);
ub=vector<double>::Zeros(sn);
y0=vector<double>::Zeros(sn);
y1=vector<double>::Zeros(sn);
for(i=0; i<=nexp; i++)
{
//--- create simmetric matrix 'A'
a=matrix<double>::Identity(sn,sn);
CMinQP::MinQPCreate(sn,state);
SetRandomAlgoBC(state);
CMinQP::MinQPSetQuadraticTerm(state,a,false);
for(j=0; j<sn; j++)
{
xoric.Set(j,1);
PrintFormat("XoriC=%0.5f \n",xoric[j]);
}
//--- create linear part
for(j=0; j<sn; j++)
{
b.Set(j,0);
for(k=0; k<sn; k++)
b.Add(j,-xoric[k]*a.Get(k,j));
PrintFormat("B[%d]=%0.5f\n",j,b[j]);
}
CMinQP::MinQPSetLinearTerm(state,b);
for(j=0; j<sn; j++)
{
db.Set(j,10);
ub.Set(j,20);
}
CMinQP::MinQPSetBC(state,db,ub);
//--- initialization for shifting
//--- initial value for 'XORi'
//--- and searching true results
for(j=0; j<sn; j++)
xori.Set(j,1);
CMinQP::MinQPSetOrigin(state,xori);
//--- optimize and get result
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x,rep);
CAblas::RMatrixMVect(sn,sn,a,0,0,0,x,0,y0,0);
CAblas::RMatrixMVect(sn,sn,a,0,0,0,x,0,y1,0);
for(j=0; j<sn; j++)
{
c=0;
for(k=0; k<sn; k++)
c-=xori[k]*a.Get(k,j);
g.Set(j,b[j]+c+y0[j]+y1[j]);
}
anti=ProjectedAntiGradNorm(sn,x,b,db,ub);
PrintFormat("SN=%d\n",sn);
PrintFormat("NEXP=%d\n",i);
PrintFormat("TermType=%d\n",rep.m_terminationtype);
for(j=0; j<sn; j++)
{
PrintFormat("X[%d]=%0.5f;\n",j,x[j]);
PrintFormat("DB[%d]=%0.5f; UB[%d]=%0.5f\n",j,db[j],j,ub[j]);
PrintFormat("XORi[%d]=%0.5f; XORiC[%d]=%0.5f;\n",j,xori[j],j,xoric[j]);
PrintFormat("Anti[%d]=%0.5f;\n",j,anti);
if(MathAbs(anti)>eps)
return(true);
}
}
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| This function performs tests specific for QuickQP solver |
//| Returns True on failure. |
//+------------------------------------------------------------------+
bool CTestMinQPUnit::QuickQPTests(void)
{
//--- create variables
bool result=false;
CMinQPState state;
CMinQPReport rep;
int n=0;
int pass=0;
int i=0;
int j=0;
int k=0;
double v=0;
double g=0;
double gnorm=0;
bool flag;
int origintype=0;
int scaletype=0;
bool isupper;
bool issparse;
int itscnt=0;
CRowInt nlist;
int nidx=0;
CMatrixDouble a;
CMatrixDouble za;
CMatrixDouble fulla;
CMatrixDouble halfa;
CMatrixDouble c;
CSparseMatrix sa;
CRowInt ct;
CRowDouble b;
CRowDouble zb;
CRowDouble bndl;
CRowDouble bndu;
CRowDouble x0;
CRowDouble x1;
CRowDouble xend0;
CRowDouble xend1;
CRowDouble xori;
CRowDouble xz;
CRowDouble s;
double eps=0;
CHighQualityRandState rs;
int i_=0;
CHighQualityRand::HQRndRandomize(rs);
//--- Convex unconstrained test:
//--- * N dimensions
//--- * positive-definite A
//--- * algorithm randomly choose dense or sparse A, and for
//--- sparse matrix it randomly choose format.
//--- * random B with normal entries
//--- * initial point is random, feasible
//--- * random origin (zero or non-zero) and scale (unit or
//--- non-unit) are generated
eps=1.0E-5;
for(n=1; n<=10; n++)
{
for(pass=1; pass<=10; pass++)
{
//--- Generate problem
origintype=CHighQualityRand::HQRndUniformI(rs,2);
scaletype=CHighQualityRand::HQRndUniformI(rs,2);
isupper=CHighQualityRand::HQRndUniformR(rs)<0.5;
issparse=CHighQualityRand::HQRndUniformR(rs)<0.5;
CMatGen::SPDMatrixRndCond(n,1.0E3,fulla);
halfa=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
if((j>=i && isupper) || (j<=i && !isupper))
halfa.Set(i,j,fulla.Get(i,j));
else
halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
}
}
DenseToSparse(halfa,n,sa);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xori=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
if(origintype==0)
xori.Set(i,0);
else
xori.Set(i,CHighQualityRand::HQRndNormal(rs));
if(scaletype==0)
s.Set(i,1);
else
s.Set(i,MathExp(0.5*CHighQualityRand::HQRndNormal(rs)));
}
//--- Solve problem
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,0,CHighQualityRand::HQRndUniformR(rs)>0.5);
CMinQP::MinQPSetLinearTerm(state,b);
if(issparse)
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
else
CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper);
if(origintype!=0)
CMinQP::MinQPSetOrigin(state,xori);
if(scaletype!=0)
CMinQP::MinQPSetScale(state,s);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:5703");
if(rep.m_terminationtype<=0)
return(result);
//--- Test - calculate constrained gradient at solution,
//--- check its norm.
gnorm=0.0;
for(i=0; i<n; i++)
{
g=b[i];
for(j=0; j<n; j++)
g+=fulla.Get(i,j)*(x1[j]-xori[j]);
gnorm+=CMath::Sqr(g);
}
gnorm=MathSqrt(gnorm);
CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:5721");
}
}
//--- Convex test:
//--- * N dimensions
//--- * random number (0..N) of random boundary constraints
//--- * positive-definite A
//--- * algorithm randomly choose dense or sparse A, and for
//--- sparse matrix it randomly choose format.
//--- * random B with normal entries
//--- * initial point is random, feasible
//--- * random origin (zero or non-zero) and scale (unit or
//--- non-unit) are generated
eps=1.0E-5;
for(n=1; n<=10; n++)
{
for(pass=1; pass<=10; pass++)
{
//--- Generate problem
origintype=CHighQualityRand::HQRndUniformI(rs,2);
scaletype=CHighQualityRand::HQRndUniformI(rs,2);
isupper=CHighQualityRand::HQRndUniformR(rs)<0.5;
issparse=CHighQualityRand::HQRndUniformR(rs)<0.5;
CMatGen::SPDMatrixRndCond(n,1.0E3,fulla);
halfa=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
if((j>=i && isupper) || (j<=i && !isupper))
halfa.Set(i,j,fulla.Get(i,j));
else
halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
}
}
DenseToSparse(halfa,n,sa);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xori=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,AL_NEGINF);
bndu.Set(i,AL_POSINF);
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
if(origintype==0)
xori.Set(i,0);
else
xori.Set(i,CHighQualityRand::HQRndNormal(rs));
if(scaletype==0)
s.Set(i,1);
else
s.Set(i,MathExp(0.5*CHighQualityRand::HQRndNormal(rs)));
j=CHighQualityRand::HQRndUniformI(rs,5);
if(j==0)
{
bndl.Set(i,0);
x0.Set(i,MathAbs(x0[i]));
}
if(j==1)
{
bndu.Set(i,0);
x0.Set(i,-MathAbs(x0[i]));
}
if(j==2)
{
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
bndu.Set(i,bndl[i]);
x0.Set(i,bndl[i]);
}
if(j==3)
{
bndl.Set(i,-0.1);
bndu.Set(i,0.1);
x0.Set(i,0.2*CHighQualityRand::HQRndUniformR(rs)-0.1);
}
}
//--- Solve problem
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,0,CHighQualityRand::HQRndUniformR(rs)>0.5);
CMinQP::MinQPSetLinearTerm(state,b);
if(issparse)
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
else
CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper);
if(origintype!=0)
CMinQP::MinQPSetOrigin(state,xori);
if(scaletype!=0)
CMinQP::MinQPSetScale(state,s);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:5818");
if(rep.m_terminationtype<=0)
return(result);
//--- Test - calculate constrained gradient at solution,
//--- check its norm.
gnorm=0.0;
for(i=0; i<n; i++)
{
g=b[i];
for(j=0; j<n; j++)
g+=fulla.Get(i,j)*(x1[j]-xori[j]);
if(x1[i]==bndl[i] && g>0.0)
g=0;
if(x1[i]==bndu[i] && g<0.0)
g=0;
gnorm+=CMath::Sqr(g);
CAp::SetErrorFlag(result,x1[i]<bndl[i],"testminqpunit.ap:5838");
CAp::SetErrorFlag(result,x1[i]>bndu[i],"testminqpunit.ap:5839");
}
gnorm=MathSqrt(gnorm);
CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:5842");
}
}
//--- Strongly non-convex test:
//--- * N dimensions, N>=2
//--- * box constraints, x[i] in [-1,+1]
//--- * A = A0-0.5*I, where A0 is SPD with unit norm and smallest
//--- singular value equal to 1.0E-3, I is identity matrix
//--- * random B with normal entries
//--- * initial point is random, feasible
//--- We perform two tests:
//--- * unconstrained problem must be recognized as unbounded
//--- * constrained problem can be successfully solved
//--- NOTE: it is important to have N>=2, because formula for A
//--- can be applied only to matrix with at least two
//--- singular values
eps=1.0E-5;
for(n=2; n<=10; n++)
{
for(pass=1; pass<=10; pass++)
{
//--- Generate problem
CMatGen::SPDMatrixRndCond(n,1.0E3,fulla);
for(i=0; i<n; i++)
fulla.Add(i,i,-0.5);
isupper=CHighQualityRand::HQRndUniformR(rs)<0.5;
halfa=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
if((j>=i && isupper) || (j<=i && !isupper))
halfa.Set(i,j,fulla.Get(i,j));
else
halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
}
}
DenseToSparse(halfa,n,sa);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,-1);
bndu.Set(i,1);
x0.Set(i,2*CHighQualityRand::HQRndUniformR(rs)-1);
}
//--- Solve problem:
//--- * without constraints we expect failure
//--- * with constraints algorithm must succeed
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,0,CHighQualityRand::HQRndUniformR(rs)>0.5);
CMinQP::MinQPSetLinearTerm(state,b);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper);
else
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:5907");
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:5911");
if(rep.m_terminationtype<=0)
return(result);
//--- Test - calculate constrained gradient at solution,
//--- check its norm.
gnorm=0.0;
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,x1,fulla,i);
g=v+b[i];
if(x1[i]==bndl[i] && g>0.0)
g=0;
if(x1[i]==bndu[i] && g<0.0)
g=0;
gnorm+=CMath::Sqr(g);
CAp::SetErrorFlag(result,x1[i]<bndl[i],"testminqpunit.ap:5930");
CAp::SetErrorFlag(result,x1[i]>bndu[i],"testminqpunit.ap:5931");
}
gnorm=MathSqrt(gnorm);
CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:5934");
}
}
//--- Basic semi-definite test:
//--- * N dimensions, N>=2
//--- * box constraints, x[i] in [-1,+1]
//--- [ 1 1 ... 1 1 ]
//--- * A = [ ... ... ... ], with one (random) diagonal entry set to -1
//--- [ 1 1 ... 1 1 ]
//--- * random B with normal entries
//--- * initial point is random, feasible
//--- We perform two tests:
//--- * unconstrained problem must be recognized as unbounded
//--- * constrained problem must be recognized as bounded and
//--- successfully solved
//--- Both problems require subtle programming when we work
//--- with semidefinite QP.
//--- NOTE: unlike BLEIC-QP algorthm, QQP may detect unboundedness
//--- of the problem when started from any x0, with any b.
//--- BLEIC-based solver requires carefully chosen x0 and b
//--- to find direction of zero curvature, but this solver
//--- can find it from any point.
nlist.Resize(12);
nlist.Set(0,2);
nlist.Set(1,3);
nlist.Set(2,4);
nlist.Set(3,5);
nlist.Set(4,6);
nlist.Set(5,7);
nlist.Set(6,8);
nlist.Set(7,9);
nlist.Set(8,10);
nlist.Set(9,20);
nlist.Set(10,40);
nlist.Set(11,80);
eps=1.0E-5;
for(nidx=0; nidx<CAp::Len(nlist); nidx++)
{
for(pass=1; pass<=10; pass++)
{
//--- Generate problem
n=nlist[nidx];
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
do
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
}
while(b[i]==0.0);
bndl.Set(i,-1);
bndu.Set(i,1);
x0.Set(i,2*CHighQualityRand::HQRndUniformR(rs)-1);
}
a=matrix<double>::Ones(n,n);
j=CHighQualityRand::HQRndUniformI(rs,n);
a.Set(j,j,-1.0);
DenseToSparse(a,n,sa);
//--- Solve problem:
//--- * without constraints we expect failure
//--- * with constraints algorithm must succeed
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,0,CHighQualityRand::HQRndUniformR(rs)>0.5);
CMinQP::MinQPSetLinearTerm(state,b);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinQP::MinQPSetQuadraticTerm(state,a,true);
else
CMinQP::MinQPSetQuadraticTermSparse(state,sa,true);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:6017");
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6021");
if(rep.m_terminationtype<=0)
return(result);
//--- Test - calculate constrained gradient at solution,
//--- check its norm.
gnorm=0.0;
for(i=0; i<n; i++)
{
g=b[i]+CAblasF::RDotVR(n,x1,a,i);
if(x1[i]==bndl[i] && g>0.0)
g=0;
if(x1[i]==bndu[i] && g<0.0)
g=0;
gnorm+=CMath::Sqr(g);
CAp::SetErrorFlag(result,x1[i]<bndl[i],"testminqpunit.ap:6042");
CAp::SetErrorFlag(result,x1[i]>bndu[i],"testminqpunit.ap:6043");
}
gnorm=MathSqrt(gnorm);
CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:6046");
}
}
//--- Linear (zero-quadratic) test:
//--- * N dimensions, N>=1
//--- * box constraints, x[i] in [-1,+1]
//--- * A = 0
//--- * random B with normal entries
//--- * initial point is random, feasible
//--- We perform two tests:
//--- * unconstrained problem must be recognized as unbounded
//--- * constrained problem can be successfully solved
//--- NOTE: we may explicitly set zero A, or assume that by
//--- default it is zero. During test we will try both
//--- ways.
eps=1.0E-5;
for(n=1; n<=10; n++)
{
for(pass=1; pass<=10; pass++)
{
//--- Generate problem
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
do
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
}
while(b[i]==0.0);
bndl.Set(i,-1);
bndu.Set(i,1);
x0.Set(i,2*CHighQualityRand::HQRndUniformR(rs)-1);
}
//--- Solve problem:
//--- * without constraints we expect failure
//--- * with constraints algorithm must succeed
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,0,CHighQualityRand::HQRndUniformR(rs)>0.5);
CMinQP::MinQPSetLinearTerm(state,b);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
a=matrix<double>::Zeros(n,n);
CMinQP::MinQPSetQuadraticTerm(state,a,true);
}
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:6104");
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6108");
if(rep.m_terminationtype<=0)
return(result);
//--- Test - calculate constrained gradient at solution,
//--- check its norm.
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(result,b[i]>0.0 && x1[i]>bndl[i],"testminqpunit.ap:6118");
CAp::SetErrorFlag(result,b[i]<0.0 && x1[i]<bndu[i],"testminqpunit.ap:6119");
}
}
}
//--- Test for Newton phase of QQP algorithm - we test that Newton
//--- phase can find good solution within one step. In order to do
//--- so we:
//--- * solve convex QP problem (dense or sparse)
//--- * with K<=N equality-only constraints ai=x=bi
//--- * with number of outer iterations limited to just 1
//--- * and with CG phase turned off (we modify internal structures
//--- of the QQP solver in order to make it)
eps=1.0E-5;
for(pass=1; pass<=10; pass++)
{
//--- Generate problem
n=50+CHighQualityRand::HQRndUniformI(rs,51);
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
DenseToSparse(a,n,sa);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
bndl.Set(i,AL_NEGINF);
bndu.Set(i,AL_POSINF);
}
else
{
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
bndu.Set(i,bndl[i]);
}
}
//--- Solve problem
//--- NOTE: we modify internal structures of QQP solver in order
//--- to deactivate CG phase
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,1,true);
state.m_qqpsettingsuser.m_cgphase=false;
CMinQP::MinQPSetLinearTerm(state,b);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinQP::MinQPSetQuadraticTerm(state,a,CHighQualityRand::HQRndNormal(rs)>0.0);
else
CMinQP::MinQPSetQuadraticTermSparse(state,sa,CHighQualityRand::HQRndNormal(rs)>0.0);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6179");
if(rep.m_terminationtype<=0)
return(result);
//--- Test - calculate constrained gradient at solution,
//--- check its norm.
gnorm=0.0;
for(i=0; i<n; i++)
{
g=b[i]+CAblasF::RDotVR(n,x1,a,i);
if(x1[i]==bndl[i] && g>0.0)
g=0;
if(x1[i]==bndu[i] && g<0.0)
g=0;
gnorm+=CMath::Sqr(g);
CAp::SetErrorFlag(result,x1[i]<bndl[i],"testminqpunit.ap:6199");
CAp::SetErrorFlag(result,x1[i]>bndu[i],"testminqpunit.ap:6200");
}
gnorm=MathSqrt(gnorm);
CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:6203");
}
//--- Test for Newton phase of QQP algorithm - we test that Newton
//--- updates work correctly, i.e. that CNewtonUpdate() internal
//--- function correctly updates inverse Hessian matrix.
//--- To test it we:
//--- * solve ill conditioned convex QP problem
//--- * with unconstrained solution XZ whose components are within [-0.5,+0.5]
//--- * with one inequality constraint X[k]>=5
//--- * with initial point such that:
//--- * X0[i] = 100 for i<>k
//--- * X0[k] = 5+1.0E-5
//--- * with number of outer iterations limited to just 1
//--- * and with CG phase turned off (we modify internal structures
//--- of the QQP solver in order to make it)
//--- The idea is that single Newton step is not enough to find solution,
//--- but with just one update we can move exactly to the solution.
//--- We perform two tests:
//--- * first one with State.QQP.NewtMaxIts set to 1, in order to
//--- make sure that algorithm fails with just one iteration
//--- * second one with State.QQP.NewtMaxIts set to 2, in order to
//--- make sure that algorithm converges when it can perform update
eps=1.0E-5;
for(pass=1; pass<=10; pass++)
{
//--- Generate problem
n=20+CHighQualityRand::HQRndUniformI(rs,20);
CMatGen::SPDMatrixRndCond(n,1.0E5,a);
DenseToSparse(a,n,sa);
CSparse::SparseConvertToCRS(sa);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xz=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
xz.Set(i,CHighQualityRand::HQRndUniformR(rs)-0.5);
x0.Set(i,100);
bndl.Set(i,AL_NEGINF);
bndu.Set(i,AL_POSINF);
}
k=CHighQualityRand::HQRndUniformI(rs,n);
x0.Set(k,5.00001);
bndl.Set(k,5.0);
CSparse::SparseMV(sa,xz,b);
b*=-1.0;
//--- Create solver
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,1,true);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,CHighQualityRand::HQRndNormal(rs)>0.0);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPSetStartingPoint(state,x0);
//--- Solve problem. First time, with no Newton updates.
//--- It must fail.
//--- NOTE: we modify internal structures of QQP solver in order
//--- to deactivate CG phase and turn off Newton updates.
state.m_qqpsettingsuser.m_cgphase=false;
state.m_qqpsettingsuser.m_cnphase=true;
state.m_qqpsettingsuser.m_cnmaxupdates=0;
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6282");
if(result)
return(result);
flag=false;
TestBCGradAndFeasibility(a,b,bndl,bndu,n,x1,eps,flag);
CAp::SetErrorFlag(result,!flag,"testminqpunit.ap:6287");
//--- Now with Newton updates - it must succeeed.
state.m_qqpsettingsuser.m_cgphase=false;
state.m_qqpsettingsuser.m_cnmaxupdates=n;
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6296");
if(result)
return(result);
flag=false;
TestBCGradAndFeasibility(a,b,bndl,bndu,n,x1,eps,flag);
CAp::SetErrorFlag(result,flag,"testminqpunit.ap:6301");
}
//--- Check that problem with general constraints results in
//--- correct error code (-5 should be returned).
c=matrix<double>::Ones(1,3);
ct.Resize(1);
c.Set(0,2,2.0);
ct.Set(0,0);
CMinQP::MinQPCreate(2,state);
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,0,CHighQualityRand::HQRndUniformR(rs)>0.5);
CMinQP::MinQPSetLC(state,c,ct,1);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype!=-5,"testminqpunit.ap:6319");
//--- Test sparse functionality. QQP solver must perform
//--- same steps independently of matrix type (dense or sparse).
//--- We generate random unconstrained test problem and solve it
//--- twice - first time we solve dense version, second time -
//--- sparse version is solved.
//--- During this test we:
//--- * use stringent stopping criteria (one outer iteration)
//--- * turn off Newton phase of the algorithm to slow down
//--- convergence
eps=1.0E-3;
itscnt=1;
n=20;
isupper=CMath::RandomReal()>0.5;
CMatGen::SPDMatrixRndCond(n,1.0E3,za);
CSparse::SparseCreate(n,n,0,sa);
a=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
if(j>=i && isupper)
{
CSparse::SparseSet(sa,i,j,za.Get(i,j));
a.Set(i,j,za.Get(i,j));
}
if(j<=i && !isupper)
{
CSparse::SparseSet(sa,i,j,za.Get(i,j));
a.Set(i,j,za.Get(i,j));
}
}
}
b=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CApServ::RandomNormal());
s.Set(i,MathPow(10.0,CApServ::RandomNormal()/10));
}
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,itscnt,false);
CMinQP::MinQPSetScale(state,s);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,isupper);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend0,rep);
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,itscnt,false);
CMinQP::MinQPSetScale(state,s);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend1,rep);
for(i=0; i<n; i++)
CAp::SetErrorFlag(result,MathAbs(xend0[i]-xend1[i])>eps,"testminqpunit.ap:6437");
//--- Test scale-invariance. QQP performs same steps on scaled and
//--- unscaled problems (assuming that scale of the variables is known).
//--- We generate random scale matrix S and random well-conditioned and
//--- well scaled matrix A. Then we solve two problems:
//--- (1) f = 0.5*x'*A*x+b'*x
//--- (identity scale matrix is used)
//--- and
//--- (2) f = 0.5*y'*(inv(S)*A*inv(S))*y + (inv(S)*b)'*y
//--- (scale matrix S is used)
//--- Solution process is started from X=0, we perform ItsCnt=1 outer
//--- iterations with Newton phase turned off (to slow down convergence;
//--- we want to prevent algorithm from converging to exact solution which
//--- is exactly same for both problems; the idea is to test that same
//--- intermediate tests are taken).
//--- As result, we must get S*x=y
eps=1.0E-3;
itscnt=1;
n=100;
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
s.Set(i,MathPow(10.0,CApServ::RandomNormal()/10));
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
za=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
za.Set(i,j,a.Get(i,j)/(s[i]*s[j]));
}
b=vector<double>::Zeros(n);
zb=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CApServ::RandomNormal());
zb.Set(i,b[i]/s[i]);
}
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,itscnt,false);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,true);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend0,rep);
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,itscnt,false);
CMinQP::MinQPSetLinearTerm(state,zb);
CMinQP::MinQPSetQuadraticTerm(state,za,true);
CMinQP::MinQPSetScale(state,s);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend1,rep);
for(i=0; i<n; i++)
CAp::SetErrorFlag(result,MathAbs(s[i]*xend0[i]-xend1[i])>eps,"testminqpunit.ap:6494");
//--- Test that QQP can efficiently use sparse matrices (i.e. it is
//--- not disguised version of some dense QP solver). In order to test
//--- it we create very large and very sparse problem (diagonal matrix
//--- with N=40.000) and perform 10 iterations of QQP solver.
//--- In case QP solver uses some form of dense linear algebra to solve
//--- this problem, it will take TOO much time to solve it. And we will
//--- notice it by EXTREME slowdown during testing.
n=40000;
CSparse::SparseCreate(n,n,0,sa);
for(i=0; i<n; i++)
CSparse::SparseSet(sa,i,i,MathPow(10.0,-(3*CMath::RandomReal())));
b=vector<double>::Zeros(n);
for(i=0; i<n; i++)
b.Set(i,CApServ::RandomNormal());
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoQuickQP(state,0.0,0.0,0.0,10,CHighQualityRand::HQRndUniformR(rs)>0.5);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTermSparse(state,sa,true);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend0,rep);
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| This function performs tests specific for BLEIC solver |
//| Returns True on error, False on success. |
//+------------------------------------------------------------------+
bool CTestMinQPUnit::BLEICTests(void)
{
//--- create variables
bool result=false;
CMinQPState state;
CMinQPReport rep;
CRowInt nlist;
int nidx=0;
CMatrixDouble a;
CMatrixDouble za;
CMatrixDouble c;
CRowDouble b;
CRowDouble zb;
CRowDouble bndl;
CRowDouble bndu;
CRowDouble s;
CRowDouble x;
CRowInt ct;
CSparseMatrix sa;
int n=0;
CRowDouble x0;
CRowDouble x1;
CHighQualityRandState rs;
int i=0;
int j=0;
int pass=0;
CRowDouble xend0;
CRowDouble xend1;
double eps=0;
double v=0;
double g=0;
double gnorm=0;
int itscnt=0;
bool isupper;
CHighQualityRand::HQRndRandomize(rs);
//--- Test sparse functionality. BLEIC-based solver must perform
//--- same steps independently of matrix type (dense or sparse).
//--- We generate random unconstrained test problem and solve it
//--- twice - first time we solve dense version, second time -
//--- sparse version is solved.
eps=1.0E-3;
itscnt=5;
n=20;
isupper=CMath::RandomReal()>0.5;
CMatGen::SPDMatrixRndCond(n,1.0E3,za);
CSparse::SparseCreate(n,n,0,sa);
a=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
if(j>=i && isupper)
{
CSparse::SparseSet(sa,i,j,za.Get(i,j));
a.Set(i,j,za.Get(i,j));
}
if(j<=i && !isupper)
{
CSparse::SparseSet(sa,i,j,za.Get(i,j));
a.Set(i,j,za.Get(i,j));
}
}
}
b=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CApServ::RandomNormal());
s.Set(i,MathPow(10.0,CApServ::RandomNormal()/10));
}
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,0.0,itscnt);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,isupper);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend0,rep);
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,0.0,itscnt);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend1,rep);
for(i=0; i<n; i++)
CAp::SetErrorFlag(result,MathAbs(xend0[i]-xend1[i])>eps,"testminqpunit.ap:6609");
//--- Test scale-invariance. BLEIC performs same steps on scaled and
//--- unscaled problems (assuming that scale of the variables is known).
//--- We generate random scale matrix S and random well-conditioned and
//--- well scaled matrix A. Then we solve two problems:
//--- (1) f = 0.5*x'*A*x+b'*x
//--- (identity scale matrix is used)
//--- and
//--- (2) f = 0.5*y'*(inv(S)*A*inv(S))*y + (inv(S)*b)'*y
//--- (scale matrix S is used)
//--- Solution process is started from X=0, we perform ItsCnt=5 steps.
//--- As result, we must get S*x=y
eps=1.0E-3;
itscnt=5;
n=20;
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
s.Set(i,MathPow(10.0,CApServ::RandomNormal()/10));
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
za=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
za.Set(i,j,a.Get(i,j)/(s[i]*s[j]));
}
b=vector<double>::Zeros(n);
zb=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CApServ::RandomNormal());
zb.Set(i,b[i]/s[i]);
}
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,0.0,itscnt);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,true);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend0,rep);
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,0.0,itscnt);
CMinQP::MinQPSetLinearTerm(state,zb);
CMinQP::MinQPSetQuadraticTerm(state,za,true);
CMinQP::MinQPSetScale(state,s);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend1,rep);
for(i=0; i<n; i++)
CAp::SetErrorFlag(result,MathAbs(s[i]*xend0[i]-xend1[i])>eps,"testminqpunit.ap:6662");
//--- Test that BLEIC can efficiently use sparse matrices (i.e. it is
//--- not disguised version of some dense QP solver). In order to test
//--- it we create very large and very sparse problem (diagonal matrix
//--- with N=20.000) and perform 10 iterations of BLEIC-based QP solver.
//--- In case QP solver uses some form of dense linear algebra to solve
//--- this problem, it will take TOO much time to solve it. And we will
//--- notice it by EXTREME slowdown during testing.
n=20000;
CSparse::SparseCreate(n,n,0,sa);
for(i=0; i<n; i++)
CSparse::SparseSet(sa,i,i,MathPow(10.0,-(3*CMath::RandomReal())));
b=vector<double>::Zeros(n);
for(i=0; i<n; i++)
b.Set(i,CApServ::RandomNormal());
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,0.0,10);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTermSparse(state,sa,true);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend0,rep);
//--- Special semi-definite test:
//--- * N dimensions, N>=2 (important!)
//--- * box constraints, x[i] in [-1,+1]
//--- [ 1 1 ... 1 1 ]
//--- * A = [ ... ... ... ]
//--- [ 1 1 ... 1 1 ]
//--- * random B such that SUM(b[i])=0.0 (important!)
//--- * initial point x0 is chosen in such way that SUM(x[i])=0.0
//--- (important!)
//--- We perform two tests:
//--- * unconstrained problem must be recognized as unbounded
//--- (when starting from x0!)
//--- * constrained problem must be recognized as bounded
//--- and successfully solved
//--- Both problems require subtle programming when we work
//--- with semidefinite QP.
//--- NOTE: it is very important to have N>=2 (otherwise problem
//--- will be bounded from below even without boundary
//--- constraints) and to have x0/b0 such that sum of
//--- components is zero (such x0 is exact minimum of x'*A*x,
//--- which allows algorithm to find direction of zero curvature
//--- at the very first step). If x0/b are chosen in other way,
//--- algorithm may be unable to find direction of zero
//--- curvature and will cycle forever, slowly decreasing
//--- function value at each iteration.
//--- This is major difference from similar test for QQP solver -
//--- QQP can find direction of zero curvature from almost any
//--- point due to internal CG solver which favors such directions.
//--- BLEIC uses LBFGS, which is less able to find direction of
//--- zero curvature.
nlist.Resize(12);
nlist.Set(0,2);
nlist.Set(1,3);
nlist.Set(2,4);
nlist.Set(3,5);
nlist.Set(4,6);
nlist.Set(5,7);
nlist.Set(6,8);
nlist.Set(7,9);
nlist.Set(8,10);
nlist.Set(9,20);
nlist.Set(10,40);
nlist.Set(11,80);
eps=1.0E-5;
for(nidx=0; nidx<CAp::Len(nlist); nidx++)
{
for(pass=1; pass<=10; pass++)
{
//--- Generate problem
n=nlist[nidx];
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
do
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
}
while(b[i]==0.0);
bndl.Set(i,-1);
bndu.Set(i,1);
x0.Set(i,2*CHighQualityRand::HQRndUniformR(rs)-1);
}
x0-=x0.Sum()/(double)n;
b-=b.Sum()/(double)n;
a=matrix<double>::Ones(n,n);
DenseToSparse(a,n,sa);
//--- Solve problem:
//--- * without constraints we expect failure
//--- * with constraints algorithm must succeed
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,0.0,0);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetStartingPoint(state,x0);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinQP::MinQPSetQuadraticTerm(state,a,true);
else
CMinQP::MinQPSetQuadraticTermSparse(state,sa,true);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:6790");
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6794");
if(rep.m_terminationtype<=0)
return(result);
//--- Test - calculate constrained gradient at solution,
//--- check its norm.
gnorm=0.0;
for(i=0; i<n; i++)
{
g=b[i]+CAblasF::RDotVR(n,x1,a,i);
if(x1[i]==bndl[i] && g>0.0)
g=0;
if(x1[i]==bndu[i] && g<0.0)
g=0;
gnorm+=CMath::Sqr(g);
CAp::SetErrorFlag(result,x1[i]<bndl[i],"testminqpunit.ap:6814");
CAp::SetErrorFlag(result,x1[i]>bndu[i],"testminqpunit.ap:6815");
}
gnorm=MathSqrt(gnorm);
CAp::SetErrorFlag(result,gnorm>eps,"testminqpunit.ap:6818");
}
}
//--- Test that BLEIC-based QP solver can solve non-convex problems
//--- which are bounded from below on the feasible set:
//--- min -||x||^2 s.t. x[i] in [-1,+1]
//--- We also test ability of the solver to detect unbounded problems
//--- (we remove one of the constraints and repeat solution process).
n=20;
eps=1.0E-14;
CSparse::SparseCreate(n,n,0,sa);
for(i=0; i<n; i++)
CSparse::SparseSet(sa,i,i,-1);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
bndl.Set(i,-1);
bndu.Set(i,1);
x.Set(i,CMath::RandomReal()-0.5);
}
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoBLEIC(state,eps,0.0,0.0,0);
CMinQP::MinQPSetQuadraticTermSparse(state,sa,true);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPSetStartingPoint(state,x);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend0,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6851");
if(rep.m_terminationtype>0)
{
for(i=0; i<n; i++)
CAp::SetErrorFlag(result,xend0[i]!=(-1.0) && xend0[i]!=1.0,"testminqpunit.ap:6854");
}
i=CMath::RandomInteger(n);
bndl.Set(i,AL_NEGINF);
bndu.Set(i,AL_POSINF);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend0,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:6861");
//--- Test that BLEIC-based QP solver can solve non-convex problems
//--- which are bounded from below on the feasible set:
//--- min -||x||^2 s.t. x[i] in [-1,+1],
//--- with inequality constraints handled as general linear ones
//--- We also test ability of the solver to detect unbounded problems
//--- (we remove last pair of constraints and try to solve modified
//--- problem).
n=20;
eps=1.0E-14;
CSparse::SparseCreate(n,n,0,sa);
for(i=0; i<n; i++)
CSparse::SparseSet(sa,i,i,-1);
c=matrix<double>::Zeros(2*n,n+1);
ct.Resize(2*n);
for(i=0; i<n; i++)
{
c.Set(2*i,i,1.0);
c.Set(2*i,n,1.0);
ct.Set(2*i,-1);
c.Set(2*i+1,i,1.0);
c.Set(2*i+1,n,-1.0);
ct.Set(2*i+1,1);
}
x=vector<double>::Zeros(n);
for(i=0; i<n; i++)
x.Set(i,CMath::RandomReal()-0.5);
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoBLEIC(state,eps,0.0,0.0,0);
CMinQP::MinQPSetQuadraticTermSparse(state,sa,true);
CMinQP::MinQPSetLC(state,c,ct,2*n);
CMinQP::MinQPSetStartingPoint(state,x);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend0,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6906");
if(rep.m_terminationtype>0)
{
for(i=0; i<n; i++)
CAp::SetErrorFlag(result,MathAbs(xend0[i]+1)>(100.0*CMath::m_machineepsilon) && MathAbs(xend0[i]-1)>(100.0*CMath::m_machineepsilon),"testminqpunit.ap:6909");
}
CMinQP::MinQPSetLC(state,c,ct,2*(n-1));
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend0,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype!=-4,"testminqpunit.ap:6913");
//--- Test that BLEIC-based QP solver can solve QP problems with
//--- zero quadratic term:
//--- min b'*x s.t. x[i] in [-1,+1]
//--- It means that QP solver can be used as linear programming solver
//--- (altough performance of such solver is worse than that of specialized
//--- LP solver).
//--- NOTE: we perform this test twice - first time without explicitly setting
//--- quadratic term (we test that default quadratic term is zero), and
//--- second time - with explicitly set quadratic term.
n=20;
CSparse::SparseCreate(n,n,0,sa);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
b=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
bndl.Set(i,-1);
bndu.Set(i,1);
b.Set(i,CApServ::RandomNormal());
}
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoBLEIC(state,eps,0.0,0.0,0);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend0,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6947");
if(rep.m_terminationtype>0)
{
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(result,b[i]>0.0 && xend0[i]!=bndl[i],"testminqpunit.ap:6951");
CAp::SetErrorFlag(result,b[i]<0.0 && xend0[i]!=bndu[i],"testminqpunit.ap:6952");
}
}
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoBLEIC(state,eps,0.0,0.0,0);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPSetQuadraticTermSparse(state,sa,true);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend0,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testminqpunit.ap:6961");
if(rep.m_terminationtype>0)
{
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(result,b[i]>0.0 && xend0[i]!=bndl[i],"testminqpunit.ap:6965");
CAp::SetErrorFlag(result,b[i]<0.0 && xend0[i]!=bndu[i],"testminqpunit.ap:6966");
}
}
//--- Test specific problem sent by V.Semenenko, which resulted in
//--- the initinite loop in FindFeasiblePoint (before fix). We do
//--- not test results returned by solver - simply being able to
//--- Stop is enough for this test.
//--- NOTE: it is important that modifications to problem are applied
//--- sequentially. Test fails after 100-5000 such modifications.
//--- One modification is not enough to cause failure.
n=3;
a=matrix<double>::Zeros(n,n);
a.Set(0,0,1.222990);
a.Set(1,1,1.934900);
a.Set(2,2,0.603924);
b=vector<double>::Zeros(n);
b.Set(0,-4.97245);
b.Set(1,-9.09039);
b.Set(2,-4.63856);
c=matrix<double>::Zeros(8,n+1);
c.Set(0,0,1);
c.Set(0,n,4.94298);
c.Set(1,0,1);
c.Set(1,n,4.79981);
c.Set(2,1,1);
c.Set(2,n,-0.4848);
c.Set(3,1,1);
c.Set(3,n,-0.73804);
c.Set(4,2,1);
c.Set(4,n,0.575729);
c.Set(5,2,1);
c.Set(5,n,0.458645);
c.Set(6,0,1);
c.Set(6,2,-1);
c.Set(6,n,-0.0546574);
c.Set(7,0,1);
c.Set(7,2,-1);
c.Set(7,n,-0.5900440);
ct.Resize(8);
ct.Set(0,-1);
ct.Set(1,1);
ct.Set(2,-1);
ct.Set(3,1);
ct.Set(4,-1);
ct.Set(5,1);
ct.Set(6,-1);
ct.Set(7,1);
s=vector<double>::Zeros(n);
s.Set(0,0.143171);
s.Set(1,0.253240);
s.Set(2,0.117084);
x0=vector<double>::Zeros(n);
x0.Set(0,3.51126);
x0.Set(1,4.05731);
x0.Set(2,6.63307);
for(pass=1; pass<=10000; pass++)
{
//--- Apply random distortion
for(j=0; j<n; j++)
b.Add(j,(2*CHighQualityRand::HQRndUniformI(rs,2)-1)*0.1);
for(j=0; j<6; j++)
c.Add(j,n,(2*CHighQualityRand::HQRndUniformI(rs,2)-1)*0.1);
//--- Solve
CMinQP::MinQPCreate(3,state);
CMinQP::MinQPSetQuadraticTerm(state,a,true);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetLC(state,c,ct,8);
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,0.0,0);
CMinQP::MinQPSetStartingPoint(state,x0);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| This function tests bound constrained quadratic programming |
//| algorithm. |
//| On failure sets error flag. |
//+------------------------------------------------------------------+
void CTestMinQPUnit::BCQPTest(bool &waserrors)
{
//--- create variables
CMinQPState state;
CMinQPReport rep;
int n=0;
int pass=0;
int i=0;
int j=0;
double v=0;
double g=0;
double gnorm=0;
int origintype=0;
int scaletype=0;
bool isupper;
bool issparse;
double bctol=0;
double lctol=0;
CMatrixDouble a;
CMatrixDouble fulla;
CMatrixDouble halfa;
CMatrixDouble c;
CSparseMatrix sa;
CRowInt ct;
CRowDouble b;
CRowDouble bndl;
CRowDouble bndu;
CRowDouble gtrial;
double vl=0;
double vu=0;
CRowDouble x0;
CRowDouble x1;
CRowDouble xori;
CRowDouble xz;
CRowDouble s;
double eps=0;
CHighQualityRandState rs;
int solvertype=0;
int i_=0;
CHighQualityRand::HQRndRandomize(rs);
//--- Convex test:
//--- * N dimensions
//--- * random number (0..N) of random boundary constraints
//--- * positive-definite A
//--- * algorithm randomly choose dense or sparse A, and for
//--- sparse matrix it randomly choose format.
//--- * random B with normal entries
//--- * initial point is random, feasible
//--- * random origin (zero or non-zero) and scale (unit or
//--- non-unit) are generated
eps=1.0E-3;
for(n=1; n<=10; n++)
{
for(pass=1; pass<=10; pass++)
{
//--- Generate problem
origintype=CHighQualityRand::HQRndUniformI(rs,2);
scaletype=CHighQualityRand::HQRndUniformI(rs,2);
isupper=CHighQualityRand::HQRndUniformR(rs)<0.5;
issparse=CHighQualityRand::HQRndUniformR(rs)<0.5;
CMatGen::SPDMatrixRndCond(n,1.0E3,fulla);
halfa=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
if((j>=i && isupper) || (j<=i && !isupper))
halfa.Set(i,j,fulla.Get(i,j));
else
halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
}
}
DenseToSparse(halfa,n,sa);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xori=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,AL_NEGINF);
bndu.Set(i,AL_POSINF);
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
if(origintype==0)
xori.Set(i,0);
else
xori.Set(i,CHighQualityRand::HQRndNormal(rs));
if(scaletype==0)
s.Set(i,1);
else
s.Set(i,MathExp(0.5*CHighQualityRand::HQRndNormal(rs)));
j=CHighQualityRand::HQRndUniformI(rs,5);
if(j==0)
{
bndl.Set(i,0);
x0.Set(i,MathAbs(x0[i]));
}
if(j==1)
{
bndu.Set(i,0);
x0.Set(i,-MathAbs(x0[i]));
}
if(j==2)
{
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
bndu.Set(i,bndl[i]);
x0.Set(i,bndl[i]);
}
if(j==3)
{
bndl.Set(i,-0.1);
bndu.Set(i,0.1);
x0.Set(i,0.2*CHighQualityRand::HQRndUniformR(rs)-0.1);
}
}
//--- Solve problem
CMinQP::MinQPCreate(n,state);
solvertype=SetRandomAlgoAllModern(state,bctol,lctol);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetStartingPoint(state,x0);
if(issparse)
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
else
CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper);
if(origintype!=0)
CMinQP::MinQPSetOrigin(state,xori);
if(scaletype!=0)
CMinQP::MinQPSetScale(state,s);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinQP::MinQPSetBC(state,bndl,bndu);
else
{
for(i=0; i<n; i++)
CMinQP::MinQPSetBCI(state,i,bndl[i],bndu[i]);
}
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminqpunit.ap:882");
CAp::SetErrorFlag(waserrors,CAp::Len(x1)<n,"testminqpunit.ap:883");
CAp::SetErrorFlag(waserrors,CAp::Len(rep.m_lagbc)<n,"testminqpunit.ap:884");
if(waserrors)
return;
//--- Test - calculate constrained gradient at solution,
//--- check its norm.
gnorm=0.0;
for(i=0; i<n; i++)
{
g=b[i];
for(j=0; j<n; j++)
g+=fulla.Get(i,j)*(x1[j]-xori[j]);
if(x1[i]<=(bndl[i]+bctol) && g>0.0)
g=0;
if(x1[i]>=(bndu[i]-bctol) && g<0.0)
g=0;
gnorm+=CMath::Sqr(g);
CAp::SetErrorFlag(waserrors,x1[i]<bndl[i],"testminqpunit.ap:904");
CAp::SetErrorFlag(waserrors,x1[i]>bndu[i],"testminqpunit.ap:905");
}
gnorm=MathSqrt(gnorm);
CAp::SetErrorFlag(waserrors,gnorm>eps,"testminqpunit.ap:908");
//--- Test Lagrange multipliers returned by the solver (we skip
//--- BLEIC solver because it does not return Lagrange vals).
if(solvertype!=-1 && solvertype!=0)
{
gtrial=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
v=b[i];
for(j=0; j<n; j++)
v+=fulla.Get(i,j)*(x1[j]-xori[j]);
gtrial.Set(i,v);
}
for(i=0; i<n; i++)
gtrial.Add(i,rep.m_lagbc[i]);
for(i=0; i<n; i++)
CAp::SetErrorFlag(waserrors,(double)(MathAbs(gtrial[i]))>1.0E-3,"testminqpunit.ap:928");
}
}
}
//--- Convex test, same box constraints for all variables:
//--- * N dimensions
//--- * box constraints set with MinQPSetBCAll()
//--- * positive-definite A
//--- * algorithm randomly choose dense or sparse A, and for
//--- sparse matrix it randomly choose format.
//--- * random B with normal entries
//--- * initial point is random, feasible
//--- * random origin (zero or non-zero) and scale (unit or
//--- non-unit) are generated
eps=1.0E-3;
for(n=1; n<=10; n++)
{
for(pass=1; pass<=10; pass++)
{
//--- Generate problem
origintype=CHighQualityRand::HQRndUniformI(rs,2);
scaletype=CHighQualityRand::HQRndUniformI(rs,2);
isupper=CHighQualityRand::HQRndUniformR(rs)<0.5;
issparse=CHighQualityRand::HQRndUniformR(rs)<0.5;
CMatGen::SPDMatrixRndCond(n,1.0E3,fulla);
halfa=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
if((j>=i && isupper) || (j<=i && !isupper))
halfa.Set(i,j,fulla.Get(i,j));
else
halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
}
}
DenseToSparse(halfa,n,sa);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xori=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
if(origintype==0)
xori.Set(i,0);
else
xori.Set(i,CHighQualityRand::HQRndNormal(rs));
if(scaletype==0)
s.Set(i,1);
else
s.Set(i,MathExp(0.5*CHighQualityRand::HQRndNormal(rs)));
}
vl=AL_NEGINF;
vu=AL_POSINF;
j=CHighQualityRand::HQRndUniformI(rs,5);
switch(j)
{
case 0:
vl=0;
break;
case 1:
vu=0;
break;
case 2:
vl=CHighQualityRand::HQRndNormal(rs);
vu=vl;
break;
case 3:
vl=-0.1-MathPow(2,CHighQualityRand::HQRndNormal(rs));
vu=0.1+MathPow(2,CHighQualityRand::HQRndNormal(rs));
break;
}
//--- Solve problem
CMinQP::MinQPCreate(n,state);
SetRandomAlgoAllModern(state,bctol,lctol);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetStartingPoint(state,x0);
if(issparse)
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
else
CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper);
if(origintype!=0)
CMinQP::MinQPSetOrigin(state,xori);
if(scaletype!=0)
CMinQP::MinQPSetScale(state,s);
CMinQP::MinQPSetBCAll(state,vl,vu);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminqpunit.ap:1017");
if(rep.m_terminationtype<=0)
return;
//--- Test - calculate constrained gradient at solution,
//--- check its norm.
gnorm=0.0;
for(i=0; i<n; i++)
{
g=b[i];
for(j=0; j<n; j++)
g+=fulla.Get(i,j)*(x1[j]-xori[j]);
if(x1[i]<=(vl+bctol) && g>0.0)
g=0;
if(x1[i]>=(vu-bctol) && g<0.0)
g=0;
gnorm+=CMath::Sqr(g);
CAp::SetErrorFlag(waserrors,x1[i]<vl,"testminqpunit.ap:1037");
CAp::SetErrorFlag(waserrors,x1[i]>vu,"testminqpunit.ap:1038");
}
gnorm=MathSqrt(gnorm);
CAp::SetErrorFlag(waserrors,gnorm>eps,"testminqpunit.ap:1041");
}
}
//--- Semidefinite test:
//--- * N dimensions
//--- * nonnegativity constraints
//--- * A = [ 1 1 ... 1 1 ; 1 1 ... 1 1 ; .... ; 1 1 ... 1 1 ]
//--- * algorithm randomly choose dense or sparse A, and for
//--- sparse matrix it randomly choose format.
//--- * random B with normal entries
//--- * initial point is random, feasible
eps=1.0E-4;
for(n=1; n<=10; n++)
{
for(pass=1; pass<=10; pass++)
{
//--- Generate problem
isupper=CHighQualityRand::HQRndUniformR(rs)<0.5;
issparse=CHighQualityRand::HQRndUniformR(rs)<0.5;
fulla=matrix<double>::Zeros(n,n);
halfa=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
fulla.Set(i,j,1.0);
if((j>=i && isupper) || (j<=i && !isupper))
halfa.Set(i,j,fulla.Get(i,j));
else
halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
}
}
DenseToSparse(halfa,n,sa);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,0.0);
bndu.Set(i,AL_POSINF);
x0.Set(i,CHighQualityRand::HQRndUniformI(rs,2));
}
//--- Solve problem
CMinQP::MinQPCreate(n,state);
SetRandomAlgoSemiDefinite(state,bctol,lctol);
CMinQP::MinQPSetStartingPoint(state,x0);
CMinQP::MinQPSetLinearTerm(state,b);
if(issparse)
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
else
CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminqpunit.ap:1101");
if(rep.m_terminationtype<=0)
return;
//--- Test - calculate constrained gradient at solution,
//--- check its norm.
gnorm=0.0;
for(i=0; i<n; i++)
{
g=b[i];
for(j=0; j<n; j++)
g+=fulla.Get(i,j)*x1[j];
if(x1[i]<=(bndl[i]+bctol) && g>0.0)
g=0;
if(x1[i]>=(bndu[i]-bctol) && g<0.0)
g=0;
gnorm+=CMath::Sqr(g);
CAp::SetErrorFlag(waserrors,x1[i]<bndl[i],"testminqpunit.ap:1121");
CAp::SetErrorFlag(waserrors,x1[i]>bndu[i],"testminqpunit.ap:1122");
}
gnorm=MathSqrt(gnorm);
CAp::SetErrorFlag(waserrors,gnorm>eps,"testminqpunit.ap:1125");
}
}
//--- Non-convex test:
//--- * N dimensions, N>=2
//--- * box constraints, x[i] in [-1,+1]
//--- * A = A0-0.5*I, where A0 is SPD with unit norm and smallest
//--- singular value equal to 1.0E-3, I is identity matrix
//--- * random B with normal entries
//--- * initial point is random, feasible
//--- We perform two tests:
//--- * unconstrained problem must be recognized as unbounded
//--- * constrained problem can be successfully solved
//--- NOTE: it is important to have N>=2, because formula for A
//--- can be applied only to matrix with at least two
//--- singular values
eps=1.0E-4;
for(n=2; n<=10; n++)
{
for(pass=1; pass<=10; pass++)
{
//--- Generate problem
CMatGen::SPDMatrixRndCond(n,1.0E3,fulla);
for(i=0; i<n; i++)
fulla.Add(i,i,-0.5);
isupper=CHighQualityRand::HQRndUniformR(rs)<0.5;
halfa=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
if((j>=i && isupper) || (j<=i && !isupper))
halfa.Set(i,j,fulla.Get(i,j));
else
halfa.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
}
}
DenseToSparse(halfa,n,sa);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,-1);
bndu.Set(i,1);
x0.Set(i,2*CHighQualityRand::HQRndUniformR(rs)-1);
}
//--- Solve problem:
//--- * without constraints we expect failure
//--- * with constraints algorithm must succeed
CMinQP::MinQPCreate(n,state);
SetRandomAlgoNonConvex(state);
CMinQP::MinQPSetStartingPoint(state,x0);
CMinQP::MinQPSetLinearTerm(state,b);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinQP::MinQPSetQuadraticTerm(state,halfa,isupper);
else
CMinQP::MinQPSetQuadraticTermSparse(state,sa,isupper);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype!=-4,"testminqpunit.ap:1192");
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminqpunit.ap:1196");
if(rep.m_terminationtype<=0)
return;
//--- Test - calculate constrained gradient at solution,
//--- check its norm.
gnorm=0.0;
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,x1,fulla,i);
g=v+b[i];
if(x1[i]==bndl[i] && g>0.0)
g=0;
if(x1[i]==bndu[i] && g<0.0)
g=0;
gnorm+=CMath::Sqr(g);
CAp::SetErrorFlag(waserrors,x1[i]<bndl[i],"testminqpunit.ap:1215");
CAp::SetErrorFlag(waserrors,x1[i]>bndu[i],"testminqpunit.ap:1216");
}
gnorm=MathSqrt(gnorm);
CAp::SetErrorFlag(waserrors,gnorm>eps,"testminqpunit.ap:1219");
}
}
//--- Linear (zero-quadratic) test:
//--- * N dimensions, N>=1
//--- * box constraints, x[i] in [-1,+1]
//--- * A = 0
//--- * random B with normal entries
//--- * initial point is random, feasible
//--- We perform two tests:
//--- * unconstrained problem must be recognized as unbounded
//--- * constrained problem can be successfully solved
//--- NOTE: we may explicitly set zero A, or assume that by
//--- default it is zero. During test we will try both
//--- ways.
eps=1.0E-4;
for(n=1; n<=10; n++)
{
for(pass=1; pass<=10; pass++)
{
//--- Generate problem
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
do
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
}
while(b[i]==0.0);
bndl.Set(i,-1);
bndu.Set(i,1);
x0.Set(i,2*CHighQualityRand::HQRndUniformR(rs)-1);
}
//--- Solve problem:
//--- * without constraints we expect failure
//--- * with constraints algorithm must succeed
CMinQP::MinQPCreate(n,state);
SetRandomAlgoSemiDefinite(state,bctol,lctol);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetStartingPoint(state,x0);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
a=matrix<double>::Zeros(n,n);
CMinQP::MinQPSetQuadraticTerm(state,a,true);
}
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype!=-4 && rep.m_terminationtype!=-2,"testminqpunit.ap:1278");
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminqpunit.ap:1283");
if(rep.m_terminationtype<=0)
return;
//--- Test - calculate constrained gradient at solution,
//--- check its norm.
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(waserrors,b[i]>0.0 && x1[i]>(bndl[i]+bctol),"testminqpunit.ap:1293");
CAp::SetErrorFlag(waserrors,b[i]<0.0 && x1[i]<(bndu[i]-bctol),"testminqpunit.ap:1294");
}
}
}
}
//+------------------------------------------------------------------+
//| This function tests equality constrained quadratic programming |
//| algorithm. |
//| Returns True on errors. |
//+------------------------------------------------------------------+
bool CTestMinQPUnit::ECQPTest(void)
{
//--- create variables
bool result;
int n=0;
int k=0;
CMatrixDouble a;
CMatrixDouble q;
CMatrixDouble c;
CMatrixDouble a2;
CRowDouble b;
CRowDouble b2;
CRowDouble xstart;
CRowDouble xstart2;
CRowDouble xend;
CRowDouble xend2;
CRowDouble x0;
CRowDouble x1;
CRowDouble xd;
CRowDouble xs;
CRowDouble tmp;
CRowDouble g;
CRowDouble bndl;
CRowDouble bndu;
CRowDouble xorigin;
CRowInt ct;
double eps=0;
double theta=0;
double f0=0;
double f1=0;
CMinQPState state;
CMinQPState state2;
CMinQPReport rep;
int i=0;
int j=0;
int pass=0;
int rk=0;
double v=0;
int aulits=0;
bool waserrors=false;
int i_=0;
//--- First test:
//--- * N*N identity A
//--- * K<N equality constraints Q*x = Q*x0, where Q is random
//--- orthogonal K*N matrix, x0 is some random vector
//--- * x1 is some random vector such that Q*x1=0. It is always possible
//--- to find such x1, because K<N
//--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x
//--- * exact solution must be equal to x0
eps=1.0E-4;
for(n=2; n<=6; n++)
{
for(k=1; k<n; k++)
{
//--- Generate problem: A, b, CMatrix, x0, XStart
CMatGen::RMatrixRndOrthogonal(n,q);
a=matrix<double>::Identity(n,n);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
x1=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
x1.Set(i,x0[i]);
xstart.Set(i,2*CMath::RandomReal()-1);
}
for(i=0; i<k; i++)
{
for(i_=0; i_<n; i_++)
c.Set(i,i_,q.Get(i,i_));
v=CAblasF::RDotVR(n,x0,q,i);
c.Set(i,n,v);
ct.Set(i,0);
v=2*CMath::RandomReal()-1;
x1+=q[i]*v;
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,x1,a,i);
b.Set(i,-v);
}
//--- Create optimizer, solve
CMinQP::MinQPCreate(n,state);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetLC(state,c,ct,k);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend,rep);
//--- Compare with analytic solution
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1400");
continue;
}
for(i=0; i<n; i++)
CAp::SetErrorFlag(waserrors,MathAbs(xend[i]-x0[i])>eps,"testminqpunit.ap:1404");
}
}
//--- Second test:
//--- * N*N SPD A
//--- * K<N equality constraints Q*x = Q*x0, where Q is random
//--- orthogonal K*N matrix, x0 is some random vector
//--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
//--- where x1 is some random vector
//--- * we check feasibility properties of the solution
//--- * we do not know analytic form of the exact solution,
//--- but we know that projection of gradient into equality constrained
//--- subspace must be zero at the solution
eps=1.0E-4;
for(n=2; n<=6; n++)
{
for(k=1; k<n; k++)
{
//--- Generate problem: A, b, CMatrix, x0, XStart
CMatGen::RMatrixRndOrthogonal(n,q);
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
x1=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
x1.Set(i,2*CMath::RandomReal()-1);
xstart.Set(i,2*CMath::RandomReal()-1);
}
for(i=0; i<k; i++)
{
for(i_=0; i_<n; i_++)
c.Set(i,i_,q.Get(i,i_));
v=CAblasF::RDotVR(n,x0,q,i);
c.Set(i,n,v);
ct.Set(i,0);
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,x1,a,i);
b.Set(i,-v);
}
//--- Create optimizer, solve
CMinQP::MinQPCreate(n,state);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetLC(state,c,ct,k);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend,rep);
//--- Calculate gradient, check projection
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1472");
continue;
}
for(i=0; i<k; i++)
{
v=CAblasF::RDotVR(n,xend,c,i);
CAp::SetErrorFlag(waserrors,MathAbs(v-c.Get(i,n))>eps,"testminqpunit.ap:1478");
}
g=b;
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xend,a,i);
g.Add(i,v);
}
for(i=0; i<k; i++)
{
v=CAblasF::RDotVR(n,g,c,i);
for(i_=0; i_<n; i_++)
g.Add(i_,- v*c.Get(i,i_));
}
v=CAblasF::RDotV2(n,g);
CAp::SetErrorFlag(waserrors,MathSqrt(v)>eps,"testminqpunit.ap:1493");
}
}
//--- Boundary and linear equality constrained QP problem:
//--- * N*N SPD A with moderate condtion number (up to 100)
//--- * boundary constraints 0<=x[i]<=1
//--- * K<N equality constraints C*x = C*x0, where Q is random
//--- K*N matrix, x0 is some random vector from the
//--- feasible hypercube (0<=x0[i]<=1)
//--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
//--- where x1 is some random vector with -1<=x1[i]<=+1.
//--- (sometimes solution is in the inner area, sometimes at the boundary)
//--- * every component of the initial point XStart is either 0 or 1
//--- (point is located at the vertices of the feasible hypercube)
//--- Solution of such problem is calculated using two methods:
//--- a) boundary and linearly constrained QP
//--- b) augmented Lagrangian boundary constrained QP: we add explicit quadratic
//--- penalty to the problem; we also add Lagrangian terms and perform many
//--- subsequent iterations to find good estimates of the Lagrange multipliers.
//--- Sometimes augmented Largangian converges to slightly different point
//--- (boundary constraints lead to extremely slow, non-smooth convergence of
//--- the Lagrange multipliers). In order to correctly handle such situations
//--- we compare function values instead of final points - and use relaxed criteria
//--- to test for convergence.
//--- NOTE: sometimes we need as much as 300 Augmented Lagrangian iterations for
//--- method to converge.
eps=5.0E-2;
theta=1.0E+4;
aulits=300;
for(n=4; n<=6; n++)
{
for(k=1; k<=n-3; k++)
{
//--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
x1=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,CMath::RandomReal());
x1.Set(i,2*CMath::RandomReal()-1);
bndl.Set(i,0.0);
bndu.Set(i,1.0);
xstart.Set(i,CMath::RandomInteger(2));
}
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
c.Set(i,j,2*CMath::RandomReal()-1);
v=CAblasF::RDotVR(n,x0,c,i);
c.Set(i,n,v);
ct.Set(i,0);
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,x1,a,i);
b.Set(i,-v);
}
//--- Create exact optimizer, solve
CMinQP::MinQPCreate(n,state);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPSetLC(state,c,ct,k);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1580");
continue;
}
//--- Solve problem using barrier functions (quadrative objective, boundary constraints,
//--- explicit penalty term added to the main quadratic matrix. Lagrangian terms improve
//--- solution quality):
//--- * A2 := A+C'*C
//--- * b2 := b-r'*C
//--- * b2 is iteratively updated using augmented Lagrangian update
//--- NOTE: we may need many outer iterations to converge to the optimal values
//--- of Lagrange multipliers. Convergence is slowed down by the presense
//--- of boundary constraints, whose activation/deactivation slows down
//--- process.
a2=matrix<double>::Zeros(n,n);
CAblas::RMatrixCopy(n,n,a,0,0,a2,0,0);
CAblas::RMatrixSyrk(n,k,theta,c,0,0,2,1.0,a2,0,0,true);
for(i=0; i<n; i++)
{
for(j=i+1; j<n; j++)
a2.Set(j,i,a2.Get(i,j));
}
b2=b;
for(i=0; i<k; i++)
{
v=c.Get(i,n)*theta;
for(i_=0; i_<n; i_++)
b2.Add(i_,- v*c.Get(i,i_));
}
CMinQP::MinQPCreate(n,state2);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetQuadraticTerm(state2,a2,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state2,xstart);
CMinQP::MinQPSetBC(state2,bndl,bndu);
for(i=1; i<=aulits; i++)
{
//--- Solve, update B2 according to augmented Lagrangian algorithm
CMinQP::MinQPSetLinearTerm(state2,b2);
CMinQP::MinQPOptimize(state2);
CMinQP::MinQPResults(state2,xend2,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1627");
continue;
}
for(j=0; j<k; j++)
{
v=CAblasF::RDotVR(n,xend2,c,j);
v=theta*(v-c.Get(j,n));
for(i_=0; i_<n; i_++)
b2.Add(i_,v*c.Get(j,i_));
}
}
//--- Calculate function value and XEnd and XEnd2
f0=0.0;
f1=0.0;
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
f0+=0.5*xend[i]*a.Get(i,j)*xend[j];
f1+=0.5*xend2[i]*a.Get(i,j)*xend2[j];
}
f0+=xend[i]*b[i];
f1+=xend2[i]*b[i];
}
//--- Check feasibility properties and compare
CAp::SetErrorFlag(waserrors,MathAbs(f0-f1)>eps,"testminqpunit.ap:1657");
for(i=0; i<k; i++)
{
v=CAblasF::RDotVR(n,xend,c,i);
CAp::SetErrorFlag(waserrors,MathAbs(v-c.Get(i,n))>(1.0E6*CMath::m_machineepsilon),"testminqpunit.ap:1661");
}
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(waserrors,xend[i]<0.0,"testminqpunit.ap:1665");
CAp::SetErrorFlag(waserrors,xend[i]>1.0,"testminqpunit.ap:1666");
}
}
}
//--- Boundary and linear equality constrained QP problem,
//--- test for correct handling of non-zero XOrigin:
//--- * N*N SPD A with moderate condtion number (up to 100)
//--- * boundary constraints 0<=x[i]<=1
//--- * K<N equality constraints Q*x = Q*x0, where Q is random
//--- orthogonal K*N matrix, x0 is some random vector from the
//--- inner area of the feasible hypercube (0.1<=x0[i]<=0.9)
//--- * optimization problem has form 0.5*(x-xorigin)'*A*(x-xorigin)+b*(x-xorigin),
//--- where b is some random vector with -1<=b[i]<=+1.
//--- (sometimes solution is in the inner area, sometimes at the boundary)
//--- * every component of the initial point XStart is random from [-1,1]
//--- Solution of such problem is calculated using two methods:
//--- a) QP with SetOrigin() call
//--- b) QP with XOrigin explicitly added to the quadratic function,
//--- Both methods should give same results; any significant difference is
//--- evidence of some error in the QP implementation.
eps=1.0E-4;
for(n=2; n<=6; n++)
{
for(k=1; k<n; k++)
{
//--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart.
//--- Additionally, we compute modified b: b2 = b-xorigin'*A
CMatGen::RMatrixRndOrthogonal(n,q);
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a);
b=vector<double>::Zeros(n);
b2=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
x1=vector<double>::Zeros(n);
xorigin=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,0.1+0.8*CMath::RandomReal());
b.Set(i,2*CMath::RandomReal()-1);
bndl.Set(i,0.0);
bndu.Set(i,1.0);
xstart.Set(i,CMath::RandomInteger(2));
xorigin.Set(i,2*CMath::RandomReal()-1);
}
for(i=0; i<k; i++)
{
for(i_=0; i_<n; i_++)
c.Set(i,i_,q.Get(i,i_));
v=CAblasF::RDotVR(n,x0,q,i);
c.Set(i,n,v);
ct.Set(i,0);
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xorigin,a,i);
b2.Set(i,b[i]-v);
}
//--- Solve with SetOrigin() call
CMinQP::MinQPCreate(n,state);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetOrigin(state,xorigin);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPSetLC(state,c,ct,k);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1750");
continue;
}
//--- Solve problem using explicit origin
CMinQP::MinQPCreate(n,state2);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state2,b2);
CMinQP::MinQPSetQuadraticTerm(state2,a,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state2,xstart);
CMinQP::MinQPSetBC(state2,bndl,bndu);
CMinQP::MinQPSetLC(state2,c,ct,k);
CMinQP::MinQPOptimize(state2);
CMinQP::MinQPResults(state2,xend2,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1768");
continue;
}
//--- Check feasibility properties and compare solutions
for(i=0; i<k; i++)
{
v=CAblasF::RDotVR(n,xend,c,i);
CAp::SetErrorFlag(waserrors,MathAbs(v-c.Get(i,n))>eps,"testminqpunit.ap:1778");
}
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(waserrors,(double)(MathAbs(xend[i]-xend2[i]))>eps,"testminqpunit.ap:1782");
CAp::SetErrorFlag(waserrors,xend[i]<0.0,"testminqpunit.ap:1783");
CAp::SetErrorFlag(waserrors,xend[i]>1.0,"testminqpunit.ap:1784");
}
}
}
//--- Boundary and linear equality constrained QP problem with excessive
//--- equality constraints:
//--- * N*N SPD A with moderate condtion number (up to 100)
//--- * boundary constraints 0<=x[i]<=1
//--- * K=2*N equality constraints Q*x = Q*x0, where Q is random matrix,
//--- x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9)
//--- * optimization problem has form 0.5*x'*A*x-b*x,
//--- where b is some random vector
//--- * because constraints are excessive, the main problem is to find
//--- feasible point; the only existing feasible point is solution,
//--- so we have to check only feasibility
eps=1.0E-4;
for(n=1; n<=6; n++)
{
//--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart
k=2*n;
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
x1=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,0.1+0.8*CMath::RandomReal());
x1.Set(i,2*CMath::RandomReal()-1);
bndl.Set(i,0.0);
bndu.Set(i,1.0);
xstart.Set(i,CMath::RandomInteger(2));
}
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
c.Set(i,j,2*CMath::RandomReal()-1);
v=CAblasF::RDotVR(n,x0,c,i);
c.Set(i,n,v);
ct.Set(i,0);
}
for(i=0; i<n; i++)
b.Set(i,2*CMath::RandomReal()-1);
//--- Create optimizer, solve
CMinQP::MinQPCreate(n,state);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPSetLC(state,c,ct,k);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend,rep);
//--- Check feasibility properties of the solution
//--- NOTE: we do not check termination type because some solvers (IPM) may return feasible X even with negative code
if(CAp::Len(xend)!=n)
{
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1858");
continue;
}
for(i=0; i<k; i++)
{
v=CAblasF::RDotVR(n,xend,c,i);
CAp::SetErrorFlag(waserrors,MathAbs(v-c.Get(i,n))>eps,"testminqpunit.ap:1864");
}
}
//--- Boundary and linear equality constrained QP problem,
//--- test checks that different starting points yield same final point:
//--- * random N from [1..6], random K from [1..2*N]
//--- * N*N SPD A with moderate condtion number (up to 100)
//--- * boundary constraints 0<=x[i]<=1
//--- * K<N random linear equality constraints C*x = C*x0,
//--- where x0 is some random vector from the inner area of the
//--- feasible hypercube (0.1<=x0[i]<=0.9)
//--- * optimization problem has form 0.5*x'*A*x+b*x,
//--- where b is some random vector with -5<=b[i]<=+5
//--- * every component of the initial point XStart is random from [-2,+2]
//--- * we perform two starts from random different XStart and compare values
//--- of the target function (although final points may be slightly different,
//--- function values should match each other)
//--- Both points should give same results; any significant difference is
//--- evidence of some error in the QP implementation.
eps=1.0E-4;
for(pass=1; pass<=50; pass++)
{
//--- Generate problem: N, K, A, b, BndL, BndU, CMatrix, x0, x1, XStart.
n=CMath::RandomInteger(5)+2;
k=CMath::RandomInteger(n-1)+1;
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a);
b=vector<double>::Zeros(n);
b2=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
xstart2=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,0.1+0.8*CMath::RandomReal());
b.Set(i,2*CMath::RandomReal()-1);
bndl.Set(i,0.0);
bndu.Set(i,1.0);
xstart.Set(i,4*CMath::RandomReal()-2);
xstart2.Set(i,4*CMath::RandomReal()-2);
}
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
c.Set(i,j,2*CMath::RandomReal()-1);
v=CAblasF::RDotVR(n,x0,c,i);
c.Set(i,n,v);
ct.Set(i,0);
}
//--- Solve with XStart
CMinQP::MinQPCreate(n,state);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPSetLC(state,c,ct,k);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1938");
continue;
}
//--- Solve with XStart2
CMinQP::MinQPSetStartingPoint(state,xstart2);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend2,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:1950");
continue;
}
//--- Calculate function value and XEnd and XEnd2, compare solutions
f0=0.0;
f1=0.0;
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
f0+=0.5*xend[i]*a.Get(i,j)*xend[j];
f1+=0.5*xend2[i]*a.Get(i,j)*xend2[j];
}
f0+=xend[i]*b[i];
f1+=xend2[i]*b[i];
}
CAp::SetErrorFlag(waserrors,MathAbs(f0-f1)>eps,"testminqpunit.ap:1969");
}
//--- Test ability to correctly handle situation where algorithm
//--- either:
//--- (1) starts from point with gradient whose projection to
//--- active set is almost zero (but not exactly zero)
//--- (2) performs step to such point
//--- In order to do this we solve problem
//--- * min 0.5*x'*x - (x0+c)'*x
//--- * subject to c'*x = c'*x0, with c and x0 random unit vectors
//--- * with initial point xs = x0+r*xd, where r is scalar,
//--- xd is vector which is orthogonal to c.
//--- * we try different r=power(2,-rk) for rk=0...70. The idea
//--- is that as we approach closer and closer to x0, which is
//--- a solution of the constrained problem, constrained gradient
//--- of the function rapidly vanishes.
eps=1.0E-6;
for(rk=0; rk<=70; rk++)
{
n=10;
//--- Generate x0, c, xd, xs, generate unit A
CApServ::RandomUnit(n,x0);
CApServ::RandomUnit(n,xd);
CApServ::RandomUnit(n,tmp);
c=matrix<double>::Zeros(1,n+1);
ct.Resize(1);
xs=vector<double>::Zeros(n);
b=vector<double>::Zeros(n);
ct.Set(0,0);
v=0;
for(i=0; i<n; i++)
{
c.Set(0,i,tmp[i]);
c.Add(0,n,tmp[i]*x0[i]);
b.Set(i,-(x0[i]+tmp[i]));
v+=tmp[i]*xd[i];
}
for(i=0; i<n; i++)
{
xd.Add(i,-v*tmp[i]);
xs.Set(i,x0[i]+xd[i]*MathPow(2,-rk));
}
a=matrix<double>::Identity(n,n);
//--- Create and solve optimization problem
CMinQP::MinQPCreate(n,state);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,true);
CMinQP::MinQPSetStartingPoint(state,xs);
CMinQP::MinQPSetLC(state,c,ct,1);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(waserrors,true,"testminqpunit.ap:2040");
continue;
}
for(i=0; i<n; i++)
CAp::SetErrorFlag(waserrors,MathAbs(xend[i]-x0[i])>eps,"testminqpunit.ap:2044");
v=CAblasF::RDotVR(n,xend,c,0)-c.Get(0,n);
CAp::SetErrorFlag(waserrors,MathAbs(v)>(1.0E5*CMath::m_machineepsilon),"testminqpunit.ap:2048");
}
//--- return result
result=waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| This function tests inequality constrained quadratic programming |
//| algorithm. |
//| On failure sets Err to True; on success leaves it unchanged. |
//+------------------------------------------------------------------+
void CTestMinQPUnit::ICQPTest(bool &err)
{
//--- create variables
int n=0;
int k=0;
CMatrixDouble a;
CMatrixDouble q;
CMatrixDouble c;
CMatrixDouble a2;
CMatrixDouble t2;
CMatrixDouble t3;
CMatrixDouble ce;
CRowDouble xs0;
CRowDouble bl;
CRowDouble bu;
CRowDouble tmp;
CRowDouble x;
CRowDouble xstart;
CRowDouble xstart2;
CRowDouble xend;
CRowDouble xend2;
CRowDouble x0;
CRowDouble x1;
CRowDouble b;
CRowDouble b2;
CRowDouble g;
CRowDouble bndl;
CRowDouble bndu;
CRowDouble xorigin;
CRowDouble tmp0;
CRowDouble tmp1;
CRowDouble da;
CRowInt ct;
bool nonnegative[];
double eps=0;
CMinQPState state;
CMinQPState state2;
CMinQPReport rep;
CMinQPReport rep2;
int i=0;
int j=0;
int pass=0;
double v=0;
double vv=0;
double f0=0;
double f1=0;
double tolconstr=0;
int bscale=0;
int akind=0;
int shiftkind=0;
int ccnt=0;
CHighQualityRandState rs;
CSNNLSSolver nnls;
bool isnonconvex;
int i_=0;
CHighQualityRand::HQRndRandomize(rs);
//--- Inequality constrained problem:
//--- * N*N diagonal A
//--- * one inequality constraint q'*x>=0, where q is random unit vector
//--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
//--- where x1 is some random vector
//--- * either:
//--- a) x1 is feasible => we must Stop at x1
//--- b) x1 is infeasible => we must Stop at the boundary q'*x=0 and
//--- projection of gradient onto q*x=0 must be zero
//--- NOTE: we make several passes because some specific kind of errors is rarely
//--- caught by this test, so we need several repetitions.
eps=1.0E-4;
for(n=2; n<=6; n++)
{
for(pass=0; pass<=4; pass++)
{
//--- Generate problem: A, b, CMatrix, x0, XStart
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a);
b=vector<double>::Zeros(n);
x1=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
c=matrix<double>::Zeros(1,n+1);
ct.Resize(1);
for(i=0; i<n; i++)
{
x1.Set(i,2*CMath::RandomReal()-1);
xstart.Set(i,2*CMath::RandomReal()-1);
}
do
{
v=0;
for(i=0; i<n; i++)
{
c.Set(0,i,2*CMath::RandomReal()-1);
v+=CMath::Sqr(c.Get(0,i));
}
v=MathSqrt(v);
}
while(v==0.0);
for(i=0; i<n; i++)
c.Mul(0,i,1.0/v);
c.Set(0,n,0);
ct.Set(0,1);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,x1,a,i);
b.Set(i,-v);
}
//--- Create optimizer, solve
CMinQP::MinQPCreate(n,state);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetLC(state,c,ct,1);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend,rep);
//--- Test
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2160");
continue;
}
v=CAblasF::RDotVR(n,x1,c,0);
if(v>=0.0)
{
//--- X1 is feasible, compare target function values at XEnd and X1
for(i=0; i<n; i++)
CAp::SetErrorFlag(err,MathAbs(xend[i]-x1[i])>eps,"testminqpunit.ap:2173");
}
else
{
//--- X1 is infeasible:
//--- * XEnd must be approximately feasible
//--- * gradient projection onto c'*x=0 must be zero
v=CAblasF::RDotVR(n,xend,c,0);
CAp::SetErrorFlag(err,v<(-eps),"testminqpunit.ap:2183");
g=b;
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xend,a,i);
g.Add(i,v);
}
v=CAblasF::RDotVR(n,g,c,0);
for(i_=0; i_<n; i_++)
g.Add(i_,- v*c.Get(0,i_));
v=CAblasF::RDotV2(n,g);
CAp::SetErrorFlag(err,MathSqrt(v)>eps,"testminqpunit.ap:2194");
}
}
}
//--- Boundary and linear equality/inequality constrained QP problem,
//--- test for correct handling of non-zero XOrigin:
//--- * N*N SPD A with moderate condtion number (up to 100)
//--- * boundary constraints 0<=x[i]<=1
//--- * K<N linear equality/inequality constraints Q*x = Q*x0, where
//--- Q is random orthogonal K*N matrix, x0 is some random vector from the
//--- inner area of the feasible hypercube (0.1<=x0[i]<=0.9)
//--- * optimization problem has form 0.5*(x-xorigin)'*A*(x-xorigin)+b*(x-xorigin),
//--- where b is some random vector with -1<=b[i]<=+1.
//--- (sometimes solution is in the inner area, sometimes at the boundary)
//--- * every component of the initial point XStart is random from [-1,1]
//--- Solution of such problem is calculated using two methods:
//--- a) QP with SetOrigin() call
//--- b) QP with XOrigin explicitly added to the quadratic function,
//--- Both methods should give same results; any significant difference is
//--- evidence of some error in the QP implementation.
eps=1.0E-4;
for(n=2; n<=6; n++)
{
for(k=1; k<n; k++)
{
//--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart.
//--- Additionally, we compute modified b: b2 = b-xorigin'*A
CMatGen::RMatrixRndOrthogonal(n,q);
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a);
b=vector<double>::Zeros(n);
b2=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
x1=vector<double>::Zeros(n);
xorigin=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,0.1+0.8*CMath::RandomReal());
b.Set(i,2*CMath::RandomReal()-1);
bndl.Set(i,0.0);
bndu.Set(i,1.0);
xstart.Set(i,CMath::RandomInteger(2));
xorigin.Set(i,2*CMath::RandomReal()-1);
}
for(i=0; i<k; i++)
{
for(i_=0; i_<n; i_++)
c.Set(i,i_,q.Get(i,i_));
v=CAblasF::RDotVR(n,x0,q,i);
c.Set(i,n,v);
ct.Set(i,CMath::RandomInteger(3)-1);
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xorigin,a,i);
b2.Set(i,b[i]-v);
}
//--- Solve with SetOrigin() call
CMinQP::MinQPCreate(n,state);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetOrigin(state,xorigin);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPSetLC(state,c,ct,k);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2278");
continue;
}
//--- Solve problem using explicit origin
CMinQP::MinQPCreate(n,state2);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state2,b2);
CMinQP::MinQPSetQuadraticTerm(state2,a,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state2,xstart);
CMinQP::MinQPSetBC(state2,bndl,bndu);
CMinQP::MinQPSetLC(state2,c,ct,k);
CMinQP::MinQPOptimize(state2);
CMinQP::MinQPResults(state2,xend2,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2296");
continue;
}
//--- Calculate function value and XEnd and XEnd2
f0=0.0;
f1=0.0;
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
f0+=0.5*(xend[i]-xorigin[i])*a.Get(i,j)*(xend[j]-xorigin[j]);
f1+=0.5*(xend2[i]-xorigin[i])*a.Get(i,j)*(xend2[j]-xorigin[j]);
}
f0+=(xend[i]-xorigin[i])*b[i];
f1+=(xend2[i]-xorigin[i])*b[i];
}
//--- Check feasibility properties and compare solutions
for(i=0; i<k; i++)
{
v=CAblasF::RDotVR(n,xend,c,i);
if(ct[i]==0)
CAp::SetErrorFlag(err,MathAbs(v-c.Get(i,n))>eps,"testminqpunit.ap:2323");
if(ct[i]>0)
CAp::SetErrorFlag(err,v<(c.Get(i,n)-eps),"testminqpunit.ap:2325");
if(ct[i]<0)
CAp::SetErrorFlag(err,v>(c.Get(i,n)+eps),"testminqpunit.ap:2327");
}
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(err,MathAbs(xend[i]-xend2[i])>eps,"testminqpunit.ap:2331");
CAp::SetErrorFlag(err,xend[i]<0.0,"testminqpunit.ap:2332");
CAp::SetErrorFlag(err,xend[i]>1.0,"testminqpunit.ap:2333");
}
}
}
//--- Boundary constraints vs linear ones:
//--- * N*N SPD A
//--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
//--- where x1 is some random vector from [-1,+1]
//--- * K=2*N constraints of the form ai<=x[i] or x[i]<=b[i],
//--- with ai in [-1.0,-0.1], bi in [+0.1,+1.0]
//--- * initial point xstart is from [-1,+2]
//--- * we solve two related QP problems:
//--- a) one with constraints posed as boundary ones
//--- b) another one with same constraints posed as general linear ones
//--- both problems must have same solution.
//--- Here we test that boundary constrained and linear inequality constrained
//--- solvers give same results.
eps=1.0E-3;
for(n=1; n<=6; n++)
{
//--- Generate problem: A, b, x0, XStart, C, CT
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a);
b=vector<double>::Zeros(n);
x1=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
c=matrix<double>::Zeros(2*n,n+1);
ct.Resize(2*n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x1.Set(i,2*CMath::RandomReal()-1);
xstart.Set(i,3*CMath::RandomReal()-1);
bndl.Set(i,-(0.1+0.9*CMath::RandomReal()));
bndu.Set(i,0.1+0.9*CMath::RandomReal());
c.Set(2*i,i,1);
c.Set(2*i,n,bndl[i]);
ct.Set(2*i,1);
c.Set(2*i+1,i,1);
c.Set(2*i+1,n,bndu[i]);
ct.Set(2*i+1,-1);
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,x1,a,i);
b.Set(i,-v);
}
//--- Solve linear inequality constrained problem
CMinQP::MinQPCreate(n,state);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetLC(state,c,ct,2*n);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend,rep);
//--- Solve boundary constrained problem
CMinQP::MinQPCreate(n,state2);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state2,b);
CMinQP::MinQPSetQuadraticTerm(state2,a,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state2,xstart);
CMinQP::MinQPSetBC(state2,bndl,bndu);
CMinQP::MinQPOptimize(state2);
CMinQP::MinQPResults(state2,xend2,rep2);
//--- Calculate gradient, check projection
if(rep.m_terminationtype<=0 || rep2.m_terminationtype<=0)
{
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2421");
continue;
}
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(err,xend[i]<(bndl[i]-eps),"testminqpunit.ap:2426");
CAp::SetErrorFlag(err,xend[i]>(bndu[i]+eps),"testminqpunit.ap:2427");
CAp::SetErrorFlag(err,MathAbs(xend[i]-xend2[i])>eps,"testminqpunit.ap:2428");
}
}
//--- Boundary 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(n=1; n<=5; n++)
{
//--- Generate X, BL, BU.
a=matrix<double>::Identity(n,n);
b=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
c=matrix<double>::Zeros(2*n,n+1);
ct.Resize(2*n);
for(i=0; i<n; i++)
{
xstart.Set(i,CMath::RandomReal());
x0.Set(i,3*CMath::RandomReal()-1);
b.Set(i,-x0[i]);
c.Set(2*i,i,1);
c.Set(2*i,n,0);
ct.Set(2*i,1);
c.Set(2*i+1,i,1);
c.Set(2*i+1,n,1);
ct.Set(2*i+1,-1);
}
//--- Create and optimize
CMinQP::MinQPCreate(n,state);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLC(state,c,ct,2*n);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2497");
continue;
}
//--- * compare solution with analytic one
//--- * check feasibility
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(err,MathAbs(xend[i]-CApServ::BoundVal(x0[i],0.0,1.0))>0.05,"testminqpunit.ap:2507");
CAp::SetErrorFlag(err,xend[i]<(-1.0E-6),"testminqpunit.ap:2508");
CAp::SetErrorFlag(err,xend[i]>(1.0+1.0E-6),"testminqpunit.ap:2509");
}
}
//--- Boundary and linear equality/inequality constrained QP problem with
//--- excessive constraints:
//--- * N*N SPD A with moderate condtion number (up to 100)
//--- * boundary constraints 0<=x[i]<=1
//--- * K=2*N equality/inequality constraints Q*x = Q*x0, where Q is random matrix,
//--- x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9)
//--- * optimization problem has form 0.5*x'*A*x-b*x,
//--- where b is some random vector
//--- * because constraints are excessive, the main problem is to find
//--- feasible point; usually, the only existing feasible point is solution,
//--- so we have to check only feasibility
eps=1.0E-4;
for(n=1; n<=6; n++)
{
//--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart
k=2*n;
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
x1=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,0.1+0.8*CMath::RandomReal());
x1.Set(i,2*CMath::RandomReal()-1);
bndl.Set(i,0.0);
bndu.Set(i,1.0);
xstart.Set(i,CMath::RandomInteger(2));
}
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
c.Set(i,j,2*CMath::RandomReal()-1);
v=CAblasF::RDotVR(n,x0,c,i);
ct.Set(i,CMath::RandomInteger(3)-1);
if(ct[i]==0)
c.Set(i,n,v);
if(ct[i]>0)
c.Set(i,n,v-1.0E-3);
if(ct[i]<0)
c.Set(i,n,v+1.0E-3);
}
for(i=0; i<n; i++)
b.Set(i,2*CMath::RandomReal()-1);
//--- Create optimizer, solve
CMinQP::MinQPCreate(n,state);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPSetLC(state,c,ct,k);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend,rep);
//--- Check feasibility properties of the solution
//--- NOTE: we do not check termination type because some solvers (IPM) may return feasible X even with negative code
if(CAp::Len(xend)!=n)
{
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2588");
continue;
}
for(i=0; i<k; i++)
{
v=CAblasF::RDotVR(n,xend,c,i);
if(ct[i]==0)
CAp::SetErrorFlag(err,MathAbs(v-c.Get(i,n))>eps,"testminqpunit.ap:2595");
if(ct[i]>0)
CAp::SetErrorFlag(err,v<(c.Get(i,n)-eps),"testminqpunit.ap:2597");
if(ct[i]<0)
CAp::SetErrorFlag(err,v>(c.Get(i,n)+eps),"testminqpunit.ap:2599");
}
}
//--- General inequality constrained problem:
//--- * N*N SPD diagonal A with moderate condtion number
//--- * no boundary constraints
//--- * K=N inequality constraints C*x >= C*x0, where C is N*N well conditioned
//--- matrix, x0 is some random vector [-1,+1]
//--- * optimization problem has form 0.5*x'*A*x-b'*x,
//--- where b is random vector from [-1,+1]
//--- * using duality, we can obtain solution of QP problem as follows:
//--- a) intermediate problem min(0.5*y'*B*y + d'*y) s.t. y>=0
//--- is solved, where B = C*inv(A)*C', d = -(C*inv(A)*b + C*x0)
//--- b) after we got dual solution ys, we calculate primal solution
//--- xs = inv(A)*(C'*ys-b)
eps=1.0E-3;
for(n=1; n<=6; n++)
{
//--- Generate problem
da=vector<double>::Zeros(n);
a=matrix<double>::Zeros(n,n);
CMatGen::RMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),t2);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
c=matrix<double>::Zeros(n,n+1);
ct.Resize(n);
for(i=0; i<n; i++)
{
da.Set(i,MathExp(8*CMath::RandomReal()-4));
a.Set(i,i,da[i]);
}
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
b.Set(i,2*CMath::RandomReal()-1);
xstart.Set(i,2*CMath::RandomReal()-1);
}
for(i=0; i<n; i++)
{
for(i_=0; i_<n; i_++)
c.Set(i,i_,t2.Get(i,i_));
v=CAblasF::RDotVR(n,x0,c,i);
c.Set(i,n,v);
ct.Set(i,1);
}
//--- Solve primal problem, check feasibility
CMinQP::MinQPCreate(n,state);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetLC(state,c,ct,n);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2665");
continue;
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xend,c,i);
CAp::SetErrorFlag(err,v<(double)(c.Get(i,n)-eps),"testminqpunit.ap:2671");
}
//--- Generate dual problem:
//--- * A2 stores new quadratic term
//--- * B2 stores new linear term
//--- * BndL/BndU store boundary constraints
t3=matrix<double>::Zeros(n,n);
a2=matrix<double>::Zeros(n,n);
CAblas::RMatrixTranspose(n,n,c,0,0,t3,0,0);
for(i=0; i<n; i++)
{
v=1/MathSqrt(da[i]);
for(i_=0; i_<n; i_++)
t3.Mul(i,i_,v);
}
CAblas::RMatrixSyrk(n,n,1.0,t3,0,0,2,0.0,a2,0,0,true);
tmp0=b/da+0;
b2=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,tmp0,c,i);
b2.Set(i,-(v+c.Get(i,n)));
}
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
bndl.Set(i,0.0);
bndu.Set(i,AL_POSINF);
}
CMinQP::MinQPCreate(n,state2);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state2,b2);
CMinQP::MinQPSetQuadraticTerm(state2,a2,true);
CMinQP::MinQPSetBC(state2,bndl,bndu);
CMinQP::MinQPOptimize(state2);
CMinQP::MinQPResults(state2,xend2,rep2);
if(rep2.m_terminationtype<=0)
{
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2726");
continue;
}
for(i=0; i<n; i++)
{
v=0.0;
for(i_=0; i_<n; i_++)
v+=c.Get(i_,i)*xend2[i_];
tmp0.Set(i,(v-b[i])/da[i]);
}
for(i=0; i<n; i++)
CAp::SetErrorFlag(err,MathAbs(tmp0[i]-xend[i])>(eps*MathMax(MathAbs(tmp0[i]),1.0)),"testminqpunit.ap:2739");
}
//--- Boundary and linear equality/inequality constrained QP problem,
//--- test checks that different starting points yield same final point:
//--- * random N from [1..6], random K from [1..2*N]
//--- * N*N SPD A with moderate condtion number (up to 100)
//--- * boundary constraints 0<=x[i]<=1
//--- * K<2*N linear inequality constraints Q*x <= Q*x0, where
//--- Q is random K*N matrix, x0 is some random vector from the
//--- inner area of the feasible hypercube (0.1<=x0[i]<=0.9)
//--- * optimization problem has form 0.5*x'*A*x+b*x,
//--- where b is some random vector with -5<=b[i]<=+5
//--- * every component of the initial point XStart is random from [-2,+2]
//--- * we perform two starts from random different XStart and compare values
//--- of the target function (although final points may be slightly different,
//--- function values should match each other)
eps=1.0E-4;
for(pass=1; pass<=50; pass++)
{
//--- Generate problem: N, K, A, b, BndL, BndU, CMatrix, x0, x1, XStart.
n=CMath::RandomInteger(5)+2;
k=CMath::RandomInteger(2*n)+1;
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a);
b=vector<double>::Zeros(n);
b2=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
xstart2=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,0.1+0.8*CMath::RandomReal());
b.Set(i,2*CMath::RandomReal()-1);
bndl.Set(i,0.0);
bndu.Set(i,1.0);
xstart.Set(i,4*CMath::RandomReal()-2);
xstart2.Set(i,4*CMath::RandomReal()-2);
}
for(i=0; i<k; i++)
{
//--- Generate I-th row of C
//--- Avoid excessive (more than N/2) equality constraints.
for(j=0; j<n; j++)
c.Set(i,j,2*CMath::RandomReal()-1);
v=CAblasF::RDotVR(n,x0,c,i);
c.Set(i,n,v);
ct.Set(i,CMath::RandomInteger(3)-1);
if(i>=(0.5*n) && ct[i]==0)
ct.Set(i,1);
if(ct[i]<0)
c.Set(i,n,c.Get(i,n)+0.1);
if(ct[i]>0)
c.Set(i,n,c.Get(i,n)-0.1);
}
//--- Solve with XStart
//--- NOTE: we do not check termination type because some solvers (IPM) may return feasible X even with negative code
CMinQP::MinQPCreate(n,state);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPSetLC(state,c,ct,k);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend,rep);
if(CAp::Len(xend)!=n || !CApServ::IsFiniteVector(xend,n))
{
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2821");
continue;
}
//--- Solve with XStart2
CMinQP::MinQPSetStartingPoint(state,xstart2);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend2,rep);
if(CAp::Len(xend2)!=n || !CApServ::IsFiniteVector(xend2,n))
{
CAp::SetErrorFlag(err,true,"testminqpunit.ap:2834");
continue;
}
//--- Calculate function value and XEnd and XEnd2, compare solutions
f0=0.0;
f1=0.0;
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
f0+=0.5*xend[i]*a.Get(i,j)*xend[j];
f1+=0.5*xend2[i]*a.Get(i,j)*xend2[j];
}
f0+=xend[i]*b[i];
f1+=xend2[i]*b[i];
}
CAp::SetErrorFlag(err,MathAbs(f0-f1)>eps,"testminqpunit.ap:2853");
}
//--- Convex/nonconvex optimization problem with excessive constraints:
//--- * N=2..5
//--- * f = 0.5*x'*A*x+b'*x
//--- * b has normally distributed entries with scale 10^BScale
//--- * several kinds of A are tried: zero, well conditioned SPD, well conditioned indefinite, low rank SPD, low rank indefinite
//--- * box constraints: x[i] in [-1,+1]
//---*2^N "excessive" general linear constraints (v_k,x)<=(v_k,v_k)+v_shift,
//--- where v_k is one of 2^N vertices of feasible hypercube, v_shift is
//--- a shift parameter:
//--- * with zero v_shift such constraints are degenerate (each vertex has
//--- N box constraints and one "redundant" linear constraint)
//--- * with positive v_shift linear constraint is always inactive
//--- * with small (about machine epsilon) but negative v_shift,
//--- constraint is close to degenerate - but not exactly
//--- We check that constrained gradient is close to zero at solution.
//--- Box constraint is considered active if distance to boundary is less
//--- than TolConstr.
//--- NOTE: TolConstr must be large enough so it won't conflict with
//--- perturbation introduced by v_shift
tolconstr=1.0E-3;
for(n=2; n<=5; n++)
{
for(akind=0; akind<=4; akind++)
{
for(shiftkind=-5; shiftkind<=1; shiftkind++)
{
for(bscale=0; bscale>=-2; bscale--)
{
//--- Generate A, B and initial point
a=matrix<double>::Zeros(n,n);
b=vector<double>::Zeros(n);
x=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,MathPow(10,bscale)*CHighQualityRand::HQRndNormal(rs));
x.Set(i,CHighQualityRand::HQRndUniformR(rs)-0.5);
}
isnonconvex=false;
switch(akind)
{
case 1:
//--- Dense well conditioned SPD
CMatGen::SPDMatrixRndCond(n,50.0,a);
break;
case 2:
//--- Dense well conditioned indefinite
CMatGen::SMatrixRndCond(n,50.0,a);
isnonconvex=true;
break;
case 3:
//--- Low rank semidefinite
tmp=vector<double>::Zeros(n);
for(k=1; k<=MathMin(3,n-1); k++)
{
for(i=0; i<n; i++)
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
v=MathPow(2,CHighQualityRand::HQRndNormal(rs));
for(i=0; i<n; i++)
for(j=0; j<n; j++)
a.Add(i,j,v*tmp[i]*tmp[j]);
}
break;
case 4:
//--- Low rank indefinite
tmp=vector<double>::Zeros(n);
for(k=1; k<=MathMin(3,n-1); k++)
{
for(i=0; i<n; i++)
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
v=CHighQualityRand::HQRndNormal(rs);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
a.Add(i,j,v*tmp[i]*tmp[j]);
}
isnonconvex=true;
break;
}
//--- Generate constraints
bl=vector<double>::Full(n,-1.0);
bu=vector<double>::Full(n,1.0);
ccnt=(int)MathRound(MathPow(2,n));
c=matrix<double>::Zeros(ccnt,n+1);
ct.Resize(ccnt);
for(i=0; i<ccnt; i++)
{
ct.Set(i,-1);
k=i;
c.Set(i,n,MathSign(shiftkind)*MathPow(10,MathAbs(shiftkind))*CMath::m_machineepsilon);
for(j=0; j<n; j++)
{
c.Set(i,j,2*(k%2)-1);
c.Add(i,n,c.Get(i,j)*c.Get(i,j));
k=k/2;
}
}
//--- Create and optimize
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetStartingPoint(state,x);
if(isnonconvex)
SetRandomAlgoNonConvexLC(state);
else
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetBC(state,bl,bu);
CMinQP::MinQPSetLC(state,c,ct,ccnt);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xs0,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminqpunit.ap:3000");
if(err)
return;
//--- Evaluate gradient at solution and test
vv=0.0;
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xs0,a,i);
v+=b[i];
if(xs0[i]<=(bl[i]+tolconstr) && v>0.0)
v=0.0;
if(xs0[i]>=(bu[i]-tolconstr) && v<0.0)
v=0.0;
vv+=CMath::Sqr(v);
}
vv=MathSqrt(vv);
CAp::SetErrorFlag(err,vv>(1.0E-3),"testminqpunit.ap:3024");
}
}
}
}
//--- Convex/nonconvex optimization problem with combination of
//--- box and linear constraints:
//--- * N=2..8
//--- * f = 0.5*x'*A*x+b'*x
//--- * b has normally distributed entries with scale 10^BScale
//--- * several kinds of A are tried: zero, well conditioned SPD,
//--- well conditioned indefinite, low rank semidefinite, low rank indefinite
//--- * box constraints: x[i] in [-1,+1]
//--- * initial point x0 = [0 0 ... 0 0]
//--- * CCnt=min(3,N-1) general linear constraints of form (c,x)=0.
//--- random mix of equality/inequality constraints is tried.
//--- x0 is guaranteed to be feasible.
//--- We check that constrained gradient is close to zero at solution.
//--- Inequality constraint is considered active if distance to boundary
//--- is less than TolConstr. We use nonnegative least squares solver
//--- in order to compute constrained gradient.
tolconstr=1.0E-3;
for(n=2; n<=8; n++)
{
for(akind=0; akind<=4; akind++)
{
for(bscale=0; bscale>=-2; bscale--)
{
//--- Generate A, B and initial point
a=matrix<double>::Zeros(n,n);
b=vector<double>::Zeros(n);
x=vector<double>::Zeros(n);
for(i=0; i<n; i++)
b.Set(i,MathPow(10,bscale)*CHighQualityRand::HQRndNormal(rs));
isnonconvex=false;
switch(akind)
{
case 1:
//--- Dense well conditioned SPD
CMatGen::SPDMatrixRndCond(n,50.0,a);
break;
case 2:
//--- Dense well conditioned indefinite
CMatGen::SMatrixRndCond(n,50.0,a);
isnonconvex=true;
break;
case 3:
//--- Low rank
tmp=vector<double>::Zeros(n);
for(k=1; k<=MathMin(3,n-1); k++)
{
for(i=0; i<n; i++)
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
v=MathPow(2,CHighQualityRand::HQRndNormal(rs));
for(i=0; i<n; i++)
for(j=0; j<n; j++)
a.Add(i,j,v*tmp[i]*tmp[j]);
}
break;
case 4:
//--- Low rank indefinite
tmp=vector<double>::Zeros(n);
for(k=1; k<=MathMin(3,n-1); k++)
{
for(i=0; i<n; i++)
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
v=CHighQualityRand::HQRndNormal(rs);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
a.Add(i,j,v*tmp[i]*tmp[j]);
}
isnonconvex=true;
break;
}
//--- Generate constraints
bl=vector<double>::Full(n,-1.0);
bu=vector<double>::Full(n,1.0);
ccnt=MathMin(3,n-1);
c=matrix<double>::Zeros(ccnt,n+1);
ct.Resize(ccnt);
for(i=0; i<ccnt; i++)
{
ct.Set(i,CHighQualityRand::HQRndUniformI(rs,3)-1);
for(j=0; j<n; j++)
c.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
}
//--- Create and optimize
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetStartingPoint(state,x);
if(isnonconvex)
SetRandomAlgoNonConvexLC(state);
else
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetBC(state,bl,bu);
CMinQP::MinQPSetLC(state,c,ct,ccnt);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xs0,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminqpunit.ap:3159");
if(err)
return;
//--- 1. evaluate unconstrained gradient at solution
//--- 2. calculate constrained gradient (NNLS solver is used
//--- to evaluate gradient subject to active constraints).
//--- In order to do this we form CE matrix, matrix of active
//--- constraints (columns store constraint vectors). Then
//--- we try to approximate gradient vector by columns of CE,
//--- subject to non-negativity restriction placed on variables
//--- corresponding to inequality constraints.
//--- Residual from such regression is a constrained gradient vector.
g=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xs0,a,i);
g.Set(i,v+b[i]);
}
ce=matrix<double>::Zeros(n,n+ccnt);
ArrayResize(nonnegative,n+ccnt);
k=0;
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(err,xs0[i]<(double)(bl[i]),"testminqpunit.ap:3187");
CAp::SetErrorFlag(err,xs0[i]>(double)(bu[i]),"testminqpunit.ap:3188");
if(xs0[i]<=(double)(bl[i]+tolconstr))
{
for(j=0; j<n; j++)
ce.Set(j,k,0.0);
ce.Set(i,k,1.0);
nonnegative[k]=true;
k++;
continue;
}
if(xs0[i]>=(double)(bu[i]-tolconstr))
{
for(j=0; j<n; j++)
ce.Set(j,k,0.0);
ce.Set(i,k,-1.0);
nonnegative[k]=true;
k++;
continue;
}
}
for(i=0; i<ccnt; i++)
{
v=CAblasF::RDotVR(n,xs0,c,i);
v-=c.Get(i,n);
CAp::SetErrorFlag(err,ct[i]==0 && MathAbs(v)>tolconstr,"testminqpunit.ap:3212");
CAp::SetErrorFlag(err,ct[i]>0 && v<(-tolconstr),"testminqpunit.ap:3213");
CAp::SetErrorFlag(err,ct[i]<0 && v>tolconstr,"testminqpunit.ap:3214");
if(ct[i]==0)
{
for(j=0; j<n; j++)
ce.Set(j,k,c.Get(i,j));
nonnegative[k]=false;
k++;
continue;
}
if((ct[i]>0 && v<=tolconstr) || (ct[i]<0 && v>=(-tolconstr)))
{
for(j=0; j<n; j++)
ce.Set(j,k,MathSign(ct[i])*c.Get(i,j));
nonnegative[k]=true;
k++;
continue;
}
}
CSNNLS::SNNLSInit(0,0,0,nnls);
CSNNLS::SNNLSSetProblem(nnls,ce,g,0,k,n);
for(i=0; i<k; i++)
{
if(!nonnegative[i])
CSNNLS::SNNLSDropNNC(nnls,i);
}
CSNNLS::SNNLSSolve(nnls,tmp);
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
g.Add(j,-tmp[i]*ce.Get(j,i));
}
vv=CAblasF::RDotV2(n,g);
vv=MathSqrt(vv);
if(akind==3 || akind==4)
CAp::SetErrorFlag(err,vv>(1.0E-2),"testminqpunit.ap:3244");
else
CAp::SetErrorFlag(err,vv>(1.0E-3),"testminqpunit.ap:3246");
}
}
}
}
//+------------------------------------------------------------------+
//| This function tests linearly constrained QP solvers. |
//| On failure sets Err to True; on success leaves it unchanged. |
//+------------------------------------------------------------------+
void CTestMinQPUnit::GeneralLCQPTest(bool &errorflag)
{
//--- create variables
CHighQualityRandState rs;
int n=0;
int i=0;
int j=0;
int k=0;
int solvertype=0;
double v=0;
double vv=0;
bool bflag;
int pass=0;
CMatrixDouble rawa;
CMatrixDouble a;
CSparseMatrix sa;
CRowDouble b;
CRowDouble b2;
CRowDouble xs;
CRowDouble x0;
CRowDouble x1;
CRowDouble x2;
CRowDouble xf;
CRowDouble xorigin;
CRowDouble s;
CRowDouble da;
CRowDouble xstart;
CRowDouble g;
CRowDouble gtrial;
CRowDouble gs;
CRowDouble tmp;
CRowDouble tmp0;
CRowDouble tmp1;
CRowDouble tmp2;
CRowDouble lagrange;
CRowDouble bndl;
CRowDouble bndu;
CMatrixDouble rawc;
CRowDouble rawcl;
CRowDouble rawcu;
CRowInt rawct;
int rawccnt=0;
CMatrixDouble densec;
CRowInt densect;
int denseccnt=0;
CSparseMatrix sparsec;
CRowInt sparsect;
int sparseccnt=0;
CMatrixDouble activeset;
bool activeeq[];
int nactive=0;
CSNNLSSolver nnls;
double constraintsrcond=0;
CRowDouble svdw;
CMatrixDouble svdu;
CMatrixDouble svdvt;
CMinQPState state;
CMinQPReport rep;
CMinQPState state2;
CMinQPReport rep2;
double f0=0;
double f1=0;
double xtol=0;
double ftol=0;
double gtol=0;
double tolconstr=0;
int bscale=0;
int akind=0;
CMatrixDouble ce;
CMatrixDouble q;
CMatrixDouble t2;
CMatrixDouble t3;
CMatrixDouble a2;
CRowDouble lagbc;
CRowDouble laglc;
bool nonnegative[];
double mx=0;
int shiftkind=0;
int nnz=0;
bool issemidefinite;
bool skiptest;
double bleicepsx=0;
double aulepsx=0;
double aulrho=0;
int aulits=0;
double ipmeps=0;
CMatrixDouble kkt;
CRowDouble kktright;
double minushuge=0;
double plushuge=0;
int i_=0;
CHighQualityRand::HQRndRandomize(rs);
bleicepsx=1.0E-9;
ipmeps=1.0E-12;
aulepsx=1.0E-12;
aulrho=5.0E3;
aulits=15;
//--- SMALL-SCALE TESTS: many tests for small N's
for(solvertype=0; solvertype<=3; solvertype++)
{
//--- Test random linearly constrained convex QP problem with known answer:
//--- * generate random A and b
//--- * generate random solution XS
//--- * calculate unconstrained gradient GS at XS
//--- * generate random box/linear constraints C, with some of them being
//--- active at XS, and some being inactive. Calculate residual gradient
//--- GP after projection of GS onto active set, add one more constraint
//--- equal to +-(GP-GS).
//--- We test here BLEIC and Dense-AUL solvers, with A being passed
//--- in dense or sparse format, and linear constraints C being passed
//--- as dense, sparse or mixed ones.
for(n=1; n<=10; n++)
{
//--- Generate random A, b and xs
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),rawa);
b=vector<double>::Zeros(n);
xs=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
xs.Set(i,CHighQualityRand::HQRndNormal(rs));
}
//--- Generate well conditioned "raw" constraints:
//--- * generate random box and CCnt-1 linear constraints
//--- * determine active set, calculate its condition number
//--- * repeat until condition number is good enough
//--- (better than 1E2; larger values sometimes result in
//--- spurious failures)
gs=vector<double>::Zeros(n);
tmp=vector<double>::Zeros(n);
CAblas::RMatrixMVect(n,n,rawa,0,0,0,xs,0,gs,0);
for(i=0; i<n; i++)
gs.Add(i,b[i]);
rawccnt=1+CHighQualityRand::HQRndUniformI(rs,n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
activeset=matrix<double>::Zeros(n,n+rawccnt);
ArrayResize(activeeq,n+rawccnt);
rawc=matrix<double>::Zeros(rawccnt,n+1);
rawct.Resize(rawccnt);
do
{
nactive=0;
for(i=0; i<n; i++)
{
bndl.Set(i,xs[i]-1-CHighQualityRand::HQRndUniformR(rs));
bndu.Set(i,xs[i]+1+CHighQualityRand::HQRndUniformR(rs));
if(CHighQualityRand::HQRndUniformR(rs)<0.66)
{
//--- I-th box constraint is inactive
continue;
}
if(CHighQualityRand::HQRndUniformR(rs)>0.50)
{
//--- I-th box constraint is equality one
bndl.Set(i,xs[i]);
bndu.Set(i,xs[i]);
activeset.Col(nactive,vector<double>::Zeros(n));
activeset.Set(i,nactive,1);
activeeq[nactive]=true;
nactive++;
}
else
{
//--- I-th box constraint is inequality one
activeset.Col(nactive,vector<double>::Zeros(n));
if(gs[i]>0.0)
{
bndl.Set(i,xs[i]);
activeset.Set(i,nactive,-1);
if(CHighQualityRand::HQRndUniformR(rs)>0.50)
bndu.Set(i,AL_POSINF);
}
else
{
bndu.Set(i,xs[i]);
activeset.Set(i,nactive,1);
if(CHighQualityRand::HQRndUniformR(rs)>0.50)
bndl.Set(i,AL_NEGINF);
}
activeeq[nactive]=false;
nactive++;
}
}
for(i=0; i<rawccnt-1; i++)
{
for(j=0; j<n; j++)
{
if(CHighQualityRand::HQRndUniformR(rs)<0.50)
rawc.Set(i,j,CHighQualityRand::HQRndNormal(rs));
else
rawc.Set(i,j,0);
}
rawc.Set(i,CHighQualityRand::HQRndUniformI(rs,n),CHighQualityRand::HQRndNormal(rs));
rawc.Set(i,n,0);
for(j=0; j<n; j++)
rawc.Add(i,n,rawc.Get(i,j)*xs[j]);
rawct.Set(i,-1);
if(CHighQualityRand::HQRndUniformR(rs)<0.66)
{
//--- I-th box constraint is inactive
rawc.Add(i,n,1+CHighQualityRand::HQRndUniformR(rs));
if(CHighQualityRand::HQRndUniformR(rs)>0.50)
{
for(i_=0; i_<=n; i_++)
rawc.Mul(i,i_,-1);
rawct.Mul(i,-1);
}
continue;
}
if(CHighQualityRand::HQRndUniformR(rs)>0.50)
{
//--- I-th box constraint is equality one
rawct.Set(i,0);
activeset.Col(nactive,rawc[i]+0);
activeeq[nactive]=true;
nactive++;
}
else
{
//--- I-th box constraint is inequality one
v=CAblasF::RDotVR(n,gs,rawc,i);
if(v>0.0)
{
rawct.Set(i,1);
activeset.Col(nactive,rawc[i]*(-1));
}
else
{
rawct.Set(i,-1);
activeset.Col(nactive,rawc[i]+0);
}
activeeq[nactive]=false;
nactive++;
}
}
tmp=gs.ToVector()*(-1);
CSNNLS::SNNLSInit(0,0,0,nnls);
CSNNLS::SNNLSSetProblem(nnls,activeset,tmp,0,nactive,n);
for(i=0; i<nactive; i++)
{
if(activeeq[i])
CSNNLS::SNNLSDropNNC(nnls,i);
}
CSNNLS::SNNLSSolve(nnls,lagrange);
tmp=gs.ToVector()*(-1);
for(i=0; i<nactive; i++)
{
v=lagrange[i];
tmp-=activeset.Col(i)*v;
}
rawc.Row(rawccnt-1,tmp);
v=CAblasF::RDotV(n,tmp,xs);
rawc.Set(rawccnt-1,n,v);
rawct.Set(rawccnt-1,-1);
//--- Calculate reciprocal condition number
if(nactive>0)
{
bflag=CSingValueDecompose::RMatrixSVD(activeset,n,nactive,0,0,0,svdw,svdu,svdvt);
//--- check
if(!CAp::Assert(bflag,"MinQPTest: integrity failure"))
{
errorflag=true;
return;
}
constraintsrcond=svdw[MathMin(nactive,n)-1]/svdw[0];
}
else
constraintsrcond=1;
//--- Check RCond
}
while(!(constraintsrcond>=0.01 && nactive<n));
//--- Create optimizer, solve
CMinQP::MinQPCreate(n,state);
switch(solvertype)
{
case 0:
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
break;
case 1:
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
break;
case 2:
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
break;
case 3:
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
break;
default:
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
CMinQP::MinQPSetBC(state,bndl,bndu);
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
//--- Test
//--- Because constrained problems are often ill-conditioned,
//--- we do NOT compare X1 with XS directly. Instead, we:
//--- a) compare function values at X1 and XS with good precision
//--- b) check constraint violation with good precision
//--- c) perform comparison for |X1-XS| with LOW precision
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:3569");
return;
}
f0=0;
f1=0;
for(i=0; i<n; i++)
{
f0+=b[i]*xs[i];
f1+=b[i]*x1[i];
for(j=0; j<n; j++)
{
f0+=0.5*xs[i]*rawa.Get(i,j)*xs[j];
f1+=0.5*x1[i]*rawa.Get(i,j)*x1[j];
}
}
CAp::SetErrorFlag(errorflag,MathAbs(f0-f1)>1.0E-3,"testminqpunit.ap:3584");
}
//--- Inequality constrained convex problem:
//--- * N*N diagonal A
//--- * one inequality constraint q'*x>=0, where q is random unit vector
//--- * optimization problem has form 0.5*x'*A*x-(xs*A)*x,
//--- where xs is some random vector
//--- * either:
//--- a) xs is feasible => we must Stop at xs
//--- b) xs is infeasible => we must Stop at the boundary q'*x=0 and
//--- projection of gradient onto q*x=0 must be zero
//--- NOTE: we make several passes because some specific kind of errors is rarely
//--- caught by this test, so we need several repetitions.
xtol=1.0E-4;
gtol=1.0E-4;
for(n=2; n<=6; n++)
{
for(pass=0; pass<=4; pass++)
{
//--- Generate problem: A, b, CMatrix, x0, XStart
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a);
b=vector<double>::Zeros(n);
xs=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
rawc=matrix<double>::Zeros(1,n+1);
rawct.Resize(1);
for(i=0; i<n; i++)
{
xs.Set(i,2*CMath::RandomReal()-1);
xstart.Set(i,2*CMath::RandomReal()-1);
}
do
{
v=0;
for(i=0; i<n; i++)
{
rawc.Set(0,i,2*CMath::RandomReal()-1);
v+=CMath::Sqr(rawc.Get(0,i));
}
v=MathSqrt(v);
}
while(v==0.0);
for(i=0; i<n; i++)
rawc.Mul(0,i,1.0/v);
rawc.Set(0,n,0);
rawct.Set(0,1);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xs,a,i);
b.Set(i,-v);
}
//--- Create optimizer, solve
CMinQP::MinQPCreate(n,state);
switch(solvertype)
{
case 0:
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
break;
case 1:
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
break;
case 2:
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
break;
case 3:
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
break;
default:
CAp::SetErrorFlag(errorflag,true,"unexpected solver type");
return;
}
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
CMinQP::MinQPSetStartingPoint(state,xstart);
RandomlySplitAndSetLCLegacy(rawc,rawct,1,n,state,rs);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
//--- Test
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:3667");
return;
}
v=CAblasF::RDotVR(n,xs,rawc,0);
if(v>=0.0)
{
//--- XS is feasible
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,MathAbs(x1[i]-xs[i])>xtol,"testminqpunit.ap:3677");
}
else
{
//--- XS is infeasible:
//--- * X1 must be approximately feasible
//--- * gradient projection onto c'*x=0 must be zero
v=CAblasF::RDotVR(n,x1,rawc,0);
CAp::SetErrorFlag(errorflag,v<(-xtol),"testminqpunit.ap:3687");
g=b;
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,x1,a,i);
g.Add(i,v);
}
v=CAblasF::RDotVR(n,g,rawc,0);
for(i_=0; i_<n; i_++)
g.Add(i_,- v*rawc.Get(0,i_));
v=CAblasF::RDotV2(n,g);
CAp::SetErrorFlag(errorflag,MathSqrt(v)>gtol,"testminqpunit.ap:3698");
}
}
}
//--- Box constraints vs linear ones:
//--- * N*N SPD A
//--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
//--- where x1 is some random vector from [-1,+1]
//--- * K=2*N constraints of the form ai<=x[i] or x[i]<=b[i],
//--- with ai in [-1.0,-0.1], bi in [+0.1,+1.0]
//--- * initial point xstart is from [-1,+2]
//--- * we solve two related QP problems:
//--- a) one with constraints posed as boundary ones
//--- b) another one with same constraints posed as general linear ones
//--- both problems must have same solution.
//--- Here we test that boundary constrained and linear inequality constrained
//--- solvers give same results.
xtol=1.0E-5;
for(n=1; n<=6; n++)
{
//--- Generate problem: A, b, x0, XStart, C, CT
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a);
b=vector<double>::Zeros(n);
x1=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
rawc=matrix<double>::Zeros(2*n,n+1);
rawct.Resize(2*n);
bndl=vector<double>::Full(n,AL_NEGINF);
bndu=vector<double>::Full(n,AL_POSINF);
for(i=0; i<n ; i++)
{
x1.Set(i,2*CMath::RandomReal()-1);
xstart.Set(i,3*CMath::RandomReal()-1);
bndl.Set(i,-(0.1+0.9*CMath::RandomReal()));
bndu.Set(i,0.1+0.9*CMath::RandomReal());
rawc.Set(2*i,i,1);
rawc.Set(2*i,n,bndl[i]);
rawct.Set(2*i,1);
rawc.Set(2*i+1,i,1);
rawc.Set(2*i+1,n,bndu[i]);
rawct.Set(2*i+1,-1);
}
for(i=0; i<n ; i++)
{
v=CAblasF::RDotVR(n,x1,a,i);
b.Set(i,-v);
}
//--- Solve linear inequality constrained problem
CMinQP::MinQPCreate(n,state);
switch(solvertype)
{
case 0:
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
break;
case 1:
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
break;
case 2:
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
break;
case 3:
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
break;
default:
CAp::SetErrorFlag(errorflag,true,"unexpected solver type");
return;
}
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
CMinQP::MinQPSetStartingPoint(state,xstart);
RandomlySplitAndSetLCLegacy(rawc,rawct,2*n,n,state,rs);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
//--- Solve boundary constrained problem
CMinQP::MinQPCreate(n,state2);
switch(solvertype)
{
case 0:
CMinQP::MinQPSetAlgoBLEIC(state2,0.0,0.0,bleicepsx,0);
break;
case 1:
CMinQP::MinQPSetAlgoDenseAUL(state2,aulepsx,aulrho,aulits);
break;
case 2:
CMinQP::MinQPSetAlgoDenseIPM(state2,ipmeps);
break;
case 3:
CMinQP::MinQPSetAlgoSparseIPM(state2,ipmeps);
break;
default:
CAp::SetErrorFlag(errorflag,true,"unexpected solver type");
return;
}
CMinQP::MinQPSetLinearTerm(state2,b);
RandomlySelectConvertAndSetQuadraticTerm(a,n,state2,rs);
CMinQP::MinQPSetStartingPoint(state2,xstart);
CMinQP::MinQPSetBC(state2,bndl,bndu);
CMinQP::MinQPOptimize(state2);
CMinQP::MinQPResults(state2,x2,rep2);
//--- Calculate gradient, check projection
if(rep.m_terminationtype<=0 || rep2.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:3804");
return;
}
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(errorflag,x1[i]<(bndl[i]-xtol),"testminqpunit.ap:3809");
CAp::SetErrorFlag(errorflag,x1[i]>(bndu[i]+xtol),"testminqpunit.ap:3810");
CAp::SetErrorFlag(errorflag,MathAbs(x1[i]-x2[i])>xtol,"testminqpunit.ap:3811");
}
}
//--- Convex/nonconvex optimization problem with combination of
//--- box and linear constraints:
//--- * N=2..8
//--- * f = 0.5*x'*A*x+b'*x
//--- * b has normally distributed entries with scale 10^BScale
//--- * several kinds of A are tried: zero, well conditioned SPD,
//--- well conditioned definite, low rank semidefinity
//--- * box constraints: x[i] in [-1,+1]
//--- * initial point x0 = [0 0 ... 0 0]
//--- * CCnt=min(3,N-1) general linear constraints of form (c,x)=0.
//--- random mix of equality/inequality constraints is tried, moderate
//--- condition number is guaranteed, x0 is guaranteed to be feasible.
//--- We check that constrained gradient is close to zero at solution.
//--- Inequality constraint is considered active if distance to boundary
//--- is less than TolConstr. We use nonnegative least squares solver
//--- in order to compute constrained gradient.
tolconstr=-99999;
for(n=2; n<=8; n++)
{
for(akind=0; akind<=3; akind++)
{
for(bscale=1; bscale>=-1; bscale--)
{
//--- Dense-AUL solver has lower precision on rank-deficient
//--- problems, so we skip AKind=3.
//--- IPM solvers can not work with indefinite problems.
if(solvertype==1 && akind==3)
continue;
if((solvertype==2 || solvertype==3) && (akind==2 || akind==4))
continue;
//--- Set up tolerances
if(solvertype==0)
tolconstr=1.0E-8;
else
{
if((solvertype==1 || solvertype==2) || solvertype==3)
{
tolconstr=1.0E-3;
if(akind==3)
tolconstr=tolconstr*5;
}
else
{
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
}
gtol=1.0E-4;
//--- Generate A, B and initial point
a=matrix<double>::Zeros(n,n);
b=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
for(i=0; i<n; i++)
b.Set(i,MathPow(10,bscale)*CHighQualityRand::HQRndNormal(rs));
if(akind==1)
{
//--- Dense well conditioned SPD
CMatGen::SPDMatrixRndCond(n,50.0,a);
}
if(akind==2)
{
//--- Dense well conditioned indefinite
CMatGen::SMatrixRndCond(n,50.0,a);
}
if(akind==3)
{
//--- Low rank semidefinite
tmp=vector<double>::Zeros(n);
for(k=1; k<=MathMin(3,n-1); k++)
{
for(i=0; i<n; i++)
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
v=MathPow(2,CHighQualityRand::HQRndNormal(rs));
for(i=0; i<n; i++)
for(j=0; j<n; j++)
a.Add(i,j,v*tmp[i]*tmp[j]);
}
}
//--- Generate constraints
bndl=vector<double>::Full(n,-1.0);
bndu=vector<double>::Full(n,1.0);
rawccnt=MathMin(3,n-1);
rawc=matrix<double>::Zeros(rawccnt,n+1);
rawct.Resize(rawccnt);
do
{
for(i=0; i<rawccnt; i++)
{
rawct.Set(i,CHighQualityRand::HQRndUniformI(rs,3)-1);
for(j=0; j<n; j++)
rawc.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
}
}
while(GetConstraintRCond(rawc,rawccnt,n)<=0.01);
//--- Create and optimize
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetStartingPoint(state,xstart);
switch(solvertype)
{
case 0:
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
break;
case 1:
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
break;
case 2:
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
break;
case 3:
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
break;
default:
CAp::SetErrorFlag(errorflag,true,"unexpected solver type");
return;
}
CMinQP::MinQPSetBC(state,bndl,bndu);
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminqpunit.ap:3961");
if(errorflag)
return;
//--- 1. evaluate unconstrained gradient at solution
//--- 2. calculate constrained gradient (NNLS solver is used
//--- to evaluate gradient subject to active constraints).
//--- In order to do this we form CE matrix, matrix of active
//--- constraints (columns store constraint vectors). Then
//--- we try to approximate gradient vector by columns of CE,
//--- subject to non-negativity restriction placed on variables
//--- corresponding to inequality constraints.
//--- Residual from such regression is a constrained gradient vector.
g=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,x1,a,i);
g.Set(i,v+b[i]);
}
ce=matrix<double>::Zeros(n,n+rawccnt);
ArrayResizeAL(nonnegative,n+rawccnt);
k=0;
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(errorflag,x1[i]<bndl[i],"testminqpunit.ap:3989");
CAp::SetErrorFlag(errorflag,x1[i]>bndu[i],"testminqpunit.ap:3990");
if(x1[i]<=(bndl[i]+tolconstr))
{
for(j=0; j<n; j++)
ce.Set(j,k,0.0);
ce.Set(i,k,1.0);
nonnegative[k]=true;
k++;
continue;
}
if(x1[i]>=(double)(bndu[i]-tolconstr))
{
for(j=0; j<n; j++)
ce.Set(j,k,0.0);
ce.Set(i,k,-1.0);
nonnegative[k]=true;
k++;
continue;
}
}
for(i=0; i<rawccnt; i++)
{
v=CAblasF::RDotVR(n,x1,rawc,i);
v-=rawc.Get(i,n);
CAp::SetErrorFlag(errorflag,rawct[i]==0 && MathAbs(v)>tolconstr,"testminqpunit.ap:4014");
CAp::SetErrorFlag(errorflag,rawct[i]>0 && v<(-tolconstr),"testminqpunit.ap:4015");
CAp::SetErrorFlag(errorflag,rawct[i]<0 && v>tolconstr,"testminqpunit.ap:4016");
if(rawct[i]==0)
{
for(j=0; j<n; j++)
ce.Set(j,k,rawc.Get(i,j));
nonnegative[k]=false;
k++;
continue;
}
if((rawct[i]>0 && v<=tolconstr) || (rawct[i]<0 && v>=(-tolconstr)))
{
for(j=0; j<n; j++)
ce.Set(j,k,MathSign(rawct[i])*rawc.Get(i,j));
nonnegative[k]=true;
k++;
continue;
}
}
CSNNLS::SNNLSInit(0,0,0,nnls);
CSNNLS::SNNLSSetProblem(nnls,ce,g,0,k,n);
for(i=0; i<k; i++)
{
if(!nonnegative[i])
CSNNLS::SNNLSDropNNC(nnls,i);
}
CSNNLS::SNNLSSolve(nnls,tmp);
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
g.Add(j,-tmp[i]*ce.Get(j,i));
}
vv=CAblasF::RDotV2(n,g);
vv=MathSqrt(vv);
CAp::SetErrorFlag(errorflag,vv>gtol,"testminqpunit.ap:4045");
}
}
}
//--- Boundary and linear equality/inequality constrained QP problem,
//--- test for correct handling of non-zero XOrigin:
//--- * N*N SPD A with moderate condtion number (up to 100)
//--- * boundary constraints 0<=x[i]<=1
//--- * K<N linear equality/inequality constraints Q*x = Q*x0, where
//--- Q is random orthogonal K*N matrix, x0 is some random vector from the
//--- inner area of the feasible hypercube (0.1<=x0[i]<=0.9)
//--- * optimization problem has form 0.5*(x-xorigin)'*A*(x-xorigin)+b*(x-xorigin),
//--- where b is some random vector with -1<=b[i]<=+1.
//--- (sometimes solution is in the inner area, sometimes at the boundary)
//--- * every component of the initial point XStart is random from [-1,1]
//--- Solution of such problem is calculated using two methods:
//--- a) QP with SetOrigin() call
//--- b) QP with XOrigin explicitly added to the quadratic function,
//--- Both methods should give same results; any significant difference is
//--- evidence of some error in the QP implementation.
ftol=-99999;
xtol=-99999;
for(n=2; n<=6; n++)
{
for(k=1; k<n; k++)
{
//--- Set up tolerances
if(solvertype==0)
{
ftol=1.0E-5;
xtol=1.0E-5;
}
else
{
if((solvertype==1 || solvertype==2) || solvertype==3)
{
ftol=1.0E-3;
xtol=1.0E-3;
}
else
{
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
}
//--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart.
//--- Additionally, we compute modified b: b2 = b-xorigin'*A
CMatGen::RMatrixRndOrthogonal(n,q);
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a);
b=vector<double>::Zeros(n);
b2=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
x1=vector<double>::Zeros(n);
xorigin=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
rawc=matrix<double>::Zeros(k,n+1);
rawct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,0.1+0.8*CMath::RandomReal());
b.Set(i,2*CMath::RandomReal()-1);
bndl.Set(i,0.0);
bndu.Set(i,1.0);
xstart.Set(i,CMath::RandomInteger(2));
xorigin.Set(i,2*CMath::RandomReal()-1);
}
for(i=0; i<k; i++)
{
for(i_=0; i_<n; i_++)
rawc.Set(i,i_,q.Get(i,i_));
v=CAblasF::RDotVR(n,x0,q,i);
rawc.Set(i,n,v);
rawct.Set(i,CMath::RandomInteger(3)-1);
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xorigin,a,i);
b2.Set(i,b[i]-v);
}
//--- Solve with SetOrigin() call
CMinQP::MinQPCreate(n,state);
if(solvertype==0)
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
else
if(solvertype==1)
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
else
if(solvertype==2)
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
else
if(solvertype==3)
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
else
{
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetOrigin(state,xorigin);
CMinQP::MinQPSetBC(state,bndl,bndu);
RandomlySplitAndSetLCLegacy(rawc,rawct,k,n,state,rs);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4153");
continue;
}
//--- Solve problem using explicit origin
CMinQP::MinQPCreate(n,state2);
if(solvertype==0)
CMinQP::MinQPSetAlgoBLEIC(state2,0.0,0.0,bleicepsx,0);
else
if(solvertype==1)
CMinQP::MinQPSetAlgoDenseAUL(state2,aulepsx,aulrho,aulits);
else
if(solvertype==2)
CMinQP::MinQPSetAlgoDenseIPM(state2,ipmeps);
else
if(solvertype==3)
CMinQP::MinQPSetAlgoSparseIPM(state2,ipmeps);
else
{
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
CMinQP::MinQPSetLinearTerm(state2,b2);
RandomlySelectConvertAndSetQuadraticTerm(a,n,state2,rs);
CMinQP::MinQPSetStartingPoint(state2,xstart);
CMinQP::MinQPSetBC(state2,bndl,bndu);
RandomlySplitAndSetLCLegacy(rawc,rawct,k,n,state2,rs);
CMinQP::MinQPOptimize(state2);
CMinQP::MinQPResults(state2,x2,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4180");
continue;
}
//--- Calculate function value at X1/X2.
//--- Compare solutions.
f0=0.0;
f1=0.0;
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
f0+=0.5*(x1[i]-xorigin[i])*a.Get(i,j)*(x1[j]-xorigin[j]);
f1+=0.5*(x2[i]-xorigin[i])*a.Get(i,j)*(x2[j]-xorigin[j]);
}
f0+=(x1[i]-xorigin[i])*b[i];
f1+=(x2[i]-xorigin[i])*b[i];
}
CAp::SetErrorFlag(errorflag,MathAbs(f0-f1)>ftol,"testminqpunit.ap:4200");
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,MathAbs(x1[i]-x2[i])>xtol,"testminqpunit.ap:4202");
}
}
//--- Test scale-invariance. Solver performs same steps on scaled and
//--- unscaled problems (assuming that scale of the variables is known
//--- and is 2^k for some integer k; latter guarantees that DENSE-AUL solver
//--- performs EXACTLY same steps even under finite-precision arithmetics).
//--- We generate random scale matrix S and random well-conditioned and
//--- well scaled matrix A. Then we solve two problems:
//--- (1) f = 0.5*x'*A*x+b'*x
//--- (identity scale matrix is used)
//--- and
//--- (2) f = 0.5*y'*(inv(S)*A*inv(S))*y + (inv(S)*b)'*y
//--- (scale matrix S is used)
//--- with correspondingly scaled constraints, origin and starting point.
//--- As result, we must get S*x=y
for(n=2; n<=6; n++)
{
for(k=1; k<=MathMax(n-2,1); k++)
{
//--- Set up tolerances:
//--- * BLEIC or IPM do not guarantee that we perform steps exactly even
//--- when scale is 2^k, so we use non-zero XTol=1E-3
//--- * DENSE-AUL provides such guarantee, so XTol=0
if((solvertype==0 || solvertype==2) || solvertype==3)
xtol=1.0E-3;
else
{
if(solvertype==1)
xtol=0;
else
{
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
}
//--- Generate "raw" problem: A, b, BndL, BndU, C/CT, XOrigin, XStart.
//--- Solve it immediately.
//--- NOTE: we make sure that there exists at least one feasible point X0.
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
xorigin=vector<double>::Zeros(n);
rawc=matrix<double>::Zeros(k,n+1);
rawct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,CHighQualityRand::HQRndUniformR(rs)-0.5);
b.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,-1-CHighQualityRand::HQRndUniformR(rs));
bndu.Set(i,1+CHighQualityRand::HQRndUniformR(rs));
xstart.Set(i,CHighQualityRand::HQRndNormal(rs));
xorigin.Set(i,CHighQualityRand::HQRndNormal(rs));
}
for(i=0; i<k; i++)
{
v=0;
for(j=0; j<n; j++)
{
rawc.Set(i,j,CHighQualityRand::HQRndNormal(rs));
v+=rawc.Get(i,j)*x0[j];
}
rawc.Set(i,n,v);
rawct.Set(i,CHighQualityRand::HQRndUniformI(rs,3)-1);
if(rawct[i]<0)
rawc.Add(i,n,0.1);
if(rawct[i]>0)
rawc.Add(i,n,-0.1);
}
RandomlySplitLC(rawc,rawct,k,n,sparsec,sparsect,sparseccnt,densec,densect,denseccnt,rs);
CMinQP::MinQPCreate(n,state);
state.m_dbgskipconstraintnormalization=true;
if(solvertype==0)
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
else
if(solvertype==1)
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
else
if(solvertype==2)
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
else
if(solvertype==3)
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
else
{
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetOrigin(state,xorigin);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPSetLCMixed(state,sparsec,sparsect,sparseccnt,densec,densect,denseccnt);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4306");
continue;
}
//--- Scale problem and solve one more time.
//--- Randomly choose between dense and sparse versions.
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
s.Set(i,MathPow(2,CHighQualityRand::HQRndUniformI(rs,7)-3));
akind=CHighQualityRand::HQRndUniformI(rs,2);
if(akind==0)
{
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
a.Mul(i,j,1.0/(s[i]*s[j]));
}
}
else
{
CSparse::SparseCreate(n,n,0,sa);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
CSparse::SparseSet(sa,i,j,a.Get(i,j)/(s[i]*s[j]));
}
for(i=0; i<n; i++)
{
b.Mul(i,1.0/s[i]);
xstart.Mul(i,s[i]);
xorigin.Mul(i,s[i]);
bndl.Mul(i,s[i]);
bndu.Mul(i,s[i]);
}
for(i=0; i<denseccnt; i++)
{
for(j=0; j<n; j++)
densec.Mul(i,j,1.0/s[j]);
}
for(i=0; i<sparseccnt; i++)
{
for(j=0; j<n; j++)
CSparse::SparseSet(sparsec,i,j,CSparse::SparseGet(sparsec,i,j)/s[j]);
}
CMinQP::MinQPCreate(n,state);
state.m_dbgskipconstraintnormalization=true;
if(solvertype==0)
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
else
if(solvertype==1)
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
else
if(solvertype==2)
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
else
if(solvertype==3)
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
else
{
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
CMinQP::MinQPSetLinearTerm(state,b);
if(akind==0)
CMinQP::MinQPSetQuadraticTerm(state,a,CMath::RandomReal()>0.5);
if(akind==1)
CMinQP::MinQPSetQuadraticTermSparse(state,sa,CMath::RandomReal()>0.5);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetOrigin(state,xorigin);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPSetLCMixed(state,sparsec,sparsect,sparseccnt,densec,densect,denseccnt);
CMinQP::MinQPSetScale(state,s);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x2,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4371");
continue;
}
//--- Compare
mx=0;
for(i=0; i<n; i++)
mx=MathMax(mx,MathAbs(x1[i]));
mx=MathMax(mx,1);
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,(double)(MathAbs(x1[i]-x2[i]/s[i]))>(double)(xtol*mx),"testminqpunit.ap:4383");
}
}
//--- General inequality constrained problem:
//--- * N*N SPD diagonal A with moderate condtion number
//--- * no boundary constraints
//--- * K=N inequality constraints C*x >= C*x0, where C is N*N well conditioned
//--- matrix, x0 is some random vector [-1,+1]
//--- * optimization problem has form 0.5*x'*A*x-b'*x,
//--- where b is random vector from [-1,+1]
//--- * using duality, we can obtain solution of QP problem as follows:
//--- a) intermediate problem min(0.5*y'*B*y + d'*y) s.t. y>=0
//--- is solved, where B = C*inv(A)*C', d = -(C*inv(A)*b + C*x0)
//--- b) after we got dual solution ys, we calculate primal solution
//--- xs = inv(A)*(C'*ys-b)
for(n=1; n<=6; n++)
{
//--- Set up tolerances
xtol=1.0E-3;
//--- Generate problem
da=vector<double>::Zeros(n);
a=matrix<double>::Zeros(n,n);
CMatGen::RMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),t2);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
rawc=matrix<double>::Zeros(n,n+1);
rawct.Resize(n);
for(i=0; i<n; i++)
{
da.Set(i,MathExp(6*CMath::RandomReal()-3));
a.Set(i,i,da[i]);
}
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
b.Set(i,2*CMath::RandomReal()-1);
xstart.Set(i,2*CMath::RandomReal()-1);
}
for(i=0; i<n; i++)
{
for(i_=0; i_<n; i_++)
rawc.Set(i,i_,t2.Get(i,i_));
v=CAblasF::RDotVR(n,x0,rawc,i);
rawc.Set(i,n,v);
rawct.Set(i,1);
}
//--- Solve primal problem, check feasibility
CMinQP::MinQPCreate(n,state);
switch(solvertype)
{
case 0:
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
break;
case 1:
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
break;
case 2:
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
break;
case 3:
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
break;
default:
CAp::Assert(false,"unexpected solver type");
errorflag=false;
return;
}
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetLC(state,rawc,rawct,n);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4462");
continue;
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,x1,rawc,i);
CAp::SetErrorFlag(errorflag,v<(rawc.Get(i,n)-xtol),"testminqpunit.ap:4468");
}
//--- Generate dual problem:
//--- * A2 stores new quadratic term
//--- * B2 stores new linear term
//--- * BndL/BndU store boundary constraints
t3=matrix<double>::Zeros(n,n);
a2=matrix<double>::Zeros(n,n);
CAblas::RMatrixTranspose(n,n,rawc,0,0,t3,0,0);
for(i=0; i<n; i++)
{
v=1/MathSqrt(da[i]);
for(i_=0; i_<n; i_++)
t3.Mul(i,i_,v);
}
CAblas::RMatrixSyrk(n,n,1.0,t3,0,0,2,0.0,a2,0,0,true);
tmp0=b/da+0;
b2=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,tmp0,rawc,i);
b2.Set(i,-(v+rawc.Get(i,n)));
}
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Full(n,AL_POSINF);
CMinQP::MinQPCreate(n,state2);
CMinQP::MinQPSetAlgoQuickQP(state2,0.0,0.0,1.0E-9,0,true);
CMinQP::MinQPSetLinearTerm(state2,b2);
CMinQP::MinQPSetQuadraticTerm(state2,a2,true);
CMinQP::MinQPSetBC(state2,bndl,bndu);
CMinQP::MinQPOptimize(state2);
CMinQP::MinQPResults(state2,x2,rep2);
if(rep2.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4523");
continue;
}
for(i=0; i<n; i++)
{
v=0.0;
for(i_=0; i_<n; i_++)
v+=rawc.Get(i_,i)*x2[i_];
tmp0.Set(i,(v-b[i])/da[i]);
}
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,(double)(MathAbs(tmp0[i]-x1[i]))>(double)(xtol*MathMax(MathAbs(tmp0[i]),1.0)),"testminqpunit.ap:4536");
}
//--- Boundary and linear equality/inequality constrained QP problem with
//--- excessive constraints:
//--- * N*N SPD A with moderate condtion number (up to 100)
//--- * boundary constraints 0<=x[i]<=1
//--- * K=2*N equality/inequality constraints Q*x = Q*x0, where Q is random matrix,
//--- x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9)
//--- * optimization problem has form 0.5*x'*A*x-b*x,
//--- where b is some random vector
//--- * because constraints are excessive, the main problem is to find
//--- feasible point; usually, the only existing feasible point is solution,
//--- so we have to check only feasibility
for(n=1; n<=6; n++)
{
//--- Set up tolerances
if(solvertype==0)
xtol=1.0E-5;
else
{
if((solvertype==1 || solvertype==2) || solvertype==3)
xtol=5.0E-3;
else
{
CAp::Assert(false,"unexpected solver type");
errorflag=false;
return;
}
}
//--- Generate problem: A, b, BndL, BndU, CMatrix, x0, x1, XStart
k=2*n;
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CMath::RandomReal()),a);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Ones(n);
x0=vector<double>::Zeros(n);
x1=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
rawc=matrix<double>::Zeros(k,n+1);
rawct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,0.1+0.8*CMath::RandomReal());
x1.Set(i,2*CMath::RandomReal()-1);
xstart.Set(i,CMath::RandomInteger(2));
}
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
rawc.Set(i,j,2*CMath::RandomReal()-1);
v=CAblasF::RDotVR(n,x0,rawc,i);
rawct.Set(i,CMath::RandomInteger(3)-1);
if(rawct[i]==0)
rawc.Set(i,n,v);
if(rawct[i]>0)
rawc.Set(i,n,v-50*xtol);
if(rawct[i]<0)
rawc.Set(i,n,v+50*xtol);
}
for(i=0; i<n; i++)
b.Set(i,2*CMath::RandomReal()-1);
//--- Create optimizer, solve
CMinQP::MinQPCreate(n,state);
switch(solvertype)
{
case 0:
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
break;
case 1:
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
break;
case 2:
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
break;
case 3:
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
break;
default:
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetBC(state,bndl,bndu);
RandomlySplitAndSetLCLegacy(rawc,rawct,k,n,state,rs);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
//--- Check feasibility properties of the solution
//--- NOTE: we do not check termination type because some solvers (IPM) may return feasible X even with negative code
if(CAp::Len(x1)!=n)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4637");
continue;
}
for(i=0; i<k; i++)
{
v=CAblasF::RDotVR(n,x1,rawc,i);
if(rawct[i]==0)
CAp::SetErrorFlag(errorflag,MathAbs(v-rawc.Get(i,n))>xtol,"testminqpunit.ap:4644");
if(rawct[i]>0)
CAp::SetErrorFlag(errorflag,v<(rawc.Get(i,n)-xtol),"testminqpunit.ap:4646");
if(rawct[i]<0)
CAp::SetErrorFlag(errorflag,v>(rawc.Get(i,n)+xtol),"testminqpunit.ap:4648");
}
}
//--- Boundary constraints posed as general linear ones:
//--- * no bound constraints
//--- * 2*N linear constraints 0 <= x[i] <= 1
//--- * 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(n=1; n<=5; n++)
{
//--- Set up tolerances
if(solvertype==0)
xtol=1.0E-3;
else
{
if((solvertype==1 || solvertype==2) || solvertype==3)
xtol=1.0E-3;
else
{
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
}
//--- Generate X, BL, BU.
a=matrix<double>::Identity(n,n);
b=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
rawc=matrix<double>::Zeros(2*n,n+1);
rawct.Resize(2*n);
k=2*n;
for(i=0; i<n; i++)
{
xstart.Set(i,CMath::RandomReal());
x0.Set(i,3*CMath::RandomReal()-1);
b.Set(i,-x0[i]);
rawc.Set(2*i,i,1);
rawc.Set(2*i,n,0);
rawct.Set(2*i,1);
rawc.Set(2*i+1,i,1);
rawc.Set(2*i+1,n,1);
rawct.Set(2*i+1,-1);
}
//--- Create and optimize
CMinQP::MinQPCreate(n,state);
switch(solvertype)
{
case 0:
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
break;
case 1:
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
break;
case 2:
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
break;
case 3:
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
break;
default:
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
RandomlySplitAndSetLCLegacy(rawc,rawct,k,n,state,rs);
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:4735");
continue;
}
//--- * compare solution with analytic one
//--- * check feasibility
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(errorflag,MathAbs(x1[i]-CApServ::BoundVal(x0[i],0.0,1.0))>xtol,"testminqpunit.ap:4745");
CAp::SetErrorFlag(errorflag,x1[i]<(0.0-xtol),"testminqpunit.ap:4746");
CAp::SetErrorFlag(errorflag,x1[i]>(1.0+xtol),"testminqpunit.ap:4747");
}
}
//--- Convex optimization problem with excessive constraints:
//--- * N=2..5
//--- * f = 0.5*x'*A*x+b'*x
//--- * b has normally distributed entries
//--- * A is diagonal with log-normally distributed entries
//--- * box constraints: x[i] in [-1,+1]
//---*2^N "excessive" general linear constraints (v_k,x)<=(v_k,v_k)+v_shift,
//--- where v_k is one of 2^N vertices of feasible hypercube, v_shift is
//--- a shift parameter:
//--- * with zero v_shift such constraints are degenerate (each vertex has
//--- N box constraints and one "redundant" linear constraint)
//--- * with positive v_shift linear constraint is always inactive
//--- * with small (about machine epsilon) but negative v_shift,
//--- constraint is close to degenerate - but not exactly
//--- Because A is diagonal, we can easily find out solution analytically.
//--- NOTE: TolConstr must be large enough so it won't conflict with
//--- perturbation introduced by v_shift
for(n=2; n<=5; n++)
{
for(shiftkind=-5; shiftkind<=1; shiftkind++)
{
//--- Set up tolerances
if(solvertype==0)
{
tolconstr=1.0E-6;
gtol=1.0E-6;
}
else
{
if((solvertype==1 || solvertype==2) || solvertype==3)
{
tolconstr=1.0E-4;
gtol=1.0E-4;
}
else
{
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
}
//--- Generate A, B and initial point
a=matrix<double>::Zeros(n,n);
b=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
xstart.Set(i,CHighQualityRand::HQRndUniformR(rs)-0.5);
}
for(i=0; i<n; i++)
a.Set(i,i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
//--- Generate constraints
bndl=vector<double>::Full(n,-1.0);
bndu=vector<double>::Full(n,1.0);
rawccnt=(int)MathRound(MathPow(2,n));
rawc=matrix<double>::Zeros(rawccnt,n+1);
rawct.Resize(rawccnt);
for(i=0; i<rawccnt; i++)
{
rawct.Set(i,-1);
k=i;
rawc.Set(i,n,MathSign(shiftkind)*MathPow(10,MathAbs(shiftkind))*CMath::m_machineepsilon);
for(j=0; j<n; j++)
{
rawc.Set(i,j,2*(k%2)-1);
rawc.Add(i,n,rawc.Get(i,j)*rawc.Get(i,j));
k=k/2;
}
}
//--- Create and optimize
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetStartingPoint(state,xstart);
switch(solvertype)
{
case 0:
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
break;
case 1:
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
break;
case 2:
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
break;
case 3:
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
break;
default:
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
CMinQP::MinQPSetBC(state,bndl,bndu);
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminqpunit.ap:4858");
if(errorflag)
return;
//--- Evaluate gradient at solution and test
vv=0.0;
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,x1,a,i);
v+=b[i];
if(x1[i]<=(bndl[i]+tolconstr) && v>0.0)
v=0.0;
if(x1[i]>=(bndu[i]-tolconstr) && v<0.0)
v=0.0;
vv+=CMath::Sqr(v);
}
vv=MathSqrt(vv);
CAp::SetErrorFlag(errorflag,vv>gtol,"testminqpunit.ap:4882");
}
}
//--- Convex optimization problem with known answer (we generate constraints
//--- and Lagrange coefficients, and then generate quadratic and linear term
//--- which satisfy KKT conditions for given Lagrange coefficients):
//--- * N=2..8
//--- * f = 0.5*x'*A*x+b'*x
//--- * several kinds of A are tried: zero, well conditioned SPD, low rank
//--- * initial point x0 = [0 0 ... 0 0]
//--- * absent bounds can be set to +-INF or to some huge number (randomly)
//--- * first, we generate set of linear constraints (without bounds),
//--- solution point X and Lagrange multipliers for linear and box constraints
//--- * then we determine bounds on variables and linear constraints which
//--- align with values/signs of Lagrange multipliers
//--- * then, having quadratic term A and known constraints and Lagrange
//--- multipliers we determine linear term B which makes KKT conditions true
for(n=1; n<=8; n++)
{
for(akind=0; akind<=2; akind++)
{
//--- Select number used for absent bounds:
//--- * +- INF
//--- * 1e9
//--- * 1e11
i=CHighQualityRand::HQRndUniformI(rs,3);
minushuge=AL_NEGINF;
plushuge=AL_POSINF;
if(i==1)
{
minushuge=-1.0E9;
plushuge=1.0E9;
}
if(i==2)
{
minushuge=-1.0E11;
plushuge=1.0E11;
}
//--- Generate quadratic term A, origin and scale
a=matrix<double>::Zeros(n,n);
issemidefinite=true;
if(akind==1)
{
//--- Dense well conditioned SPD
CMatGen::SPDMatrixRndCond(n,50.0,a);
issemidefinite=false;
}
if(akind==2)
{
//--- Low rank semidefinite
tmp=vector<double>::Zeros(n);
for(k=1; k<=MathMin(3,n-1); k++)
{
for(i=0; i<n; i++)
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
v=MathPow(2,CHighQualityRand::HQRndNormal(rs));
for(i=0; i<n; i++)
for(j=0; j<n; j++)
a.Add(i,j,v*tmp[i]*tmp[j]);
}
issemidefinite=true;
}
s=vector<double>::Zeros(n);
xorigin=vector<double>::Zeros(n);
for(j=0; j<n; j++)
{
xorigin.Set(j,CHighQualityRand::HQRndNormal(rs));
s.Set(j,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
}
//--- Generate linear constraints (left parts)
rawccnt=CHighQualityRand::HQRndUniformI(rs,2*n+1);
rawc=matrix<double>::Zeros(rawccnt,n);
for(i=0; i<rawccnt; i++)
{
nnz=CHighQualityRand::HQRndUniformI(rs,n+1);
for(k=0; k<nnz; k++)
{
j=CHighQualityRand::HQRndUniformI(rs,n);
v=CHighQualityRand::HQRndNormal(rs);
v+=0.1*CApServ::PosSign(v);
rawc.Set(i,j,v);
}
}
//--- Generate Lagrange multipliers, with at most NActive<N being non-zero
lagbc=vector<double>::Zeros(n);
laglc=vector<double>::Zeros(rawccnt);
nactive=CHighQualityRand::HQRndUniformI(rs,n);
k=CHighQualityRand::HQRndUniformI(rs,MathMin(nactive,rawccnt)+1);
for(i=0; i<k; i++)
laglc.Set(CHighQualityRand::HQRndUniformI(rs,rawccnt),CHighQualityRand::HQRndNormal(rs));
for(i=k; i<nactive; i++)
lagbc.Set(CHighQualityRand::HQRndUniformI(rs,n),CHighQualityRand::HQRndNormal(rs));
//--- Generate solution and gradient at the solution, set B to -G
xf=vector<double>::Zeros(n);
for(i=0; i<n; i++)
xf.Set(i,CHighQualityRand::HQRndNormal(rs));
g=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
v=0;
for(j=0; j<n; j++)
v+=a.Get(i,j)*(xf[j]-xorigin[j]);
g.Set(i,v+lagbc[i]);
}
for(i=0; i<rawccnt; i++)
{
for(j=0; j<n; j++)
g.Add(j,laglc[i]*rawc.Get(i,j));
}
b=g*(-1.0)+0;
//--- Set up bounds according to Lagrange multipliers
//--- NOTE: for semidefinite problems we set all variable bounds
bndl=vector<double>::Full(n,minushuge);
bndu=vector<double>::Full(n,plushuge);
for(j=0; j<n; j++)
{
if(CHighQualityRand::HQRndNormal(rs)>0.0 || issemidefinite)
bndl.Set(j,xf[j]-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
if(CHighQualityRand::HQRndNormal(rs)>0.0 || issemidefinite)
bndu.Set(j,xf[j]+MathPow(2,CHighQualityRand::HQRndNormal(rs)));
if(lagbc[j]!=0.0)
{
if(CHighQualityRand::HQRndUniformR(rs)<0.15)
{
bndl.Set(j,xf[j]);
bndu.Set(j,xf[j]);
}
else
{
if(lagbc[j]<0.0)
bndl.Set(j,xf[j]);
if(lagbc[j]>0.0)
bndu.Set(j,xf[j]);
}
}
}
rawcl=vector<double>::Zeros(rawccnt);
rawcu=vector<double>::Zeros(rawccnt);
for(i=0; i<rawccnt; i++)
{
v=CAblasF::RDotVR(n,xf,rawc,i);
rawcl.Set(i,minushuge);
rawcu.Set(i,plushuge);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
rawcl.Set(i,v-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
if(CHighQualityRand::HQRndNormal(rs)>0.0)
rawcu.Set(i,v+MathPow(2,CHighQualityRand::HQRndNormal(rs)));
if(laglc[i]!=0.0)
{
if(CHighQualityRand::HQRndUniformR(rs)<0.15)
{
//--- Active equality constraint
rawcl.Set(i,v);
rawcu.Set(i,v);
}
else
{
//--- Active inequality constraint
if(laglc[i]<0.0)
rawcl.Set(i,v);
if(laglc[i]>0.0)
rawcu.Set(i,v);
}
}
if((!MathIsValidNumber(rawcl[i]) || rawcl[i]==minushuge) && (!MathIsValidNumber(rawcu[i]) || rawcu[i]==plushuge))
{
//--- At least one bound must be present for linear constraint
if(CHighQualityRand::HQRndNormal(rs)>0.0)
rawcl.Set(i,v-10);
else
rawcu.Set(i,v+10);
}
}
//--- Completely skip some solvers depending on their properties:
//--- * Dense-AUL solver has lower precision on rank-deficient
//--- problems, so we skip AKind=0 and AKind=2.
//--- * BLEIC solver is always skipped
if(solvertype==1 && akind!=1)
continue;
if(solvertype==0)
continue;
//--- Solve
CMinQP::MinQPCreate(n,state);
switch(solvertype)
{
case 0:
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
break;
case 1:
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
break;
case 2:
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
break;
case 3:
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
break;
default:
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
CMinQP::MinQPSetBC(state,bndl,bndu);
RandomlySplitAndSetLC2(rawc,rawcl,rawcu,rawccnt,n,state,rs);
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
CMinQP::MinQPSetScale(state,s);
CMinQP::MinQPSetOrigin(state,xorigin);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminqpunit.ap:5138");
CAp::SetErrorFlag(errorflag,CAp::Len(x1)<n,"testminqpunit.ap:5139");
CAp::SetErrorFlag(errorflag,CAp::Len(rep.m_lagbc)<n,"testminqpunit.ap:5140");
CAp::SetErrorFlag(errorflag,CAp::Len(rep.m_laglc)<rawccnt,"testminqpunit.ap:5141");
if(errorflag)
return;
//--- Test function value at the solution
f0=QuadraticTarget(a,b,n,x1);
f1=QuadraticTarget(a,b,n,xf);
CAp::SetErrorFlag(errorflag,MathAbs(f0-f1)>(CApServ::RMaxAbs3(f0,f1,1.0)*1.0E-3),"testminqpunit.ap:5150");
//--- Test Lagrange multipliers returned by the solver; test is skipped:
//--- * for BLEIC solver
//--- * for overconstrained DENSE-AUL
skiptest=false;
skiptest=skiptest||solvertype==0;
skiptest=skiptest||(solvertype==1 && (double)(nactive)>(double)(0.5*(n-1)));
if(!skiptest)
{
gtrial=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
v=b[i];
for(j=0; j<n; j++)
v+=a.Get(i,j)*(x1[j]-xorigin[j]);
gtrial.Set(i,v+rep.m_lagbc[i]);
}
for(i=0; i<rawccnt; i++)
gtrial+=rawc[i]*rep.m_laglc[i];
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,MathAbs(gtrial[i])>1.0E-3,"testminqpunit.ap:5176");
}
}
}
}
//--- Large-scale tests: a few selected tests for large N's
for(solvertype=0; solvertype<=3; solvertype++)
{
//--- General equality constrained problem:
//--- * N*N SPD (non-diagonal) A with moderate condition number
//--- * no box constraints
//--- * K<N equality constraints C*x = C*x0, where C is K*N well conditioned
//--- matrix, x0 is some random vector [-1,+1]
//--- * optimization problem has form 0.5*x'*A*x-b'*x,
//--- where b is random vector from [-1,+1]
//--- * true solution of QP problem is obtained with KKT matrix;
//--- we compare value returned by solver against one calculated
//--- via KKT conditions.
n=60;
rawccnt=40;
if(solvertype==0)
xtol=1.0E-3;
else
{
if((solvertype==1 || solvertype==2) || solvertype==3)
xtol=1.0E-3;
else
{
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
}
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),a);
CMatGen::RMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),t2);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
rawc=matrix<double>::Zeros(rawccnt,n+1);
rawct.Resize(rawccnt);
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
b.Set(i,2*CMath::RandomReal()-1);
xstart.Set(i,2*CMath::RandomReal()-1);
}
for(i=0; i<rawccnt; i++)
{
for(i_=0; i_<n; i_++)
rawc.Set(i,i_,t2.Get(i,i_));
v=CAblasF::RDotVR(n,x0,rawc,i);
rawc.Set(i,n,v);
rawct.Set(i,0);
}
CMinQP::MinQPCreate(n,state);
switch(solvertype)
{
case 0:
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
break;
case 1:
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
break;
case 2:
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
break;
case 3:
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
break;
default:
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
CMinQP::MinQPSetStartingPoint(state,xstart);
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:5255");
continue;
}
for(i=0; i<rawccnt; i++)
{
v=CAblasF::RDotVR(n,x1,rawc,i);
CAp::SetErrorFlag(errorflag,v<(rawc.Get(i,n)-xtol),"testminqpunit.ap:5261");
}
kkt=matrix<double>::Zeros(n+rawccnt,n+rawccnt);
kktright=vector<double>::Zeros(n+rawccnt);
CAblas::RMatrixCopy(n,n,a,0,0,kkt,0,0);
CAblas::RMatrixCopy(rawccnt,n,rawc,0,0,kkt,n,0);
CAblas::RMatrixTranspose(rawccnt,n,rawc,0,0,kkt,0,n);
for(i=0; i<n; i++)
kktright.Set(i,-b[i]);
for(i=0; i<rawccnt; i++)
kktright.Set(n+i,rawc.Get(i,n));
CFbls::FblsSolveLS(kkt,kktright,n+rawccnt,n+rawccnt,tmp0,tmp1,tmp2);
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,MathAbs(kktright[i]-x1[i])>(xtol*MathMax(MathAbs(kktright[i]),1.0)),"testminqpunit.ap:5280");
//--- General inequality constrained problem:
//--- * N*N SPD diagonal A with moderate condtion number
//--- * no box constraints
//--- * K<N inequality constraints C*x >= C*x0, where C is N*N well conditioned
//--- matrix, x0 is some random vector [-1,+1]
//--- * optimization problem has form 0.5*x'*A*x-b'*x,
//--- where b is random vector from [-1,+1]
//--- * using duality, we can obtain solution of QP problem as follows:
//--- a) intermediate problem min(0.5*y'*B*y + d'*y) s.t. y>=0
//--- is solved, where B = C*inv(A)*C', d = -(C*inv(A)*b + C*x0)
//--- b) after we got dual solution ys, we calculate primal solution
//--- xs = inv(A)*(C'*ys-b)
n=60;
rawccnt=40;
if(solvertype==0)
xtol=1.0E-3;
else
{
if((solvertype==1 || solvertype==2) || solvertype==3)
xtol=1.0E-3;
else
{
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
}
da=vector<double>::Zeros(n);
a=matrix<double>::Zeros(n,n);
CMatGen::RMatrixRndCond(n,MathPow(10.0,2*CMath::RandomReal()),t2);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
rawc=matrix<double>::Zeros(rawccnt,n+1);
rawct.Resize(rawccnt);
for(i=0; i<n; i++)
{
da.Set(i,MathExp(8*CMath::RandomReal()-4));
a.Set(i,i,da[i]);
}
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
b.Set(i,2*CMath::RandomReal()-1);
xstart.Set(i,2*CMath::RandomReal()-1);
}
for(i=0; i<rawccnt; i++)
{
for(i_=0; i_<n; i_++)
rawc.Set(i,i_,t2.Get(i,i_));
v=CAblasF::RDotVR(n,x0,rawc,i);
rawc.Set(i,n,v);
rawct.Set(i,1);
}
CMinQP::MinQPCreate(n,state);
switch(solvertype)
{
case 0:
CMinQP::MinQPSetAlgoBLEIC(state,0.0,0.0,bleicepsx,0);
break;
case 1:
CMinQP::MinQPSetAlgoDenseAUL(state,aulepsx,aulrho,aulits);
break;
case 2:
CMinQP::MinQPSetAlgoDenseIPM(state,ipmeps);
break;
case 3:
CMinQP::MinQPSetAlgoSparseIPM(state,ipmeps);
break;
default:
CAp::Assert(false,"unexpected solver type");
errorflag=true;
return;
}
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(a,n,state,rs);
CMinQP::MinQPSetStartingPoint(state,xstart);
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,x1,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:5362");
continue;
}
for(i=0; i<rawccnt; i++)
{
v=CAblasF::RDotVR(n,x1,rawc,i);
CAp::SetErrorFlag(errorflag,v<(rawc.Get(i,n)-xtol),"testminqpunit.ap:5368");
}
t3=matrix<double>::Zeros(n,rawccnt);
a2=matrix<double>::Zeros(rawccnt,rawccnt);
CAblas::RMatrixTranspose(rawccnt,n,rawc,0,0,t3,0,0);
for(i=0; i<n; i++)
{
v=1/MathSqrt(da[i]);
for(i_=0; i_<rawccnt; i_++)
t3.Mul(i,i_,v);
}
CAblas::RMatrixSyrk(rawccnt,n,1.0,t3,0,0,2,0.0,a2,0,0,true);
tmp0=b/da+0;
b2=vector<double>::Zeros(rawccnt);
for(i=0; i<rawccnt; i++)
{
v=CAblasF::RDotVR(n,tmp0,rawc,i);
b2.Set(i,-(v+rawc.Get(i,n)));
}
bndl=vector<double>::Zeros(rawccnt);
bndu=vector<double>::Full(rawccnt,AL_POSINF);
CMinQP::MinQPCreate(rawccnt,state2);
CMinQP::MinQPSetAlgoQuickQP(state2,0.0,0.0,1.0E-9,0,true);
CMinQP::MinQPSetLinearTerm(state2,b2);
CMinQP::MinQPSetQuadraticTerm(state2,a2,true);
CMinQP::MinQPSetBC(state2,bndl,bndu);
CMinQP::MinQPOptimize(state2);
CMinQP::MinQPResults(state2,x2,rep2);
if(rep2.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:5421");
continue;
}
for(i=0; i<n; i++)
{
v=0.0;
for(i_=0; i_<rawccnt; i_++)
v+=rawc.Get(i_,i)*x2[i_];
tmp0.Set(i,(v-b[i])/da[i]);
}
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,(double)(MathAbs(tmp0[i]-x1[i]))>(double)(xtol*MathMax(MathAbs(tmp0[i]),1.0)),"testminqpunit.ap:5434");
}
}
//+------------------------------------------------------------------+
//| This function tests special inequality constrained QP problems. |
//| Returns True on errors. |
//+------------------------------------------------------------------+
bool CTestMinQPUnit::SpecialICQPTests(void)
{
//--- create variables
bool result;
CMatrixDouble a;
CMatrixDouble c;
CRowDouble xstart;
CRowDouble xend;
CRowDouble xexact;
CRowDouble b;
CRowDouble bndl;
CRowDouble bndu;
CRowInt ct;
CMinQPState state;
CMinQPReport rep;
bool waserrors=false;
int i=0;
int j=0;
//--- Test 1: reported by Vanderlande Industries.
//--- Tests algorithm ability to handle degenerate constraints.
a=matrix<double>::Identity(3,3);
b.Resize(3);
b.Set(0,-50);
b.Set(1,-50);
b.Set(2,-75);
bndl=vector<double>::Zeros(3);
bndu=vector<double>::Full(3,100);
bndu.Set(2,150);
xstart=vector<double>::Zeros(3);
xstart.Set(1,100);
xexact=xstart;
xexact.Set(2,50);
c=matrix<double>::Zeros(3,4);
c.Set(0,0,1);
c.Set(0,1,-1);
c.Set(0,2,0);
c.Set(0,3,-100);
c.Set(1,0,1);
c.Set(1,1,0);
c.Set(1,2,-1);
c.Set(1,3,0);
c.Set(2,0,-1);
c.Set(2,1,0);
c.Set(2,2,1);
c.Set(2,3,50);
ct.Resize(3);
ct.Set(0,-1);
ct.Set(1,-1);
ct.Set(2,-1);
CMinQP::MinQPCreate(3,state);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,true);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPSetLC(state,c,ct,3);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend,rep);
if(rep.m_terminationtype>0)
{
for(i=0; i<=2; i++)
waserrors=waserrors||(double)(MathAbs(xend[i]-xexact[i]))>(1.0E6*CMath::m_machineepsilon);
}
else
waserrors=true;
//--- Test 2: reported by Vanderlande Industries.
//--- Tests algorithm ability to handle degenerate constraints.
a=matrix<double>::Identity(3,3);
b=vector<double>::Full(3,-50);
b.Set(2,-75);
bndl=vector<double>::Zeros(3);
bndu=vector<double>::Full(3,100);
bndu.Set(2,150);
xstart=bndu;
xstart.Set(0,0);
xexact=xstart;
xexact.Set(2,100);
c=matrix<double>::Zeros(3,4);
c.Set(0,0,1);
c.Set(0,1,-1);
c.Set(0,3,-100);
c.Set(1,1,1);
c.Set(1,2,-1);
c.Set(2,1,-1);
c.Set(2,2,1);
c.Set(2,3,50);
ct.Resize(3);
ct.Fill(-1);
CMinQP::MinQPCreate(3,state);
SetRandomAlgoConvexLC(state);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetQuadraticTerm(state,a,true);
CMinQP::MinQPSetStartingPoint(state,xstart);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPSetLC(state,c,ct,3);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xend,rep);
if(rep.m_terminationtype>0)
{
for(i=0; i<=2; i++)
waserrors=waserrors||(double)(MathAbs(xend[i]-xexact[i]))>(1.0E6*CMath::m_machineepsilon);
}
else
waserrors=true;
//--- return result
result=waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| This function tests linearly constrained DENSE-AUL solver |
//| On failure sets Err to True; on success leaves it unchanged. |
//+------------------------------------------------------------------+
void CTestMinQPUnit::DenseAULTests(bool &errorflag)
{
CHighQualityRandState rs;
int n=0;
int i=0;
int j=0;
int k=0;
double v=0;
double vv=0;
int scaletype=0;
CMatrixDouble rawa;
CMatrixDouble z;
CRowDouble bndl;
CRowDouble bndu;
CMatrixDouble rawc;
CRowInt rawct;
int rawccnt=0;
CRowDouble b;
CRowDouble x0;
CRowDouble xf;
CRowDouble r;
CRowDouble xsol;
CRowDouble s;
CMinQPState state;
CMinQPReport rep;
double epsx=0;
double xtol=0;
double rho=0;
int outerits=0;
CDenseSolverReport svrep;
int i_=0;
CHighQualityRand::HQRndRandomize(rs);
//--- Test that unconstrained problem is solved with high precision,
//--- independently of Rho and/or outer iterations count.
//--- 50% of problems are rescaled wildly (with scale being passed to
//--- the solver).
epsx=1.0E-12;
xtol=1.0E-7;
for(n=1; n<=10; n++)
{
for(scaletype=0; scaletype<=1; scaletype++)
{
//--- Generate random A, b, X0 and XSOL
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa);
b=vector<double>::Zeros(n);
xsol=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
xsol.Set(i,CHighQualityRand::HQRndNormal(rs));
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xsol,rawa,i);
b.Set(i,-v);
}
//--- Generate scale vector, apply it
s=vector<double>::Ones(n);
if(scaletype>0)
for(i=0; i<n; i++)
s.Set(i,MathPow(10,CHighQualityRand::HQRndUniformR(rs)*20-10));
for(i=0; i<n; i++)
for(j=0; j<n; j++)
rawa.Mul(i,j,1.0/(s[i]*s[j]));
b/=s;
x0*=s;
xsol*=s;
//--- Create optimizer, solve
rho=MathPow(10,2*CHighQualityRand::HQRndUniformR(rs));
outerits=CHighQualityRand::HQRndUniformI(rs,5);
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoDenseAUL(state,epsx,rho,outerits);
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
CMinQP::MinQPSetScale(state,s);
CMinQP::MinQPSetStartingPoint(state,x0);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xf,rep);
//--- Compare against analytically known solution
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:7154");
return;
}
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,(double)(MathAbs(xf[i]-xsol[i])/s[i])>xtol,"testminqpunit.ap:7158");
}
}
//--- Test that problem with zero constraint matrix can be solved
//--- (with high precision). We do not perform any additional "tweaks"
//--- like scaling of variables, just want to test ability to handle
//--- zero matrices.
epsx=1.0E-12;
xtol=1.0E-7;
for(n=1; n<=10; n++)
{
//--- Generate random A, b, X0 and XSOL
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa);
b=vector<double>::Zeros(n);
xsol=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
xsol.Set(i,CHighQualityRand::HQRndNormal(rs));
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xsol,rawa,i);
b.Set(i,-v);
}
rawccnt=CHighQualityRand::HQRndUniformI(rs,2*n);
rawc=matrix<double>::Zeros(rawccnt,n+1);
rawct.Resize(rawccnt);
for(i=0; i<rawccnt; i++)
rawct.Set(i,CHighQualityRand::HQRndUniformI(rs,3)-1);
//--- Create optimizer, solve
rho=MathPow(10,2*CHighQualityRand::HQRndUniformR(rs));
outerits=CHighQualityRand::HQRndUniformI(rs,5);
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoDenseAUL(state,epsx,rho,outerits);
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
CMinQP::MinQPSetLC(state,rawc,rawct,rawccnt);
CMinQP::MinQPSetStartingPoint(state,x0);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xf,rep);
//--- Compare against analytically known solution
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:7218");
return;
}
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,MathAbs(xf[i]-xsol[i])>xtol,"testminqpunit.ap:7222");
}
//--- Test that box/linearly inequality constrained problem with ALL constraints
//--- being inactive at BOTH initial and final points is solved with high precision.
//--- 50% of problems are rescaled wildly (with scale being passed to
//--- the solver).
epsx=1.0E-12;
xtol=1.0E-7;
for(n=1; n<=10; n++)
{
for(scaletype=0; scaletype<=1; scaletype++)
{
//--- Generate random A, b, X0 and XSOL
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa);
b=vector<double>::Zeros(n);
xsol=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
xsol.Set(i,CHighQualityRand::HQRndNormal(rs));
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xsol,rawa,i);
b.Set(i,-v);
}
//--- Generate such set of inequality constraints that ALL
//--- constraints are inactive at both X0 and XSOL.
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
bndl.Set(i,MathMin(x0[i],xsol[i])-1-CHighQualityRand::HQRndUniformR(rs));
bndu.Set(i,MathMax(x0[i],xsol[i])+1+CHighQualityRand::HQRndUniformR(rs));
}
rawccnt=CHighQualityRand::HQRndUniformI(rs,2*n);
rawc=matrix<double>::Zeros(rawccnt,n+1);
rawct.Resize(rawccnt);
for(i=0; i<rawccnt; i++)
{
v=0;
vv=0;
for(j=0; j<n; j++)
{
rawc.Set(i,j,CHighQualityRand::HQRndNormal(rs));
v+=rawc.Get(i,j)*x0[j];
vv+=rawc.Get(i,j)*xsol[j];
}
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
rawct.Set(i,1);
rawc.Set(i,n,MathMin(v,vv)-1-CHighQualityRand::HQRndUniformR(rs));
}
else
{
rawct.Set(i,-1);
rawc.Set(i,n,MathMax(v,vv)+1+CHighQualityRand::HQRndUniformR(rs));
}
}
//--- Generate scale vector, apply it
s=vector<double>::Ones(n);
if(scaletype>0)
for(i=0; i<n; i++)
s.Set(i,MathPow(10,CHighQualityRand::HQRndUniformR(rs)*20-10));
for(i=0; i<n; i++)
for(j=0; j<n; j++)
rawa.Mul(i,j,1.0/(s[i]*s[j]));
b/=s;
x0*=s;
xsol*=s;
bndl*=s;
bndu*=s;
for(i=0; i<rawccnt; i++)
{
for(j=0; j<n; j++)
rawc.Mul(i,j,1.0/s[j]);
}
//--- Create optimizer, solve
rho=100.0;
outerits=1;
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetStartingPoint(state,x0);
CMinQP::MinQPSetAlgoDenseAUL(state,epsx,rho,outerits);
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
CMinQP::MinQPSetBC(state,bndl,bndu);
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
CMinQP::MinQPSetScale(state,s);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xf,rep);
//--- Compare against analytically known solution
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:7339");
return;
}
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,(MathAbs(xf[i]-xsol[i])/s[i])>xtol,"testminqpunit.ap:7343");
}
}
//--- Test that linear equality constrained problem is solved with high precision.
//--- 50% of problems are rescaled wildly (variable scaling, with scale being
//--- passed to the solver).
epsx=1.0E-12;
xtol=1.0E-6;
for(n=1; n<=10; n++)
{
for(scaletype=0; scaletype<=1; scaletype++)
{
//--- Generate random A, b, X0, constraints
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa);
b=vector<double>::Zeros(n);
xsol=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
b.Set(i,CHighQualityRand::HQRndNormal(rs));
}
CMatGen::RMatrixRndCond(n,10,z);
rawccnt=MathMax(n-2,0);
if(rawccnt>0)
{
rawc=matrix<double>::Zeros(rawccnt,n+1);
rawct.Resize(rawccnt);
for(i=0; i<rawccnt; i++)
{
for(j=0; j<n; j++)
rawc.Set(i,j,z.Get(i,j));
rawct.Set(i,0);
rawc.Set(i,n,CHighQualityRand::HQRndNormal(rs));
}
}
//--- Generate scale vector, apply it
s=vector<double>::Ones(n);
if(scaletype>0)
for(i=0; i<n; i++)
{
s.Set(i,MathPow(10,CHighQualityRand::HQRndUniformR(rs)*20-10));
}
for(i=0; i<n; i++)
for(j=0; j<n; j++)
rawa.Mul(i,j,1.0/(s[i]*s[j]));
b/=s;
x0*=s;
xsol*=s;
for(i=0; i<rawccnt; i++)
for(j=0; j<n; j++)
rawc.Mul(i,j,1.0/s[j]);
//--- Create optimizer, solve
rho=100.0;
outerits=3;
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoDenseAUL(state,epsx,rho,outerits);
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
CMinQP::MinQPSetScale(state,s);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xf,rep);
//--- Solve problem analytically using Lagrangian approach
z=matrix<double>::Zeros(n+rawccnt,n+rawccnt);
r=vector<double>::Zeros(n+rawccnt);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
z.Set(i,j,rawa.Get(i,j));
for(i=0; i<n; i++)
r.Set(i,-b[i]);
for(i=0; i<rawccnt; i++)
{
for(j=0; j<n; j++)
{
z.Set(n+i,j,rawc.Get(i,j));
z.Set(j,n+i,rawc.Get(i,j));
}
r.Set(n+i,rawc.Get(i,n));
}
CDenseSolver::RMatrixSolve(z,n+rawccnt,r,k,svrep,xsol);
//--- check
if(!CAp::Assert(k>0,"MinQPTest: integrity check failed"))
{
errorflag=true;
return;
}
//--- Compare against analytically known solution
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:7453");
return;
}
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,(MathAbs(xf[i]-xsol[i])/s[i])>xtol,"testminqpunit.ap:7457");
}
}
//--- MEDIUM-SCALE VERSION OF PREVIOUS TEST.
//--- Test that linear equality constrained problem is solved with high precision.
//--- 50% of problems are rescaled wildly (variable scaling, with scale being
//--- passed to the solver).
epsx=1.0E-12;
xtol=1.0E-6;
for(n=99; n<=101; n++)
{
for(scaletype=0; scaletype<=1; scaletype++)
{
//--- Generate random A, b, X0, constraints
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa);
b=vector<double>::Zeros(n);
xsol=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
b.Set(i,CHighQualityRand::HQRndNormal(rs));
}
CMatGen::RMatrixRndCond(n,10,z);
rawccnt=MathMax(n-2,0);
if(rawccnt>0)
{
rawc=matrix<double>::Zeros(rawccnt,n+1);
rawct.Resize(rawccnt);
for(i=0; i<rawccnt; i++)
{
for(j=0; j<n; j++)
rawc.Set(i,j,z.Get(i,j));
rawct.Set(i,0);
rawc.Set(i,n,CHighQualityRand::HQRndNormal(rs));
}
}
//--- Generate scale vector, apply it
s=vector<double>::Ones(n);
if(scaletype>0)
for(i=0; i<n; i++)
s.Set(i,MathPow(10,CHighQualityRand::HQRndUniformR(rs)*20-10));
for(i=0; i<n; i++)
for(j=0; j<n; j++)
rawa.Mul(i,j,1.0/(s[i]*s[j]));
b/=s;
x0*=s;
xsol*=s;
for(i=0; i<rawccnt; i++)
for(j=0; j<n; j++)
rawc.Mul(i,j,1.0/s[j]);
//--- Create optimizer, solve
rho=100.0;
outerits=3;
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoDenseAUL(state,epsx,rho,outerits);
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
CMinQP::MinQPSetScale(state,s);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xf,rep);
//--- Solve problem analytically using Lagrangian approach
z=matrix<double>::Zeros(n+rawccnt,n+rawccnt);
r=vector<double>::Zeros(n+rawccnt);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
z.Set(i,j,rawa.Get(i,j));
for(i=0; i<n; i++)
r.Set(i,-b[i]);
for(i=0; i<rawccnt; i++)
{
for(j=0; j<n; j++)
{
z.Set(n+i,j,rawc.Get(i,j));
z.Set(j,n+i,rawc.Get(i,j));
}
r.Set(n+i,rawc.Get(i,n));
}
CDenseSolver::RMatrixSolve(z,n+rawccnt,r,k,svrep,xsol);
if(!CAp::Assert(k>0,"MinQPTest: integrity check failed"))
{
errorflag=true;
return;
}
//--- Compare against analytically known solution
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:7569");
return;
}
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,(MathAbs(xf[i]-xsol[i])/s[i])>xtol,"testminqpunit.ap:7573");
}
}
//--- Test that constraints are automatically scaled to adapt to problem curvature
//--- (that multiplication of A and b by some large/small number does not affect
//--- solver).
//--- We generate random well-scaled problem, and multiply A/b by some large/small number,
//--- and test that problem is still solved with high precision.
//--- NOTE: just to make things worse, we rescale variables randomly, but primary
//--- idea of this test is to check for multiplication of A/B
epsx=1.0E-12;
xtol=1.0E-6;
for(n=1; n<=10; n++)
{
for(scaletype=-1; scaletype<=1; scaletype++)
{
//--- Generate random A, b, X0, constraints
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),rawa);
b=vector<double>::Zeros(n);
xsol=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
b.Set(i,CHighQualityRand::HQRndNormal(rs));
}
CMatGen::RMatrixRndCond(n,10,z);
rawccnt=MathMax(n-2,0);
if(rawccnt>0)
{
rawc=matrix<double>::Zeros(rawccnt,n+1);
rawct.Resize(rawccnt);
for(i=0; i<rawccnt; i++)
{
for(j=0; j<n; j++)
rawc.Set(i,j,z.Get(i,j));
rawct.Set(i,0);
rawc.Set(i,n,CHighQualityRand::HQRndNormal(rs));
}
}
//--- Solve problem analytically using Lagrangian approach
z=matrix<double>::Zeros(n+rawccnt,n+rawccnt);
r=vector<double>::Zeros(n+rawccnt);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
z.Set(i,j,rawa.Get(i,j));
for(i=0; i<n; i++)
r.Set(i,-b[i]);
for(i=0; i<rawccnt; i++)
{
for(j=0; j<n; j++)
{
z.Set(n+i,j,rawc.Get(i,j));
z.Set(j,n+i,rawc.Get(i,j));
}
r.Set(n+i,rawc.Get(i,n));
}
CDenseSolver::RMatrixSolve(z,n+rawccnt,r,k,svrep,xsol);
if(rawccnt>0)
xsol.Resize(n);
if(!CAp::Assert(k>0,"MinQPTest: integrity check failed"))
{
errorflag=true;
return;
}
//--- Generate scale vector, apply it
s=vector<double>::Ones(n);
for(i=0; i<n; i++)
s.Set(i,MathPow(10,CHighQualityRand::HQRndUniformR(rs)*20-10));
for(i=0; i<n; i++)
for(j=0; j<n; j++)
rawa.Mul(i,j,1.0/(s[i]*s[j]));
b/=s;
x0*=s;
xsol*=s;
for(i=0; i<rawccnt; i++)
for(j=0; j<n; j++)
rawc.Mul(i,j,1.0/s[j]);
//--- Multiply A/B by some coefficient with wild magnitude
v=MathPow(10,10*scaletype);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
rawa.Mul(i,j,v);
b*=v;
//--- Create optimizer, solve
rho=100.0;
outerits=3;
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoDenseAUL(state,epsx,rho,outerits);
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
RandomlySplitAndSetLCLegacy(rawc,rawct,rawccnt,n,state,rs);
CMinQP::MinQPSetScale(state,s);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xf,rep);
//--- Compare against analytically known solution
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:7696");
return;
}
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,(double)(MathAbs(xf[i]-xsol[i])/s[i])>xtol,"testminqpunit.ap:7700");
}
}
//--- Test that problem with slight negative curvature f = -0.001*|x|^2
//--- subject to general linear (!) constraints -1 <= x[i] <= +1 is
//--- correctly solved. Initial point is in [-0.001,+0.001] range.
//--- NOTE: this test is mostly intended for working set selection algorithm;
//--- it must correctly detect non-SPD problems and select full working
//--- set; starting with zero working set will result in recognition of
//--- problem as unbounded one and premature termination of algorithm.
epsx=1.0E-12;
xtol=1.0E-6;
for(n=1; n<=10; n++)
{
//--- Generate A, b, X0, constraints
rawa=matrix<double>::Zeros(n,n);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
rawa.Set(i,i,-0.001);
x0.Set(i,0.001*CHighQualityRand::HQRndNormal(rs));
}
rawc=matrix<double>::Zeros(2*n,n+1);
rawct.Resize(2*n);
for(i=0; i<n; i++)
{
rawc.Set(2*i,i,1);
rawc.Set(2*i+1,i,1);
rawc.Set(2*i,n,-1);
rawc.Set(2*i+1,n,1);
rawct.Set(2*i,1);
rawct.Set(2*i+1,-1);
}
//--- Create optimizer, solve
rho=100.0;
outerits=5;
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoDenseAUL(state,epsx,rho,outerits);
CMinQP::MinQPSetStartingPoint(state,x0);
CMinQP::MinQPSetQuadraticTerm(state,rawa,true);
CMinQP::MinQPSetLinearTerm(state,b);
CMinQP::MinQPSetLC(state,rawc,rawct,2*n);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xf,rep);
//--- Test
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminqpunit.ap:7765");
if(errorflag)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,(double)(MathAbs(MathAbs(xf[i])-1))>xtol,"testminqpunit.ap:7769");
}
}
//+------------------------------------------------------------------+
//| This function tests IPM QP solver |
//| On failure sets Err to True; on success leaves it unchanged. |
//+------------------------------------------------------------------+
void CTestMinQPUnit::IPMTests(bool &errorflag)
{
//--- create variables
CHighQualityRandState rs;
int n=0;
int nmain=0;
int nslack=0;
int i=0;
int j=0;
int k=0;
double v=0;
int nnz=0;
int nactive=0;
double f0=0;
double f1=0;
CMatrixDouble maina;
CMatrixDouble fulla;
CRowDouble s;
CRowDouble xorigin;
CRowDouble xf;
CRowDouble x1;
CRowDouble g;
CRowDouble b;
CRowDouble gtrial;
CRowDouble bndl;
CRowDouble bndu;
int akind=0;
CMatrixDouble rawc;
CRowDouble rawcl;
CRowDouble rawcu;
int rawccnt=0;
CSparseMatrix sparsec;
int sparseccnt=0;
CMatrixDouble densec;
int denseccnt=0;
bool issemidefinite;
CRowDouble tmp;
CRowDouble lagbc;
CRowDouble laglc;
CSparseMatrix dummysparse;
CVIPMState vsolver;
CRowDouble replagbc;
CRowDouble replaglc;
int repterminationtype=0;
double epsx=0;
double xtol=0;
CRowDouble xsol;
CRowDouble x0;
CRowInt rawct;
CMinQPState state;
CMinQPReport rep;
int i_=0;
CHighQualityRand::HQRndRandomize(rs);
//--- Test handling of slack variables by IPM solver. We use IPM directly,
//--- without wrapping it with MinQP interface layer.
//--- Convex optimization problem with known answer (we generate constraints
//--- and Lagrange coefficients, and then generate quadratic and linear term
//--- which satisfy KKT conditions for given Lagrange coefficients):
//--- * N=2..8
//--- * f = 0.5*x'*A*x+b'*x
//--- * several kinds of A are tried: zero, well conditioned SPD, low rank
//--- * initial point x0 = [0 0 ... 0 0]
//--- * first, we generate set of linear constraints (without bounds),
//--- solution point X and Lagrange multipliers for linear and box constraints
//--- * then we determine bounds on variables and linear constraints which
//--- align with values/signs of Lagrange multipliers
//--- * then, having quadratic term A and known constraints and Lagrange
//--- multipliers we determine linear term B which makes KKT conditions true
epsx=1.0E-12;
for(nmain=1; nmain<=8; nmain++)
{
for(nslack=0; nslack<=8; nslack++)
{
for(akind=0; akind<=2; akind++)
{
n=nmain+nslack;
//--- Generate quadratic term A, origin and scale
maina=matrix<double>::Zeros(nmain,nmain);
issemidefinite=true;
if(akind==1)
{
//--- Dense well conditioned SPD
CMatGen::SPDMatrixRndCond(nmain,50.0,maina);
issemidefinite=false;
}
if(akind==2)
{
//--- Low rank semidefinite
tmp=vector<double>::Zeros(nmain);
for(k=1; k<=MathMin(3,nmain-1); k++)
{
for(i=0; i<nmain; i++)
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
v=MathPow(2,CHighQualityRand::HQRndNormal(rs));
for(i=0; i<nmain; i++)
for(j=0; j<nmain; j++)
maina.Add(i,j,tmp[i]*tmp[j]);
}
issemidefinite=true;
}
issemidefinite=issemidefinite||nslack>0;
fulla=matrix<double>::Zeros(n,n);
for(i=0; i<nmain; i++)
for(j=0; j<nmain; j++)
fulla.Set(i,j,maina.Get(i,j));
s=vector<double>::Zeros(n);
xorigin=vector<double>::Zeros(n);
for(j=0; j<n; j++)
{
xorigin.Set(j,CHighQualityRand::HQRndNormal(rs));
s.Set(j,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
}
//--- Generate linear constraints (left parts)
rawccnt=CHighQualityRand::HQRndUniformI(rs,2*n+1);
if(rawccnt>0)
{
rawc=matrix<double>::Zeros(rawccnt,n);
for(i=0; i<rawccnt; i++)
{
nnz=CHighQualityRand::HQRndUniformI(rs,nmain+1);
for(k=0; k<nnz; k++)
{
j=CHighQualityRand::HQRndUniformI(rs,nmain);
v=CHighQualityRand::HQRndNormal(rs);
v+=0.1*CApServ::PosSign(v);
rawc.Set(i,j,v);
}
}
for(j=nmain; j<n; j++)
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
v=CHighQualityRand::HQRndNormal(rs);
v+=0.1*CApServ::PosSign(v);
rawc.Set(CHighQualityRand::HQRndUniformI(rs,rawccnt),j,v);
}
}
//--- Generate Lagrange multipliers, with 0<=NActive<=N-1 being non-zero
lagbc=vector<double>::Zeros(n);
laglc=vector<double>::Zeros(rawccnt);
nactive=CHighQualityRand::HQRndUniformI(rs,n);
k=CHighQualityRand::HQRndUniformI(rs,MathMin(nactive,rawccnt)+1);
for(i=0; i<k; i++)
{
v=CHighQualityRand::HQRndNormal(rs);
v+=0.01*CApServ::PosSign(v);
laglc.Set(CHighQualityRand::HQRndUniformI(rs,rawccnt),v);
}
for(i=k; i<nactive; i++)
{
v=CHighQualityRand::HQRndNormal(rs);
v+=0.01*CApServ::PosSign(v);
lagbc.Set(CHighQualityRand::HQRndUniformI(rs,n),v);
}
//--- Generate solution and gradient at the solution, set B to -G
xf=vector<double>::Zeros(n);
for(i=0; i<n; i++)
xf.Set(i,CHighQualityRand::HQRndNormal(rs));
g=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
v=0;
for(j=0; j<n; j++)
v+=fulla.Get(i,j)*(xf[j]-xorigin[j]);
g.Set(i,v+lagbc[i]);
}
for(i=0; i<rawccnt; i++)
for(j=0; j<n; j++)
g.Add(j,laglc[i]*rawc.Get(i,j));
b=g*(-1.0)+0;
//--- Set up bounds according to Lagrange multipliers
//--- NOTE: for semidefinite problems we set all variable bounds
bndl=vector<double>::Full(n,AL_NEGINF);
bndu=vector<double>::Full(n,AL_POSINF);
for(j=0; j<n; j++)
{
if((CHighQualityRand::HQRndNormal(rs)>0.0 || issemidefinite) || j>=nmain)
bndl.Set(j,xf[j]-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
if((CHighQualityRand::HQRndNormal(rs)>0.0 || issemidefinite) || j>=nmain)
bndu.Set(j,xf[j]+MathPow(2,CHighQualityRand::HQRndNormal(rs)));
if(lagbc[j]!=0.0)
{
if(CHighQualityRand::HQRndUniformR(rs)<0.15)
{
bndl.Set(j,xf[j]);
bndu.Set(j,xf[j]);
}
else
{
if(lagbc[j]<0.0)
bndl.Set(j,xf[j]);
if(lagbc[j]>0.0)
bndu.Set(j,xf[j]);
}
}
}
rawcl=vector<double>::Full(rawccnt,AL_NEGINF);
rawcu=vector<double>::Full(rawccnt,AL_POSINF);
for(i=0; i<rawccnt; i++)
{
v=CAblasF::RDotVR(n,xf,rawc,i);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
rawcl.Set(i,v-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
if(CHighQualityRand::HQRndNormal(rs)>0.0)
rawcu.Set(i,v+MathPow(2,CHighQualityRand::HQRndNormal(rs)));
if(laglc[i]!=0.0)
{
if(CHighQualityRand::HQRndUniformR(rs)<0.15)
{
//--- Active equality constraint
rawcl.Set(i,v);
rawcu.Set(i,v);
}
else
{
//--- Active inequality constraint
if(laglc[i]<0.0)
rawcl.Set(i,v);
if(laglc[i]>0.0)
rawcu.Set(i,v);
}
}
if(!MathIsValidNumber(rawcl[i]) && !MathIsValidNumber(rawcu[i]))
{
//--- At least one bound must be present for linear constraint
if(CHighQualityRand::HQRndNormal(rs)>0.0)
rawcl.Set(i,v-10);
else
rawcu.Set(i,v+10);
}
}
//--- Randomly split constraints into dense and sparse parts
sparseccnt=CHighQualityRand::HQRndUniformI(rs,rawccnt+1);
denseccnt=rawccnt-sparseccnt;
if(sparseccnt>0)
{
CSparse::SparseCreate(sparseccnt,n,0,sparsec);
for(i=0; i<sparseccnt; i++)
for(j=0; j<n; j++)
CSparse::SparseSet(sparsec,i,j,rawc.Get(i,j));
CSparse::SparseConvertToCRS(sparsec);
}
if(denseccnt>0)
{
densec=matrix<double>::Zeros(denseccnt,n);
for(i=0; i<denseccnt; i++)
for(j=0; j<n; j++)
densec.Set(i,j,rawc.Get(i+sparseccnt,j));
}
//--- Solve
CVIPMSolver::VIPMInitDenseWithSlacks(vsolver,s,xorigin,nmain,n);
CVIPMSolver::VIPMSetQuadraticLinear(vsolver,fulla,dummysparse,0,CHighQualityRand::HQRndNormal(rs)>0.0,b);
CVIPMSolver::VIPMSetConstraints(vsolver,bndl,bndu,sparsec,sparseccnt,densec,denseccnt,rawcl,rawcu);
CVIPMSolver::VIPMSetCond(vsolver,epsx,epsx,epsx);
CVIPMSolver::VIPMOptimize(vsolver,false,x1,replagbc,replaglc,repterminationtype);
CAp::SetErrorFlag(errorflag,repterminationtype<=0,"testminqpunit.ap:8095");
CAp::SetErrorFlag(errorflag,CAp::Len(x1)<n,"testminqpunit.ap:8096");
CAp::SetErrorFlag(errorflag,CAp::Len(replagbc)<n,"testminqpunit.ap:8097");
CAp::SetErrorFlag(errorflag,CAp::Len(replaglc)<rawccnt,"testminqpunit.ap:8098");
if(errorflag)
return;
//--- Test function value at the solution
f0=QuadraticTarget(fulla,b,n,x1);
f1=QuadraticTarget(fulla,b,n,xf);
CAp::SetErrorFlag(errorflag,MathAbs(f0-f1)>(CApServ::RMaxAbs3(f0,f1,1.0)*1.0E-3),"testminqpunit.ap:8107");
//--- Test Lagrange multipliers returned by the solver
gtrial=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
v=b[i];
for(j=0; j<n; j++)
v+=fulla.Get(i,j)*(x1[j]-xorigin[j]);
gtrial.Set(i,v+replagbc[i]);
}
for(i=0; i<rawccnt; i++)
{
for(j=0; j<n; j++)
gtrial.Add(j,replaglc[i]*rawc.Get(i,j));
}
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,(double)(MathAbs(gtrial[i]))>1.0E-3,"testminqpunit.ap:8126");
}
}
}
//--- Test that problem with zero constraint matrix can be solved
//--- (with high precision). We do not perform any additional "tweaks"
//--- like scaling of variables, just want to test ability to handle
//--- zero matrices.
epsx=1.0E-8;
xtol=1.0E-5;
for(n=1; n<=10; n++)
{
//--- Generate random A, b, X0 and XSOL
CMatGen::SPDMatrixRndCond(n,MathPow(10.0,3*CHighQualityRand::HQRndUniformR(rs)),fulla);
b=vector<double>::Zeros(n);
xsol=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
xsol.Set(i,CHighQualityRand::HQRndNormal(rs));
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xsol,fulla,i);
b.Set(i,-v);
}
rawccnt=CHighQualityRand::HQRndUniformI(rs,2*n);
rawc=matrix<double>::Zeros(rawccnt,n+1);
rawct.Resize(rawccnt);
for(i=0; i<rawccnt; i++)
rawct.Set(i,CHighQualityRand::HQRndUniformI(rs,3)-1);
//--- Create optimizer, solve
CMinQP::MinQPCreate(n,state);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinQP::MinQPSetAlgoDenseIPM(state,epsx);
else
CMinQP::MinQPSetAlgoSparseIPM(state,epsx);
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(fulla,n,state,rs);
CMinQP::MinQPSetLC(state,rawc,rawct,rawccnt);
CMinQP::MinQPSetStartingPoint(state,x0);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xf,rep);
//--- Compare against analytically known solution
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(errorflag,true,"testminqpunit.ap:8187");
return;
}
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,(double)(MathAbs(xf[i]-xsol[i]))>xtol,"testminqpunit.ap:8191");
}
}
//+------------------------------------------------------------------+
//| This function tests various special properties |
//| On failure sets Err to True; on success leaves it unchanged. |
//+------------------------------------------------------------------+
void CTestMinQPUnit::SpecTests(bool &errorflag)
{
//--- create variables
CHighQualityRandState rs;
CMinQPState state;
CMinQPReport rep;
CMatrixDouble rawa;
CRowDouble b;
CRowDouble s;
CRowDouble x0;
CRowDouble xs;
CRowDouble xs2;
CRowDouble bndl;
CRowDouble bndu;
int n=0;
int i=0;
int j=0;
double xtol=0;
CHighQualityRand::HQRndRandomize(rs);
//--- Test correctness of MinQPSetScaleDiagAuto():
//--- * that it correctly handles matrices with positive diagonals
//--- * that it correctly handles non-positive elements
//--- First test is performed by comparing one step of automatically
//--- scaled QP-BLEIC with one step of manually scaled QP-BLEIC.
xtol=1.0E-6;
for(n=1; n<=10; n++)
{
//--- Generate random box constrained QP problem with specially
//--- crafted diagonal.
rawa=matrix<double>::Zeros(n,n);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
bndl=vector<double>::Full(n,-1.0);
bndu=vector<double>::Full(n,1.0);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
rawa.Set(i,j,CHighQualityRand::HQRndNormal(rs));
rawa.Set(i,i,MathPow(2,CHighQualityRand::HQRndUniformI(rs,9)-4));
b.Set(i,CHighQualityRand::HQRndNormal(rs));
}
//--- Create solver
CMinQP::MinQPCreate(n,state);
CMinQP::MinQPSetAlgoBLEIC(state,0,0,0,1);
CMinQP::MinQPSetLinearTerm(state,b);
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
CMinQP::MinQPSetBC(state,bndl,bndu);
CMinQP::MinQPSetStartingPoint(state,x0);
//--- Solve with automatic scaling
//--- Solve with manual scaling
//--- Compare
CMinQP::MinQPSetScaleAutoDiag(state);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xs,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminqpunit.ap:8266");
CAp::SetErrorFlag(errorflag,CAp::Len(xs)!=n,"testminqpunit.ap:8267");
if(errorflag)
return;
//---
s=MathPow(rawa.Diag()+0,(-0.5));
CMinQP::MinQPSetScale(state,s);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xs2,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testminqpunit.ap:8276");
CAp::SetErrorFlag(errorflag,CAp::Len(xs2)!=n,"testminqpunit.ap:8277");
if(errorflag)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(errorflag,(double)(MathAbs(xs[i]-xs2[i]))>xtol,"testminqpunit.ap:8281");
//--- Check that automatic scaling fails for matrices with zero or negative elements
i=CHighQualityRand::HQRndUniformI(rs,n);
j=CHighQualityRand::HQRndUniformI(rs,2)-1;
rawa.Set(i,i,j);
RandomlySelectConvertAndSetQuadraticTerm(rawa,n,state,rs);
CMinQP::MinQPSetScaleAutoDiag(state);
CMinQP::MinQPOptimize(state);
CMinQP::MinQPResults(state,xs,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype!=-9,"testminqpunit.ap:8294");
}
}
//+------------------------------------------------------------------+
//| Function normal |
//+------------------------------------------------------------------+
double CTestMinQPUnit::ProjectedAntiGradNorm(int n,CRowDouble &x,
CRowDouble &g,
CRowDouble &bndl,
CRowDouble &bndu)
{
//--- create variables
double result=0;
int i=0;
double r=0;
for(i=0; i<n; i++)
{
//--- check
if(!CAp::Assert(x[i]>=bndl[i] && x[i]<=bndu[i],"ProjectedAntiGradNormal: boundary constraints violation"))
return(EMPTY_VALUE);
if(((x[i]>bndl[i] && x[i]<bndu[i]) || (x[i]==bndl[i] && (double)(-g[i])>0.0)) || (x[i]==bndu[i] && (double)(-g[i])<0.0))
r+=g[i]*g[i];
}
//--- return result
result=MathSqrt(r);
return(result);
}
//+------------------------------------------------------------------+
//| This function tests that norm of bound-constrained gradient at |
//| point X is less than Eps: |
//| * unconstrained gradient is A*x+b |
//| * if I-th component is at the boundary, and antigradient points|
//| outside of the feasible area, I-th component of constrained |
//| gradient is zero |
//| This function accepts QP terms A and B, bound constraints, |
//| current point, and performs test. Additionally, it checks that |
//| point is feasible w.r.t. boundary constraints. |
//| In case of failure, error flag is set. Otherwise, it is not |
//| modified. |
//| IMPORTANT: this function does NOT use SetErrorFlag() to modify |
//| flag. If you want to use SetErrorFlag() for easier |
//| tracking of errors, you should store flag returned by |
//| this function into separate variable TmpFlag and call |
//| SetErrorFlag(ErrorFlag, TmpFlag) yourself. |
//+------------------------------------------------------------------+
void CTestMinQPUnit::TestBCGradAndFeasibility(CMatrixDouble &a,
CRowDouble &b,
CRowDouble &bndl,
CRowDouble &bndu,
int n,CRowDouble &x,
double eps,
bool &errorflag)
{
//--- create variables
double g=0;
double gnorm=0;
for(int i=0; i<n; i++)
{
g=b[i]+CAblasF::RDotVR(n,x,a,i);
if(x[i]==bndl[i] && g>0.0)
g=0;
if(x[i]==bndu[i] && g<0.0)
g=0;
gnorm+=CMath::Sqr(g);
if(x[i]<bndl[i])
errorflag=true;
if(x[i]>bndu[i])
errorflag=true;
}
gnorm=MathSqrt(gnorm);
if(gnorm>eps)
errorflag=true;
}
//+------------------------------------------------------------------+
//| set random type of the QP solver. |
//| All "modern" solvers can be chosen. |
//| OUTPUT PARAMETERS: |
//| BCTol - expected precision of box constraints handling |
//| assuming unit scale of variables. |
//| LCTol - expected precinion of linear constraints |
//| handling assuming unit scale of variables. |
//| RESULT: |
//| Solver type: |
//| *-1 for QuickQP |
//| * 0 for BLEIC-QP |
//| * 1 for DENSE-AUL |
//| * 2 for DENSE-IPM |
//| * 3 for SPARSE-IPM |
//| BCTol and LCTol have following meaning - if some constraint is |
//| active, it means that we should be at most TOL units away from |
//| boundary. It is possible that zero value is returned. |
//| From definition it follows that if we stopped at more than TOL |
//| units away from the boundary, gradient in corresponding direction|
//| is nearly zero. |
//+------------------------------------------------------------------+
int CTestMinQPUnit::SetRandomAlgoAllModern(CMinQPState &s,
double &bctol,
double &lctol)
{
//--- create variables
int result=0;
bctol=0;
lctol=0;
result=CMath::RandomInteger(5)-1;
switch(result)
{
case -1:
CMinQP::MinQPSetAlgoQuickQP(s,1.0E-12,0.0,0.0,0,CMath::RandomReal()>0.5);
bctol=0;
lctol=0;
break;
case 0:
CMinQP::MinQPSetAlgoBLEIC(s,1.0E-12,0.0,0.0,0);
bctol=0;
lctol=1.0E-8;
break;
case 1:
CMinQP::MinQPSetAlgoDenseAUL(s,1.0E-12,1000.0,10);
bctol=1.0E-3;
lctol=1.0E-3;
break;
case 2:
CMinQP::MinQPSetAlgoDenseIPM(s,1.0E-12);
bctol=1.0E-3;
lctol=1.0E-3;
break;
case 3:
CMinQP::MinQPSetAlgoSparseIPM(s,1.0E-12);
bctol=1.0E-3;
lctol=1.0E-3;
break;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| set random type of theQP solver |
//+------------------------------------------------------------------+
void CTestMinQPUnit::SetRandomAlgoNonConvex(CMinQPState &s)
{
//--- create variables
int i=0;
i=1+CMath::RandomInteger(2);
if(i==1)
CMinQP::MinQPSetAlgoBLEIC(s,1.0E-12,0.0,0.0,0);
if(i==2)
CMinQP::MinQPSetAlgoQuickQP(s,1.0E-12,0.0,0.0,0,CMath::RandomReal()>0.5);
}
//+------------------------------------------------------------------+
//| set random type of theQP solver |
//+------------------------------------------------------------------+
void CTestMinQPUnit::SetRandomAlgoSemiDefinite(CMinQPState &s,
double &bctol,
double &lctol)
{
bctol=0;
lctol=0;
int i=1+CMath::RandomInteger(4);
switch(i)
{
case 1:
CMinQP::MinQPSetAlgoBLEIC(s,1.0E-12,0.0,0.0,0);
bctol=0;
lctol=1.0E-8;
break;
case 2:
CMinQP::MinQPSetAlgoQuickQP(s,1.0E-12,0.0,0.0,0,CMath::RandomReal()>0.5);
bctol=0;
lctol=0;
break;
case 3:
CMinQP::MinQPSetAlgoDenseIPM(s,1.0E-12);
bctol=1.0E-3;
lctol=1.0E-3;
break;
case 4:
CMinQP::MinQPSetAlgoSparseIPM(s,1.0E-12);
bctol=1.0E-3;
lctol=1.0E-3;
break;
}
}
//+------------------------------------------------------------------+
//| set random type of the QP solver, must support boundary |
//| constraints |
//+------------------------------------------------------------------+
void CTestMinQPUnit::SetRandomAlgoBC(CMinQPState &s)
{
int i=CMath::RandomInteger(2);
if(i==0)
CMinQP::MinQPSetAlgoQuickQP(s,0.0,0.0,1.0E-13,0,true);
if(i==1)
CMinQP::MinQPSetAlgoBLEIC(s,1.0E-12,0.0,0.0,0);
}
//+------------------------------------------------------------------+
//| set random type of the QP solver, must support convex problems |
//| with boundary/linear constraints |
//+------------------------------------------------------------------+
void CTestMinQPUnit::SetRandomAlgoConvexLC(CMinQPState &s)
{
int i=CMath::RandomInteger(4);
switch(i)
{
case 0:
CMinQP::MinQPSetAlgoDenseAUL(s,1.0E-12,10000,15);
break;
case 1:
CMinQP::MinQPSetAlgoBLEIC(s,0.0,0.0,1.0E-12,0);
break;
case 2:
CMinQP::MinQPSetAlgoDenseIPM(s,1.0E-12);
break;
case 3:
CMinQP::MinQPSetAlgoSparseIPM(s,1.0E-12);
break;
}
}
//+------------------------------------------------------------------+
//| set random type of the QP solver, |
//| must support nonconvex problems with boundary/linear constraints |
//+------------------------------------------------------------------+
void CTestMinQPUnit::SetRandomAlgoNonConvexLC(CMinQPState &s)
{
int i=CMath::RandomInteger(1);
if(i==0)
CMinQP::MinQPSetAlgoBLEIC(s,1.0E-12,0.0,0.0,0);
}
//+------------------------------------------------------------------+
//| Convert dense matrix to sparse matrix using random format |
//+------------------------------------------------------------------+
void CTestMinQPUnit::DenseToSparse(CMatrixDouble &a,int n,
CSparseMatrix &s)
{
//--- create variables
CSparseMatrix s0;
CSparse::SparseCreate(n,n,n*n,s0);
for(int i=0; i<n; i++)
for(int j=0; j<n; j++)
CSparse::SparseSet(s0,i,j,a.Get(i,j));
CSparse::SparseCopyToBuf(s0,CMath::RandomInteger(3),s);
}
//+------------------------------------------------------------------+
//| Randomly split constraints into dense and sparse parts |
//+------------------------------------------------------------------+
void CTestMinQPUnit::RandomlySplitLC(CMatrixDouble &rawc,
CRowInt &rawct,
int rawccnt,int n,
CSparseMatrix &sparsec,
CRowInt &sparsect,
int &sparseccnt,
CMatrixDouble &densec,
CRowInt &densect,
int &denseccnt,
CHighQualityRandState &rs)
{
//--- create variables
int i=0;
int j=0;
sparseccnt=0;
denseccnt=0;
//--- Split "raw" constraints into dense and sparse parts
sparseccnt=CHighQualityRand::HQRndUniformI(rs,rawccnt+1);
denseccnt=rawccnt-sparseccnt;
if(sparseccnt>0)
{
CSparse::SparseCreate(sparseccnt,n+1,0,sparsec);
sparsect.Resize(sparseccnt);
for(i=0; i<sparseccnt; i++)
{
for(j=0; j<=n; j++)
CSparse::SparseSet(sparsec,i,j,rawc.Get(i,j));
sparsect.Set(i,rawct[i]);
}
}
if(denseccnt>0)
{
densec=matrix<double>::Zeros(denseccnt,n+1);
densect.Resize(denseccnt);
for(i=0; i<denseccnt; i++)
{
for(j=0; j<=n; j++)
densec.Set(i,j,rawc.Get(sparseccnt+i,j));
densect.Set(i,rawct[sparseccnt+i]);
}
}
}
//+------------------------------------------------------------------+
//| Randomly split constraints into dense and sparse parts and set |
//| them; legacy API is used. |
//+------------------------------------------------------------------+
void CTestMinQPUnit::RandomlySplitAndSetLCLegacy(CMatrixDouble &rawc,
CRowInt &rawct,
int rawccnt,int n,
CMinQPState &state,
CHighQualityRandState &rs)
{
//--- create variables
CMatrixDouble densec;
CRowInt densect;
CSparseMatrix sparsec;
CRowInt sparsect;
int denseccnt=0;
int sparseccnt=0;
//--- reset constraints
CMinQP::MinQPSetLC(state,densec,densect,0);
//--- split and set
RandomlySplitLC(rawc,rawct,rawccnt,n,sparsec,sparsect,sparseccnt,densec,densect,denseccnt,rs);
if(CHighQualityRand::HQRndUniformR(rs)>0.5 || denseccnt*sparseccnt>0)
CMinQP::MinQPSetLCMixed(state,sparsec,sparsect,sparseccnt,densec,densect,denseccnt);
else
{
if(denseccnt>0)
CMinQP::MinQPSetLC(state,densec,densect,denseccnt);
if(sparseccnt>0)
CMinQP::MinQPSetLCSparse(state,sparsec,sparsect,sparseccnt);
}
}
//+------------------------------------------------------------------+
//| Randomly split 2-sided constraints into dense and sparse parts |
//| and set them; |
//+------------------------------------------------------------------+
void CTestMinQPUnit::RandomlySplitAndSetLC2(CMatrixDouble &rawc,
CRowDouble &rawcl,
CRowDouble &rawcu,
int rawccnt,int n,
CMinQPState &state,
CHighQualityRandState &rs)
{
//--- create variables
CMatrixDouble densec;
CRowDouble densecl;
CRowDouble densecu;
CSparseMatrix sparsec;
CRowDouble sparsecl;
CRowDouble sparsecu;
CRowDouble wrkcl;
CRowDouble wrkcu;
int denseccnt=0;
int sparseccnt=0;
int i=0;
int j=0;
int appenddense=0;
int appendsparse=0;
CRowDouble cv;
CRowInt ci;
int nnz=0;
//--- reset constraints
CMinQP::MinQPSetLC2Dense(state,densec,densecl,densecu,0);
//--- Split "raw" constraints into dense and sparse parts
sparseccnt=CHighQualityRand::HQRndUniformI(rs,rawccnt+1);
denseccnt=rawccnt-sparseccnt;
appenddense=CHighQualityRand::HQRndUniformI(rs,denseccnt+1);
denseccnt=denseccnt-appenddense;
appendsparse=CHighQualityRand::HQRndUniformI(rs,sparseccnt+1);
sparseccnt=sparseccnt-appendsparse;
wrkcl=vector<double>::Zeros(sparseccnt+denseccnt);
wrkcu=vector<double>::Zeros(sparseccnt+denseccnt);
if(sparseccnt>0)
{
CSparse::SparseCreate(sparseccnt,n,0,sparsec);
sparsecl=vector<double>::Zeros(sparseccnt);
sparsecu=vector<double>::Zeros(sparseccnt);
for(i=0; i<sparseccnt; i++)
{
for(j=0; j<n; j++)
CSparse::SparseSet(sparsec,i,j,rawc.Get(i,j));
sparsecl.Set(i,rawcl[i]);
sparsecu.Set(i,rawcu[i]);
wrkcl.Set(i,sparsecl[i]);
wrkcu.Set(i,sparsecu[i]);
}
}
if(denseccnt>0)
{
densec=matrix<double>::Zeros(denseccnt,n);
densecl=vector<double>::Zeros(denseccnt);
densecu=vector<double>::Zeros(denseccnt);
for(i=0; i<denseccnt; i++)
{
for(j=0; j<n; j++)
densec.Set(i,j,rawc.Get(sparseccnt+appendsparse+i,j));
densecl.Set(i,rawcl[sparseccnt+appendsparse+i]);
densecu.Set(i,rawcu[sparseccnt+appendsparse+i]);
wrkcl.Set(sparseccnt+i,densecl[i]);
wrkcu.Set(sparseccnt+i,densecu[i]);
}
}
//--- split and set
if(CHighQualityRand::HQRndUniformR(rs)>0.5 || denseccnt*sparseccnt>0)
CMinQP::MinQPSetLC2Mixed(state,sparsec,sparseccnt,densec,denseccnt,wrkcl,wrkcu);
else
{
if(denseccnt>0)
CMinQP::MinQPSetLC2Dense(state,densec,densecl,densecu,denseccnt);
if(sparseccnt>0)
CMinQP::MinQPSetLC2(state,sparsec,sparsecl,sparsecu,sparseccnt);
}
cv=vector<double>::Zeros(2*n);
ci.Resize(2*n);
for(i=sparseccnt; i<sparseccnt+appendsparse; i++)
{
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
//--- Add sparse constraint using AddLC2()
//--- First, generate sparse representation
nnz=0;
for(j=0; j<n; j++)
{
if(rawc.Get(i,j)!=0.0)
{
cv.Set(nnz,rawc.Get(i,j));
ci.Set(nnz,j);
nnz++;
}
}
//--- Add duplicates which do not change constraint (after simplification)
while((nnz>0 && nnz<CAp::Len(ci)) && CHighQualityRand::HQRndUniformR(rs)<(double)(0.75))
{
j=CHighQualityRand::HQRndUniformI(rs,nnz);
ci.Set(nnz,ci[j]);
cv.Set(nnz,CHighQualityRand::HQRndNormal(rs));
cv.Add(j,-cv[nnz]);
nnz++;
}
//--- Add constraint to the set
CMinQP::MinQPAddLC2(state,ci,cv,nnz,rawcl[i],rawcu[i]);
}
else
{
//--- Add sparse constraint using AddLC2SparseFromDense()
for(j=0; j<n; j++)
cv.Set(j,rawc.Get(i,j));
CMinQP::MinQPAddLC2SparseFromDense(state,cv,rawcl[i],rawcu[i]);
}
}
for(i=rawccnt-appenddense; i<rawccnt; i++)
{
for(j=0; j<n; j++)
cv.Set(j,rawc.Get(i,j));
CMinQP::MinQPAddLC2Dense(state,cv,rawcl[i],rawcu[i]);
}
}
//+------------------------------------------------------------------+
//| Randomly selects triangle of full symmetric matrix, converts it |
//| to one of the matrix storage formats (dense or sparse) and sets. |
//+------------------------------------------------------------------+
void CTestMinQPUnit::RandomlySelectConvertAndSetQuadraticTerm(CMatrixDouble &a,
int n,
CMinQPState &state,
CHighQualityRandState &rs)
{
//--- create variables
int i=0;
int j=0;
CMatrixDouble densea;
CSparseMatrix sparsea;
bool isupper;
bool isdense;
isupper=CHighQualityRand::HQRndUniformR(rs)>0.5;
isdense=CHighQualityRand::HQRndUniformR(rs)>0.5;
if(isupper && isdense)
{
densea=a.TriU()+0;
CMinQP::MinQPSetQuadraticTerm(state,densea,isupper);
return;
}
if(!isupper && isdense)
{
densea=a.TriL()+0;
CMinQP::MinQPSetQuadraticTerm(state,densea,isupper);
return;
}
if(isupper && !isdense)
{
CSparse::SparseCreate(n,n,0,sparsea);
for(i=0; i<n; i++)
for(j=i; j<n; j++)
CSparse::SparseSet(sparsea,i,j,a.Get(i,j));
CMinQP::MinQPSetQuadraticTermSparse(state,sparsea,isupper);
return;
}
if(!isupper && !isdense)
{
CSparse::SparseCreate(n,n,0,sparsea);
for(i=0; i<n; i++)
for(j=0; j<=i; j++)
CSparse::SparseSet(sparsea,i,j,a.Get(i,j));
CMinQP::MinQPSetQuadraticTermSparse(state,sparsea,isupper);
return;
}
}
//+------------------------------------------------------------------+
//| This function returns reciprocal of condition number of general |
//| linear constraints. |
//+------------------------------------------------------------------+
double CTestMinQPUnit::GetConstraintRCond(CMatrixDouble &c,int k,int n)
{
//--- create variables
double result=0;
CRowDouble svdw;
CMatrixDouble svdu;
CMatrixDouble svdvt;
bool bflag=CSingValueDecompose::RMatrixSVD(c,k,n,0,0,0,svdw,svdu,svdvt);
//--- check
if(!CAp::Assert(bflag,"MinQPTest: integrity failure"))
return(0);
if(svdw[0]>0.0)
result=svdw[MathMin(k,n)-1]/svdw[0];
else
result=1;
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Computes target function 0.5*x'*H*x+c'*x |
//+------------------------------------------------------------------+
double CTestMinQPUnit::QuadraticTarget(CMatrixDouble &a,
CRowDouble &b,int n,
CRowDouble &x)
{
double result=0;
for(int i=0; i<n; i++)
{
result=result+b[i]*x[i];
for(int j=0; j<n; j++)
result+=0.5*x[i]*a.Get(i,j)*x[j];
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestMinLPUnit
{
public:
static const int m_solverscount;
static bool TestMinLP(bool silent);
private:
static void BasicTests(bool &err);
static void SingleCallTests(bool &err);
static void ValidateSolution(CRowDouble &c,CRowDouble &bndl,CRowDouble &bndu,int n,CMatrixDouble &a,CRowDouble &al,CRowDouble &au,int m,CRowDouble &xcand,CMinLPReport &rep,int solvertype,double &errprim,double &errdual,double &errslack);
static void GenerateLPProblem(CHighQualityRandState &rs,int n,CRowDouble &c,CRowDouble &bndl,CRowDouble &bndu,CMatrixDouble &a,CRowDouble &al,CRowDouble &au,int &m);
static void GenerateUnboundedLPProblem(CHighQualityRandState &rs,int n,CRowDouble &c,CRowDouble &bndl,CRowDouble &bndu,CMatrixDouble &a,CRowDouble &al,CRowDouble &au,int &m);
static void GenerateInfeasibleLPProblem(CHighQualityRandState &rs,int n,CRowDouble &c,CRowDouble &bndl,CRowDouble &bndu,CMatrixDouble &a,CRowDouble &al,CRowDouble &au,int &m);
static void GenerateCABXD(CHighQualityRandState &rs,int n,int m,CRowDouble &c,CMatrixDouble &a,CRowDouble &xx,CRowDouble &d,CRowInt &basicnonbasic);
static void GenerateBounds(CHighQualityRandState &rs,int n,int m,CRowDouble &xx,CRowDouble &d,CRowInt &basicnonbasic,CRowDouble &bndl,CRowDouble &bndu,CRowDouble &al,CRowDouble &au);
static void ModifyAndSendConstraintsTo(int n,CMatrixDouble &a,CRowDouble &al,CRowDouble &au,int m,CHighQualityRandState &rs,CMinLPState &state);
static void SelectRandomSolver(CMinLPState &state);
static void SelectSolver(CMinLPState &state,int st);
static void ShiftFromZero(CRowDouble &c,int n,double s);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
const int CTestMinLPUnit::m_solverscount=2;
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestMinLPUnit::TestMinLP(bool silent)
{
//--- create variables
bool result=true;
bool basicerrors=false;
bool singlecall=false;
bool waserrors;
BasicTests(basicerrors);
SingleCallTests(singlecall);
//--- report
waserrors=basicerrors||singlecall;
if(!silent)
{
Print("TESTING MinLP");
PrintResult("BASIC TESTS",!basicerrors);
Print("COMMON TESTS:");
PrintResult("* single call usage",!singlecall);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| Basic tests; sets error flag on failure, does not touch it on |
//| success. |
//+------------------------------------------------------------------+
void CTestMinLPUnit::BasicTests(bool &err)
{
//--- create variables
double primtol=1.0E-4;
double dualtol=1.0E-4;
double slacktol=1.0E-4;
int i=0;
int j=0;
int k=0;
int n=0;
int ccnt=0;
int pass=0;
CMinLPState state;
CMinLPReport rep;
CRowDouble c;
CRowDouble x;
CRowDouble x0;
CRowDouble x2;
CRowDouble bndl;
CRowDouble bndu;
CRowDouble al;
CRowDouble au;
CMatrixDouble a;
double v0=0;
double v1=0;
double errslack=0;
int solvertype=0;
CHighQualityRandState rs;
CHighQualityRand::HQRndRandomize(rs);
//--- Test default state of the solver
for(pass=1; pass<=5; pass++)
{
n=1+CHighQualityRand::HQRndUniformI(rs,10);
CMinLP::MinLPCreate(n,state);
SelectRandomSolver(state);
CMinLP::MinLPOptimize(state);
CMinLP::MinLPResults(state,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:100");
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:101");
if(err)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(err,x[i]!=0.0,"testminlpunit.ap:105");
}
//--- Test box constrained problems without linear constraints
for(n=1; n<=10; n++)
{
for(pass=1; pass<=20; pass++)
{
solvertype=CHighQualityRand::HQRndUniformI(rs,m_solverscount);
c=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
//--- Feasible bounded problems
for(i=0; i<n; i++)
{
c.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
bndu.Set(i,bndl[i]+CHighQualityRand::HQRndUniformI(rs,3));
}
ShiftFromZero(c,n,0.01);
CMinLP::MinLPCreate(n,state);
SelectSolver(state,solvertype);
CMinLP::MinLPSetCost(state,c);
CMinLP::MinLPSetBC(state,bndl,bndu);
CMinLP::MinLPOptimize(state);
CMinLP::MinLPResults(state,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:135");
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:136");
if(err)
return;
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(err,c[i]>0.0 && (x[i]<bndl[i] || x[i]>(bndl[i]+primtol)),"testminlpunit.ap:141");
CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:142");
CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:143");
CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:144");
}
for(i=0; i<n; i++)
{
c.Set(i,CHighQualityRand::HQRndNormal(rs));
v0=CHighQualityRand::HQRndNormal(rs);
v1=CHighQualityRand::HQRndNormal(rs);
bndl.Set(i,MathMin(v0,v1));
bndu.Set(i,MathMax(v0,v1));
if(c[i]>0.0)
bndu.Set(i,AL_POSINF);
if(c[i]<0.0)
bndl.Set(i,AL_NEGINF);
}
ShiftFromZero(c,n,0.01);
CMinLP::MinLPCreate(n,state);
SelectSolver(state,solvertype);
CMinLP::MinLPSetCost(state,c);
CMinLP::MinLPSetBC(state,bndl,bndu);
CMinLP::MinLPOptimize(state);
CMinLP::MinLPResults(state,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:166");
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:167");
if(err)
return;
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(err,c[i]>0.0 && (x[i]<bndl[i] || x[i]>(bndl[i]+primtol)),"testminlpunit.ap:172");
CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:173");
CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:174");
CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:175");
}
//--- Feasible bounded problems with zeros in cost vector
for(i=0; i<n; i++)
{
c.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
bndu.Set(i,bndl[i]+CHighQualityRand::HQRndUniformI(rs,3));
}
ShiftFromZero(c,n,0.01);
k=CHighQualityRand::HQRndUniformI(rs,n);
c.Set(k,0);
bndl.Set(k,AL_NEGINF);
bndu.Set(k,AL_POSINF);
CMinLP::MinLPCreate(n,state);
SelectSolver(state,solvertype);
CMinLP::MinLPSetCost(state,c);
CMinLP::MinLPSetBC(state,bndl,bndu);
CMinLP::MinLPOptimize(state);
CMinLP::MinLPResults(state,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:199");
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:200");
if(err)
return;
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(err,c[i]>0.0 && (x[i]<bndl[i] || x[i]>(bndl[i]+primtol)),"testminlpunit.ap:205");
CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:206");
CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:207");
CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:208");
}
CAp::SetErrorFlag(err,x[k]!=0.0,"testminlpunit.ap:210");
bndl.Set(k,CHighQualityRand::HQRndNormal(rs));
bndu.Set(k,AL_POSINF);
CMinLP::MinLPSetBC(state,bndl,bndu);
CMinLP::MinLPOptimize(state);
CMinLP::MinLPResults(state,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:217");
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:218");
if(err)
return;
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(err,c[i]>0.0 && (x[i]<bndl[i] || x[i]>(bndl[i]+primtol)),"testminlpunit.ap:223");
CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:224");
CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:225");
CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:226");
}
CAp::SetErrorFlag(err,x[k]<bndl[k],"testminlpunit.ap:228");
CAp::SetErrorFlag(err,solvertype==0 && x[k]!=(double)(bndl[k]),"testminlpunit.ap:229");
bndl.Set(k,AL_NEGINF);
bndu.Set(k,CHighQualityRand::HQRndNormal(rs));
CMinLP::MinLPSetBC(state,bndl,bndu);
CMinLP::MinLPOptimize(state);
CMinLP::MinLPResults(state,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:236");
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:237");
if(err)
return;
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(err,c[i]>0.0 && (x[i]<bndl[i] || x[i]>(bndl[i]+primtol)),"testminlpunit.ap:242");
CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:243");
CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:244");
CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:245");
}
CAp::SetErrorFlag(err,x[k]>bndu[k],"testminlpunit.ap:247");
CAp::SetErrorFlag(err,solvertype==0 && x[k]!=bndu[k],"testminlpunit.ap:248");
//--- Infeasible problems
for(i=0; i<n; i++)
{
c.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
bndu.Add(i,CHighQualityRand::HQRndUniformI(rs,3));
}
k=CHighQualityRand::HQRndUniformI(rs,n);
bndu.Set(k,bndl[k]-1);
CMinLP::MinLPCreate(n,state);
SelectSolver(state,solvertype);
CMinLP::MinLPSetCost(state,c);
CMinLP::MinLPSetBC(state,bndl,bndu);
CMinLP::MinLPOptimize(state);
CMinLP::MinLPResults(state,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype!=-3,"testminlpunit.ap:268");
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:269");
if(err)
return;
//--- Unbounded problems
//--- NOTE: we solve it without regularization, because with regularizer any LP problem is bounded
if(solvertype!=1)
{
for(i=0; i<n; i++)
{
do
{
c.Set(i,CHighQualityRand::HQRndNormal(rs));
}
while(c[i]==0.0);
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
bndu.Set(i,bndl[i]+CHighQualityRand::HQRndUniformI(rs,3));
}
k=CHighQualityRand::HQRndUniformI(rs,n);
if(c[k]>0.0)
bndl.Set(k,AL_NEGINF);
else
bndu.Set(k,AL_POSINF);
CMinLP::MinLPCreate(n,state);
SelectSolver(state,solvertype);
CMinLP::MinLPSetCost(state,c);
CMinLP::MinLPSetBC(state,bndl,bndu);
CMinLP::MinLPOptimize(state);
CMinLP::MinLPResults(state,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype!=-4,"testminlpunit.ap:300");
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:301");
if(err)
return;
}
}
}
//--- Test linearly constrained problems with all variables being boxed ones
//--- and with all linear constraints being inequality ones satisfied in the
//--- box internals.
for(n=1; n<=10; n++)
{
solvertype=CHighQualityRand::HQRndUniformI(rs,m_solverscount);
ccnt=1+CHighQualityRand::HQRndUniformI(rs,10);
c=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
a=matrix<double>::Zeros(ccnt,n);
al=vector<double>::Zeros(ccnt);
au=vector<double>::Zeros(ccnt);
for(i=0; i<n; i++)
{
c.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,-0.5-0.5*CHighQualityRand::HQRndUniformR(rs));
bndu.Set(i,0.5+0.5*CHighQualityRand::HQRndUniformR(rs));
}
ShiftFromZero(c,n,0.01);
for(i=0; i<ccnt; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,2*CHighQualityRand::HQRndUniformR(rs)-1);
al.Set(i,-(2*n));
au.Set(i,2*n);
}
CMinLP::MinLPCreate(n,state);
SelectSolver(state,solvertype);
CMinLP::MinLPSetCost(state,c);
CMinLP::MinLPSetBC(state,bndl,bndu);
CMinLP::MinLPSetLC2Dense(state,a,al,au,ccnt);
CMinLP::MinLPOptimize(state);
CMinLP::MinLPResults(state,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:343");
CAp::SetErrorFlag(err,CAp::Len(x)<n,"testminlpunit.ap:344");
if(err)
return;
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(err,c[i]>0.0 && (x[i]<bndl[i] || x[i]>(bndl[i]+primtol)),"testminlpunit.ap:349");
CAp::SetErrorFlag(err,c[i]<0.0 && (x[i]>bndu[i] || x[i]<(bndu[i]-primtol)),"testminlpunit.ap:350");
CAp::SetErrorFlag(err,(c[i]>0.0 && solvertype==0) && x[i]!=bndl[i],"testminlpunit.ap:351");
CAp::SetErrorFlag(err,(c[i]<0.0 && solvertype==0) && x[i]!=bndu[i],"testminlpunit.ap:352");
}
}
//--- Test linearly constrained problems with all variables being boxed ones
//--- and with random linear constraints, with at least one feasible point
//--- in the box internals.
for(n=1; n<=10; n++)
{
solvertype=CHighQualityRand::HQRndUniformI(rs,m_solverscount);
ccnt=1+CHighQualityRand::HQRndUniformI(rs,10);
c=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
a=matrix<double>::Zeros(ccnt,n);
al=vector<double>::Zeros(ccnt);
au=vector<double>::Zeros(ccnt);
for(i=0; i<n; i++)
{
c.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,-0.5-0.5*CHighQualityRand::HQRndUniformR(rs));
bndu.Set(i,0.5+0.5*CHighQualityRand::HQRndUniformR(rs));
x0.Set(i,0.5*(bndl[i]+bndu[i]));
}
for(i=0; i<ccnt; i++)
{
v0=0.0;
for(j=0; j<n; j++)
{
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
v0+=a.Get(i,j)*x0[j];
}
al.Set(i,v0-0.25*CHighQualityRand::HQRndUniformR(rs));
au.Set(i,v0+0.25*CHighQualityRand::HQRndUniformR(rs));
}
CMinLP::MinLPCreate(n,state);
SelectSolver(state,solvertype);
CMinLP::MinLPSetCost(state,c);
CMinLP::MinLPSetBC(state,bndl,bndu);
CMinLP::MinLPSetLC2Dense(state,a,al,au,ccnt);
CMinLP::MinLPOptimize(state);
CMinLP::MinLPResults(state,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminlpunit.ap:397");
CAp::SetErrorFlag(err,CAp::Len(x)!=n,"testminlpunit.ap:398");
CAp::SetErrorFlag(err,CAp::Len(rep.m_y)!=ccnt,"testminlpunit.ap:399");
CAp::SetErrorFlag(err,CAp::Len(rep.m_stats)!=n+ccnt,"testminlpunit.ap:400");
if(err)
return;
ValidateSolution(c,bndl,bndu,n,a,al,au,ccnt,x,rep,solvertype,v0,v1,errslack);
CAp::SetErrorFlag(err,MathAbs(v0)>primtol,"testminlpunit.ap:404");
CAp::SetErrorFlag(err,MathAbs(v1)>dualtol,"testminlpunit.ap:405");
CAp::SetErrorFlag(err,MathAbs(errslack)>slacktol,"testminlpunit.ap:406");
}
}
//+------------------------------------------------------------------+
//| Single - call tests; |
//| sets error flag on failure, does not touch it on success. |
//+------------------------------------------------------------------+
void CTestMinLPUnit::SingleCallTests(bool &err)
{
//--- create variables
double primtol=1.0E-3;
double dualtol=1.0E-3;
double slacktol=1.0E-3;
double etol=1.0E-8;
double ftol=1.0E-3;
int n=0;
int pass=0;
CMinLPState state0;
CMinLPReport rep0;
CMinLPState state1;
CMinLPReport rep1;
int i=0;
int j=0;
CRowDouble c;
CRowDouble bndl;
CRowDouble bndu;
CRowDouble al;
CRowDouble au;
CRowDouble s;
CRowDouble bndls;
CRowDouble bndus;
CRowDouble cs;
CMatrixDouble a;
CMatrixDouble a1;
CRowDouble x0;
CRowDouble x1;
int m=0;
int n0=0;
double v0=0;
double v1=0;
double f=0;
double f1=0;
double errp=0;
double errd=0;
double errs=0;
double alpha=0;
CHighQualityRandState rs;
int solvertype=0;
CHighQualityRand::HQRndRandomize(rs);
//--- Try different feasible problems
for(n=1; n<=50; n++)
{
for(pass=1; pass<=30; pass++)
{
solvertype=CHighQualityRand::HQRndUniformI(rs,m_solverscount);
//--- Generate random feasible problem and solve it using basic MinLPSetLC2Dense() API
GenerateLPProblem(rs,n,c,bndl,bndu,a,al,au,m);
CMinLP::MinLPCreate(n,state0);
SelectSolver(state0,solvertype);
CMinLP::MinLPSetCost(state0,c);
CMinLP::MinLPSetBC(state0,bndl,bndu);
CMinLP::MinLPSetLC2Dense(state0,a,al,au,m);
CMinLP::MinLPOptimize(state0);
CMinLP::MinLPResults(state0,x0,rep0);
CAp::SetErrorFlag(err,rep0.m_terminationtype<=0,"testminlpunit.ap:470");
CAp::SetErrorFlag(err,CAp::Len(x0)!=n || !CApServ::IsFiniteVector(x0,n),"testminlpunit.ap:471");
if(err)
return;
ValidateSolution(c,bndl,bndu,n,a,al,au,m,x0,rep0,solvertype,errp,errd,errs);
CAp::SetErrorFlag(err,MathAbs(errp)>primtol,"testminlpunit.ap:475");
CAp::SetErrorFlag(err,MathAbs(errd)>dualtol,"testminlpunit.ap:476");
CAp::SetErrorFlag(err,MathAbs(errs)>slacktol,"testminlpunit.ap:477");
CAp::SetErrorFlag(err,MathAbs(errp-rep0.m_primalerror)>(etol*MathMax(errp,1)),"testminlpunit.ap:478");
CAp::SetErrorFlag(err,MathAbs(errd-rep0.m_dualerror)>(etol*MathMax(errd,1)),"testminlpunit.ap:479");
CAp::SetErrorFlag(err,MathAbs(errs-rep0.m_slackerror)>(etol*MathMax(errs,1)),"testminlpunit.ap:480");
//--- Apply random modification to the problem (and to the way
//--- we pass constraints to the solver), try solving one more time
//--- and compare with the original solution.
//--- NOTE: because our test suite has degenerate problems, we do
//--- not compare points returned - only target values are
//--- compared.
CMinLP::MinLPCreate(n,state1);
SelectSolver(state1,solvertype);
CMinLP::MinLPSetCost(state1,c);
CMinLP::MinLPSetBC(state1,bndl,bndu);
ModifyAndSendConstraintsTo(n,a,al,au,m,rs,state1);
CMinLP::MinLPOptimize(state1);
CMinLP::MinLPResults(state1,x1,rep1);
CAp::SetErrorFlag(err,rep1.m_terminationtype<=0,"testminlpunit.ap:500");
CAp::SetErrorFlag(err,CAp::Len(x1)!=n,"testminlpunit.ap:501");
if(err)
return;
f=CAblasF::RDotV(n,x0,c);
f1=CAblasF::RDotV(n,x1,c);
CAp::SetErrorFlag(err,MathAbs(f1-f)>ftol,"testminlpunit.ap:511");
//--- Test scaling (random scale is applied, target values for
//--- original and scaled problems are compared).
//--- NOTE: because our test suite has degenerate problems, we do
//--- not compare points returned - only target values.
s=vector<double>::Zeros(n);
cs=vector<double>::Zeros(n);
bndls=vector<double>::Zeros(n);
bndus=vector<double>::Zeros(n);
for(i=0; i<n; i++)
s.Set(i,MathPow(2,CHighQualityRand::HQRndUniformI(rs,21)-10));
cs=c/s+0;
bndls=bndl*s+0;
bndus=bndu*s+0;
a1=matrix<double>::Zeros(m,n);
for(i=0; i<m; i++)
a1.Row(i,a[i]/s.ToVector());
CMinLP::MinLPCreate(n,state1);
SelectSolver(state1,solvertype);
CMinLP::MinLPSetScale(state1,s);
CMinLP::MinLPSetCost(state1,cs);
CMinLP::MinLPSetBC(state1,bndls,bndus);
CMinLP::MinLPSetLC2Dense(state1,a1,al,au,m);
CMinLP::MinLPOptimize(state1);
CMinLP::MinLPResults(state1,x1,rep1);
CAp::SetErrorFlag(err,rep1.m_terminationtype<=0,"testminlpunit.ap:544");
CAp::SetErrorFlag(err,CAp::Len(x1)!=n,"testminlpunit.ap:545");
if(err)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(err,(double)(MathAbs(x0[i]-x1[i]/s[i]))>(1000.0*CMath::m_machineepsilon),"testminlpunit.ap:557");
}
}
//--- Test infeasible/unbounded problems
for(n=1; n<=50; n++)
{
for(pass=1; pass<=30; pass++)
{
solvertype=CHighQualityRand::HQRndUniformI(rs,m_solverscount);
//--- Generate random primal unbounded
//--- NOTE: because we use constraint validation code, we can not
//--- use ModifyAndSendConstraintsTo() function - it permutes
//--- constraints order and prevents correct validation.
GenerateUnboundedLPProblem(rs,n,c,bndl,bndu,a,al,au,m);
CMinLP::MinLPCreate(n,state0);
SelectSolver(state0,solvertype);
CMinLP::MinLPSetCost(state0,c);
CMinLP::MinLPSetBC(state0,bndl,bndu);
CMinLP::MinLPSetLC2Dense(state0,a,al,au,m);
CMinLP::MinLPOptimize(state0);
CMinLP::MinLPResults(state0,x0,rep0);
CAp::SetErrorFlag(err,rep0.m_terminationtype!=-4 && rep0.m_terminationtype!=-2,"testminlpunit.ap:584");
CAp::SetErrorFlag(err,CAp::Len(x0)!=n || !CApServ::IsFiniteVector(x0,n),"testminlpunit.ap:585");
if(err)
return;
ValidateSolution(c,bndl,bndu,n,a,al,au,m,x0,rep0,solvertype,errp,errd,errs);
CAp::SetErrorFlag(err,MathAbs(errp-rep0.m_primalerror)>(etol*MathMax(errp,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:589");
CAp::SetErrorFlag(err,MathAbs(errd-rep0.m_dualerror)>(etol*MathMax(errd,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:590");
CAp::SetErrorFlag(err,MathAbs(errs-rep0.m_slackerror)>(etol*MathMax(errs,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:591");
//--- Generate random primal infeasible
//--- NOTE: because we use constraint validation code, we can not
//--- use ModifyAndSendConstraintsTo() function - it permutes
//--- constraints order and prevents correct validation.
GenerateInfeasibleLPProblem(rs,n,c,bndl,bndu,a,al,au,m);
CMinLP::MinLPCreate(n,state0);
SelectSolver(state0,solvertype);
CMinLP::MinLPSetCost(state0,c);
CMinLP::MinLPSetBC(state0,bndl,bndu);
CMinLP::MinLPSetLC2Dense(state0,a,al,au,m);
CMinLP::MinLPOptimize(state0);
CMinLP::MinLPResults(state0,x0,rep0);
CAp::SetErrorFlag(err,rep0.m_terminationtype!=-3 && rep0.m_terminationtype!=-2,"testminlpunit.ap:610");
CAp::SetErrorFlag(err,CAp::Len(x0)!=n || !CApServ::IsFiniteVector(x0,n),"testminlpunit.ap:611");
if(err)
return;
ValidateSolution(c,bndl,bndu,n,a,al,au,m,x0,rep0,solvertype,errp,errd,errs);
CAp::SetErrorFlag(err,MathAbs(errp-rep0.m_primalerror)>(etol*MathMax(errp,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:615");
CAp::SetErrorFlag(err,MathAbs(errd-rep0.m_dualerror)>(etol*MathMax(errd,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:616");
CAp::SetErrorFlag(err,MathAbs(errs-rep0.m_slackerror)>(etol*MathMax(errs,MathMax(CAblasF::RMaxAbsV(n,x0),1))),"testminlpunit.ap:617");
}
}
for(n=2; n<=50; n++)
{
for(pass=1; pass<=30; pass++)
{
//--- Special test for simplex solver.
//--- Generate carefully crafted primal infeasible - the problem is ALMOST feasible.
//--- So, we expect simplex solver to Stop at the best point possible. We also test
//--- that X and Y are correctly unscaled on return.
//--- check
if(!CAp::Assert(n>=2,"LPTEST: integrity check failed"))
{
err=true;
return;
}
n0=n/2;
//--- check
if(!CAp::Assert(n0<n,"LPTEST: integrity check failed"))
{
err=true;
return;
}
alpha=0.0001;
c=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
if(i<n0)
c.Set(i,-MathPow(2,4*CHighQualityRand::HQRndUniformR(rs)-2));
else
c.Set(i,CHighQualityRand::HQRndUniformR(rs)-0.5);
bndl.Set(i,-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
bndu.Set(i,1.0);
s.Set(i,MathPow(2,2*CHighQualityRand::HQRndUniformR(rs)-1));
}
m=1+CHighQualityRand::HQRndUniformI(rs,n);
a=matrix<double>::Zeros(m,n);
al=vector<double>::Zeros(m);
au=vector<double>::Zeros(m);
for(i=0; i<=m-2; i++)
{
v0=0;
for(j=n0; j<n; j++)
{
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
v0+=0.5*a.Get(i,j);
}
al.Set(i,v0-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
au.Set(i,v0+MathPow(2,CHighQualityRand::HQRndNormal(rs)));
}
v0=0;
for(i=0; i<n; i++)
{
if(i<n0)
a.Set(m-1,i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
else
a.Set(m-1,i,0);
v0+=a[m-1][ i];
}
al.Set(m-1,v0+alpha);
au.Set(m-1,al[m-1]);
CMinLP::MinLPCreate(n,state0);
CMinLP::MinLPSetAlgoDSS(state0,0.0);
CMinLP::MinLPSetCost(state0,c);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinLP::MinLPSetScale(state0,s);
CMinLP::MinLPSetBC(state0,bndl,bndu);
CMinLP::MinLPSetLC2Dense(state0,a,al,au,m);
CMinLP::MinLPOptimize(state0);
CMinLP::MinLPResults(state0,x0,rep0);
CAp::SetErrorFlag(err,rep0.m_terminationtype!=-3 && rep0.m_terminationtype!=-2,"testminlpunit.ap:686");
CAp::SetErrorFlag(err,CAp::Len(x0)!=n || !CApServ::IsFiniteVector(x0,n),"testminlpunit.ap:687");
if(err)
return;
for(i=0; i<n0 ; i++)
CAp::SetErrorFlag(err,x0[i]<1.0,"testminlpunit.ap:691");
ValidateSolution(c,bndl,bndu,n,a,al,au,m,x0,rep0,solvertype,errp,errd,errs);
CAp::SetErrorFlag(err,MathAbs(errd)>0.001,"testminlpunit.ap:693");
CAp::SetErrorFlag(err,MathAbs(errp-rep0.m_primalerror)>(etol*MathMax(errp,1)),"testminlpunit.ap:694");
CAp::SetErrorFlag(err,MathAbs(errd-rep0.m_dualerror)>(etol*MathMax(errd,1)),"testminlpunit.ap:695");
CAp::SetErrorFlag(err,MathAbs(errs-rep0.m_slackerror)>(etol*MathMax(errs,1)),"testminlpunit.ap:696");
}
}
//--- Check SetBCAll() and SetBCi()
//--- We generate random problem with box constraints
//--- L<=x[i]<=U, with L<0<U, and random linear constraints
//--- feasible at x=0.
//--- In order to test SetBCAll() we solve it two times,
//--- first one with box constraints specified via setbc(),
//--- second one with setbcall(). Both solutions are compared.
//--- After that we rewrite box constraints to be different
//--- for each variable, and send them to the first solver
//--- via setbc() and to the second one via sequential setbci().
//--- Both solutions are compared.
for(n=1; n<=30; n++)
{
solvertype=CHighQualityRand::HQRndUniformI(rs,m_solverscount);
v0=-MathPow(2,CHighQualityRand::HQRndNormal(rs));
v1=MathPow(2,CHighQualityRand::HQRndNormal(rs));
m=CHighQualityRand::HQRndUniformI(rs,2*n);
c=vector<double>::Zeros(n);
bndl=vector<double>::Full(n,v0);
bndu=vector<double>::Full(n,v1);
a=matrix<double>::Zeros(m,n);
al=vector<double>::Zeros(m);
au=vector<double>::Zeros(m);
for(i=0; i<n; i++)
c.Set(i,CHighQualityRand::HQRndNormal(rs));
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
a.Set(i,j,CHighQualityRand::HQRndUniformI(rs,2)*CHighQualityRand::HQRndNormal(rs));
al.Set(i,-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
au.Set(i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
}
//--- SetBCAll() vs SetBC()
x0=vector<double>::Zeros(CHighQualityRand::HQRndUniformI(rs,2*n));
x1=vector<double>::Zeros(CHighQualityRand::HQRndUniformI(rs,2*n));
CMinLP::MinLPCreate(n,state0);
SelectSolver(state0,solvertype);
CMinLP::MinLPSetCost(state0,c);
CMinLP::MinLPSetBC(state0,bndl,bndu);
CMinLP::MinLPSetLC2Dense(state0,a,al,au,m);
CMinLP::MinLPOptimize(state0);
CMinLP::MinLPResults(state0,x0,rep0);
CAp::SetErrorFlag(err,rep0.m_terminationtype<=0,"testminlpunit.ap:754");
CAp::SetErrorFlag(err,CAp::Len(x0)!=n,"testminlpunit.ap:755");
if(err)
return;
CMinLP::MinLPCreate(n,state1);
SelectSolver(state1,solvertype);
CMinLP::MinLPSetCost(state1,c);
CMinLP::MinLPSetBCAll(state1,v0,v1);
CMinLP::MinLPSetLC2Dense(state1,a,al,au,m);
CMinLP::MinLPOptimize(state1);
CMinLP::MinLPResults(state1,x1,rep1);
CAp::SetErrorFlag(err,rep1.m_terminationtype<=0,"testminlpunit.ap:765");
CAp::SetErrorFlag(err,CAp::Len(x1)!=n,"testminlpunit.ap:766");
if(err)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(err,x0[i]!=x1[i],"testminlpunit.ap:770");
//--- SetBCi() vs SetBC()
x0=vector<double>::Zeros(CHighQualityRand::HQRndUniformI(rs,2*n));
x1=vector<double>::Zeros(CHighQualityRand::HQRndUniformI(rs,2*n));
for(i=0; i<n; i++)
{
bndl.Set(i,-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
bndu.Set(i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
CMinLP::MinLPSetBCi(state1,i,bndl[i],bndu[i]);
}
CMinLP::MinLPSetBC(state0,bndl,bndu);
CMinLP::MinLPOptimize(state0);
CMinLP::MinLPResults(state0,x0,rep0);
CAp::SetErrorFlag(err,rep0.m_terminationtype<=0,"testminlpunit.ap:786");
CAp::SetErrorFlag(err,CAp::Len(x0)!=n,"testminlpunit.ap:787");
if(err)
return;
CMinLP::MinLPOptimize(state1);
CMinLP::MinLPResults(state1,x1,rep1);
CAp::SetErrorFlag(err,rep1.m_terminationtype<=0,"testminlpunit.ap:792");
CAp::SetErrorFlag(err,CAp::Len(x1)!=n,"testminlpunit.ap:793");
if(err)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(err,x0[i]!=x1[i],"testminlpunit.ap:797");
}
}
//+------------------------------------------------------------------+
//| This function validates solution returned by LP solver. It |
//| returns primal and dual feasibility errors for this solution. |
//| SolverType: |
//| * 0 for dual simplex solver; additional test relying on |
//| explicit information about constraint statuses is performed|
//| * 1 for interior point method; only straightforward dual |
//| feasibility is tested |
//+------------------------------------------------------------------+
void CTestMinLPUnit::ValidateSolution(CRowDouble &c,CRowDouble &bndl,
CRowDouble &bndu,int n,
CMatrixDouble &a,CRowDouble &al,
CRowDouble &au,int m,
CRowDouble &xcand,CMinLPReport &rep,
int solvertype,double &errprim,
double &errdual,double &errslack)
{
//--- create variables
int i=0;
int j=0;
double v=0;
bool checkstats;
CRowDouble d;
errprim=0;
errdual=0;
errslack=0;
checkstats=solvertype==0;
errprim=0;
errdual=0;
errslack=0;
//--- Test primal feasibility
for(i=0; i<n; i++)
{
if(MathIsValidNumber(bndl[i]))
errprim=MathMax(errprim,bndl[i]-xcand[i]);
if(MathIsValidNumber(bndu[i]))
errprim=MathMax(errprim,xcand[i]-bndu[i]);
}
for(i=0; i<m; i++)
{
v=CAblasF::RDotVR(n,xcand,a,i);
if(MathIsValidNumber(al[i]))
errprim=MathMax(errprim,al[i]-v);
if(MathIsValidNumber(au[i]))
errprim=MathMax(errprim,v-au[i]);
}
//--- Test dual feasibility using Lagrange multipliers
d=c;
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
d.Add(j,rep.m_laglc[i]*a.Get(i,j));
}
for(i=0; i<n; i++)
d.Add(i,rep.m_lagbc[i]);
errdual=MathMax(errdual,CAblasF::RMaxAbsV(n,d));
//--- Test complementary slackness
for(i=0; i<n; i++)
{
if(MathIsValidNumber(bndl[i]))
errslack=MathMax(errslack,MathMax(xcand[i]-bndl[i],0.0)*MathMax(-rep.m_lagbc[i],0.0));
if(MathIsValidNumber(bndu[i]))
errslack=MathMax(errslack,MathMax(bndu[i]-xcand[i],0.0)*MathMax(rep.m_lagbc[i],0.0));
}
for(i=0; i<m; i++)
{
v=CAblasF::RDotVR(n,xcand,a,i);
if(MathIsValidNumber(al[i]))
errslack=MathMax(errslack,MathMax(v-al[i],0.0)*MathMax(-rep.m_laglc[i],0.0));
if(MathIsValidNumber(au[i]))
errslack=MathMax(errslack,MathMax(au[i]-v,0.0)*MathMax(rep.m_laglc[i],0.0));
}
}
//+------------------------------------------------------------------+
//| This function generates random LP problem with user - specified |
//| number of variables, with several problem types being randomly |
//| chosen internally. |
//| Constraint counts and types are generated randomly; it is |
//| possible that zero amount of constraints is generated. |
//+------------------------------------------------------------------+
void CTestMinLPUnit::GenerateLPProblem(CHighQualityRandState &rs,
int n,CRowDouble &c,
CRowDouble &bndl,
CRowDouble &bndu,
CMatrixDouble &a,
CRowDouble &al,
CRowDouble &au,int &m)
{
//--- create variables
int ptype=0;
int pcount=0;
CRowDouble x0;
CRowDouble xx;
CRowDouble d;
CRowInt basicnonbasic;
int i=0;
int j=0;
int k=0;
int kk=0;
int kt=0;
double v=0;
double q=0;
double big=0;
double mincoeff=0;
int nprimal=0;
int ndual=0;
m=0;
x0=vector<double>::Zeros(n);
c=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
//--- Choose problem type
pcount=4;
ptype=CHighQualityRand::HQRndUniformI(rs,pcount);
mincoeff=0.001;
//--- A linearly constrained LP problem with all variables/linear constraints
//--- being boxed (easy start).
if(ptype==0)
{
q=5.0;
m=1+CHighQualityRand::HQRndUniformI(rs,2*n);
a=matrix<double>::Zeros(m,n);
al=vector<double>::Zeros(m);
au=vector<double>::Zeros(m);
for(i=0; i<n; i++)
{
c.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,MathPow(q,CHighQualityRand::HQRndNormal(rs))-MathPow(q,CHighQualityRand::HQRndNormal(rs)));
bndu.Set(i,bndl[i]+MathPow(q,CHighQualityRand::HQRndNormal(rs)));
v=0.01+0.98*CHighQualityRand::HQRndUniformR(rs);
x0.Set(i,(1-v)*bndl[i]+v*bndu[i]);
}
for(i=0; i<m; i++)
{
v=0.0;
for(j=0; j<n; j++)
{
a.Set(i,j,CHighQualityRand::HQRndNormal(rs)*CHighQualityRand::HQRndUniformI(rs,2));
a.Add(i,j,MathSign(a.Get(i,j))*mincoeff);
v+=a.Get(i,j)*x0[j];
}
al.Set(i,v-MathPow(q,CHighQualityRand::HQRndNormal(rs)));
au.Set(i,v+MathPow(q,CHighQualityRand::HQRndNormal(rs)));
}
return;
}
//--- A linearly constrained LP problem with up to M variables (linear
//--- constraints) being non-boxed (free or having just one constraint).
//--- A bit harder than all-boxed version.
//--- NOTE: components of C[] corresponding to these non-boxed variables
//--- are chosen in a way which guarantees dual feasibility of the
//--- problem.
if(ptype==1)
{
q=2.0;
big=100.00;
m=1+CHighQualityRand::HQRndUniformI(rs,2*n);
a=matrix<double>::Zeros(m,n);
al=vector<double>::Zeros(m);
au=vector<double>::Zeros(m);
for(i=0; i<n; i++)
{
c.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,MathPow(q,CHighQualityRand::HQRndNormal(rs))-MathPow(q,CHighQualityRand::HQRndNormal(rs)));
bndu.Set(i,bndl[i]+MathPow(q,CHighQualityRand::HQRndNormal(rs)));
v=0.01+0.98*CHighQualityRand::HQRndUniformR(rs);
x0.Set(i,(1-v)*bndl[i]+v*bndu[i]);
}
for(i=0; i<m; i++)
{
v=0.0;
for(j=0; j<n; j++)
{
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
a.Add(i,j,MathSign(a.Get(i,j))*mincoeff);
v+=a.Get(i,j)*x0[j];
}
al.Set(i,v-MathPow(q,CHighQualityRand::HQRndNormal(rs)));
au.Set(i,v+MathPow(q,CHighQualityRand::HQRndNormal(rs)));
}
kk=1+CHighQualityRand::HQRndUniformI(rs,m);
for(k=0; k<=kk-1; k++)
{
i=CHighQualityRand::HQRndUniformI(rs,n+m);
if(i<n)
{
kt=CHighQualityRand::HQRndUniformI(rs,3);
if(kt==0)
{
bndl.Set(i,AL_NEGINF);
c.Set(i,-(big*(1+CHighQualityRand::HQRndUniformR(rs))));
}
if(kt==1)
{
bndu.Set(i,AL_POSINF);
c.Set(i,big*(1+CHighQualityRand::HQRndUniformR(rs)));
}
if(kt==2)
{
bndl.Set(i,AL_NEGINF);
bndu.Set(i,AL_POSINF);
}
}
else
{
kt=CHighQualityRand::HQRndUniformI(rs,3);
if(kt==0)
au.Set(i-n,AL_POSINF);
if(kt==1)
al.Set(i-n,AL_NEGINF);
if(kt==2)
{
al.Set(i-n,AL_NEGINF);
au.Set(i-n,AL_POSINF);
}
}
}
return;
}
//--- A randomly generated non-degenerate LP problem. A mix of fixed,
//--- range, upper/lower bounds and free variables.
if(ptype==2)
{
m=1+CHighQualityRand::HQRndUniformI(rs,2*n);
GenerateCABXD(rs,n,m,c,a,xx,d,basicnonbasic);
GenerateBounds(rs,n,m,xx,d,basicnonbasic,bndl,bndu,al,au);
return;
}
//--- A randomly generated primal/dual degenerate LP problem. A mix of
//--- fixed, range, upper/lower bounds and free variables.
if(ptype==3)
{
m=1+CHighQualityRand::HQRndUniformI(rs,2*n);
GenerateCABXD(rs,n,m,c,a,xx,d,basicnonbasic);
GenerateBounds(rs,n,m,xx,d,basicnonbasic,bndl,bndu,al,au);
ndual=1+CHighQualityRand::HQRndUniformI(rs,m);
for(i=0; i<=ndual-1; i++)
{
j=basicnonbasic[m+CHighQualityRand::HQRndUniformI(rs,n)];
if(j<n)
{
c.Add(j,- d[j]);
d.Set(j,0);
}
}
nprimal=1+CHighQualityRand::HQRndUniformI(rs,m);
for(i=0; i<nprimal; i++)
{
j=basicnonbasic[CHighQualityRand::HQRndUniformI(rs,m)];
if(j<n)
{
if(CHighQualityRand::HQRndNormal(rs)>0.0)
bndl.Set(j,xx[j]);
else
bndu.Set(j,xx[j]);
}
else
{
if(CHighQualityRand::HQRndNormal(rs)>0.0)
al.Set(j-n,xx[j]);
else
au.Set(j-n,xx[j]);
}
}
return;
}
CAp::Assert(false,"GenerateLPProblem failed");
}
//+------------------------------------------------------------------+
//| This function generates random primal unbounded LP problem. |
//+------------------------------------------------------------------+
void CTestMinLPUnit::GenerateUnboundedLPProblem(CHighQualityRandState &rs,
int n,CRowDouble &c,
CRowDouble &bndl,
CRowDouble &bndu,
CMatrixDouble &a,
CRowDouble &al,
CRowDouble &au,int &m)
{
//--- create variables
int i=0;
int j=0;
int ptype=0;
int pcount=0;
double v=0;
double vv=0;
double q=0;
CRowDouble x0;
m=0;
c=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
//--- Choose problem type
pcount=2;
ptype=CHighQualityRand::HQRndUniformI(rs,pcount);
//--- Boundary-only constraints, can be easily made primal unbounded
if(ptype==0)
{
q=5.0;
m=0;
for(i=0; i<n; i++)
{
c.Set(i,CHighQualityRand::HQRndNormal(rs));
v=MathPow(q,CHighQualityRand::HQRndNormal(rs))-MathPow(q,CHighQualityRand::HQRndNormal(rs));
if(c[i]>0.0)
{
bndl.Set(i,v);
bndu.Set(i,AL_POSINF);
}
else
{
bndl.Set(i,AL_NEGINF);
bndu.Set(i,v);
}
}
i=CHighQualityRand::HQRndUniformI(rs,n);
do
{
c.Set(i,CHighQualityRand::HQRndNormal(rs));
}
while(c[i]==0.0);
c.Add(i,0.1*MathSign(c[i]));
v=MathPow(q,CHighQualityRand::HQRndNormal(rs))-MathPow(q,CHighQualityRand::HQRndNormal(rs));
if(c[i]<0.0)
{
bndl.Set(i,v);
bndu.Set(i,AL_POSINF);
}
else
{
bndl.Set(i,AL_NEGINF);
bndu.Set(i,v);
}
return;
}
//--- Boundary and single-sided linear constraints; a bit more tricky to make it primal unbounded
if(ptype==1)
{
q=5.0;
m=1+CHighQualityRand::HQRndUniformI(rs,2*n);
a=matrix<double>::Zeros(m,n);
al=vector<double>::Zeros(m);
au=vector<double>::Zeros(m);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
c.Set(i,CHighQualityRand::HQRndNormal(rs));
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
if(c[i]>=0.0)
{
bndl.Set(i,AL_NEGINF);
bndu.Set(i,x0[i]);
}
else
{
bndl.Set(i,x0[i]);
bndu.Set(i,AL_POSINF);
}
}
for(i=0; i<m; i++)
{
v=0;
vv=0;
for(j=0; j<n; j++)
{
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
v+=a.Get(i,j)*x0[j];
vv+=a.Get(i,j)*c[j];
}
if(vv>=0.0)
{
al.Set(i,AL_NEGINF);
au.Set(i,v);
}
else
{
al.Set(i,v);
au.Set(i,AL_POSINF);
}
}
return;
}
CAp::Assert(false,"GenerateUnboundedLPProblem failed");
}
//+------------------------------------------------------------------+
//| This function generates random primal infeasible LP problem |
//+------------------------------------------------------------------+
void CTestMinLPUnit::GenerateInfeasibleLPProblem(CHighQualityRandState &rs,
int n,CRowDouble &c,
CRowDouble &bndl,
CRowDouble &bndu,
CMatrixDouble &a,
CRowDouble &al,
CRowDouble &au,int &m)
{
//--- create variables
int i=0;
int ptype=0;
int pcount=0;
double v=0;
double q=0;
double minerr=0;
CRowDouble xx;
CRowDouble d;
CRowDouble x0;
CRowInt tmpi;
m=0;
c=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
//--- Choose problem type
pcount=3;
ptype=CHighQualityRand::HQRndUniformI(rs,pcount);
//--- Boundary-only constraints, can be easily made primal infeasible
if(ptype==0)
{
q=5.0;
m=0;
for(i=0; i<n; i++)
{
c.Set(i,CHighQualityRand::HQRndNormal(rs));
v=MathPow(q,CHighQualityRand::HQRndNormal(rs))-MathPow(q,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,v-MathPow(q,CHighQualityRand::HQRndNormal(rs)));
bndu.Set(i,v+MathPow(q,CHighQualityRand::HQRndNormal(rs)));
}
i=CHighQualityRand::HQRndUniformI(rs,n);
v=bndl[i];
bndl.Set(i,bndu[i]);
bndu.Set(i,v);
return;
}
//--- Linearly constrained problem with infeasible box constraints
if(ptype==1)
{
q=5;
m=1+CHighQualityRand::HQRndUniformI(rs,2*n);
GenerateCABXD(rs,n,m,c,a,xx,d,tmpi);
GenerateBounds(rs,n,m,xx,d,tmpi,bndl,bndu,al,au);
i=CHighQualityRand::HQRndUniformI(rs,n);
v=MathPow(q,CHighQualityRand::HQRndNormal(rs))-MathPow(q,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,v+MathPow(q,CHighQualityRand::HQRndNormal(rs)));
bndu.Set(i,v-MathPow(q,CHighQualityRand::HQRndNormal(rs)));
return;
}
//--- Linearly constrained problem with infeasible linear constraints
if(ptype==2)
{
q=5;
minerr=0.01;
m=1+CHighQualityRand::HQRndUniformI(rs,2*n);
GenerateCABXD(rs,n,m,c,a,xx,d,tmpi);
GenerateBounds(rs,n,m,xx,d,tmpi,bndl,bndu,al,au);
CApServ::RMatrixResize(a,CAp::Rows(a)+1,CAp::Cols(a));
al.Resize(al.Size()+1);
au.Resize(au.Size()+1);
for(i=0; i<n; i++)
a.Set(m,i,a.Get(m-1,i));
v=minerr+MathPow(q,CHighQualityRand::HQRndNormal(rs));
v=v*MathMax(1.0,MathSqrt(CAblasF::RDotRR(n,a,m,a,m)));
if(MathIsValidNumber(al[m-1]) && MathIsValidNumber(au[m-1]))
{
al.Set(m,au[m-1]+v);
au.Set(m,al[m]+MathPow(q,CHighQualityRand::HQRndNormal(rs)));
}
if(MathIsValidNumber(al[m-1]) && !MathIsValidNumber(au[m-1]))
{
au.Set(m,al[m-1]-v);
al.Set(m,au[m]-MathPow(q,CHighQualityRand::HQRndNormal(rs)));
}
if(!MathIsValidNumber(al[m-1]) && MathIsValidNumber(au[m-1]))
{
al.Set(m,au[m-1]+v);
au.Set(m,al[m]+MathPow(q,CHighQualityRand::HQRndNormal(rs)));
}
if(!MathIsValidNumber(al[m-1]) && !MathIsValidNumber(au[m-1]))
{
al.Set(m,v);
au.Set(m,v-MathPow(q,CHighQualityRand::HQRndNormal(rs)));
}
m++;
return;
}
CAp::Assert(false,"GenerateUnboundedLPProblem failed");
}
//+------------------------------------------------------------------+
//| This function generates random C[] and A[] as well as feasible |
//| point XX[N + M], with XX[0..N - 1] being structural variables and|
//| XX[N..N + M - 1] being values of logical variables. |
//| It also splits structural / logical variables into basic/nonbasic|
//| ones and generates BasicNonbasic[] array, with indexes of basic |
//| variables being stored in first M positions, and indexes of |
//| non-basic variables being stored in the next N positions. |
//+------------------------------------------------------------------+
void CTestMinLPUnit::GenerateCABXD(CHighQualityRandState &rs,
int n,
int m,
CRowDouble &c,
CMatrixDouble &a,
CRowDouble &xx,
CRowDouble &d,
CRowInt &basicnonbasic)
{
//--- create variables
CRowDouble y;
CRowDouble cx;
CMatrixDouble ax;
CRowInt pivots;
int i=0;
int j=0;
int k=0;
int kk=0;
double v=0;
double mincoeff=0.001;
//--- Randomly partition columns into basic and nonbasic ones
basicnonbasic.Resize(n+m);
for(i=0; i<=n+m-1; i++)
basicnonbasic.Set(i,i);
for(i=0; i<m; i++)
{
k=i+CHighQualityRand::HQRndUniformI(rs,n+m-i);
kk=basicnonbasic[i];
basicnonbasic.Set(i,basicnonbasic[k]);
basicnonbasic.Set(k,kk);
}
//--- Generate constraint matrix A
a=matrix<double>::Zeros(m,n);
ax=matrix<double>::Zeros(m,n+m);
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
{
ax.Set(i,j,CHighQualityRand::HQRndNormal(rs));
ax.Add(i,j,MathSign(ax.Get(i,j))*mincoeff);
a.Set(i,j,ax.Get(i,j));
}
ax.Set(i,n+i,-1);
}
//--- Generate feasible point
xx=vector<double>::Zeros(n+m);
for(i=0; i<n; i++)
xx.Set(i,CHighQualityRand::HQRndNormal(rs));
for(i=0; i<m; i++)
{
v=0;
for(j=0; j<n; j++)
v+=xx[j]*ax.Get(i,j);
xx.Set(n+i,v);
}
//--- Generate random Y.
//--- Entries corresponding to basic constraints must be zero.
y=vector<double>::Zeros(m);
for(i=0; i<m; i++)
y.Set(i,CHighQualityRand::HQRndNormal(rs));
for(i=0; i<m; i++)
if(basicnonbasic[i]>=n)
y.Set(basicnonbasic[i]-n,0);
//--- Generate D and C
d=vector<double>::Zeros(n+m);
CAblas::RMatrixGemVect(n+m,m,-1.0,ax,0,0,1,y,0,0.0,d,0);
cx=vector<double>::Zeros(n+m);
for(i=0; i<n; i++)
cx.Set(i,CHighQualityRand::HQRndNormal(rs));
for(i=0; i<m; i++)
cx.Set(basicnonbasic[i],-d[basicnonbasic[i]]);
CAblasF::RAddV(n+m,1.0,cx,d);
c=vector<double>::Zeros(n);
CAblasF::RCopyV(n,cx,c);
}
//+------------------------------------------------------------------+
//| This function generates random non-degenerate bounds for given XX|
//| and D. |
//+------------------------------------------------------------------+
void CTestMinLPUnit::GenerateBounds(CHighQualityRandState &rs,
int n,int m,
CRowDouble &xx,
CRowDouble &d,
CRowInt &basicnonbasic,
CRowDouble &bndl,
CRowDouble &bndu,
CRowDouble &al,
CRowDouble &au)
{
//--- create variables
double v0=0;
double v1=0;
double q=0;
int i=0;
int kk=0;
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
al=vector<double>::Zeros(m);
au=vector<double>::Zeros(m);
//--- Choose bounds in such a way that non-basic variables are
//--- strictly at the bounds and dual feasible, and all basic
//--- variables are strictly within bounds.
q=5;
for(kk=0; kk<=n+m-1; kk++)
{
i=basicnonbasic[kk];
v0=xx[i]-MathPow(q,CHighQualityRand::HQRndNormal(rs));
v1=xx[i]+MathPow(q,CHighQualityRand::HQRndNormal(rs));
if(CHighQualityRand::HQRndNormal(rs)>0.0)
v0=AL_NEGINF;
if(CHighQualityRand::HQRndNormal(rs)>0.0)
v1=AL_POSINF;
if(kk>=m)
{
if((double)(d[i])>=0.0)
{
v0=xx[i];
if(CHighQualityRand::HQRndUniformR(rs)<0.05)
v1=v0;
}
else
{
v1=xx[i];
if(CHighQualityRand::HQRndUniformR(rs)<0.05)
v0=v1;
}
}
if(i<n)
{
bndl.Set(i,v0);
bndu.Set(i,v1);
}
else
{
al.Set(i-n,v0);
au.Set(i-n,v1);
}
}
}
//+------------------------------------------------------------------+
//| This function sets linear constraints using randomly chosen |
//| sequence of SetLC / AddLC calls. |
//| It may also modify constraints: |
//| * split two-sided constraint into two one-sided constraints |
//| * create duplicates |
//| * insert zero dummies |
//| Thus, you should not use this function when you want to test |
//| things like correctness of Lagrange multipliers and so on. |
//+------------------------------------------------------------------+
void CTestMinLPUnit::ModifyAndSendConstraintsTo(int n,CMatrixDouble &a,
CRowDouble &al,
CRowDouble &au,
int m,
CHighQualityRandState &rs,
CMinLPState &state)
{
//--- create variables
int stype=0;
CRowDouble ai;
CRowInt idxi;
int nz=0;
int nzmod=0;
CMatrixDouble a1;
CSparseMatrix sa;
CRowInt ct;
int ccnt=0;
int i=0;
int j=0;
int k=0;
int t=0;
int minit=0;
int nadd=0;
int ndup=0;
double v=0;
//--- Choose sequence type
stype=CHighQualityRand::HQRndUniformI(rs,4);
//--- Straightforward SetLC2() call
if(stype==0)
{
CMinLP::MinLPSetLC2Dense(state,a,al,au,m);
return;
}
//--- SetLC1() call (conversion to other format)
if(stype==1)
{
a1=matrix<double>::Zeros(2*m,n+1);
ct.Resize(2*m);
ccnt=0;
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
{
a1.Set(ccnt+0,j,a.Get(i,j));
a1.Set(ccnt+1,j,a.Get(i,j));
}
if((MathIsValidNumber(al[i]) && MathIsValidNumber(au[i])) && al[i]==au[i])
{
a1.Set(ccnt,n,al[i]);
ct.Set(ccnt,0);
ccnt++;
}
else
{
if(MathIsValidNumber(al[i]))
{
a1.Set(ccnt,n,al[i]);
ct.Set(ccnt,1);
ccnt++;
}
if(MathIsValidNumber(au[i]))
{
a1.Set(ccnt,n,au[i]);
ct.Set(ccnt,-1);
ccnt++;
}
}
}
CMinLP::MinLPSetLC(state,a1,ct,ccnt);
return;
}
//--- Straightforward SetLC2Sparse() call
if(stype==2)
{
if(m==0)
{
CMinLP::MinLPSetLC2(state,sa,al,au,0);
return;
}
CSparse::SparseCreate(m,n,0,sa);
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
if(a.Get(i,j)!=0.0)
CSparse::SparseSet(sa,i,j,a.Get(i,j));
}
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CSparse::SparseConvertToCRS(sa);
CMinLP::MinLPSetLC2(state,sa,al,au,m);
return;
}
//--- A few rows are added with SetLC2Dense() call, the rest
//--- is processed with random mix of AddLC2Dense()/AddLC2()
if(stype==3)
{
if(m==0)
{
CMinLP::MinLPSetLC2(state,sa,al,au,0);
return;
}
minit=CHighQualityRand::HQRndUniformI(rs,m);
CMinLP::MinLPSetLC2Dense(state,a,al,au,minit);
for(i=minit; i<m; i++)
{
if(CHighQualityRand::HQRndUniformI(rs,2)==0)
{
//--- Add as dense row
ai=vector<double>::Zeros(n);
for(j=0; j<n; j++)
ai.Set(j,a.Get(i,j));
CMinLP::MinLPAddLC2Dense(state,ai,al[i],au[i]);
}
else
{
//--- Add as sparse row with shuffle and possible duplicates
ai=vector<double>::Zeros(n);
idxi.Resize(n);
nz=0;
for(j=0; j<n; j++)
{
if(a.Get(i,j)!=0.0)
{
ai.Set(nz,a.Get(i,j));
idxi.Set(nz,j);
nz++;
}
}
ai.Resize(nz);
idxi.Resize(nz);
//--- Already existing elements are split in two
nadd=0;
if(nz!=0)
{
nadd=CHighQualityRand::HQRndUniformI(rs,2)*CHighQualityRand::HQRndUniformI(rs,4);
ai.Resize(nz+nadd);
idxi.Resize(nz+nadd);
for(j=0; j<=nadd-1; j++)
{
k=CHighQualityRand::HQRndUniformI(rs,nz);
v=CHighQualityRand::HQRndNormal(rs);
idxi.Set(nz+j,idxi[k]);
ai.Set(nz+j,v);
ai.Set(k,ai[k]-v);
}
}
//--- Possibly nonexistent elements are added as +V and -V
//--- Do not performed for NZ=0 rows because it may introduce slightly nonzero coefficients
//--- to exactly zero row (constraint normalization goes crazy).
ndup=CHighQualityRand::HQRndUniformI(rs,2)*CHighQualityRand::HQRndUniformI(rs,4);
if(nz==0)
ndup=0;
ai.Resize(nz+nadd+2*ndup);
idxi.Resize(nz+nadd+2*ndup);
for(j=0; j<=ndup-1; j++)
{
k=CHighQualityRand::HQRndUniformI(rs,n);
v=CHighQualityRand::HQRndNormal(rs);
idxi.Set(nz+nadd+2*j+0,k);
idxi.Set(nz+nadd+2*j+1,k);
ai.Set(nz+nadd+2*j+0,v);
ai.Set(nz+nadd+2*j+1,-v);
}
nzmod=nz+nadd+2*ndup;
for(j=0; j<=nzmod-1; j++)
{
k=j+CHighQualityRand::HQRndUniformI(rs,nzmod-j);
t=idxi[j];
idxi.Set(j,idxi[k]);
idxi.Set(k,t);
v=ai[j];
ai.Set(j,ai[k]);
ai.Set(k,v);
}
CMinLP::MinLPAddLC2(state,idxi,ai,nzmod,al[i],au[i]);
}
}
return;
}
CAp::Assert(false,"MINLPTest: integrity check failed");
}
//+------------------------------------------------------------------+
//| This function selects random LP solver |
//+------------------------------------------------------------------+
void CTestMinLPUnit::SelectRandomSolver(CMinLPState &state)
{
int k=CMath::RandomInteger(2);
if(k==0)
CMinLP::MinLPSetAlgoDSS(state,0.0);
if(k==1)
CMinLP::MinLPSetAlgoIPM(state,0.0);
}
//+------------------------------------------------------------------+
//| This function selects specific LP solver. |
//| Reg parameter is used to regularize IPM algo, ignore for DSS |
//+------------------------------------------------------------------+
void CTestMinLPUnit::SelectSolver(CMinLPState &state,int st)
{
if(st==0)
{
CMinLP::MinLPSetAlgoDSS(state,0.0);
return;
}
if(st==1)
{
CMinLP::MinLPSetAlgoIPM(state,0.0);
return;
}
CAp::Assert(false,"SelectSolver: unexpected solver");
}
//+------------------------------------------------------------------+
//| Shifts nonzero elements of C[] away from zero |
//+------------------------------------------------------------------+
void CTestMinLPUnit::ShiftFromZero(CRowDouble &c,int n,double s)
{
for(int i=0; i<n; i++)
c.Add(i,MathSign(c[i])*s);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestMinNLCUnit
{
public:
static const int m_maxsolvertype;
static const int m_maxoptguardlevel;
static bool CTestMinNLCUnit::TestMinNLC(bool silent);
private:
static void TestBC(bool &waserrors);
static void TestLC(bool &waserrors);
static void TestNLC(bool &waserrors);
static void TestOther(bool &waserrors);
static void TestOptGuard(bool &waserrors);
static void TestBugs(bool &waserrors);
static void TestOptGuardC1Test0ReportForTask0(bool &err,COptGuardNonC1Test0Report &rep,CMatrixDouble &a,int n);
static void TestOptGuardC1Test1ReportForTask0(bool &err,COptGuardNonC1Test1Report &rep,CMatrixDouble &a,int n);
static void TestOptGuardC1Test0ReportForTask1(bool &err,COptGuardNonC1Test0Report &rep,CMatrixDouble &a,int n,int goodidx);
static void TestOptGuardC1Test1ReportForTask1(bool &err,COptGuardNonC1Test1Report &rep,CMatrixDouble &a,int n,int goodidx);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
const int CTestMinNLCUnit::m_maxsolvertype=2;
const int CTestMinNLCUnit::m_maxoptguardlevel=1;
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestMinNLCUnit::TestMinNLC(bool silent)
{
//--- create variables
bool result;
bool waserrors;
bool bcerr;
bool lcerr;
bool nlcerr;
bool othererr;
bool optguarderr;
bool bugs;
waserrors=false;
bcerr=false;
lcerr=false;
nlcerr=false;
othererr=false;
optguarderr=false;
bugs=false;
TestBugs(bugs);
TestBC(bcerr);
TestLC(lcerr);
TestNLC(nlcerr);
TestOther(othererr);
TestOptGuard(optguarderr);
//--- end
waserrors=((((bcerr||lcerr)||nlcerr)||othererr)||bugs)||optguarderr;
if(!silent)
{
Print("TESTING MINNLC OPTIMIZATION");
Print("GENERIC TESTS:");
PrintResult("* box constrained",!bcerr);
PrintResult("* linearly constrained",!lcerr);
PrintResult("* nonlinearly constrained",!nlcerr);
PrintResult("* other properties",!othererr);
PrintResult("* optguard integrity monitor",!optguarderr);
PrintResult("* fixed bugs",!bugs);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| This function tests bound constrained quadratic programming |
//| algorithm. |
//| On failure sets error flag. |
//+------------------------------------------------------------------+
void CTestMinNLCUnit::TestBC(bool &waserrors)
{
CMinNLCState state;
CMinNLCReport rep;
COptGuardReport ogrep;
int n=0;
int pass=0;
int i=0;
int j=0;
int aulits=0;
double tolx=0;
double tolg=0;
int scaletype=0;
double rho=0;
int solvertype=0;
CMatrixDouble fulla;
CRowDouble b;
CRowDouble bndl;
CRowDouble bndu;
CRowDouble s;
CRowDouble x0;
CRowDouble x1;
double gnorm=0;
double g=0;
int prectype=0;
CHighQualityRandState rs;
CHighQualityRand::HQRndRandomize(rs);
for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++)
{
//--- Convex test:
//--- * N dimensions
//--- * random number (0..N) of random boundary constraints
//--- * positive-definite quadratic programming problem
//--- * initial point is random (maybe infeasible!)
//--- * random scale (unit or non-unit)
aulits=10;
rho=200.0;
tolx=0.0005;
tolg=0.01;
for(n=1; n<=10; n++)
{
for(pass=1; pass<=10; pass++)
{
for(prectype=0; prectype<=2; prectype++)
{
//--- Generate well-conditioned problem with unit scale
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,AL_NEGINF);
bndu.Set(i,AL_POSINF);
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
j=CHighQualityRand::HQRndUniformI(rs,5);
if(j==0)
bndl.Set(i,0);
if(j==1)
bndu.Set(i,0);
if(j==2)
{
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
bndu.Set(i,bndl[i]);
}
if(j==3)
{
bndl.Set(i,-0.1);
bndu.Set(i,0.1);
}
}
//--- Apply scaling to quadratic/linear term, so problem becomes
//--- well-conditioned in the scaled coordinates.
scaletype=CHighQualityRand::HQRndUniformI(rs,2);
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
if(scaletype==0)
s.Set(i,1);
else
s.Set(i,MathExp(5*CHighQualityRand::HQRndNormal(rs)));
}
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
fulla.Mul(i,j,1.0/(s[i]*s[j]));
}
x0*=s;
bndl*=s;
bndu*=s;
b/=s;
//--- Solve problem
CMinNLC::MinNLCCreate(n,x0,state);
if(solvertype==0)
{
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
if(prectype==1)
CMinNLC::MinNLCSetPrecExactLowRank(state,0);
if(prectype==2)
CMinNLC::MinNLCSetPrecExactRobust(state,0);
}
else
{
if(solvertype==1)
CMinNLC::MinNLCSetAlgoSLP(state);
else
{
if(solvertype==2)
CMinNLC::MinNLCSetAlgoSQP(state);
else
{
CAp::Assert(false);
waserrors=true;
return;
}
}
}
if(scaletype!=0)
CMinNLC::MinNLCSetScale(state,s);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
CMinNLC::MinNLCSetBC(state,bndl,bndu);
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
for(j=0; j<n; j++)
{
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:240");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:241");
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:245");
}
if(waserrors)
return;
//--- Check constraint violation reports
CAp::SetErrorFlag(waserrors,rep.m_bcerr>tolx,"testminnlcunit.ap:253");
CAp::SetErrorFlag(waserrors,rep.m_bcidx>=n,"testminnlcunit.ap:254");
CAp::SetErrorFlag(waserrors,rep.m_lcerr>0.0,"testminnlcunit.ap:255");
CAp::SetErrorFlag(waserrors,rep.m_lcidx>=0,"testminnlcunit.ap:256");
//--- Check feasibility properties
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(waserrors,MathIsValidNumber(bndl[i]) && x1[i]<=(double)(bndl[i]-tolx*s[i]),"testminnlcunit.ap:263");
CAp::SetErrorFlag(waserrors,MathIsValidNumber(bndu[i]) && x1[i]>=(double)(bndu[i]+tolx*s[i]),"testminnlcunit.ap:264");
}
//--- Test - calculate scaled constrained gradient at solution,
//--- check its norm.
gnorm=0.0;
for(i=0; i<n; i++)
{
g=b[i]+CAblasF::RDotVR(n,x1,fulla,i);
g*=s[i];
if((MathIsValidNumber(bndl[i]) && MathAbs(x1[i]-bndl[i])<(double)(tolx*s[i])) && g>0.0)
g=0;
if((MathIsValidNumber(bndu[i]) && MathAbs(x1[i]-bndu[i])<(double)(tolx*s[i])) && g<0.0)
g=0;
gnorm+=CMath::Sqr(g);
}
gnorm=MathSqrt(gnorm);
CAp::SetErrorFlag(waserrors,gnorm>tolg,"testminnlcunit.ap:286");
}
}
}
//--- Non-convex test:
//--- * N dimensions, N>=2
//--- * box constraints, x[i] in [-1,+1]
//--- * A is symmetric indefinite with condition number 50.0
//--- * random B with normal entries
//--- * initial point is random, feasible
//--- * scale is always unit
//--- We check that constrained problem can be successfully solved.
//--- We do not check ability to detect unboundedness of unconstrained
//--- problem because there is such functionality in MinNLC.
aulits=50;
rho=200.0;
tolx=0.0005;
tolg=0.01;
for(n=2; n<=10; n++)
{
for(pass=1; pass<=10; pass++)
{
for(prectype=0; prectype<=2; prectype++)
{
//--- Generate problem
fulla.Fill(0.0);
for(i=0; i<n; i++)
fulla.Set(i,i,-1-CHighQualityRand::HQRndUniformR(rs));
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,0.05*CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,-1);
bndu.Set(i,1);
x0.Set(i,2*CHighQualityRand::HQRndUniformR(rs)-1);
}
//--- Solve problem:
//--- * without constraints we expect failure
//--- * with constraints algorithm must succeed
CMinNLC::MinNLCCreate(n,x0,state);
if(solvertype==0)
{
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
if(prectype==0)
CMinNLC::MinNLCSetPrecInexact(state);
if(prectype==1)
CMinNLC::MinNLCSetPrecExactLowRank(state,0);
if(prectype==2)
CMinNLC::MinNLCSetPrecExactRobust(state,0);
}
else
{
if(solvertype==1)
CMinNLC::MinNLCSetAlgoSLP(state);
else
{
if(solvertype==2)
CMinNLC::MinNLCSetAlgoSQP(state);
else
{
CAp::Assert(false);
waserrors=true;
return;
}
}
}
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
CMinNLC::MinNLCSetBC(state,bndl,bndu);
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
for(j=0; j<n; j++)
{
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:378");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:379");
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:383");
}
if(waserrors)
return;
//--- Check feasibility properties
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(waserrors,MathIsValidNumber(bndl[i]) && x1[i]<=(double)(bndl[i]-tolx),"testminnlcunit.ap:393");
CAp::SetErrorFlag(waserrors,MathIsValidNumber(bndu[i]) && x1[i]>=(double)(bndu[i]+tolx),"testminnlcunit.ap:394");
}
//--- Test - calculate scaled constrained gradient at solution,
//--- check its norm.
gnorm=0.0;
for(i=0; i<n; i++)
{
g=b[i]+CAblasF::RDotVR(n,x1,fulla,i);
if((MathIsValidNumber(bndl[i]) && MathAbs(x1[i]-bndl[i])<tolx) && g>0.0)
g=0;
if((MathIsValidNumber(bndu[i]) && MathAbs(x1[i]-bndu[i])<tolx) && g<0.0)
g=0;
gnorm+=CMath::Sqr(g);
}
gnorm=MathSqrt(gnorm);
CAp::SetErrorFlag(waserrors,gnorm>tolg,"testminnlcunit.ap:415");
}
}
}
}
}
//+------------------------------------------------------------------+
//| This function tests linearly constrained quadratic programming |
//| algorithm. |
//| Sets error flag on failure. |
//+------------------------------------------------------------------+
void CTestMinNLCUnit::TestLC(bool &waserrors)
{
//--- create variables
int n=0;
int k=0;
int i=0;
int j=0;
int pass=0;
CMatrixDouble q;
CMatrixDouble fulla;
double v=0;
double vv=0;
CRowDouble tmp;
CRowDouble bl;
CRowDouble bu;
CRowDouble b;
CRowDouble xs0;
CRowDouble xstart;
CRowDouble x;
CRowDouble x0;
CRowDouble x1;
CRowDouble x2;
CRowDouble xm;
CRowDouble s;
CRowDouble g;
CRowDouble bndl;
CRowDouble bndu;
CMatrixDouble a;
CMatrixDouble c;
CMatrixDouble ce;
CRowInt ct;
bool nonnegative[];
double tolx=0;
double tolg=0;
double tolf=0;
int aulits=0;
double rho=0;
CMinNLCState state;
CMinNLCReport rep;
COptGuardReport ogrep;
int scaletype=0;
double f0=0;
double f1=0;
double tolconstr=0;
int bscale=0;
int akind=0;
int ccnt=0;
int shiftkind=0;
int prectype=0;
int solvertype=0;
double gnrm2=0;
CHighQualityRandState rs;
CSNNLSSolver nnls;
int i_=0;
CHighQualityRand::HQRndRandomize(rs);
for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++)
{
//--- First test:
//--- * K<N equality constraints Q*x = Q*x0, where Q is random
//--- orthogonal K*N matrix, x0 is some random vector
//--- * quadratic programming problem with identity quadratic term A and
//--- linear term equal to xm*A, where xm is some random vector such
//--- that Q*xm=0. It is always possible to find such xm, because K<N
//--- Thus, optimization problem has form 0.5*x'*I*x-xm'*x.
//--- * exact solution must be equal to x0
//--- NOTE: this test is important because it is the only linearly constrained one
//--- which uses non-unit scaling!
rho=200.0;
tolx=0.0005;
tolf=0.0001;
aulits=50;
for(n=2; n<=6; n++)
{
for(k=1; k<n; k++)
{
for(prectype=-1; prectype<=2; prectype++)
{
//--- Generate problem: A, b, CMatrix, x0, XStart
CMatGen::RMatrixRndOrthogonal(n,q);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xm=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
xm.Set(i,x0[i]);
xstart.Set(i,2*CMath::RandomReal()-1);
}
for(i=0; i<k; i++)
{
for(i_=0; i_<n; i_++)
c.Set(i,i_,q.Get(i,i_));
v=CAblasF::RDotVR(n,x0,q,i);
c.Set(i,n,v);
ct.Set(i,0);
v=2*CMath::RandomReal()-1;
for(i_=0; i_<n; i_++)
xm.Add(i_,v*q.Get(i,i_));
}
for(i=0; i<n; i++)
b.Set(i,-xm[i]);
//--- Apply scaling to linear term and known solution,
//--- so problem becomes well-conditioned in the scaled coordinates.
scaletype=CHighQualityRand::HQRndUniformI(rs,2);
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
if(scaletype==0)
s.Set(i,1);
else
s.Set(i,MathExp(5*CHighQualityRand::HQRndNormal(rs)));
}
x0*=s;
xstart*=s;
b/=s;
for(i=0; i<k; i++)
for(j=0; j<n; j++)
c.Mul(i,j,1.0/s[j]);
//--- Create optimizer, solve
CMinNLC::MinNLCCreate(n,xstart,state);
CMinNLC::MinNLCSetScale(state,s);
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
CMinNLC::MinNLCSetLC(state,c,ct,k);
if(solvertype==0)
{
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
if(prectype==0)
CMinNLC::MinNLCSetPrecInexact(state);
if(prectype==1)
CMinNLC::MinNLCSetPrecExactLowRank(state,0);
if(prectype==2)
CMinNLC::MinNLCSetPrecExactRobust(state,0);
}
else
{
if(solvertype==1)
CMinNLC::MinNLCSetAlgoSLP(state);
else
{
if(solvertype==2)
CMinNLC::MinNLCSetAlgoSQP(state);
else
{
CAp::Assert(false);
waserrors=true;
return;
}
}
}
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]+0.5*CMath::Sqr(state.m_x[i])/CMath::Sqr(s[i]));
state.m_j.Set(0,i,b[i]+state.m_x[i]/CMath::Sqr(s[i]));
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:580");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:581");
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:585");
}
if(waserrors)
return;
//--- Check constraint violation reports
CAp::SetErrorFlag(waserrors,rep.m_bcerr>0.0,"testminnlcunit.ap:593");
CAp::SetErrorFlag(waserrors,rep.m_bcidx>=0,"testminnlcunit.ap:594");
CAp::SetErrorFlag(waserrors,rep.m_lcerr>tolx,"testminnlcunit.ap:595");
CAp::SetErrorFlag(waserrors,rep.m_lcidx>=n,"testminnlcunit.ap:596");
//--- Compare with analytic solution
f0=0;
f1=0;
for(i=0; i<n; i++)
{
f0+=b[i]*x0[i]+0.5*CMath::Sqr(x0[i]/s[i]);
f1+=b[i]*x1[i]+0.5*CMath::Sqr(x1[i]/s[i]);
}
CAp::SetErrorFlag(waserrors,(double)(MathAbs(f1-f0))>(double)(tolf),"testminnlcunit.ap:608");
}
}
}
//--- Inequality constrained problem:
//--- * N*N diagonal A
//--- * one inequality constraint q'*x>=0, where q is random unit vector
//--- * optimization problem has form 0.5*x'*A*x-(x1*A)*x,
//--- where x1 is some random vector
//--- * either:
//--- a) x1 is feasible => we must Stop at x1
//--- b) x1 is infeasible => we must Stop at the boundary q'*x=0 and
//--- projection of gradient onto q*x=0 must be zero
//--- NOTE: we make several passes because some specific kind of errors is rarely
//--- caught by this test, so we need several repetitions.
rho=200.0;
tolx=0.0005;
tolg=0.01;
aulits=50;
for(n=2; n<=6; n++)
{
for(pass=0; pass<=4; pass++)
{
//--- Generate problem: A, b, CMatrix, x0, XStart
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
b=vector<double>::Zeros(n);
xm=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
c=matrix<double>::Zeros(1,n+1);
ct.Resize(1);
for(i=0; i<n; i++)
{
xm.Set(i,2*CMath::RandomReal()-1);
xstart.Set(i,2*CMath::RandomReal()-1);
}
do
{
v=0;
for(i=0; i<n; i++)
{
c.Set(0,i,2*CMath::RandomReal()-1);
v+=CMath::Sqr(c.Get(0,i));
}
v=MathSqrt(v);
}
while(v==0.0);
for(i=0; i<n; i++)
c.Mul(0,i,1.0/v);
c.Set(0,n,0);
ct.Set(0,1);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xm,fulla,i);
b.Set(i,-v);
}
//--- Apply scaling to linear term and known solution,
//--- so problem becomes well-conditioned in the scaled coordinates.
scaletype=CHighQualityRand::HQRndUniformI(rs,2);
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
if(scaletype==0)
s.Set(i,1);
else
s.Set(i,MathExp(CHighQualityRand::HQRndNormal(rs)));
}
for(i=0; i<n; i++)
for(j=0; j<n; j++)
fulla.Mul(i,j,1.0/(s[i]*s[j]));
xm*=s;
xstart*=s;
b/=s;
for(j=0; j<n; j++)
c.Mul(0,j,1.0/s[j]);
//--- Create optimizer, solve
CMinNLC::MinNLCCreate(n,xstart,state);
if(solvertype==0)
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
else
{
if(solvertype==1)
CMinNLC::MinNLCSetAlgoSLP(state);
else
{
if(solvertype==2)
CMinNLC::MinNLCSetAlgoSQP(state);
else
{
CAp::Assert(false);
waserrors=true;
return;
}
}
}
CMinNLC::MinNLCSetLC(state,c,ct,1);
CMinNLC::MinNLCSetScale(state,s);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
for(j=0; j<n; j++)
{
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:730");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:731");
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:735");
}
if(waserrors)
return;
//--- Check constraint violation reports
CAp::SetErrorFlag(waserrors,rep.m_bcerr>0.0,"testminnlcunit.ap:743");
CAp::SetErrorFlag(waserrors,rep.m_bcidx>=0,"testminnlcunit.ap:744");
CAp::SetErrorFlag(waserrors,rep.m_lcerr>tolx,"testminnlcunit.ap:745");
CAp::SetErrorFlag(waserrors,rep.m_lcidx>=n,"testminnlcunit.ap:746");
//--- Test solution
g=b;
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,x1,fulla,i);
g.Add(i,v);
}
v=CAblasF::RDotVR(n,x1,c,0);
CAp::SetErrorFlag(waserrors,v<(double)(-tolx),"testminnlcunit.ap:759");
if(v<tolx)
{
//--- Point at the boundary, project gradient into
//--- equality-constrained subspace.
v=CAblasF::RDotVR(n,g,c,0);
vv=CAblasF::RDotRR(n,c,0,c,0);
v=v/vv;
for(i_=0; i_<n; i_++)
g.Add(i_,- c.Get(0,i_)*v);
}
v=0.0;
for(i=0; i<n; i++)
v+=CMath::Sqr(g[i]*s[i]);
CAp::SetErrorFlag(waserrors,MathSqrt(v)>tolg,"testminnlcunit.ap:779");
}
}
//--- Equality-constrained test:
//--- * N*N SPD A
//--- * K<N equality constraints Q*x = Q*x0, where Q is random
//--- orthogonal K*N matrix, x0 is some random vector
//--- * optimization problem has form 0.5*x'*A*x-(xm*A)*x,
//--- where xm is some random vector
//--- * we check feasibility properties of the solution
//--- * we do not know analytic form of the exact solution,
//--- but we know that projection of gradient into equality constrained
//--- subspace must be zero at the solution
rho=200.0;
tolx=0.0005;
tolg=0.01;
aulits=50;
for(n=2; n<=6; n++)
{
for(k=1; k<n; k++)
{
//--- Generate problem: A, b, CMatrix, x0, XStart
CMatGen::RMatrixRndOrthogonal(n,q);
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xm=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
xm.Set(i,2*CMath::RandomReal()-1);
xstart.Set(i,2*CMath::RandomReal()-1);
}
for(i=0; i<k; i++)
{
for(i_=0; i_<n; i_++)
c.Set(i,i_,q.Get(i,i_));
v=CAblasF::RDotVR(n,x0,q,i);
c.Set(i,n,v);
ct.Set(i,0);
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xm,fulla,i);
b.Set(i,-v);
}
//--- Create optimizer, solve
CMinNLC::MinNLCCreate(n,xstart,state);
if(solvertype==0)
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
else
{
if(solvertype==1)
CMinNLC::MinNLCSetAlgoSLP(state);
else
{
if(solvertype==2)
CMinNLC::MinNLCSetAlgoSQP(state);
else
{
CAp::Assert(false);
waserrors=true;
return;
}
}
}
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
CMinNLC::MinNLCSetLC(state,c,ct,k);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
for(j=0; j<n; j++)
{
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:874");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:875");
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:879");
}
if(waserrors)
return;
//--- Check constraint violation reports
CAp::SetErrorFlag(waserrors,rep.m_bcerr>0.0,"testminnlcunit.ap:887");
CAp::SetErrorFlag(waserrors,rep.m_bcidx>=0,"testminnlcunit.ap:888");
CAp::SetErrorFlag(waserrors,rep.m_lcerr>tolx,"testminnlcunit.ap:889");
CAp::SetErrorFlag(waserrors,rep.m_lcidx>=n,"testminnlcunit.ap:890");
//--- Check feasibility properties and gradient projection
for(i=0; i<k; i++)
{
v=CAblasF::RDotVR(n,x1,c,i);
CAp::SetErrorFlag(waserrors,MathAbs(v-c.Get(i,n))>tolx,"testminnlcunit.ap:898");
}
g=b;
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,x1,fulla,i);
g.Add(i,v);
}
for(i=0; i<k; i++)
{
v=CAblasF::RDotVR(n,g,c,i);
for(i_=0; i_<n; i_++)
g.Add(i_,- c.Get(i,i_)*v);
}
v=CAblasF::RDotV2(n,g);
CAp::SetErrorFlag(waserrors,MathSqrt(v)>tolg,"testminnlcunit.ap:913");
}
}
//--- Boundary constraints vs linear ones:
//--- * N*N SPD A
//--- * optimization problem has form 0.5*x'*A*x-(xm*A)*x,
//--- where xm is some random vector from [-1,+1]
//--- * K=2*N constraints of the form ai<=x[i] or x[i]<=b[i],
//--- with ai in [-1.0,-0.1], bi in [+0.1,+1.0]
//--- * initial point xstart is from [-1,+2]
//--- * we solve two related QP problems:
//--- a) one with constraints posed as boundary ones
//--- b) another one with same constraints posed as general linear ones
//--- both problems must have same solution.
//--- Here we test that boundary constrained and linear inequality constrained
//--- solvers give same results.
rho=200.0;
tolx=0.0005;
tolf=0.00001;
aulits=50;
for(n=1; n<=6; n++)
{
//--- Generate problem: A, b, x0, XStart, C, CT
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
b=vector<double>::Zeros(n);
xm=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
c=matrix<double>::Zeros(2*n,n+1);
ct.Resize(2*n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
xm.Set(i,2*CMath::RandomReal()-1);
x0.Set(i,3*CMath::RandomReal()-1);
bndl.Set(i,-(0.1+0.9*CMath::RandomReal()));
bndu.Set(i,0.1+0.9*CMath::RandomReal());
c.Set(2*i+0,i,1);
c.Set(2*i+0,n,bndl[i]);
ct.Set(2*i+0,1);
c.Set(2*i+1,i,1);
c.Set(2*i+1,n,bndu[i]);
ct.Set(2*i+1,-1);
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xm,fulla,i);
b.Set(i,-v);
}
//--- Solve linear inequality constrained problem
CMinNLC::MinNLCCreate(n,x0,state);
if(solvertype==0)
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
else
{
if(solvertype==1)
CMinNLC::MinNLCSetAlgoSLP(state);
else
{
if(solvertype==2)
CMinNLC::MinNLCSetAlgoSQP(state);
else
{
CAp::Assert(false);
waserrors=true;
return;
}
}
}
CMinNLC::MinNLCSetLC(state,c,ct,2*n);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
for(j=0; j<n; j++)
{
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:1013");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1014");
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1018");
}
if(waserrors)
return;
//--- Solve boundary constrained problem
CMinNLC::MinNLCCreate(n,x0,state);
if(solvertype==0)
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
else
{
if(solvertype==1)
CMinNLC::MinNLCSetAlgoSLP(state);
else
{
if(solvertype==2)
CMinNLC::MinNLCSetAlgoSQP(state);
else
{
CAp::Assert(false);
waserrors=true;
return;
}
}
}
CMinNLC::MinNLCSetBC(state,bndl,bndu);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
for(j=0; j<n; j++)
{
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x2,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x2,n),"testminnlcunit.ap:1060");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1061");
if(waserrors)
return;
//--- Compare solutions
f0=0;
f1=0;
for(i=0; i<n; i++)
{
f0+=b[i]*x1[i];
f1+=b[i]*x2[i];
for(j=0; j<n; j++)
{
f0+=0.5*x1[i]*fulla.Get(i,j)*x1[j];
f1+=0.5*x2[i]*fulla.Get(i,j)*x2[j];
}
}
CAp::SetErrorFlag(waserrors,MathAbs(f0-f1)>(double)(tolf),"testminnlcunit.ap:1080");
}
//--- Boundary and linear equality constrained QP problem with excessive
//--- equality constraints:
//--- * N*N SPD A with moderate condtion number (up to 100)
//--- * boundary constraints 0<=x[i]<=1
//--- * K=2*N equality constraints Q*x = Q*x0, where Q is random matrix,
//--- x0 is some random vector from the feasible hypercube (0.1<=x0[i]<=0.9)
//--- * optimization problem has form 0.5*x'*A*x-b*x,
//--- where b is some random vector
//--- * because constraints are excessive, the main problem is to find
//--- feasible point; the only existing feasible point is solution,
//--- so we have to check only feasibility
rho=1000.0;
tolx=0.0005;
aulits=10;
for(n=1; n<=6; n++)
{
//--- Generate problem: A, b, BndL, BndU, CMatrix, x0, xm, XStart
k=2*n;
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xm=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,0.1+0.8*CMath::RandomReal());
xm.Set(i,2*CMath::RandomReal()-1);
bndl.Set(i,0.0);
bndu.Set(i,1.0);
xstart.Set(i,CMath::RandomInteger(2));
}
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
c.Set(i,j,2*CMath::RandomReal()-1);
v=CAblasF::RDotVR(n,x0,c,i);
c.Set(i,n,v);
ct.Set(i,0);
}
for(i=0; i<n; i++)
b.Set(i,2*CMath::RandomReal()-1);
//--- Create optimizer, solve
CMinNLC::MinNLCCreate(n,xstart,state);
if(solvertype==0)
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
else
{
if(solvertype==1)
CMinNLC::MinNLCSetAlgoSLP(state);
else
{
if(solvertype==2)
CMinNLC::MinNLCSetAlgoSQP(state);
else
{
CAp::Assert(false);
waserrors=true;
return;
}
}
}
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
CMinNLC::MinNLCSetLC(state,c,ct,k);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
for(j=0; j<n; j++)
{
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:1177");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1178");
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1182");
}
if(waserrors)
return;
for(i=0; i<k; i++)
{
v=CAblasF::RDotVR(n,x1,c,i);
CAp::SetErrorFlag(waserrors,MathAbs(v-c.Get(i,n))>tolx,"testminnlcunit.ap:1189");
}
}
//--- Boundary and linear equality/inequality constrained QP problem with
//--- excessive constraints:
//--- * N*N SPD A with moderate condtion number (up to 100)
//--- * boundary constraints 0<=x[i]<=1
//--- * K=2*N equality/inequality constraints:
//--- * N/2 equality ones q'*x = q'*xm for random vector q
//--- * the rest are inequality ones, feasible at xm (xm is an inner point for these constraints)
//--- where xm is some random vector from the feasible hypercube (0.1<=xm[i]<=0.9)
//--- * optimization problem has form 0.5*x'*A*x-b*x,
//--- where b is some random vector
//--- * because constraints are excessive, the main problem is to find
//--- feasible point; we do not check that algorithm found a solution,
//--- we just check that it found feasible point.
//--- NOTE: this problem is difficult one (estimates of Lagrange multipliers converge
//--- slowly), so we use relaxed tolerances - 0.010 for AUL solver
rho=1000.0;
aulits=30;
for(n=1; n<=6; n++)
{
//--- Generate problem: A, b, BndL, BndU, CMatrix, xm, x1, XStart
k=2*n;
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
xm=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
xm.Set(i,0.1+0.8*CMath::RandomReal());
bndl.Set(i,0.0);
bndu.Set(i,1.0);
x0.Set(i,CMath::RandomInteger(2));
b.Set(i,2*CMath::RandomReal()-1);
}
for(i=0; i<n/2; i++)
{
v=0;
for(j=0; j<n; j++)
{
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
v+=c.Get(i,j)*xm[j];
}
c.Set(i,n,v);
ct.Set(i,0);
}
for(i=n/2; i<k; i++)
{
v=0;
for(j=0; j<n; j++)
{
c.Set(i,j,CHighQualityRand::HQRndNormal(rs)/MathSqrt(n));
v+=c.Get(i,j)*xm[j];
}
c.Set(i,n,v);
ct.Set(i,2*CHighQualityRand::HQRndUniformI(rs,2)-1);
if(ct[i]>0)
c.Add(i,n,-0.1);
else
c.Add(i,n,0.1);
}
//--- Create optimizer, solve
CMinNLC::MinNLCCreate(n,x0,state);
if(solvertype==0)
{
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
tolx=0.0050;
}
else
{
if(solvertype==1)
{
CMinNLC::MinNLCSetAlgoSLP(state);
tolx=0.0010;
}
else
{
if(solvertype==2)
{
CMinNLC::MinNLCSetAlgoSQP(state);
tolx=0.0010;
}
else
{
CAp::Assert(false);
waserrors=true;
return;
}
}
}
CMinNLC::MinNLCSetBC(state,bndl,bndu);
CMinNLC::MinNLCSetLC(state,c,ct,k);
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
for(j=0; j<n; j++)
{
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:1309");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1310");
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1314");
}
if(waserrors)
return;
for(i=0; i<k; i++)
{
v=CAblasF::RDotVR(n,x1,c,i);
if(ct[i]==0)
CAp::SetErrorFlag(waserrors,MathAbs(v-c.Get(i,n))>tolx,"testminnlcunit.ap:1322");
if(ct[i]>0)
CAp::SetErrorFlag(waserrors,v<(c.Get(i,n)-tolx),"testminnlcunit.ap:1324");
if(ct[i]<0)
CAp::SetErrorFlag(waserrors,v>(c.Get(i,n)+tolx),"testminnlcunit.ap:1326");
}
}
//--- Boundary and linear equality constrained QP problem,
//--- test checks that different starting points yield same final point:
//--- * random N from [1..6], random K from [1..N-1]
//--- * N*N SPD A with moderate condtion number (important!)
//--- * boundary constraints 0<=x[i]<=1
//--- * K<N random linear equality constraints C*x = C*x0,
//--- where x0 is some random vector from the inner area of the
//--- feasible hypercube (0.1<=x0[i]<=0.9)
//--- * optimization problem has form 0.5*x'*A*x+b*x,
//--- where b is some random vector with -5<=b[i]<=+5
//--- We solve this problem two times:
//--- * each time from different initial point XStart in [-2,+2]
//--- * we compare values of the target function (although final points
//--- may be slightly different, function values should match each other)
//--- Both points should give same results; any significant difference is
//--- evidence of some error in the QP implementation.
rho=1000.0;
tolf=0.0001;
aulits=10;
for(pass=1; pass<=50; pass++)
{
//--- Generate problem: N, K, A, b, BndL, BndU, CMatrix, x0, xm, XStart.
n=CMath::RandomInteger(5)+2;
k=1+CMath::RandomInteger(n-1);
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xstart=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,0.1+0.8*CMath::RandomReal());
b.Set(i,2*CMath::RandomReal()-1);
bndl.Set(i,0.0);
bndu.Set(i,1.0);
}
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
c.Set(i,j,2*CMath::RandomReal()-1);
c.Add(i,i,4);
v=CAblasF::RDotVR(n,x0,c,i);
c.Set(i,n,v);
ct.Set(i,0);
}
//--- Start from first point
for(i=0; i<n; i++)
xstart.Set(i,4*CMath::RandomReal()-2);
CMinNLC::MinNLCCreate(n,xstart,state);
if(solvertype==0)
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
else
{
if(solvertype==1)
CMinNLC::MinNLCSetAlgoSLP(state);
else
{
if(solvertype==2)
CMinNLC::MinNLCSetAlgoSQP(state);
else
{
CAp::Assert(false);
waserrors=true;
return;
}
}
}
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
CMinNLC::MinNLCSetBC(state,bndl,bndu);
CMinNLC::MinNLCSetLC(state,c,ct,k);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
for(j=0; j<n; j++)
{
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x0,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x0,n),"testminnlcunit.ap:1430");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1431");
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1435");
}
if(waserrors)
return;
//--- Start from another point
for(i=0; i<n; i++)
xstart.Set(i,4*CMath::RandomReal()-2);
CMinNLC::MinNLCRestartFrom(state,xstart);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
for(j=0; j<n; j++)
{
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:1466");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1467");
if(waserrors)
return;
//--- Calculate function value at X0 and X1, compare solutions
f0=0.0;
f1=0.0;
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
f0+=0.5*x0[i]*fulla.Get(i,j)*x0[j];
f1+=0.5*x1[i]*fulla.Get(i,j)*x1[j];
}
f0+=x0[i]*b[i];
f1+=x1[i]*b[i];
}
CAp::SetErrorFlag(waserrors,MathAbs(f0-f1)>(double)(tolf),"testminnlcunit.ap:1486");
}
//--- Convex/nonconvex optimization problem with excessive
//--- (degenerate constraints):
//--- * N=2..7
//--- * f = 0.5*x'*A*x+b'*x
//--- * b has normally distributed entries with scale 10^BScale
//--- * several kinds of A are tried: zero, well conditioned SPD, well conditioned indefinite, low rank
//--- * box constraints: x[i] in [-1,+1]
//---*2^N "excessive" general linear constraints (v_k,x)<=(v_k,v_k)+v_shift,
//--- where v_k is one of 2^N vertices of feasible hypercube, v_shift is
//--- a shift parameter:
//--- * with zero v_shift such constraints are degenerate (each vertex has
//--- N box constraints and one "redundant" linear constraint)
//--- * with positive v_shift linear constraint is always inactive
//--- * with small (about machine epsilon) but negative v_shift,
//--- constraint is close to degenerate - but not exactly
//--- We check that constrained gradient is close to zero at solution.
//--- Box constraint is considered active if distance to boundary is less
//--- than TolConstr.
//--- NOTE: because AUL algorithm is less exact than its active set counterparts,
//--- VERY loose tolerances are used for this test.
tolconstr=1.0E-2;
for(n=2; n<=6; n++)
{
for(akind=0; akind<=3; akind++)
{
//--- Choose random parameters
shiftkind=CHighQualityRand::HQRndUniformI(rs,7)-5;
bscale=CHighQualityRand::HQRndUniformI(rs,3)-2;
//--- Generate A, B and initial point
a=matrix<double>::Zeros(n,n);
b=vector<double>::Zeros(n);
x=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,MathPow(10,bscale)*CHighQualityRand::HQRndNormal(rs));
x.Set(i,CHighQualityRand::HQRndUniformR(rs)-0.5);
}
if(akind==1)
{
//--- Dense well conditioned SPD
CMatGen::SPDMatrixRndCond(n,50.0,a);
}
if(akind==2)
{
//--- Dense well conditioned indefinite
CMatGen::SMatrixRndCond(n,50.0,a);
}
if(akind==3)
{
//--- Low rank
tmp=vector<double>::Zeros(n);
for(i=0; i<n; i++)
a.Set(i,i,1.0E-9);
for(k=1; k<=MathMin(3,n-1); k++)
{
for(i=0; i<n; i++)
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
v=CHighQualityRand::HQRndNormal(rs);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
a.Add(i,j,v*tmp[i]*tmp[j]);
}
}
//--- Generate constraints
bl=vector<double>::Zeros(n);
bu=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
bl.Set(i,-1.0);
bu.Set(i,1.0);
}
ccnt=(int)MathRound(MathPow(2,n));
c=matrix<double>::Zeros(ccnt,n+1);
ct.Resize(ccnt);
for(i=0; i<ccnt; i++)
{
ct.Set(i,-1);
k=i;
c.Set(i,n,MathSign(shiftkind)*MathPow(10,MathAbs(shiftkind))*CMath::m_machineepsilon);
for(j=0; j<n; j++)
{
c.Set(i,j,2*(k%2)-1);
c.Add(i,n,c.Get(i,j)*c.Get(i,j));
k=k/2;
}
}
//--- Create and optimize
CMinNLC::MinNLCCreate(n,x,state);
CMinNLC::MinNLCSetBC(state,bl,bu);
CMinNLC::MinNLCSetLC(state,c,ct,ccnt);
CMinNLC::MinNLCSetCond(state,1.0E-12,0);
if(solvertype==0)
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
else
{
if(solvertype==1)
CMinNLC::MinNLCSetAlgoSLP(state);
else
{
if(solvertype==2)
CMinNLC::MinNLCSetAlgoSQP(state);
else
{
CAp::Assert(false);
waserrors=true;
return;
}
}
}
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
while(CMinNLC::MinNLCIteration(state))
{
CAp::Assert(state.m_needfij);
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,state.m_x[i]*b[i]);
state.m_j.Set(0,i,b[i]);
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,state.m_x,a,i);
state.m_fi.Add(0,0.5*state.m_x[i]*v);
state.m_j.Add(0,i,v);
}
}
CMinNLC::MinNLCResults(state,xs0,rep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1641");
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1645");
}
if(waserrors)
return;
//--- Evaluate gradient at solution and test
vv=0.0;
gnrm2=0;
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xs0,a,i);
v+=b[i];
gnrm2+=v*v;
if(xs0[i]<=(bl[i]+tolconstr) && v>0.0)
v=0.0;
if(xs0[i]>=(bu[i]-tolconstr) && v<0.0)
v=0.0;
vv+=CMath::Sqr(v);
}
vv=MathSqrt(vv/(gnrm2+1.0));
CAp::SetErrorFlag(waserrors,vv>(1.0E-2),"testminnlcunit.ap:1672");
}
}
//--- Linear/convex optimization problem with combination of
//--- box and linear constraints:
//--- * N=2..8
//--- * f = 0.5*x'*A*x+b'*x
//--- * b has normally distributed entries with scale 10^BScale
//--- * several kinds of A are tried: zero, well conditioned SPD
//--- * box constraints: x[i] in [-1,+1]
//--- * initial point x0 = [0 0 ... 0 0]
//--- * CCnt=min(3,N-1) general linear constraints of form (c,x)=0.
//--- random mix of equality/inequality constraints is tried.
//--- x0 is guaranteed to be feasible.
//--- We check that constrained gradient is close to zero at solution.
//--- Inequality constraint is considered active if distance to boundary
//--- is less than TolConstr. We use nonnegative least squares solver
//--- in order to compute constrained gradient.
tolconstr=1.0E-2;
for(n=2; n<=8; n++)
{
for(akind=0; akind<=1; akind++)
{
for(bscale=0; bscale>=-2; bscale--)
{
//--- Generate A, B and initial point
a=matrix<double>::Zeros(n,n);
b=vector<double>::Zeros(n);
x=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,MathPow(10,bscale)*CHighQualityRand::HQRndNormal(rs));
x.Set(i,0.0);
}
if(akind==1)
{
//--- Dense well conditioned SPD
CMatGen::SPDMatrixRndCond(n,50.0,a);
}
if(solvertype!=0)
{
//--- AUL performs poorly on such problems,
//--- but SLP works goo.
if(akind==2)
{
//--- Dense well conditioned indefinite
CMatGen::SMatrixRndCond(n,50.0,a);
}
if(akind==3)
{
//--- Low rank
tmp=vector<double>::Zeros(n);
for(i=0; i<n; i++)
a.Set(i,i,1.0E-9);
for(k=1; k<=MathMin(3,n-1); k++)
{
for(i=0; i<n; i++)
tmp.Set(i,CHighQualityRand::HQRndNormal(rs));
v=CHighQualityRand::HQRndNormal(rs);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
a.Add(i,j,v*tmp[i]*tmp[j]);
}
}
}
//--- Generate constraints
bl=vector<double>::Zeros(n);
bu=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
bl.Set(i,-1.0);
bu.Set(i,1.0);
}
ccnt=MathMin(3,n-1);
c=matrix<double>::Zeros(ccnt,n+1);
ct.Resize(ccnt);
for(i=0; i<ccnt; i++)
{
ct.Set(i,CHighQualityRand::HQRndUniformI(rs,3)-1);
c.Set(i,n,0.0);
for(j=0; j<n; j++)
c.Set(i,j,CHighQualityRand::HQRndUniformR(rs)-0.5);
c.Add(i,i,4);
}
//--- Create and optimize
CMinNLC::MinNLCCreate(n,x,state);
CMinNLC::MinNLCSetBC(state,bl,bu);
CMinNLC::MinNLCSetLC(state,c,ct,ccnt);
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
if(solvertype==0)
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,10);
else
{
if(solvertype==1)
CMinNLC::MinNLCSetAlgoSLP(state);
else
{
if(solvertype==2)
CMinNLC::MinNLCSetAlgoSQP(state);
else
CAp::Assert(false);
}
}
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
while(CMinNLC::MinNLCIteration(state))
{
CAp::Assert(state.m_needfij);
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,state.m_x[i]*b[i]);
state.m_j.Set(0,i,b[i]);
}
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,state.m_x,a,i);
state.m_fi.Add(0,0.5*state.m_x[i]*v);
state.m_j.Add(0,i,v);
}
}
CMinNLC::MinNLCResults(state,xs0,rep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1817");
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:1821");
}
if(waserrors)
return;
//--- 1. evaluate unconstrained gradient at solution
//--- 2. calculate constrained gradient (NNLS solver is used
//--- to evaluate gradient subject to active constraints).
//--- In order to do this we form CE matrix, matrix of active
//--- constraints (columns store constraint vectors). Then
//--- we try to approximate gradient vector by columns of CE,
//--- subject to non-negativity restriction placed on variables
//--- corresponding to inequality constraints.
//--- Residual from such regression is a constrained gradient vector.
g=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,xs0,a,i);
g.Set(i,v+b[i]);
}
ce=matrix<double>::Zeros(n,n+ccnt);
ArrayResize(nonnegative,n+ccnt);
k=0;
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(waserrors,xs0[i]<(bl[i]-tolconstr),"testminnlcunit.ap:1850");
CAp::SetErrorFlag(waserrors,xs0[i]>(bu[i]+tolconstr),"testminnlcunit.ap:1851");
if(xs0[i]<=(bl[i]+tolconstr))
{
for(j=0; j<n; j++)
ce.Set(j,k,0.0);
ce.Set(i,k,1.0);
nonnegative[k]=true;
k++;
continue;
}
if(xs0[i]>=(bu[i]-tolconstr))
{
for(j=0; j<n; j++)
ce.Set(j,k,0.0);
ce.Set(i,k,-1.0);
nonnegative[k]=true;
k++;
continue;
}
}
for(i=0; i<ccnt; i++)
{
v=CAblasF::RDotVR(n,xs0,c,i);
v-=c.Get(i,n);
CAp::SetErrorFlag(waserrors,ct[i]==0 && MathAbs(v)>tolconstr,"testminnlcunit.ap:1875");
CAp::SetErrorFlag(waserrors,ct[i]>0 && v<(-tolconstr),"testminnlcunit.ap:1876");
CAp::SetErrorFlag(waserrors,ct[i]<0 && v>tolconstr,"testminnlcunit.ap:1877");
if(ct[i]==0)
{
for(j=0; j<n; j++)
ce.Set(j,k,c.Get(i,j));
nonnegative[k]=false;
k++;
continue;
}
if((ct[i]>0 && v<=tolconstr) || (ct[i]<0 && v>=(-tolconstr)))
{
for(j=0; j<n; j++)
ce.Set(j,k,MathSign(ct[i])*c.Get(i,j));
nonnegative[k]=true;
k++;
continue;
}
}
CSNNLS::SNNLSInit(0,0,0,nnls);
CSNNLS::SNNLSSetProblem(nnls,ce,g,0,k,n);
for(i=0; i<k; i++)
{
if(!nonnegative[i])
CSNNLS::SNNLSDropNNC(nnls,i);
}
CSNNLS::SNNLSSolve(nnls,tmp);
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
g.Add(j,-tmp[i]*ce.Get(j,i));
}
vv=CAblasF::RDotV2(n,g);
vv=MathSqrt(vv);
CAp::SetErrorFlag(waserrors,vv>(1.0E-3),"testminnlcunit.ap:1906");
}
}
}
}
}
//+------------------------------------------------------------------+
//| This function tests nonlinearly constrained quadratic programming|
//| algorithm. |
//| Sets error flag on failure. |
//+------------------------------------------------------------------+
void CTestMinNLCUnit::TestNLC(bool &waserrors)
{
//--- create variables
int n=0;
int n2=0;
double tolx=0;
double tolf=0;
double tolg=0;
int aulits=0;
double rho=0;
CMinNLCState state;
CMinNLCReport rep;
COptGuardReport ogrep;
int scaletype=0;
CRowDouble x0;
CRowDouble x1;
CRowDouble b;
CRowDouble bndl;
CRowDouble bndu;
CRowDouble s;
CRowDouble g;
CRowDouble rnlc;
CRowDouble xref;
CRowDouble lagmult;
CRowInt ckind;
CMatrixDouble fulla;
CMatrixDouble c;
CRowInt ct;
int cntbc=0;
int cntlc=0;
int cntnlec=0;
int cntnlic=0;
int cntactive=0;
int i=0;
int j=0;
int k=0;
int pass=0;
int klc=0;
int knlec=0;
int knlic=0;
double v=0;
double vv=0;
double vx=0;
double vy=0;
double gnorm2=0;
double rawgnorm2=0;
double fref=0;
double diffstep=0;
CHighQualityRandState rs;
int prectype=0;
int solvertype=0;
CHighQualityRand::HQRndRandomize(rs);
for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++)
{
//--- Basic test:
//--- * 2-dimensional problem
//--- * target function F(x0,x1) = (x0-1)^2 + (x1-1)^2
//--- * one nonlinear constraint Z(x0,x1) = x0^2+x1^2-1,
//--- which is tried as equality and inequality one
rho=200.0;
tolx=0.0005;
aulits=50;
n=2;
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
x0.Set(i,2*CMath::RandomReal()-1);
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
CMinNLC::MinNLCSetNLC(state,0,1);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,CMath::Sqr(state.m_x[0]-1)+CMath::Sqr(state.m_x[1]-1));
state.m_j.Set(0,0,2*(state.m_x[0]-1));
state.m_j.Set(0,1,2*(state.m_x[1]-1));
state.m_fi.Set(1,CMath::Sqr(state.m_x[0])+CMath::Sqr(state.m_x[1])-1);
state.m_j.Set(1,0,2*state.m_x[0]);
state.m_j.Set(1,1,2*state.m_x[1]);
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:1996");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:1997");
if(waserrors)
return;
//---
CAp::SetErrorFlag(waserrors,(MathAbs(x1[0]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:2000");
CAp::SetErrorFlag(waserrors,(MathAbs(x1[1]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:2001");
CMinNLC::MinNLCSetNLC(state,1,0);
CMinNLC::MinNLCRestartFrom(state,x0);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,CMath::Sqr(state.m_x[0]-1)+CMath::Sqr(state.m_x[1]-1));
state.m_j.Set(0,0,2*(state.m_x[0]-1));
state.m_j.Set(0,1,2*(state.m_x[1]-1));
state.m_fi.Set(1,CMath::Sqr(state.m_x[0])+CMath::Sqr(state.m_x[1])-1);
state.m_j.Set(1,0,2*state.m_x[0]);
state.m_j.Set(1,1,2*state.m_x[1]);
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:2021");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:2022");
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:2026");
}
if(waserrors)
return;
CAp::SetErrorFlag(waserrors,(MathAbs(x1[0]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:2030");
CAp::SetErrorFlag(waserrors,(MathAbs(x1[1]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:2031");
//--- This test checks correctness of scaling being applied to nonlinear
//--- constraints. We solve bound constrained scaled problem and check
//--- that solution is correct.
aulits=50;
rho=200.0;
tolx=0.0005;
tolg=0.01;
for(n=1; n<=10; n++)
{
for(pass=1; pass<=10; pass++)
{
//--- Generate well-conditioned problem with unit scale
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
}
cntnlec=CHighQualityRand::HQRndUniformI(rs,n);
cntnlic=n-cntnlec;
for(i=0; i<cntnlec ; i++)
{
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
bndu.Set(i,bndl[i]);
}
for(i=cntnlec; i<n; i++)
{
bndl.Set(i,CHighQualityRand::HQRndNormal(rs));
bndu.Set(i,bndl[i]+0.5);
}
//--- Apply scaling to quadratic/linear term, so problem becomes
//--- well-conditioned in the scaled coordinates.
scaletype=CHighQualityRand::HQRndUniformI(rs,2);
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
if(scaletype==0)
s.Set(i,1);
else
s.Set(i,MathExp(5*CHighQualityRand::HQRndNormal(rs)));
}
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
fulla.Mul(i,j,1.0/(s[i]*s[j]));
}
x0*=s;
bndl*=s;
bndu*=s;
b/=s;
//--- Solve problem with boundary constraints posed as nonlinear ones
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetScale(state,s);
CMinNLC::MinNLCSetNLC(state,cntnlec,2*cntnlic);
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
for(i=0; i<=cntnlec+2*cntnlic; i++)
{
state.m_fi.Set(i,0);
for(j=0; j<n; j++)
state.m_j.Set(i,j,0);
}
//--- Function itself
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
for(j=0; j<n; j++)
{
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
}
}
//--- Equality constraints
for(i=0; i<cntnlec ; i++)
{
state.m_fi.Set(1+i,(state.m_x[i]-bndl[i])/s[i]);
state.m_j.Set(1+i,i,1/s[i]);
}
//--- Inequality constraints
for(i=0; i<cntnlic; i++)
{
k=cntnlec+i;
state.m_fi.Set(1+cntnlec+2*i+0,(bndl[k]-state.m_x[k])/s[k]);
state.m_j.Set(1+cntnlec+2*i+0,k,-(1/s[k]));
state.m_fi.Set(1+cntnlec+2*i+1,(state.m_x[k]-bndu[k])/s[k]);
state.m_j.Set(1+cntnlec+2*i+1,k,1/s[k]);
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:2165");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:2166");
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:2170");
}
if(waserrors)
return;
//--- Check feasibility properties
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(waserrors,MathIsValidNumber(bndl[i]) && x1[i]<=(double)(bndl[i]-tolx*s[i]),"testminnlcunit.ap:2180");
CAp::SetErrorFlag(waserrors,MathIsValidNumber(bndu[i]) && x1[i]>=(double)(bndu[i]+tolx*s[i]),"testminnlcunit.ap:2181");
}
//--- Test - calculate scaled constrained gradient at solution,
//--- check its norm.
g=vector<double>::Zeros(n);
gnorm2=0.0;
for(i=0; i<n; i++)
{
g.Set(i,b[i]+CAblasF::RDotVR(n,x1,fulla,i));
g.Set(i,s[i]*g[i]);
if((MathIsValidNumber(bndl[i]) && MathAbs(x1[i]-bndl[i])<(double)(tolx*s[i])) && (double)(g[i])>0.0)
g.Set(i,0);
if((MathIsValidNumber(bndu[i]) && MathAbs(x1[i]-bndu[i])<(double)(tolx*s[i])) && (double)(g[i])<0.0)
g.Set(i,0);
gnorm2+=CMath::Sqr(g[i]);
}
CAp::SetErrorFlag(waserrors,gnorm2>(double)(CMath::Sqr(tolg)),"testminnlcunit.ap:2203");
}
}
//--- Complex problem with mix of boundary, linear and nonlinear constraints:
//--- * quadratic target function f(x) = 0.5*x'*A*x + b'*x
//--- * unit scaling is used
//--- * problem size N is even
//--- * all variables are divided into pairs: x[0] and x[1], x[2] and x[3], ...
//--- * constraints are set for pairs of variables, i.e. each constraint involves
//--- only pair of adjacent variables (x0/x1, x2/x3, x4/x5 and so on), and each
//--- pair of variables has at most one constraint which binds them
//--- * for variables u and v following kinds of constraints can be randomly set:
//--- * CKind=0 no constraint
//--- * CKind=1 boundary equality constraint: u=a, v=b
//--- * CKind=2 boundary inequality constraint: a0<=u<=b0, a1<=v<=b1
//--- * CKind=3 linear equality constraint: a*u+b*v = c
//--- * CKind=4 linear inequality constraint: a*u+b*v <= c
//--- * CKind=5 nonlinear equality constraint: u^2+v^2 = 1
//--- * CKind=6 nonlinear inequality constraint: u^2+v^2 <= 1
//--- * it is relatively easy to calculated projected gradient for such problem
aulits=50;
rho=200.0;
tolx=0.0005;
tolg=0.005;
n=20;
n2=n/2;
for(pass=1; pass<=5; pass++)
{
for(prectype=1; prectype<=2; prectype++)
{
//--- Generate well-conditioned problem with unit scale
CMatGen::SPDMatrixRndCond(n,1.0E2,fulla);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
c=matrix<double>::Zeros(n,n+1);
ct.Resize(n);
x0=vector<double>::Zeros(n);
ckind.Resize(n2);
rnlc=vector<double>::Zeros(n2);
cntbc=0;
cntlc=0;
cntnlec=0;
cntnlic=0;
for(i=0; i<n; i++)
{
bndl.Set(i,AL_NEGINF);
bndu.Set(i,AL_POSINF);
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
b.Set(i,10*CHighQualityRand::HQRndNormal(rs));
}
for(i=0; i<n2 ; i++)
{
ckind.Set(i,CHighQualityRand::HQRndUniformI(rs,7));
rnlc.Set(i,0);
if(ckind[i]==0)
{
//--- Unconstrained
continue;
}
if(ckind[i]==1)
{
//--- Bound equality constrained
bndl.Set(2*i,CHighQualityRand::HQRndUniformR(rs)-0.5);
bndu.Set(2*i,bndl[2*i+0]);
bndl.Set(2*i+1,CHighQualityRand::HQRndUniformR(rs)-0.5);
bndu.Set(2*i+1,bndl[2*i+1]);
cntbc++;
continue;
}
if(ckind[i]==2)
{
//--- Bound inequality constrained
bndl.Set(2*i,CHighQualityRand::HQRndUniformR(rs)-0.5);
bndu.Set(2*i,bndl[2*i+0]+0.5);
bndl.Set(2*i+1,CHighQualityRand::HQRndUniformR(rs)-0.5);
bndu.Set(2*i+1,bndl[2*i+1]+0.5);
cntbc++;
continue;
}
if(ckind[i]==3)
{
//--- Linear equality constrained
for(j=0; j<=n; j++)
c.Set(cntlc,j,0.0);
vx=CHighQualityRand::HQRndUniformR(rs)-0.5;
vy=CHighQualityRand::HQRndUniformR(rs)-0.5;
c.Set(cntlc,2*i+0,vx);
c.Set(cntlc,2*i+1,vy);
c.Set(cntlc,n,CHighQualityRand::HQRndUniformR(rs)-0.5);
ct.Set(cntlc,0);
cntlc++;
continue;
}
if(ckind[i]==4)
{
//--- Linear inequality constrained
for(j=0; j<=n; j++)
c.Set(cntlc,j,0.0);
vx=CHighQualityRand::HQRndUniformR(rs)-0.5;
vy=CHighQualityRand::HQRndUniformR(rs)-0.5;
c.Set(cntlc,2*i,vx);
c.Set(cntlc,2*i+1,vy);
c.Set(cntlc,n,CHighQualityRand::HQRndUniformR(rs)-0.5);
ct.Set(cntlc,-1);
cntlc++;
continue;
}
if(ckind[i]==5)
{
//--- Nonlinear equality constrained
rnlc.Set(i,0.5+CHighQualityRand::HQRndUniformR(rs));
cntnlec++;
continue;
}
if(ckind[i]==6)
{
//--- Nonlinear inequality constrained
rnlc.Set(i,0.5+CHighQualityRand::HQRndUniformR(rs));
cntnlic++;
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
//--- Solve problem
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
if(prectype==0)
CMinNLC::MinNLCSetPrecInexact(state);
if(prectype==1)
CMinNLC::MinNLCSetPrecExactLowRank(state,0);
if(prectype==2)
CMinNLC::MinNLCSetPrecExactRobust(state,0);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
CMinNLC::MinNLCSetCond(state,1.0E-6,0);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
CMinNLC::MinNLCSetCond(state,1.0E-6,0);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetBC(state,bndl,bndu);
CMinNLC::MinNLCSetLC(state,c,ct,cntlc);
CMinNLC::MinNLCSetNLC(state,cntnlec,cntnlic);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinNLC::MinNLCOptGuardSmoothness(state,CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel+1));
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
//--- Evaluate target function
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
for(j=0; j<n; j++)
{
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
}
}
//--- Evaluate constraint functions
knlec=1;
knlic=1+cntnlec;
for(i=0; i<n2 ; i++)
{
if(ckind[i]==5)
{
state.m_fi.Set(knlec,0);
for(j=0; j<n; j++)
state.m_j.Set(knlec,j,0.0);
state.m_fi.Set(knlec,CMath::Sqr(state.m_x[2*i+0])+CMath::Sqr(state.m_x[2*i+1])-rnlc[i]);
state.m_j.Set(knlec,2*i+0,2*state.m_x[2*i+0]);
state.m_j.Set(knlec,2*i+1,2*state.m_x[2*i+1]);
knlec++;
continue;
}
if(ckind[i]==6)
{
state.m_fi.Set(knlic,0);
for(j=0; j<n; j++)
state.m_j.Set(knlic,j,0.0);
state.m_fi.Set(knlic,CMath::Sqr(state.m_x[2*i+0])+CMath::Sqr(state.m_x[2*i+1])-rnlc[i]);
state.m_j.Set(knlic,2*i+0,2*state.m_x[2*i+0]);
state.m_j.Set(knlic,2*i+1,2*state.m_x[2*i+1]);
knlic++;
continue;
}
}
if(!CAp::Assert(knlec==1+cntnlec) ||
!CAp::Assert(knlic==1+cntnlec+cntnlic))
{
waserrors=true;
return;
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:2434");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:2435");
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:2439");
}
if(waserrors)
return;
//--- Check feasibility properties
klc=0;
for(i=0; i<n2 ; i++)
{
if(ckind[i]==0)
{
//--- Unconstrained
continue;
}
if(ckind[i]==1)
{
//--- Bound equality constrained
CAp::SetErrorFlag(waserrors,MathAbs(x1[2*i+0]-bndl[2*i+0])>tolx,"testminnlcunit.ap:2462");
CAp::SetErrorFlag(waserrors,MathAbs(x1[2*i+1]-bndl[2*i+1])>tolx,"testminnlcunit.ap:2463");
continue;
}
if(ckind[i]==2)
{
//--- Bound inequality constrained
CAp::SetErrorFlag(waserrors,x1[2*i ]<(bndl[2*i+0]-tolx),"testminnlcunit.ap:2471");
CAp::SetErrorFlag(waserrors,x1[2*i ]>(bndu[2*i+0]+tolx),"testminnlcunit.ap:2472");
CAp::SetErrorFlag(waserrors,x1[2*i+1]<(bndl[2*i+1]-tolx),"testminnlcunit.ap:2473");
CAp::SetErrorFlag(waserrors,x1[2*i+1]>(bndu[2*i+1]+tolx),"testminnlcunit.ap:2474");
continue;
}
if(ckind[i]==3)
{
//--- Linear equality constrained
v=x1[2*i+0]*c[klc][ 2*i]+x1[2*i+1]*c[klc][ 2*i+1]-c[klc][n];
CAp::SetErrorFlag(waserrors,MathAbs(v)>tolx,"testminnlcunit.ap:2483");
klc++;
continue;
}
if(ckind[i]==4)
{
//--- Linear inequality constrained
v=x1[2*i+0]*c[klc][ 2*i]+x1[2*i+1]*c[klc][ 2*i+1]-c[klc][n];
CAp::SetErrorFlag(waserrors,v>tolx,"testminnlcunit.ap:2493");
klc++;
continue;
}
if(ckind[i]==5)
{
//--- Nonlinear equality constrained
v=CMath::Sqr(x1[2*i+0])+CMath::Sqr(x1[2*i+1])-rnlc[i];
CAp::SetErrorFlag(waserrors,MathAbs(v)>tolx,"testminnlcunit.ap:2503");
continue;
}
if(ckind[i]==6)
{
//--- Nonlinear inequality constrained
v=CMath::Sqr(x1[2*i+0])+CMath::Sqr(x1[2*i+1])-rnlc[i];
CAp::SetErrorFlag(waserrors,v>tolx,"testminnlcunit.ap:2512");
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
//--- Test - calculate scaled constrained gradient at solution,
//--- check its norm.
gnorm2=0.0;
rawgnorm2=0;
g=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
v=b[i]+CAblasF::RDotVR(n,x1,fulla,i);
g.Set(i,v);
rawgnorm2+=v*v;
}
klc=0;
for(i=0; i<n2 ; i++)
{
if(ckind[i]==0)
{
//--- Unconstrained
gnorm2+=CMath::Sqr(g[2*i+0])+CMath::Sqr(g[2*i+1]);
CAp::SetErrorFlag(waserrors,gnorm2>(double)(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2542");
continue;
}
if(ckind[i]==1)
{
//--- Bound equality constrained, unconditionally set gradient to zero
g.Set(2*i+0,0.0);
g.Set(2*i+1,0.0);
gnorm2+=CMath::Sqr(g[2*i+0])+CMath::Sqr(g[2*i+1]);
CAp::SetErrorFlag(waserrors,gnorm2>(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2553");
continue;
}
if(ckind[i]==2)
{
//--- Bound inequality constrained, conditionally set gradient to zero
//--- (when constraint is active)
if(x1[2*i ]<(bndl[2*i]+tolx) || x1[2*i ]>(bndu[2*i]-tolx))
g.Set(2*i,0.0);
if(x1[2*i+1]<(bndl[2*i+1]+tolx) || x1[2*i+1]>(bndu[2*i+1]-tolx))
g.Set(2*i+1,0.0);
gnorm2+=CMath::Sqr(g[2*i+0])+CMath::Sqr(g[2*i+1]);
CAp::SetErrorFlag(waserrors,gnorm2>(double)(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2567");
continue;
}
if(ckind[i]==3)
{
//--- Linear equality constrained, unconditionally project gradient into
//--- equality constrained subspace
v=g[2*i+0]*c[klc][ 2*i]+g[2*i+1]*c[klc][ 2*i+1];
vv=CMath::Sqr(c[klc][ 2*i])+CMath::Sqr(c[klc][ 2*i+1]);
g.Add(2*i,c[klc][ 2*i]*(-v/vv));
g.Add(2*i+1,c[klc][ 2*i+1]*(-v/vv));
klc++;
gnorm2+=CMath::Sqr(g[2*i])+CMath::Sqr(g[2*i+1]);
CAp::SetErrorFlag(waserrors,gnorm2>(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2582");
continue;
}
if(ckind[i]==4)
{
//--- Linear inequality constrained, conditionally project gradient
//--- (when constraint is active)
v=x1[2*i]*c[klc][ 2*i]+x1[2*i+1]*c[klc][ 2*i+1]-c[klc][ n];
if(v>(-tolx))
{
v=g[2*i+0]*c[klc][ 2*i]+g[2*i+1]*c[klc][ 2*i+1];
vv=CMath::Sqr(c[klc][ 2*i])+CMath::Sqr(c[klc][ 2*i+1]);
g.Add(2*i,c[klc][ 2*i]*(-v/vv));
g.Add(2*i+1,c[klc][ 2*i+1]*(-v/vv));
}
klc++;
gnorm2+=CMath::Sqr(g[2*i])+CMath::Sqr(g[2*i+1]);
CAp::SetErrorFlag(waserrors,gnorm2>(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2601");
continue;
}
if(ckind[i]==5)
{
//--- Nonlinear equality constrained, unconditionally project gradient
//--- NOTE: here we rely on the fact that corresponding components of X
//--- sum to one.
v=CApServ::Coalesce(MathSqrt(CMath::Sqr(x1[2*i+0])+CMath::Sqr(x1[2*i+1])),1.0);
vx=x1[2*i]/v;
vy=x1[2*i+1]/v;
v=g[2*i]*vx+g[2*i+1]*vy;
g.Add(2*i,- vx*v);
g.Add(2*i+1,- vy*v);
gnorm2+=CMath::Sqr(g[2*i+0])+CMath::Sqr(g[2*i+1]);
CAp::SetErrorFlag(waserrors,gnorm2>(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2619");
continue;
}
if(ckind[i]==6)
{
//--- Nonlinear inequality constrained, conditionally project gradient
//--- (when constraint is active)
//--- NOTE: here we rely on the fact that corresponding components of X
//--- sum to one.
v=CMath::Sqr(x1[2*i+0])+CMath::Sqr(x1[2*i+1])-rnlc[i];
if(v>(double)(-tolx))
{
v=CApServ::Coalesce(MathSqrt(CMath::Sqr(x1[2*i+0])+CMath::Sqr(x1[2*i+1])),1.0);
vx=x1[2*i ]/v;
vy=x1[2*i+1]/v;
v=g[2*i]*vx+g[2*i+1]*vy;
g.Add(2*i,- vx*v);
g.Add(2*i+1,- vy*v);
}
gnorm2+=CMath::Sqr(g[2*i])+CMath::Sqr(g[2*i+1]);
CAp::SetErrorFlag(waserrors,gnorm2>(CMath::Sqr(tolg)*MathMax(rawgnorm2,1.0)),"testminnlcunit.ap:2642");
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
}
}
}
//--- Test a sequence of inequality constrained problems with varying N/NLIC and
//--- known answer. The problem formulation is prepared as follows:
//--- * first, we generate a solution point and active constraints with Lagrange
//--- multipliers of our choice; at least one constraint is active at the solution.
//--- * then we select the target function to match our constraints
//--- * finally, we add inactive constraints
rho=1000.0;
tolf=0.002;
aulits=10;
diffstep=0.0001;
for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++)
{
for(n=1; n<=10; n++)
{
for(cntnlic=1; cntnlic<=10; cntnlic++)
{
//--- Create constraint matrix C that stores coefficients of quadratic
//--- constraints: first N columns store linear terms, next N columns
//--- store diagonal of the quadratic term, next column stores constant term.
CHighQualityRand::HQRndNormalV(rs,n,xref);
cntactive=1+CHighQualityRand::HQRndUniformI(rs,n);
c=matrix<double>::Zeros(cntnlic,2*n+1);
lagmult=vector<double>::Zeros(cntnlic);
CAblasF::RSetAllocV(n,0.0,g);
for(i=0; i<cntnlic; i++)
{
//--- Generate linear and quadratic terms for a constraint, adjust its
//--- constant term depending on whether it is active or not
v=0;
for(j=0; j<n; j++)
{
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
c.Set(i,n+j,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
v+=c.Get(i,j)*xref[j]+c.Get(i,n+j)*CMath::Sqr(xref[j]);
}
c.Set(i,2*n,-v);
if(i<cntactive)
{
//--- Constraint is active, accumulate its gradient in G
lagmult.Set(i,0.05+MathPow(2,CHighQualityRand::HQRndNormal(rs)));
for(j=0; j<n; j++)
g.Add(j,lagmult[i]*(c.Get(i,j)+2*c.Get(i,n+j)*xref[j]));
}
else
{
//--- Constraint is inactive, adjust its constant term
lagmult.Set(i,0.0);
c.Add(i,2*n,-0.1-MathPow(2,CHighQualityRand::HQRndNormal(rs)));
}
}
for(i=0; i<cntnlic; i++)
{
j=i+CHighQualityRand::HQRndUniformI(rs,cntnlic-i);
if(j!=i)
{
CApServ::SwapRows(c,i,j,2*n+1);
CApServ::SwapElements(lagmult,i,j);
}
}
//--- Generate target function F that has gradient at XRef[] exactly equal to -G
CAblasF::RAllocV(n,b);
CAblasF::RCopyMulV(n,-1.0,g,b);
fref=CAblasF::RDotV(n,b,xref);
//--- Solve the problem
CHighQualityRand::HQRndNormalV(rs,n,x0);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CMinNLC::MinNLCCreate(n,x0,state);
else
CMinNLC::MinNLCCreateF(n,x0,diffstep,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
prectype=CHighQualityRand::HQRndUniformI(rs,3);
if(prectype==0)
CMinNLC::MinNLCSetPrecInexact(state);
if(prectype==1)
CMinNLC::MinNLCSetPrecExactLowRank(state,0);
if(prectype==2)
CMinNLC::MinNLCSetPrecExactRobust(state,0);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
CMinNLC::MinNLCSetCond(state,1.0E-6,0);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
CMinNLC::MinNLCSetCond(state,1.0E-6,0);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetNLC(state,0,cntnlic);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfi)
{
//--- Evaluate target function
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
state.m_fi.Add(0,b[i]*state.m_x[i]);
//--- Evaluate constraint functions
k=1;
for(i=0; i<cntnlic; i++)
{
state.m_fi.Set(k,c.Get(i,2*n));
for(j=0; j<n; j++)
state.m_fi.Add(k,c.Get(i,j)*state.m_x[j]+c.Get(i,n+j)*CMath::Sqr(state.m_x[j]));
k++;
}
if(!CAp::Assert(k==1+cntnlic))
{
waserrors=true;
return;
}
continue;
}
if(state.m_needfij)
{
//--- Evaluate target function
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
}
//--- Evaluate constraint functions
k=1;
for(i=0; i<cntnlic; i++)
{
state.m_fi.Set(k,c.Get(i,2*n));
for(j=0; j<n; j++)
{
state.m_fi.Add(k,c.Get(i,j)*state.m_x[j]+c.Get(i,n+j)*CMath::Sqr(state.m_x[j]));
state.m_j.Set(k,j,c.Get(i,j)+2*c.Get(i,n+j)*state.m_x[j]);
}
k++;
}
if(!CAp::Assert(k==1+cntnlic))
{
waserrors=true;
return;
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:2818");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:2819");
if(waserrors)
return;
CAp::SetErrorFlag(waserrors,MathAbs(fref-CAblasF::RDotV(n,x1,b))>(tolf*(CAblasF::RMaxAbsV(n,b)+1)),"testminnlcunit.ap:2824");
}
}
}
}
//+------------------------------------------------------------------+
//| This function performs additional tests |
//| On failure sets error flag. |
//+------------------------------------------------------------------+
void CTestMinNLCUnit::TestOther(bool &waserrors)
{
//--- create variables
CHighQualityRandState rs;
double v=0;
double h=0;
double fl=0;
double fr=0;
double fl2=0;
double fr2=0;
double dfl=0;
double dfr=0;
double dfl2=0;
double dfr2=0;
double d2fl=0;
double d2fr=0;
double d2fl2=0;
double d2fr2=0;
double f0=0;
double df=0;
double d2f=0;
double ndf=0;
double nd2f=0;
double dtol=0;
double diffstep=0;
CMinNLCState state;
CMinNLCReport rep;
COptGuardReport ogrep;
double rho=0;
int aulits=0;
double tolx=0;
int i=0;
int j=0;
int k=0;
int n=0;
CRowDouble s;
CRowDouble b;
CRowDouble x0;
CRowDouble x1;
CRowDouble x2;
CRowDouble x3;
CRowDouble xlast;
CRowDouble bndl;
CRowDouble bndu;
CRowDouble xu;
double condv=0;
CMatrixDouble a;
CMatrixDouble c;
CMatrixDouble fulla;
CRowInt ct;
int nlbfgs=0;
int nexactlowrank=0;
int nexactrobust=0;
int nnone=0;
int ctype=0;
int trialidx=0;
int blocksize=0;
int blockcnt=0;
int maxits=0;
int spoiliteration=0;
int stopiteration=0;
int spoilvar=0;
double spoilval=0;
int pass=0;
int solvertype=0;
int badidx0=0;
int badidx1=0;
int nlec=0;
int nlic=0;
double ss=0;
int stopcallidx=0;
int callidx=0;
bool terminationrequested;
bool firstrep;
int i_=0;
CHighQualityRand::HQRndRandomize(rs);
//--- Test equality penalty function (correctly calculated and smooth)
h=1.0E-4;
v=-0.98;
dtol=1.0E-3;
while(v<=0.98)
{
//--- Test numerical derivative; this test also checks continuity of the
//--- function
CMinNLC::MinNLCEqualityPenaltyFunction(v-2*h,fl2,dfl2,d2fl2);
CMinNLC::MinNLCEqualityPenaltyFunction(v-h,fl,dfl,d2fl);
CMinNLC::MinNLCEqualityPenaltyFunction(v+h,fr,dfr,d2fr);
CMinNLC::MinNLCEqualityPenaltyFunction(v+2*h,fr2,dfr2,d2fr2);
CMinNLC::MinNLCEqualityPenaltyFunction(v,f0,df,d2f);
ndf=(-fr2+8*fr-8*fl+fl2)/(12*h);
CAp::SetErrorFlag(waserrors,MathAbs(ndf-df)>(dtol*MathMax(MathAbs(ndf),1)),"testminnlcunit.ap:2903");
nd2f=(-dfr2+8*dfr-8*dfl+dfl2)/(12*h);
CAp::SetErrorFlag(waserrors,(MathAbs(nd2f-d2f))>(dtol*MathMax(MathAbs(nd2f),1)),"testminnlcunit.ap:2905");
//--- Next point
v+=h;
}
CMinNLC::MinNLCEqualityPenaltyFunction(0.0,f0,df,d2f);
CAp::SetErrorFlag(waserrors,f0!=0.0,"testminnlcunit.ap:2913");
CAp::SetErrorFlag(waserrors,(double)(df)!=0.0,"testminnlcunit.ap:2914");
//--- Test inequality penalty function (correctly calculated and smooth)
h=1.0E-4;
v=0.02;
dtol=1.0E-3;
while(v<=2.0)
{
//--- Test numerical derivative; this test also checks continuity of the
//--- function
CMinNLC::MinNLCInequalityShiftFunction(v-2*h,fl2,dfl2,d2fl2);
CMinNLC::MinNLCInequalityShiftFunction(v-h,fl,dfl,d2fl);
CMinNLC::MinNLCInequalityShiftFunction(v+h,fr,dfr,d2fr);
CMinNLC::MinNLCInequalityShiftFunction(v+2*h,fr2,dfr2,d2fr2);
CMinNLC::MinNLCInequalityShiftFunction(v,f0,df,d2f);
ndf=(-fr2+8*fr-8*fl+fl2)/(12*h);
CAp::SetErrorFlag(waserrors,MathAbs(ndf-df)>(dtol*MathMax(MathAbs(ndf),1)),"testminnlcunit.ap:2938");
nd2f=(-dfr2+8*dfr-8*dfl+dfl2)/(12*h);
CAp::SetErrorFlag(waserrors,MathAbs(nd2f-d2f)>(dtol*MathMax(MathAbs(nd2f),1)),"testminnlcunit.ap:2940");
//--- Next point
v+=h;
}
CMinNLC::MinNLCInequalityShiftFunction(1.0,f0,df,d2f);
CAp::SetErrorFlag(waserrors,MathAbs(f0)>(1.0E-6),"testminnlcunit.ap:2948");
CAp::SetErrorFlag(waserrors,MathAbs(df+1)>1.0E-6,"testminnlcunit.ap:2949");
//--- Test different properties shared by all solvers
for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++)
{
//--- Test location reports
aulits=50;
rho=200.0;
n=2;
x0=vector<double>::Zeros(n);
xlast=vector<double>::Zeros(n);
x0.Set(0,0.1+0.1*CHighQualityRand::HQRndUniformR(rs));
x0.Set(1,0.2+0.1*CHighQualityRand::HQRndUniformR(rs));
xlast.Set(0,0);
xlast.Set(1,0);
firstrep=true;
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
break;
}
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
CMinNLC::MinNLCSetNLC(state,0,1);
CMinNLC::MinNLCSetXRep(state,true);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,CMath::Sqr(state.m_x[0]-1)+CMath::Sqr(state.m_x[1]-1));
state.m_j.Set(0,0,2*(state.m_x[0]-1));
state.m_j.Set(0,1,2*(state.m_x[1]-1));
state.m_fi.Set(1,CMath::Sqr(state.m_x[0])+CMath::Sqr(state.m_x[1])-1);
state.m_j.Set(1,0,2*state.m_x[0]);
state.m_j.Set(1,1,2*state.m_x[1]);
continue;
}
if(state.m_xupdated)
{
//--- If first point reported, compare with initial one
if(firstrep)
{
CAp::SetErrorFlag(waserrors,MathAbs(state.m_x[0]-x0[0])>(1.0E4*CMath::m_machineepsilon),"testminnlcunit.ap:3002");
CAp::SetErrorFlag(waserrors,MathAbs(state.m_x[1]-x0[1])>(1.0E4*CMath::m_machineepsilon),"testminnlcunit.ap:3003");
}
firstrep=false;
//--- Save last point
xlast.Set(0,state.m_x[0]);
xlast.Set(1,state.m_x[1]);
//--- Done
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3021");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3022");
if(waserrors)
return;
CAp::SetErrorFlag(waserrors,MathAbs(x1[0]-xlast[0])>(1.0E4*CMath::m_machineepsilon),"testminnlcunit.ap:3025");
CAp::SetErrorFlag(waserrors,MathAbs(x1[1]-xlast[1])>(1.0E4*CMath::m_machineepsilon),"testminnlcunit.ap:3026");
//--- Test numerical differentiation
aulits=50;
rho=200.0;
tolx=0.001;
diffstep=0.0001;
n=2;
x0=vector<double>::Zeros(n);
x0.Set(0,0.1);
x0.Set(1,0.2);
CMinNLC::MinNLCCreateF(n,x0,diffstep,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
CMinNLC::MinNLCSetNLC(state,0,1);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfi)
{
state.m_fi.Set(0,CMath::Sqr(state.m_x[0]-1)+CMath::Sqr(state.m_x[1]-1));
state.m_fi.Set(1,CMath::Sqr(state.m_x[0])+CMath::Sqr(state.m_x[1])-1);
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3062");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3063");
if(waserrors)
return;
CAp::SetErrorFlag(waserrors,(double)(MathAbs(x1[0]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:3066");
CAp::SetErrorFlag(waserrors,(double)(MathAbs(x1[1]-MathSqrt(2)/2))>tolx,"testminnlcunit.ap:3067");
//--- Test integrity checks for NAN/INF:
//--- * algorithm solves optimization problem, which is normal for some time (quadratic)
//--- * after 5-th step we choose random component of gradient and consistently spoil
//--- it by NAN or INF.
//--- * we check that correct termination code is returned (-8)
n=100;
for(pass=1; pass<=10; pass++)
{
spoiliteration=5;
stopiteration=8;
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
//--- Gradient can be spoiled by +INF, -INF, NAN
spoilvar=CHighQualityRand::HQRndUniformI(rs,n);
i=CHighQualityRand::HQRndUniformI(rs,3);
spoilval=AL_NaN;
if(i==0)
spoilval=AL_NEGINF;
if(i==1)
spoilval=AL_POSINF;
}
else
{
//--- Function value can be spoiled only by NAN
//--- (+INF can be recognized as legitimate value during optimization)
spoilvar=-1;
spoilval=AL_NaN;
}
CMatGen::SPDMatrixRndCond(n,1.0E5,fulla);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
}
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
aulits=5;
rho=1.0E3;
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetCond(state,0.0,stopiteration);
CMinNLC::MinNLCSetXRep(state,true);
k=-1;
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
for(j=0; j<n; j++)
{
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
}
}
if(k>=spoiliteration)
{
if(spoilvar<0)
state.m_fi.Set(0,spoilval);
else
state.m_j.Set(0,spoilvar,spoilval);
}
continue;
}
if(state.m_xupdated)
{
k++;
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype!=-8,"testminnlcunit.ap:3160");
}
//--- Test that optimizer respects box constraints in all
//--- intermediate points:
//--- * test that analytic Jacobian respects them
//--- * test that numerical Jacobian respects them
//--- NOTE: we skip SolverType=0 (AUL) because AUL optimizer
//--- does not provide such guarantee
if(solvertype!=0)
{
n=10;
CMatGen::SPDMatrixRndCond(n,1.0E3,fulla);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
bndl.Set(i,0);
bndu.Set(i,AL_POSINF);
}
else
{
bndl.Set(i,AL_NEGINF);
bndu.Set(i,0);
}
}
//--- Check analytic Jacobian
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetBC(state,bndl,bndu);
CMinNLC::MinNLCSetCond(state,1.0E-8,0);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(waserrors,state.m_x[i]<bndl[i],"testminnlcunit.ap:3215");
CAp::SetErrorFlag(waserrors,state.m_x[i]>bndu[i],"testminnlcunit.ap:3216");
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
for(j=0; j<n; j++)
{
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
state.m_j.Add(0,i,fulla.Get(i,j)*state.m_x[j]);
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3230");
//--- Check numerical Jacobian
CMinNLC::MinNLCCreateF(n,x0,1.0E-4,state);
switch(solvertype)
{
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetBC(state,bndl,bndu);
CMinNLC::MinNLCSetCond(state,1.0E-8,0);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfi)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(waserrors,state.m_x[i]<bndl[i],"testminnlcunit.ap:3251");
CAp::SetErrorFlag(waserrors,state.m_x[i]>bndu[i],"testminnlcunit.ap:3252");
state.m_fi.Add(0,b[i]*state.m_x[i]);
for(j=0; j<n; j++)
state.m_fi.Add(0,0.5*state.m_x[i]*fulla.Get(i,j)*state.m_x[j]);
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3262");
}
//--- Test constraint violation reports for completely unconstrained
//--- problems and problems with all constraints being satisfied exactly
tolx=0.01;
for(pass=1; pass<=10; pass++)
{
n=1+CHighQualityRand::HQRndUniformI(rs,5);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,10);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetCond(state,1.0E-7,200);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,MathPow(state.m_x[i],4));
state.m_j.Set(0,i,4*MathPow(state.m_x[i],3));
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
//--- Check solution itself
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3305");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3306");
if(waserrors)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(waserrors,MathAbs(x1[i])>tolx,"testminnlcunit.ap:3310");
//--- Check constraint violation reports
CAp::SetErrorFlag(waserrors,rep.m_bcerr!=0.0,"testminnlcunit.ap:3315");
CAp::SetErrorFlag(waserrors,rep.m_bcidx!=-1,"testminnlcunit.ap:3316");
CAp::SetErrorFlag(waserrors,rep.m_lcerr!=0.0,"testminnlcunit.ap:3317");
CAp::SetErrorFlag(waserrors,rep.m_lcidx!=-1,"testminnlcunit.ap:3318");
CAp::SetErrorFlag(waserrors,rep.m_nlcerr!=0.0,"testminnlcunit.ap:3319");
CAp::SetErrorFlag(waserrors,rep.m_nlcidx!=-1,"testminnlcunit.ap:3320");
}
for(pass=1; pass<=10; pass++)
{
n=1+CHighQualityRand::HQRndUniformI(rs,5);
k=1+CHighQualityRand::HQRndUniformI(rs,5);
x0=vector<double>::Zeros(n);
xu=vector<double>::Zeros(n);
bndl=vector<double>::Full(n,-1000.0);
bndu=vector<double>::Full(n,1000.0);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
xu.Set(i,CHighQualityRand::HQRndNormal(rs));
}
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
c.Set(i,n,1000);
ct.Set(i,-1);
}
else
{
c.Set(i,n,-1000);
ct.Set(i,1);
}
}
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,20);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetBC(state,bndl,bndu);
CMinNLC::MinNLCSetLC(state,c,ct,k);
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,MathPow(state.m_x[i]-xu[i],2));
state.m_j.Set(0,i,2*(state.m_x[i]-xu[i]));
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
//--- Check solution itself, calculate reference violation values
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3385");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3386");
if(waserrors)
return;
//--- Check constraint violation reports
CAp::SetErrorFlag(waserrors,rep.m_bcerr!=0.0,"testminnlcunit.ap:3393");
CAp::SetErrorFlag(waserrors,rep.m_bcidx!=-1,"testminnlcunit.ap:3394");
CAp::SetErrorFlag(waserrors,rep.m_lcerr!=0.0,"testminnlcunit.ap:3395");
CAp::SetErrorFlag(waserrors,rep.m_lcidx!=-1,"testminnlcunit.ap:3396");
CAp::SetErrorFlag(waserrors,rep.m_nlcerr!=0.0,"testminnlcunit.ap:3397");
CAp::SetErrorFlag(waserrors,rep.m_nlcidx!=-1,"testminnlcunit.ap:3398");
}
//--- Test constraint violation reports for box/linearly constrained
//--- problems. We generate a problem which can not satisfy one (and
//--- just one) general linear constraint.
tolx=0.001;
for(pass=1; pass<=10; pass++)
{
//--- Formulate problem with inconsistent constraints
n=2+CHighQualityRand::HQRndUniformI(rs,5);
k=1+CHighQualityRand::HQRndUniformI(rs,5);
s=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xu=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
ct.Resize(k);
for(i=0; i<n; i++)
{
s.Set(i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
xu.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,-(0.5*CHighQualityRand::HQRndUniformR(rs))-0.1);
bndu.Set(i,0.5*CHighQualityRand::HQRndUniformR(rs)+0.1);
}
for(i=0; i<k; i++)
{
for(j=0; j<n; j++)
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
c.Set(i,n,1000);
ct.Set(i,-1);
}
else
{
c.Set(i,n,-1000);
ct.Set(i,1);
}
}
CAp::Assert(n>=2,"NLCTest: integrity check failed");
badidx0=CHighQualityRand::HQRndUniformI(rs,k);
badidx1=CHighQualityRand::HQRndUniformI(rs,n);
for(j=0; j<n; j++)
c.Set(badidx0,j,0);
for(j=0; j<k; j++)
c.Set(j,badidx1,0);
c.Set(badidx0,badidx1,1);
c.Set(badidx0,n,10*(2*CHighQualityRand::HQRndUniformI(rs,2)-1));
if(CHighQualityRand::HQRndNormal(rs)>0.0)
ct.Set(badidx0,0);
else
ct.Set(badidx0,(int)MathSign(c.Get(badidx0,n)));
//--- Create and try to solve
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,20);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetBC(state,bndl,bndu);
CMinNLC::MinNLCSetLC(state,c,ct,k);
CMinNLC::MinNLCSetScale(state,s);
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,MathPow(state.m_x[i]-xu[i],2));
state.m_j.Set(0,i,2*(state.m_x[i]-xu[i]));
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
//--- Check solution itself, calculate reference violation values
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3493");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3494");
if(waserrors)
return;
//--- Check constraint violation reports
if(rep.m_bcerr>0.0)
{
v=MathMax(bndl[badidx1]-x1[badidx1],x1[badidx1]-bndu[badidx1]);
v=v/s[badidx1];
CAp::SetErrorFlag(waserrors,rep.m_bcidx!=badidx1,"testminnlcunit.ap:3505");
CAp::SetErrorFlag(waserrors,(double)(MathAbs(rep.m_bcerr-v))>(double)(1.0E3*CMath::m_machineepsilon),"testminnlcunit.ap:3507");
}
else
{
CAp::SetErrorFlag(waserrors,rep.m_bcerr!=0.0,"testminnlcunit.ap:3512");
CAp::SetErrorFlag(waserrors,rep.m_bcidx!=-1,"testminnlcunit.ap:3513");
CAp::SetErrorFlag(waserrors,x1[badidx1]<bndl[badidx1] || x1[badidx1]>bndu[badidx1],"testminnlcunit.ap:3514");
}
CAp::SetErrorFlag(waserrors,rep.m_lcidx!=badidx0,"testminnlcunit.ap:3516");
CAp::SetErrorFlag(waserrors,MathAbs(rep.m_lcerr-MathAbs(x1[badidx1]-c.Get(badidx0,n))/s[badidx1])>(double)(1.0E3*CMath::m_machineepsilon),"testminnlcunit.ap:3517");
CAp::SetErrorFlag(waserrors,rep.m_nlcerr!=0.0,"testminnlcunit.ap:3518");
CAp::SetErrorFlag(waserrors,rep.m_nlcidx!=-1,"testminnlcunit.ap:3519");
}
//--- Test constraint violation reports for box/nonlinearly constrained
//--- problems. We generate a problem which can not satisfy one (and
//--- just one) general linear constraint.
//--- NOTE: it is important to have N>=NLEC+NLIC
tolx=0.001;
for(pass=1; pass<=10; pass++)
{
//--- Formulate problem with inconsistent constraints
nlec=1+CHighQualityRand::HQRndUniformI(rs,5);
nlic=1+CHighQualityRand::HQRndUniformI(rs,5);
k=nlec+nlic;
n=k+CHighQualityRand::HQRndUniformI(rs,5);
s=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xu=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
c=matrix<double>::Zeros(k,n+1);
for(i=0; i<n; i++)
{
s.Set(i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
xu.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,-(0.5*CHighQualityRand::HQRndUniformR(rs))-0.1);
bndu.Set(i,0.5*CHighQualityRand::HQRndUniformR(rs)+0.1);
}
for(i=0; i<=nlec-1; i++)
{
for(j=0; j<n; j++)
c.Set(i,j,0);
c.Set(i,i,1);
c.Set(i,n,bndl[i]+CHighQualityRand::HQRndUniformR(rs)*(bndu[i]-bndl[i]));
}
for(i=nlec; i<k; i++)
{
for(j=0; j<n; j++)
c.Set(i,j,0);
c.Set(i,i,1);
c.Set(i,n,1000);
}
badidx0=CHighQualityRand::HQRndUniformI(rs,k);
if(badidx0<nlec)
{
if(CHighQualityRand::HQRndNormal(rs)>0.0)
c.Set(badidx0,n,bndu[badidx0]+10);
else
c.Set(badidx0,n,bndl[badidx0]-10);
}
else
c.Set(badidx0,n,-1000);
//--- Create and try to solve
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,20);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetBC(state,bndl,bndu);
CMinNLC::MinNLCSetNLC(state,nlec,nlic);
CMinNLC::MinNLCSetScale(state,s);
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,MathPow(state.m_x[i]-xu[i],2));
state.m_j.Set(0,i,2*(state.m_x[i]-xu[i]));
}
for(i=0; i<k; i++)
{
state.m_fi.Set(i+1,-c.Get(i,n));
for(j=0; j<n; j++)
{
state.m_fi.Set(i+1,state.m_fi[i+1]+c.Get(i,j)*state.m_x[j]);
state.m_j.Set(i+1,j,c.Get(i,j));
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
//--- Check solution itself, calculate reference violation values
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3622");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3623");
if(waserrors)
return;
//--- Check constraint violation reports
if(badidx0<nlec)
{
if(rep.m_bcerr>0.0)
{
v=MathMax(bndl[badidx0]-x1[badidx0],x1[badidx0]-bndu[badidx0]);
v=v/s[badidx0];
CAp::SetErrorFlag(waserrors,rep.m_bcidx!=badidx0,"testminnlcunit.ap:3636");
CAp::SetErrorFlag(waserrors,(double)(MathAbs(rep.m_bcerr-v))>(double)(1.0E3*CMath::m_machineepsilon),"testminnlcunit.ap:3638");
}
else
{
CAp::SetErrorFlag(waserrors,rep.m_bcerr!=0.0,"testminnlcunit.ap:3643");
CAp::SetErrorFlag(waserrors,rep.m_bcidx!=-1,"testminnlcunit.ap:3644");
CAp::SetErrorFlag(waserrors,(double)(x1[badidx0])<(double)(bndl[badidx0]) || (double)(x1[badidx0])>(double)(bndu[badidx0]),"testminnlcunit.ap:3645");
}
}
CAp::SetErrorFlag(waserrors,rep.m_lcidx!=-1,"testminnlcunit.ap:3648");
CAp::SetErrorFlag(waserrors,rep.m_lcerr!=0.0,"testminnlcunit.ap:3649");
CAp::SetErrorFlag(waserrors,rep.m_nlcidx!=badidx0,"testminnlcunit.ap:3650");
CAp::SetErrorFlag(waserrors,(double)(MathAbs(rep.m_nlcerr-MathAbs(x1[badidx0]-c.Get(badidx0,n))))>(double)(1.0E4*CMath::m_machineepsilon),"testminnlcunit.ap:3651");
}
//--- Test support for termination requests:
//--- * to terminate with correct return code = 8
//---*to return point which was "current" at the moment of termination
for(pass=1; pass<=50; pass++)
{
n=3;
ss=100;
x0=vector<double>::Zeros(n);
xlast=vector<double>::Zeros(n);
for(i=0; i<n; i++)
x0.Set(i,6+CHighQualityRand::HQRndUniformR(rs));
stopcallidx=CHighQualityRand::HQRndUniformI(rs,20);
maxits=25;
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,20);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetCond(state,0,maxits);
CMinNLC::MinNLCSetXRep(state,true);
callidx=0;
terminationrequested=false;
xlast=x0;
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,ss*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_j.Set(0,0,2*ss*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[2]-state.m_x[0])*-1);
state.m_j.Set(0,1,2*state.m_x[1]);
state.m_j.Set(0,2,2*(state.m_x[2]-state.m_x[0]));
if(callidx==stopcallidx)
{
CMinNLC::MinNLCRequestTermination(state);
terminationrequested=true;
}
callidx++;
continue;
}
if(state.m_xupdated)
{
if(!terminationrequested)
xlast=state.m_x;
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype!=8,"testminnlcunit.ap:3708");
for(i=0; i<n; i++)
CAp::SetErrorFlag(waserrors,x1[i]!=(double)(xlast[i]),"testminnlcunit.ap:3710");
}
}
//--- AUL-specific test.
//--- Test preconditioning:
//--- * compare number of iterations required to solve problem with
//--- different preconditioners (LBFGS, exact, none)
//--- * a set of trials is performed (100 trials)
//--- * each trial is a solution of boundary/linearly constrained problem
//--- (linear constraints may be posed as nonlinear ones) with normalized
//--- constraint matrix. Normalization is essential for reproducibility
//--- of results.
//--- Outer loop checks handling of different types of constraints
//--- (posed as linear or nonlinear ones)
n=30;
blocksize=3;
blockcnt=3;
rho=1.0E3;
aulits=5;
condv=1.0E2;
x0=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
c=matrix<double>::Zeros(blocksize*blockcnt,n+1);
ct.Resize(blocksize*blockcnt);
for(ctype=0; ctype<=1; ctype++)
{
//--- First, initialize iteration counters
nlbfgs=0;
nexactlowrank=0;
nexactrobust=0;
nnone=0;
//--- Perform trials
for(trialidx=0; trialidx<=99; trialidx++)
{
//--- Generate:
//--- * boundary constraints BndL/BndU and initial point X0
//--- * block-diagonal matrix of linear constraints C such
//--- that X0 is feasible w.r.t. constraints given by C
for(i=0; i<n; i++)
{
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
bndl.Set(i,0);
bndu.Set(i,AL_POSINF);
x0.Set(i,CHighQualityRand::HQRndUniformR(rs));
}
else
{
bndl.Set(i,0);
bndu.Set(i,0);
x0.Set(i,0);
}
}
for(i=0; i<=blocksize*blockcnt-1; i++)
for(j=0; j<n; j++)
c.Set(i,j,0.0);
for(k=0; k<=blockcnt-1; k++)
{
CMatGen::RMatrixRndCond(blocksize,condv,a);
for(i=0; i<=blocksize-1; i++)
for(j=0; j<=blocksize-1; j++)
c.Set(k*blocksize+i,k*blocksize+j,a.Get(i,j));
}
for(i=0; i<=blocksize*blockcnt-1; i++)
{
v=CAblasF::RDotRR(n,c,i,c,i);
v=1/MathSqrt(v);
for(i_=0; i_<n; i_++)
c.Mul(i,i_,v);
v=CAblasF::RDotVR(n,x0,c,i);
c.Set(i,n,v);
ct.Set(i,CHighQualityRand::HQRndUniformI(rs,3)-1);
}
//--- Test unpreconditioned iteration
CMinNLC::MinNLCCreate(n,x0,state);
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
if(ctype==0)
CMinNLC::MinNLCSetLC(state,c,ct,blocksize*blockcnt);
else
CMinNLC::MinNLCSetNLC(state,blocksize*blockcnt,0);
CMinNLC::MinNLCSetPrecNone(state);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,CMath::Sqr(state.m_x[i]));
state.m_j.Set(0,i,2*state.m_x[i]);
}
if(ctype==1)
{
for(i=0; i<=blocksize*blockcnt-1; i++)
{
v=CAblasF::RDotVR(n,state.m_x,c,i);
state.m_fi.Set(1+i,v-c.Get(i,n));
for(i_=0; i_<n; i_++)
state.m_j.Set(1+i,i_,c.Get(i,i_));
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3834");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3835");
if(waserrors)
return;
nnone+=rep.m_iterationscount;
//--- Test LBFGS preconditioned iteration
CMinNLC::MinNLCCreate(n,x0,state);
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
if(ctype==0)
CMinNLC::MinNLCSetLC(state,c,ct,blocksize*blockcnt);
else
CMinNLC::MinNLCSetNLC(state,blocksize*blockcnt,0);
CMinNLC::MinNLCSetPrecInexact(state);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,CMath::Sqr(state.m_x[i]));
state.m_j.Set(0,i,2*state.m_x[i]);
}
if(ctype==1)
{
for(i=0; i<=blocksize*blockcnt-1; i++)
{
v=CAblasF::RDotVR(n,state.m_x,c,i);
state.m_fi.Set(1+i,v-c.Get(i,n));
for(i_=0; i_<n; i_++)
state.m_j.Set(1+i,i_,c.Get(i,i_));
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3875");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3876");
if(waserrors)
return;
nlbfgs+=rep.m_iterationscount;
//--- Test exact low rank preconditioner
CMinNLC::MinNLCCreate(n,x0,state);
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
if(ctype==0)
CMinNLC::MinNLCSetLC(state,c,ct,blocksize*blockcnt);
else
CMinNLC::MinNLCSetNLC(state,blocksize*blockcnt,0);
CMinNLC::MinNLCSetPrecExactLowRank(state,3);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,CMath::Sqr(state.m_x[i]));
state.m_j.Set(0,i,2*state.m_x[i]);
}
if(ctype==1)
{
for(i=0; i<=blocksize*blockcnt-1; i++)
{
v=CAblasF::RDotVR(n,state.m_x,c,i);
state.m_fi.Set(1+i,v-c.Get(i,n));
for(i_=0; i_<n; i_++)
state.m_j.Set(1+i,i_,c.Get(i,i_));
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3916");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3917");
if(waserrors)
return;
nexactlowrank+=rep.m_iterationscount;
//--- Test exact robust preconditioner
CMinNLC::MinNLCCreate(n,x0,state);
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
CMinNLC::MinNLCSetCond(state,1.0E-7,0);
if(ctype==0)
CMinNLC::MinNLCSetLC(state,c,ct,blocksize*blockcnt);
else
CMinNLC::MinNLCSetNLC(state,blocksize*blockcnt,0);
CMinNLC::MinNLCSetPrecExactRobust(state,3);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,CMath::Sqr(state.m_x[i]));
state.m_j.Set(0,i,2*state.m_x[i]);
}
if(ctype==1)
{
for(i=0; i<=blocksize*blockcnt-1; i++)
{
v=CAblasF::RDotVR(n,state.m_x,c,i);
state.m_fi.Set(1+i,v-c.Get(i,n));
for(i_=0; i_<n; i_++)
state.m_j.Set(1+i,i_,c.Get(i,i_));
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:3957");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:3958");
if(waserrors)
return;
nexactrobust+=rep.m_iterationscount;
}
//--- Compare.
//--- Preconditioners must be significantly different,
//--- with exact being best one, inexact being second,
//--- "none" being worst option.
CAp::SetErrorFlag(waserrors,!((double)(nexactlowrank)<(double)(0.9*nlbfgs)),"testminnlcunit.ap:3971");
CAp::SetErrorFlag(waserrors,!((double)(nexactrobust)<(double)(0.9*nlbfgs)),"testminnlcunit.ap:3972");
CAp::SetErrorFlag(waserrors,!((double)(nlbfgs)<(double)(0.9*nnone)),"testminnlcunit.ap:3973");
}
}
//+------------------------------------------------------------------+
//| This function tests OptGuard |
//| On failure sets error flag. |
//+------------------------------------------------------------------+
void CTestMinNLCUnit::TestOptGuard(bool &waserrors)
{
//--- create variables
CHighQualityRandState rs;
double v=0;
CMinNLCState state;
CMinNLCReport rep;
COptGuardReport ogrep;
COptGuardNonC1Test0Report ognonc1test0strrep;
COptGuardNonC1Test0Report ognonc1test0lngrep;
COptGuardNonC1Test1Report ognonc1test1strrep;
COptGuardNonC1Test1Report ognonc1test1lngrep;
int i=0;
int j=0;
int n=0;
CMatrixDouble a;
CMatrixDouble a1;
CRowDouble s;
CRowDouble x0;
CRowDouble x1;
CRowDouble xlast;
CRowDouble b;
CRowDouble bndl;
CRowDouble bndu;
CRowDouble xu;
double diffstep=0;
int pass=0;
int solvertype=0;
double vbnd=0;
double fscale=0;
int defecttype=0;
double vshift=0;
double vpower=0;
int cntabove=0;
int cntbelow=0;
bool linesearchstarted;
bool wasgoodlinesearch0;
bool wasgoodlinesearch1;
int shortsessions=0;
int maxshortsessions=0;
double stplen=0;
double shortstplen=0;
bool failed;
int passcount=0;
int maxfails=0;
int failurecounter=0;
int maxc1test0fails=0;
int maxc1test1fails=0;
int c1test0fails=0;
int c1test1fails=0;
int goodidx=0;
double avgstr0len=0;
double avglng0len=0;
double avgstr1len=0;
double avglng1len=0;
int funcidx=0;
int varidx=0;
int skind=0;
CMatrixDouble jactrue;
CMatrixDouble jacdefect;
CHighQualityRand::HQRndRandomize(rs);
//--- Test functionality which should work in all NLC solvers
for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++)
{
//--- Check that gradient verification is disabled by default:
//--- gradient checking for bad problem must return nothing
n=10;
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
x0.Set(i,1.0+0.1*i);
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
CMatGen::SPDMatrixRndCond(n,1.0E3,a1);
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,5);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetCond(state,1.0E-7,10);
CMinNLC::MinNLCSetNLC(state,0,1);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,state.m_x,a,i);
state.m_fi.Add(0,0.5*state.m_x[i]*v);
}
state.m_fi.Set(1,0);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,state.m_x,a1,i);
state.m_fi.Add(1,0.5*state.m_x[i]*v);
}
for(i=0; i<n; i++)
{
state.m_j.Set(0,i,0);
state.m_j.Set(1,i,0);
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:4085");
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:4086");
if(waserrors)
return;
CAp::SetErrorFlag(waserrors,CAp::Len(ogrep.m_badgradxbase)!=0,"testminnlcunit.ap:4089");
CAp::SetErrorFlag(waserrors,CAp::Rows(ogrep.m_badgraduser)!=0,"testminnlcunit.ap:4090");
CAp::SetErrorFlag(waserrors,CAp::Cols(ogrep.m_badgraduser)!=0,"testminnlcunit.ap:4091");
CAp::SetErrorFlag(waserrors,CAp::Rows(ogrep.m_badgradnum)!=0,"testminnlcunit.ap:4092");
CAp::SetErrorFlag(waserrors,CAp::Cols(ogrep.m_badgradnum)!=0,"testminnlcunit.ap:4093");
if(waserrors)
return;
CAp::SetErrorFlag(waserrors,ogrep.m_badgradsuspected,"testminnlcunit.ap:4096");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=-1,"testminnlcunit.ap:4097");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=-1,"testminnlcunit.ap:4098");
//--- Test that C0/C1 continuity monitoring is disabled by default;
//--- we solve nonsmooth problem and test that nothing is returned
//--- by OptGuard.
n=10;
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
a=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,1);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetCond(state,1.0E-9,50);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
state.m_j.Set(0,i,0);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,state.m_x,a,i);
state.m_fi.Add(0,MathAbs(v));
v=MathSign(v);
for(j=0; j<n; j++)
state.m_j.Add(0,j,v*a.Get(i,j));
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:4146");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:4147");
if(waserrors)
return;
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0suspected,"testminnlcunit.ap:4150");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1suspected,"testminnlcunit.ap:4151");
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:4152");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0fidx>=0,"testminnlcunit.ap:4153");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx>=0,"testminnlcunit.ap:4154");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1test0positive,"testminnlcunit.ap:4155");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1test1positive,"testminnlcunit.ap:4156");
//--- Test gradient checking functionality, try various
//--- defect types:
//--- * accidental zeroing of some gradient component
//--- * accidental addition of 1.0 to some component
//--- * accidental multiplication by 2.0
//--- Try distorting both target and constraints.
diffstep=0.001;
n=10;
for(skind=0; skind<=1; skind++)
{
for(funcidx=0; funcidx<=1; funcidx++)
{
for(defecttype=-1; defecttype<=2; defecttype++)
{
varidx=CHighQualityRand::HQRndUniformI(rs,n);
x0=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
s.Set(i,MathPow(10,skind*(30*CHighQualityRand::HQRndUniformR(rs)-15)));
x0.Set(i,(1.0+0.1*i)*s[i]);
j=CHighQualityRand::HQRndUniformI(rs,3);
bndl.Set(i,-(100*s[i]));
bndu.Set(i,100*s[i]);
if(j==1)
bndl.Set(i,x0[i]);
if(j==2)
bndu.Set(i,x0[i]);
}
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
CMatGen::SPDMatrixRndCond(n,1.0E3,a1);
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,5);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCOptGuardGradient(state,diffstep);
CMinNLC::MinNLCSetCond(state,1.0E-7,10);
CMinNLC::MinNLCSetScale(state,s);
CMinNLC::MinNLCSetBC(state,bndl,bndu);
CMinNLC::MinNLCSetNLC(state,0,1);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
if(solvertype!=0)
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(waserrors,state.m_x[i]<bndl[i],"testminnlcunit.ap:4216");
CAp::SetErrorFlag(waserrors,state.m_x[i]>bndu[i],"testminnlcunit.ap:4217");
}
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
v=0;
for(j=0; j<n; j++)
v+=state.m_x[j]/s[j]*a.Get(i,j);
state.m_fi.Add(0,0.5*(state.m_x[i]/s[i])*v);
state.m_j.Set(0,i,v);
}
state.m_fi.Set(1,0);
for(i=0; i<n; i++)
{
v=0;
for(j=0; j<n; j++)
v+=state.m_x[j]/s[j]*a1.Get(i,j);
state.m_fi.Add(1,0.5*(state.m_x[i]/s[i])*v);
state.m_j.Set(1,i,v);
}
if(defecttype==0)
state.m_j.Set(funcidx,varidx,0);
if(defecttype==1)
state.m_j.Set(funcidx,varidx,state.m_j.Get(funcidx,varidx)+1);
if(defecttype==2)
state.m_j.Mul(funcidx,varidx,2);
for(i=0; i<n; i++)
{
state.m_j.Mul(0,i,1.0/s[i]);
state.m_j.Mul(1,i,1.0/s[i]);
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CMinNLC::MinNLCOptGuardResults(state,ogrep);
//--- Check that something is returned
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:4267");
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:4268");
if(waserrors)
return;
//--- Compute reference values for true and spoiled Jacobian at X0
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(ogrep.m_badgradxbase,n),"testminnlcunit.ap:4275");
if(waserrors)
return;
jactrue=matrix<double>::Zeros(2,n);
jacdefect=matrix<double>::Zeros(2,n);
for(i=0; i<n; i++)
{
v=0;
for(j=0; j<n; j++)
v+=ogrep.m_badgradxbase[j]/s[j]*a.Get(i,j);
jactrue.Set(0,i,v);
jacdefect.Set(0,i,v);
}
for(i=0; i<n; i++)
{
v=0;
for(j=0; j<n; j++)
v+=ogrep.m_badgradxbase[j]/s[j]*a1.Get(i,j);
jactrue.Set(1,i,v);
jacdefect.Set(1,i,v);
}
if(defecttype==0)
jacdefect.Set(funcidx,varidx,0);
if(defecttype==1)
jacdefect.Add(funcidx,varidx,1);
if(defecttype==2)
jacdefect.Mul(funcidx,varidx,2);
for(i=0; i<n; i++)
{
jactrue.Mul(0,i,1.0/s[i]);
jactrue.Mul(1,i,1.0/s[i]);
jacdefect.Mul(0,i,1.0/s[i]);
jacdefect.Mul(1,i,1.0/s[i]);
}
//--- Check OptGuard report
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteMatrix(ogrep.m_badgraduser,2,n),"testminnlcunit.ap:4313");
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteMatrix(ogrep.m_badgradnum,2,n),"testminnlcunit.ap:4314");
if(waserrors)
return;
if(defecttype>=0)
{
CAp::SetErrorFlag(waserrors,!ogrep.m_badgradsuspected,"testminnlcunit.ap:4319");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=funcidx,"testminnlcunit.ap:4320");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=varidx,"testminnlcunit.ap:4321");
}
else
{
CAp::SetErrorFlag(waserrors,ogrep.m_badgradsuspected,"testminnlcunit.ap:4325");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=-1,"testminnlcunit.ap:4326");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=-1,"testminnlcunit.ap:4327");
}
for(i=0; i<=1; i++)
for(j=0; j<n; j++)
{
CAp::SetErrorFlag(waserrors,(MathAbs(jactrue.Get(i,j)-ogrep.m_badgradnum.Get(i,j)))>(0.01/s[j]),"testminnlcunit.ap:4332");
CAp::SetErrorFlag(waserrors,(MathAbs(jacdefect.Get(i,j)-ogrep.m_badgraduser.Get(i,j)))>(0.01/s[j]),"testminnlcunit.ap:4333");
}
}
}
}
//--- Make sure than no false positives are reported for larger
//--- problems where numerical noise can be an issue:
//--- * N=100 dimensions
//--- * nonnegativity constraints
//--- * positive-definite quadratic programming problem
//--- * upper limit on iterations count, MaxIts=25
//--- We simply test that OptGuard does not return error code.
n=100;
CMatGen::SPDMatrixRndCond(n,1.0E2,a);
b=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
bndl.Set(i,0);
bndu.Set(i,AL_POSINF);
x0.Set(i,MathPow(2.0,CHighQualityRand::HQRndNormal(rs)));
}
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,3);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCOptGuardSmoothness(state,1+CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel));
CMinNLC::MinNLCSetBC(state,bndl,bndu);
CMinNLC::MinNLCSetCond(state,1.0E-7,25);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
for(j=0; j<n; j++)
{
state.m_fi.Add(0,0.5*state.m_x[i]*a.Get(i,j)*state.m_x[j]);
state.m_j.Set(0,i,a.Get(i,j)*state.m_x[j]);
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:4391");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:4392");
if(waserrors)
return;
CMinNLC::MinNLCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:4396");
}
//--- Test functionality which works only in some NLC solvers
for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++)
{
//--- Skip
if(solvertype==2)
continue;
//--- Test detection of discontinuities in the target function
//--- and its gradient (analytic gradient is used).
//--- Target function is convex quadratic modified by addition of
//--- nonsmooth/discontinuous (depending on DefectType) perturbation.
//--- This test is complicated because OptGuard does NOT guarantee
//--- that C0/C1 violations are ALWAYS caught. OptGuard needs line
//--- search to perform
//--- * at least 4 function evaluations, with discontinuity in the
//--- middle of them (at least two function values from the left,
//--- at least two from the right)
//--- * at least 7 function evaluations to catch C1 violation
//--- Furthermore, it is possible that optimizer will perform a few
//--- function evaluations BEFORE and AFTER line search starts,
//--- which complicates everything.
//--- N, VPower and VBnd are selected randomly at the start of the test.
for(defecttype=0; defecttype<=1; defecttype++)
{
n=1+CHighQualityRand::HQRndUniformI(rs,10);
vpower=10*MathPow(10,-(0.2*CHighQualityRand::HQRndUniformR(rs)));
vbnd=1*MathPow(10,-(0.2*CHighQualityRand::HQRndUniformR(rs)));
fscale=0.1;
maxshortsessions=4;
shortstplen=1.0E-6;
shortsessions=0;
for(pass=1; pass<=100; pass++)
{
//--- Formulate problem
s=vector<double>::Zeros(n);
xlast=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
xu=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
do
{
v=0;
for(i=0; i<n; i++)
{
xu.Set(i,0.1+CHighQualityRand::HQRndUniformR(rs));
v+=CMath::Sqr(xu[i]);
}
v=MathSqrt(v);
}
while(v<=0.0);
for(i=0; i<n; i++)
xu.Mul(i,1.0/v);
for(i=0; i<n; i++)
{
xlast.Set(i,0);
x0.Set(i,2*xu[i]+0.1*CHighQualityRand::HQRndUniformR(rs));
bndl.Set(i,0.0);
bndu.Set(i,AL_POSINF);
s.Set(i,MathPow(2,0.1*CHighQualityRand::HQRndNormal(rs)));
}
if(defecttype==0)
vshift=1;
else
vshift=0;
//--- Prepare code which detects "good" (long enough) line searches
cntbelow=0;
cntabove=0;
wasgoodlinesearch0=false;
wasgoodlinesearch1=false;
linesearchstarted=false;
//--- Create and try to solve
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,5);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetBC(state,bndl,bndu);
CMinNLC::MinNLCSetScale(state,s);
CMinNLC::MinNLCSetCond(state,1.0E-7,1000);
CMinNLC::MinNLCOptGuardSmoothness(state,1+CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel));
CMinNLC::MinNLCSetXRep(state,true);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,fscale*MathPow(state.m_x[i],2));
state.m_j.Set(0,i,2*fscale*state.m_x[i]);
}
v=CAblasF::RDotV(n,state.m_x,xu);
if(v<vbnd)
{
state.m_fi.Add(0,(vshift+vpower*(vbnd-v)));
for(i=0; i<n; i++)
state.m_j.Add(0,i,- vpower*xu[i]);
if(linesearchstarted)
cntbelow++;
}
else
{
if(linesearchstarted)
cntabove++;
}
continue;
}
if(state.m_xupdated)
{
//--- Finalize previous line search
if(linesearchstarted)
{
stplen=0;
for(i=0; i<n; i++)
stplen+=CMath::Sqr(state.m_x[i]-xlast[i]);
stplen=MathSqrt(stplen);
wasgoodlinesearch0=wasgoodlinesearch0||((cntbelow>=2 && cntabove>=2) && (double)(stplen)>=(double)(shortstplen));
wasgoodlinesearch1=wasgoodlinesearch1||((cntbelow>=2 && cntabove>=2) && (double)(stplen)>=(double)(shortstplen));
}
//--- Start new line search
linesearchstarted=true;
cntbelow=0;
cntabove=0;
xlast=state.m_x;
v=CAblasF::RDotV(n,state.m_x,xu);
if(v<vbnd)
cntbelow++;
else
cntabove++;
//--- Done
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CMinNLC::MinNLCOptGuardResults(state,ogrep);
//--- Check basic properties of the solution
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:4573");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:4574");
if(waserrors)
return;
//--- Check OptGuard report, increase
if(defecttype==0)
{
if(wasgoodlinesearch0)
{
CAp::SetErrorFlag(waserrors,COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:4585");
CAp::SetErrorFlag(waserrors,!ogrep.m_nonc0suspected,"testminnlcunit.ap:4586");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0fidx!=0,"testminnlcunit.ap:4587");
}
else
shortsessions++;
}
if(defecttype==1)
{
if(wasgoodlinesearch1)
{
CAp::SetErrorFlag(waserrors,COptGuardApi::OptGuardAllClear(ogrep),"testminnlcunit.ap:4596");
CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminnlcunit.ap:4597");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx!=0,"testminnlcunit.ap:4598");
}
else
shortsessions++;
}
if(waserrors)
return;
}
//--- Check that short optimization sessions are rare.
CAp::SetErrorFlag(waserrors,shortsessions>maxshortsessions,"testminnlcunit.ap:4610");
}
//--- One more test for detection of C1 continuity violations in the target.
//--- Target function is a sum of |(x,c_i)| for i=1..N.
//--- No constraints is present.
//--- Analytic gradient is provided.
//--- OptGuard should be able to detect violations in more than
//--- 99.9% of runs; it means that 100 runs should have no more than 4
//--- failures in all cases (even after multiple repeated tests; according
//--- to the binomial distribution quantiles).
//--- We select some N and perform exhaustive search for this N.
//--- NOTE: we skip SQP for this test
passcount=100;
maxfails=4;
maxc1test0fails=10;
maxc1test1fails=10;
n=1+CHighQualityRand::HQRndUniformI(rs,10);
failurecounter=0;
c1test0fails=0;
c1test1fails=0;
avgstr0len=0;
avglng0len=0;
avgstr1len=0;
avglng1len=0;
for(pass=1; pass<=passcount; pass++)
{
//--- Formulate problem
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
s.Set(i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
a=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
//--- Create and try to solve
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,1);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetCond(state,1.0E-7,50);
CMinNLC::MinNLCSetScale(state,s);
CMinNLC::MinNLCOptGuardSmoothness(state,1+CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel));
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
state.m_j.Set(0,i,0);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,state.m_x,a,i);
state.m_fi.Add(0,MathAbs(v));
v=MathSign(v);
for(j=0; j<n; j++)
state.m_j.Add(0,j,v*a.Get(i,j));
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CMinNLC::MinNLCOptGuardResults(state,ogrep);
//--- Check basic properties of the solution
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:4700");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:4701");
if(waserrors)
return;
//--- Check generic OptGuard report: distinguish between "hard"
//--- failures which result in immediate termination
//--- (C0 violation being reported) and "soft" ones
//--- (C1 violation is NOT reported) which accumulate
//--- until we exhaust limit.
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0suspected,"testminnlcunit.ap:4712");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0fidx>=0,"testminnlcunit.ap:4713");
failed=false;
failed=failed||COptGuardApi::OptGuardAllClear(ogrep);
failed=failed||!ogrep.m_nonc1suspected;
failed=failed||ogrep.m_nonc1fidx!=0;
if(failed)
failurecounter++;
//--- Check C1 continuity test #0
CMinNLC::MinNLCOptGuardNonC1Test0Results(state,ognonc1test0strrep,ognonc1test0lngrep);
CMinNLC::MinNLCOptGuardNonC1Test1Results(state,ognonc1test1strrep,ognonc1test1lngrep);
if(ogrep.m_nonc1test0positive)
{
CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminnlcunit.ap:4728");
CAp::SetErrorFlag(waserrors,!ognonc1test0strrep.m_positive,"testminnlcunit.ap:4729");
CAp::SetErrorFlag(waserrors,!ognonc1test0lngrep.m_positive,"testminnlcunit.ap:4730");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx!=0,"testminnlcunit.ap:4731");
TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0strrep,a,n);
TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0lngrep,a,n);
avgstr0len+=(double)ognonc1test0strrep.m_cnt/(double)passcount;
avglng0len+=(double)ognonc1test0lngrep.m_cnt/(double)passcount;
}
else
{
CAp::SetErrorFlag(waserrors,ognonc1test0strrep.m_positive,"testminnlcunit.ap:4739");
CAp::SetErrorFlag(waserrors,ognonc1test0lngrep.m_positive,"testminnlcunit.ap:4740");
TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0strrep,a,n);
TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0lngrep,a,n);
c1test0fails++;
}
if(ogrep.m_nonc1test1positive)
{
CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminnlcunit.ap:4747");
CAp::SetErrorFlag(waserrors,!ognonc1test1strrep.m_positive,"testminnlcunit.ap:4748");
CAp::SetErrorFlag(waserrors,!ognonc1test1lngrep.m_positive,"testminnlcunit.ap:4749");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx!=0,"testminnlcunit.ap:4750");
TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1strrep,a,n);
TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1lngrep,a,n);
avgstr1len+=(double)ognonc1test1strrep.m_cnt/(double)passcount;
avglng1len+=(double)ognonc1test1lngrep.m_cnt/(double)passcount;
}
else
{
CAp::SetErrorFlag(waserrors,ognonc1test1strrep.m_positive,"testminnlcunit.ap:4758");
CAp::SetErrorFlag(waserrors,ognonc1test1lngrep.m_positive,"testminnlcunit.ap:4759");
TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1strrep,a,n);
TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1lngrep,a,n);
c1test1fails++;
}
}
CAp::SetErrorFlag(waserrors,failurecounter>maxfails,"testminnlcunit.ap:4766");
CAp::SetErrorFlag(waserrors,c1test0fails>maxc1test0fails,"testminnlcunit.ap:4767");
CAp::SetErrorFlag(waserrors,c1test1fails>maxc1test1fails,"testminnlcunit.ap:4768");
CAp::SetErrorFlag(waserrors,(double)(avglng0len)<=(double)(avgstr0len),"testminnlcunit.ap:4769");
CAp::SetErrorFlag(waserrors,(double)(avglng1len)<=(double)(avgstr1len),"testminnlcunit.ap:4770");
//--- Detection of C1 continuity violations in the target under numerical differentiation:
//--- * target function is a sum of |(x,c_i)| for i=1..N.
//--- * no constraints is present.
//--- * analytic gradient is provided.
//--- OptGuard should always be able to detect violations in more than
//--- 99% of runs (note: reduced strength when compared with analytic gradient);
//--- it means that 100 runs should have no more than 10 failures in all cases
//--- (even after multiple repeated tests; according to the binomial distribution
//--- quantiles).
//--- We select some N and perform exhaustive search for this N.
//--- NOTE: we skip SQP solver for this test
diffstep=0.0001;
passcount=100;
maxfails=10;
n=1+CHighQualityRand::HQRndUniformI(rs,10);
failurecounter=0;
for(pass=1; pass<=passcount; pass++)
{
//--- Formulate problem
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
s.Set(i,0.01*MathPow(2,0.33*CHighQualityRand::HQRndNormal(rs)));
a=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
//--- Create and try to solve
CMinNLC::MinNLCCreateF(n,x0,diffstep,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,1);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetCond(state,1.0E-7,50);
CMinNLC::MinNLCOptGuardSmoothness(state,1+CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel));
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfi)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,state.m_x,a,i);
state.m_fi.Add(0,MathAbs(v));
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CMinNLC::MinNLCOptGuardResults(state,ogrep);
//--- Check basic properties of the solution
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:4845");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:4846");
if(waserrors)
return;
//--- Check OptGuard report: distinguish between "hard"
//--- failures which result in immediate termination
//--- (C0 violation being reported) and "soft" ones
//--- (C1 violation is NOT reported) which accumulate
//--- until we exhaust limit.
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0suspected,"testminnlcunit.ap:4857");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0fidx>=0,"testminnlcunit.ap:4858");
failed=false;
failed=failed||COptGuardApi::OptGuardAllClear(ogrep);
failed=failed||!ogrep.m_nonc1suspected;
failed=failed||ogrep.m_nonc1fidx!=0;
if(failed)
failurecounter++;
}
CAp::SetErrorFlag(waserrors,failurecounter>maxfails,"testminnlcunit.ap:4866");
//--- Detection of C1 continuity violations in the nonlinear constraints.
//--- This test is a bit tricky because optimizers are less sensitive to
//--- continuity violations in constraints, so we may have hard time collecting
//--- enough statistics. In order to do so we solve carefully designed hard
//--- problem with MULTIPLE bad constraints and only one good constraint.
//--- Optimizer may report any of bad constraints, but not good one.
//--- We select some N and perform exhaustive search for this N.
passcount=100;
maxfails=20;
n=5;
failurecounter=0;
c1test0fails=0;
c1test1fails=0;
for(pass=1; pass<=passcount; pass++)
{
//--- Formulate problem, select constraint index to perturb with
//--- nonsmoothness; make sure that this constraint is active at
//--- the solution
x0=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
b=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,CHighQualityRand::HQRndUniformR(rs)-0.5);
s.Set(i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
b.Set(i,CHighQualityRand::HQRndNormal(rs));
}
a=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
a.Set(i,j,MathPow(2,0.01*CHighQualityRand::HQRndNormal(rs)));
goodidx=CHighQualityRand::HQRndUniformI(rs,n);
//--- Create and try to solve
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,1000.0,3);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetCond(state,1.0E-7,50);
CMinNLC::MinNLCSetNLC(state,0,n);
CMinNLC::MinNLCSetScale(state,s);
CMinNLC::MinNLCOptGuardSmoothness(state,1+CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel));
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
state.m_j.Set(0,i,b[i]);
}
for(i=0; i<n; i++)
{
state.m_fi.Set(1+i,-1);
for(j=0; j<n; j++)
{
state.m_fi.Set(1+i,state.m_fi[1+i]+a.Get(i,j)*CMath::Sqr(state.m_x[j]));
state.m_j.Set(1+i,j,2*a.Get(i,j)*state.m_x[j]);
}
if(i!=goodidx)
{
state.m_fi.Set(1+i,MathMax(state.m_fi[1+i],0.0));
for(j=0; j<n; j++)
state.m_j.Mul(1+i,j,MathSign(state.m_fi[1+i]));
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CMinNLC::MinNLCOptGuardResults(state,ogrep);
//--- Check basic properties of the solution
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:4962");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:4963");
if(waserrors)
return;
//--- Check generic OptGuard report: distinguish between "hard"
//--- failures which result in immediate termination
//--- (C0 violation being reported) and "soft" ones
//--- (C1 violation is NOT reported) which accumulate
//--- until we exhaust limit.
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0suspected,"testminnlcunit.ap:4974");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0fidx>=0,"testminnlcunit.ap:4975");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx==goodidx+1,"testminnlcunit.ap:4976");
failed=false;
failed=failed||COptGuardApi::OptGuardAllClear(ogrep);
failed=failed||!ogrep.m_nonc1suspected;
if(failed)
failurecounter++;
//--- Check C1 continuity tests #0 and #1
CMinNLC::MinNLCOptGuardNonC1Test0Results(state,ognonc1test0strrep,ognonc1test0lngrep);
CMinNLC::MinNLCOptGuardNonC1Test1Results(state,ognonc1test1strrep,ognonc1test1lngrep);
if(ogrep.m_nonc1test0positive)
{
CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminnlcunit.ap:4990");
CAp::SetErrorFlag(waserrors,!ognonc1test0strrep.m_positive,"testminnlcunit.ap:4991");
CAp::SetErrorFlag(waserrors,!ognonc1test0lngrep.m_positive,"testminnlcunit.ap:4992");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx<1,"testminnlcunit.ap:4993");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx>n,"testminnlcunit.ap:4994");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx==goodidx+1,"testminnlcunit.ap:4995");
TestOptGuardC1Test0ReportForTask1(waserrors,ognonc1test0strrep,a,n,goodidx);
TestOptGuardC1Test0ReportForTask1(waserrors,ognonc1test0lngrep,a,n,goodidx);
}
else
{
CAp::SetErrorFlag(waserrors,ognonc1test0strrep.m_positive,"testminnlcunit.ap:5001");
CAp::SetErrorFlag(waserrors,ognonc1test0lngrep.m_positive,"testminnlcunit.ap:5002");
TestOptGuardC1Test0ReportForTask1(waserrors,ognonc1test0strrep,a,n,goodidx);
TestOptGuardC1Test0ReportForTask1(waserrors,ognonc1test0lngrep,a,n,goodidx);
c1test0fails++;
}
if(ogrep.m_nonc1test1positive)
{
CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminnlcunit.ap:5009");
CAp::SetErrorFlag(waserrors,!ognonc1test1strrep.m_positive,"testminnlcunit.ap:5010");
CAp::SetErrorFlag(waserrors,!ognonc1test1lngrep.m_positive,"testminnlcunit.ap:5011");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx<1,"testminnlcunit.ap:5012");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx>n,"testminnlcunit.ap:5013");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx==goodidx+1,"testminnlcunit.ap:5014");
TestOptGuardC1Test1ReportForTask1(waserrors,ognonc1test1strrep,a,n,goodidx);
TestOptGuardC1Test1ReportForTask1(waserrors,ognonc1test1lngrep,a,n,goodidx);
}
else
{
CAp::SetErrorFlag(waserrors,ognonc1test1strrep.m_positive,"testminnlcunit.ap:5020");
CAp::SetErrorFlag(waserrors,ognonc1test1lngrep.m_positive,"testminnlcunit.ap:5021");
TestOptGuardC1Test1ReportForTask1(waserrors,ognonc1test1strrep,a,n,goodidx);
TestOptGuardC1Test1ReportForTask1(waserrors,ognonc1test1lngrep,a,n,goodidx);
c1test1fails++;
}
}
CAp::SetErrorFlag(waserrors,failurecounter>maxfails,"testminnlcunit.ap:5027");
}
}
//+------------------------------------------------------------------+
//| This function performs tests for fixed bugs |
//| On failure sets error flag. |
//+------------------------------------------------------------------+
void CTestMinNLCUnit::TestBugs(bool &waserrors)
{
//--- create variables
CHighQualityRandState rs;
int n=0;
int aulits=0;
int maxits=0;
double rho=0;
int ckind=0;
int i=0;
int j=0;
int k=0;
int solvertype=0;
CMinNLCState state;
CMinNLCReport rep;
CRowDouble x0;
CRowDouble x1;
CRowDouble x2;
CRowDouble bndl;
CRowDouble bndu;
CRowDouble d;
CRowDouble b;
CRowInt ct;
CMatrixDouble c;
CHighQualityRand::HQRndRandomize(rs);
//--- Bug description (fixed): sometimes on non-convex problems, when
//--- Lagrange coefficient for inequality constraint becomes small,
//--- algorithm performs VERY deep step into infeasible area (step is 1E50),
//--- which de-stabilizes it and prevents from converging back to feasible area.
//--- Very rare situation, but must be fixed with additional "convexifying" term.
//--- This test reproduces situation with convexified term turned off, then
//--- checks that introduction of term solves issue.
//--- We perform three kinds of tests:
//--- * with box inequality constraint
//--- * with linear inequality constraint
//--- * with nonlinear inequality constraint
//--- In all three cases we:
//--- * first time solve non-convex problem with artificially moved stabilizing
//--- point and decreased initial value of Lagrange multiplier.
//--- * second time we solve problem with good stabilizing point, but zero Lagrange multiplier
//--- * last time solve same problem, but with default settings
aulits=1;
maxits=1;
rho=100.0;
n=1;
x0=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Full(n,AL_POSINF);
c=matrix<double>::Zeros(1,2);
ct.Resize(1);
c.Set(0,0,1.0);
ct.Set(0,1);
for(ckind=0; ckind<=2; ckind++)
{
CMinNLC::MinNLCCreate(n,x0,state);
state.m_stabilizingpoint=-1.0E300;
state.m_initialinequalitymultiplier=1.0E-12;
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
CMinNLC::MinNLCSetCond(state,0.0,maxits);
switch(ckind)
{
case 0:
CMinNLC::MinNLCSetBC(state,bndl,bndu);
break;
case 1:
CMinNLC::MinNLCSetLC(state,c,ct,1);
break;
case 2:
CMinNLC::MinNLCSetNLC(state,0,1);
break;
}
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,state.m_x[0]-CMath::Sqr(state.m_x[0]));
state.m_j.Set(0,0,1-2*state.m_x[0]);
if(ckind==2)
{
state.m_fi.Set(1,-state.m_x[0]);
state.m_j.Set(1,0,-1);
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:5125");
if(waserrors)
return;
CAp::SetErrorFlag(waserrors,x1[0]>(-1.0E6),"testminnlcunit.ap:5128");
CMinNLC::MinNLCCreate(n,x0,state);
state.m_stabilizingpoint=-1.0E2;
state.m_initialinequalitymultiplier=1.0E-12;
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
CMinNLC::MinNLCSetCond(state,0.0,maxits);
switch(ckind)
{
case 0:
CMinNLC::MinNLCSetBC(state,bndl,bndu);
break;
case 1:
CMinNLC::MinNLCSetLC(state,c,ct,1);
break;
case 2:
CMinNLC::MinNLCSetNLC(state,0,1);
break;
}
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,state.m_x[0]-CMath::Sqr(state.m_x[0]));
state.m_j.Set(0,0,1-2*state.m_x[0]);
if(ckind==2)
{
state.m_fi.Set(1,-state.m_x[0]);
state.m_j.Set(1,0,-1);
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:5157");
if(waserrors)
return;
CAp::SetErrorFlag(waserrors,x1[0]<(double)(3*state.m_stabilizingpoint),"testminnlcunit.ap:5160");
CMinNLC::MinNLCCreate(n,x0,state);
CMinNLC::MinNLCSetAlgoAUL(state,rho,aulits);
CMinNLC::MinNLCSetCond(state,0.0,maxits);
switch(ckind)
{
case 0:
CMinNLC::MinNLCSetBC(state,bndl,bndu);
break;
case 1:
CMinNLC::MinNLCSetLC(state,c,ct,1);
break;
case 2:
CMinNLC::MinNLCSetNLC(state,0,1);
break;
}
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,state.m_x[0]-CMath::Sqr(state.m_x[0]));
state.m_j.Set(0,0,1-2*state.m_x[0]);
if(ckind==2)
{
state.m_fi.Set(1,-state.m_x[0]);
state.m_j.Set(1,0,-1);
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:5187");
if(waserrors)
return;
CAp::SetErrorFlag(waserrors,x1[0]<(double)(3*state.m_stabilizingpoint),"testminnlcunit.ap:5190");
}
//--- This test checks report by E. Pozamantir. Relevant for SLP
//--- and related methods, but we test it for all algorithms.
//--- Description:
//--- The sequential linear programming solver performs warm-start
//--- at each iteration, i.e. it reuses previously found LP basis.
//--- However, when some initially non-zero Jacobian entries become
//--- exactly zero (possible with constraints which are nonsmooth
//--- at some distance from the boundary), our warm-start strategy
//--- may fail because warm-start basis becomes exactly degenerate.
//--- In order to test that this bug was fixed we solve carefully
//--- designed noisy test problem with Jacobian entries being randomly
//--- turned on and off. Simply being able to return from the solver
//--- without triggering critical exception is enough.
for(solvertype=0; solvertype<=m_maxsolvertype; solvertype++)
{
for(n=6; n<=15; n++)
{
//--- Setup problem
x0=vector<double>::Zeros(n);
d=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
d.Set(i,CMath::Sqr(CHighQualityRand::HQRndNormal(rs)));
}
k=1+CHighQualityRand::HQRndUniformI(rs,n/3);
c=matrix<double>::Zeros(2*k,n+1);
for(i=0; i<2*k; i++)
for(j=0; j<n; j++)
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
for(i=0; i<k; i++)
c.Set(i,n,0);
for(i=k; i<2*k; i++)
c.Set(i,n,0.1+CHighQualityRand::HQRndUniformR(rs));
//--- Create solver and solve problem
CMinNLC::MinNLCCreate(n,x0,state);
switch(solvertype)
{
case 0:
CMinNLC::MinNLCSetAlgoAUL(state,200.0,20);
break;
case 1:
CMinNLC::MinNLCSetAlgoSLP(state);
break;
case 2:
CMinNLC::MinNLCSetAlgoSQP(state);
break;
default:
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCSetCond(state,1.0E-7,20);
CMinNLC::MinNLCSetNLC(state,k,k);
while(CMinNLC::MinNLCIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0);
for(j=0; j<n; j++)
{
state.m_fi.Add(0,0.5*d[j]*CMath::Sqr(state.m_x[j]));
state.m_j.Set(0,j,d[j]*state.m_x[j]);
}
for(i=0; i<=2*k-1; i++)
{
state.m_fi.Set(1+i,-c.Get(i,n));
for(j=0; j<n; j++)
{
state.m_fi.Add(1+i,c.Get(i,j)*state.m_x[j]);
state.m_j.Set(1+i,j,CHighQualityRand::HQRndUniformI(rs,2)*c.Get(i,j));
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinNLC::MinNLCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminnlcunit.ap:5277");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminnlcunit.ap:5278");
if(waserrors)
return;
}
}
}
//+------------------------------------------------------------------+
//| This function tests report of "non-C1" test #0 for task #0 |
//| given by matrix A. |
//| On failure sets error flag. |
//+------------------------------------------------------------------+
void CTestMinNLCUnit::TestOptGuardC1Test0ReportForTask0(bool &err,
COptGuardNonC1Test0Report &rep,
CMatrixDouble &a,
int n)
{
//--- create variables
double va=0;
double vb=0;
bool hasc1discontinuities;
if(rep.m_positive)
{
//--- Check positive report, first checks
CAp::SetErrorFlag(err,rep.m_fidx!=0,"testminnlcunit.ap:5307");
CAp::SetErrorFlag(err,rep.m_n!=n,"testminnlcunit.ap:5308");
CAp::SetErrorFlag(err,!(0<=rep.m_stpidxa),"testminnlcunit.ap:5309");
CAp::SetErrorFlag(err,!(rep.m_stpidxa<rep.m_stpidxb),"testminnlcunit.ap:5310");
CAp::SetErrorFlag(err,!(rep.m_stpidxb<rep.m_cnt),"testminnlcunit.ap:5311");
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=rep.m_n,"testminnlcunit.ap:5312");
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=rep.m_n,"testminnlcunit.ap:5313");
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=rep.m_cnt,"testminnlcunit.ap:5314");
CAp::SetErrorFlag(err,CAp::Len(rep.m_f)!=rep.m_cnt,"testminnlcunit.ap:5315");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_x0,n),"testminnlcunit.ap:5316");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_d,n),"testminnlcunit.ap:5317");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_stp,rep.m_cnt),"testminnlcunit.ap:5318");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_f,rep.m_cnt),"testminnlcunit.ap:5319");
if(err)
return;
//--- Check consistency of Stp.
for(int k=0; k<=rep.m_cnt-2; k++)
CAp::SetErrorFlag(err,rep.m_stp[k]>=rep.m_stp[k+1],"testminnlcunit.ap:5327");
//--- Check that interval [#StpIdxA,#StpIdxB] contains at least one discontinuity
hasc1discontinuities=false;
for(int i=0; i<n; i++)
{
va=0;
vb=0;
for(int j=0; j<n; j++)
{
va+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[rep.m_stpidxa]);
vb+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[rep.m_stpidxb]);
}
hasc1discontinuities=hasc1discontinuities||MathSign(va)!=MathSign(vb);
}
CAp::SetErrorFlag(err,!hasc1discontinuities,"testminnlcunit.ap:5344");
}
else
{
//--- Check negative report: fields must be empty
CAp::SetErrorFlag(err,rep.m_stpidxa!=-1,"testminnlcunit.ap:5351");
CAp::SetErrorFlag(err,rep.m_stpidxb!=-1,"testminnlcunit.ap:5352");
CAp::SetErrorFlag(err,rep.m_fidx!=-1,"testminnlcunit.ap:5353");
CAp::SetErrorFlag(err,rep.m_cnt!=0,"testminnlcunit.ap:5354");
CAp::SetErrorFlag(err,rep.m_n!=0,"testminnlcunit.ap:5355");
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=0,"testminnlcunit.ap:5356");
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=0,"testminnlcunit.ap:5357");
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=0,"testminnlcunit.ap:5358");
CAp::SetErrorFlag(err,CAp::Len(rep.m_f)!=0,"testminnlcunit.ap:5359");
}
}
//+------------------------------------------------------------------+
//| This function tests report of "non-C1" test #1 for task #0 |
//| given by matrix A. |
//| On failure sets error flag. |
//+------------------------------------------------------------------+
void CTestMinNLCUnit::TestOptGuardC1Test1ReportForTask0(bool &err,
COptGuardNonC1Test1Report &rep,
CMatrixDouble &a,int n)
{
//--- create variables
double va=0;
double vb=0;
bool tooclose;
bool hasc1discontinuities;
if(rep.m_positive)
{
//--- Check positive report, first checks
CAp::SetErrorFlag(err,rep.m_fidx!=0,"testminnlcunit.ap:5387");
CAp::SetErrorFlag(err,rep.m_vidx<0,"testminnlcunit.ap:5388");
CAp::SetErrorFlag(err,rep.m_vidx>n,"testminnlcunit.ap:5389");
CAp::SetErrorFlag(err,rep.m_n!=n,"testminnlcunit.ap:5390");
CAp::SetErrorFlag(err,!(0<=rep.m_stpidxa),"testminnlcunit.ap:5391");
CAp::SetErrorFlag(err,!(rep.m_stpidxa<rep.m_stpidxb),"testminnlcunit.ap:5392");
CAp::SetErrorFlag(err,!(rep.m_stpidxb<rep.m_cnt),"testminnlcunit.ap:5393");
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=rep.m_n,"testminnlcunit.ap:5394");
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=rep.m_n,"testminnlcunit.ap:5395");
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=rep.m_cnt,"testminnlcunit.ap:5396");
CAp::SetErrorFlag(err,CAp::Len(rep.m_g)!=rep.m_cnt,"testminnlcunit.ap:5397");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_x0,n),"testminnlcunit.ap:5398");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_d,n),"testminnlcunit.ap:5399");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_stp,rep.m_cnt),"testminnlcunit.ap:5400");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_g,rep.m_cnt),"testminnlcunit.ap:5401");
if(err)
return;
//--- Check consistency of Stp
for(int k=0; k<=rep.m_cnt-2; k++)
CAp::SetErrorFlag(err,rep.m_stp[k]>=rep.m_stp[k+1],"testminnlcunit.ap:5409");
//--- Check that interval [#StpIdxA,#StpIdxB] contains at least one discontinuity
tooclose=false;
hasc1discontinuities=false;
for(int i=0; i<n; i++)
{
va=0;
vb=0;
for(int j=0; j<n; j++)
{
va+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[rep.m_stpidxa]);
vb+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[rep.m_stpidxb]);
}
tooclose=(tooclose||(double)(MathAbs(va))<(double)(1.0E-8))||(double)(MathAbs(vb))<(double)(1.0E-8);
hasc1discontinuities=hasc1discontinuities||MathSign(va)!=MathSign(vb);
}
if(!tooclose)
CAp::SetErrorFlag(err,!hasc1discontinuities,"testminnlcunit.ap:5429");
}
else
{
//--- Check negative report: fields must be empty
CAp::SetErrorFlag(err,rep.m_stpidxa!=-1,"testminnlcunit.ap:5436");
CAp::SetErrorFlag(err,rep.m_stpidxb!=-1,"testminnlcunit.ap:5437");
CAp::SetErrorFlag(err,rep.m_fidx!=-1,"testminnlcunit.ap:5438");
CAp::SetErrorFlag(err,rep.m_vidx!=-1,"testminnlcunit.ap:5439");
CAp::SetErrorFlag(err,rep.m_cnt!=0,"testminnlcunit.ap:5440");
CAp::SetErrorFlag(err,rep.m_n!=0,"testminnlcunit.ap:5441");
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=0,"testminnlcunit.ap:5442");
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=0,"testminnlcunit.ap:5443");
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=0,"testminnlcunit.ap:5444");
CAp::SetErrorFlag(err,CAp::Len(rep.m_g)!=0,"testminnlcunit.ap:5445");
}
}
//+------------------------------------------------------------------+
//| This function tests report of "non-C1" test #0 for task #1 |
//| given by matrix A. |
//| On failure sets error flag. |
//+------------------------------------------------------------------+
void CTestMinNLCUnit::TestOptGuardC1Test0ReportForTask1(bool &err,
COptGuardNonC1Test0Report &rep,
CMatrixDouble &a,
int n,
int goodidx)
{
if(rep.m_positive)
{
//--- Check positive report, first checks
CAp::SetErrorFlag(err,rep.m_fidx==goodidx+1,"testminnlcunit.ap:5469");
CAp::SetErrorFlag(err,rep.m_n!=n,"testminnlcunit.ap:5470");
CAp::SetErrorFlag(err,!(0<=rep.m_stpidxa),"testminnlcunit.ap:5471");
CAp::SetErrorFlag(err,!(rep.m_stpidxa<rep.m_stpidxb),"testminnlcunit.ap:5472");
CAp::SetErrorFlag(err,!(rep.m_stpidxb<rep.m_cnt),"testminnlcunit.ap:5473");
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=rep.m_n,"testminnlcunit.ap:5474");
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=rep.m_n,"testminnlcunit.ap:5475");
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=rep.m_cnt,"testminnlcunit.ap:5476");
CAp::SetErrorFlag(err,CAp::Len(rep.m_f)!=rep.m_cnt,"testminnlcunit.ap:5477");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_x0,n),"testminnlcunit.ap:5478");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_d,n),"testminnlcunit.ap:5479");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_stp,rep.m_cnt),"testminnlcunit.ap:5480");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_f,rep.m_cnt),"testminnlcunit.ap:5481");
if(err)
return;
}
else
{
//--- Check negative report: fields must be empty
CAp::SetErrorFlag(err,rep.m_stpidxa!=-1,"testminnlcunit.ap:5490");
CAp::SetErrorFlag(err,rep.m_stpidxb!=-1,"testminnlcunit.ap:5491");
CAp::SetErrorFlag(err,rep.m_fidx!=-1,"testminnlcunit.ap:5492");
CAp::SetErrorFlag(err,rep.m_cnt!=0,"testminnlcunit.ap:5493");
CAp::SetErrorFlag(err,rep.m_n!=0,"testminnlcunit.ap:5494");
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=0,"testminnlcunit.ap:5495");
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=0,"testminnlcunit.ap:5496");
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=0,"testminnlcunit.ap:5497");
CAp::SetErrorFlag(err,CAp::Len(rep.m_f)!=0,"testminnlcunit.ap:5498");
}
}
//+------------------------------------------------------------------+
//| This function tests report of "non-C1" test #1 for task #1 given |
//| by matrix A. |
//| On failure sets error flag. |
//+------------------------------------------------------------------+
void CTestMinNLCUnit::TestOptGuardC1Test1ReportForTask1(bool &err,
COptGuardNonC1Test1Report &rep,
CMatrixDouble &a,
int n,
int goodidx)
{
if(rep.m_positive)
{
//--- Check positive report, first checks
CAp::SetErrorFlag(err,rep.m_fidx==goodidx+1,"testminnlcunit.ap:5521");
CAp::SetErrorFlag(err,rep.m_vidx<0,"testminnlcunit.ap:5522");
CAp::SetErrorFlag(err,rep.m_vidx>n,"testminnlcunit.ap:5523");
CAp::SetErrorFlag(err,rep.m_n!=n,"testminnlcunit.ap:5524");
CAp::SetErrorFlag(err,!(0<=rep.m_stpidxa),"testminnlcunit.ap:5525");
CAp::SetErrorFlag(err,!(rep.m_stpidxa<rep.m_stpidxb),"testminnlcunit.ap:5526");
CAp::SetErrorFlag(err,!(rep.m_stpidxb<rep.m_cnt),"testminnlcunit.ap:5527");
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=rep.m_n,"testminnlcunit.ap:5528");
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=rep.m_n,"testminnlcunit.ap:5529");
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=rep.m_cnt,"testminnlcunit.ap:5530");
CAp::SetErrorFlag(err,CAp::Len(rep.m_g)!=rep.m_cnt,"testminnlcunit.ap:5531");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_x0,n),"testminnlcunit.ap:5532");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_d,n),"testminnlcunit.ap:5533");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_stp,rep.m_cnt),"testminnlcunit.ap:5534");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_g,rep.m_cnt),"testminnlcunit.ap:5535");
if(err)
return;
}
else
{
//--- Check negative report: fields must be empty
CAp::SetErrorFlag(err,rep.m_stpidxa!=-1,"testminnlcunit.ap:5544");
CAp::SetErrorFlag(err,rep.m_stpidxb!=-1,"testminnlcunit.ap:5545");
CAp::SetErrorFlag(err,rep.m_fidx!=-1,"testminnlcunit.ap:5546");
CAp::SetErrorFlag(err,rep.m_vidx!=-1,"testminnlcunit.ap:5547");
CAp::SetErrorFlag(err,rep.m_cnt!=0,"testminnlcunit.ap:5548");
CAp::SetErrorFlag(err,rep.m_n!=0,"testminnlcunit.ap:5549");
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=0,"testminnlcunit.ap:5550");
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=0,"testminnlcunit.ap:5551");
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=0,"testminnlcunit.ap:5552");
CAp::SetErrorFlag(err,CAp::Len(rep.m_g)!=0,"testminnlcunit.ap:5553");
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestMinNSUnit
{
public:
static const double m_scalingtesttol;
static const int m_scalingtestcnt;
static bool TestMinNS(bool silent);
private:
static void BasicTest0UC(bool &errors);
static void BasicTest1UC(bool &errors);
static void BasicTest0BC(bool &errors);
static void BasicTest1BC(bool &errors);
static void BasicTest0LC(bool &errors);
static void BasicTest1LC(bool &errors);
static void BasicTest0NLC(bool &errors);
static void TestUC(bool &primaryerrors,bool &othererrors);
static void TestBC(bool &primaryerrors,bool &othererrors);
static void TestLC(bool &primaryerrors,bool &othererrors);
static void TestNLC(bool &primaryerrors,bool &othererrors);
static void TestOther(bool &othererrors);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
const double CTestMinNSUnit::m_scalingtesttol=1.0E-6;
const int CTestMinNSUnit::m_scalingtestcnt=3;
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestMinNSUnit::TestMinNS(bool silent)
{
//--- create variables
bool result;
bool waserrors;
bool ucerrors;
bool bcerrors;
bool lcerrors;
bool nlcerrors;
bool othererrors;
waserrors=false;
ucerrors=false;
bcerrors=false;
lcerrors=false;
nlcerrors=false;
othererrors=false;
//--- Basic tests
BasicTest0NLC(nlcerrors);
BasicTest0UC(ucerrors);
BasicTest1UC(ucerrors);
BasicTest0BC(bcerrors);
BasicTest1BC(bcerrors);
BasicTest0LC(lcerrors);
BasicTest1LC(lcerrors);
//--- Special tests
TestOther(othererrors);
//--- Full scale tests
TestUC(ucerrors,othererrors);
TestBC(bcerrors,othererrors);
TestLC(lcerrors,othererrors);
TestNLC(nlcerrors,othererrors);
//--- end
waserrors=(((ucerrors||bcerrors)||lcerrors)||nlcerrors)||othererrors;
if(!silent)
{
Print("TESTING MINNS OPTIMIZATION");
Print("TESTS:");
PrintResult("* UNCONSTRAINED",!ucerrors);
PrintResult("* BOUND CONSTRAINED",!bcerrors);
PrintResult("* LINEARLY CONSTRAINED",!lcerrors);
PrintResult("* NONLINEARLY CONSTRAINED",!nlcerrors);
PrintResult("* OTHER PROPERTIES",!othererrors);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| Basic unconstrained test |
//+------------------------------------------------------------------+
void CTestMinNSUnit::BasicTest0UC(bool &errors)
{
//--- create variables
int n=5;
int passcount=10;
int i=0;
CRowDouble x0;
CRowDouble x1;
CRowDouble d;
CMinNSState s;
CMinNSReport rep;
double sumits=0;
double sumnfev=0;
int pass=0;
x0=vector<double>::Zeros(n);
d=vector<double>::Zeros(n);
for(pass=1; pass<=10; pass++)
{
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
d.Set(i,MathPow(10,2*CMath::RandomReal()-1));
}
CMinNS::MinNSCreate(n,x0,s);
CMinNS::MinNSSetAlgoAGS(s,0.1,0.0);
while(CMinNS::MinNSIteration(s))
{
if(s.m_needfij)
{
s.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
s.m_fi.Add(0,d[i]*MathAbs(s.m_x[i]));
s.m_j.Set(0,i,d[i]*MathSign(s.m_x[i]));
}
continue;
}
CAp::Assert(false);
errors=true;
return;
}
CMinNS::MinNSResults(s,x1,rep);
CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:143");
if(errors)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[i]) || MathAbs(x1[i])>0.001,"testminnsunit.ap:147");
sumits+=(double)rep.m_iterationscount/(double)passcount;
sumnfev+=(double)rep.m_nfev/(double)passcount;
}
}
//+--------------------------------------------------------------------+
//| Basic unconstrained test: nonsmooth Rosenbrock posed as |
//| unconstrained problem. |
//| [ ]|
//|min[10*|x0^2-x1|+(1-x0)^2+100*max(sqrt(2)*x0-1,0)+100*max(2*x1-1,0)]|
//| [ ]|
//| It's exact solution is x0=1/sqrt(2), x1=1/2 |
//+--------------------------------------------------------------------+
void CTestMinNSUnit::BasicTest1UC(bool &errors)
{
//--- create variables
int n=2;
double v0=0;
double v1=0;
CRowDouble x0;
CRowDouble x1;
CMinNSState s;
CMinNSReport rep;
x0=vector<double>::Zeros(n);
CMinNS::MinNSCreate(n,x0,s);
CMinNS::MinNSSetAlgoAGS(s,0.1,0.0);
while(CMinNS::MinNSIteration(s))
{
if(s.m_needfij)
{
v0=s.m_x[0];
v1=s.m_x[1];
s.m_fi.Set(0,10*MathAbs(CMath::Sqr(v0)-v1)+CMath::Sqr(v0-1));
s.m_j.Set(0,0,10*MathSign(CMath::Sqr(v0)-v1)*2*v0+2*(v0-1));
s.m_j.Set(0,1,10*MathSign(CMath::Sqr(v0)-v1)*-1);
if((MathSqrt(2.0)*v0-1.0)>0.0)
{
s.m_fi.Add(0,100*(MathSqrt(2)*v0-1));
s.m_j.Add(0,0,100*MathSqrt(2));
}
if((2.0*v1-1.0)>0.0)
{
s.m_fi.Add(0,100.0*(2.0*v1-1.0));
s.m_j.Add(0,1,100.0*2.0);
}
continue;
}
CAp::Assert(false);
errors=true;
return;
}
CMinNS::MinNSResults(s,x1,rep);
CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:204");
if(errors)
return;
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[0]) || (MathAbs(x1[0]-1.0/MathSqrt(2.0)))>0.001,"testminnsunit.ap:207");
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[1]) || (MathAbs(x1[1]-0.5))>0.001,"testminnsunit.ap:208");
}
//+------------------------------------------------------------------+
//| Basic box constrained test |
//+------------------------------------------------------------------+
void CTestMinNSUnit::BasicTest0BC(bool &errors)
{
//--- create variables
int n=5;
int passcount=10;
int pass=0;
int i=0;
CRowDouble x0;
CRowDouble x1;
CRowDouble d;
CRowDouble bl;
CRowDouble bu;
CMinNSState s;
CMinNSReport rep;
double sumits=0;
double sumnfev=0;
double v0=0;
double v1=0;
x0=vector<double>::Zeros(n);
bl=vector<double>::Zeros(n);
bu=vector<double>::Zeros(n);
d=vector<double>::Zeros(n);
for(pass=1; pass<=10; pass++)
{
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
d.Set(i,MathPow(10,2*CMath::RandomReal()-1));
v0=2*CMath::RandomReal()-1;
v1=2*CMath::RandomReal()-1;
bl.Set(i,MathMin(v0,v1));
bu.Set(i,MathMax(v0,v1));
}
CMinNS::MinNSCreate(n,x0,s);
CMinNS::MinNSSetAlgoAGS(s,0.1,0.0);
CMinNS::MinNSSetBC(s,bl,bu);
while(CMinNS::MinNSIteration(s))
{
if(s.m_needfij)
{
s.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
s.m_fi.Add(0,d[i]*MathAbs(s.m_x[i]));
s.m_j.Set(0,i,d[i]*MathSign(s.m_x[i]));
}
continue;
}
CAp::Assert(false);
errors=true;
return;
}
CMinNS::MinNSResults(s,x1,rep);
CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:263");
if(errors)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[i]) || (double)(MathAbs(x1[i]-CApServ::BoundVal(0.0,bl[i],bu[i])))>0.001,"testminnsunit.ap:267");
sumits+=(double)rep.m_iterationscount/(double)passcount;
sumnfev+=(double)rep.m_nfev/(double)passcount;
}
}
//+------------------------------------------------------------------+
//| Basic constrained test: nonsmooth Rosenbrock posed as box |
//| constrained problem. |
//| [ ] |
//| minimize [ 10*|x0^2-x1| + (1-x0)^2 ] |
//| [ ] |
//| s.t. x0<=1/sqrt(2), x1<=0.5 |
//| It's exact solution is x0=1/sqrt(2), x1=1/2 |
//+------------------------------------------------------------------+
void CTestMinNSUnit::BasicTest1BC(bool &errors)
{
//--- create variables
int n=2;
double v0=0;
double v1=0;
CRowDouble x0;
CRowDouble x1;
CRowDouble bndl;
CRowDouble bndu;
CMinNSState s;
CMinNSReport rep;
x0=vector<double>::Zeros(n);
bndl=vector<double>::Full(n,AL_NEGINF);
bndu=vector<double>::Zeros(n);
bndu.Set(0,1.0/MathSqrt(2));
bndu.Set(1,0.5);
CMinNS::MinNSCreate(n,x0,s);
CMinNS::MinNSSetBC(s,bndl,bndu);
CMinNS::MinNSSetAlgoAGS(s,0.1,0.0);
while(CMinNS::MinNSIteration(s))
{
if(s.m_needfij)
{
v0=s.m_x[0];
v1=s.m_x[1];
s.m_fi.Set(0,10*MathAbs(CMath::Sqr(v0)-v1)+CMath::Sqr(v0-1));
s.m_j.Set(0,0,10*MathSign(CMath::Sqr(v0)-v1)*2*v0+2*(v0-1));
s.m_j.Set(0,1,10*MathSign(CMath::Sqr(v0)-v1)*-1);
continue;
}
CAp::Assert(false);
errors=true;
return;
}
CMinNS::MinNSResults(s,x1,rep);
CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:323");
if(errors)
return;
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[0]) || (double)(MathAbs(x1[0]-1/MathSqrt(2)))>0.001,"testminnsunit.ap:326");
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[1]) || (double)(MathAbs(x1[1]-0.5))>0.001,"testminnsunit.ap:327");
}
//+------------------------------------------------------------------+
//| Basic linearly constrained test |
//+------------------------------------------------------------------+
void CTestMinNSUnit::BasicTest0LC(bool &errors)
{
//--- create variables
double d=-10.0;
int n=5;
int passcount=10;
int pass=0;
int i=0;
int j=0;
CRowDouble x0;
CRowDouble x1;
CMatrixDouble c;
CRowInt ct;
CMinNSState s;
CMinNSReport rep;
double sumits=0;
double sumnfev=0;
int nc=0;
x0=vector<double>::Zeros(n);
c=matrix<double>::Zeros(2*n,n+1);
ct.Resize(2*n);
for(pass=1; pass<=10; pass++)
{
nc=0;
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
if(CMath::RandomReal()<0.5)
{
for(j=0; j<=n; j++)
c.Set(nc,j,0.0);
c.Set(nc,i,1.0+CMath::RandomReal());
ct.Set(nc,0);
nc++;
}
else
{
for(j=0; j<=n; j++)
{
c.Set(nc+0,j,0.0);
c.Set(nc+1,j,0.0);
}
c.Set(nc+0,i,1.0+CMath::RandomReal());
c.Set(nc+1,i,1.0+CMath::RandomReal());
ct.Set(nc+0,1);
ct.Set(nc+1,-1);
nc+=2;
}
}
CMinNS::MinNSCreate(n,x0,s);
CMinNS::MinNSSetAlgoAGS(s,0.1,0.0);
CMinNS::MinNSSetLC(s,c,ct,nc);
while(CMinNS::MinNSIteration(s))
{
if(s.m_needfij)
{
s.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
s.m_fi.Set(0,d*CMath::Sqr(s.m_x[i]));
s.m_j.Set(0,i,d*2*s.m_x[i]);
}
continue;
}
CAp::Assert(false);
errors=true;
return;
}
CMinNS::MinNSResults(s,x1,rep);
CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:404");
if(errors)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[i]) || MathAbs(x1[i])>0.001,"testminnsunit.ap:408");
sumits+=(double)rep.m_iterationscount/(double)passcount;
sumnfev+=(double)rep.m_nfev/(double)passcount;
}
}
//+------------------------------------------------------------------+
//| Basic constrained test: nonsmooth Rosenbrock posed as linearly |
//| constrained problem. |
//| [ ] |
//| minimize [ 10*|x0^2-x1| + (1-x0)^2 ] |
//| [ ] |
//| s.t. x0<=1/sqrt(2), x1<=0.5 |
//| It's exact solution is x0=1/sqrt(2), x1=1/2 |
//+------------------------------------------------------------------+
void CTestMinNSUnit::BasicTest1LC(bool &errors)
{
//--- create variables
int n=2;
double v0=0;
double v1=0;
CRowDouble x0;
CRowDouble x1;
CMatrixDouble c;
CRowInt ct;
CMinNSState s;
CMinNSReport rep;
x0=vector<double>::Zeros(n);
c=matrix<double>::Zeros(2,n+1);
ct.Resize(2);
c.Set(0,0,1.0);
c.Set(0,2,1/MathSqrt(2));
c.Set(1,1,1.0);
c.Set(1,2,0.5);
ct.Set(0,-1);
ct.Set(1,-1);
CMinNS::MinNSCreate(n,x0,s);
CMinNS::MinNSSetLC(s,c,ct,2);
CMinNS::MinNSSetAlgoAGS(s,0.1,0.0);
while(CMinNS::MinNSIteration(s))
{
if(s.m_needfij)
{
v0=s.m_x[0];
v1=s.m_x[1];
s.m_fi.Set(0,10*MathAbs(CMath::Sqr(v0)-v1)+CMath::Sqr(v0-1));
s.m_j.Set(0,0,10*MathSign(CMath::Sqr(v0)-v1)*2*v0+2*(v0-1));
s.m_j.Set(0,1,10*MathSign(CMath::Sqr(v0)-v1)*-1);
continue;
}
CAp::Assert(false);
errors=true;
return;
}
CMinNS::MinNSResults(s,x1,rep);
CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:470");
if(errors)
return;
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[0]) || (double)(MathAbs(x1[0]-1/MathSqrt(2)))>0.001,"testminnsunit.ap:473");
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[1]) || (double)(MathAbs(x1[1]-0.5))>0.001,"testminnsunit.ap:474");
}
//+------------------------------------------------------------------+
//| Basic nonlinearly constrained test |
//+------------------------------------------------------------------+
void CTestMinNSUnit::BasicTest0NLC(bool &errors)
{
//--- create variables
double d=-10.0;
int n=5;
int passcount=10;
int pass=0;
int i=0;
int j=0;
CRowDouble x0;
CRowDouble x1;
CMatrixDouble ec;
CMatrixDouble ic;
int nec=0;
int nic=0;
CMinNSState s;
CMinNSReport rep;
double sumits=0;
double sumnfev=0;
x0=vector<double>::Zeros(n);
ec=matrix<double>::Zeros(2*n,n+1);
ic=matrix<double>::Zeros(2*n,n+1);
for(pass=1; pass<=10; pass++)
{
nec=0;
nic=0;
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
if(CMath::RandomReal()<0.5)
{
for(j=0; j<=n; j++)
ec.Set(nec,j,0.0);
ec.Set(nec,i,1.0+CMath::RandomReal());
nec++;
}
else
{
for(j=0; j<=n; j++)
{
ic.Set(nic+0,j,0.0);
ic.Set(nic+1,j,0.0);
}
ic.Set(nic+0,i,1.0+CMath::RandomReal());
ic.Set(nic+1,i,-1.0-CMath::RandomReal());
nic+=2;
}
}
CMinNS::MinNSCreate(n,x0,s);
CMinNS::MinNSSetAlgoAGS(s,0.1,100.0);
CMinNS::MinNSSetNLC(s,nec,nic);
while(CMinNS::MinNSIteration(s))
{
if(s.m_needfij)
{
s.m_fi.Set(0,0.0);
for(j=0; j<n; j++)
{
s.m_fi.Set(0,d*CMath::Sqr(s.m_x[j]));
s.m_j.Set(0,j,d*2*s.m_x[j]);
}
for(i=0; i<nec ; i++)
{
s.m_fi.Set(1+i,-ec.Get(i,n));
for(j=0; j<n; j++)
{
s.m_fi.Add(1+i,s.m_x[j]*ec.Get(i,j));
s.m_j.Set(1+i,j,ec.Get(i,j));
}
}
for(i=0; i<nic ; i++)
{
s.m_fi.Set(1+nec+i,-ic.Get(i,n));
for(j=0; j<n; j++)
{
s.m_fi.Add(1+nec+i,s.m_x[j]*ic.Get(i,j));
s.m_j.Set(1+nec+i,j,ic.Get(i,j));
}
}
continue;
}
CAp::Assert(false);
errors=true;
return;
}
CMinNS::MinNSResults(s,x1,rep);
CAp::SetErrorFlag(errors,rep.m_terminationtype<=0,"testminnsunit.ap:567");
if(errors)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(errors,!MathIsValidNumber(x1[i]) || MathAbs(x1[i])>0.001,"testminnsunit.ap:571");
sumits+=(double)rep.m_iterationscount/(double)passcount;
sumnfev+=(double)rep.m_nfev/(double)passcount;
}
}
//+------------------------------------------------------------------+
//| Unconstrained test |
//+------------------------------------------------------------------+
void CTestMinNSUnit::TestUC(bool &primaryerrors,bool &othererrors)
{
//--- create variables
int n=0;
int i=0;
CRowDouble x0;
CRowDouble x0s;
CRowDouble x1;
CRowDouble x1s;
CRowDouble d;
CRowDouble xc;
CRowDouble s;
CRowDouble xrfirst;
CRowDouble xrlast;
CMinNSState state;
CMinNSReport rep;
double v=0;
int pass=0;
bool requirexrep;
double epsrad=0;
bool werexreports;
double repferr=0;
double xtol=0;
int i_=0;
for(pass=1; pass<=10; pass++)
{
for(n=1; n<=5; n++)
{
//--- First test:
//--- * test that problem is successfully solved
//--- * test that X-reports are performed correctly - present
//--- when requested, return first and last points correctly,
//--- not present by default, function value is reported
//--- correctly.
//--- * we use non-unit scale, randomly chosen one, which results
//--- in badly conditioned problems (to check robustness)
x0=vector<double>::Zeros(n);
xc=vector<double>::Zeros(n);
d=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
xrfirst=vector<double>::Zeros(n);
xrlast=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,10*(2*CMath::RandomReal()-1));
xc.Set(i,2*CMath::RandomReal()-1);
d.Set(i,MathPow(10,2*(2*CMath::RandomReal()-1)));
s.Set(i,MathPow(10,2*(2*CMath::RandomReal()-1)));
}
requirexrep=CMath::RandomReal()>0.5;
xtol=0.01*MathPow(10,-(1*CMath::RandomReal()));
epsrad=xtol/100;
CMinNS::MinNSCreate(n,x0,state);
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
CMinNS::MinNSSetCond(state,epsrad,0);
CMinNS::MinNSSetScale(state,s);
if(requirexrep)
CMinNS::MinNSSetXRep(state,true);
werexreports=false;
repferr=0.0;
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]-xc[i]));
}
continue;
}
if(state.m_xupdated)
{
if(!werexreports)
{
for(i_=0; i_<n; i_++)
xrfirst.Set(i_,state.m_x[i_]);
}
for(i_=0; i_<n; i_++)
xrlast.Set(i_,state.m_x[i_]);
werexreports=true;
v=0.0;
for(i=0; i<n; i++)
v+=d[i]*MathAbs(state.m_x[i]-xc[i]);
repferr=MathMax(repferr,MathAbs(v-state.m_f));
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:668");
CAp::SetErrorFlag(othererrors,werexreports && !requirexrep,"testminnsunit.ap:669");
CAp::SetErrorFlag(othererrors,requirexrep && !werexreports,"testminnsunit.ap:670");
CAp::SetErrorFlag(othererrors,repferr>(10000.0*CMath::m_machineepsilon),"testminnsunit.ap:671");
if(primaryerrors||othererrors)
return;
//---
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]) || (double)(MathAbs(x1[i]-xc[i])/s[i])>xtol,"testminnsunit.ap:676");
if(requirexrep)
{
CAp::SetErrorFlag(othererrors,!MathIsValidNumber(xrfirst[i]) || (double)(MathAbs(x0[i]-xrfirst[i]))>(100.0*CMath::m_machineepsilon),"testminnsunit.ap:679");
CAp::SetErrorFlag(othererrors,!MathIsValidNumber(xrlast[i]) || (double)(MathAbs(x1[i]-xrlast[i]))>(100.0*CMath::m_machineepsilon),"testminnsunit.ap:680");
}
}
//--- Test numerical differentiation:
//--- * test that problem is successfully solved
//--- * test that correct function value is reported
x0=vector<double>::Zeros(n);
xc=vector<double>::Zeros(n);
d=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
xrlast=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,10*(2*CMath::RandomReal()-1));
xc.Set(i,2*CMath::RandomReal()-1);
d.Set(i,MathPow(10,2*(2*CMath::RandomReal()-1)));
s.Set(i,MathPow(10,2*(2*CMath::RandomReal()-1)));
}
xtol=0.01*MathPow(10,-(1*CMath::RandomReal()));
epsrad=xtol/100;
CMinNS::MinNSCreateF(n,x0,epsrad/100,state);
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
CMinNS::MinNSSetCond(state,epsrad,0);
CMinNS::MinNSSetScale(state,s);
CMinNS::MinNSSetXRep(state,true);
repferr=0.0;
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfi)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
continue;
}
if(state.m_xupdated)
{
v=0.0;
for(i=0; i<n; i++)
v+=d[i]*MathAbs(state.m_x[i]-xc[i]);
repferr=MathMax(repferr,MathAbs(v-state.m_f));
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:731");
CAp::SetErrorFlag(othererrors,repferr>(10000.0*CMath::m_machineepsilon),"testminnsunit.ap:732");
if(primaryerrors || othererrors)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]) || (double)(MathAbs(x1[i]-xc[i])/s[i])>xtol,"testminnsunit.ap:736");
//--- Test scaling: we perform several steps on unit-scale problem,
//--- then we perform same amount of steps on re-scaled problem,
//--- starting from same point (but scaled according to chosen scale).
//--- Correctly written optimizer should perform essentially same steps
//--- (up to scale) on both problems. At least, it holds within first
//--- several steps, before rounding errors start to accumulate.
//--- NOTE: we also check that correctly scaled points are reported.
//--- And, as side effect, we check MinNSRestartFrom().
//--- NOTE: we use moderate scale and diagonal coefficients in order
//--- to have well-conditioned system. We test correctness of
//--- formulae here, not robustness of algorithm.
x0=vector<double>::Zeros(n);
xc=vector<double>::Zeros(n);
x0s=vector<double>::Zeros(n);
d=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
xrlast=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
s.Set(i,MathPow(10,2.0*CMath::RandomReal()-1.0));
d.Set(i,MathPow(10,2.0*CMath::RandomReal()-1.0));
x0.Set(i,2.0*CMath::RandomReal()-1.0);
xc.Set(i,2.0*CMath::RandomReal()-1.0);
}
x0s=x0*s+0;
//---
CMinNS::MinNSCreate(n,x0,state);
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
CMinNS::MinNSSetCond(state,0.0,m_scalingtestcnt);
CMinNS::MinNSSetXRep(state,false);
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]-xc[i]));
}
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:788");
if(primaryerrors || othererrors)
return;
CMinNS::MinNSSetScale(state,s);
CMinNS::MinNSSetXRep(state,true);
CMinNS::MinNSRestartFrom(state,x0s);
werexreports=false;
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]/s[i]-xc[i]));
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]/s[i]-xc[i])/s[i]);
}
continue;
}
if(state.m_xupdated)
{
for(i_=0; i_<n; i_++)
xrlast.Set(i_,state.m_x[i_]);
werexreports=true;
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1s,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:818");
if(primaryerrors || othererrors)
return;
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(primaryerrors,(!MathIsValidNumber(x1[i]) || !MathIsValidNumber(x1s[i]) || (double)(MathAbs(x1[i]-x1s[i]/s[i]))>(double)(1.0E-4)),"testminnsunit.ap:823");
CAp::SetErrorFlag(othererrors,!MathIsValidNumber(xrlast[i]) || (double)(MathAbs(x1s[i]-xrlast[i]))>m_scalingtesttol,"testminnsunit.ap:827");
}
}
}
}
//+------------------------------------------------------------------+
//| Box constrained test |
//+------------------------------------------------------------------+
void CTestMinNSUnit::TestBC(bool &primaryerrors,bool &othererrors)
{
//--- create variables
int passcount=10;
int maxn=5;
int n=0;
int i=0;
int j=0;
int k=0;
CRowDouble x0;
CRowDouble x0s;
CRowDouble x1;
CRowDouble x1s;
CRowDouble b;
CRowDouble d;
CRowDouble xc;
CRowDouble s;
CRowDouble bndl;
CRowDouble bndu;
CRowDouble scaledbndl;
CRowDouble scaledbndu;
CRowDouble xrfirst;
CRowDouble xrlast;
CMatrixDouble a;
CMinNSState state;
CMinNSReport rep;
double v=0;
double v0=0;
double v1=0;
int pass=0;
bool requirexrep;
double epsrad=0;
bool werexreports;
double repferr=0;
double xtol=0;
double conda=0;
double gnorm=0;
int i_=0;
//--- First test:
//--- * sparse function
//--- * test that problem is successfully solved
//--- * non-unit scale is used, which results in badly conditioned problem
//--- * check that all iterates are feasible (box-constrained)
for(pass=1; pass<=passcount; pass++)
{
for(n=1; n<=maxn; n++)
{
x0=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
xc=vector<double>::Zeros(n);
d=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
xrfirst=vector<double>::Zeros(n);
xrlast=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,10*(2*CMath::RandomReal()-1));
xc.Set(i,2*CMath::RandomReal()-1);
d.Set(i,MathPow(10,2*(2*CMath::RandomReal()-1)));
s.Set(i,MathPow(10,2*(2*CMath::RandomReal()-1)));
bndl.Set(i,AL_NEGINF);
bndu.Set(i,AL_POSINF);
k=CMath::RandomInteger(5);
if(k==1)
bndl.Set(i,2*CMath::RandomReal()-1);
if(k==2)
bndu.Set(i,2*CMath::RandomReal()-1);
if(k==3)
{
v0=2*CMath::RandomReal()-1;
v1=2*CMath::RandomReal()-1;
bndl.Set(i,MathMin(v0,v1));
bndu.Set(i,MathMax(v0,v1));
}
if(k==4)
{
bndl.Set(i,2*CMath::RandomReal()-1);
bndu.Set(i,bndl[i]);
}
}
requirexrep=CMath::RandomReal()>0.5;
xtol=0.01*MathPow(10,-(1*CMath::RandomReal()));
epsrad=xtol/100;
CMinNS::MinNSCreate(n,x0,state);
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
CMinNS::MinNSSetCond(state,epsrad,0);
CMinNS::MinNSSetBC(state,bndl,bndu);
CMinNS::MinNSSetScale(state,s);
if(requirexrep)
CMinNS::MinNSSetXRep(state,true);
werexreports=false;
repferr=0.0;
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]-xc[i]));
}
continue;
}
if(state.m_xupdated)
{
if(!werexreports)
{
for(i_=0; i_<n; i_++)
xrfirst.Set(i_,state.m_x[i_]);
}
for(i_=0; i_<n; i_++)
xrlast.Set(i_,state.m_x[i_]);
werexreports=true;
v=0.0;
for(i=0; i<n; i++)
v+=d[i]*MathAbs(state.m_x[i]-xc[i]);
repferr=MathMax(repferr,MathAbs(v-state.m_f));
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(primaryerrors,state.m_x[i]<bndl[i],"testminnsunit.ap:944");
CAp::SetErrorFlag(primaryerrors,state.m_x[i]>bndu[i],"testminnsunit.ap:945");
}
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:953");
CAp::SetErrorFlag(othererrors,werexreports && !requirexrep,"testminnsunit.ap:954");
CAp::SetErrorFlag(othererrors,requirexrep && !werexreports,"testminnsunit.ap:955");
CAp::SetErrorFlag(othererrors,repferr>(10000.0*CMath::m_machineepsilon),"testminnsunit.ap:956");
if(primaryerrors || othererrors)
return;
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]) || (double)(MathAbs(x1[i]-CApServ::BoundVal(xc[i],bndl[i],bndu[i]))/s[i])>xtol,"testminnsunit.ap:961");
CAp::SetErrorFlag(primaryerrors,x1[i]<bndl[i],"testminnsunit.ap:962");
CAp::SetErrorFlag(primaryerrors,x1[i]>bndu[i],"testminnsunit.ap:963");
if(requirexrep)
{
CAp::SetErrorFlag(othererrors,!MathIsValidNumber(xrfirst[i]) || (double)(MathAbs(CApServ::BoundVal(x0[i],bndl[i],bndu[i])-xrfirst[i]))>(100.0*CMath::m_machineepsilon),"testminnsunit.ap:966");
CAp::SetErrorFlag(othererrors,!MathIsValidNumber(xrlast[i]) || (double)(MathAbs(x1[i]-xrlast[i]))>(100.0*CMath::m_machineepsilon),"testminnsunit.ap:967");
}
}
}
}
//--- A bit harder test:
//--- * dense quadratic function (smooth), may be prone to different
//--- rounding-related issues
//--- * non-negativity box constraints
//--- * unit scale is used
//--- * extreme stopping criteria (EpsX=1.0E-12)
//--- * single pass for each problem size
//--- * check that constrained gradient at solution is small
conda=1.0E3;
epsrad=1.0E-12;
for(n=1; n<=10; n++)
{
x0=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
b=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,1.0);
b.Set(i,CMath::RandomReal()-0.5);
bndl.Set(i,0.0);
bndu.Set(i,AL_POSINF);
}
CMatGen::SPDMatrixRndCond(n,conda,a);
CMinNS::MinNSCreate(n,x0,state);
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
CMinNS::MinNSSetCond(state,epsrad,0);
CMinNS::MinNSSetBC(state,bndl,bndu);
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
state.m_j.Set(0,i,0.0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,b[i]*state.m_x[i]);
for(j=0; j<n; j++)
state.m_fi.Add(0,0.5*state.m_x[i]*a.Get(i,j)*state.m_x[j]);
}
for(i=0; i<n; i++)
state.m_j.Add(0,i,b[i]+CAblasF::RDotVR(n,state.m_x,a,i));
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1030");
if(primaryerrors || othererrors)
return;
gnorm=0.0;
for(i=0; i<n; i++)
{
v=b[i]+CAblasF::RDotVR(n,x1,a,i);
if(x1[i]==bndl[i] && v>0.0)
v=0;
if(x1[i]==bndu[i] && v<0.0)
v=0;
gnorm+=CMath::Sqr(v);
CAp::SetErrorFlag(primaryerrors,x1[i]<bndl[i],"testminnsunit.ap:1045");
CAp::SetErrorFlag(primaryerrors,x1[i]>bndu[i],"testminnsunit.ap:1046");
}
gnorm=MathSqrt(gnorm);
CAp::SetErrorFlag(primaryerrors,gnorm>(1.0E-4),"testminnsunit.ap:1049");
}
//--- Test on HIGHLY nonconvex bound constrained problem.
//--- Algorithm should be able to Stop.
//--- NOTE: because algorithm can be attracted to saddle points,
//--- x[i] may be -1, +1 or approximately zero.
for(pass=1; pass<=passcount; pass++)
{
for(n=1; n<=maxn; n++)
{
x0=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,CMath::RandomReal()-0.5);
bndl.Set(i,-1.0);
bndu.Set(i,1.0);
}
epsrad=0.0001;
xtol=15.0*epsrad;
CMinNS::MinNSCreate(n,x0,state);
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
CMinNS::MinNSSetCond(state,epsrad,0);
CMinNS::MinNSSetBC(state,bndl,bndu);
v=-1000.0;
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
v0=MathAbs(state.m_x[i]);
v1=MathSign(state.m_x[i]);
state.m_fi.Add(0,v*(v0+v0*v0));
state.m_j.Set(0,i,v*(v1+2*v0*v1));
}
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1095");
for(i=0; i<n; i++)
{
v=MathAbs(x1[i]);
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]),"testminnsunit.ap:1099");
CAp::SetErrorFlag(primaryerrors,v!=1.0 && v>xtol,"testminnsunit.ap:1100");
}
}
}
//--- Test numerical differentiation:
//--- * test that problem is successfully solved
//--- * test that correct function value is reported
//--- * test that all iterates are within bound-constrained area
for(pass=1; pass<=passcount; pass++)
{
for(n=1; n<=maxn; n++)
{
x0=vector<double>::Zeros(n);
xc=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
d=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
xrlast=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,10*(2*CMath::RandomReal()-1));
xc.Set(i,2*CMath::RandomReal()-1);
d.Set(i,MathPow(10,2*(2*CMath::RandomReal()-1)));
s.Set(i,MathPow(10,2*(2*CMath::RandomReal()-1)));
bndl.Set(i,AL_NEGINF);
bndu.Set(i,AL_POSINF);
k=CMath::RandomInteger(5);
if(k==1)
bndl.Set(i,2*CMath::RandomReal()-1);
if(k==2)
bndu.Set(i,2*CMath::RandomReal()-1);
if(k==3)
{
v0=2*CMath::RandomReal()-1;
v1=2*CMath::RandomReal()-1;
bndl.Set(i,MathMin(v0,v1));
bndu.Set(i,MathMax(v0,v1));
}
if(k==4)
{
bndl.Set(i,2*CMath::RandomReal()-1);
bndu.Set(i,bndl[i]);
}
}
xtol=0.01*MathPow(10,-(2*CMath::RandomReal()));
epsrad=xtol/100;
CMinNS::MinNSCreateF(n,x0,epsrad/100,state);
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
CMinNS::MinNSSetCond(state,epsrad,0);
CMinNS::MinNSSetScale(state,s);
CMinNS::MinNSSetBC(state,bndl,bndu);
CMinNS::MinNSSetXRep(state,true);
repferr=0.0;
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfi)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
CAp::SetErrorFlag(primaryerrors,state.m_x[i]<bndl[i],"testminnsunit.ap:1164");
CAp::SetErrorFlag(primaryerrors,state.m_x[i]>bndu[i],"testminnsunit.ap:1165");
}
continue;
}
if(state.m_xupdated)
{
v=0.0;
for(i=0; i<n; i++)
{
v+=d[i]*MathAbs(state.m_x[i]-xc[i]);
CAp::SetErrorFlag(primaryerrors,state.m_x[i]<bndl[i],"testminnsunit.ap:1176");
CAp::SetErrorFlag(primaryerrors,state.m_x[i]>bndu[i],"testminnsunit.ap:1177");
}
repferr=MathMax(repferr,MathAbs(v-state.m_f));
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1186");
CAp::SetErrorFlag(othererrors,repferr>(10000.0*CMath::m_machineepsilon),"testminnsunit.ap:1187");
if(primaryerrors || othererrors)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]) || (double)(MathAbs(x1[i]-CApServ::BoundVal(xc[i],bndl[i],bndu[i]))/s[i])>xtol,"testminnsunit.ap:1191");
}
}
//--- Test scaling: we perform several steps on unit-scale problem,
//--- then we perform same amount of steps on re-scaled problem,
//--- starting from same point (but scaled according to chosen scale).
//--- Correctly written optimizer should perform essentially same steps
//--- (up to scale) on both problems. At least, it holds within first
//--- several steps, before rounding errors start to accumulate.
//--- NOTE: we also check that correctly scaled points are reported.
//--- And, as side effect, we check MinNSRestartFrom().
//--- NOTE: we use very low scale and diagonal coefficients in order
//--- to have well-conditioned system. We test correctness of
//--- formulae here, not robustness of algorithm.
for(pass=1; pass<=passcount; pass++)
{
for(n=1; n<=maxn; n++)
{
x0=vector<double>::Zeros(n);
xc=vector<double>::Zeros(n);
x0s=vector<double>::Zeros(n);
d=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
xrlast=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
scaledbndl=vector<double>::Zeros(n);
scaledbndu=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
s.Set(i,MathPow(2,CMath::RandomInteger(5)-2));
d.Set(i,MathPow(10,CMath::RandomReal()-0.5));
x0.Set(i,2*CMath::RandomReal()-1);
xc.Set(i,2*CMath::RandomReal()-1);
x0s.Set(i,x0[i]*s[i]);
bndl.Set(i,AL_NEGINF);
bndu.Set(i,AL_POSINF);
k=CMath::RandomInteger(5);
if(k==1)
bndl.Set(i,2*CMath::RandomReal()-1);
if(k==2)
bndu.Set(i,2*CMath::RandomReal()-1);
if(k==3)
{
v0=2*CMath::RandomReal()-1;
v1=2*CMath::RandomReal()-1;
bndl.Set(i,MathMin(v0,v1));
bndu.Set(i,MathMax(v0,v1));
}
if(k==4)
{
bndl.Set(i,2*CMath::RandomReal()-1);
bndu.Set(i,bndl[i]);
}
scaledbndl.Set(i,bndl[i]*s[i]);
scaledbndu.Set(i,bndu[i]*s[i]);
}
CMinNS::MinNSCreate(n,x0,state);
CMinNS::MinNSSetAlgoAGS(state,0.01,0.0);
CMinNS::MinNSSetCond(state,0.0,m_scalingtestcnt);
CMinNS::MinNSSetBC(state,bndl,bndu);
CMinNS::MinNSSetXRep(state,false);
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]-xc[i]));
}
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1272");
if(primaryerrors || othererrors)
return;
CMinNS::MinNSSetScale(state,s);
CMinNS::MinNSSetBC(state,scaledbndl,scaledbndu);
CMinNS::MinNSRestartFrom(state,x0s);
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]/s[i]-xc[i]));
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]/s[i]-xc[i])/s[i]);
}
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1s,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1293");
if(primaryerrors || othererrors)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(primaryerrors,(!MathIsValidNumber(x1[i]) || !MathIsValidNumber(x1s[i])) || (double)(MathAbs(x1[i]-x1s[i]/s[i]))>(double)(m_scalingtesttol),"testminnsunit.ap:1297");
}
}
}
//+------------------------------------------------------------------+
//| Linearly constrained test |
//+------------------------------------------------------------------+
void CTestMinNSUnit::TestLC(bool &primaryerrors,bool &othererrors)
{
//--- create variables
int n=0;
int i=0;
int j=0;
int k=0;
int nc=0;
CRowDouble x0;
CRowDouble x0s;
CRowDouble x1;
CRowDouble x2;
CRowDouble x1s;
CRowDouble d;
CRowDouble xc;
CRowDouble s;
CRowDouble bndl;
CRowDouble bndu;
CMatrixDouble c;
CMatrixDouble scaledc;
CRowInt ct;
CRowDouble scaledbndl;
CRowDouble scaledbndu;
CRowDouble xrfirst;
CRowDouble xrlast;
CMinNSState state;
CMinNSReport rep;
double v=0;
double v0=0;
double v1=0;
double vv=0;
double flast0=0;
double flast1=0;
int pass=0;
double epsrad=0;
double repferr=0;
double xtol=0;
double ftol=0;
double rho=0;
int i_=0;
for(pass=1; pass<=10; pass++)
{
for(n=1; n<=5; n++)
{
//--- First test:
//--- * smooth problem
//--- * subject to random linear constraints
//--- * with non-unit scale
//--- We:
//--- * compare function value at constrained solution with function
//--- value for penalized unconstrained problem. We do not compare
//--- actual X-values returned, because they are highly unstable -
//--- function values at minimum show better stability.
//--- * check that correct function values are reported
x0=vector<double>::Zeros(n);
xc=vector<double>::Zeros(n);
d=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,10*(2*CMath::RandomReal()-1));
xc.Set(i,2*CMath::RandomReal()-1);
d.Set(i,1+CMath::RandomReal());
s.Set(i,1+CMath::RandomReal());
}
nc=CMath::RandomInteger((n+1)/2);
if(nc>0)
{
c=matrix<double>::Zeros(nc,n+1);
ct.Resize(nc);
for(i=0; i<nc; i++)
{
ct.Set(i,CMath::RandomInteger(3)-1);
for(j=0; j<=n; j++)
c.Set(i,j,CMath::RandomReal()-0.5);
}
}
epsrad=0.00001;
ftol=0.01;
CMinNS::MinNSCreate(n,x0,state);
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
CMinNS::MinNSSetCond(state,epsrad,0);
CMinNS::MinNSSetScale(state,s);
CMinNS::MinNSSetXRep(state,true);
CMinNS::MinNSSetLC(state,c,ct,nc);
repferr=0.0;
flast0=AL_NaN;
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,d[i]*CMath::Sqr(state.m_x[i]-xc[i]));
state.m_j.Set(0,i,d[i]*(2*(state.m_x[i]-xc[i])));
}
continue;
}
if(state.m_xupdated)
{
flast0=0.0;
for(i=0; i<n; i++)
flast0+=d[i]*CMath::Sqr(state.m_x[i]-xc[i]);
repferr=MathMax(repferr,MathAbs(flast0-state.m_f));
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1402");
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(flast0),"testminnsunit.ap:1403");
CAp::SetErrorFlag(othererrors,repferr>(10000.0*CMath::m_machineepsilon),"testminnsunit.ap:1404");
if(primaryerrors || othererrors)
return;
CMinNS::MinNSSetLC(state,c,ct,0);
CMinNS::MinNSRestartFrom(state,x0);
rho=10000.0;
repferr=0.0;
flast1=AL_NaN;
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,d[i]*CMath::Sqr(state.m_x[i]-xc[i]));
state.m_j.Set(0,i,d[i]*(2*(state.m_x[i]-xc[i])));
}
for(i=0; i<nc; i++)
{
v=CAblasF::RDotVR(n,state.m_x,c,i);
v-=c.Get(i,n);
vv=0.0;
if(ct[i]<0)
{
vv=MathSign(MathMax(v,0.0));
v=MathMax(v,0.0);
}
if(ct[i]==0)
{
vv=MathSign(v);
v=MathAbs(v);
}
if(ct[i]>0)
{
vv=-MathSign(MathMax(-v,0.0));
v=MathMax(-v,0.0);
}
state.m_fi.Add(0,rho*v);
for(j=0; j<n; j++)
state.m_j.Add(0,j,rho*vv*c.Get(i,j));
}
continue;
}
if(state.m_xupdated)
{
flast1=0.0;
for(i=0; i<n; i++)
flast1+=d[i]*CMath::Sqr(state.m_x[i]-xc[i]);
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x2,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1462");
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(flast1),"testminnsunit.ap:1463");
if(primaryerrors || othererrors)
return;
CAp::SetErrorFlag(primaryerrors,(double)(MathAbs(flast0-flast1))>ftol,"testminnsunit.ap:1466");
//--- Test on HIGHLY nonconvex linearly constrained problem.
//--- Algorithm should be able to Stop at the bounds.
x0=vector<double>::Zeros(n);
c=matrix<double>::Zeros(2*n,n+1);
ct.Resize(2*n);
for(i=0; i<n; i++)
{
x0.Set(i,CMath::RandomReal()-0.5);
c.Set(2*i+0,i,1.0);
c.Set(2*i+0,n,-1.0);
ct.Set(2*i+0,1);
c.Set(2*i+1,i,1.0);
c.Set(2*i+1,n,1.0);
ct.Set(2*i+1,-1);
}
epsrad=0.0001;
xtol=15.0*epsrad;
CMinNS::MinNSCreate(n,x0,state);
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
CMinNS::MinNSSetCond(state,epsrad,0);
CMinNS::MinNSSetLC(state,c,ct,2*n);
v=-1000.0;
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
v0=MathAbs(state.m_x[i]);
v1=MathSign(state.m_x[i]);
state.m_fi.Add(0,v*(v0+v0*v0));
state.m_j.Set(0,i,v*(v1+2*v0*v1));
}
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1516");
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]),"testminnsunit.ap:1519");
CAp::SetErrorFlag(primaryerrors,((double)(MathAbs(x1[i]-1))>xtol && MathAbs(x1[i])>xtol) && (double)(MathAbs(x1[i]+1))>xtol,"testminnsunit.ap:1520");
}
//--- Test scaling: we perform several steps on unit-scale problem,
//--- then we perform same amount of steps on re-scaled problem,
//--- starting from same point (but scaled according to chosen scale).
//--- Correctly written optimizer should perform essentially same steps
//--- (up to scale) on both problems. At least, it holds within first
//--- several steps, before rounding errors start to accumulate.
//--- NOTE: we also check that correctly scaled points are reported.
//--- And, as side effect, we check MinNSRestartFrom().
//--- NOTE: we use moderate scale and diagonal coefficients in order
//--- to have well-conditioned system. We test correctness of
//--- formulae here, not robustness of algorithm.
x0=vector<double>::Zeros(n);
xc=vector<double>::Zeros(n);
x0s=vector<double>::Zeros(n);
d=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
xrlast=vector<double>::Zeros(n);
c=matrix<double>::Zeros(2*n,n+1);
scaledc=matrix<double>::Zeros(2*n,n+1);
ct.Resize(2*n);
ct.Fill(0);
for(i=0; i<n; i++)
{
s.Set(i,MathPow(10,2*CMath::RandomReal()-1));
d.Set(i,MathPow(10,2*CMath::RandomReal()-1));
x0.Set(i,2*CMath::RandomReal()-1);
xc.Set(i,2*CMath::RandomReal()-1);
x0s.Set(i,x0[i]*s[i]);
k=CMath::RandomInteger(5);
if(k==1)
{
c.Set(2*i+0,i,1.0);
c.Set(2*i+0,n,2*CMath::RandomReal()-1);
ct.Set(2*i+0,1);
}
if(k==2)
{
c.Set(2*i+0,i,1.0);
c.Set(2*i+0,n,2*CMath::RandomReal()-1);
ct.Set(2*i+0,-1);
}
if(k==3)
{
v0=2*CMath::RandomReal()-1;
v1=2*CMath::RandomReal()-1;
c.Set(2*i+0,i,1.0);
c.Set(2*i+0,n,MathMin(v0,v1));
c.Set(2*i+1,i,1.0);
c.Set(2*i+1,n,MathMax(v0,v1));
ct.Set(2*i+0,1);
ct.Set(2*i+1,-1);
}
if(k==4)
{
c.Set(2*i+0,i,1.0);
c.Set(2*i+0,n,2*CMath::RandomReal()-1);
ct.Set(2*i+0,0);
}
}
for(i=0; i<=2*n-1; i++)
{
for(j=0; j<n; j++)
scaledc.Set(i,j,c.Get(i,j)/s[j]);
scaledc.Set(i,n,c.Get(i,n));
}
CMinNS::MinNSCreate(n,x0,state);
CMinNS::MinNSSetAlgoAGS(state,0.1,0.0);
CMinNS::MinNSSetCond(state,0.0,m_scalingtestcnt);
CMinNS::MinNSSetLC(state,c,ct,2*n);
CMinNS::MinNSSetXRep(state,false);
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]-xc[i]));
}
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1622");
if(primaryerrors || othererrors)
return;
CMinNS::MinNSSetScale(state,s);
CMinNS::MinNSSetLC(state,scaledc,ct,2*n);
CMinNS::MinNSRestartFrom(state,x0s);
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]/s[i]-xc[i]));
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]/s[i]-xc[i])/s[i]);
}
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1s,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1643");
if(primaryerrors || othererrors)
return;
for(i=0; i<n; i++)
CAp::SetErrorFlag(primaryerrors,(!MathIsValidNumber(x1[i]) || !MathIsValidNumber(x1s[i])) || (double)(MathAbs(x1[i]-x1s[i]/s[i]))>(double)(m_scalingtesttol),"testminnsunit.ap:1647");
}
}
}
//+------------------------------------------------------------------+
//| Nonlinearly constrained test |
//+------------------------------------------------------------------+
void CTestMinNSUnit::TestNLC(bool &primaryerrors,bool &othererrors)
{
//--- create variables
int passcount=10;
int maxn=5;
double rho=100.0;
int n=0;
int i=0;
int j=0;
int nc=0;
int nec=0;
CRowDouble x0;
CRowDouble x0s;
CRowDouble x1;
CRowDouble x2;
CRowDouble x1s;
CRowDouble d;
CRowDouble xc;
CRowDouble s;
CRowDouble bndl;
CRowDouble bndu;
CRowDouble b;
CRowDouble r;
CMatrixDouble c;
CMatrixDouble scaledc;
CRowInt ct;
CRowDouble scaledbndl;
CRowDouble scaledbndu;
CRowDouble xrfirst;
CRowDouble xrlast;
CMinNSState state;
CMinNSReport rep;
double v=0;
int pass=0;
double epsrad=0;
double xtol=0;
double diffstep=0;
//--- First test:
//--- * simple problem
//--- * subject to random nonlinear constraints of form r[i]*x[i] OPERATION 0.0,
//--- where OPERATION is <= or =
//--- * with non-unit scale
//--- We:
//--- * compare numerical solution with analytic one, which can be
//--- easily calculated
for(pass=1; pass<=passcount; pass++)
{
for(n=1; n<=maxn; n++)
{
for(nc=1; nc<=n; nc++)
{
for(nec=0; nec<=nc; nec++)
{
x0=vector<double>::Zeros(n);
xc=vector<double>::Zeros(n);
d=vector<double>::Zeros(n);
r=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
xc.Set(i,2*CMath::RandomReal()-1);
d.Set(i,MathPow(10,CMath::RandomReal()-0.5));
s.Set(i,MathPow(10,CMath::RandomReal()-0.5));
r.Set(i,(2*CMath::RandomInteger(2)-1)*(0.1+CMath::RandomReal()));
}
xtol=0.01;
epsrad=xtol/100;
CMinNS::MinNSCreate(n,x0,state);
CMinNS::MinNSSetAlgoAGS(state,0.1,rho);
CMinNS::MinNSSetCond(state,epsrad,0);
CMinNS::MinNSSetScale(state,s);
CMinNS::MinNSSetNLC(state,nec,nc-nec);
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]-xc[i]));
}
for(i=1; i<=nc; i++)
{
state.m_fi.Set(i,state.m_x[i-1]*r[i-1]);
for(j=0; j<n; j++)
state.m_j.Set(i,j,0.0);
state.m_j.Set(i,i-1,r[i-1]);
}
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1743");
if(primaryerrors || othererrors)
return;
for(i=0; i<n; i++)
{
v=xc[i];
if(i<nec)
v=0.0;
if(i>=nec && i<nc)
{
if(r[i]>0.0)
v=MathMin(v,0.0);
if(r[i]<0.0)
v=MathMax(v,0.0);
}
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]),"testminnsunit.ap:1762");
CAp::SetErrorFlag(primaryerrors,MathAbs(x1[i]-v)>(double)(xtol*s[i]),"testminnsunit.ap:1763");
}
}
}
}
}
//--- Numerical differentiation test.
for(pass=1; pass<=passcount; pass++)
{
for(n=1; n<=maxn; n++)
{
for(nc=1; nc<=n; nc++)
{
for(nec=0; nec<=nc; nec++)
{
x0=vector<double>::Zeros(n);
xc=vector<double>::Zeros(n);
d=vector<double>::Zeros(n);
r=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
xc.Set(i,2*CMath::RandomReal()-1);
d.Set(i,MathPow(10,CMath::RandomReal()-0.5));
s.Set(i,MathPow(10,CMath::RandomReal()-0.5));
r.Set(i,(2*CMath::RandomInteger(2)-1)*(0.1+CMath::RandomReal()));
}
xtol=0.01;
epsrad=xtol/100;
diffstep=epsrad/10;
CMinNS::MinNSCreateF(n,x0,diffstep,state);
CMinNS::MinNSSetAlgoAGS(state,0.1,rho);
CMinNS::MinNSSetCond(state,epsrad,0);
CMinNS::MinNSSetScale(state,s);
CMinNS::MinNSSetNLC(state,nec,nc-nec);
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfi)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
for(i=1; i<=nc; i++)
state.m_fi.Set(i,state.m_x[i-1]*r[i-1]);
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1813");
if(primaryerrors || othererrors)
return;
for(i=0; i<n; i++)
{
v=xc[i];
if(i<nec)
v=0.0;
if(i>=nec && i<nc)
{
if(r[i]>0.0)
v=MathMin(v,0.0);
if(r[i]<0.0)
v=MathMax(v,0.0);
}
CAp::SetErrorFlag(primaryerrors,!MathIsValidNumber(x1[i]),"testminnsunit.ap:1832");
CAp::SetErrorFlag(primaryerrors,MathAbs(x1[i]-v)>(xtol*s[i]),"testminnsunit.ap:1833");
}
}
}
}
}
//--- Test scaling: we perform several steps on unit-scale problem,
//--- then we perform same amount of steps on re-scaled problem,
//--- starting from same point (but scaled according to chosen scale).
//--- Correctly written optimizer should perform essentially same steps
//--- (up to scale) on both problems. At least, it holds within first
//--- several steps, before rounding errors start to accumulate.
//--- NOTE: we use moderate scale and diagonal coefficients in order
//--- to have well-conditioned system. We test correctness of
//--- formulae here, not robustness of algorithm.
//--- NOTE: we have to use very relaxed thresholds for this test because
//--- each AGS iteration involves solution of several nested QP
//--- subproblems, so rounding errors accumulate too quickly.
//--- It does not mean that algorithm is inexact, just that two
//--- almost identical optimization sessions diverge too fast to
//--- compare them.
for(pass=1; pass<=passcount; pass++)
{
for(n=1; n<=maxn; n++)
{
for(nc=1; nc<=n; nc++)
{
for(nec=0; nec<=nc; nec++)
{
x0=vector<double>::Zeros(n);
x0s=vector<double>::Zeros(n);
xc=vector<double>::Zeros(n);
d=vector<double>::Zeros(n);
r=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x0.Set(i,2*CMath::RandomReal()-1);
xc.Set(i,2*CMath::RandomReal()-1);
d.Set(i,MathPow(10,CMath::RandomReal()-0.5));
s.Set(i,MathPow(10,CMath::RandomReal()-0.5));
r.Set(i,(2*CMath::RandomInteger(2)-1)*(0.1+CMath::RandomReal()));
x0s.Set(i,x0[i]*s[i]);
}
CMinNS::MinNSCreate(n,x0,state);
CMinNS::MinNSSetAlgoAGS(state,0.1,1.0);
CMinNS::MinNSSetCond(state,0.0,m_scalingtestcnt);
CMinNS::MinNSSetNLC(state,nec,nc-nec);
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]-xc[i]));
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]-xc[i]));
}
for(i=1; i<=nc; i++)
{
state.m_fi.Set(i,state.m_x[i-1]*r[i-1]);
for(j=0; j<n; j++)
state.m_j.Set(i,j,0.0);
state.m_j.Set(i,i-1,r[i-1]);
}
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1,rep);
CAp::SetErrorFlag(primaryerrors,rep.m_terminationtype<=0,"testminnsunit.ap:1906");
if(primaryerrors || othererrors)
return;
CMinNS::MinNSSetScale(state,s);
CMinNS::MinNSRestartFrom(state,x0s);
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
state.m_fi.Set(0,0.0);
for(i=0; i<n; i++)
{
state.m_fi.Add(0,d[i]*MathAbs(state.m_x[i]/s[i]-xc[i]));
state.m_j.Set(0,i,d[i]*MathSign(state.m_x[i]/s[i]-xc[i])/s[i]);
}
for(i=1; i<=nc; i++)
{
state.m_fi.Set(i,state.m_x[i-1]/s[i-1]*r[i-1]);
for(j=0; j<n; j++)
state.m_j.Set(i,j,0.0);
state.m_j.Set(i,i-1,r[i-1]/s[i-1]);
}
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1s,rep);
for(i=0; i<n; i++)
CAp::SetErrorFlag(primaryerrors,(!MathIsValidNumber(x1[i]) || !MathIsValidNumber(x1s[i])) || (double)(MathAbs(x1[i]-x1s[i]/s[i]))>(double)(m_scalingtesttol),"testminnsunit.ap:1937");
}
}
}
}
}
//+------------------------------------------------------------------+
//| Special tests |
//+------------------------------------------------------------------+
void CTestMinNSUnit::TestOther(bool &othererrors)
{
//--- create variables
int n=0;
int k=0;
CRowDouble x0;
CRowDouble x1;
CMinNSState state;
CMinNSReport rep;
double v0=0;
double v1=0;
double v=0;
double xtol=0;
double epsrad=0;
double rho=0;
//--- First test:
//--- * 2D problem, minimization of F(x0,x1)=x1
//--- * two constraints, with wildly different magnitudes
//--- * G0(x0,x1)=Rho*Abs(x0+x1)=0
//--- * H0(x0,x1)=Rho*(x0-1000)<=0
//--- where Rho is some large value
//--- Optimizer should be able to deal with situation when
//--- magnitude of Jacobian components is so wildly different.
n=2;
x0=vector<double>::Zeros(n);
x0.Set(0,0.1);
x0.Set(1,1.0);
epsrad=0.00001;
xtol=0.01;
for(k=0; k<=6; k++)
{
rho=MathPow(10,k);
CMinNS::MinNSCreate(n,x0,state);
CMinNS::MinNSSetAlgoAGS(state,0.1,10.0);
CMinNS::MinNSSetCond(state,epsrad,0);
CMinNS::MinNSSetNLC(state,1,1);
v=1000.0;
while(CMinNS::MinNSIteration(state))
{
if(state.m_needfij)
{
v0=state.m_x[0];
v1=state.m_x[1];
state.m_fi.Set(0,v1);
state.m_j.Set(0,0,0.0);
state.m_j.Set(0,1,1.0);
state.m_fi.Set(1,rho*MathAbs(v0+v1));
state.m_j.Set(1,0,rho*MathSign(v0+v1));
state.m_j.Set(1,1,rho*MathSign(v0+v1));
state.m_fi.Set(2,rho*(v0-v));
state.m_j.Set(2,0,rho);
state.m_j.Set(2,1,0.0);
continue;
}
CAp::Assert(false);
othererrors=true;
return;
}
CMinNS::MinNSResults(state,x1,rep);
CAp::SetErrorFlag(othererrors,rep.m_terminationtype<=0,"testminnsunit.ap:2003");
CAp::SetErrorFlag(othererrors,!MathIsValidNumber(x1[0]),"testminnsunit.ap:2004");
CAp::SetErrorFlag(othererrors,!MathIsValidNumber(x1[1]),"testminnsunit.ap:2005");
CAp::SetErrorFlag(othererrors,MathAbs(x1[0]-v)>xtol,"testminnsunit.ap:2006");
CAp::SetErrorFlag(othererrors,MathAbs(x1[1]+v)>xtol,"testminnsunit.ap:2007");
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestMinBCUnit
{
public:
static const int m_maxoptguardlevel;
static bool TestMinBC(bool silent);
private:
static void CalcIIP2(CMinBCState &state,int n,int fk);
static void TestFeasibility(bool &feaserr,bool &converr,bool &interr);
static void TestOther(bool &err);
static void TestPreconditioning(bool &err);
static void TestOptGuard(bool &waserrors);
static void SetRandomPreconditioner(CMinBCState &state,int n,int preckind);
static void TestOptGuardC1Test0ReportForTask0(bool &err,COptGuardNonC1Test0Report &rep,CMatrixDouble &a,int n);
static void TestOptGuardC1Test1ReportForTask0(bool &err,COptGuardNonC1Test1Report &rep,CMatrixDouble &a,int n);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
const int CTestMinBCUnit::m_maxoptguardlevel=1;
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestMinBCUnit::TestMinBC(bool silent)
{
//--- create variables
bool result;
bool waserrors;
bool feasibilityerrors;
bool othererrors;
bool precerrors;
bool interrors;
bool converrors;
bool optguarderrors;
waserrors=false;
feasibilityerrors=false;
othererrors=false;
precerrors=false;
interrors=false;
converrors=false;
optguarderrors=false;
//--- tests
TestFeasibility(feasibilityerrors,converrors,interrors);
TestOther(othererrors);
TestPreconditioning(precerrors);
TestOptGuard(optguarderrors);
//--- end
waserrors=((((feasibilityerrors||othererrors)||converrors)||interrors)||precerrors)||optguarderrors;
if(!silent)
{
Print("TESTING BC OPTIMIZATION");
PrintResult("FEASIBILITY PROPERTIES",!feasibilityerrors);
PrintResult("PRECONDITIONING",!precerrors);
PrintResult("OTHER PROPERTIES",!othererrors);
PrintResult("CONVERGENCE PROPERTIES",!converrors);
PrintResult("INTERNAL ERRORS",!interrors);
PrintResult("OPTGUARD",!optguarderrors);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| 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. |
//+------------------------------------------------------------------+
void CTestMinBCUnit::CalcIIP2(CMinBCState &state,int n,int fk)
{
if(!state.m_needfg)
return;
for(int i=0; i<n; i++)
state.m_g.Set(i,MathPow(i*i+1,2*fk)*2*state.m_x[i]);
state.m_f=CAblasF::RDotV(n,state.m_x,state.m_g)/2.0;
}
//+------------------------------------------------------------------+
//| 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). |
//+------------------------------------------------------------------+
void CTestMinBCUnit::TestFeasibility(bool &feaserr,bool &converr,
bool &interr)
{
//--- create variables
int nmax=5;
double weakepsg=1.0E-4;
int passcount=10;
int pkind=0;
int preckind=0;
int pass=0;
int n=0;
int i=0;
int p=0;
double v=0;
CRowDouble bl;
CRowDouble bu;
CRowDouble x;
CRowDouble g;
CRowDouble x0;
CRowDouble xc;
CRowDouble xs;
CRowDouble svdw;
CMatrixDouble csvdu;
CMatrixDouble svdvt;
CMinBCState state;
CMinBCReport rep;
int dkind=0;
double diffstep=0;
for(pass=1; pass<=passcount; pass++)
{
//--- 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;
bl=vector<double>::Zeros(n);
bu=vector<double>::Zeros(n);
x=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
bl.Set(i,0);
bu.Set(i,1);
x.Set(i,CMath::RandomReal());
x0.Set(i,3*CMath::RandomReal()-1);
}
//--- Create and optimize
if(dkind==0)
CMinBC::MinBCCreate(n,x,state);
if(dkind==1)
CMinBC::MinBCCreateF(n,x,diffstep,state);
CMinBC::MinBCSetBC(state,bl,bu);
CMinBC::MinBCSetCond(state,weakepsg,0.0,0.0,0);
SetRandomPreconditioner(state,n,preckind);
while(CMinBC::MinBCIteration(state))
{
if(state.m_needf || state.m_needfg)
state.m_f=0;
for(i=0; i<n; i++)
{
if(state.m_needf || state.m_needfg)
state.m_f+=MathPow(state.m_x[i]-x0[i],p);
if(state.m_needfg)
state.m_g.Set(i,p*MathPow(state.m_x[i]-x0[i],p-1));
feaserr=feaserr||state.m_x[i]<0.0;
feaserr=feaserr||state.m_x[i]>1.0;
}
}
CMinBC::MinBCResults(state,x,rep);
if(rep.m_terminationtype<=0)
{
converr=true;
return;
}
//--- * compare solution with analytic one
//--- * check feasibility
v=0.0;
for(i=0; i<n; i++)
{
if(x[i]>0.0 && x[i]<1.0)
v+=CMath::Sqr(p*MathPow(x[i]-x0[i],p-1));
feaserr=feaserr||x[i]<0.0;
feaserr=feaserr||x[i]>1.0;
}
converr=converr||MathSqrt(v)>(double)(weakepsg);
}
}
}
}
//--- Same as previous problem, but with minor modifications:
//--- * some bound constraints are 0<=x[i]<=1, some are Ci=x[i]=Ci
//--- * 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;
bl=vector<double>::Zeros(n);
bu=vector<double>::Zeros(n);
x=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
if(CMath::RandomReal()>0.5)
{
bl.Set(i,0);
bu.Set(i,1);
}
else
{
bl.Set(i,CMath::RandomReal());
bu.Set(i,bl[i]);
}
x.Set(i,CMath::RandomReal());
x0.Set(i,3*CMath::RandomReal()-1);
}
//--- Create and optimize
if(dkind==0)
CMinBC::MinBCCreate(n,x,state);
if(dkind==1)
CMinBC::MinBCCreateF(n,x,diffstep,state);
CMinBC::MinBCSetBC(state,bl,bu);
CMinBC::MinBCSetCond(state,weakepsg,0.0,0.0,0);
SetRandomPreconditioner(state,n,preckind);
while(CMinBC::MinBCIteration(state))
{
if(state.m_needf || state.m_needfg)
state.m_f=0;
for(i=0; i<n; i++)
{
if(state.m_needf || state.m_needfg)
state.m_f+=MathPow(state.m_x[i]-x0[i],p);
if(state.m_needfg)
state.m_g.Set(i,p*MathPow(state.m_x[i]-x0[i],p-1));
feaserr=feaserr||state.m_x[i]<(double)(bl[i]);
feaserr=feaserr||state.m_x[i]>(double)(bu[i]);
}
}
CMinBC::MinBCResults(state,x,rep);
if(rep.m_terminationtype<=0)
{
converr=true;
return;
}
//--- * compare solution with analytic one
//--- * check feasibility
v=0.0;
for(i=0; i<n; i++)
{
if(x[i]>(double)(bl[i]) && x[i]<(double)(bu[i]))
v+=CMath::Sqr(p*MathPow(x[i]-x0[i],p-1));
feaserr=feaserr||x[i]<(double)(bl[i]);
feaserr=feaserr||x[i]>(double)(bu[i]);
}
converr=converr||MathSqrt(v)>(double)(weakepsg);
}
}
}
}
//--- 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;
bl=vector<double>::Zeros(n);
bu=vector<double>::Zeros(n);
x=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
bl.Set(i,0);
bu.Set(i,1);
x.Set(i,CMath::RandomReal());
x0.Set(i,3*CMath::RandomReal()-1);
}
i=CMath::RandomInteger(n);
bl.Set(i,1);
bu.Set(i,0);
//--- Create and optimize
CMinBC::MinBCCreate(n,x,state);
CMinBC::MinBCSetBC(state,bl,bu);
CMinBC::MinBCSetCond(state,weakepsg,0.0,0.0,0);
SetRandomPreconditioner(state,n,preckind);
while(CMinBC::MinBCIteration(state))
{
if(state.m_needfg)
{
state.m_f=0;
for(i=0; i<n; i++)
{
state.m_f+=MathPow(state.m_x[i]-x0[i],p);
state.m_g.Set(i,p*MathPow(state.m_x[i]-x0[i],p-1));
}
continue;
}
//--- Unknown protocol specified
interr=true;
return;
}
CMinBC::MinBCResults(state,x,rep);
feaserr=feaserr||rep.m_terminationtype!=-3;
}
}
}
}
}
//+------------------------------------------------------------------+
//| This function additional properties. |
//| On failure sets Err to True(leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
void CTestMinBCUnit::TestOther(bool &err)
{
//--- create variables
int passcount=0;
int pass=0;
int n=0;
int i=0;
int j=0;
int k=0;
CRowDouble bl;
CRowDouble bu;
CRowDouble x;
CRowDouble xf;
CRowDouble x0;
CRowDouble x1;
CRowDouble b;
CRowDouble xlast;
CRowDouble a;
CRowDouble s;
CRowDouble h;
CMatrixDouble fulla;
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;
CMinBCState state;
double epsx=0;
double epsg=0;
double eps=0;
double tmpeps=0;
CMinBCReport rep;
double diffstep=0;
int dkind=0;
bool wasf;
bool wasfg;
double r=0;
CHighQualityRandState rs;
int spoiliteration=0;
int stopiteration=0;
int spoilvar=0;
double spoilval=0;
double ss=0;
int stopcallidx=0;
int callidx=0;
int maxits=0;
bool terminationrequested;
int i_=0;
CHighQualityRand::HQRndRandomize(rs);
epsx=1.0E-4;
epsg=1.0E-8;
passcount=10;
//--- Try to reproduce bug 570 (optimizer hangs on problems where it is required
//--- to perform very small step - less than 1E-50 - in order to activate constraints).
//--- The problem being solved is:
//--- min x[0]+x[1]+...+x[n-1]
//--- subject to
//--- x[i]>=0, for i=0..m_n-1
//--- with initial point
//--- x[0] = 1.0E-100, x[1]=x[2]=...=0.5
//--- We try to reproduce this problem in different settings:
//--- * boundary-only constraints - we test that completion code is positive,
//--- and all x[] are EXACTLY zero
//--- * boundary constraints posed as general linear ones - we test that
//--- completion code is positive, and all x[] are APPROXIMATELY zero.
n=10;
x=vector<double>::Full(n,0.5);
bl=vector<double>::Zeros(n);
bu=vector<double>::Full(n,AL_POSINF);
x.Set(0,1.0E-100);
CMinBC::MinBCCreate(n,x,state);
CMinBC::MinBCSetBC(state,bl,bu);
CMinBC::MinBCSetCond(state,0,0,0,2*n);
while(CMinBC::MinBCIteration(state))
{
if(state.m_needfg)
{
state.m_f=0;
for(i=0; i<n; i++)
{
state.m_f+=state.m_x[i];
state.m_g.Set(i,1.0);
}
}
}
CMinBC::MinBCResults(state,xf,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype<=0,"testminbcunit.ap:494");
if(rep.m_terminationtype>0)
{
for(i=0; i<n; i++)
CAp::SetErrorFlag(err,xf[i]!=0.0,"testminbcunit.ap:497");
}
//--- Test reports:
//--- * first value must be starting point
//--- * last value must be last point
for(pass=1; pass<=passcount; pass++)
{
n=50;
x=vector<double>::Full(n,10);
xlast=vector<double>::Zeros(n);
bl=vector<double>::Zeros(n);
bu=vector<double>::Full(n,AL_POSINF);
for(i=0; i<n; i++)
bl.Set(i,2*CMath::RandomReal()-1);
CMinBC::MinBCCreate(n,x,state);
CMinBC::MinBCSetBC(state,bl,bu);
CMinBC::MinBCSetCond(state,1.0E-64,0,0,10);
CMinBC::MinBCSetXRep(state,true);
fprev=CMath::m_maxrealnumber;
while(CMinBC::MinBCIteration(state))
{
if(state.m_needfg)
{
state.m_f=0;
for(i=0; i<n; i++)
{
state.m_f+=CMath::Sqr((1+i)*state.m_x[i]);
state.m_g.Set(i,2*(1+i)*state.m_x[i]);
}
}
if(state.m_xupdated)
{
if(fprev==CMath::m_maxrealnumber)
{
for(i=0; i<n; i++)
CAp::SetErrorFlag(err,state.m_x[i]!=x[i],"testminbcunit.ap:538");
}
fprev=state.m_f;
xlast=state.m_x;
}
}
CMinBC::MinBCResults(state,x,rep);
for(i=0; i<n; i++)
CAp::SetErrorFlag(err,x[i]!=(double)(xlast[i]),"testminbcunit.ap:545");
}
//--- Test differentiation vs. analytic gradient
//--- (first one issues NeedF requests, second one issues NeedFG requests)
for(pass=1; pass<=passcount; pass++)
{
n=10;
diffstep=1.0E-6;
for(dkind=0; dkind<=1; dkind++)
{
x=vector<double>::Ones(n);
xlast=vector<double>::Zeros(n);
if(dkind==0)
CMinBC::MinBCCreate(n,x,state);
if(dkind==1)
CMinBC::MinBCCreateF(n,x,diffstep,state);
CMinBC::MinBCSetCond(state,1.0E-6,0,epsx,0);
wasf=false;
wasfg=false;
while(CMinBC::MinBCIteration(state))
{
if(state.m_needf || state.m_needfg)
state.m_f=0;
for(i=0; i<n; i++)
{
if(state.m_needf || state.m_needfg)
state.m_f+=CMath::Sqr((1+i)*state.m_x[i]);
if(state.m_needfg)
state.m_g.Set(i,2*(1+i)*state.m_x[i]);
}
wasf=wasf||state.m_needf;
wasfg=wasfg||state.m_needfg;
}
CMinBC::MinBCResults(state,x,rep);
if(dkind==0)
CAp::SetErrorFlag(err,wasf || !wasfg,"testminbcunit.ap:585");
if(dkind==1)
CAp::SetErrorFlag(err,!wasf || wasfg,"testminbcunit.ap:587");
}
}
//--- 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.
for(pass=1; pass<=passcount; pass++)
{
x=vector<double>::Zeros(1);
s=vector<double>::Zeros(1);
diffstep=CMath::RandomReal()*1.0E-6;
s.Set(0,MathExp(CMath::RandomReal()*4-2));
x.Set(0,0);
CMinBC::MinBCCreateF(1,x,diffstep,state);
CMinBC::MinBCSetCond(state,1.0E-6,0,epsx,0);
CMinBC::MinBCSetScale(state,s);
v=0;
while(CMinBC::MinBCIteration(state))
{
state.m_f=CMath::Sqr(state.m_x[0]);
v=MathMax(v,MathAbs(state.m_x[0]));
}
CMinBC::MinBCResults(state,x,rep);
r=v/(s[0]*diffstep);
CAp::SetErrorFlag(err,MathAbs(MathLog(r))>(MathLog(1+1000*CMath::m_machineepsilon)),"testminbcunit.ap:618");
}
//--- Test stpmax
for(pass=1; pass<=passcount; pass++)
{
n=1;
x=vector<double>::Zeros(n);
bl=vector<double>::Zeros(n);
bu=vector<double>::Zeros(n);
x.Set(0,100);
bl.Set(0,2*CMath::RandomReal()-1);
bu.Set(0,AL_POSINF);
stpmax=0.05+0.05*CMath::RandomReal();
CMinBC::MinBCCreate(n,x,state);
CMinBC::MinBCSetBC(state,bl,bu);
CMinBC::MinBCSetCond(state,epsg,0,epsx,0);
CMinBC::MinBCSetXRep(state,true);
CMinBC::MinBCSetStpMax(state,stpmax);
xprev=x[0];
while(CMinBC::MinBCIteration(state))
{
if(state.m_needfg)
{
state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]);
state.m_g.Set(0,MathExp(state.m_x[0])-MathExp(-state.m_x[0]));
CAp::SetErrorFlag(err,MathAbs(state.m_x[0]-xprev)>((1+MathSqrt(CMath::m_machineepsilon))*stpmax),"testminbcunit.ap:646");
}
if(state.m_xupdated)
{
CAp::SetErrorFlag(err,(double)(MathAbs(state.m_x[0]-xprev))>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax),"testminbcunit.ap:650");
xprev=state.m_x[0];
}
}
}
//--- Ability to solve problems with function which is unbounded from below
for(pass=1; pass<=passcount; pass++)
{
n=1;
x=vector<double>::Zeros(n);
bl=vector<double>::Zeros(n);
bu=vector<double>::Zeros(n);
bl.Set(0,4*CMath::RandomReal()+1);
bu.Set(0,bl[0]+1);
x.Set(0,0.5*(bl[0]+bu[0]));
CMinBC::MinBCCreate(n,x,state);
CMinBC::MinBCSetBC(state,bl,bu);
CMinBC::MinBCSetCond(state,epsg,0,epsx,0);
while(CMinBC::MinBCIteration(state))
{
if(state.m_needfg)
{
state.m_f=-(1.0E8*CMath::Sqr(state.m_x[0]));
state.m_g.Set(0,-(2.0E8*state.m_x[0]));
}
}
CMinBC::MinBCResults(state,x,rep);
CAp::SetErrorFlag(err,MathAbs(x[0]-bu[0])>epsx,"testminbcunit.ap:680");
}
//--- 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
//--- * 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 stringent stopping conditions
//--- * and we test that in the extremum stopping conditions are
//--- satisfied subject to the current scaling coefficients.
for(pass=1; pass<=passcount; pass++)
{
tmpeps=1.0E-5;
for(n=1; n<=10; n++)
{
for(ckind=0; ckind<=1; ckind++)
{
for(pkind=0; pkind<=2; pkind++)
{
x=vector<double>::Zeros(n);
a=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
h=vector<double>::Zeros(n);
bl=vector<double>::Full(n,-100000);
bu=vector<double>::Full(n,100000);
for(i=0; i<n; i++)
{
x.Set(i,CMath::RandomReal()+1);
a.Set(i,MathExp(MathLog(10)*(2*CMath::RandomReal()-1)));
s.Set(i,MathExp(MathLog(10)*(2*CMath::RandomReal()-1)));
h.Set(i,MathExp(MathLog(10)*(2*CMath::RandomReal()-1)));
}
CMinBC::MinBCCreate(n,x,state);
if(ckind==1)
CMinBC::MinBCSetBC(state,bl,bu);
if(pkind==1)
CMinBC::MinBCSetPrecDiag(state,h);
if(pkind==2)
CMinBC::MinBCSetPrecScale(state);
CMinBC::MinBCSetCond(state,tmpeps,0,0,0);
CMinBC::MinBCSetScale(state,s);
while(CMinBC::MinBCIteration(state))
{
if(state.m_needfg)
{
state.m_f=0;
for(i=0; i<n; i++)
{
state.m_f+=a[i]*CMath::Sqr(state.m_x[i]);
state.m_g.Set(i,2*a[i]*state.m_x[i]);
}
}
}
CMinBC::MinBCResults(state,x,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(err,true,"testminbcunit.ap:747");
return;
}
v=0;
for(i=0; i<n; i++)
v+=CMath::Sqr(s[i]*2*a[i]*x[i]);
v=MathSqrt(v);
CAp::SetErrorFlag(err,v>(double)(tmpeps),"testminbcunit.ap:754");
}
}
}
}
//--- 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+4, M is either 1.0E8, 1.0E20 or +INF
//--- (we try different combinations)
for(pass=1; pass<=passcount; pass++)
{
for(ckind=0; ckind<=1; ckind++)
{
for(mkind=0; mkind<=2; mkind++)
{
//--- Choose c and M
vc=1;
vm=1;
if(ckind==0)
vc=1.0;
if(ckind==1)
vc=1.0E+4;
if(mkind==0)
vm=1.0E+8;
if(mkind==1)
vm=1.0E+20;
if(mkind==2)
vm=AL_POSINF;
//--- Create optimizer, solve optimization problem
epsg=1.0E-6*vc;
x=vector<double>::Zeros(1);
CMinBC::MinBCCreate(1,x,state);
CMinBC::MinBCSetCond(state,epsg,0,0,0);
while(CMinBC::MinBCIteration(state))
{
if(state.m_needfg)
{
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.Set(0,1/CMath::Sqr(1-state.m_x[0])-1/CMath::Sqr(1+state.m_x[0])+vc);
}
else
{
state.m_f=vm;
state.m_g.Set(0,0);
}
}
}
CMinBC::MinBCResults(state,x,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(err,true,"testminbcunit.ap:822");
return;
}
CAp::SetErrorFlag(err,(MathAbs(1/CMath::Sqr(1-x[0])-1/CMath::Sqr(1+x[0])+vc))>(double)(epsg),"testminbcunit.ap:825");
}
}
}
//--- Test behaviour on noisy functions.
//--- Consider following problem:
//--- * f(x,y) = (x+1)^2 + (y+1)^2 + 10000*MachineEpsilon*RandomReal()
//--- * boundary constraints x>=0, y>=0
//--- * starting point (x0,y0)=(10*MachineEpsilon,1.0)
//--- Such problem contains small numerical noise. Without noise its
//--- solution is (xs,ys)=(0,0), which is easy to find. However, presence
//--- of the noise makes it hard to solve:
//--- * noisy f(x,y) is monotonically decreasing only when we perform
//--- steps orders of magnitude larger than 10000*MachineEpsilon
//--- * at small scales f(x,y) is non-monotonic and non-convex
//--- * however, our first step must be done towards
//--- (x1,y1) = (0,1-some_small_value), and length of such step is
//--- many times SMALLER than 10000*MachineEpsilon
//--- * second step, from (x1,y1) to (xs,ys), will be large enough to
//--- ignore numerical noise, so the only problem is to perform
//--- first step
//--- Naive implementation of BC should fail sometimes (sometimes -
//--- due to non-deterministic nature of noise) on such problem. However,
//--- our improved implementation should solve it correctly. We test
//--- several variations of inner stopping criteria.
for(pass=1; pass<=passcount; pass++)
{
eps=1.0E-9;
x.Resize(2);
bl=vector<double>::Zeros(2);
bu=vector<double>::Zeros(2);
x.Set(0,10*CMath::m_machineepsilon);
x.Set(1,1.0);
bu.Set(0,AL_POSINF);
bu.Set(1,AL_POSINF);
for(ckind=0; ckind<=2; ckind++)
{
CMinBC::MinBCCreate(2,x,state);
CMinBC::MinBCSetBC(state,bl,bu);
if(ckind==0)
CMinBC::MinBCSetCond(state,eps,0,0,0);
if(ckind==1)
CMinBC::MinBCSetCond(state,0,eps,0,0);
if(ckind==2)
CMinBC::MinBCSetCond(state,0,0,eps,0);
while(CMinBC::MinBCIteration(state))
{
if(state.m_needfg)
{
state.m_f=CMath::Sqr(state.m_x[0]+1)+CMath::Sqr(state.m_x[1]+1)+10000*CMath::m_machineepsilon*CMath::RandomReal();
state.m_g.Set(0,2*(state.m_x[0]+1));
state.m_g.Set(1,2*(state.m_x[1]+1));
}
}
CMinBC::MinBCResults(state,xf,rep);
if((rep.m_terminationtype<=0 || xf[0]!=0.0) || xf[1]!=0.0)
{
CAp::SetErrorFlag(err,true,"testminbcunit.ap:889");
return;
}
}
}
//--- Deterministic variation of the previous problem.
//--- Consider following problem:
//--- * boundary constraints x>=0, y>=0
//--- * starting point (x0,y0)=(10*MachineEpsilon,1.0)
//--- / (x+1)^2 + (y+1)^2, for (x,y)<>(x0,y0)
//--- * f(x,y) = |
//--- \ (x+1)^2 + (y+1)^2 - 0.1, for (x,y)=(x0,y0)
//--- Such problem contains deterministic numerical noise (-0.1 at
//--- starting point). Without noise its solution is easy to find.
//--- However, presence of the noise makes it hard to solve:
//--- * our first step must be done towards (x1,y1) = (0,1-some_small_value),
//--- but such step will increase function valye by approximately 0.1 -
//--- instead of decreasing it.
//--- Naive implementation of BC should fail on such problem. However,
//--- our improved implementation should solve it correctly. We test
//--- several variations of inner stopping criteria.
for(pass=1; pass<=passcount; pass++)
{
eps=1.0E-9;
x=vector<double>::Ones(2);
bl=vector<double>::Zeros(2);
bu=vector<double>::Zeros(2);
x.Set(0,10*CMath::m_machineepsilon);
bu.Set(0,AL_POSINF);
bu.Set(1,AL_POSINF);
for(ckind=0; ckind<=2; ckind++)
{
CMinBC::MinBCCreate(2,x,state);
CMinBC::MinBCSetBC(state,bl,bu);
if(ckind==0)
CMinBC::MinBCSetCond(state,eps,0,0,0);
if(ckind==1)
CMinBC::MinBCSetCond(state,0,eps,0,0);
if(ckind==2)
CMinBC::MinBCSetCond(state,0,0,eps,0);
while(CMinBC::MinBCIteration(state))
{
if(state.m_needfg)
{
state.m_f=CMath::Sqr(state.m_x[0]+1)+CMath::Sqr(state.m_x[1]+1);
if(state.m_x[0]==x[0] && (double)(state.m_x[1])==x[1])
state.m_f-=0.1;
state.m_g.Set(0,2*(state.m_x[0]+1));
state.m_g.Set(1,2*(state.m_x[1]+1));
}
}
CMinBC::MinBCResults(state,xf,rep);
if((rep.m_terminationtype<=0 || xf[0]!=0.0) || xf[1]!=0.0)
{
CAp::SetErrorFlag(err,true,"testminbcunit.ap:952");
return;
}
}
}
//--- Test integrity checks for NAN/INF:
//--- * algorithm solves optimization problem, which is normal for some time (quadratic)
//--- * after 5-th step we choose random component of gradient and consistently spoil
//--- it by NAN or INF.
//--- * we check that correct termination code is returned (-8)
n=100;
for(pass=1; pass<=10; pass++)
{
spoiliteration=5;
stopiteration=8;
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
//--- Gradient can be spoiled by +INF, -INF, NAN
spoilvar=CHighQualityRand::HQRndUniformI(rs,n);
i=CHighQualityRand::HQRndUniformI(rs,3);
spoilval=AL_NaN;
if(i==0)
spoilval=AL_NEGINF;
if(i==1)
spoilval=AL_POSINF;
}
else
{
//--- Function value can be spoiled only by NAN
//--- (+INF can be recognized as legitimate value during optimization)
spoilvar=-1;
spoilval=AL_NaN;
}
CMatGen::SPDMatrixRndCond(n,1.0E5,fulla);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
}
CMinBC::MinBCCreate(n,x0,state);
CMinBC::MinBCSetCond(state,0.0,0.0,0.0,stopiteration);
CMinBC::MinBCSetXRep(state,true);
k=-1;
while(CMinBC::MinBCIteration(state))
{
if(state.m_needfg)
{
state.m_f=0;
double temp=0;
for(i=0; i<n; i++)
{
temp=CAblasF::RDotVR(n,state.m_x,fulla,i);
state.m_f+=state.m_x[i]*(b[i]+temp/2);
state.m_g.Set(i,b[i]+temp);
}
if(k>=spoiliteration)
{
if(spoilvar<0)
state.m_f=spoilval;
else
state.m_g.Set(spoilvar,spoilval);
}
continue;
}
if(state.m_xupdated)
{
k++;
continue;
}
CAp::Assert(false);
err=true;
return;
}
CMinBC::MinBCResults(state,x1,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype!=-8,"testminbcunit.ap:1037");
}
//--- Check algorithm ability to handle request for termination:
//--- * to terminate with correct return code = 8
//---*to return point which was "current" at the moment of termination
//--- NOTE: we solve problem with "corrupted" preconditioner which makes it hard
//--- to converge in less than StopCallIdx iterations
for(pass=1; pass<=50; pass++)
{
n=3;
ss=100;
x=vector<double>::Zeros(n);
xlast=vector<double>::Zeros(n);
for(i=0; i<n; i++)
x.Set(i,6+CMath::RandomReal());
s=vector<double>::Full(3,0.00001);
s.Set(2,10000.0);
stopcallidx=CMath::RandomInteger(20);
maxits=25;
CMinBC::MinBCCreate(n,x,state);
CMinBC::MinBCSetCond(state,0,0,0,maxits);
CMinBC::MinBCSetXRep(state,true);
CMinBC::MinBCSetPrecDiag(state,s);
callidx=0;
terminationrequested=false;
xlast=x;
while(CMinBC::MinBCIteration(state))
{
if(state.m_needfg)
{
state.m_f=ss*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.Set(0,2*ss*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[2]-state.m_x[0])*-1);
state.m_g.Set(1,2*state.m_x[1]);
state.m_g.Set(2,2*(state.m_x[2]-state.m_x[0]));
if(callidx==stopcallidx)
{
CMinBC::MinBCRequestTermination(state);
terminationrequested=true;
}
callidx++;
continue;
}
if(state.m_xupdated)
{
if(!terminationrequested)
xlast=state.m_x;
continue;
}
CAp::Assert(false);
err=true;
return;
}
CMinBC::MinBCResults(state,x,rep);
CAp::SetErrorFlag(err,rep.m_terminationtype!=8,"testminbcunit.ap:1095");
for(i=0; i<n; i++)
CAp::SetErrorFlag(err,x[i]!=(double)(xlast[i]),"testminbcunit.ap:1097");
}
}
//+------------------------------------------------------------------+
//| This function tests preconditioning |
//| On failure sets Err to True(leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
void CTestMinBCUnit::TestPreconditioning(bool &err)
{
//--- create variables
int pass=0;
int n=0;
CRowDouble x;
CRowDouble x0;
int i=0;
int k=0;
CMatrixDouble v;
CRowDouble bl;
CRowDouble bu;
CRowDouble vd;
CRowDouble d;
CRowDouble units;
CRowDouble s;
int cntb1=0;
int cntb2=0;
int cntg1=0;
int cntg2=0;
double epsg=0;
CRowDouble diagh;
CMinBCState state;
CMinBCReport rep;
int ckind=0;
int fk=0;
//--- 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)*x[i])^2, i=0..N-1)
//--- * constraints:
//--- 0) absent
//--- 1) box
//--- N - problem size
//--- K - number of repeated passes (should be large enough to average out random factors)
k=100;
epsg=1.0E-8;
for(n=10; n<=10; n++)
{
for(ckind=0; ckind<=1; ckind++)
{
fk=1;
x=vector<double>::Zeros(n);
units=vector<double>::Ones(n);
CMinBC::MinBCCreate(n,x,state);
CMinBC::MinBCSetCond(state,epsg,0.0,0.0,0);
if(ckind==1)
{
bl=vector<double>::Full(n,-1.0);
bu=vector<double>::Full(n,1.0);
CMinBC::MinBCSetBC(state,bl,bu);
}
//--- Test it with default preconditioner VS. perturbed diagonal preconditioner
CMinBC::MinBCSetPrecDefault(state);
CMinBC::MinBCSetScale(state,units);
cntb1=0;
for(pass=0; pass<k; pass++)
{
for(i=0; i<n; i++)
x.Set(i,2*CMath::RandomReal()-1);
CMinBC::MinBCRestartFrom(state,x);
while(CMinBC::MinBCIteration(state))
CalcIIP2(state,n,fk);
CMinBC::MinBCResults(state,x,rep);
cntb1+=rep.m_iterationscount;
err=err||rep.m_terminationtype<=0;
if(err)
Sleep(0);
}
diagh=vector<double>::Zeros(n);
for(i=0; i<n; i++)
diagh.Set(i,2*MathPow(i*i+1,2*fk)*(0.8+0.4*CMath::RandomReal()));
CMinBC::MinBCSetPrecDiag(state,diagh);
CMinBC::MinBCSetScale(state,units);
cntg1=0;
for(pass=0; pass<k; pass++)
{
for(i=0; i<n; i++)
x.Set(i,2*CMath::RandomReal()-1);
CMinBC::MinBCRestartFrom(state,x);
while(CMinBC::MinBCIteration(state))
CalcIIP2(state,n,fk);
CMinBC::MinBCResults(state,x,rep);
cntg1+=rep.m_iterationscount;
err=err||rep.m_terminationtype<=0;
if(err)
Sleep(0);
}
err=err||cntb1<cntg1;
if(err)
Sleep(0);
//--- Test it with scale-based preconditioner
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
s.Set(i,1/MathSqrt(2*MathPow(i*i+1,2*fk)*(0.8+0.4*CMath::RandomReal())));
CMinBC::MinBCSetPrecDefault(state);
CMinBC::MinBCSetScale(state,s);
cntb2=0;
for(pass=0; pass<k; pass++)
{
for(i=0; i<n; i++)
x.Set(i,2*CMath::RandomReal()-1);
CMinBC::MinBCRestartFrom(state,x);
while(CMinBC::MinBCIteration(state))
CalcIIP2(state,n,fk);
CMinBC::MinBCResults(state,x,rep);
cntb2+=rep.m_iterationscount;
err=err||rep.m_terminationtype<=0;
if(err)
Sleep(0);
}
CMinBC::MinBCSetPrecScale(state);
CMinBC::MinBCSetScale(state,s);
cntg2=0;
for(pass=0; pass<k; pass++)
{
for(i=0; i<n; i++)
x.Set(i,2*CMath::RandomReal()-1);
CMinBC::MinBCRestartFrom(state,x);
while(CMinBC::MinBCIteration(state))
CalcIIP2(state,n,fk);
CMinBC::MinBCResults(state,x,rep);
cntg2+=rep.m_iterationscount;
err=err||rep.m_terminationtype<=0;
if(err)
Sleep(0);
}
err=err||cntb2<cntg2;
if(err)
Sleep(0);
}
}
}
//+------------------------------------------------------------------+
//| This function tests OptGuard |
//| On failure sets error flag. |
//+------------------------------------------------------------------+
void CTestMinBCUnit::TestOptGuard(bool &waserrors)
{
CHighQualityRandState rs;
double v=0;
CMinBCState state;
CMinBCReport rep;
COptGuardReport ogrep;
COptGuardNonC1Test0Report ognonc1test0strrep;
COptGuardNonC1Test0Report ognonc1test0lngrep;
COptGuardNonC1Test1Report ognonc1test1strrep;
COptGuardNonC1Test1Report ognonc1test1lngrep;
int i=0;
int j=0;
int n=0;
CMatrixDouble a;
CMatrixDouble a1;
CRowDouble s;
CRowDouble x0;
CRowDouble x1;
CRowDouble b;
CRowDouble bndl;
CRowDouble bndu;
double diffstep=0;
int pass=0;
int defecttype=0;
bool failed;
int passcount=0;
int maxfails=0;
int failurecounter=0;
int maxc1test0fails=0;
int maxc1test1fails=0;
int c1test0fails=0;
int c1test1fails=0;
double avgstr0len=0;
double avglng0len=0;
double avgstr1len=0;
double avglng1len=0;
int varidx=0;
int skind=0;
CMatrixDouble jactrue;
CMatrixDouble jacdefect;
CHighQualityRand::HQRndRandomize(rs);
//--- Check that gradient verification is disabled by default:
//--- gradient checking for bad problem must return nothing
n=10;
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
x0.Set(i,1.0+0.1*i);
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
CMatGen::SPDMatrixRndCond(n,1.0E3,a1);
CMinBC::MinBCCreate(n,x0,state);
CMinBC::MinBCSetCond(state,0,0,1.0E-9,10);
while(CMinBC::MinBCIteration(state))
{
if(state.m_needfg)
{
state.m_f=0;
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,state.m_x,a,i);
state.m_f+=0.5*state.m_x[i]*v;
}
for(i=0; i<n; i++)
state.m_g.Set(i,0);
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinBC::MinBCResults(state,x1,rep);
CMinBC::MinBCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminbcunit.ap:1321");
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminbcunit.ap:1322");
if(waserrors)
return;
CAp::SetErrorFlag(waserrors,CAp::Len(ogrep.m_badgradxbase)!=0,"testminbcunit.ap:1325");
CAp::SetErrorFlag(waserrors,CAp::Rows(ogrep.m_badgraduser)!=0,"testminbcunit.ap:1326");
CAp::SetErrorFlag(waserrors,CAp::Cols(ogrep.m_badgraduser)!=0,"testminbcunit.ap:1327");
CAp::SetErrorFlag(waserrors,CAp::Rows(ogrep.m_badgradnum)!=0,"testminbcunit.ap:1328");
CAp::SetErrorFlag(waserrors,CAp::Cols(ogrep.m_badgradnum)!=0,"testminbcunit.ap:1329");
if(waserrors)
return;
CAp::SetErrorFlag(waserrors,ogrep.m_badgradsuspected,"testminbcunit.ap:1332");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=-1,"testminbcunit.ap:1333");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=-1,"testminbcunit.ap:1334");
//--- Test that C0/C1 continuity monitoring is disabled by default;
//--- we solve nonsmooth problem and test that nothing is returned
//--- by OptGuard.
n=10;
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
a=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
CMinBC::MinBCCreate(n,x0,state);
CMinBC::MinBCSetCond(state,0,0,1.0E-9,50);
while(CMinBC::MinBCIteration(state))
{
if(state.m_needfg)
{
state.m_f=0;
for(i=0; i<n; i++)
state.m_g.Set(i,0);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,state.m_x,a,i);
state.m_f+=MathAbs(v);
v=MathSign(v);
for(j=0; j<n; j++)
state.m_g.Add(j,v*a.Get(i,j));
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinBC::MinBCResults(state,x1,rep);
CMinBC::MinBCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminbcunit.ap:1374");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminbcunit.ap:1375");
if(waserrors)
return;
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0suspected,"testminbcunit.ap:1378");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1suspected,"testminbcunit.ap:1379");
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminbcunit.ap:1380");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0fidx>=0,"testminbcunit.ap:1381");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx>=0,"testminbcunit.ap:1382");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1test0positive,"testminbcunit.ap:1383");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1test1positive,"testminbcunit.ap:1384");
//--- Test gradient checking functionality, try various
//--- defect types:
//--- * accidental zeroing of some gradient component
//--- * accidental addition of 1.0 to some component
//--- * accidental multiplication by 2.0
//--- Try distorting both target and constraints.
diffstep=0.001;
n=10;
for(skind=0; skind<=1; skind++)
{
for(defecttype=-1; defecttype<=2; defecttype++)
{
varidx=CHighQualityRand::HQRndUniformI(rs,n);
x0=vector<double>::Zeros(n);
s=vector<double>::Zeros(n);
bndl=vector<double>::Zeros(n);
bndu=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
s.Set(i,MathPow(10,skind*(30*CHighQualityRand::HQRndUniformR(rs)-15)));
x0.Set(i,(1.0+0.1*i)*s[i]);
j=CHighQualityRand::HQRndUniformI(rs,3);
bndl.Set(i,-(100*s[i]));
bndu.Set(i,100*s[i]);
if(j==1)
bndl.Set(i,x0[i]);
if(j==2)
bndu.Set(i,x0[i]);
}
CMatGen::SPDMatrixRndCond(n,1.0E3,a);
CMatGen::SPDMatrixRndCond(n,1.0E3,a1);
CMinBC::MinBCCreate(n,x0,state);
CMinBC::MinBCOptGuardGradient(state,diffstep);
CMinBC::MinBCSetCond(state,0,0,1.0E-9,10);
CMinBC::MinBCSetScale(state,s);
CMinBC::MinBCSetBC(state,bndl,bndu);
while(CMinBC::MinBCIteration(state))
{
if(state.m_needfg)
{
for(i=0; i<n; i++)
{
CAp::SetErrorFlag(waserrors,state.m_x[i]<bndl[i],"testminbcunit.ap:1432");
CAp::SetErrorFlag(waserrors,state.m_x[i]>bndu[i],"testminbcunit.ap:1433");
}
state.m_f=0;
for(i=0; i<n; i++)
{
v=0;
for(j=0; j<n; j++)
v+=state.m_x[j]/s[j]*a.Get(i,j);
state.m_f+=0.5*(state.m_x[i]/s[i])*v;
state.m_g.Set(i,v);
}
if(defecttype==0)
state.m_g.Set(varidx,0);
if(defecttype==1)
state.m_g.Add(varidx,1);
if(defecttype==2)
state.m_g.Mul(varidx,2);
for(i=0; i<n; i++)
state.m_g.Mul(i,1.0/s[i]);
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinBC::MinBCResults(state,x1,rep);
CMinBC::MinBCOptGuardResults(state,ogrep);
//--- Check that something is returned
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminbcunit.ap:1466");
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminbcunit.ap:1467");
if(waserrors)
return;
//--- Compute reference values for true and spoiled Jacobian at X0
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(ogrep.m_badgradxbase,n),"testminbcunit.ap:1474");
if(waserrors)
return;
jactrue=matrix<double>::Zeros(1,n);
jacdefect=matrix<double>::Zeros(1,n);
for(i=0; i<n; i++)
{
v=0;
for(j=0; j<n; j++)
v+=ogrep.m_badgradxbase[j]/s[j]*a.Get(i,j);
jactrue.Set(0,i,v);
jacdefect.Set(0,i,v);
}
if(defecttype==0)
jacdefect.Set(0,varidx,0);
if(defecttype==1)
jacdefect.Add(0,varidx,1);
if(defecttype==2)
jacdefect.Mul(0,varidx,2);
for(i=0; i<n; i++)
{
jactrue.Mul(0,i,1.0/s[i]);
jacdefect.Mul(0,i,1.0/s[i]);
}
//--- Check OptGuard report
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteMatrix(ogrep.m_badgraduser,1,n),"testminbcunit.ap:1502");
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteMatrix(ogrep.m_badgradnum,1,n),"testminbcunit.ap:1503");
if(waserrors)
return;
if(defecttype>=0)
{
CAp::SetErrorFlag(waserrors,!ogrep.m_badgradsuspected,"testminbcunit.ap:1508");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=0,"testminbcunit.ap:1509");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=varidx,"testminbcunit.ap:1510");
}
else
{
CAp::SetErrorFlag(waserrors,ogrep.m_badgradsuspected,"testminbcunit.ap:1514");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradfidx!=-1,"testminbcunit.ap:1515");
CAp::SetErrorFlag(waserrors,ogrep.m_badgradvidx!=-1,"testminbcunit.ap:1516");
}
for(j=0; j<n; j++)
{
CAp::SetErrorFlag(waserrors,(MathAbs(jactrue.Get(0,j)-ogrep.m_badgradnum.Get(0,j)))>(0.01/s[j]),"testminbcunit.ap:1520");
CAp::SetErrorFlag(waserrors,(MathAbs(jacdefect.Get(0,j)-ogrep.m_badgraduser.Get(0,j)))>(0.01/s[j]),"testminbcunit.ap:1521");
}
}
}
//--- A test for detection of C1 continuity violations in the target.
//--- Target function is a sum of |(x,c_i)| for i=1..N.
//--- No constraints is present.
//--- Analytic gradient is provided.
//--- OptGuard should be able to detect violations in more than
//--- 99.9% of runs; it means that 100 runs should have no more than 4
//--- failures in all cases (even after multiple repeated tests; according
//--- to the binomial distribution quantiles).
//--- We select some N and perform exhaustive search for this N.
passcount=100;
maxfails=4;
maxc1test0fails=10;
maxc1test1fails=10;
n=1+CHighQualityRand::HQRndUniformI(rs,10);
failurecounter=0;
c1test0fails=0;
c1test1fails=0;
avgstr0len=0;
avglng0len=0;
avgstr1len=0;
avglng1len=0;
for(pass=1; pass<=passcount; pass++)
{
//--- Formulate problem
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
s.Set(i,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
a=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
//--- Create and try to solve
CMinBC::MinBCCreate(n,x0,state);
CMinBC::MinBCSetCond(state,0,0,1.0E-9,50);
CMinBC::MinBCSetScale(state,s);
CMinBC::MinBCOptGuardSmoothness(state,1+CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel));
while(CMinBC::MinBCIteration(state))
{
if(state.m_needfg)
{
state.m_f=0;
for(i=0; i<n; i++)
state.m_g.Set(i,0);
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,state.m_x,a,i);
state.m_f+=MathAbs(v);
v=MathSign(v);
for(j=0; j<n; j++)
state.m_g.Add(j,v*a.Get(i,j));
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinBC::MinBCResults(state,x1,rep);
CMinBC::MinBCOptGuardResults(state,ogrep);
//--- Check basic properties of the solution
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminbcunit.ap:1602");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminbcunit.ap:1603");
if(waserrors)
return;
//--- Check generic OptGuard report: distinguish between "hard"
//--- failures which result in immediate termination
//--- (C0 violation being reported) and "soft" ones
//--- (C1 violation is NOT reported) which accumulate
//--- until we exhaust limit.
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0suspected,"testminbcunit.ap:1614");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0fidx>=0,"testminbcunit.ap:1615");
failed=false;
failed=failed||COptGuardApi::OptGuardAllClear(ogrep);
failed=failed||!ogrep.m_nonc1suspected;
failed=failed||ogrep.m_nonc1fidx!=0;
if(failed)
failurecounter++;
//--- Check C1 continuity test #0
CMinBC::MinBCOptGuardNonC1Test0Results(state,ognonc1test0strrep,ognonc1test0lngrep);
CMinBC::MinBCOptGuardNonC1Test1Results(state,ognonc1test1strrep,ognonc1test1lngrep);
if(ogrep.m_nonc1test0positive)
{
CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminbcunit.ap:1630");
CAp::SetErrorFlag(waserrors,!ognonc1test0strrep.m_positive,"testminbcunit.ap:1631");
CAp::SetErrorFlag(waserrors,!ognonc1test0lngrep.m_positive,"testminbcunit.ap:1632");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx!=0,"testminbcunit.ap:1633");
TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0strrep,a,n);
TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0lngrep,a,n);
avgstr0len+=(double)ognonc1test0strrep.m_cnt/(double)passcount;
avglng0len+=(double)ognonc1test0lngrep.m_cnt/(double)passcount;
}
else
{
CAp::SetErrorFlag(waserrors,ognonc1test0strrep.m_positive,"testminbcunit.ap:1641");
CAp::SetErrorFlag(waserrors,ognonc1test0lngrep.m_positive,"testminbcunit.ap:1642");
TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0strrep,a,n);
TestOptGuardC1Test0ReportForTask0(waserrors,ognonc1test0lngrep,a,n);
c1test0fails++;
}
if(ogrep.m_nonc1test1positive)
{
CAp::SetErrorFlag(waserrors,!ogrep.m_nonc1suspected,"testminbcunit.ap:1649");
CAp::SetErrorFlag(waserrors,!ognonc1test1strrep.m_positive,"testminbcunit.ap:1650");
CAp::SetErrorFlag(waserrors,!ognonc1test1lngrep.m_positive,"testminbcunit.ap:1651");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc1fidx!=0,"testminbcunit.ap:1652");
TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1strrep,a,n);
TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1lngrep,a,n);
avgstr1len+=(double)ognonc1test1strrep.m_cnt/(double)passcount;
avglng1len+=(double)ognonc1test1lngrep.m_cnt/(double)passcount;
}
else
{
CAp::SetErrorFlag(waserrors,ognonc1test1strrep.m_positive,"testminbcunit.ap:1660");
CAp::SetErrorFlag(waserrors,ognonc1test1lngrep.m_positive,"testminbcunit.ap:1661");
TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1strrep,a,n);
TestOptGuardC1Test1ReportForTask0(waserrors,ognonc1test1lngrep,a,n);
c1test1fails++;
}
}
CAp::SetErrorFlag(waserrors,failurecounter>maxfails,"testminbcunit.ap:1668");
CAp::SetErrorFlag(waserrors,c1test0fails>maxc1test0fails,"testminbcunit.ap:1669");
CAp::SetErrorFlag(waserrors,c1test1fails>maxc1test1fails,"testminbcunit.ap:1670");
CAp::SetErrorFlag(waserrors,(double)(avglng0len)<=(double)(avgstr0len),"testminbcunit.ap:1671");
CAp::SetErrorFlag(waserrors,(double)(avglng1len)<=(double)(avgstr1len),"testminbcunit.ap:1672");
//--- Detection of C1 continuity violations in the target under numerical differentiation:
//--- * target function is a sum of |(x,c_i)| for i=1..N.
//--- * no constraints is present.
//--- * analytic gradient is provided.
//--- OptGuard should always be able to detect violations in more than
//--- 99% of runs (note: reduced strength when compared with analytic gradient);
//--- it means that 100 runs should have no more than 10 failures in all cases
//--- (even after multiple repeated tests; according to the binomial distribution
//--- quantiles).
//--- We select some N and perform exhaustive search for this N.
diffstep=0.0001;
passcount=100;
maxfails=10;
n=1+CHighQualityRand::HQRndUniformI(rs,10);
failurecounter=0;
for(pass=1; pass<=passcount; pass++)
{
//--- Formulate problem
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
x0.Set(i,CHighQualityRand::HQRndNormal(rs));
s=vector<double>::Zeros(n);
for(i=0; i<n; i++)
s.Set(i,0.01*MathPow(2,0.33*CHighQualityRand::HQRndNormal(rs)));
a=matrix<double>::Zeros(n,n);
for(i=0; i<n; i++)
for(j=0; j<n; j++)
a.Set(i,j,CHighQualityRand::HQRndNormal(rs));
//--- Create and try to solve
CMinBC::MinBCCreateF(n,x0,diffstep,state);
CMinBC::MinBCSetCond(state,0,0,1.0E-9,50);
CMinBC::MinBCOptGuardSmoothness(state,1+CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel));
while(CMinBC::MinBCIteration(state))
{
if(state.m_needf)
{
state.m_f=0;
for(i=0; i<n; i++)
{
v=CAblasF::RDotVR(n,state.m_x,a,i);
state.m_f+=MathAbs(v);
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinBC::MinBCResults(state,x1,rep);
CMinBC::MinBCOptGuardResults(state,ogrep);
//--- Check basic properties of the solution
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminbcunit.ap:1738");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminbcunit.ap:1739");
if(waserrors)
return;
//--- Check OptGuard report: distinguish between "hard"
//--- failures which result in immediate termination
//--- (C0 violation being reported) and "soft" ones
//--- (C1 violation is NOT reported) which accumulate
//--- until we exhaust limit.
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0suspected,"testminbcunit.ap:1750");
CAp::SetErrorFlag(waserrors,ogrep.m_nonc0fidx>=0,"testminbcunit.ap:1751");
failed=false;
failed=failed||COptGuardApi::OptGuardAllClear(ogrep);
failed=failed||!ogrep.m_nonc1suspected;
failed=failed||ogrep.m_nonc1fidx!=0;
if(failed)
failurecounter++;
}
CAp::SetErrorFlag(waserrors,failurecounter>maxfails,"testminbcunit.ap:1759");
//--- Make sure than no false positives are reported for larger
//--- problems where numerical noise can be an issue:
//--- * N=100 dimensions
//--- * positive-definite quadratic programming problem
//--- * upper limit on iterations count, MaxIts=25
//--- We simply test that OptGuard does not return error code.
n=100;
CMatGen::SPDMatrixRndCond(n,1.0E2,a);
b=vector<double>::Zeros(n);
x0=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
b.Set(i,CHighQualityRand::HQRndNormal(rs));
x0.Set(i,MathPow(2.0,CHighQualityRand::HQRndNormal(rs)));
}
CMinBC::MinBCCreate(n,x0,state);
CMinBC::MinBCOptGuardSmoothness(state,1+CHighQualityRand::HQRndUniformI(rs,m_maxoptguardlevel));
CMinBC::MinBCSetCond(state,0,0,1.0E-9,25);
while(CMinBC::MinBCIteration(state))
{
if(state.m_needfg)
{
state.m_f=0;
state.m_g=b;
for(i=0; i<n; i++)
{
state.m_f+=b[i]*state.m_x[i];
for(j=0; j<n; j++)
{
state.m_f+=0.5*state.m_x[i]*a.Get(i,j)*state.m_x[j];
state.m_g.Add(i,a.Get(i,j)*state.m_x[j]);
}
}
continue;
}
CAp::Assert(false);
waserrors=true;
return;
}
CMinBC::MinBCResults(state,x1,rep);
CAp::SetErrorFlag(waserrors,!CApServ::IsFiniteVector(x1,n),"testminbcunit.ap:1802");
CAp::SetErrorFlag(waserrors,rep.m_terminationtype<=0,"testminbcunit.ap:1803");
if(waserrors)
return;
CMinBC::MinBCOptGuardResults(state,ogrep);
CAp::SetErrorFlag(waserrors,!COptGuardApi::OptGuardAllClear(ogrep),"testminbcunit.ap:1807");
}
//+------------------------------------------------------------------+
//| This function sets random preconditioner: |
//| * unit one, for PrecKind = 0 |
//| * diagonal - based one, for PrecKind = 1 |
//| * scale - based one, for PrecKind = 2 |
//+------------------------------------------------------------------+
void CTestMinBCUnit::SetRandomPreconditioner(CMinBCState &state,
int n,
int preckind)
{
CRowDouble p;
if(preckind==1)
{
p=vector<double>::Zeros(n);
for(int i=0; i<n; i++)
p.Set(i,MathExp(6*CMath::RandomReal()-3));
CMinBC::MinBCSetPrecDiag(state,p);
}
else
CMinBC::MinBCSetPrecDefault(state);
}
//+------------------------------------------------------------------+
//| This function tests report of "non-C1" test #0 for task #0 given |
//| by matrix A. |
//| On failure sets error flag. |
//+------------------------------------------------------------------+
void CTestMinBCUnit::TestOptGuardC1Test0ReportForTask0(bool &err,
COptGuardNonC1Test0Report &rep,
CMatrixDouble &a,
int n)
{
//--- create variables
int i=0;
int j=0;
int k=0;
double v=0;
double vv=0;
double va=0;
double vb=0;
bool hasc1discontinuities;
if(rep.m_positive)
{
//--- Check positive report, first checks
CAp::SetErrorFlag(err,rep.m_fidx!=0,"testminbcunit.ap:1858");
CAp::SetErrorFlag(err,rep.m_n!=n,"testminbcunit.ap:1859");
CAp::SetErrorFlag(err,!(0<=rep.m_stpidxa),"testminbcunit.ap:1860");
CAp::SetErrorFlag(err,!(rep.m_stpidxa<rep.m_stpidxb),"testminbcunit.ap:1861");
CAp::SetErrorFlag(err,!(rep.m_stpidxb<rep.m_cnt),"testminbcunit.ap:1862");
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=rep.m_n,"testminbcunit.ap:1863");
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=rep.m_n,"testminbcunit.ap:1864");
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=rep.m_cnt,"testminbcunit.ap:1865");
CAp::SetErrorFlag(err,CAp::Len(rep.m_f)!=rep.m_cnt,"testminbcunit.ap:1866");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_x0,n),"testminbcunit.ap:1867");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_d,n),"testminbcunit.ap:1868");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_stp,rep.m_cnt),"testminbcunit.ap:1869");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_f,rep.m_cnt),"testminbcunit.ap:1870");
if(err)
return;
//--- Check consistency of X0, D, Stp and F
for(k=0; k<=rep.m_cnt-2; k++)
CAp::SetErrorFlag(err,rep.m_stp[k]>=rep.m_stp[k+1],"testminbcunit.ap:1878");
for(k=0; k<rep.m_cnt; k++)
{
v=0;
for(i=0; i<n; i++)
{
vv=0;
for(j=0; j<n; j++)
vv+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[k]);
v+=MathAbs(vv);
}
CAp::SetErrorFlag(err,MathAbs(v-rep.m_f[k])>(1.0E-6*MathMax(MathAbs(v),1)),"testminbcunit.ap:1891");
}
//--- Check that interval [#StpIdxA,#StpIdxB] contains at least one discontinuity
hasc1discontinuities=false;
for(i=0; i<n; i++)
{
va=0;
vb=0;
for(j=0; j<n; j++)
{
va+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[rep.m_stpidxa]);
vb+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[rep.m_stpidxb]);
}
hasc1discontinuities=hasc1discontinuities||MathSign(va)!=MathSign(vb);
}
CAp::SetErrorFlag(err,!hasc1discontinuities,"testminbcunit.ap:1909");
}
else
{
//--- Check negative report: fields must be empty
CAp::SetErrorFlag(err,rep.m_stpidxa!=-1,"testminbcunit.ap:1916");
CAp::SetErrorFlag(err,rep.m_stpidxb!=-1,"testminbcunit.ap:1917");
CAp::SetErrorFlag(err,rep.m_fidx!=-1,"testminbcunit.ap:1918");
CAp::SetErrorFlag(err,rep.m_cnt!=0,"testminbcunit.ap:1919");
CAp::SetErrorFlag(err,rep.m_n!=0,"testminbcunit.ap:1920");
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=0,"testminbcunit.ap:1921");
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=0,"testminbcunit.ap:1922");
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=0,"testminbcunit.ap:1923");
CAp::SetErrorFlag(err,CAp::Len(rep.m_f)!=0,"testminbcunit.ap:1924");
}
}
//+------------------------------------------------------------------+
//| This function tests report of "non-C1" test #1 for task #0 given |
//| by matrix A. |
//| On failure sets error flag. |
//+------------------------------------------------------------------+
void CTestMinBCUnit::TestOptGuardC1Test1ReportForTask0(bool &err,
COptGuardNonC1Test1Report &rep,
CMatrixDouble &a,
int n)
{
//--- create variables
int i=0;
int j=0;
int k=0;
double v=0;
double vv=0;
double va=0;
double vb=0;
bool tooclose;
bool hasc1discontinuities;
if(rep.m_positive)
{
//--- Check positive report, first checks
CAp::SetErrorFlag(err,rep.m_fidx!=0,"testminbcunit.ap:1952");
CAp::SetErrorFlag(err,rep.m_vidx<0,"testminbcunit.ap:1953");
CAp::SetErrorFlag(err,rep.m_vidx>n,"testminbcunit.ap:1954");
CAp::SetErrorFlag(err,rep.m_n!=n,"testminbcunit.ap:1955");
CAp::SetErrorFlag(err,!(0<=rep.m_stpidxa),"testminbcunit.ap:1956");
CAp::SetErrorFlag(err,!(rep.m_stpidxa<rep.m_stpidxb),"testminbcunit.ap:1957");
CAp::SetErrorFlag(err,!(rep.m_stpidxb<rep.m_cnt),"testminbcunit.ap:1958");
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=rep.m_n,"testminbcunit.ap:1959");
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=rep.m_n,"testminbcunit.ap:1960");
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=rep.m_cnt,"testminbcunit.ap:1961");
CAp::SetErrorFlag(err,CAp::Len(rep.m_g)!=rep.m_cnt,"testminbcunit.ap:1962");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_x0,n),"testminbcunit.ap:1963");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_d,n),"testminbcunit.ap:1964");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_stp,rep.m_cnt),"testminbcunit.ap:1965");
CAp::SetErrorFlag(err,!CApServ::IsFiniteVector(rep.m_g,rep.m_cnt),"testminbcunit.ap:1966");
if(err)
return;
//--- Check consistency of X0, D, Stp and G
for(k=0; k<rep.m_cnt-1; k++)
CAp::SetErrorFlag(err,rep.m_stp[k]>=rep.m_stp[k+1],"testminbcunit.ap:1974");
for(k=0; k<rep.m_cnt; k++)
{
v=0;
tooclose=false;
for(i=0; i<n; i++)
{
vv=0;
for(j=0; j<n; j++)
vv+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[k]);
v+=MathSign(vv)*a.Get(i,rep.m_vidx);
tooclose=tooclose||MathAbs(vv)<(1.0E-4);
}
if(!tooclose)
CAp::SetErrorFlag(err,MathAbs(v-rep.m_g[k])>(1.0E-6*MathMax(MathAbs(v),1)),"testminbcunit.ap:1988");
}
//--- Check that interval [#StpIdxA,#StpIdxB] contains at least one discontinuity
tooclose=false;
hasc1discontinuities=false;
for(i=0; i<n; i++)
{
va=0;
vb=0;
for(j=0; j<n; j++)
{
va+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[rep.m_stpidxa]);
vb+=a.Get(i,j)*(rep.m_x0[j]+rep.m_d[j]*rep.m_stp[rep.m_stpidxb]);
}
tooclose=(tooclose||(double)(MathAbs(va))<(double)(1.0E-8))||(double)(MathAbs(vb))<(double)(1.0E-8);
hasc1discontinuities=hasc1discontinuities||MathSign(va)!=MathSign(vb);
}
if(!tooclose)
CAp::SetErrorFlag(err,!hasc1discontinuities,"testminbcunit.ap:2009");
}
else
{
//--- Check negative report: fields must be empty
CAp::SetErrorFlag(err,rep.m_stpidxa!=-1,"testminbcunit.ap:2016");
CAp::SetErrorFlag(err,rep.m_stpidxb!=-1,"testminbcunit.ap:2017");
CAp::SetErrorFlag(err,rep.m_fidx!=-1,"testminbcunit.ap:2018");
CAp::SetErrorFlag(err,rep.m_vidx!=-1,"testminbcunit.ap:2019");
CAp::SetErrorFlag(err,rep.m_cnt!=0,"testminbcunit.ap:2020");
CAp::SetErrorFlag(err,rep.m_n!=0,"testminbcunit.ap:2021");
CAp::SetErrorFlag(err,CAp::Len(rep.m_x0)!=0,"testminbcunit.ap:2022");
CAp::SetErrorFlag(err,CAp::Len(rep.m_d)!=0,"testminbcunit.ap:2023");
CAp::SetErrorFlag(err,CAp::Len(rep.m_stp)!=0,"testminbcunit.ap:2024");
CAp::SetErrorFlag(err,CAp::Len(rep.m_g)!=0,"testminbcunit.ap:2025");
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestNormalDistrUnit
{
public:
static bool TestNormalDistr(bool silent);
private:
static void TestNormal(bool &errorflag);
static void TestBVN(bool &errorflag);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestNormalDistrUnit::TestNormalDistr(bool silent)
{
//--- create variables
bool result;
bool nrmerr;
bool bvnerr;
bool waserrors;
nrmerr=false;
bvnerr=false;
TestNormal(nrmerr);
TestBVN(bvnerr);
//--- report
waserrors=bvnerr||nrmerr;
if(!silent)
{
Print("TESTING NORMAL DISTRIBUTION");
PrintResult("NORMAL DISTRIBUTION",!nrmerr);
PrintResult("BIVARIATE NORMALS",!bvnerr);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- end
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| Normal test |
//+------------------------------------------------------------------+
void CTestNormalDistrUnit::TestNormal(bool &errorflag)
{
//--- create variables
double v0=0;
double v1=0;
double x=0;
double h=0;
CHighQualityRandState rs;
CHighQualityRand::HQRndRandomize(rs);
//--- Test that PDF is roughly equal to derivative of CDF
for(int k=0; k<=999; k++)
{
x=CHighQualityRand::HQRndNormal(rs);
h=1.0E-5;
v0=CNormalDistr::NormalPDF(x);
v1=(CNormalDistr::NormalCDF(x+h)-CNormalDistr::NormalCDF(x-h))/(2*h);
CAp::SetErrorFlag(errorflag,MathAbs(v0-v1)>(1.0E-4),"testnormaldistrunit.ap:75");
}
}
//+------------------------------------------------------------------+
//| Bivariate normal test |
//+------------------------------------------------------------------+
void CTestNormalDistrUnit::TestBVN(bool &errorflag)
{
//--- create variables
double v0=0;
double v1=0;
double err=0;
double x=0;
double y=0;
double rho=0;
double h=0;
CHighQualityRandState rs;
CHighQualityRand::HQRndRandomize(rs);
//--- Test bivariate normal CDF values (small Rho and hard Rho)
err=0;
err=MathMax(err,MathAbs(0.142221121370770000-CAlglib::BivariateNormalCDF(-1.060937077356340000,1.523763953950230000,0.344134938671007000)));
err=MathMax(err,MathAbs(0.001090824383322160-CNormalDistr::BivariateNormalCDF(-3.034280037001620000,0.633583566571867000,0.196737644948391000)));
err=MathMax(err,MathAbs(0.000063428666799461-CNormalDistr::BivariateNormalCDF(-3.577198111015300000,-2.584892928002350000,0.603284544092224000)));
err=MathMax(err,MathAbs(0.000000019144648137-CNormalDistr::BivariateNormalCDF(-1.053171728048290000,-2.987017621703680000,-0.659498170394145000)));
err=MathMax(err,MathAbs(0.972551066792029000-CNormalDistr::BivariateNormalCDF(4.785178416287600000,1.919693671247900000,0.261346800934746000)));
err=MathMax(err,MathAbs(0.000000608605272790-CNormalDistr::BivariateNormalCDF(-4.852815283224590000,2.398320119295830000,0.662485812586403000)));
err=MathMax(err,MathAbs(0.576135824931800000-CNormalDistr::BivariateNormalCDF(0.192020298597275000,4.695461487450740000,-0.110028699320946000)));
err=MathMax(err,MathAbs(0.876357061322732000-CNormalDistr::BivariateNormalCDF(2.095780141251770000,1.211768206209080000,0.397007061864995000)));
err=MathMax(err,MathAbs(0.002969212104812400-CNormalDistr::BivariateNormalCDF(-2.654007687508430000,1.340051767837440000,-0.233916559318503000)));
err=MathMax(err,MathAbs(0.000377048151161364-CNormalDistr::BivariateNormalCDF(1.128363097510700000,-2.781006578069910000,-0.642877478801918000)));
err=MathMax(err,MathAbs(0.995657061734790000-CNormalDistr::BivariateNormalCDF(3.966506850977000000,2.626841258388710000,0.291409185863929000)));
err=MathMax(err,MathAbs(0.003282876365551870-CNormalDistr::BivariateNormalCDF(-2.717530641700190000,3.217920162027340000,-0.101773464540366000)));
err=MathMax(err,MathAbs(0.002099371685469470-CNormalDistr::BivariateNormalCDF(-1.811681729272450000,-2.262911120125770000,0.361735313431128000)));
err=MathMax(err,MathAbs(0.648944114852307000-CNormalDistr::BivariateNormalCDF(1.861468373436860000,0.432740073549983000,0.092845182466246300)));
err=MathMax(err,MathAbs(0.000000000094851728-CNormalDistr::BivariateNormalCDF(-4.898527851968480000,-3.491204153631050000,-0.010492822687090300)));
err=MathMax(err,MathAbs(0.000416778593465223-CNormalDistr::BivariateNormalCDF(-3.341356669094100000,1.862802982022170000,0.398642687655347000)));
err=MathMax(err,MathAbs(0.741640376816388000-CNormalDistr::BivariateNormalCDF(4.687494092358740000,0.648415139929991000,-0.692925257444683000)));
err=MathMax(err,MathAbs(0.868844042717264000-CNormalDistr::BivariateNormalCDF(2.369093655782270000,1.148153167494120000,0.297877516862745000)));
err=MathMax(err,MathAbs(0.999356215351682000-CNormalDistr::BivariateNormalCDF(4.352384277131720000,3.221749932900420000,-0.257163446680127000)));
err=MathMax(err,MathAbs(0.998760969813713000-CNormalDistr::BivariateNormalCDF(3.422289814750960000,3.111433954663520000,0.198012195099628000)));
CAp::SetErrorFlag(errorflag,err>(1.0E-12),"testnormaldistrunit.ap:121");
err=0;
err=MathMax(err,MathAbs(0.080575405379940000-CNormalDistr::BivariateNormalCDF(-1.060937077356340000,1.523763953950230000,-0.999999999999000000)));
err=MathMax(err,MathAbs(0.000000000000000007-CNormalDistr::BivariateNormalCDF(-1.060937077356340000,-1.060937077356340000,-0.999999999999000000)));
err=MathMax(err,MathAbs(0.000000000000000007-CNormalDistr::BivariateNormalCDF(-1.060937077356340000,-1.060937077354570000,-0.999999999999000000)));
err=MathMax(err,MathAbs(0.000000000000000016-CNormalDistr::BivariateNormalCDF(-3.034280037001620000,0.633583566571867000,-0.999999999990000000)));
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-3.034280037001620000,-3.034280037001620000,-0.999999999990000000)));
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-3.034280037001620000,-3.034280036974940000,-0.999999999990000000)));
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-3.577198111015300000,-2.584892928002350000,-0.999999999900000000)));
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-3.577198111015300000,-3.577198111015300000,-0.999999999900000000)));
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-3.577198111015300000,-3.577198111015280000,-0.999999999900000000)));
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-1.053171728048290000,-2.987017621703680000,-0.999999999000000000)));
err=MathMax(err,MathAbs(0.000000000000000021-CNormalDistr::BivariateNormalCDF(-1.053171728048290000,-1.053171728048290000,-0.999999999000000000)));
err=MathMax(err,MathAbs(0.000000000000000014-CNormalDistr::BivariateNormalCDF(-1.053171728048290000,-1.052982935276290000,-0.999999999000000000)));
err=MathMax(err,MathAbs(0.972550843757766000-CNormalDistr::BivariateNormalCDF(4.785178416287600000,1.919693671247900000,-0.999999990000000000)));
err=MathMax(err,MathAbs(0.999998291644901000-CNormalDistr::BivariateNormalCDF(4.785178416287600000,4.785178416287600000,-0.999999990000000000)));
err=MathMax(err,MathAbs(0.999998291644901000-CNormalDistr::BivariateNormalCDF(4.785178416287600000,4.785178416295720000,-0.999999990000000000)));
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-4.852815283224590000,2.398320119295830000,-0.999999900000000000)));
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-4.852815283224590000,-4.852815283224590000,-0.999999900000000000)));
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-4.852815283224590000,-4.852815283224590000,-0.999999900000000000)));
err=MathMax(err,MathAbs(0.576135517318684000-CNormalDistr::BivariateNormalCDF(0.192020298597275000,4.695461487450740000,-0.999999000000000000)));
err=MathMax(err,MathAbs(0.152273694692214000-CNormalDistr::BivariateNormalCDF(0.192020298597275000,0.192020298597275000,-0.999999000000000000)));
err=MathMax(err,MathAbs(0.152273697664791000-CNormalDistr::BivariateNormalCDF(0.192020298597275000,0.192020306187062000,-0.999999000000000000)));
err=MathMax(err,MathAbs(0.869148589647661000-CNormalDistr::BivariateNormalCDF(2.095780141251770000,1.211768206209080000,-0.999990000000000000)));
err=MathMax(err,MathAbs(0.963898301217005000-CNormalDistr::BivariateNormalCDF(2.095780141251770000,2.095780141251770000,-0.999990000000000000)));
err=MathMax(err,MathAbs(0.963898301217035000-CNormalDistr::BivariateNormalCDF(2.095780141251770000,2.095780141252440000,-0.999990000000000000)));
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-2.654007687508430000,1.340051767837440000,-0.999900000000000000)));
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-2.654007687508430000,-2.654007687508430000,-0.999900000000000000)));
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-2.654007687508430000,-2.654007613149620000,-0.999900000000000000)));
err=MathMax(err,MathAbs(0.000000000000000019-CNormalDistr::BivariateNormalCDF(1.128363097510700000,-2.781006578069910000,-0.999000000000000000)));
err=MathMax(err,MathAbs(0.740833394117601000-CNormalDistr::BivariateNormalCDF(1.128363097510700000,1.128363097510700000,-0.999000000000000000)));
err=MathMax(err,MathAbs(0.740862731628071000-CNormalDistr::BivariateNormalCDF(1.128363097510700000,1.128502099120660000,-0.999000000000000000)));
err=MathMax(err,MathAbs(0.995654456762395000-CNormalDistr::BivariateNormalCDF(3.966506850977000000,2.626841258388710000,-0.990000000000000000)));
err=MathMax(err,MathAbs(0.999927066319845000-CNormalDistr::BivariateNormalCDF(3.966506850977000000,3.966506850977000000,-0.990000000000000000)));
err=MathMax(err,MathAbs(0.999927066319846000-CNormalDistr::BivariateNormalCDF(3.966506850977000000,3.966506850981670000,-0.990000000000000000)));
err=MathMax(err,MathAbs(0.002769246133985220-CNormalDistr::BivariateNormalCDF(-2.717530641700190000,3.217920162027340000,-0.900000000000000000)));
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-2.717530641700190000,-2.717530641700190000,-0.900000000000000000)));
err=MathMax(err,MathAbs(0.000000000000000000-CNormalDistr::BivariateNormalCDF(-2.717530641700190000,-2.717530635181090000,-0.900000000000000000)));
err=MathMax(err,MathAbs(0.010079636581695100-CNormalDistr::BivariateNormalCDF(-1.811681729272450000,-2.262911120125770000,0.900000000000000000)));
err=MathMax(err,MathAbs(0.021493462833838000-CNormalDistr::BivariateNormalCDF(-1.811681729272450000,-1.811681729272450000,0.900000000000000000)));
err=MathMax(err,MathAbs(0.021493462833871500-CNormalDistr::BivariateNormalCDF(-1.811681729272450000,-1.811681729271170000,0.900000000000000000)));
err=MathMax(err,MathAbs(0.667398193291227000-CNormalDistr::BivariateNormalCDF(1.861468373436860000,0.432740073549983000,0.990000000000000000)));
err=MathMax(err,MathAbs(0.964688949692122000-CNormalDistr::BivariateNormalCDF(1.861468373436860000,1.861468373436860000,0.990000000000000000)));
err=MathMax(err,MathAbs(0.964688949699170000-CNormalDistr::BivariateNormalCDF(1.861468373436860000,1.861468373617680000,0.990000000000000000)));
err=MathMax(err,MathAbs(0.000000482786767864-CNormalDistr::BivariateNormalCDF(-4.898527851968480000,-3.491204153631050000,0.999000000000000000)));
err=MathMax(err,MathAbs(0.000000439041574222-CNormalDistr::BivariateNormalCDF(-4.898527851968480000,-4.898527851968480000,0.999000000000000000)));
err=MathMax(err,MathAbs(0.000000439041575582-CNormalDistr::BivariateNormalCDF(-4.898527851968480000,-4.898527850755250000,0.999000000000000000)));
err=MathMax(err,MathAbs(0.000416850246666766-CNormalDistr::BivariateNormalCDF(-3.341356669094100000,1.862802982022170000,0.999900000000000000)));
err=MathMax(err,MathAbs(0.000408379488679640-CNormalDistr::BivariateNormalCDF(-3.341356669094100000,-3.341356669094100000,0.999900000000000000)));
err=MathMax(err,MathAbs(0.000408379488680111-CNormalDistr::BivariateNormalCDF(-3.341356669094100000,-3.341356669093450000,0.999900000000000000)));
err=MathMax(err,MathAbs(0.741641759669778000-CNormalDistr::BivariateNormalCDF(4.687494092358740000,0.648415139929991000,0.999990000000000000)));
err=MathMax(err,MathAbs(0.999998605095178000-CNormalDistr::BivariateNormalCDF(4.687494092358740000,4.687494092358740000,0.999990000000000000)));
err=MathMax(err,MathAbs(0.999998606247630000-CNormalDistr::BivariateNormalCDF(4.687494092358740000,4.687843556049730000,0.999990000000000000)));
err=MathMax(err,MathAbs(0.874547330614982000-CNormalDistr::BivariateNormalCDF(2.369093655782270000,1.148153167494120000,0.999999000000000000)));
err=MathMax(err,MathAbs(0.991070530231016000-CNormalDistr::BivariateNormalCDF(2.369093655782270000,2.369093655782270000,0.999999000000000000)));
err=MathMax(err,MathAbs(0.991070530231065000-CNormalDistr::BivariateNormalCDF(2.369093655782270000,2.369093655786410000,0.999999000000000000)));
err=MathMax(err,MathAbs(0.999362948580782000-CNormalDistr::BivariateNormalCDF(4.352384277131720000,3.221749932900420000,0.999999900000000000)));
err=MathMax(err,MathAbs(0.999993261271150000-CNormalDistr::BivariateNormalCDF(4.352384277131720000,4.352384277131720000,0.999999900000000000)));
err=MathMax(err,MathAbs(0.999993261272904000-CNormalDistr::BivariateNormalCDF(4.352384277131720000,4.352384391237480000,0.999999900000000000)));
err=MathMax(err,MathAbs(0.999069094420465000-CNormalDistr::BivariateNormalCDF(3.422289814750960000,3.111433954663520000,0.999999990000000000)));
err=MathMax(err,MathAbs(0.999689455134645000-CNormalDistr::BivariateNormalCDF(3.422289814750960000,3.422289814750960000,0.999999990000000000)));
err=MathMax(err,MathAbs(0.999689455134658000-CNormalDistr::BivariateNormalCDF(3.422289814750960000,3.422289814777010000,0.999999990000000000)));
err=MathMax(err,MathAbs(0.294719708527162000-CNormalDistr::BivariateNormalCDF(-0.539648565868907000,2.177679562057720000,0.999999999000000000)));
err=MathMax(err,MathAbs(0.294713555379044000-CNormalDistr::BivariateNormalCDF(-0.539648565868907000,-0.539648565868907000,0.999999999000000000)));
err=MathMax(err,MathAbs(0.294718518285085000-CNormalDistr::BivariateNormalCDF(-0.539648565868907000,-0.539602058260055000,0.999999999000000000)));
err=MathMax(err,MathAbs(0.000002811467986141-CNormalDistr::BivariateNormalCDF(-4.540093229575050000,2.436946780486250000,0.999999999900000000)));
err=MathMax(err,MathAbs(0.000002811392754616-CNormalDistr::BivariateNormalCDF(-4.540093229575050000,-4.540093229575050000,0.999999999900000000)));
err=MathMax(err,MathAbs(0.000002811467986126-CNormalDistr::BivariateNormalCDF(-4.540093229575050000,-4.540001786779430000,0.999999999900000000)));
err=MathMax(err,MathAbs(0.565106870340374000-CNormalDistr::BivariateNormalCDF(0.163929988133744000,3.995097146641120000,0.999999999990000000)));
err=MathMax(err,MathAbs(0.565106168077456000-CNormalDistr::BivariateNormalCDF(0.163929988133744000,0.163929988133744000,0.999999999990000000)));
err=MathMax(err,MathAbs(0.565106168096292000-CNormalDistr::BivariateNormalCDF(0.163929988133744000,0.163929988229317000,0.999999999990000000)));
err=MathMax(err,MathAbs(0.000064751025417698-CNormalDistr::BivariateNormalCDF(3.421155338081630000,-3.827403648909790000,0.999999999999000000)));
err=MathMax(err,MathAbs(0.999688220825439000-CNormalDistr::BivariateNormalCDF(3.421155338081630000,3.421155338081630000,0.999999999999000000)));
err=MathMax(err,MathAbs(0.999688221472430000-CNormalDistr::BivariateNormalCDF(3.421155338081630000,3.421174755498880000,0.999999999999000000)));
CAp::SetErrorFlag(errorflag,err>(1.0E-12),"testnormaldistrunit.ap:195");
//--- Test that BVN PDF is roughly equal to derivative of BVN CDF
for(int k=0; k<=999; k++)
{
//--- Generate trial point
x=CHighQualityRand::HQRndNormal(rs);
y=CHighQualityRand::HQRndNormal(rs);
rho=0;
//--- Compare two values: normal PDF and differentiation of normal CDF with step H
h=1.0E-5;
v0=CNormalDistr::BivariateNormalPDF(x,y,rho);
v1=(CNormalDistr::BivariateNormalCDF(x+h,y+h,rho)+CNormalDistr::BivariateNormalCDF(x-h,y-h,rho)-CNormalDistr::BivariateNormalCDF(x+h,y-h,rho)-CNormalDistr::BivariateNormalCDF(x-h,y+h,rho))/CMath::Sqr(2*h);
CAp::SetErrorFlag(errorflag,MathAbs(v0-v1)>(1.0E-4),"testnormaldistrunit.ap:215");
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestWSRUnit
{
public:
CTestWSRUnit(void) {};
~CTestWSRUnit(void) {};
//---
static bool TestWSR(bool silent);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestWSRUnit::TestWSR(bool silent)
{
//--- create variables
bool result;
bool waserrors;
CRowDouble xa;
int n=0;
int i=0;
double taill=0;
double tailr=0;
double tailb=0;
double taillprev=0;
double tailrprev=0;
double ebase=0;
double eshift=0;
waserrors=false;
//--- Test monotonicity of tail values for moving value of E
for(n=5; n<=50; n++)
{
//--- Generate uniform and sorted X spanning [0,1]
xa=vector<double>::Zeros(n);
for(i=0; i<n; i++)
xa.Set(i,(double)i/(double)(n-1));
//--- Test N+1 values of E
ebase=-(0.5/(n-1));
eshift=(double)1/(double)(n-1);
tailrprev=0;
taillprev=1;
for(i=0; i<=n; i++)
{
CWilcoxonSignedRank::WilcoxonSignedRankTest(xa,n,ebase+eshift*i,tailb,taill,tailr);
CAp::SetErrorFlag(waserrors,tailb!=(2*MathMin(taill,tailr)),"testwsrunit.ap:42");
CAp::SetErrorFlag(waserrors,tailrprev>tailr,"testwsrunit.ap:43");
CAp::SetErrorFlag(waserrors,taillprev<taill,"testwsrunit.ap:44");
tailrprev=tailr;
taillprev=taill;
}
}
//--- Test for integer overflow in the function: if one crucial
//--- calculation step is performed in 32-bit integer arithmetics,
//--- it will return incorrect results.
//--- We use special handcrafted N, such that in 32-bit integer
//--- arithmetics int32(N*N)<0. Such negative N leads to domain
//--- error in the sqrt() function.
n=50000;
xa=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
xa.Set(i,MathSin(10*i));
}
CWilcoxonSignedRank::WilcoxonSignedRankTest(xa,n,0.0,tailb,taill,tailr);
CAp::SetErrorFlag(waserrors,!MathIsValidNumber(tailb),"testwsrunit.ap:64");
CAp::SetErrorFlag(waserrors,!MathIsValidNumber(taill),"testwsrunit.ap:65");
CAp::SetErrorFlag(waserrors,!MathIsValidNumber(tailr),"testwsrunit.ap:66");
if(!silent)
PrintResult("TEST SUMMARY",!waserrors);
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestMannWhitneyUUnit
{
public:
CTestMannWhitneyUUnit(void) {};
~CTestMannWhitneyUUnit(void) {};
//---
static bool TestMannWhitneyU(bool silent);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestMannWhitneyUUnit::TestMannWhitneyU(bool silent)
{
//--- create variables
bool result;
bool waserrors;
CRowDouble x;
CRowDouble y;
int testmin=0;
int testmax=0;
int testcnt=0;
int pass=0;
int n=0;
int m=0;
int i=0;
int k=0;
double taill=0;
double tailr=0;
double tailb=0;
double taill1=0;
double tailr1=0;
double tailb1=0;
double taillprev=0;
double tailrprev=0;
double ebase=0;
double eshift=0;
int ecnt=0;
double worsterr=0;
double v=0;
CHighQualityRandState rs;
waserrors=false;
CHighQualityRand::HQRndRandomize(rs);
//--- Test monotonicity of tail values for monotinically moving distributions.
for(n=5; n<=20; n++)
{
for(m=5; m<=20; m++)
{
//--- Generate uniform and sorted X/Y spanning [0,1]
x=vector<double>::Zeros(n);
for(i=0; i<n; i++)
x.Set(i,(double)i/(double)(n-1)+100*CMath::m_machineepsilon*CHighQualityRand::HQRndNormal(rs));
y=vector<double>::Zeros(m);
for(i=0; i<m; i++)
y.Set(i,(double)i/(double)(m-1)+100*CMath::m_machineepsilon*CHighQualityRand::HQRndNormal(rs));
//--- Test 100 values of E
ecnt=100;
ebase=-1.1;
eshift=-(2*ebase/(ecnt-1));
tailrprev=0;
taillprev=1;
y+=ebase;
for(k=0; k<=ecnt-1; k++)
{
CMannWhitneyU::CMannWhitneyUTest(x,n,y,m,tailb,taill,tailr);
CAp::SetErrorFlag(waserrors,tailb!=(2*MathMin(taill,tailr)),"testmannwhitneyuunit.ap:57");
CAp::SetErrorFlag(waserrors,tailrprev>tailr,"testmannwhitneyuunit.ap:58");
CAp::SetErrorFlag(waserrors,taillprev<taill,"testmannwhitneyuunit.ap:59");
tailrprev=tailr;
taillprev=taill;
y+=eshift;
}
}
}
//--- Test frequency of p-value 0.05
testmin=5;
testmax=50;
testcnt=10000;
worsterr=0.0;
for(n=testmin; n<=testmax; n++)
{
m=n+CHighQualityRand::HQRndUniformI(rs,testmax-n+1);
x=vector<double>::Zeros(n);
y=vector<double>::Zeros(m);
//--- Generate two uniformly distributed values, calculate p-value for both-tails, repeat
k=0;
for(pass=0; pass<=testcnt-1; pass++)
{
for(i=0; i<n; i++)
x.Set(i,CHighQualityRand::HQRndUniformR(rs));
for(i=0; i<m; i++)
y.Set(i,CHighQualityRand::HQRndUniformR(rs));
CMannWhitneyU::CMannWhitneyUTest(x,n,y,m,tailb,taill,tailr);
if(tailb<0.05)
k++;
}
v=MathAbs((double)k/(double)testcnt-0.05);
worsterr=MathMax(worsterr,v);
//--- Test error in quantile; for different N's we have different tolerances
if(n<10)
CAp::SetErrorFlag(waserrors,v>0.035,"testmannwhitneyuunit.ap:101");
else
{
if(n<15)
CAp::SetErrorFlag(waserrors,v>0.025,"testmannwhitneyuunit.ap:103");
else
{
if(n<30)
CAp::SetErrorFlag(waserrors,v>0.020,"testmannwhitneyuunit.ap:105");
else
CAp::SetErrorFlag(waserrors,v>0.015,"testmannwhitneyuunit.ap:107");
}
}
}
//--- Test symmetry properties
for(n=5; n<=50; n++)
{
for(m=5; m<=50; m++)
{
x=vector<double>::Zeros(n);
y=vector<double>::Zeros(m);
for(i=0; i<n; i++)
x.Set(i,CHighQualityRand::HQRndUniformR(rs));
for(i=0; i<m; i++)
y.Set(i,CHighQualityRand::HQRndUniformR(rs));
CMannWhitneyU::CMannWhitneyUTest(x,n,y,m,tailb,taill,tailr);
CMannWhitneyU::CMannWhitneyUTest(y,m,x,n,tailb1,taill1,tailr1);
CAp::SetErrorFlag(waserrors,MathAbs(tailb-tailb1)>(1.0E-12),"testmannwhitneyuunit.ap:126");
CAp::SetErrorFlag(waserrors,MathAbs(taill-tailr1)>(1.0E-12),"testmannwhitneyuunit.ap:127");
CAp::SetErrorFlag(waserrors,MathAbs(tailr-taill1)>(1.0E-12),"testmannwhitneyuunit.ap:128");
}
}
//--- Test for integer overflow in the function: if one crucial
//--- calculation step is performed in 32-bit integer arithmetics,
//--- it will return incorrect results.
//--- We use special handcrafted N, such that in 32-bit integer
//--- arithmetics int32(N*N)<0. Such negative N leads to domain
//--- error in the sqrt() function.
n=50000;
x=vector<double>::Zeros(n);
y=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
x.Set(i,MathSin(10*i));
y.Set(i,MathSin(13*i));
}
CMannWhitneyU::CMannWhitneyUTest(x,n,y,n,tailb,taill,tailr);
CAp::SetErrorFlag(waserrors,!MathIsValidNumber(tailb),"testmannwhitneyuunit.ap:149");
CAp::SetErrorFlag(waserrors,!MathIsValidNumber(taill),"testmannwhitneyuunit.ap:150");
CAp::SetErrorFlag(waserrors,!MathIsValidNumber(tailr),"testmannwhitneyuunit.ap:151");
if(!silent)
PrintResult("TEST SUMMARY",!waserrors);
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestSTestUnit
{
public:
static bool TestSTest(bool silent);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestSTestUnit::TestSTest(bool silent)
{
//--- create variables
bool waserrors=false;
double eps=1.0E-3;
bool result;
CRowDouble x;
double taill=0;
double tailr=0;
double tailb=0;
//--- Test 1
x=vector<double>::Zeros(6);
x.Set(0,-3);
x.Set(1,-2);
x.Set(2,-1);
x.Set(3,1);
x.Set(4,2);
x.Set(5,3);
CSignTest::OneSampleSignTest(x,6,0.0,tailb,taill,tailr);
waserrors=waserrors||MathAbs(taill-0.65625)>eps;
waserrors=waserrors||MathAbs(tailr-0.65625)>eps;
waserrors=waserrors||MathAbs(tailb-1.00000)>eps;
CSignTest::OneSampleSignTest(x,6,-1.0,tailb,taill,tailr);
waserrors=waserrors||MathAbs(taill-0.81250)>eps;
waserrors=waserrors||MathAbs(tailr-0.50000)>eps;
waserrors=waserrors||MathAbs(tailb-1.00000)>eps;
CSignTest::OneSampleSignTest(x,6,-1.5,tailb,taill,tailr);
waserrors=waserrors||MathAbs(taill-0.89062)>eps;
waserrors=waserrors||MathAbs(tailr-0.34375)>eps;
waserrors=waserrors||MathAbs(tailb-0.68750)>eps;
CSignTest::OneSampleSignTest(x,6,-3.0,tailb,taill,tailr);
waserrors=waserrors||MathAbs(taill-1.00000)>eps;
waserrors=waserrors||MathAbs(tailr-0.03125)>eps;
waserrors=waserrors||MathAbs(tailb-0.06250)>eps;
//--- Test 2
x=vector<double>::Full(3,2);
CSignTest::OneSampleSignTest(x,3,2.0,tailb,taill,tailr);
waserrors=waserrors||taill!=1.0;
waserrors=waserrors||tailr!=1.0;
waserrors=waserrors||tailb!=1.0;
//--- Final report
if(!silent)
{
PrintResult("SIGN TEST",!waserrors);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestStudentTestsUnit
{
public:
static bool TestStudentTests(bool silent);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestStudentTestsUnit::TestStudentTests(bool silent)
{
//--- create variables
bool waserrors=false;
double eps=0.001;
bool result;
CRowDouble x;
CRowDouble y;
CRowDouble xa;
CRowDouble ya;
CRowDouble xb;
CRowDouble yb;
int n=0;
int i=0;
double taill=0;
double tailr=0;
double tailb=0;
double taill1=0;
double tailr1=0;
double tailb1=0;
//--- 1-sample test
n=8;
x.Resize(8);
x.Set(0,-3.0);
x.Set(1,-1.5);
x.Set(2,-1.0);
x.Set(3,-0.5);
x.Set(4,0.5);
x.Set(5,1.0);
x.Set(6,1.5);
x.Set(7,3.0);
CStudentTests::StudentTest1(x,n,0.0,tailb,taill,tailr);
waserrors=waserrors||MathAbs(tailb-1.00000)>eps;
waserrors=waserrors||MathAbs(taill-0.50000)>eps;
waserrors=waserrors||MathAbs(tailr-0.50000)>eps;
CStudentTests::StudentTest1(x,n,1.0,tailb,taill,tailr);
waserrors=waserrors||MathAbs(tailb-0.17816)>eps;
waserrors=waserrors||MathAbs(taill-0.08908)>eps;
waserrors=waserrors||MathAbs(tailr-0.91092)>eps;
CStudentTests::StudentTest1(x,n,-1.0,tailb,taill,tailr);
waserrors=waserrors||MathAbs(tailb-0.17816)>eps;
waserrors=waserrors||MathAbs(taill-0.91092)>eps;
waserrors=waserrors||MathAbs(tailr-0.08908)>eps;
x=vector<double>::Full(8,1.1);
CStudentTests::StudentTest1(x,n,1.1,tailb,taill,tailr);
waserrors=waserrors||tailb!=1.0;
waserrors=waserrors||taill!=1.0;
waserrors=waserrors||tailr!=1.0;
CStudentTests::StudentTest1(x,n,0.0,tailb,taill,tailr);
waserrors=waserrors||tailb!=0.0;
waserrors=waserrors||taill!=1.0;
waserrors=waserrors||tailr!=0.0;
x.Set(7,1.1);
CStudentTests::StudentTest1(x,1,1.1,tailb,taill,tailr);
waserrors=waserrors||tailb!=1.0;
waserrors=waserrors||taill!=1.0;
waserrors=waserrors||tailr!=1.0;
CStudentTests::StudentTest1(x,1,0.0,tailb,taill,tailr);
waserrors=waserrors||tailb!=0.0;
waserrors=waserrors||taill!=1.0;
waserrors=waserrors||tailr!=0.0;
//--- 2-sample pooled (equal variance) test
n=8;
x.Resize(8);
y.Resize(8);
x.Set(0,-3.0);
x.Set(1,-1.5);
x.Set(2,-1.0);
x.Set(3,-0.5);
x.Set(4,0.5);
x.Set(5,1.0);
x.Set(6,1.5);
x.Set(7,3.0);
y.Set(0,-2.0);
y.Set(1,-0.5);
y.Set(2,0.0);
y.Set(3,0.5);
y.Set(4,1.5);
y.Set(5,2.0);
y.Set(6,2.5);
y.Set(7,4.0);
CStudentTests::StudentTest2(x,n,y,n,tailb,taill,tailr);
waserrors=waserrors||MathAbs(tailb-0.30780)>eps;
waserrors=waserrors||MathAbs(taill-0.15390)>eps;
waserrors=waserrors||MathAbs(tailr-0.84610)>eps;
CStudentTests::StudentTest2(x,n,y,n-1,tailb,taill,tailr);
waserrors=waserrors||MathAbs(tailb-0.53853)>eps;
waserrors=waserrors||MathAbs(taill-0.26927)>eps;
waserrors=waserrors||MathAbs(tailr-0.73074)>eps;
CStudentTests::StudentTest2(x,n-1,y,n,tailb,taill,tailr);
waserrors=waserrors||MathAbs(tailb-0.13829)>eps;
waserrors=waserrors||MathAbs(taill-0.06915)>eps;
waserrors=waserrors||MathAbs(tailr-0.93086)>eps;
x=vector<double>::Full(8,-1.0);
y=vector<double>::Ones(8);
CStudentTests::StudentTest2(x,n,y,n,tailb,taill,tailr);
waserrors=waserrors||tailb!=0.0;
waserrors=waserrors||taill!=0.0;
waserrors=waserrors||tailr!=1.0;
CStudentTests::StudentTest2(x,n,y,n-1,tailb,taill,tailr);
waserrors=waserrors||tailb!=0.0;
waserrors=waserrors||taill!=0.0;
waserrors=waserrors||tailr!=1.0;
CStudentTests::StudentTest2(x,n,y,1,tailb,taill,tailr);
waserrors=waserrors||tailb!=0.0;
waserrors=waserrors||taill!=0.0;
waserrors=waserrors||tailr!=1.0;
CStudentTests::StudentTest2(x,n-1,y,n,tailb,taill,tailr);
waserrors=waserrors||tailb!=0.0;
waserrors=waserrors||taill!=0.0;
waserrors=waserrors||tailr!=1.0;
CStudentTests::StudentTest2(x,1,y,n,tailb,taill,tailr);
waserrors=waserrors||tailb!=0.0;
waserrors=waserrors||taill!=0.0;
waserrors=waserrors||tailr!=1.0;
CStudentTests::StudentTest2(x,1,y,1,tailb,taill,tailr);
waserrors=waserrors||tailb!=0.0;
waserrors=waserrors||taill!=0.0;
waserrors=waserrors||tailr!=1.0;
CStudentTests::StudentTest2(y,1,x,1,tailb,taill,tailr);
waserrors=waserrors||tailb!=0.0;
waserrors=waserrors||taill!=1.0;
waserrors=waserrors||tailr!=0.0;
x=vector<double>::Full(8,1.1);
y=vector<double>::Full(8,1.1);
CStudentTests::StudentTest2(x,n,y,n,tailb,taill,tailr);
waserrors=waserrors||tailb!=1.0;
waserrors=waserrors||taill!=1.0;
waserrors=waserrors||tailr!=1.0;
CStudentTests::StudentTest2(x,n,y,n-1,tailb,taill,tailr);
waserrors=waserrors||tailb!=1.0;
waserrors=waserrors||taill!=1.0;
waserrors=waserrors||tailr!=1.0;
CStudentTests::StudentTest2(x,n,y,1,tailb,taill,tailr);
waserrors=waserrors||tailb!=1.0;
waserrors=waserrors||taill!=1.0;
waserrors=waserrors||tailr!=1.0;
CStudentTests::StudentTest2(x,n-1,y,n,tailb,taill,tailr);
waserrors=waserrors||tailb!=1.0;
waserrors=waserrors||taill!=1.0;
waserrors=waserrors||tailr!=1.0;
CStudentTests::StudentTest2(x,1,y,n,tailb,taill,tailr);
waserrors=waserrors||tailb!=1.0;
waserrors=waserrors||taill!=1.0;
waserrors=waserrors||tailr!=1.0;
CStudentTests::StudentTest2(x,1,y,1,tailb,taill,tailr);
waserrors=waserrors||tailb!=1.0;
waserrors=waserrors||taill!=1.0;
waserrors=waserrors||tailr!=1.0;
//--- 2-sample unpooled (unequal variance) test:
//--- * test on two non-constant samples
//--- * tests on different combinations of non-constant and constant samples
n=8;
xa.Resize(n);
ya.Resize(n);
xb.Resize(n);
yb.Resize(n);
xa.Set(0,-3.0);
xa.Set(1,-1.5);
xa.Set(2,-1.0);
xa.Set(3,-0.5);
xa.Set(4,0.5);
xa.Set(5,1.0);
xa.Set(6,1.5);
xa.Set(7,3.0);
ya.Set(0,-1.0);
ya.Set(1,-0.5);
ya.Set(2,0.0);
ya.Set(3,0.5);
ya.Set(4,1.5);
ya.Set(5,2.0);
ya.Set(6,2.5);
ya.Set(7,3.0);
xb.Set(0,-1.1);
xb.Set(1,-1.1);
xb.Set(2,-1.1);
xb.Set(3,-1.1);
xb.Set(4,-1.1);
xb.Set(5,-1.1);
xb.Set(6,-1.1);
xb.Set(7,-1.1);
yb.Set(0,1.1);
yb.Set(1,1.1);
yb.Set(2,1.1);
yb.Set(3,1.1);
yb.Set(4,1.1);
yb.Set(5,1.1);
yb.Set(6,1.1);
yb.Set(7,1.1);
CStudentTests::UnequalVarianceTest(xa,n,ya,n,tailb,taill,tailr);
waserrors=waserrors||MathAbs(tailb-0.25791)>eps;
waserrors=waserrors||MathAbs(taill-0.12896)>eps;
waserrors=waserrors||MathAbs(tailr-0.87105)>eps;
CStudentTests::UnequalVarianceTest(xa,n,yb,n,tailb,taill,tailr);
CStudentTests::StudentTest1(xa,n,1.1,tailb1,taill1,tailr1);
waserrors=waserrors||MathAbs(tailb-tailb1)>eps;
waserrors=waserrors||MathAbs(taill-taill1)>eps;
waserrors=waserrors||MathAbs(tailr-tailr1)>eps;
CStudentTests::UnequalVarianceTest(xa,n,yb,1,tailb,taill,tailr);
CStudentTests::StudentTest1(xa,n,1.1,tailb1,taill1,tailr1);
waserrors=waserrors||MathAbs(tailb-tailb1)>eps;
waserrors=waserrors||MathAbs(taill-taill1)>eps;
waserrors=waserrors||MathAbs(tailr-tailr1)>eps;
CStudentTests::UnequalVarianceTest(xb,n,ya,n,tailb,taill,tailr);
CStudentTests::StudentTest1(ya,n,-1.1,tailb1,taill1,tailr1);
waserrors=waserrors||MathAbs(tailb-tailb1)>eps;
waserrors=waserrors||MathAbs(taill-tailr1)>eps;
waserrors=waserrors||MathAbs(tailr-taill1)>eps;
CStudentTests::UnequalVarianceTest(xb,1,ya,n,tailb,taill,tailr);
CStudentTests::StudentTest1(ya,n,-1.1,tailb1,taill1,tailr1);
waserrors=waserrors||MathAbs(tailb-tailb1)>eps;
waserrors=waserrors||MathAbs(taill-tailr1)>eps;
waserrors=waserrors||MathAbs(tailr-taill1)>eps;
CStudentTests::UnequalVarianceTest(xb,1,yb,1,tailb,taill,tailr);
waserrors=waserrors||tailb!=0.0;
waserrors=waserrors||taill!=0.0;
waserrors=waserrors||tailr!=1.0;
CStudentTests::UnequalVarianceTest(yb,1,xb,1,tailb,taill,tailr);
waserrors=waserrors||tailb!=0.0;
waserrors=waserrors||taill!=1.0;
waserrors=waserrors||tailr!=0.0;
CStudentTests::UnequalVarianceTest(xb,1,xb,1,tailb,taill,tailr);
waserrors=waserrors||tailb!=1.0;
waserrors=waserrors||taill!=1.0;
waserrors=waserrors||tailr!=1.0;
//--- Test for integer overflow in the function: if one crucial
//--- calculation step is performed in 32-bit integer arithmetics,
//--- it will return incorrect results.
//--- We use special handcrafted N, such that in 32-bit integer
//--- arithmetics int32(N*N)<0. Such negative N leads to domain
//--- error in the incomplete beta function.
n=50000;
xa=vector<double>::Zeros(n);
ya=vector<double>::Zeros(n);
for(i=0; i<n; i++)
{
xa.Set(i,CMath::RandomReal());
ya.Set(i,CMath::RandomReal());
}
CStudentTests::UnequalVarianceTest(xa,n,ya,n,tailb,taill,tailr);
if(!silent)
PrintResult("TEST SUMMARY",!waserrors);
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestFitSphereUnit
{
public:
static bool TestFitSphere(bool silent);
private:
static void TestSphereFittingLS(bool &err);
static void TestSphereFittingNS(bool &err);
static void TestSphereFittingVosswinkel2(bool &err);
static void CalcRadII(CMatrixDouble &xy,int npoints,int nx,CRowDouble &cx,double &rlo,double &rhi);
static void CalcLSError(CMatrixDouble &xy,int npoints,int nx,CRowDouble &cx,double &err);
static void AddValue(CMatrixDouble &xy,int &cnt,double v);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestFitSphereUnit::TestFitSphere(bool silent)
{
//--- create variables
bool result;
bool nserrors;
bool lserrors;
bool waserrors;
nserrors=false;
lserrors=false;
//--- Sphere fitting, several different test suites
TestSphereFittingLS(lserrors);
TestSphereFittingNS(nserrors);
TestSphereFittingVosswinkel2(nserrors);
//--- report
waserrors=nserrors||lserrors;
if(!silent)
{
Print("TESTING FITSPHERE");
PrintResult("* LEAST SQUARES CIRCLE FITTING",!lserrors);
PrintResult("* NON-SMOOTH FITTING (MC,MI,MZ)",!nserrors);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| This function tests least squares (LS) sphere fitting using |
//| generic synthetic datasets |
//| On failure sets Err to True (leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
void CTestFitSphereUnit::TestSphereFittingLS(bool &err)
{
//--- create variables
CHighQualityRandState rs;
CMatrixDouble xy;
double xtol=0;
int npoints=0;
int nx=0;
CRowDouble cx;
CRowDouble cy;
double rlo=0;
double rhi=0;
double ftol=0;
int i=0;
int j=0;
int k=0;
double v=0;
double vv=0;
double v0=0;
double v1=0;
int problemtype=0;
CHighQualityRand::HQRndRandomize(rs);
xtol=1.0E-5;
//--- Generate random problem
for(nx=1; nx<=4; nx++)
{
//--- Generate synthetic dataset, at least 5 points
npoints=5+2*nx+CHighQualityRand::HQRndUniformI(rs,50+(int)MathRound(MathPow(4,nx)));
xy=matrix<double>::Zeros(npoints,nx);
for(i=0; i<npoints ; i++)
{
v=0;
for(j=0; j<nx ; j++)
{
vv=CHighQualityRand::HQRndNormal(rs);
v+=CMath::Sqr(vv);
xy.Set(i,j,vv);
}
if(!CAp::Assert(v>0.0))
{
err=true;
return;
}
v=(1+0.1*CHighQualityRand::HQRndUniformR(rs))/MathSqrt(v);
for(j=0; j<nx ; j++)
xy.Mul(i,j,v);
}
//--- Solve with generic solver, check
rlo=0;
rhi=0;
problemtype=0;
CFitSphere::FitSphereX(xy,npoints,nx,problemtype,0.0,0,0.0,cx,rlo,rhi);
CAp::SetErrorFlag(err,rlo!=rhi,"testfitsphereunit.ap:109");
vv=0.0;
for(i=0; i<npoints ; i++)
{
v=CAblasF::RDotVR(nx,cx,xy,i);
vv+=MathSqrt(v)/npoints;
}
CAp::SetErrorFlag(err,MathAbs(vv-rlo)>xtol,"testfitsphereunit.ap:118");
CalcLSError(xy,npoints,nx,cx,v0);
//--- Check that small perturbations to center position increase target function
//--- NOTE: in fact, we do allow small increase in target function - but no more
//--- than FTol=1E-6*XTol. It helps to avoid spurious error reports in
//--- degenerate cases.
ftol=1.0E-6*xtol;
for(j=0; j<nx ; j++)
{
cy=cx;
cy.Add(j,xtol);
CalcLSError(xy,npoints,nx,cy,v1);
CAp::SetErrorFlag(err,v1<(v0-ftol),"testfitsphereunit.ap:137");
cy=cx;
cy.Add(j,-xtol);
CalcLSError(xy,npoints,nx,cy,v1);
CAp::SetErrorFlag(err,v1<(v0-ftol),"testfitsphereunit.ap:144");
}
//--- Compare against results returned by specific solver
CFitSphere::FitSphereLS(xy,npoints,nx,cy,v);
CAp::SetErrorFlag(err,v!=rlo,"testfitsphereunit.ap:151");
for(j=0; j<nx; j++)
CAp::SetErrorFlag(err,cy[j]!=cx[j],"testfitsphereunit.ap:153");
}
}
//+------------------------------------------------------------------+
//| This function tests sphere fitting using generic synthetic |
//| datasets and non - smooth target functions(MC, MI, MZ fitting) |
//| On failure sets Err to True(leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
void CTestFitSphereUnit::TestSphereFittingNS(bool &err)
{
//--- create variables
CHighQualityRandState rs;
CMatrixDouble xy;
int npoints=0;
int nx=0;
CRowDouble cx;
CRowDouble cy;
double rlo=0;
double rhi=0;
double rlo2=0;
double rhi2=0;
double xtol=0;
double ftol=0;
int i=0;
int j=0;
int k=0;
double v=0;
double vv=0;
int problemtype=0;
double vlo=0;
double vhi=0;
CHighQualityRand::HQRndRandomize(rs);
xtol=1.0E-5;
//--- Generate random problem
for(nx=1; nx<=4; nx++)
{
//--- Generate synthetic dataset
npoints=50+(int)MathRound(MathPow(4,nx));
xy=matrix<double>::Zeros(npoints,nx);
for(i=0; i<npoints ; i++)
{
v=0;
for(j=0; j<nx ; j++)
{
vv=CHighQualityRand::HQRndNormal(rs);
v+=CMath::Sqr(vv);
xy.Set(i,j,vv);
}
CAp::Assert(v>0.0);
v=(1+0.1*CHighQualityRand::HQRndUniformR(rs))/MathSqrt(v);
for(j=0; j<nx ; j++)
xy.Mul(i,j,v);
}
//--- Perform various kinds of fit, NLC solver is used
for(problemtype=1; problemtype<=3; problemtype++)
{
//--- Solve with generic solver
rlo=0;
rhi=0;
CFitSphere::FitSphereX(xy,npoints,nx,problemtype,0.0,0,0.0,cx,rlo,rhi);
//--- Check that small perturbations to center position increase target function
//--- NOTE: in fact, we do allow small increase in target function - but no more
//--- than FTol=1E-6*XTol. It helps to avoid spurious error reports in
//--- degenerate cases.
ftol=1.0E-6*xtol;
cy=vector<double>::Zeros(nx);
if(problemtype==2 || problemtype==3)
vlo=1;
else
vlo=0;
if(problemtype==1 || problemtype==3)
vhi=1;
else
vhi=0;
for(j=0; j<nx ; j++)
{
cy=cx;
cy.Add(j,xtol);
CalcRadII(xy,npoints,nx,cy,rlo2,rhi2);
CAp::SetErrorFlag(err,(rhi2*vhi-rlo2*vlo)<(rhi*vhi-rlo*vlo-ftol),"testfitsphereunit.ap:245");
cy=cx;
cy.Add(j,-xtol);
CalcRadII(xy,npoints,nx,cy,rlo2,rhi2);
CAp::SetErrorFlag(err,(rhi2*vhi-rlo2*vlo)<(rhi*vhi-rlo*vlo-ftol),"testfitsphereunit.ap:252");
}
//--- Compare against results returned by specific solver
if(problemtype==1)
{
CFitSphere::FitSphereMC(xy,npoints,nx,cy,rhi2);
CAp::SetErrorFlag(err,rhi2!=rhi,"testfitsphereunit.ap:261");
for(j=0; j<nx ; j++)
CAp::SetErrorFlag(err,cy[j]!=cx[j],"testfitsphereunit.ap:263");
}
if(problemtype==2)
{
CFitSphere::FitSphereMI(xy,npoints,nx,cy,rlo2);
CAp::SetErrorFlag(err,rlo2!=rlo,"testfitsphereunit.ap:268");
for(j=0; j<nx ; j++)
CAp::SetErrorFlag(err,cy[j]!=cx[j],"testfitsphereunit.ap:270");
}
if(problemtype==3)
{
CFitSphere::FitSphereMZ(xy,npoints,nx,cy,rlo2,rhi2);
CAp::SetErrorFlag(err,rlo2!=rlo,"testfitsphereunit.ap:275");
CAp::SetErrorFlag(err,rhi2!=rhi,"testfitsphereunit.ap:276");
for(j=0; j<nx ; j++)
CAp::SetErrorFlag(err,cy[j]!=cx[j],"testfitsphereunit.ap:278");
}
}
}
}
//+------------------------------------------------------------------+
//| This function tests sphere fitting |
//| On failure sets Err to True(leaves it unchanged otherwise) |
//+------------------------------------------------------------------+
void CTestFitSphereUnit::TestSphereFittingVosswinkel2(bool &err)
{
//--- create variables
CHighQualityRandState rs;
CMatrixDouble xy;
int cnt=0;
CRowDouble cx;
double rlo=0;
double rhi=0;
double tol=0;
CHighQualityRand::HQRndRandomize(rs);
//--- Test problem #2 by Vosswinkel GmbH
xy=matrix<double>::Zeros(40,2);
cnt=0;
AddValue(xy,cnt,0.1026);
AddValue(xy,cnt,0.000036);
AddValue(xy,cnt,0.101119);
AddValue(xy,cnt,0.016144);
AddValue(xy,cnt,0.096754);
AddValue(xy,cnt,0.031654);
AddValue(xy,cnt,0.088981);
AddValue(xy,cnt,0.045634);
AddValue(xy,cnt,0.082056);
AddValue(xy,cnt,0.06008);
AddValue(xy,cnt,0.074966);
AddValue(xy,cnt,0.075647);
AddValue(xy,cnt,0.065);
AddValue(xy,cnt,0.090471);
AddValue(xy,cnt,0.052411);
AddValue(xy,cnt,0.104381);
AddValue(xy,cnt,0.036436);
AddValue(xy,cnt,0.114859);
AddValue(xy,cnt,0.019034);
AddValue(xy,cnt,0.126577);
AddValue(xy,cnt,-0.001191);
AddValue(xy,cnt,0.139295);
AddValue(xy,cnt,-0.024689);
AddValue(xy,cnt,0.147143);
AddValue(xy,cnt,-0.049729);
AddValue(xy,cnt,0.147861);
AddValue(xy,cnt,-0.076402);
AddValue(xy,cnt,0.145907);
AddValue(xy,cnt,-0.103928);
AddValue(xy,cnt,0.139553);
AddValue(xy,cnt,-0.133726);
AddValue(xy,cnt,0.130429);
AddValue(xy,cnt,-0.159051);
AddValue(xy,cnt,0.112298);
AddValue(xy,cnt,-0.179496);
AddValue(xy,cnt,0.08821);
AddValue(xy,cnt,-0.194562);
AddValue(xy,cnt,0.059989);
AddValue(xy,cnt,-0.204838);
AddValue(xy,cnt,0.029135);
AddValue(xy,cnt,-0.206971);
AddValue(xy,cnt,-0.00349);
AddValue(xy,cnt,-0.206207);
AddValue(xy,cnt,-0.036427);
AddValue(xy,cnt,-0.197079);
AddValue(xy,cnt,-0.06806);
AddValue(xy,cnt,-0.180492);
AddValue(xy,cnt,-0.096353);
AddValue(xy,cnt,-0.158203);
AddValue(xy,cnt,-0.119891);
AddValue(xy,cnt,-0.132669);
AddValue(xy,cnt,-0.138375);
AddValue(xy,cnt,-0.105652);
AddValue(xy,cnt,-0.152229);
AddValue(xy,cnt,-0.078587);
AddValue(xy,cnt,-0.16316);
AddValue(xy,cnt,-0.049984);
AddValue(xy,cnt,-0.167084);
AddValue(xy,cnt,-0.022067);
AddValue(xy,cnt,-0.165233);
AddValue(xy,cnt,0.004002);
AddValue(xy,cnt,-0.16075);
AddValue(xy,cnt,0.028058);
AddValue(xy,cnt,-0.151829);
AddValue(xy,cnt,0.050088);
AddValue(xy,cnt,-0.141178);
AddValue(xy,cnt,0.067646);
AddValue(xy,cnt,-0.124169);
AddValue(xy,cnt,0.081421);
AddValue(xy,cnt,-0.10567);
AddValue(xy,cnt,0.087305);
AddValue(xy,cnt,-0.082618);
AddValue(xy,cnt,0.094189);
AddValue(xy,cnt,-0.064399);
AddValue(xy,cnt,0.099445);
AddValue(xy,cnt,-0.047018);
AddValue(xy,cnt,0.09936);
AddValue(xy,cnt,-0.028981);
AddValue(xy,cnt,0.101784);
AddValue(xy,cnt,-0.012918);
tol=1.0E-7;
//--- MZ problem, NLC solver
rlo=0;
rhi=0;
CFitSphere::FitSphereMZ(xy,CAp::Rows(xy),CAp::Cols(xy),cx,rlo,rhi);
CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:395");
if(err)
return;
CAp::SetErrorFlag(err,MathAbs(cx[0]+0.050884688)>tol,"testfitsphereunit.ap:398");
CAp::SetErrorFlag(err,MathAbs(cx[1]+0.011472328)>tol,"testfitsphereunit.ap:399");
CAp::SetErrorFlag(err,MathAbs(rlo-0.150973382)>tol,"testfitsphereunit.ap:400");
CAp::SetErrorFlag(err,MathAbs(rhi-0.164374709)>tol,"testfitsphereunit.ap:401");
rlo=0;
rhi=0;
CFitSphere::FitSphereX(xy,CAp::Rows(xy),CAp::Cols(xy),3,0.0,0,0.0,cx,rlo,rhi);
CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:406");
if(err)
return;
CAp::SetErrorFlag(err,MathAbs(cx[0]+0.050884688)>tol,"testfitsphereunit.ap:409");
CAp::SetErrorFlag(err,MathAbs(cx[1]+0.011472328)>tol,"testfitsphereunit.ap:410");
CAp::SetErrorFlag(err,MathAbs(rlo-0.150973382)>tol,"testfitsphereunit.ap:411");
CAp::SetErrorFlag(err,MathAbs(rhi-0.164374709)>tol,"testfitsphereunit.ap:412");
//--- MC problem, NLC solver
rlo=0;
rhi=0;
CFitSphere::FitSphereMC(xy,CAp::Rows(xy),CAp::Cols(xy),cx,rhi);
CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:421");
if(err)
return;
CAp::SetErrorFlag(err,MathAbs(cx[0]+0.051137580)>tol,"testfitsphereunit.ap:424");
CAp::SetErrorFlag(err,MathAbs(cx[1]+0.011680985)>tol,"testfitsphereunit.ap:425");
CAp::SetErrorFlag(err,rlo!=0.0,"testfitsphereunit.ap:426");
CAp::SetErrorFlag(err,MathAbs(rhi-0.164365735)>tol,"testfitsphereunit.ap:427");
rlo=0;
rhi=0;
CFitSphere::FitSphereX(xy,CAp::Rows(xy),CAp::Cols(xy),1,0.0,0,0.0,cx,rlo,rhi);
CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:432");
if(err)
return;
CAp::SetErrorFlag(err,MathAbs(cx[0]+0.051137580)>tol,"testfitsphereunit.ap:435");
CAp::SetErrorFlag(err,MathAbs(cx[1]+0.011680985)>tol,"testfitsphereunit.ap:436");
CAp::SetErrorFlag(err,rlo!=0.0,"testfitsphereunit.ap:437");
CAp::SetErrorFlag(err,MathAbs(rhi-0.164365735)>tol,"testfitsphereunit.ap:438");
//--- MI problem, NLC solver
rlo=0;
rhi=0;
CFitSphere::FitSphereMI(xy,CAp::Rows(xy),CAp::Cols(xy),cx,rlo);
CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:447");
if(err)
return;
CAp::SetErrorFlag(err,MathAbs(cx[0]+0.054593489)>tol,"testfitsphereunit.ap:450");
CAp::SetErrorFlag(err,MathAbs(cx[1]+0.007459466)>tol,"testfitsphereunit.ap:451");
CAp::SetErrorFlag(err,MathAbs(rlo-0.152429205)>tol,"testfitsphereunit.ap:452");
CAp::SetErrorFlag(err,rhi!=0.0,"testfitsphereunit.ap:453");
rlo=0;
rhi=0;
CFitSphere::FitSphereX(xy,CAp::Rows(xy),CAp::Cols(xy),2,0.0,0,0.0,cx,rlo,rhi);
CAp::SetErrorFlag(err,CAp::Len(cx)!=2,"testfitsphereunit.ap:458");
if(err)
return;
CAp::SetErrorFlag(err,MathAbs(cx[0]+0.054593489)>tol,"testfitsphereunit.ap:461");
CAp::SetErrorFlag(err,MathAbs(cx[1]+0.007459466)>tol,"testfitsphereunit.ap:462");
CAp::SetErrorFlag(err,MathAbs(rlo-0.152429205)>tol,"testfitsphereunit.ap:463");
CAp::SetErrorFlag(err,rhi!=0.0,"testfitsphereunit.ap:464");
}
//+------------------------------------------------------------------+
//| Used to calculate RLo / Rhi given XY and center position |
//+------------------------------------------------------------------+
void CTestFitSphereUnit::CalcRadII(CMatrixDouble &xy,
int npoints,
int nx,
CRowDouble &cx,
double &rlo,
double &rhi)
{
double v=0;
rlo=0;
rhi=0;
rlo=CMath::m_maxrealnumber;
rhi=0;
for(int i=0; i<npoints ; i++)
{
v=0;
for(int j=0; j<nx ; j++)
v+=CMath::Sqr(xy.Get(i,j)-cx[j]);
v=MathSqrt(v);
rhi=MathMax(rhi,v);
rlo=MathMin(rlo,v);
}
}
//+------------------------------------------------------------------+
//| Used to calculate least squares error given XY and center |
//| position |
//+------------------------------------------------------------------+
void CTestFitSphereUnit::CalcLSError(CMatrixDouble &xy,
int npoints,
int nx,
CRowDouble &cx,
double &err)
{
//--- create variables
int i=0;
int j=0;
double v=0;
double rad=0;
err=0;
rad=0.0;
for(i=0; i<npoints ; i++)
{
v=0;
for(j=0; j<nx ; j++)
v+=CMath::Sqr(cx[j]-xy.Get(i,j));
rad+=MathSqrt(v)/npoints;
}
err=0.0;
for(i=0; i<npoints ; i++)
{
v=0;
for(j=0; j<nx ; j++)
v+=CMath::Sqr(cx[j]-xy.Get(i,j));
err+=CMath::Sqr(rad-MathSqrt(v));
}
}
//+------------------------------------------------------------------+
//| Used to initialize dynamic array with constant values |
//+------------------------------------------------------------------+
void CTestFitSphereUnit::AddValue(CMatrixDouble &xy,
int &cnt,
double v)
{
xy.Set(cnt/CAp::Cols(xy),cnt%CAp::Cols(xy),v);
cnt++;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestSpline3DUnit
{
public:
static bool TestSpline3D(bool silence);
private:
static bool BasicTest(void);
static bool TestUnpack(void);
static bool TestLinTrans(void);
static bool TestTrilinearreSample(void);
static void BuildRndGrid(bool isvect,bool reorder,int &n,int &m,int &l,int &d,CRowDouble &x,CRowDouble &y,CRowDouble &z,CRowDouble &f);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestSpline3DUnit::TestSpline3D(bool silence)
{
//--- create variables
bool result;
bool waserrors;
bool basicerr;
bool unpackerr;
bool lintransferr;
bool trilinreserr;
basicerr=BasicTest();
unpackerr=TestUnpack();
lintransferr=TestLinTrans();
trilinreserr=TestTrilinearreSample();
waserrors=((basicerr||unpackerr)||lintransferr)||trilinreserr;
if(!silence)
{
Print("TESTING 3D SPLINE");
PrintResult("BASIC TEST",!basicerr);
PrintResult("UNPACK TEST",!unpackerr);
PrintResult("LIN_TRANSF TEST",!lintransferr);
PrintResult("TRILINEAR RESAMPLING TEST",!trilinreserr);
//--- Summary
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| The function does test basic functionality. |
//+------------------------------------------------------------------+
bool CTestSpline3DUnit::BasicTest(void)
{
//--- create variables
double eps=1000*CMath::m_machineepsilon;
CSpline3DInterpolant c;
CSpline3DInterpolant cc;
CRowDouble vvf;
double vsf=0;
int d=0;
int m=0;
int n=0;
int l=0;
CRowDouble x;
CRowDouble y;
CRowDouble z;
CRowDouble sf;
CRowDouble vf;
int pass=0;
int passcount=0;
int i=0;
int j=0;
int k=0;
int offs=0;
int di=0;
double ax=0;
double ay=0;
double az=0;
double axy=0;
double ayz=0;
double vx=0;
double vy=0;
double vz=0;
//--- Test spline ability to reproduce D-dimensional vector function
//--- f[idx](x,y,z) = idx+AX*x + AY*y + AZ*z + AXY*x*y + AYZ*y*z
//--- with random AX/AY/...
//--- We generate random test function, build spline, then evaluate
//--- it in the random test point.
for(d=1; d<=3; d++)
{
n=2+CMath::RandomInteger(4);
m=2+CMath::RandomInteger(4);
l=2+CMath::RandomInteger(4);
x=vector<double>::Zeros(n);
for(i=0; i<n; i++)
x.Set(i,i);
y=vector<double>::Zeros(m);
for(i=0; i<m; i++)
y.Set(i,i);
z=vector<double>::Zeros(l);
for(i=0; i<l; i++)
z.Set(i,i);
vf=vector<double>::Zeros(l*m*n*d);
offs=0;
ax=2*CMath::RandomReal()-1;
ay=2*CMath::RandomReal()-1;
az=2*CMath::RandomReal()-1;
axy=2*CMath::RandomReal()-1;
ayz=2*CMath::RandomReal()-1;
for(k=0; k<l; k++)
{
for(j=0; j<m; j++)
for(i=0; i<n; i++)
for(di=0; di<d; di++)
{
vf.Set(offs,di+ax*i+ay*j+az*k+axy*i*j+ayz*j*k);
offs++;
}
}
CSpline3D::Spline3DBuildTrilinearV(x,n,y,m,z,l,vf,d,c);
vx=CMath::RandomReal()*n;
vy=CMath::RandomReal()*m;
vz=CMath::RandomReal()*l;
CSpline3D::Spline3DCalcV(c,vx,vy,vz,vf);
for(di=0; di<d; di++)
{
if(MathAbs(di+ax*vx+ay*vy+az*vz+axy*vx*vy+ayz*vy*vz-vf[di])>eps)
return(true);
}
if(d==1)
{
vsf=CSpline3D::Spline3DCalc(c,vx,vy,vz);
if(MathAbs(ax*vx+ay*vy+az*vz+axy*vx*vy+ayz*vy*vz-vsf)>eps)
return(true);
}
}
//--- Generate random grid and test function.
//--- Test spline ability to reproduce function values at grid nodes.
passcount=20;
for(pass=1; pass<=passcount; pass++)
{
//--- Prepare a model and check that functions (Spline3DBuildTrilinear,
//--- Spline3DCalc,Spline3DCalcV) work correctly and
BuildRndGrid(true,true,n,m,l,d,x,y,z,vf);
CApServ::RVectorSetLengthAtLeast(sf,n*m*l);
//--- Check that the model's values are equal to the function's values
//--- in grid points
CSpline3D::Spline3DBuildTrilinearV(x,n,y,m,z,l,vf,d,c);
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
for(k=0; k<l; k++)
{
CSpline3D::Spline3DCalcV(c,x[i],y[j],z[k],vvf);
for(di=0; di<d; di++)
if((double)(MathAbs(vf[d*(n*(m*k+j)+i)+di]-vvf[di]))>eps)
return(true);
}
}
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| Unpack / UnpackV test |
//+------------------------------------------------------------------+
bool CTestSpline3DUnit::TestUnpack(void)
{
//--- create variables
int passcount=20;
bool result;
CSpline3DInterpolant c;
CMatrixDouble tbl0;
CMatrixDouble tbl1;
int n=0;
int m=0;
int l=0;
int d=0;
int sz=0;
int un=0;
int um=0;
int ul=0;
int ud=0;
int ust=0;
int uvn=0;
int uvm=0;
int uvl=0;
int uvd=0;
int uvst=0;
int ci=0;
int cj=0;
int ck=0;
CRowDouble x;
CRowDouble y;
CRowDouble z;
CRowDouble sf;
CRowDouble vf;
int p0=0;
int p1=0;
double tx=0;
double ty=0;
double tz=0;
double v1=0;
double v2=0;
double err=0;
int pass=0;
bool bperr;
int i=0;
int j=0;
int k=0;
int di=0;
int i0=0;
for(pass=1; pass<=passcount; pass++)
{
//--- generate random grid.
//--- NOTE: for this test we need ordered grid, i.e. grid
//--- with nodes in ascending order
BuildRndGrid(true,false,n,m,l,d,x,y,z,vf);
sz=n*m*l;
CApServ::RVectorSetLengthAtLeast(sf,sz);
CSpline3D::Spline3DBuildTrilinearV(x,n,y,m,z,l,vf,d,c);
CSpline3D::Spline3DUnpackV(c,uvn,uvm,uvl,uvd,uvst,tbl0);
for(di=0; di<d; di++)
{
//--- DI-th component copy of a vector-function to
//--- a scalar function
for(i=0; i<=sz-1; i++)
sf.Set(i,vf[d*i+di]);
CSpline3D::Spline3DBuildTrilinearV(x,n,y,m,z,l,sf,1,c);
CSpline3D::Spline3DUnpackV(c,un,um,ul,ud,ust,tbl1);
for(i=0; i<=n-2; i++)
{
for(j=0; j<=m-2; j++)
{
for(k=0; k<=l-2; k++)
{
p1=(n-1)*((m-1)*k+j)+i;
p0=d*p1+di;
//--- Check that all components are correct:
//--- *first check, that unpacked componets are equal
//--- to packed components;
bperr=(un!=n || um!=m || ul!=l || tbl1.Get(p1,0)!=x[i] || tbl1.Get(p1,1)!=x[i+1] || tbl1.Get(p1,2)!=y[j] ||
tbl1.Get(p1,3)!=y[j+1] || tbl1.Get(p1,4)!=z[k] || tbl1.Get(p1,5)!=z[k+1] || uvn!=n || uvm!=m ||
uvl!=l || uvd!=d || tbl0.Get(p0,0)!=x[i] || tbl0.Get(p0,1)!=x[i+1] || tbl0.Get(p0,2)!=y[j] ||
tbl0.Get(p0,3)!=y[j+1] || tbl0.Get(p0,4)!=z[k] || tbl0.Get(p0,5)!=z[k+1]);
//--- *check, that all components unpacked by Unpack
//--- function are equal to all components unpacked
//--- by UnpackV function.
for(i0=0; i0<=13; i0++)
bperr=bperr||tbl0.Get(p0,i0)!=tbl1.Get(p1,i0);
if(bperr)
{
result=true;
return(result);
}
tx=(0.001+0.999*CMath::RandomReal())*(tbl1.Get(p1,1)-tbl1.Get(p1,0));
ty=(0.001+0.999*CMath::RandomReal())*(tbl1.Get(p1,3)-tbl1.Get(p1,2));
tz=(0.001+0.999*CMath::RandomReal())*(tbl1.Get(p1,5)-tbl1.Get(p1,4));
//--- Interpolation properties for:
//--- *scalar function;
v1=0;
for(ci=0; ci<=1; ci++)
{
for(cj=0; cj<=1; cj++)
for(ck=0; ck<=1; ck++)
v1+=tbl1.Get(p1,6+2*(2*ck+cj)+ci)*MathPow(tx,ci)*MathPow(ty,cj)*MathPow(tz,ck);
}
v2=CSpline3D::Spline3DCalc(c,tbl1.Get(p1,0)+tx,tbl1.Get(p1,2)+ty,tbl1.Get(p1,4)+tz);
err=MathMax(err,MathAbs(v1-v2));
//--- *component of vector function.
v1=0;
for(ci=0; ci<=1; ci++)
{
for(cj=0; cj<=1; cj++)
for(ck=0; ck<=1; ck++)
v1+=tbl0.Get(p0,6+2*(2*ck+cj)+ci)*MathPow(tx,ci)*MathPow(ty,cj)*MathPow(tz,ck);
}
v2=CSpline3D::Spline3DCalc(c,tbl0.Get(p0,0)+tx,tbl0.Get(p0,2)+ty,tbl0.Get(p0,4)+tz);
err=MathMax(err,MathAbs(v1-v2));
}
}
}
}
}
//--- return result
result=err>(double)(1.0E+5*CMath::m_machineepsilon);
return(result);
}
//+------------------------------------------------------------------+
//| LinTrans test |
//+------------------------------------------------------------------+
bool CTestSpline3DUnit::TestLinTrans(void)
{
//--- create variables
int passcount=15;
bool result;
CSpline3DInterpolant c;
CSpline3DInterpolant c2;
int m=0;
int n=0;
int l=0;
int d=0;
CRowDouble x;
CRowDouble y;
CRowDouble z;
CRowDouble f;
double a1=0;
double a2=0;
double a3=0;
double b1=0;
double b2=0;
double b3=0;
double tx=0;
double ty=0;
double tz=0;
double vx=0;
double vy=0;
double vz=0;
CRowDouble v1;
CRowDouble v2;
int pass=0;
int xjob=0;
int yjob=0;
int zjob=0;
double err=0;
int i=0;
for(pass=1; pass<=passcount; pass++)
{
BuildRndGrid(true,false,n,m,l,d,x,y,z,f);
CSpline3D::Spline3DBuildTrilinearV(x,n,y,m,z,l,f,d,c);
for(xjob=0; xjob<=1; xjob++)
{
for(yjob=0; yjob<=1; yjob++)
{
for(zjob=0; zjob<=1; zjob++)
{
//--- Prepare
do
{
a1=2.0*CMath::RandomReal()-1.0;
}
while(a1==0.0);
a1=a1*xjob;
b1=x[0]+CMath::RandomReal()*(x[n-1]-x[0]+2.0)-1.0;
do
{
a2=2.0*CMath::RandomReal()-1.0;
}
while(a2==0.0);
a2=a2*yjob;
b2=y[0]+CMath::RandomReal()*(y[m-1]-y[0]+2.0)-1.0;
do
{
a3=2.0*CMath::RandomReal()-1.0;
}
while(a3==0.0);
a3=a3*zjob;
b3=z[0]+CMath::RandomReal()*(z[l-1]-z[0]+2.0)-1.0;
//--- Test XYZ
CSpline3D::Spline3DCopy(c,c2);
CSpline3D::Spline3DLinTransXYZ(c2,a1,b1,a2,b2,a3,b3);
tx=x[0]+CMath::RandomReal()*(x[n-1]-x[0]);
ty=y[0]+CMath::RandomReal()*(y[m-1]-y[0]);
tz=z[0]+CMath::RandomReal()*(z[l-1]-z[0]);
if(xjob==0)
{
tx=b1;
vx=x[0]+CMath::RandomReal()*(x[n-1]-x[0]);
}
else
vx=(tx-b1)/a1;
if(yjob==0)
{
ty=b2;
vy=y[0]+CMath::RandomReal()*(y[m-1]-y[0]);
}
else
vy=(ty-b2)/a2;
if(zjob==0)
{
tz=b3;
vz=z[0]+CMath::RandomReal()*(z[l-1]-z[0]);
}
else
vz=(tz-b3)/a3;
CSpline3D::Spline3DCalcV(c,tx,ty,tz,v1);
CSpline3D::Spline3DCalcV(c2,vx,vy,vz,v2);
for(i=0; i<d; i++)
err=MathMax(err,MathAbs(v1[i]-v2[i]));
if(err>(double)(1.0E+4*CMath::m_machineepsilon))
{
result=true;
return(result);
}
//--- Test F
CSpline3D::Spline3DCopy(c,c2);
CSpline3D::Spline3DLinTransF(c2,a1,b1);
tx=x[0]+CMath::RandomReal()*(x[n-1]-x[0]);
ty=y[0]+CMath::RandomReal()*(y[m-1]-y[0]);
tz=z[0]+CMath::RandomReal()*(z[l-1]-z[0]);
CSpline3D::Spline3DCalcV(c,tx,ty,tz,v1);
CSpline3D::Spline3DCalcV(c2,tx,ty,tz,v2);
for(i=0; i<d; i++)
err=MathMax(err,MathAbs(a1*v1[i]+b1-v2[i]));
}
}
}
}
//--- return result
result=err>(1.0E+4*CMath::m_machineepsilon);
return(result);
}
//+------------------------------------------------------------------+
//| Resample test |
//+------------------------------------------------------------------+
bool CTestSpline3DUnit::TestTrilinearreSample(void)
{
//--- create variables
bool result=false;
int passcount=20;
CSpline3DInterpolant c;
int n=0;
int m=0;
int l=0;
int n2=0;
int m2=0;
int l2=0;
CRowDouble x;
CRowDouble y;
CRowDouble z;
CRowDouble f;
CRowDouble fr;
double v1=0;
double v2=0;
double err=0;
double mf=0;
int pass=0;
int i=0;
int j=0;
int k=0;
for(pass=1; pass<=passcount; pass++)
{
n=CMath::RandomInteger(4)+2;
m=CMath::RandomInteger(4)+2;
l=CMath::RandomInteger(4)+2;
n2=CMath::RandomInteger(4)+2;
m2=CMath::RandomInteger(4)+2;
l2=CMath::RandomInteger(4)+2;
CApServ::RVectorSetLengthAtLeast(x,n);
CApServ::RVectorSetLengthAtLeast(y,m);
CApServ::RVectorSetLengthAtLeast(z,l);
CApServ::RVectorSetLengthAtLeast(f,n*m*l);
for(i=0; i<n; i++)
x.Set(i,(double)i/(double)(n-1));
for(i=0; i<m; i++)
y.Set(i,(double)i/(double)(m-1));
for(i=0; i<l; i++)
z.Set(i,(double)i/(double)(l-1));
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
for(k=0; k<l; k++)
f.Set(n*(m*k+j)+i,2*CMath::RandomReal()-1);
}
CSpline3D::Spline3DResampleTrilinear(f,l,m,n,l2,m2,n2,fr);
CSpline3D::Spline3DBuildTrilinearV(x,n,y,m,z,l,f,1,c);
err=0;
mf=0;
for(i=0; i<n2 ; i++)
{
for(j=0; j<=m2-1; j++)
for(k=0; k<=l2-1; k++)
{
v1=CSpline3D::Spline3DCalc(c,(double)i/(double)(n2-1),(double)j/(double)(m2-1),(double)k/(double)(l2-1));
v2=fr[n2*(m2*k+j)+i];
err=MathMax(err,MathAbs(v1-v2));
mf=MathMax(mf,MathAbs(v1));
}
}
result=result||(err/mf)>(1.0E+4*CMath::m_machineepsilon);
if(result)
return(result);
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| The function does build random function on random grid with |
//| random number of points: |
//| * N, M, K - random from 2 to 5 |
//| * D - 1 in case IsVect=False, 1..3 in case |
//| IsVect=True |
//| * X, Y, Z - each variable spans from MinV to MaxV, with |
//| MinV is random number from [-1.5, 0.5] and |
//| MaxV is random number from [0.5, 1.5]. All |
//| nodes are well separated. All nodes are |
//| randomly reordered in case Reorder=False. |
//| When Reorder=True, nodes are returned in |
//| ascending order. |
//| * F - random values from [-1, +1] |
//+------------------------------------------------------------------+
void CTestSpline3DUnit::BuildRndGrid(bool isvect,bool reorder,
int &n,int &m,int &l,int &d,
CRowDouble &x,CRowDouble &y,
CRowDouble &z,CRowDouble &f)
{
//--- create variables
double st=0;
int i=0;
int j=0;
int k=0;
int di=0;
double v=0;
double mx=0;
double maxv=0;
double minv=0;
n=0;
m=0;
l=0;
d=0;
st=0.3;
m=CMath::RandomInteger(4)+2;
n=CMath::RandomInteger(4)+2;
l=CMath::RandomInteger(4)+2;
if(isvect)
d=CMath::RandomInteger(3)+1;
else
d=1;
CApServ::RVectorSetLengthAtLeast(x,n);
CApServ::RVectorSetLengthAtLeast(y,m);
CApServ::RVectorSetLengthAtLeast(z,l);
CApServ::RVectorSetLengthAtLeast(f,n*m*l*d);
//--- Fill X
x.Set(0,0);
for(i=1; i<n; i++)
x.Set(i,x[i-1]+st+CMath::RandomReal());
minv=-0.5-CMath::RandomReal();
maxv=0.5+CMath::RandomReal();
mx=x[n-1];
x=x/mx*(maxv-minv)+minv;
if(reorder)
for(i=0; i<n; i++)
{
k=CMath::RandomInteger(n);
x.Swap(i,k);
}
//--- Fill Y
y.Set(0,0);
for(i=1; i<m; i++)
y.Set(i,y[i-1]+st+CMath::RandomReal());
minv=-0.5-CMath::RandomReal();
maxv=0.5+CMath::RandomReal();
mx=y[m-1];
y=y/mx*(maxv-minv)+minv;
if(reorder)
for(i=0; i<m; i++)
{
k=CMath::RandomInteger(m);
y.Swap(i,k);
}
//--- Fill Z
z.Set(0,0);
for(i=1; i<l; i++)
z.Set(i,z[i-1]+st+CMath::RandomReal());
minv=-0.5-CMath::RandomReal();
maxv=0.5+CMath::RandomReal();
mx=z[l-1];
z=z/mx*(maxv-minv)+minv;
if(reorder)
for(i=0; i<l; i++)
{
k=CMath::RandomInteger(l);
z.Swap(i,k);
}
//--- Fill F
for(i=0; i<n; i++)
{
for(j=0; j<m; j++)
for(k=0; k<l; k++)
for(di=0; di<d; di++)
f.Set(d*(n*(m*k+j)+i)+di,2*CMath::RandomReal()-1);
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestRBFUnit
{
public:
static const int m_algorithmscnt;
static const int m_xrbfalgocnt;
static const int m_xrbfc1algocnt;
static const int m_xrbfsmoothingalgocnt;
static const double m_tol;
static const int m_mxits;
static const double m_heps;
static bool TestRBF(bool silent);
static bool SqrDegMatrixRBFTest(bool silent);
static bool BasicMultilayErRBF1DTest(void);
private:
static bool SpecialTest(void);
static bool BasicRBFTest(void);
static bool IrregularRBFTest(void);
static bool LinearityModelRBFTest(void);
static bool SerializationTest(void);
static bool SearcherR(const CMatrixDouble &y0,const CMatrixDouble &y1,int n,int ny,int errtype,const CRowDouble &b1,const CRowDouble &delta);
static bool BasicMultilayerRBFTest(void);
static void GridCalc23Test(bool &errorflag);
static bool BasicXRBFTest(void);
static bool ScaledXRBFTest(void);
static bool GridXRBFTest(void);
static bool BasicHRBFTest(void);
static bool ScaleHRBFTtest(void);
static bool SpecHRBFTest(void);
static bool GridHRBFTest(void);
static void GradTest(bool &err);
static void GenerateNearlyRegularGrid(int n,int nx,int ny,CHighQualityRandState &rs,CMatrixDouble &xy,double &meanseparation);
static void GenerateGoodRandomGrid(int n,int nx,int ny,CHighQualityRandState &rs,CMatrixDouble &xy,double &meanseparation);
static void GenerateSeparatedWithInbox(CMatrixDouble &xy,int n,int nx,double mindist,CHighQualityRandState &rs,CRowDouble &x);
static void SetAlgorithm(CRBFModel &s,int n,int nx,int ny,bool hasscale,int algoidx,double meanpointsseparation,bool &hasgradientatnodes,bool &hashessianatnodes);
static void SetXRBFAlgorithmExact(CRBFModel &s,int algoidx,double meanpointsseparation);
static void SetXRBFAlgorithmSmoothing(CRBFModel &s,int algoidx,double lambdav,double meanpointsseparation);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
const int CTestRBFUnit::m_algorithmscnt=6;
const int CTestRBFUnit::m_xrbfalgocnt=3;
const int CTestRBFUnit::m_xrbfc1algocnt=2;
const int CTestRBFUnit::m_xrbfsmoothingalgocnt=3;
const double CTestRBFUnit::m_tol=1.0E-10;
const int CTestRBFUnit::m_mxits=0;
const double CTestRBFUnit::m_heps=1.0E-12;
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestRBFUnit::TestRBF(bool silent)
{
//--- create variables
bool result;
bool specialerrors;
bool basicrbferrors;
bool irregularrbferrors;
bool linearitymodelrbferr;
bool sqrdegmatrixrbferr;
bool sererrors;
bool multilayerrbf1derrors;
bool multilayerrbferrors;
bool gridcalc23errors;
bool graderrors;
bool hrbfbasicerrors;
bool hrbfscaleerrors;
bool hrbfspecerrors;
bool hrbfgriderrors;
bool hrbferrors;
bool xrbfbasicerrors;
bool xrbfscaleerrors;
bool xrbfgriderrors;
bool xrbferrors;
bool waserrors;
graderrors=false;
//--- Shared tests
GradTest(graderrors);
//--- XRBF tests
xrbfbasicerrors=BasicXRBFTest();
xrbfscaleerrors=ScaledXRBFTest();
xrbfgriderrors=GridXRBFTest();
xrbferrors=(xrbfbasicerrors||xrbfscaleerrors||xrbfgriderrors);
//--- HRBF tests
hrbfbasicerrors=BasicHRBFTest();
hrbfspecerrors=SpecHRBFTest();
hrbfscaleerrors=ScaleHRBFTtest();
hrbfgriderrors=GridHRBFTest();
hrbferrors=(hrbfbasicerrors||hrbfspecerrors||hrbfscaleerrors||hrbfgriderrors);
//--- Other tests
specialerrors=SpecialTest();
basicrbferrors=BasicRBFTest();
irregularrbferrors=IrregularRBFTest();
linearitymodelrbferr=LinearityModelRBFTest();
sqrdegmatrixrbferr=SqrDegMatrixRBFTest(silent);
multilayerrbf1derrors=false;
multilayerrbferrors=BasicMultilayerRBFTest();
sererrors=SerializationTest();
gridcalc23errors=false;
GridCalc23Test(gridcalc23errors);
//--- report
waserrors=(graderrors||specialerrors||basicrbferrors||irregularrbferrors||linearitymodelrbferr||sqrdegmatrixrbferr ||
sererrors||multilayerrbf1derrors||multilayerrbferrors||gridcalc23errors||hrbferrors||xrbferrors);
if(!silent)
{
Print("TESTING RBF");
Print("GENERAL TESTS:");
PrintResult("* differentiation test",!graderrors);
PrintResult("* serialization test",!sererrors);
PrintResult("* special properties",!specialerrors);
Print("RBF-V3:");
PrintResult("* basic XRBF test",!xrbfbasicerrors);
PrintResult("* scale-related tests",!xrbfscaleerrors);
PrintResult("* grid calculation tests",!xrbfgriderrors);
Print("RBF-V2:");
PrintResult("* basic HRBF test",!hrbfbasicerrors);
PrintResult("* scale-related tests",!hrbfscaleerrors);
PrintResult("* grid calculation tests",!hrbfgriderrors);
PrintResult("* special properties",!hrbfspecerrors);
Print("RBF-V1:");
PrintResult("* basicRBFTest",!basicrbferrors);
PrintResult("* irregularRBFTest",!irregularrbferrors);
PrintResult("* linearity test",!linearitymodelrbferr);
PrintResult("* SqrDegMatrixRBFTest",!sqrdegmatrixrbferr);
PrintResult("* MultiLayerRBFErrors in 1D test",!multilayerrbf1derrors);
PrintResult("* MultiLayerRBFErrors in 2-3D test",!multilayerrbferrors);
PrintResult("* GridCalc2/3V",!gridcalc23errors);
//--- was errors?
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| The test has to check, that algorithm can solve problems of |
//| matrix are degenerate. |
//| * used model with linear term; |
//| * points locate in a subspace of dimension less than an |
//| original space. |
//+------------------------------------------------------------------+
bool CTestRBFUnit::SqrDegMatrixRBFTest(bool silent)
{
//--- create variables
double zx=10;
double px=15;
double zy=10;
double py=15;
double eps=1.0E-6;
int ny=1;
CRBFModel s;
CRBFReport rep;
int nx=0;
int k0=0;
int k1=0;
int np=0;
double sx=0;
double sy=0;
double q=0;
double z=0;
CRowDouble point;
CMatrixDouble a;
CRowDouble d0;
CRowDouble d1;
int gen=0;
CRowDouble pvd0;
CRowDouble pvd1;
double pvdnorm=0;
double vnorm=0;
double dd0=0;
double dd1=0;
CMatrixDouble gp;
CRowDouble x;
CRowDouble y;
int unx=0;
int uny=0;
CMatrixDouble xwr;
CMatrixDouble v;
int i=0;
int j=0;
int k=0;
int modelversion=0;
int i_=0;
for(nx=2; nx<=3; nx++)
{
//--- prepare test problem
sx=MathPow(zx,px*(CMath::RandomInteger(3)-1));
sy=MathPow(zy,py*(CMath::RandomInteger(3)-1));
x=vector<double>::Zeros(nx);
y=vector<double>::Zeros(ny);
point=vector<double>::Zeros(nx);
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetCond(s,m_heps,m_heps,m_mxits);
q=0.25+CMath::RandomReal();
z=4.5+CMath::RandomReal();
CRBF::RBFSetAlgoQNN(s,q,z);
//--- start points for grid
for(i=0; i<nx ; i++)
point.Set(i,sx*(2*CMath::RandomReal()-1));
if(nx==2)
{
for(k0=2; k0<=4; k0++)
{
CMatGen::RMatrixRndOrthogonal(nx,a);
d0=vector<double>::Zeros(nx);
for(i_=0; i_<nx ; i_++)
d0.Set(i_,a.Get(i_,0));
np=k0;
gp=matrix<double>::Zeros(np,nx+ny);
//--- create grid
for(i=0; i<k0 ; i++)
{
gp.Set(i,0,point[0]+sx*i*d0[0]);
gp.Set(i,1,point[1]+sx*i*d0[1]);
for(k=0; k<ny; k++)
gp.Set(i,nx+k,sy*(2*CMath::RandomReal()-1));
}
CRBF::RBFSetPoints(s,gp,np);
CRBF::RBFBuildModel(s,rep);
for(i=0; i<np; i++)
{
x.Set(0,gp.Get(i,0));
x.Set(1,gp.Get(i,1));
CRBF::RBFCalc(s,x,y);
for(j=0; j<ny; j++)
{
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
return(true);
}
}
}
}
if(nx==3)
{
for(k0=2; k0<=4; k0++)
{
for(k1=2; k1<=4; k1++)
{
for(gen=1; gen<=2; gen++)
{
CMatGen::RMatrixRndOrthogonal(nx,a);
d0=vector<double>::Zeros(nx);
for(i_=0; i_<nx ; i_++)
d0.Set(i_,a.Get(i_,0));
//--- create grid
np=-1;
if(gen==1)
{
np=k0;
gp=matrix<double>::Zeros(np,nx+ny);
for(i=0; i<k0 ; i++)
{
gp.Set(i,0,point[0]+sx*i*d0[0]);
gp.Set(i,1,point[1]+sx*i*d0[1]);
gp.Set(i,2,point[2]+sx*i*d0[2]);
for(k=0; k<ny; k++)
gp.Set(i,nx+k,sy*(2*CMath::RandomReal()-1));
}
}
if(gen==2)
{
d1=vector<double>::Zeros(nx);
for(i_=0; i_<nx ; i_++)
d1.Set(i_,a.Get(i_,1));
np=k0*k1;
gp=matrix<double>::Zeros(np,nx+ny);
for(i=0; i<k0 ; i++)
{
for(j=0; j<k1 ; j++)
{
gp.Set(i*k1+j,0,sx*i*d0[0]+sx*j*d1[0]);
gp.Set(i*k1+j,1,sx*i*d0[1]+sx*j*d1[1]);
gp.Set(i*k1+j,2,sx*i*d0[2]+sx*j*d1[2]);
for(k=0; k<ny; k++)
gp.Set(i*k1+j,nx+k,sy*(2*CMath::RandomReal()-1));
}
}
}
if(!CAp::Assert(np>=0,"rbf test: integrity error"))
return(true);
CRBF::RBFSetPoints(s,gp,np);
CRBF::RBFBuildModel(s,rep);
for(i=0; i<np; i++)
{
x.Set(0,gp.Get(i,0));
x.Set(1,gp.Get(i,1));
x.Set(2,gp.Get(i,2));
CRBF::RBFCalc(s,x,y);
for(j=0; j<ny; j++)
{
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
return(true);
}
}
if(gen==2)
{
CRBF::RBFUnpack(s,unx,uny,xwr,np,v,modelversion);
dd0=(d0[0]*v.Get(0,0)+d0[1]*v.Get(0,1)+d0[2]*v.Get(0,2))/(d0[0]*d0[0]+d0[1]*d0[1]+d0[2]*d0[2]);
dd1=(d1[0]*v.Get(0,0)+d1[1]*v.Get(0,1)+d1[2]*v.Get(0,2))/(d1[0]*d1[0]+d1[1]*d1[1]+d1[2]*d1[2]);
pvd0=vector<double>::Zeros(nx);
pvd1=vector<double>::Zeros(nx);
for(i=0; i<nx ; i++)
{
pvd0.Set(i,dd0*d0[i]);
pvd1.Set(i,dd1*d1[i]);
}
pvdnorm=MathSqrt(CMath::Sqr(v.Get(0,0)-pvd0[0]-pvd1[0])+CMath::Sqr(v.Get(0,1)-pvd0[1]-pvd1[1])+CMath::Sqr(v.Get(0,2)-pvd0[2]-pvd1[2]));
vnorm=MathSqrt(CMath::Sqr(v.Get(0,0))+CMath::Sqr(v.Get(0,1))+CMath::Sqr(v.Get(0,2)));
if(pvdnorm>(vnorm*m_tol))
return(true);
}
}
}
}
}
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| Function for testing basic functionality of RBF module on regular|
//| grids with multi-layer algorithm in 1D. |
//+------------------------------------------------------------------+
bool CTestRBFUnit::BasicMultilayErRBF1DTest(void)
{
//--- create variables
double a=1.0;
double b=1.0/9.0;
double f1=1.0;
double f2=10.0;
int passcount=5;
int n=100;
CRBFModel s;
CRBFReport rep;
int nx=0;
int ny=0;
int linterm=0;
double q=0;
double r=0;
int errtype=0;
CRowDouble delta;
int nlayers=0;
CRowDouble a1;
CRowDouble b1;
CMatrixDouble gp;
CRowDouble x;
CRowDouble y;
CMatrixDouble mody0;
CMatrixDouble mody1;
CMatrixDouble gy;
CRowDouble gpgx0;
CRowDouble gpgx1;
int pass=0;
int i=0;
int j=0;
gpgx0=vector<double>::Zeros(n);
gpgx1=vector<double>::Zeros(n);
for(i=0; i<n; i++)
gpgx0.Set(i,(double)i/(double)n);
r=1;
for(pass=0; pass<=passcount-1; pass++)
{
nx=CMath::RandomInteger(2)+2;
ny=CMath::RandomInteger(3)+1;
linterm=CMath::RandomInteger(3)+1;
x=vector<double>::Zeros(nx);
y=vector<double>::Zeros(ny);
a1=vector<double>::Zeros(ny);
b1=vector<double>::Zeros(ny);
delta=vector<double>::Zeros(ny);
mody0=matrix<double>::Zeros(n,ny);
mody1=matrix<double>::Zeros(n,ny);
for(i=0; i<ny; i++)
{
a1.Set(i,a+0.01*a*(2*CMath::RandomReal()-1));
b1.Set(i,b+0.01*b*(2*CMath::RandomReal()-1));
delta.Set(i,0.35*b1[i]);
}
gp=matrix<double>::Zeros(n,nx+ny);
//--- create grid
for(i=0; i<n; i++)
{
gp.Set(i,0,(double)i/(double)n);
for(j=0; j<ny; j++)
{
gp.Set(i,nx+j,a1[j]*MathCos(f1*2*M_PI*gp.Get(i,0))+b1[j]*MathCos(f2*2*M_PI*gp.Get(i,0)));
mody0.Set(i,j,gp.Get(i,nx+j));
}
}
q=1;
nlayers=1;
errtype=1;
//--- test multilayer algorithm with different parameters
while(q>=(1/(2*f2)))
{
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetAlgoMultilayer(s,r,nlayers,0.0);
if(linterm==1)
CRBF::RBFSetLinTerm(s);
if(linterm==2)
CRBF::RBFSetConstTerm(s);
if(linterm==3)
CRBF::RBFSetZeroTerm(s);
CRBF::RBFSetPoints(s,gp,n);
CRBF::RBFBuildModel(s,rep);
if(ny==1)
{
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
x.Set(j,gp.Get(i,j));
switch(nx)
{
case 2:
mody1.Set(i,0,CRBF::RBFCalc2(s,x[0],x[1]));
break;
case 3:
mody1.Set(i,0,CRBF::RBFCalc3(s,x[0],x[1],x[2]));
break;
default:
CAp::Assert(false,"BasicMultiLayerRBFTest1D: Invalid variable NX(NX neither 2 nor 3)");
return(true);
}
}
if(SearcherR(mody0,mody1,n,ny,errtype,b1,delta))
return(true);
if(nx==2)
{
CRBF::RBFGridCalc2(s,gpgx0,n,gpgx1,n,gy);
for(i=0; i<n; i++)
mody1.Set(i,0,gy.Get(i,0));
}
if(SearcherR(mody0,mody1,n,ny,errtype,b1,delta))
return(true);
}
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
x.Set(j,gp.Get(i,j));
CRBF::RBFCalc(s,x,y);
for(j=0; j<ny; j++)
mody1.Set(i,j,y[j]);
}
if(SearcherR(mody0,mody1,n,ny,errtype,b1,delta))
return(true);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
x.Set(j,gp.Get(i,j));
CRBF::RBFCalcBuf(s,x,y);
for(j=0; j<ny; j++)
mody1.Set(i,j,y[j]);
}
if(SearcherR(mody0,mody1,n,ny,errtype,b1,delta))
return(true);
q=q/2;
nlayers=nlayers+1;
if(errtype==1 && q<=(1.0/f2))
errtype=2;
}
}
//--- return result
return(false);
}
//+------------------------------------------------------------------+
//| This function tests special cases: |
//| * uninitialized RBF model will correctly return zero values |
//| * RBF correctly handles 1 or 2 distinct points |
//| * when we have many uniformly spaced points and one outlier, |
//| filter which is applied to radii, makes all radii equal |
//| (RBF - QNN). |
//| * RBF-ML with NLayers = 0 gives linear model |
//| * Hierarchical RBF with NLayers = 0 gives linear model |
//+------------------------------------------------------------------+
bool CTestRBFUnit::SpecialTest(void)
{
//--- create variables
bool result=false;
double errtol=1.0E-9;
CRBFModel s;
CRBFReport rep;
int n=0;
int nx=0;
int ny=0;
int i=0;
int j=0;
int k=0;
int t=0;
CMatrixDouble xy;
CMatrixDouble vf;
CRowDouble x;
CRowDouble y;
int termtype=0;
int tmpnx=0;
int tmpny=0;
int tmpnc=0;
CMatrixDouble xwr;
CMatrixDouble v;
double sx=0;
double z=0;
double va=0;
double vb=0;
double vc=0;
double vd=0;
int modelversion=0;
double vv=0;
//--- Create model in the default state (no parameters/points specified).
//--- With probability 0.5 we do one of the following:
//--- * test that default state of the model is a zero model (all Calc()
//--- functions return zero)
//--- * call RBFBuildModel() (without specifying anything) and test that
//--- all Calc() functions return zero.
//--- NOTE: because NX varies between 1 and 4, both V1 (old) and V2 RBFs
//--- are tested.
for(nx=1; nx<=4; nx++)
{
for(ny=1; ny<=3; ny++)
{
CRBF::RBFCreate(nx,ny,s);
if(CMath::RandomReal()>0.5)
{
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:251");
return(result);
}
}
x=vector<double>::Zeros(nx);
y=vector<double>::Zeros(1);
for(i=0; i<nx ; i++)
x.Set(i,2*CMath::RandomReal()-1);
CRBF::RBFCalc(s,x,y);
if(CAp::Len(y)!=ny)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:262");
return(result);
}
for(i=0; i<ny; i++)
{
if(y[i]!=0.0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:268");
return(result);
}
}
}
}
//--- Create default model with 1 point and different types of linear term.
//--- Test algorithm on such dataset.
//--- NOTE: because NX varies between 1 and 4, both V1 (old) and V2 RBFs
//--- are tested.
for(nx=1; nx<=4; nx++)
{
for(ny=1; ny<=3; ny++)
{
CRBF::RBFCreate(nx,ny,s);
for(termtype=0; termtype<=1; termtype++)
{
if(termtype==0)
CRBF::RBFSetLinTerm(s);
if(termtype==1)
CRBF::RBFSetConstTerm(s);
xy=matrix<double>::Zeros(1,nx+ny);
for(i=0; i<nx+ny; i++)
xy.Set(0,i,2*CMath::RandomReal()-1);
CRBF::RBFSetPoints(s,xy,1);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:297");
return(result);
}
//--- First, test that model exactly reproduces our dataset in the specified point
x=vector<double>::Zeros(nx);
for(i=0; i<nx ; i++)
x.Set(i,xy.Get(0,i));
CRBF::RBFCalc(s,x,y);
if(CAp::Len(y)!=ny)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:310");
return(result);
}
for(i=0; i<ny; i++)
{
if(MathAbs(y[i]-xy.Get(0,nx+i))>errtol)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:316");
return(result);
}
}
}
}
}
//--- Create model with 2 points and different types of linear term.
//--- Test algorithm on such dataset.
for(nx=2; nx<=3; nx++)
{
for(ny=1; ny<=3; ny++)
{
CRBF::RBFCreate(nx,ny,s);
for(termtype=0; termtype<=1; termtype++)
{
if(termtype==0)
CRBF::RBFSetLinTerm(s);
if(termtype==1)
CRBF::RBFSetConstTerm(s);
if(termtype==2)
CRBF::RBFSetZeroTerm(s);
xy=matrix<double>::Zeros(2,nx+ny);
for(i=0; i<nx+ny; i++)
xy.Set(0,i,2*CMath::RandomReal()-1);
xy.Row(1,xy[0]+1.0);
CRBF::RBFSetPoints(s,xy,2);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
result=true;
return(result);
}
for(j=0; j<=1; j++)
{
x=vector<double>::Zeros(nx);
for(i=0; i<nx ; i++)
x.Set(i,xy.Get(j,i));
CRBF::RBFCalc(s,x,y);
if(CAp::Len(y)!=ny)
{
result=true;
return(result);
}
for(i=0; i<ny; i++)
{
if(MathAbs(y[i]-xy.Get(j,nx+i))>errtol)
{
result=true;
return(result);
}
}
}
}
}
}
//--- Generate a set of points (xi,yi) = (SX*i,0), and one
//--- outlier (x_far,y_far)=(-1000*SX,0).
//--- Radii filtering should place a bound on the radius of outlier.
for(nx=2; nx<=3; nx++)
{
for(ny=1; ny<=3; ny++)
{
sx=MathExp(-5+10*CMath::RandomReal());
CRBF::RBFCreate(nx,ny,s);
xy=matrix<double>::Zeros(20,nx+ny);
for(i=0; i<=CAp::Rows(xy)-1; i++)
{
xy.Set(i,0,sx*i);
for(j=0; j<ny; j++)
xy.Set(i,nx+j,CMath::RandomReal());
}
xy.Set(CAp::Rows(xy)-1,0,-(1000*sx));
CRBF::RBFSetPoints(s,xy,CAp::Rows(xy));
//--- Try random Z from [1,5]
z=1+CMath::RandomReal()*4;
CRBF::RBFSetAlgoQNN(s,1.0,z);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
result=true;
return(result);
}
CRBF::RBFUnpack(s,tmpnx,tmpny,xwr,tmpnc,v,modelversion);
if(((((tmpnx!=nx || tmpny!=ny) || tmpnc!=CAp::Rows(xy)) || CAp::Cols(xwr)!=nx+ny+1) || CAp::Rows(xwr)!=tmpnc) || modelversion!=1)
{
result=true;
return(result);
}
for(i=0; i<=tmpnc-2; i++)
{
if(MathAbs(xwr.Get(i,nx+ny)-sx)>errtol)
{
result=true;
return(result);
}
}
if(MathAbs(xwr.Get(tmpnc-1,nx+ny)-z*sx)>errtol)
{
result=true;
return(result);
}
}
}
//--- RBF-ML with NLayers=0 gives us linear model.
//--- In order to perform this test, we use test function which
//--- is perfectly linear and see whether RBF model is able to
//--- reproduce such function.
n=5;
for(ny=1; ny<=3; ny++)
{
va=2*CMath::RandomReal()-1;
vb=2*CMath::RandomReal()-1;
vc=2*CMath::RandomReal()-1;
vd=2*CMath::RandomReal()-1;
//--- Test NX=2.
//--- Generate linear function using random coefficients VA/VB/VC.
//--- Function is K-dimensional vector-valued, each component has slightly
//--- different coefficients.
xy=matrix<double>::Zeros(n*n,2+ny);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
xy.Set(n*i+j,0,i);
xy.Set(n*i+j,1,j);
for(k=0; k<ny; k++)
xy.Set(n*i+j,2+k,(va+0.1*k)*i+(vb+0.2*k)*j+(vc+0.3*k));
}
}
CRBF::RBFCreate(2,ny,s);
CRBF::RBFSetPoints(s,xy,n*n);
CRBF::RBFSetAlgoMultilayer(s,1.0,0,0.01);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
result=true;
return(result);
}
x=vector<double>::Zeros(2);
x.Set(0,(n-1)*CMath::RandomReal());
x.Set(1,(n-1)*CMath::RandomReal());
if(ny==1 && MathAbs(CRBF::RBFCalc2(s,x[0],x[1])-(va*x[0]+vb*x[1]+vc))>errtol)
{
result=true;
return(result);
}
CRBF::RBFCalc(s,x,y);
for(k=0; k<ny; k++)
if(MathAbs(y[k]-((va+0.1*k)*x[0]+(vb+0.2*k)*x[1]+(vc+0.3*k)))>errtol)
{
result=true;
return(result);
}
//--- Test NX=3.
//--- Generate linear function using random coefficients VA/VB/VC/VC.
//--- Function is K-dimensional vector-valued, each component has slightly
//--- different coefficients.
xy=matrix<double>::Zeros(n*n*n,3+ny);
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
for(t=0; t<n; t++)
{
xy.Set(n*n*i+n*j+t,0,i);
xy.Set(n*n*i+n*j+t,1,j);
xy.Set(n*n*i+n*j+t,2,t);
for(k=0; k<ny; k++)
xy.Set(n*n*i+n*j+t,3+k,(va+0.1*k)*i+(vb+0.2*k)*j+(vc+0.3*k)*t+(vd+0.4*k));
}
}
}
CRBF::RBFCreate(3,ny,s);
CRBF::RBFSetPoints(s,xy,n*n*n);
CRBF::RBFSetAlgoMultilayer(s,1.0,0,0.01);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
result=true;
return(result);
}
x=vector<double>::Zeros(3);
x.Set(0,(n-1)*CMath::RandomReal());
x.Set(1,(n-1)*CMath::RandomReal());
x.Set(2,(n-1)*CMath::RandomReal());
if(ny==1 && MathAbs(CRBF::RBFCalc3(s,x[0],x[1],x[2])-(va*x[0]+vb*x[1]+vc*x[2]+vd))>errtol)
{
result=true;
return(result);
}
CRBF::RBFCalc(s,x,y);
for(k=0; k<ny; k++)
{
if(MathAbs(y[k]-((va+0.1*k)*x[0]+(vb+0.2*k)*x[1]+(vc+0.3*k)*x[2]+(vd+0.4*k)))>errtol)
{
result=true;
return(result);
}
}
}
//--- HierarchicalRBF with NLayers=0 gives us linear model.
//--- In order to perform this test, we use test function which
//--- is perfectly linear and see whether RBF model is able to
//--- reproduce such function.
n=15;
for(nx=1; nx<=5; nx++)
{
for(ny=1; ny<=3; ny++)
{
vf=matrix<double>::Zeros(ny,nx+1);
for(i=0; i<ny; i++)
{
for(j=0; j<=nx; j++)
vf.Set(i,j,2*CMath::RandomReal()-1);
}
xy=matrix<double>::Zeros(n,nx+ny);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
xy.Set(i,j,CMath::RandomReal());
for(j=0; j<ny; j++)
xy.Set(i,nx+j,vf.Get(j,nx)+CAblasF::RDotRR(nx,vf,j,xy,i));
}
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetPoints(s,xy,n);
CRBF::RBFSetAlgoHierarchical(s,1.0,0,0.0);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:574");
return(result);
}
x=vector<double>::Zeros(nx);
for(i=0; i<nx ; i++)
x.Set(i,CMath::RandomReal());
CRBF::RBFCalc(s,x,y);
for(k=0; k<ny; k++)
{
vv=vf.Get(k,nx)+CAblasF::RDotVR(nx,x,vf,k);
CAp::SetErrorFlag(result,MathAbs(vv-y[k])>errtol,"testrbfunit.ap:586");
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing basic functionality of RBF module on regular|
//| grids. |
//+------------------------------------------------------------------+
bool CTestRBFUnit::BasicRBFTest(void)
{
//--- create variables
double zx=10;
double px=15;
double zy=10;
double py=15;
double eps=1.0E-6;
bool result;
CRBFModel s;
CRBFReport rep;
CRBFCalcBuffer calcbuf;
int nx=0;
int ny=0;
int k0=0;
int k1=0;
int k2=0;
int linterm=0;
int np=0;
double sx=0;
double sy=0;
double q=0;
double z=0;
CRowDouble point;
CMatrixDouble gp;
CRowDouble x;
CRowDouble y;
CMatrixDouble gy;
int unx=0;
int uny=0;
CMatrixDouble xwr;
CMatrixDouble v;
CRowDouble gpgx0;
CRowDouble gpgx1;
int i=0;
int j=0;
int k=0;
int l=0;
int fidx=0;
int modelversion=0;
//--- Problem types:
//--- * 2 and 3-dimensional problems
//--- * problems with zero, constant, linear terms
//--- * different scalings of X and Y values (1.0, 1E-15, 1E+15)
//--- * regular grids different grid sizes (from 2 to 4 points for each dimension)
//--- We check that:
//--- * RBF model correctly reproduces function value (testes with different Calc() functions)
//--- * unpacked model containt correct radii
//--- * linear term has correct form
for(nx=2; nx<=3; nx++)
{
for(ny=1; ny<=2; ny++)
{
//--- prepare test problem
linterm=CMath::RandomInteger(3)+1;
sx=MathPow(zx,px*(CMath::RandomInteger(3)-1));
sy=MathPow(zy,py*(CMath::RandomInteger(3)-1));
x=vector<double>::Zeros(nx);
y=vector<double>::Zeros(ny);
point=vector<double>::Zeros(nx);
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetCond(s,m_heps,m_heps,m_mxits);
q=0.25+CMath::RandomReal();
z=4.5+CMath::RandomReal();
CRBF::RBFSetAlgoQNN(s,q,z);
if(linterm==1)
CRBF::RBFSetLinTerm(s);
if(linterm==2)
CRBF::RBFSetConstTerm(s);
if(linterm==3)
CRBF::RBFSetZeroTerm(s);
//--- start points for grid
for(i=0; i<nx ; i++)
point.Set(i,sx*(2*CMath::RandomReal()-1));
//--- 2-dimensional test problem
if(nx==2)
{
for(k0=2; k0<=4; k0++)
{
for(k1=2; k1<=4; k1++)
{
np=k0*k1;
gp=matrix<double>::Zeros(np,nx+ny);
//--- create grid
for(i=0; i<k0 ; i++)
{
for(j=0; j<k1 ; j++)
{
gp.Set(i*k1+j,0,point[0]+sx*i);
gp.Set(i*k1+j,1,point[1]+sx*j);
for(k=0; k<ny; k++)
gp.Set(i*k1+j,nx+k,sy*(2*CMath::RandomReal()-1));
}
}
CRBF::RBFSetPoints(s,gp,np);
CRBF::RBFBuildModel(s,rep);
CRBF::RBFCreateCalcBuffer(s,calcbuf);
if(ny==1)
{
gpgx0=vector<double>::Zeros(k0);
gpgx1=vector<double>::Zeros(k1);
for(i=0; i<k0 ; i++)
gpgx0.Set(i,point[0]+sx*i);
for(i=0; i<k1 ; i++)
gpgx1.Set(i,point[1]+sx*i);
CRBF::RBFGridCalc2(s,gpgx0,k0,gpgx1,k1,gy);
for(i=0; i<k0 ; i++)
{
for(j=0; j<k1 ; j++)
if(MathAbs(gy.Get(i,j)-gp.Get(i*k1+j,nx))>(sy*eps))
{
result=true;
return(result);
}
}
}
for(i=0; i<np; i++)
{
//--- For each row we randomly choose a function to test
//--- and call it. We do not call multiple functions per
//--- row because carry-over effects may mask errors in
//--- some function (say, it is possible that function
//--- simply returns results from previous call of some
//--- other function which were stored in the RBF model;
//--- in this case, previous call with same parameters
//--- may hide deficiencies in the function).
x.Set(0,gp.Get(i,0));
x.Set(1,gp.Get(i,1));
fidx=CMath::RandomInteger(4);
if(fidx==0 && ny==1)
{
y.Set(0,CRBF::RBFCalc2(s,x[0],x[1]));
if(MathAbs(gp.Get(i,nx)-y[0])>(sy*eps))
{
result=true;
return(result);
}
}
if(fidx==1)
{
CRBF::RBFCalc(s,x,y);
for(j=0; j<ny; j++)
{
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
{
result=true;
return(result);
}
}
}
if(fidx==2)
{
CRBF::RBFCalcBuf(s,x,y);
for(j=0; j<ny; j++)
{
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
{
result=true;
return(result);
}
}
}
if(fidx==3)
{
CRBF::RBFTSCalcBuf(s,calcbuf,x,y);
for(j=0; j<ny; j++)
{
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
{
result=true;
return(result);
}
}
}
}
//--- test for RBFUnpack
CRBF::RBFUnpack(s,unx,uny,xwr,np,v,modelversion);
if((((((nx!=unx || ny!=uny) || CAp::Rows(xwr)!=np) || CAp::Cols(xwr)!=nx+ny+1) || CAp::Rows(v)!=ny) || CAp::Cols(v)!=nx+1) || modelversion!=1)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:784");
return(result);
}
for(i=0; i<np; i++)
{
if(MathAbs(xwr.Get(i,unx+uny)-q*sx)>(sx*eps))
{
result=true;
return(result);
}
}
if(linterm==2)
{
for(i=0; i<=unx-1; i++)
for(j=0; j<=uny-1; j++)
if(v.Get(j,i)!=0.0)
{
result=true;
return(result);
}
}
if(linterm==3)
{
for(i=0; i<=unx; i++)
for(j=0; j<=uny-1; j++)
if(v.Get(j,i)!=0.0)
{
result=true;
return(result);
}
}
}
}
}
//--- 3-dimensional test problems
if(nx==3)
{
for(k0=2; k0<=4; k0++)
{
for(k1=2; k1<=4; k1++)
{
for(k2=2; k2<=4; k2++)
{
np=k0*k1*k2;
gp=matrix<double>::Zeros(np,nx+ny);
//--- create grid
for(i=0; i<k0 ; i++)
{
for(j=0; j<k1 ; j++)
for(k=0; k<k2 ; k++)
{
gp.Set((i*k1+j)*k2+k,0,point[0]+sx*i);
gp.Set((i*k1+j)*k2+k,1,point[1]+sx*j);
gp.Set((i*k1+j)*k2+k,2,point[2]+sx*k);
for(l=0; l<ny; l++)
gp.Set((i*k1+j)*k2+k,nx+l,sy*(2*CMath::RandomReal()-1));
}
}
CRBF::RBFSetPoints(s,gp,np);
CRBF::RBFBuildModel(s,rep);
CRBF::RBFCreateCalcBuffer(s,calcbuf);
for(i=0; i<np; i++)
{
//--- For each row we randomly choose a function to test
//--- and call it. We do not call multiple functions per
//--- row because carry-over effects may mask errors in
//--- some function (say, it is possible that function
//--- simply returns results from previous call of some
//--- other function which were stored in the RBF model;
//--- in this case, previous call with same parameters
//--- may hide deficiencies in the function).
x.Set(0,gp.Get(i,0));
x.Set(1,gp.Get(i,1));
x.Set(2,gp.Get(i,2));
fidx=CMath::RandomInteger(4);
if(fidx==0 && ny==1)
{
y.Set(0,CRBF::RBFCalc3(s,x[0],x[1],x[2]));
if(MathAbs(gp.Get(i,nx)-y[0])>(sy*eps))
{
result=true;
return(result);
}
}
if(fidx==1)
{
CRBF::RBFCalc(s,x,y);
for(j=0; j<ny; j++)
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
{
result=true;
return(result);
}
}
if(fidx==2)
{
CRBF::RBFCalcBuf(s,x,y);
for(j=0; j<ny; j++)
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
{
result=true;
return(result);
}
}
if(fidx==3)
{
CRBF::RBFTSCalcBuf(s,calcbuf,x,y);
for(j=0; j<ny; j++)
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
{
result=true;
return(result);
}
}
}
//--- test for RBFUnpack
CRBF::RBFUnpack(s,unx,uny,xwr,np,v,modelversion);
if((((((nx!=unx || ny!=uny) || CAp::Rows(xwr)!=np) || CAp::Cols(xwr)!=nx+ny+1) || CAp::Rows(v)!=ny) || CAp::Cols(v)!=nx+1) || modelversion!=1)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:901");
return(result);
}
for(i=0; i<np; i++)
{
if((double)(MathAbs(xwr.Get(i,unx+uny)-q*sx))>(sx*eps))
{
result=true;
return(result);
}
}
if(linterm==2)
{
for(i=0; i<=unx-1; i++)
for(j=0; j<=uny-1; j++)
if(v.Get(j,i)!=0.0)
{
result=true;
return(result);
}
}
if(linterm==3)
{
for(i=0; i<=unx; i++)
for(j=0; j<=uny-1; j++)
if(v.Get(j,i)!=0.0)
{
result=true;
return(result);
}
}
}
}
}
}
}
}
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing RBF module on irregular grids. |
//+------------------------------------------------------------------+
bool CTestRBFUnit::IrregularRBFTest(void)
{
//--- create variables
double zx=10;
double px=15;
double zy=10;
double py=15;
double noiselevel=0.1;
double eps=1.0E-6;
bool result;
CRBFModel s;
CRBFReport rep;
int nx=0;
int ny=0;
int k0=0;
int k1=0;
int k2=0;
int linterm=0;
int np=0;
double sx=0;
double sy=0;
double q=0;
double z=0;
CRowDouble point;
CMatrixDouble gp;
CRowDouble x;
CRowDouble y;
CMatrixDouble gy;
int i=0;
int j=0;
int k=0;
int l=0;
//--- Problem types:
//--- * 2 and 3-dimensional problems
//--- * problems with zero, constant, linear terms
//--- * different scalings of X and Y values (1.0, 1E-15, 1E+15)
//--- * noisy grids, which are just regular grids with different grid sizes
//--- (from 2 to 4 points for each dimension) and moderate amount of random
//--- noise added to all node positions.
//--- We check that:
//--- * RBF model correctly reproduces function value (testes with different Calc() functions)
for(nx=2; nx<=3; nx++)
{
//--- prepare test problem
linterm=CMath::RandomInteger(3)+1;
ny=CMath::RandomInteger(2)+1;
sx=MathPow(zx,px*(CMath::RandomInteger(3)-1));
sy=MathPow(zy,py*(CMath::RandomInteger(3)-1));
x=vector<double>::Zeros(nx);
y=vector<double>::Zeros(ny);
point=vector<double>::Zeros(nx);
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetCond(s,m_heps,m_heps,m_mxits);
q=0.25+CMath::RandomReal();
z=4.5+CMath::RandomReal();
CRBF::RBFSetAlgoQNN(s,q,z);
if(linterm==1)
CRBF::RBFSetLinTerm(s);
if(linterm==2)
CRBF::RBFSetConstTerm(s);
if(linterm==3)
CRBF::RBFSetZeroTerm(s);
//--- start points for grid
for(i=0; i<nx ; i++)
point.Set(i,sx*(2*CMath::RandomReal()-1));
//--- 2-dimensional test problems
if(nx==2)
{
for(k0=2; k0<=4; k0++)
{
for(k1=2; k1<=4; k1++)
{
np=k0*k1;
gp=matrix<double>::Zeros(np,nx+ny);
//--- create grid
for(i=0; i<k0 ; i++)
for(j=0; j<k1 ; j++)
{
gp.Set(i*k1+j,0,point[0]+sx*i+noiselevel*sx*(2*CMath::RandomReal()-1));
gp.Set(i*k1+j,1,point[1]+sx*j+noiselevel*sx*(2*CMath::RandomReal()-1));
for(k=0; k<ny; k++)
gp.Set(i*k1+j,nx+k,sy*(2*CMath::RandomReal()-1));
}
CRBF::RBFSetPoints(s,gp,np);
CRBF::RBFBuildModel(s,rep);
for(i=0; i<np; i++)
{
x.Set(0,gp.Get(i,0));
x.Set(1,gp.Get(i,1));
if(ny==1)
{
y.Set(0,CRBF::RBFCalc2(s,x[0],x[1]));
if(MathAbs(gp.Get(i,nx)-y[0])>(sy*eps))
{
result=true;
return(result);
}
}
//---
CRBF::RBFCalc(s,x,y);
for(j=0; j<ny; j++)
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
{
result=true;
return(result);
}
//---
CRBF::RBFCalcBuf(s,x,y);
for(j=0; j<ny; j++)
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
{
result=true;
return(result);
}
}
}
}
}
//--- 3-dimensional test problems
if(nx==3)
{
for(k0=2; k0<=4; k0++)
{
for(k1=2; k1<=4; k1++)
{
for(k2=2; k2<=4; k2++)
{
np=k0*k1*k2;
gp=matrix<double>::Zeros(np,nx+ny);
//--- create grid
for(i=0; i<k0 ; i++)
{
for(j=0; j<k1 ; j++)
{
for(k=0; k<k2 ; k++)
{
gp.Set((i*k1+j)*k2+k,0,point[0]+sx*i+noiselevel*sx*(2*CMath::RandomReal()-1));
gp.Set((i*k1+j)*k2+k,1,point[1]+sx*j+noiselevel*sx*(2*CMath::RandomReal()-1));
gp.Set((i*k1+j)*k2+k,2,point[2]+sx*k+noiselevel*sx*(2*CMath::RandomReal()-1));
for(l=0; l<ny; l++)
gp.Set((i*k1+j)*k2+k,nx+l,sy*(2*CMath::RandomReal()-1));
}
}
}
CRBF::RBFSetPoints(s,gp,np);
CRBF::RBFBuildModel(s,rep);
for(i=0; i<np; i++)
{
x.Set(0,gp.Get(i,0));
x.Set(1,gp.Get(i,1));
x.Set(2,gp.Get(i,2));
if(ny==1)
{
y.Set(0,CRBF::RBFCalc3(s,x[0],x[1],x[2]));
if(MathAbs(gp.Get(i,nx)-y[0])>(sy*eps))
{
result=true;
return(result);
}
}
//---
CRBF::RBFCalc(s,x,y);
for(j=0; j<ny; j++)
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
{
result=true;
return(result);
}
//---
CRBF::RBFCalcBuf(s,x,y);
for(j=0; j<ny; j++)
if(MathAbs(gp.Get(i,nx+j)-y[j])>(sy*eps))
{
result=true;
return(result);
}
}
}
}
}
}
}
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| The test does check, that algorithm can build linear model for|
//| the data sets, when Y depends on X linearly. |
//+------------------------------------------------------------------+
bool CTestRBFUnit::LinearityModelRBFTest(void)
{
//--- create variables
double zx=10;
double px=15;
double zy=10;
double py=15;
int ny=1;
bool result;
CRBFModel s;
CRBFReport rep;
int nx=0;
int k0=0;
int k1=0;
int k2=0;
int linterm=0;
int np=0;
double sx=0;
double sy=0;
double q=0;
double z=0;
CRowDouble point;
CRowDouble a;
CMatrixDouble gp;
CRowDouble x;
CRowDouble y;
int unx=0;
int uny=0;
CMatrixDouble xwr;
CMatrixDouble v;
int i=0;
int j=0;
int k=0;
int l=0;
int modelversion=0;
for(nx=2; nx<=3; nx++)
{
//--- prepare test problem
linterm=CMath::RandomInteger(3)+1;
sx=MathPow(zx,px*(CMath::RandomInteger(3)-1));
sy=MathPow(zy,py*(CMath::RandomInteger(3)-1));
x=vector<double>::Zeros(nx);
y=vector<double>::Zeros(ny);
point=vector<double>::Zeros(nx);
CRBF::RBFCreate(nx,ny,s);
q=0.25+CMath::RandomReal();
z=4.5+CMath::RandomReal();
CRBF::RBFSetAlgoQNN(s,q,z);
a=vector<double>::Zeros(nx+1);
if(linterm==1)
{
CRBF::RBFSetLinTerm(s);
for(i=0; i<nx ; i++)
a.Set(i,sy*(2*CMath::RandomReal()-1)/sx);
a.Set(nx,sy*(2*CMath::RandomReal()-1));
}
if(linterm==2)
{
CRBF::RBFSetConstTerm(s);
for(i=0; i<nx ; i++)
a.Set(i,0);
a.Set(nx,sy*(2*CMath::RandomReal()-1));
}
if(linterm==3)
{
CRBF::RBFSetZeroTerm(s);
for(i=0; i<=nx; i++)
a.Set(i,0);
}
//--- start points for grid
for(i=0; i<nx ; i++)
point.Set(i,sx*(2*CMath::RandomReal()-1));
if(nx==2)
{
for(k0=2; k0<=4; k0++)
{
for(k1=2; k1<=4; k1++)
{
np=k0*k1;
gp=matrix<double>::Zeros(np,nx+ny);
//--- create grid
for(i=0; i<k0 ; i++)
{
for(j=0; j<k1 ; j++)
{
gp.Set(i*k1+j,0,point[0]+sx*i);
gp.Set(i*k1+j,1,point[1]+sx*j);
gp.Set(i*k1+j,nx,a[nx]);
for(k=0; k<nx ; k++)
gp.Add(i*k1+j,nx,gp.Get(i*k1+j,k)*a[k]);
}
}
CRBF::RBFSetPoints(s,gp,np);
CRBF::RBFBuildModel(s,rep);
//--- test for RBFUnpack
CRBF::RBFUnpack(s,unx,uny,xwr,np,v,modelversion);
if((((((nx!=unx || ny!=uny) || CAp::Rows(xwr)!=np) || CAp::Cols(xwr)!=nx+ny+1) || CAp::Rows(v)!=ny) || CAp::Cols(v)!=nx+1) || modelversion!=1)
{
result=true;
return(result);
}
for(i=0; i<nx ; i++)
{
if(MathAbs(v.Get(0,i)-a[i])>(sy/sx*m_tol))
{
result=true;
return(result);
}
}
if(MathAbs(v.Get(0,nx)-a[nx])>(sy*m_tol))
{
result=true;
return(result);
}
for(i=0; i<np; i++)
if(MathAbs(xwr.Get(i,unx))>(sy*m_tol))
{
result=true;
return(result);
}
}
}
}
//---
if(nx==3)
{
for(k0=2; k0<=4; k0++)
{
for(k1=2; k1<=4; k1++)
{
for(k2=2; k2<=4; k2++)
{
np=k0*k1*k2;
gp=matrix<double>::Zeros(np,nx+ny);
//--- create grid
for(i=0; i<k0 ; i++)
{
for(j=0; j<k1 ; j++)
for(k=0; k<k2 ; k++)
{
gp.Set((i*k1+j)*k2+k,0,point[0]+sx*i);
gp.Set((i*k1+j)*k2+k,1,point[1]+sx*j);
gp.Set((i*k1+j)*k2+k,2,point[2]+sx*k);
gp.Set((i*k1+j)*k2+k,nx,a[nx]);
for(l=0; l<nx ; l++)
gp.Add((i*k1+j)*k2+k,nx,gp.Get((i*k1+j)*k2+k,l)*a[l]);
}
}
CRBF::RBFSetPoints(s,gp,np);
CRBF::RBFBuildModel(s,rep);
//--- test for RBFUnpack
CRBF::RBFUnpack(s,unx,uny,xwr,np,v,modelversion);
if((((((nx!=unx || ny!=uny) || CAp::Rows(xwr)!=np) || CAp::Cols(xwr)!=nx+ny+1) || CAp::Rows(v)!=ny) || CAp::Cols(v)!=nx+1) || modelversion!=1)
{
result=true;
return(result);
}
for(i=0; i<nx ; i++)
{
if(MathAbs(v.Get(0,i)-a[i])>(sy/sx*m_tol))
{
result=true;
return(result);
}
}
if(MathAbs(v.Get(0,nx)-a[nx])>(sy*m_tol))
{
result=true;
return(result);
}
for(i=0; i<np; i++)
{
if(MathAbs(xwr.Get(i,unx))>(sy*m_tol))
{
result=true;
return(result);
}
}
}
}
}
}
}
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| This function tests serialization |
//+------------------------------------------------------------------+
bool CTestRBFUnit::SerializationTest(void)
{
//--- create variables
bool result=false;
CRBFModel s;
CRBFModel s2;
CRBFReport rep;
int n=0;
int nx=0;
int ny=0;
int k0=0;
int k1=0;
int k2=0;
int i=0;
int i0=0;
int i1=0;
int i2=0;
int j=0;
int k=0;
double rbase=0;
int nlayers=0;
int bf=0;
int gridsize=0;
CMatrixDouble xy;
CRowDouble testpoint;
CRowDouble y0;
CRowDouble y1;
CRowDouble scalevec;
CHighQualityRandState rs;
double meanseparation=0;
int algoidx=0;
CHighQualityRand::HQRndRandomize(rs);
//--- This function generates random 2 or 3 dimensional problem,
//--- builds RBF model (QNN is used), serializes/unserializes it, then compares
//--- models by calculating model value at some random point.
//--- Additionally we test that new model (one which was restored
//--- after serialization) has lost all model construction settings,
//--- i.e. if we call RBFBuildModel() on a NEW model, we will get
//--- empty (zero) model.
for(nx=2; nx<=3; nx++)
{
for(ny=1; ny<=2; ny++)
{
//--- prepare test problem
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetAlgoQNN(s,1.0,5.0);
CRBF::RBFSetLinTerm(s);
if(nx==2)
{
//--- 2-dimensional problem
k0=2+CMath::RandomInteger(4);
k1=2+CMath::RandomInteger(4);
xy=matrix<double>::Zeros(k0*k1,nx+ny);
for(i0=0; i0<k0 ; i0++)
{
for(i1=0; i1<k1 ; i1++)
{
xy.Set(i0*k1+i1,0,i0+0.1*(2*CMath::RandomReal()-1));
xy.Set(i0*k1+i1,1,i1+0.1*(2*CMath::RandomReal()-1));
for(j=0; j<ny; j++)
xy.Set(i0*k1+i1,nx+j,2*CMath::RandomReal()-1);
}
}
testpoint=vector<double>::Zeros(nx);
testpoint.Set(0,CMath::RandomReal()*(k0-1));
testpoint.Set(1,CMath::RandomReal()*(k1-1));
}
else
{
//--- 3-dimensional problem
k0=2+CMath::RandomInteger(4);
k1=2+CMath::RandomInteger(4);
k2=2+CMath::RandomInteger(4);
xy=matrix<double>::Zeros(k0*k1*k2,nx+ny);
for(i0=0; i0<k0 ; i0++)
{
for(i1=0; i1<k1 ; i1++)
{
for(i2=0; i2<k2 ; i2++)
{
xy.Set(i0*k1*k2+i1*k2+i2,0,i0+0.1*(2*CMath::RandomReal()-1));
xy.Set(i0*k1*k2+i1*k2+i2,1,i1+0.1*(2*CMath::RandomReal()-1));
xy.Set(i0*k1*k2+i1*k2+i2,2,i2+0.1*(2*CMath::RandomReal()-1));
for(j=0; j<ny; j++)
xy.Set(i0*k1*k2+i1*k2+i2,nx+j,2*CMath::RandomReal()-1);
}
}
}
testpoint=vector<double>::Zeros(nx);
testpoint.Set(0,CMath::RandomReal()*(k0-1));
testpoint.Set(1,CMath::RandomReal()*(k1-1));
testpoint.Set(2,CMath::RandomReal()*(k2-1));
}
CRBF::RBFSetPoints(s,xy,CAp::Rows(xy));
//--- Build model, serialize, compare
CRBF::RBFBuildModel(s,rep);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
_local_serializer.Alloc_Start();
CRBF::RBFAlloc(_local_serializer,s);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CRBF::RBFSerialize(_local_serializer,s);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CRBF::RBFUnserialize(_local_serializer,s2);
_local_serializer.Stop();
}
CRBF::RBFCalc(s,testpoint,y0);
CRBF::RBFCalc(s2,testpoint,y1);
if(CAp::Len(y0)!=ny || CAp::Len(y1)!=ny)
{
result=true;
return(result);
}
for(j=0; j<ny; j++)
{
if(y0[j]!=y1[j])
{
result=true;
return(result);
}
}
//--- Check that calling RBFBuildModel() on S2 (new model)
//--- will result in construction of zero model, i.e. test
//--- that serialization restores model, but not dataset
//--- which was used to build model.
CRBF::RBFBuildModel(s2,rep);
CRBF::RBFCalc(s2,testpoint,y1);
if(CAp::Len(y1)!=ny)
{
result=true;
return(result);
}
for(j=0; j<ny; j++)
{
if(y1[j]!=0.0)
{
result=true;
return(result);
}
}
}
}
//--- This function generates random 2 or 3 dimensional problem,
//--- builds model using RBF-ML algo, serializes/unserializes it,
//--- then compares models by calculating model value at some
//--- random point.
//--- Additionally we test that new model (one which was restored
//--- after serialization) has lost all model construction settings,
//--- i.e. if we call RBFBuildModel() on a NEW model, we will get
//--- empty (zero) model.
for(nx=2; nx<=3; nx++)
{
for(ny=1; ny<=2; ny++)
{
//--- prepare test problem
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetAlgoMultilayer(s,5.0,5,1.0E-3);
CRBF::RBFSetLinTerm(s);
if(nx==2)
{
//--- 2-dimensional problem
k0=2+CMath::RandomInteger(4);
k1=2+CMath::RandomInteger(4);
xy=matrix<double>::Zeros(k0*k1,nx+ny);
for(i0=0; i0<k0 ; i0++)
{
for(i1=0; i1<k1 ; i1++)
{
xy.Set(i0*k1+i1,0,i0+0.1*(2*CMath::RandomReal()-1));
xy.Set(i0*k1+i1,1,i1+0.1*(2*CMath::RandomReal()-1));
for(j=0; j<ny; j++)
xy.Set(i0*k1+i1,nx+j,2*CMath::RandomReal()-1);
}
}
testpoint=vector<double>::Zeros(nx);
testpoint.Set(0,CMath::RandomReal()*(k0-1));
testpoint.Set(1,CMath::RandomReal()*(k1-1));
}
else
{
//--- 3-dimensional problem
k0=2+CMath::RandomInteger(4);
k1=2+CMath::RandomInteger(4);
k2=2+CMath::RandomInteger(4);
xy=matrix<double>::Zeros(k0*k1*k2,nx+ny);
for(i0=0; i0<k0 ; i0++)
{
for(i1=0; i1<k1 ; i1++)
for(i2=0; i2<k2 ; i2++)
{
xy.Set(i0*k1*k2+i1*k2+i2,0,i0+0.1*(2*CMath::RandomReal()-1));
xy.Set(i0*k1*k2+i1*k2+i2,1,i1+0.1*(2*CMath::RandomReal()-1));
xy.Set(i0*k1*k2+i1*k2+i2,2,i2+0.1*(2*CMath::RandomReal()-1));
for(j=0; j<ny; j++)
xy.Set(i0*k1*k2+i1*k2+i2,nx+j,2*CMath::RandomReal()-1);
}
}
testpoint=vector<double>::Zeros(nx);
testpoint.Set(0,CMath::RandomReal()*(k0-1));
testpoint.Set(1,CMath::RandomReal()*(k1-1));
testpoint.Set(2,CMath::RandomReal()*(k2-1));
}
CRBF::RBFSetPoints(s,xy,CAp::Rows(xy));
//--- Build model, serialize, compare
CRBF::RBFBuildModel(s,rep);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
_local_serializer.Alloc_Start();
CRBF::RBFAlloc(_local_serializer,s);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CRBF::RBFSerialize(_local_serializer,s);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CRBF::RBFUnserialize(_local_serializer,s2);
_local_serializer.Stop();
}
CRBF::RBFCalc(s,testpoint,y0);
CRBF::RBFCalc(s2,testpoint,y1);
if(CAp::Len(y0)!=ny || CAp::Len(y1)!=ny)
{
result=true;
return(result);
}
for(j=0; j<ny; j++)
{
if(y0[j]!=y1[j])
{
result=true;
return(result);
}
}
//--- Check that calling RBFBuildModel() on S2 (new model)
//--- will result in construction of zero model, i.e. test
//--- that serialization restores model, but not dataset
//--- which was used to build model.
CRBF::RBFBuildModel(s2,rep);
CRBF::RBFCalc(s2,testpoint,y1);
if(CAp::Len(y1)!=ny)
{
result=true;
return(result);
}
for(j=0; j<ny; j++)
if(y1[j]!=0.0)
{
result=true;
return(result);
}
}
}
//--- This function generates random 1...4-dimensional problem,
//--- builds model using HRBF algo, serializes/unserializes it,
//--- then compares models by calculating model value at some
//--- random point.
//--- NOTE: we choose at random whether to use default scaling -
//--- or user-supplied one.
//--- Additionally we test that new model (one which was restored
//--- after serialization) has lost all model construction settings,
//--- i.e. if we call RBFBuildModel() on a NEW model, we will get
//--- empty (zero) model.
for(nx=1; nx<=4; nx++)
{
for(ny=1; ny<=2; ny++)
{
//--- problem setup
n=150;
rbase=0.33;
nlayers=5;
gridsize=(int)MathRound(MathPow(n,(double)1/(double)nx))+1;
bf=CMath::RandomInteger(2);
n=(int)MathRound(MathPow(gridsize,nx));
xy=matrix<double>::Zeros(n,nx+ny);
CAp::Assert(gridsize>1);
CAp::Assert((double)(n)==(double)(MathPow(gridsize,nx)));
for(i=0; i<n; i++)
{
k=i;
for(j=0; j<nx ; j++)
{
xy.Set(i,j,(double)(k%gridsize)/(double)(gridsize-1));
k=k/gridsize;
}
for(j=0; j<ny; j++)
xy.Set(i,nx+j,CMath::RandomReal()-0.5);
}
testpoint=vector<double>::Zeros(nx);
for(j=0; j<nx ; j++)
testpoint.Set(j,CMath::RandomReal());
scalevec=vector<double>::Zeros(nx);
for(j=0; j<nx ; j++)
scalevec.Set(j,MathPow(2,2*CMath::RandomReal()-1));
//--- prepare test problem
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetV2BF(s,bf);
CRBF::RBFSetAlgoHierarchical(s,rbase,nlayers,0.0);
CRBF::RBFSetLinTerm(s);
if(CMath::RandomReal()>0.5)
CRBF::RBFSetPoints(s,xy,CAp::Rows(xy));
else
CRBF::RBFSetPointsAndScales(s,xy,CAp::Rows(xy),scalevec);
//--- Build model, serialize, compare
CRBF::RBFBuildModel(s,rep);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CRBF::RBFAlloc(_local_serializer,s);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CRBF::RBFSerialize(_local_serializer,s);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CRBF::RBFUnserialize(_local_serializer,s2);
_local_serializer.Stop();
}
CRBF::RBFCalc(s,testpoint,y0);
CRBF::RBFCalc(s2,testpoint,y1);
if(CAp::Len(y0)!=ny || CAp::Len(y1)!=ny)
{
result=true;
return(result);
}
for(j=0; j<ny; j++)
{
if(y0[j]!=y1[j])
{
result=true;
return(result);
}
}
//--- Check that calling RBFBuildModel() on S2 (new model)
//--- will result in construction of zero model, i.e. test
//--- that serialization restores model, but not dataset
//--- which was used to build model.
CRBF::RBFBuildModel(s2,rep);
CRBF::RBFCalc(s2,testpoint,y1);
if(CAp::Len(y1)!=ny)
{
result=true;
return(result);
}
for(j=0; j<ny; j++)
{
if(y1[j]!=0.0)
{
result=true;
return(result);
}
}
}
}
//--- This function generates random 1...4-dimensional problem,
//--- builds model using XRBF algo, serializes/unserializes it,
//--- then compares models by calculating model value at some
//--- random point.
//--- NOTE: we choose at random whether to use default scaling -
//--- or user-supplied one.
//--- Additionally we test that new model (one which was restored
//--- after serialization) has lost all model construction settings,
//--- i.e. if we call RBFBuildModel() on a NEW model, we will get
//--- empty (zero) model.
for(algoidx=0; algoidx<m_xrbfalgocnt ; algoidx++)
{
for(nx=1; nx<=4; nx++)
{
for(ny=1; ny<=2; ny++)
{
//--- problem setup
n=150;
GenerateGoodRandomGrid(n,nx,ny,rs,xy,meanseparation);
testpoint=vector<double>::Zeros(nx);
for(j=0; j<nx ; j++)
testpoint.Set(j,CHighQualityRand::HQRndNormal(rs));
scalevec=vector<double>::Zeros(nx);
for(j=0; j<nx ; j++)
scalevec.Set(j,MathPow(2,CHighQualityRand::HQRndNormal(rs)));
//--- prepare test problem
CRBF::RBFCreate(nx,ny,s);
SetXRBFAlgorithmExact(s,algoidx,meanseparation);
if(CHighQualityRand::HQRndNormal(rs)>0.5)
CRBF::RBFSetPoints(s,xy,CAp::Rows(xy));
else
CRBF::RBFSetPointsAndScales(s,xy,CAp::Rows(xy),scalevec);
//--- Clear target model
CRBF::RBFCreate(1,1,s2);
CRBF::RBFBuildModel(s2,rep);
CRBF::RBFCalc(s2,testpoint,y1);
if(CAp::Len(y1)!=1)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:1913");
return(result);
}
if((double)(y1[0])!=0.0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:1918");
return(result);
}
//--- Build model, serialize, compare
CRBF::RBFBuildModel(s,rep);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
_local_serializer.Reset();
_local_serializer.Alloc_Start();
CRBF::RBFAlloc(_local_serializer,s);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CRBF::RBFSerialize(_local_serializer,s);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CRBF::RBFUnserialize(_local_serializer,s2);
_local_serializer.Stop();
}
CRBF::RBFCalc(s,testpoint,y0);
CRBF::RBFCalc(s2,testpoint,y1);
if(CAp::Len(y0)!=ny || CAp::Len(y1)!=ny)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:1933");
return(result);
}
for(j=0; j<ny; j++)
{
if(y0[j]!=y1[j])
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:1939");
return(result);
}
}
//--- Check that calling RBFBuildModel() on S2 (new model)
//--- will result in construction of zero model, i.e. test
//--- that serialization restores model, but not dataset
//--- which was used to build model.
CRBF::RBFBuildModel(s2,rep);
CRBF::RBFCalc(s2,testpoint,y1);
if(CAp::Len(y1)!=ny)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:1953");
return(result);
}
for(j=0; j<ny; j++)
if(y1[j]!=0.0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:1959");
return(result);
}
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestRBFUnit::SearcherR(const CMatrixDouble &Y0,
const CMatrixDouble &Y1,
int n,int ny,int errtype,
const CRowDouble &B1,
const CRowDouble &Delta)
{
//--- create variables
double oralerr=1.0E-1;
double iralerr=1.0E-2;
int lb=25;
int rb=75;
CRowDouble irerr;
CRowDouble orerr;
int i=0;
int j=0;
CMatrixDouble y0=Y0;
CMatrixDouble y1=Y1;
CRowDouble b1=B1;
CRowDouble delta=Delta;
//--- check parameters
if(!CAp::Assert(n>0,"SearchErr: invalid parameter N(N<=0)."))
return(true);
if(!CAp::Assert(ny>0,"SearchErr: invalid parameter NY(NY<=0)."))
return(true);
orerr=vector<double>::Zeros(ny);
irerr=vector<double>::Zeros(ny);
if(errtype==1)
{
for(i=0; i<n; i++)
{
for(j=0; j<ny; j++)
{
if(orerr[j]<(double)(MathAbs(y0.Get(i,j)-y1.Get(i,j))))
orerr.Set(j,MathAbs(y0.Get(i,j)-y1.Get(i,j)));
}
}
for(i=0; i<ny; i++)
{
if(orerr[i]>(b1[i]+delta[i]) || orerr[i]<(b1[i]-delta[i]))
return(true);
}
}
else
{
if(errtype==2)
{
for(i=0; i<n; i++)
{
for(j=0; j<ny; j++)
if(i>lb && i<rb)
{
if(irerr[j]<MathAbs(y0.Get(i,j)-y1.Get(i,j)))
irerr.Set(j,MathAbs(y0.Get(i,j)-y1.Get(i,j)));
}
else
{
if(orerr[j]<MathAbs(y0.Get(i,j)-y1.Get(i,j)))
orerr.Set(j,MathAbs(y0.Get(i,j)-y1.Get(i,j)));
}
}
for(i=0; i<ny; i++)
if(orerr[i]>oralerr || irerr[i]>iralerr)
return(true);
}
else
{
CAp::Assert(false,"SearchErr: invalid argument ErrType(ErrType neither 1 nor 2)");
return(true);
}
}
//--- create variables
return(false);
}
//+------------------------------------------------------------------+
//| Function for testing basic functionality of RBF module on regular|
//| grids with multi-layer algorithm in 2-3D. |
//+------------------------------------------------------------------+
bool CTestRBFUnit::BasicMultilayerRBFTest(void)
{
//--- create variables
bool result=false;
int range=10;
int passcount=10;
double eps=1.0E-6;
CRBFModel s;
CRBFReport rep;
CRBFCalcBuffer calcbuf;
int nx=0;
int ny=0;
int k0=0;
int k1=0;
int k2=0;
int linterm=0;
int np=0;
double q=0;
int layers=0;
double s1=0;
double s2=0;
double gstep=0;
CRowDouble point;
CMatrixDouble gp;
CRowDouble x;
CRowDouble y;
CMatrixDouble gy;
CRowDouble gpgx0;
CRowDouble gpgx1;
CRowDouble gpgx2;
CRowDouble gcy;
int pass=0;
int i=0;
int j=0;
int k=0;
int l=0;
int fidx=0;
double r0=0;
int margin=0;
int gridsize=0;
double threshold=0;
double v=0;
//--- Test that RBF model with sufficient layers will exactly reproduce
//--- target function.
for(pass=0; pass<=passcount-1; pass++)
{
//--- prepare test problem
k0=6+CMath::RandomInteger(3);
k1=6+CMath::RandomInteger(3);
k2=6+CMath::RandomInteger(3);
s1=MathPow(range,CMath::RandomInteger(3)-1);
s2=MathPow(range,CMath::RandomInteger(3)-1);
nx=CMath::RandomInteger(2)+2;
ny=CMath::RandomInteger(2)+1;
linterm=CMath::RandomInteger(3)+1;
layers=5;
gstep=s1/6;
q=s1;
//--- Create RBF structure and auxiliary structures
x=vector<double>::Zeros(nx);
y=vector<double>::Zeros(ny);
point=vector<double>::Zeros(nx);
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetAlgoMultilayer(s,q,layers,0.0);
if(linterm==1)
CRBF::RBFSetLinTerm(s);
if(linterm==2)
CRBF::RBFSetConstTerm(s);
if(linterm==3)
CRBF::RBFSetZeroTerm(s);
for(i=0; i<nx ; i++)
point.Set(i,s1*(2*CMath::RandomReal()-1));
//--- 2-dimensional test problem
if(nx==2)
{
np=k0*k1;
gp=matrix<double>::Zeros(np,nx+ny);
//--- create grid
for(i=0; i<k0 ; i++)
{
for(j=0; j<k1 ; j++)
{
gp.Set(i*k1+j,0,point[0]+gstep*i);
gp.Set(i*k1+j,1,point[1]+gstep*j);
for(k=0; k<ny; k++)
gp.Set(i*k1+j,nx+k,s2*(2*CMath::RandomReal()-1));
}
}
CRBF::RBFSetPoints(s,gp,np);
CRBF::RBFBuildModel(s,rep);
CRBF::RBFCreateCalcBuffer(s,calcbuf);
if(ny==1)
{
gpgx0=vector<double>::Zeros(k0);
gpgx1=vector<double>::Zeros(k1);
for(i=0; i<k0 ; i++)
gpgx0.Set(i,point[0]+gstep*i);
for(i=0; i<k1 ; i++)
gpgx1.Set(i,point[1]+gstep*i);
CRBF::RBFGridCalc2(s,gpgx0,k0,gpgx1,k1,gy);
for(i=0; i<k0 ; i++)
for(j=0; j<k1 ; j++)
CAp::SetErrorFlag(result,(double)(MathAbs(gy.Get(i,j)-gp.Get(i*k1+j,nx)))>(double)(s2*eps),"testrbfunit.ap:2366");
}
for(i=0; i<np; i++)
{
//--- For each row we randomly choose a function to test
//--- and call it. We do not call multiple functions per
//--- row because carry-over effects may mask errors in
//--- some function (say, it is possible that function
//--- simply returns results from previous call of some
//--- other function which were stored in the RBF model;
//--- in this case, previous call with same parameters
//--- may hide deficiencies in the function).
x.Set(0,gp.Get(i,0));
x.Set(1,gp.Get(i,1));
fidx=CMath::RandomInteger(4);
if(fidx==0 && ny!=1)
continue;
if(fidx==0)
y.Set(0,CRBF::RBFCalc2(s,x[0],x[1]));
if(fidx==1)
CRBF::RBFCalc(s,x,y);
if(fidx==2)
CRBF::RBFCalcBuf(s,x,y);
if(fidx==3)
CRBF::RBFTSCalcBuf(s,calcbuf,x,y);
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,MathAbs(gp.Get(i,nx+j)-y[j])>(s2*eps),"testrbfunit.ap:2394");
}
}
//--- 3-dimensional test problems
if(nx==3)
{
np=k0*k1*k2;
gp=matrix<double>::Zeros(np,nx+ny);
//--- create grid, build model
gpgx0=vector<double>::Zeros(k0);
gpgx1=vector<double>::Zeros(k1);
gpgx2=vector<double>::Zeros(k2);
for(i=0; i<k0 ; i++)
gpgx0.Set(i,point[0]+gstep*i);
for(i=0; i<k1 ; i++)
gpgx1.Set(i,point[1]+gstep*i);
for(i=0; i<k2 ; i++)
gpgx2.Set(i,point[2]+gstep*i);
for(i=0; i<k0 ; i++)
{
for(j=0; j<k1 ; j++)
for(k=0; k<k2 ; k++)
{
gp.Set((i*k1+j)*k2+k,0,gpgx0[i]);
gp.Set((i*k1+j)*k2+k,1,gpgx1[j]);
gp.Set((i*k1+j)*k2+k,2,gpgx2[k]);
for(l=0; l<ny; l++)
gp.Set((i*k1+j)*k2+k,nx+l,s2*(2*CMath::RandomReal()-1));
}
}
CRBF::RBFSetPoints(s,gp,np);
CRBF::RBFBuildModel(s,rep);
CRBF::RBFCreateCalcBuffer(s,calcbuf);
//--- Test RBFCalc3(), RBFCalc() and RBFCalcBuf() vs expected values on the grid (we expect good fit).
for(i=0; i<np; i++)
{
//--- For each row we randomly choose a function to test
//--- and call it. We do not call multiple functions per
//--- row because carry-over effects may mask errors in
//--- some function (say, it is possible that function
//--- simply returns results from previous call of some
//--- other function which were stored in the RBF model;
//--- in this case, previous call with same parameters
//--- may hide deficiencies in the function).
x.Set(0,gp.Get(i,0));
x.Set(1,gp.Get(i,1));
x.Set(2,gp.Get(i,2));
fidx=CMath::RandomInteger(4);
if(fidx==0 && ny!=1)
continue;
if(fidx==0)
y.Set(0,CRBF::RBFCalc3(s,x[0],x[1],x[2]));
if(fidx==1)
CRBF::RBFCalc(s,x,y);
if(fidx==2)
CRBF::RBFCalcBuf(s,x,y);
if(fidx==3)
CRBF::RBFTSCalcBuf(s,calcbuf,x,y);
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,MathAbs(gp.Get(i,nx+j)-y[j])>(s2*eps),"testrbfunit.ap:2462");
}
//--- Test RBFGridCalc3V vs RBFCalc()
CRBF::RBFGridCalc3V(s,gpgx0,k0,gpgx1,k1,gpgx2,k2,gcy);
for(i=0; i<k0 ; i++)
{
for(j=0; j<k1 ; j++)
for(k=0; k<k2 ; k++)
{
x.Set(0,gpgx0[i]);
x.Set(1,gpgx1[j]);
x.Set(2,gpgx2[k]);
CRBF::RBFCalcBuf(s,x,y);
for(l=0; l<ny; l++)
CAp::SetErrorFlag(result,MathAbs(y[l]-gcy[l+ny*(i+j*k0+k*k0*k1)])>(1.0E-9*s2),"testrbfunit.ap:2479");
}
}
}
}
//--- Test smoothing properties of RBF model: model with just one layer
//--- and large initial radius will produce "average" value of neighbors.
//--- In order to test it we create regular grid, fill it with regular
//--- +1/-1 pattern, and test model values in the inner points. Model
//--- values should be around zero (we use handcrafted threshold to test
//--- it). Radius is chosen to be several times larger than grid step.
//--- We perform test for 2D model, because same behavior is expected
//--- regardless of dimensionality.
r0=3;
margin=5;
gridsize=2*margin+20;
threshold=0.1;
nx=2;
ny=1;
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetAlgoMultilayer(s,r0,1,0.0);
CRBF::RBFSetZeroTerm(s);
gp=matrix<double>::Zeros(gridsize*gridsize,nx+ny);
for(i=0; i<gridsize; i++)
{
for(j=0; j<gridsize; j++)
{
gp.Set(i*gridsize+j,0,i);
gp.Set(i*gridsize+j,1,j);
gp.Set(i*gridsize+j,2,0.10*CMath::RandomReal()-0.05+(2*((i+j)%2)-1));
}
}
CRBF::RBFSetPoints(s,gp,gridsize*gridsize);
CRBF::RBFBuildModel(s,rep);
v=0.0;
for(i=margin; i<gridsize-margin ; i++)
{
for(j=margin; j<gridsize-margin ; j++)
v=MathMax(v,MathAbs(CRBF::RBFCalc2(s,i,j)));
}
CAp::SetErrorFlag(result,v>threshold,"testrbfunit.ap:2526");
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing basic functionality of RBF module on regular|
//| grids with multi-layer algorithm in 2-3D. |
//+------------------------------------------------------------------+
void CTestRBFUnit::GridCalc23Test(bool &errorflag)
{
//--- create variables
CRBFModel s;
CRBFReport rep;
CHighQualityRandState rs;
int pass=0;
int i=0;
int j=0;
int k=0;
int l=0;
double perturbation=0;
CRowInt kx;
double sx=0;
double sy=0;
int nx=0;
int ny=0;
int linterm=0;
int layers=0;
int npoints=0;
CMatrixDouble xy;
bool gf[];
bool rf[];
CRowDouble x0;
CRowDouble x1;
CRowDouble x2;
CRowDouble gy;
CRowDouble gy2;
CRowDouble x;
CRowDouble y;
double sparsity=0;
CHighQualityRand::HQRndRandomize(rs);
for(pass=0; pass<=24; pass++)
{
//--- prepare test problem
kx.Resize(3);
for(i=0; i<=2; i++)
{
//--- 66% of cases - large grid
if(CHighQualityRand::HQRndUniformI(rs,3)==0)
{
kx.Set(i,(int)MathRound(10*MathPow(10,CHighQualityRand::HQRndUniformR(rs))));
continue;
}
//--- 33% of cases - small grid
k=CHighQualityRand::HQRndUniformI(rs,3);
if(k==0)
kx.Set(i,1);
if(k==1)
kx.Set(i,2);
if(k==2)
kx.Set(i,10);
}
sx=MathPow(10,CHighQualityRand::HQRndUniformI(rs,3)-1);
sy=MathPow(10,CHighQualityRand::HQRndUniformI(rs,3)-1);
nx=3;
ny=1+CHighQualityRand::HQRndUniformI(rs,5);
linterm=CHighQualityRand::HQRndUniformI(rs,3)+1;
layers=CHighQualityRand::HQRndUniformI(rs,3)+1;
npoints=100;
xy=matrix<double>::Zeros(npoints,nx+ny);
for(i=0; i<npoints ; i++)
{
for(j=0; j<nx ; j++)
xy.Set(i,j,CHighQualityRand::HQRndUniformR(rs)*sx);
for(j=0; j<ny; j++)
xy.Set(i,nx+j,CHighQualityRand::HQRndUniformR(rs)*sy);
}
//--- Create RBF model
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetAlgoMultilayer(s,0.1*sx,layers,0.0);
if(linterm==1)
CRBF::RBFSetLinTerm(s);
if(linterm==2)
CRBF::RBFSetConstTerm(s);
if(linterm==3)
CRBF::RBFSetZeroTerm(s);
CRBF::RBFSetPoints(s,xy,npoints);
CRBF::RBFBuildModel(s,rep);
CAp::SetErrorFlag(errorflag,rep.m_terminationtype<=0,"testrbfunit.ap:2623");
if(errorflag)
return;
//--- Prepare test grid
x0=vector<double>::Zeros(kx[0]);
for(i=0; i<kx[0] ; i++)
{
perturbation=0.5*(CHighQualityRand::HQRndUniformR(rs)-0.5);
x0.Set(i,sx*(i+perturbation)/kx[0]);
}
x1=vector<double>::Zeros(kx[1]);
for(i=0; i<kx[1] ; i++)
{
perturbation=0.5*(CHighQualityRand::HQRndUniformR(rs)-0.5);
x1.Set(i,sx*(i+perturbation)/kx[1]);
}
x2=vector<double>::Zeros(kx[2]);
for(i=0; i<kx[2] ; i++)
{
perturbation=0.5*(CHighQualityRand::HQRndUniformR(rs)-0.5);
x2.Set(i,sx*(i+perturbation)/kx[2]);
}
//--- Test calculation on grid
x=vector<double>::Zeros(nx);
y=vector<double>::Zeros(ny);
gy.Resize(0);
CRBF::RBFGridCalc3V(s,x0,kx[0],x1,kx[1],x2,kx[2],gy);
for(i=0; i<kx[0] ; i++)
{
for(j=0; j<kx[1] ; j++)
for(k=0; k<kx[2] ; k++)
{
x.Set(0,x0[i]);
x.Set(1,x1[j]);
x.Set(2,x2[k]);
CRBF::RBFCalcBuf(s,x,y);
for(l=0; l<ny; l++)
CAp::SetErrorFlag(errorflag,(double)(MathAbs(y[l]-gy[l+ny*(i+j*kx[0]+k*kx[0]*kx[1])]))>(double)(1.0E-9*sy),"testrbfunit.ap:2665");
}
}
//--- Test calculation on subset of regular grid:
//--- * select sparsity coefficient (from 1.0 to 0.001)
//--- * fill bitmap array
//--- * Test 1: compare full and subset versions
//--- * Test 2: check sparsity. Subset function may perform additional
//--- evaluations because it processes data micro-row by micro-row.
//--- So, we can't check that all elements which were not flagged
//--- are zero - some of them may become non-zero. However, if entire
//--- row is empty, we can reasonably expect (informal guarantee)
//--- that it is not processed. So, we check empty (completely
//--- unflagged) rows
sparsity=MathPow(10,-CHighQualityRand::HQRndUniformI(rs,4));
ArrayResize(gf,kx[0]*kx[1]*kx[2]);
ArrayResize(rf,kx[1]*kx[2]);
ArrayInitialize(rf,false);
for(i=0; i<=kx[0]*kx[1]*kx[2]-1; i++)
{
gf[i]=CHighQualityRand::HQRndUniformR(rs)<sparsity;
if(gf[i])
rf[i/kx[0]]=true;
}
x=vector<double>::Zeros(nx);
y=vector<double>::Zeros(ny);
CRBF::RBFGridCalc3VSubset(s,x0,kx[0],x1,kx[1],x2,kx[2],gf,gy);
CRBF::RBFGridCalc3V(s,x0,kx[0],x1,kx[1],x2,kx[2],gy2);
for(i=0; i<=ny*kx[0]*kx[1]*kx[2]-1; i++)
{
CAp::SetErrorFlag(errorflag,gf[i/ny] && MathAbs(gy[i]-gy2[i])>(1.0E-9*sy),"testrbfunit.ap:2701");
CAp::SetErrorFlag(errorflag,!rf[i/(ny*kx[0])] && gy[i]!=0.0,"testrbfunit.ap:2702");
}
}
}
//+------------------------------------------------------------------+
//| Function for testing basic functionality of XRBF |
//+------------------------------------------------------------------+
bool CTestRBFUnit::BasicXRBFTest(void)
{
//--- create variables
bool result=false;
CRBFModel s;
CRBFModel s2;
CRBFReport rep;
CRBFCalcBuffer tsbuf;
int algoidx=0;
int nx=0;
int ny=0;
int nduplicate=0;
double mx=0;
double errtol=0;
double meanseparation=0;
double lambdav=0;
CMatrixDouble xy;
CMatrixDouble xytest;
CMatrixDouble uxwr;
CMatrixDouble uv;
CMatrixDouble xy2;
CRowDouble x;
CRowDouble y;
CRowDouble y2;
CRowDouble xzero;
CRowDouble yref;
CRowDouble scalevec;
bool bflags[];
int n=0;
int ntest=0;
int gridsize=0;
int i=0;
int j=0;
int k=0;
double delta=0;
double refrms=0;
double refmax=0;
double v=0;
int functype=0;
int densitytype=0;
double width=0;
double lowprec=0;
double highprec=0;
int shaketype=0;
double maxerr=0;
bool fractionalerror;
int unx=0;
int uny=0;
int unc=0;
int modelversion=0;
bool hasscale;
int nondistinctcnt=0;
int termtype=0;
int leadingzeros=0;
double avx2_epsilon=1e-14;
CHighQualityRandState rs;
CHighQualityRand::HQRndRandomize(rs);
//--- First test - random problem, ability to build model
//--- which reproduces function value in all points with
//--- good precision.
//--- All dataset points are located in unit cube on
//--- regular grid. We do not use smoothing for this test.
//--- We use/test following functions:
//--- * RBFCalc()
//--- * RBFCalc2()
//--- * RBFCalc3()
//--- * RBFCalcBuf()
//--- * RBFTsCalcBuf()
errtol=1.0E-5;
for(algoidx=0; algoidx<m_xrbfalgocnt ; algoidx++)
{
for(nx=1; nx<=4; nx++)
{
for(ny=1; ny<=2; ny++)
{
//--- problem setup
n=150;
GenerateNearlyRegularGrid(n,nx,ny,rs,xy,meanseparation);
//--- Build model
CRBF::RBFCreate(nx,ny,s);
SetXRBFAlgorithmExact(s,algoidx,meanseparation);
CRBF::RBFSetPoints(s,xy,n);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:2800");
return(result);
}
CRBF::RBFCreateCalcBuffer(s,tsbuf);
//--- Test ability to reproduce function value
//--- NOTE: we use RBFCalc(XZero) to guarantee that internal state of
//--- RBF model is "reset" between subsequent calls of different
//--- functions. It allows us to make sure that we have no bug
//--- like function simply returning latest result
CAblasF::RSetAllocV(nx,0.0,xzero);
x=vector<double>::Zeros(nx);
y=vector<double>::Zeros(ny);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
x.Set(j,xy.Get(i,j));
CRBF::RBFCalc(s,xzero,y);
if(CMath::RandomReal()>0.5)
yref=vector<double>::Zeros(ny+1);
else
yref.Resize(0);
CRBF::RBFCalc(s,x,yref);
CAp::SetErrorFlag(result,CAp::Len(yref)!=ny,"testrbfunit.ap:2828");
if(result)
return(result);
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,(double)(MathAbs(yref[j]-xy.Get(i,nx+j)))>(double)(errtol*n),"testrbfunit.ap:2832");
if(nx==1 && ny==1)
{
CRBF::RBFCalc(s,xzero,y);
CAp::SetErrorFlag(result,CRBF::RBFCalc1(s,x[0])!=yref[0],"testrbfunit.ap:2838");
}
else
CAp::SetErrorFlag(result,CRBF::RBFCalc1(s,1.2)!=0.0,"testrbfunit.ap:2841");
if(nx==2 && ny==1)
{
CRBF::RBFCalc(s,xzero,y);
CAp::SetErrorFlag(result,MathAbs(CRBF::RBFCalc2(s,x[0],x[1])-yref[0])>avx2_epsilon,"testrbfunit.ap:2847");
}
else
CAp::SetErrorFlag(result,CRBF::RBFCalc2(s,1.2,3.4)!=0.0,"testrbfunit.ap:2850");
if(nx==3 && ny==1)
{
CRBF::RBFCalc(s,xzero,y);
CAp::SetErrorFlag(result,MathAbs(CRBF::RBFCalc3(s,x[0],x[1],x[2])-yref[0])>avx2_epsilon,"testrbfunit.ap:2856");
}
else
CAp::SetErrorFlag(result,CRBF::RBFCalc3(s,1.2,3.4,5.6)!=0.0,"testrbfunit.ap:2859");
CRBF::RBFCalc(s,xzero,y);
if(CMath::RandomReal()>0.5)
{
y=vector<double>::Zeros(ny+1);
CRBF::RBFCalcBuf(s,x,y);
CAp::SetErrorFlag(result,CAp::Len(y)!=ny+1,"testrbfunit.ap:2867");
}
else
{
y=vector<double>::Zeros(ny-1);
CRBF::RBFCalcBuf(s,x,y);
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:2873");
}
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,y[j]!=yref[j],"testrbfunit.ap:2876");
CRBF::RBFCalc(s,xzero,y);
if(CMath::RandomReal()>0.5)
{
y=vector<double>::Zeros(ny+1);
CRBF::RBFTSCalcBuf(s,tsbuf,x,y);
CAp::SetErrorFlag(result,CAp::Len(y)!=ny+1,"testrbfunit.ap:2884");
}
else
{
y=vector<double>::Zeros(ny-1);
CRBF::RBFTSCalcBuf(s,tsbuf,x,y);
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:2890");
}
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,y[j]!=yref[j],"testrbfunit.ap:2893");
}
}
}
}
//--- Test ability to handle surface normal conditions
algoidx=CHighQualityRand::HQRndUniformI(rs,m_xrbfc1algocnt);
nx=2;
ny=CHighQualityRand::HQRndUniformI(rs,4);
//--- Test ability to handle all dataset sizes from 0 to 10
errtol=1.0E-5;
for(algoidx=0; algoidx<m_xrbfalgocnt ; algoidx++)
{
for(nx=1; nx<=4; nx++)
{
for(ny=1; ny<=2; ny++)
{
for(n=0; n<=10; n++)
{
GenerateGoodRandomGrid(n,nx,ny,rs,xy,meanseparation);
CRBF::RBFCreate(nx,ny,s);
SetXRBFAlgorithmExact(s,algoidx,meanseparation);
if(n>0)
{
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CAblasF::RAllocV(nx,scalevec);
for(i=0; i<nx ; i++)
scalevec.Set(i,MathPow(2,0.15*CHighQualityRand::HQRndNormal(rs)));
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
}
else
CRBF::RBFSetPoints(s,xy,n);
}
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:2929");
return(result);
}
for(i=0; i<n; i++)
{
CAblasF::RAllocV(nx,x);
CAblasF::RCopyRV(nx,xy,i,x);
CRBF::RBFCalc(s,x,y);
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:2937");
if(result)
return(result);
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,MathAbs(y[j]-xy.Get(i,nx+j))>errtol,"testrbfunit.ap:2941");
}
}
}
}
}
//--- Test ability to handle datasets with non-distinct points:
//--- * the model correctly predicts mean value at non-distinct points
//--- * Rep.RMSError and Rep.MaxError are correctly computed
errtol=1.0E-4;
for(algoidx=0; algoidx<m_xrbfalgocnt ; algoidx++)
{
for(nx=1; nx<=4; nx++)
{
for(ny=1; ny<=2; ny++)
{
n=1+CHighQualityRand::HQRndUniformI(rs,20);
GenerateGoodRandomGrid(n,nx,ny,rs,xy,meanseparation);
CAblasF::RAllocM(2*n,nx+ny,xy2);
for(i=0; i<n; i++)
{
CAblasF::RCopyRR(nx+ny,xy,i,xy2,2*i+0);
CAblasF::RCopyRR(nx+ny,xy,i,xy2,2*i+1);
v=0.001+CHighQualityRand::HQRndUniformR(rs);
for(j=0; j<ny; j++)
{
xy2.Add(2*i,nx+j,-v);
xy2.Add(2*i+1,nx+j,v);
}
}
CRBF::RBFCreate(nx,ny,s);
SetXRBFAlgorithmExact(s,algoidx,meanseparation);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CAblasF::RAllocV(nx,scalevec);
for(i=0; i<nx ; i++)
scalevec.Set(i,MathPow(2,0.15*CHighQualityRand::HQRndNormal(rs)));
CRBF::RBFSetPointsAndScales(s,xy2,2*n,scalevec);
}
else
{
CAblasF::RSetAllocV(nx,1.0,scalevec);
CRBF::RBFSetPoints(s,xy2,2*n);
}
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:2985");
return(result);
}
refrms=0;
refmax=0;
for(i=0; i<n; i++)
{
CAblasF::RAllocV(nx,x);
CAblasF::RCopyRV(nx,xy,i,x);
CRBF::RBFCalc(s,x,y);
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:2995");
if(result)
return(result);
for(j=0; j<ny; j++)
{
CAp::SetErrorFlag(result,MathAbs(y[j]-0.5*(xy2.Get(2*i,nx+j)+xy2.Get(2*i+1,nx+j)))>errtol,"testrbfunit.ap:3000");
refrms=refrms+CMath::Sqr(y[j]-xy2.Get(2*i,nx+j))+CMath::Sqr(y[j]-xy2.Get(2*i+1,nx+j));
refmax=CApServ::RMaxAbs3(refmax,y[j]-xy2.Get(2*i,nx+j),y[j]-xy2.Get(2*i+1,nx+j));
}
}
refrms=MathSqrt(refrms/(2*n*ny));
CAp::SetErrorFlag(result,MathAbs(refrms-rep.m_rmserror)>errtol,"testrbfunit.ap:3006");
CAp::SetErrorFlag(result,MathAbs(refmax-rep.m_maxerror)>errtol,"testrbfunit.ap:3007");
}
}
}
//--- Test ability of smoothing RBFs to handle datasets with nearly (but not exactly!) non-distinct points:
//--- * the model correctly predicts mean value at these points
//--- * Rep.RMSError and Rep.MaxError are correctly computed
//--- NOTE: at least NX+1 points are required for this test
errtol=0.20;
lambdav=1.0E-4;
for(algoidx=0; algoidx<=m_xrbfsmoothingalgocnt-1; algoidx++)
{
for(nx=1; nx<=4; nx++)
{
for(ny=1; ny<=2; ny++)
{
n=1+CHighQualityRand::HQRndUniformI(rs,20);
n=MathMax(n,(int)MathRound(MathPow(2,nx))+1);
GenerateNearlyRegularGrid(n,nx,ny,rs,xy,meanseparation);
CAblasF::RAllocM(2*n,nx+ny,xy2);
for(i=0; i<n; i++)
{
delta=1.0E-5*MathPow(10,-(4*CHighQualityRand::HQRndUniformR(rs)));
CAblasF::RCopyRR(nx+ny,xy,i,xy2,2*i+0);
CAblasF::RCopyRR(nx+ny,xy,i,xy2,2*i+1);
j=CHighQualityRand::HQRndUniformI(rs,nx);
xy2.Add(2*i+1,j,CHighQualityRand::HQRndNormal(rs)*delta);
v=CHighQualityRand::HQRndUniformR(rs)-0.5;
for(j=0; j<ny; j++)
{
xy2.Add(2*i+0,nx+j,-v);
xy2.Add(2*i+1,nx+j,v);
}
}
CRBF::RBFCreate(nx,ny,s);
SetXRBFAlgorithmSmoothing(s,algoidx,lambdav,meanseparation);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CAblasF::RAllocV(nx,scalevec);
for(i=0; i<nx ; i++)
scalevec.Set(i,MathPow(2,0.15*CHighQualityRand::HQRndNormal(rs)));
CRBF::RBFSetPointsAndScales(s,xy2,2*n,scalevec);
}
else
{
CAblasF::RSetAllocV(nx,1.0,scalevec);
CRBF::RBFSetPoints(s,xy2,2*n);
}
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3058");
return(result);
}
for(i=0; i<n; i++)
{
CAblasF::RAllocV(nx,x);
CAblasF::RCopyRV(nx,xy2,2*i+0,x);
CRBF::RBFCalc(s,x,y);
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:3066");
if(result)
return(result);
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,MathAbs(y[j]-0.5*(xy2.Get(2*i,nx+j)+xy2.Get(2*i+1,nx+j)))>errtol,"testrbfunit.ap:3070");
CAblasF::RCopyRV(nx,xy2,2*i+1,x);
CRBF::RBFCalc(s,x,y);
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:3073");
if(result)
return(result);
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,MathAbs(y[j]-0.5*(xy2.Get(2*i,nx+j)+xy2.Get(2*i+1,nx+j)))>errtol,"testrbfunit.ap:3077");
}
refrms=0;
refmax=0;
for(i=0; i<=2*n-1; i++)
{
CAblasF::RAllocV(nx,x);
CAblasF::RCopyRV(nx,xy2,i,x);
CRBF::RBFCalc(s,x,y);
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:3086");
if(result)
return(result);
for(j=0; j<ny; j++)
{
refrms=refrms+CMath::Sqr(y[j]-xy2.Get(i,nx+j));
refmax=MathMax(refmax,MathAbs(y[j]-xy2.Get(i,nx+j)));
}
}
refrms=MathSqrt(refrms/(2*n*ny));
CAp::SetErrorFlag(result,MathAbs(refrms-rep.m_rmserror)>(1.0E-5),"testrbfunit.ap:3096");
CAp::SetErrorFlag(result,MathAbs(refmax-rep.m_maxerror)>(1.0E-5),"testrbfunit.ap:3097");
}
}
}
//--- Test ability to handle datasets consisting of several isolated "islands",
//--- far away from each other
errtol=1.0E-3;
for(algoidx=0; algoidx<m_xrbfalgocnt ; algoidx++)
{
for(nx=1; nx<=4; nx++)
{
ny=1;
n=50;
GenerateGoodRandomGrid(n,nx,1,rs,xy2,meanseparation);
nduplicate=2*n;
CAblasF::RAllocM(nduplicate,nx+1,xy);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
{
xy.Set(2*i+0,j,xy2.Get(i,j));
xy.Set(2*i+1,j,xy2.Get(i,j)+1000);
}
xy.Set(2*i+0,nx,CHighQualityRand::HQRndNormal(rs));
xy.Set(2*i+1,nx,CHighQualityRand::HQRndNormal(rs));
}
CRBF::RBFCreate(nx,ny,s);
SetXRBFAlgorithmExact(s,algoidx,meanseparation);
CRBF::RBFSetPoints(s,xy,nduplicate);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3129");
return(result);
}
if(rep.m_rmserror>errtol)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3134");
return(result);
}
v=0;
for(i=0; i<=nduplicate-1; i++)
{
CAblasF::RCopyRV(nx,xy,i,x);
CRBF::RBFCalc(s,x,y);
for(j=0; j<ny; j++)
v+=CMath::Sqr(y[j]-xy.Get(i,nx+j));
}
v=MathSqrt(v/(nduplicate*ny));
CAp::SetErrorFlag(result,v>errtol,"testrbfunit.ap:3146");
}
}
//--- Test ability to handle degenerate problems:
//--- * we generate dataset with all points being well separated, except for a few
//--- that have neighbors that are close enough to ruin the multiquadric algorithm
//--- but not close enough to be merged
//--- * we test that the model that was built actually uses Reg-QR
//--- * we also test that small and easy model without degeneracies does not use Reg-QR
//--- The idea of this test is to check that the solver signals about bad condition numbers
//--- by setting DbgRegQRUsedForDDM field.
for(nx=1; nx<=2; nx++)
{
//--- Solve easy problem, test that Reg-QR solver was NOT used
ny=1+CHighQualityRand::HQRndUniformI(rs,3);
n=5;
GenerateGoodRandomGrid(n,nx,ny,rs,xy,meanseparation);
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetPoints(s,xy,n);
CRBF::RBFSetAlgoMultiQuadricAuto(s,0.0);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3174");
return(result);
}
CAp::SetErrorFlag(result,s.m_model3.m_dbgregqrusedforddm,"testrbfunit.ap:3177");
CAp::SetErrorFlag(result,rep.m_rmserror>0.001,"testrbfunit.ap:3178");
//--- Solve badly conditioned problem, test that Reg-QR solver was used
ny=1+CHighQualityRand::HQRndUniformI(rs,3);
n=100;
nduplicate=100;
GenerateGoodRandomGrid(n,nx,ny,rs,xy,meanseparation);
CApServ::RMatrixGrowRowsTo(xy,n+nduplicate,nx+ny);
for(i=0; i<=nduplicate-1; i++)
{
CAblasF::RCopyRR(nx+ny,xy,CHighQualityRand::HQRndUniformI(rs,n),xy,n+i);
k=CHighQualityRand::HQRndUniformI(rs,nx);
xy.Add(n+i,k,0.00001*CHighQualityRand::HQRndNormal(rs));
}
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetPoints(s,xy,n+nduplicate);
CRBF::RBFSetAlgoMultiQuadricAuto(s,0.0);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3200");
return(result);
}
CAp::SetErrorFlag(result,!s.m_model3.m_dbgregqrusedforddm,"testrbfunit.ap:3203");
}
//--- Test that various polynomial term types are actually handled:
//---*we generate "good random" problem
//--- * try various term types: linear, constant, zero
//--- * directly access model term and check that corresponding entries are zero
errtol=1.0E-3;
for(algoidx=0; algoidx<m_xrbfalgocnt ; algoidx++)
{
for(nx=1; nx<=4; nx++)
{
for(ny=1; ny<=2; ny++)
{
for(termtype=0; termtype<=2; termtype++)
{
n=nx+2+CHighQualityRand::HQRndUniformI(rs,10);
GenerateGoodRandomGrid(n,nx,ny,rs,xy,meanseparation);
CRBF::RBFCreate(nx,ny,s);
SetXRBFAlgorithmExact(s,algoidx,meanseparation);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
CAblasF::RAllocV(nx,scalevec);
for(i=0; i<nx ; i++)
scalevec.Set(i,MathPow(2,0.15*CHighQualityRand::HQRndNormal(rs)));
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
}
else
CRBF::RBFSetPoints(s,xy,n);
leadingzeros=0;
if(termtype==0)
{
if(CHighQualityRand::HQRndNormal(rs)>0.0)
CRBF::RBFSetLinTerm(s);
leadingzeros=0;
}
if(termtype==1)
{
CRBF::RBFSetConstTerm(s);
leadingzeros=nx;
}
if(termtype==2)
{
CRBF::RBFSetZeroTerm(s);
leadingzeros=nx+1;
}
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3250");
return(result);
}
if((double)(rep.m_maxerror)>errtol)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3255");
return(result);
}
for(i=0; i<n; i++)
{
CAblasF::RAllocV(nx,x);
CAblasF::RCopyRV(nx,xy,i,x);
CRBF::RBFCalc(s,x,y);
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,CAp::Len(y)!=ny || (double)(MathAbs(y[j]-xy.Get(i,nx+j)))>errtol,"testrbfunit.ap:3265");
}
CAp::SetErrorFlag(result,s.m_modelversion!=3,"testrbfunit.ap:3267");
CAp::SetErrorFlag(result,CAp::Rows(s.m_model3.m_v)<ny,"testrbfunit.ap:3268");
CAp::SetErrorFlag(result,CAp::Cols(s.m_model3.m_v)<nx+1,"testrbfunit.ap:3269");
if(result)
return(result);
for(i=0; i<ny; i++)
{
for(j=0; j<leadingzeros ; j++)
CAp::SetErrorFlag(result,s.m_model3.m_v.Get(i,j)!=0.0,"testrbfunit.ap:3275");
for(j=leadingzeros; j<=nx; j++)
CAp::SetErrorFlag(result,s.m_model3.m_v.Get(i,j)==0.0,"testrbfunit.ap:3277");
}
}
}
}
}
//--- Test RBFUnpack()
for(algoidx=0; algoidx<m_xrbfalgocnt ; algoidx++)
{
for(nx=1; nx<=4; nx++)
{
for(ny=1; ny<=2; ny++)
{
//--- problem setup
n=150;
gridsize=(int)MathRound(MathPow(n,(double)1/(double)nx))+1;
n=(int)MathRound(MathPow(gridsize,nx));
meanseparation=(double)1/(double)gridsize;
xy=matrix<double>::Zeros(n,nx+ny);
CAp::Assert(gridsize>1);
CAp::Assert((double)(n)==(double)(MathPow(gridsize,nx)));
for(i=0; i<n; i++)
{
k=i;
for(j=0; j<nx ; j++)
{
xy.Set(i,j,(double)(k%gridsize)/(double)(gridsize-1));
k/=gridsize;
}
for(j=0; j<ny; j++)
xy.Set(i,nx+j,CHighQualityRand::HQRndNormal(rs));
}
hasscale=CHighQualityRand::HQRndNormal(rs)>0.0;
if(hasscale)
{
scalevec=vector<double>::Zeros(nx);
for(j=0; j<nx ; j++)
scalevec.Set(j,MathPow(2,2*CHighQualityRand::HQRndUniformR(rs)-1));
}
else
CAblasF::RSetAllocV(nx,1.0,scalevec);
nondistinctcnt=CHighQualityRand::HQRndUniformI(rs,2)*CHighQualityRand::HQRndUniformI(rs,10);
if(nondistinctcnt>0)
{
CApServ::RMatrixGrowRowsTo(xy,n+nondistinctcnt,nx+ny);
for(i=n; i<=n+nondistinctcnt-1; i++)
CAblasF::RCopyRR(nx+ny,xy,CHighQualityRand::HQRndUniformI(rs,n),xy,i);
n+=nondistinctcnt;
}
//--- Build model: either build directly in S, or build in S2 and serialize/unserialize it to S.
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
//--- Build in S directly
CRBF::RBFCreate(nx,ny,s);
SetXRBFAlgorithmExact(s,algoidx,meanseparation);
if(hasscale)
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
else
CRBF::RBFSetPoints(s,xy,n);
CRBF::RBFBuildModel(s,rep);
}
else
{
//--- Build in S2, copy via serialization/unserialization.
//--- This branch tests our ability to correctly save the model state.
CRBF::RBFCreate(nx,ny,s2);
SetXRBFAlgorithmExact(s2,algoidx,meanseparation);
if(hasscale)
CRBF::RBFSetPointsAndScales(s2,xy,n,scalevec);
else
CRBF::RBFSetPoints(s2,xy,n);
CRBF::RBFBuildModel(s2,rep);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
_local_serializer.Alloc_Start();
CRBF::RBFAlloc(_local_serializer,s2);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CRBF::RBFSerialize(_local_serializer,s2);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//---
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CRBF::RBFUnserialize(_local_serializer,s);
_local_serializer.Stop();
}
}
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3358");
return(result);
}
//--- Test RBFUnpack()
CRBF::RBFUnpack(s,unx,uny,uxwr,unc,uv,modelversion);
if(modelversion!=3)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3368");
return(result);
}
if((unx!=nx || uny!=ny) || unc!=n-nondistinctcnt)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3373");
return(result);
}
if(CAp::Cols(uv)!=nx+1 || CAp::Rows(uv)!=ny)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3378");
return(result);
}
if(CAp::Cols(uxwr)!=nx+ny+nx+3 || CAp::Rows(uxwr)!=unc)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3383");
return(result);
}
CAblasF::BSetAllocV(n,false,bflags);
for(i=0; i<=unc-1; i++)
{
//--- Test center information stored in the model:
//--- * all center indexes are in [0,N)
//--- * all center indexes are distinct
//--- * compare centers with dataset points
CAp::SetErrorFlag(result,uxwr.Get(i,nx+ny+nx+2)<0.0,"testrbfunit.ap:3395");
CAp::SetErrorFlag(result,uxwr.Get(i,nx+ny+nx+2)>(n-1),"testrbfunit.ap:3396");
k=(int)MathRound(uxwr.Get(i,nx+ny+nx+2));
if(k>=0 && k<n)
{
CAp::SetErrorFlag(result,bflags[k],"testrbfunit.ap:3400");
bflags[k]=true;
for(j=0; j<nx ; j++)
CAp::SetErrorFlag(result,MathAbs(uxwr.Get(i,j)-xy.Get(k,j))>(1.0E-9),"testrbfunit.ap:3403");
}
}
x=vector<double>::Zeros(nx);
y=vector<double>::Zeros(ny);
for(i=0; i<=9; i++)
{
for(j=0; j<nx ; j++)
x.Set(j,CHighQualityRand::HQRndUniformR(rs));
CRBF::RBFCalc(s,x,yref);
mx=1.0;
for(j=0; j<ny; j++)
{
y.Set(j,uv.Get(j,nx));
for(k=0; k<nx ; k++)
y.Add(j,x[k]*uv.Get(j,k));
}
for(k=0; k<=unc-1; k++)
{
v=0;
for(j=0; j<nx ; j++)
v+=CMath::Sqr(uxwr.Get(k,j)-x[j])/CMath::Sqr(uxwr.Get(k,nx+ny+j));
if(algoidx==0)
{
//--- Thin plate spline
CAp::SetErrorFlag(result,uxwr.Get(k,nx+ny+nx)!=2.0,"testrbfunit.ap:3435");
CAp::SetErrorFlag(result,uxwr.Get(k,nx+ny+nx+1)!=0.0,"testrbfunit.ap:3436");
if(v!=0.0)
v*=MathLog(MathSqrt(v));
else
v=0;
}
if(algoidx==1)
{
//--- Multiquadric
CAp::SetErrorFlag(result,uxwr.Get(k,nx+ny+nx+0)!=10.0,"testrbfunit.ap:3447");
CAp::SetErrorFlag(result,uxwr.Get(k,nx+ny+nx+1)<=0.0,"testrbfunit.ap:3448");
v=MathSqrt(v+CMath::Sqr(uxwr.Get(k,nx+ny+nx+1)));
}
if(algoidx==2)
{
//--- Biharmonic
CAp::SetErrorFlag(result,uxwr.Get(k,nx+ny+nx)!=1.0,"testrbfunit.ap:3456");
CAp::SetErrorFlag(result,uxwr.Get(k,nx+ny+nx+1)!=0.0,"testrbfunit.ap:3457");
v=MathSqrt(v);
}
for(j=0; j<ny; j++)
{
y.Add(j,v*uxwr.Get(k,nx+j));
mx=MathMax(mx,MathAbs(uxwr.Get(k,nx+j)));
}
}
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,(double)(MathAbs(y[j]-yref[j]))>(double)(1.0E-9*mx),"testrbfunit.ap:3469");
}
}
}
}
//--- Test that smooth 1D function is reproduced (between nodes)
//--- with good precision. We test two model types: model with
//--- three layers and moderate initial radius, and model with
//--- large initial radius and large number of layers.
//--- This test:
//--- * generates test function on [-Width,+Width]. Two sets of
//--- nodes are generated-"model" ones and "test" ones.
//---*builds RBF model using "model" dataset
//---*test model using "test" dataset. Test points are more
//--- dense and are spread in [-0.9*Width, +0.9*Width] (reduced
//--- interval is used because RBF models are too bad near the
//--- boundaries).
//--- NOTE: we calculate maximum error for given function type
//--- and grid density over all modifications of the task,
//--- and only after that we perform comparison with tolerance
//--- level. It allows easier debugging.
for(functype=0; functype<=2; functype++)
{
for(densitytype=0; densitytype<=1; densitytype++)
{
//--- Select tolerance
lowprec=-999999;
highprec=-999999;
if(functype==0)
{
lowprec=1.0E-2;
highprec=1.0E-3;
}
else
{
if(functype==1)
{
lowprec=1.0E-1;
highprec=1.0E-2;
}
else
{
if(functype==2)
{
lowprec=1.0E-3;
highprec=1.0E-4;
}
else
{
CAp::Assert(false);
return(true);
}
}
}
if(densitytype==0)
errtol=lowprec;
else
errtol=highprec;
//--- Test
maxerr=0;
for(shaketype=0; shaketype<=1; shaketype++)
{
//--- Generate grid
width=1;
fractionalerror=false;
if(functype==0)
{
//--- sin(x) on [-2*pi,+2*pi]
n=20*(int)MathRound(MathPow(4,densitytype));
width=M_PI;
fractionalerror=false;
}
else
{
if(functype==1)
{
//--- exp(x) on [-3,+3]
n=50*(int)MathRound(MathPow(4,densitytype));
width=3;
fractionalerror=true;
}
else
{
if(functype==2)
{
//--- 1/(1+x^2) on [-3,+3]
n=50*(int)MathRound(MathPow(4,densitytype));
width=3;
fractionalerror=false;
}
else
{
CAp::Assert(false);
return(true);
}
}
}
xy=matrix<double>::Zeros(n,2);
for(i=0; i<n; i++)
{
v=shaketype*0.25*(CMath::RandomReal()-0.5);
v=(i+v)/(n-1);
v=2*v-1;
xy.Set(i,0,width*v);
}
ntest=n*10;
xytest=matrix<double>::Zeros(ntest,2);
for(i=0; i<ntest ; i++)
xytest.Set(i,0,0.9*width*((double)(2*i)/(double)(ntest-1)-1));
//--- Evaluate function
if(functype==0)
{
//--- sin(x)
for(i=0; i<n; i++)
xy.Set(i,1,MathSin(xy.Get(i,0)));
for(i=0; i<ntest ; i++)
xytest.Set(i,1,MathSin(xytest.Get(i,0)));
}
else
{
if(functype==1)
{
//--- exp(x)
for(i=0; i<n; i++)
xy.Set(i,1,MathExp(xy.Get(i,0)));
for(i=0; i<ntest ; i++)
xytest.Set(i,1,MathExp(xytest.Get(i,0)));
}
else
{
if(functype==2)
{
//--- 1/(1+x^2)
for(i=0; i<n; i++)
xy.Set(i,1,1.0/(1.0+CMath::Sqr(xy.Get(i,0))));
for(i=0; i<ntest ; i++)
xytest.Set(i,1,1.0/(1+CMath::Sqr(xytest.Get(i,0))));
}
else
{
CAp::Assert(false);
return(true);
}
}
}
//--- Build model
CRBF::RBFCreate(1,1,s);
CRBF::RBFSetAlgoThinPlateSpline(s,0.0);
CRBF::RBFSetPoints(s,xy,n);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3624");
return(result);
}
//--- Check
x=vector<double>::Zeros(1);
for(i=0; i<ntest ; i++)
{
x.Set(0,xytest.Get(i,0));
CRBF::RBFCalc(s,x,y);
if(fractionalerror)
maxerr=MathMax(maxerr,MathAbs(y[0]-xytest.Get(i,1))/MathAbs(xytest.Get(i,1)));
else
maxerr=MathMax(maxerr,MathAbs(y[0]-xytest.Get(i,1)));
}
}
//--- Check error
CAp::SetErrorFlag(result,maxerr>errtol,"testrbfunit.ap:3646");
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing scaling - related functionality of XRBF |
//| Returns True on failure(error flag is set). |
//+------------------------------------------------------------------+
bool CTestRBFUnit::ScaledXRBFTest(void)
{
//--- create variables
bool result=false;
CRBFModel s;
CRBFModel s2;
CRBFReport rep;
CRBFCalcBuffer tsbuf;
int nx=0;
int ny=0;
double nodetol=0;
double randtol=0;
CMatrixDouble xy;
CMatrixDouble xy2;
CRowDouble x;
CRowDouble y;
CRowDouble y2;
CRowDouble xzero;
CRowDouble yref;
CRowDouble scalex;
CRowDouble scaley;
CRowDouble c0;
CRowDouble c1;
int n=0;
int gridsize=0;
int i=0;
int j=0;
int k=0;
CHighQualityRandState rs;
CHighQualityRand::HQRndRandomize(rs);
//--- First test - random problem, test that using scaling
//--- does not change model significantly (except for
//--- rounding-related errors).
//--- We also apply scaling to Y, in order to test that it
//--- is correctly handled too.
for(nx=1; nx<=2; nx++)
{
for(ny=1; ny<=2; ny++)
{
//--- problem setup
nodetol=0.001;
randtol=0.010;
scalex=vector<double>::Zeros(nx);
for(i=0; i<nx ; i++)
scalex.Set(i,MathPow(4,CHighQualityRand::HQRndNormal(rs)));
scaley=vector<double>::Zeros(ny);
for(i=0; i<ny; i++)
scaley.Set(i,MathPow(4,CHighQualityRand::HQRndNormal(rs)));
n=150;
gridsize=(int)MathRound(MathPow(n,(double)1/(double)nx))+1;
n=(int)MathRound(MathPow(gridsize,nx));
xy=matrix<double>::Zeros(n,nx+ny);
if(!CAp::Assert(gridsize>1))
return(true);
if(!CAp::Assert(n==MathPow(gridsize,nx)))
return(true);
c0=vector<double>::Zeros(nx);
for(j=0; j<nx ; j++)
c0.Set(j,CMath::RandomReal()-0.5);
c1=vector<double>::Zeros(ny);
for(j=0; j<ny; j++)
c1.Set(j,CMath::RandomReal()-0.5);
for(i=0; i<n; i++)
{
k=i;
for(j=0; j<nx ; j++)
{
xy.Set(i,j,(double)(k%gridsize)/(double)(gridsize-1));
k/=gridsize;
}
for(j=0; j<ny; j++)
{
xy.Set(i,nx+j,0);
for(k=0; k<nx ; k++)
xy.Add(i,nx+j,c0[k]*MathCos(M_PI*(1+k)*xy.Get(i,k)));
xy.Mul(i,nx+j,c1[j]);
}
}
xy2=matrix<double>::Zeros(n,nx+ny);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
xy2.Set(i,j,xy.Get(i,j)* scalex[j]);
for(j=0; j<ny; j++)
xy2.Set(i,nx+j,xy.Get(i,nx+j)* scaley[j]);
}
//--- Build models
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetAlgoThinPlateSpline(s,0.0);
CRBF::RBFSetPoints(s,xy,n);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3751");
return(result);
}
CRBF::RBFCreate(nx,ny,s2);
CRBF::RBFSetAlgoThinPlateSpline(s2,0.0);
CRBF::RBFSetPointsAndScales(s2,xy2,n,scalex);
CRBF::RBFBuildModel(s2,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3760");
return(result);
}
//--- Compare model values in grid points
x=vector<double>::Zeros(nx);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
x.Set(j,xy.Get(i,j));
CRBF::RBFCalc(s,x,y);
for(j=0; j<nx ; j++)
x.Set(j,xy2.Get(i,j));
CRBF::RBFCalc(s2,x,y2);
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,MathAbs(y[j]-y2[j]/scaley[j])>nodetol,"testrbfunit.ap:3777");
}
//--- Compare model values in random points
x=vector<double>::Zeros(nx);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
x.Set(j,CMath::RandomReal());
CRBF::RBFCalc(s,x,y);
for(j=0; j<nx ; j++)
x.Mul(j,scalex[j]);
CRBF::RBFCalc(s2,x,y2);
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,MathAbs(y[j]-y2[j]/scaley[j])>randtol,"testrbfunit.ap:3793");
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Test gridded evaluation of XRBFs. |
//| Returns True on errors. |
//+------------------------------------------------------------------+
bool CTestRBFUnit::GridXRBFTest(void)
{
//--- create variables
bool result=false;
CRBFModel s;
CRBFReport rep;
int nx=0;
int ny=0;
bool hasscale;
double errtol=0;
int n=0;
CMatrixDouble xy;
CMatrixDouble xy2;
CMatrixDouble y2;
CRowDouble x0;
CRowDouble x1;
CRowDouble x2;
CRowDouble x02;
CRowDouble x12;
CRowDouble x22;
CRowDouble scalevec;
CRowDouble scalevec2;
bool needy[];
bool rowflags[];
int n0=0;
int n1=0;
int n2=0;
int nkind=0;
double v=0;
int i=0;
int j=0;
int k=0;
int i0=0;
int i1=0;
int i2=0;
CRowDouble x;
CRowDouble y;
CRowDouble yv;
CRowDouble yv2;
//--- Test 2-dimensional grid calculation
errtol=1.0E-12;
nx=2;
for(ny=1; ny<=4; ny++)
{
for(nkind=0; nkind<=2; nkind++)
{
//--- problem setup
n=150;
xy=matrix<double>::Zeros(n,nx+ny);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
xy.Set(i,j,CMath::RandomReal());
for(j=0; j<ny; j++)
xy.Set(i,nx+j,CMath::RandomReal()-0.5);
}
hasscale=CMath::RandomInteger(2)==0;
if(hasscale)
{
scalevec=vector<double>::Zeros(nx);
for(j=0; j<nx ; j++)
scalevec.Set(j,MathPow(2,2*CMath::RandomReal()-1));
}
//--- Build model
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetAlgoThinPlateSpline(s,0.0);
if(hasscale)
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
else
CRBF::RBFSetPoints(s,xy,n);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3869");
return(result);
}
//--- Prepare grid to test
n0=1+CMath::RandomInteger(50);
n1=1+CMath::RandomInteger(50);
if(nkind==1)
{
k=CMath::RandomInteger(2);
if(k==0)
n0=1;
if(k==1)
n1=1;
}
else
{
if(nkind==2)
{
n0=1;
n1=1;
}
else
{
if(!CAp::Assert(nkind==0))
return(true);
}
}
x0=vector<double>::Zeros(n0);
x0.Set(0,CMath::RandomReal());
for(i=1; i<n0 ; i++)
x0.Set(i,x0[i-1]+CMath::RandomReal()/n0);
x1=vector<double>::Zeros(n1);
x1.Set(0,CMath::RandomReal());
for(i=1; i<n1; i++)
x1.Set(i,x1[i-1]+CMath::RandomReal()/n1);
ArrayResize(needy,n0*n1);
v=MathPow(10,-(3*CMath::RandomReal()));
for(i=0; i<n0*n1 ; i++)
needy[i]=CMath::RandomReal()<v;
//--- Test at grid
x=vector<double>::Zeros(nx);
CRBF::RBFGridCalc2V(s,x0,n0,x1,n1,yv);
for(i0=0; i0<n0 ; i0++)
{
for(i1=0; i1<n1; i1++)
{
x.Set(0,x0[i0]);
x.Set(1,x1[i1]);
CRBF::RBFCalc(s,x,y);
for(i=0; i<ny; i++)
CAp::SetErrorFlag(result,(double)(MathAbs(y[i]-yv[i+ny*(i0+i1*n0)]))>errtol,"testrbfunit.ap:3918");
}
}
//--- Test calculation on subset of regular grid:
//--- * Test 1: compare full and subset versions
//--- * Test 2: check sparsity. Subset function may perform additional
//--- evaluations because it processes data micro-row by micro-row.
//--- So, we can't check that all elements which were not flagged
//--- are zero - some of them may become non-zero. However, if entire
//--- row is empty, we can reasonably expect (informal guarantee)
//--- that it is not processed. So, we check empty (completely
//--- unflagged) rows
CRBF::RBFGridCalc2VSubset(s,x0,n0,x1,n1,needy,yv2);
for(i=0; i<=ny*n0*n1-1; i++)
CAp::SetErrorFlag(result,needy[i/ny] && MathAbs(yv[i]-yv2[i])>errtol,"testrbfunit.ap:3937");
//--- Test legacy function
CRBF::RBFGridCalc2(s,x0,n0,x1,n1,y2);
for(i=0; i<n0*n1 ; i++)
{
if(ny==1)
CAp::SetErrorFlag(result,MathAbs(yv[i]-y2.Get(i%n0,i/n0))>errtol,"testrbfunit.ap:3947");
else
CAp::SetErrorFlag(result,y2.Get(i%n0,i/n0)!=0.0,"testrbfunit.ap:3949");
}
}
}
//--- Test 3-dimensional grid calculation
errtol=1.0E-12;
nx=3;
for(ny=1; ny<=4; ny++)
{
for(nkind=0; nkind<=2; nkind++)
{
//--- problem setup
n=150;
xy=matrix<double>::Zeros(n,nx+ny);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
xy.Set(i,j,CMath::RandomReal());
for(j=0; j<ny; j++)
xy.Set(i,nx+j,CMath::RandomReal()-0.5);
}
hasscale=CMath::RandomInteger(2)==0;
if(hasscale)
{
scalevec=vector<double>::Zeros(nx);
for(j=0; j<nx ; j++)
scalevec.Set(j,MathPow(2,2*CMath::RandomReal()-1));
}
//--- Build model
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetAlgoThinPlateSpline(s,0.0);
if(hasscale)
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
else
CRBF::RBFSetPoints(s,xy,n);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:3993");
return(result);
}
//--- Prepare grid to test
n0=1+CMath::RandomInteger(50);
n1=1+CMath::RandomInteger(50);
n2=1+CMath::RandomInteger(50);
if(nkind==1)
{
k=CMath::RandomInteger(3);
if(k==0)
n0=1;
if(k==1)
n1=1;
if(k==2)
n2=1;
}
else
{
if(nkind==2)
{
n0=1;
n1=1;
n2=1;
}
else
{
if(!CAp::Assert(nkind==0))
return(true);
}
}
x0=vector<double>::Zeros(n0);
x0.Set(0,CMath::RandomReal());
for(i=1; i<n0 ; i++)
x0.Set(i,x0[i-1]+CMath::RandomReal()/n0);
x1=vector<double>::Zeros(n1);
x1.Set(0,CMath::RandomReal());
for(i=1; i<n1; i++)
x1.Set(i,x1[i-1]+CMath::RandomReal()/n1);
x2=vector<double>::Zeros(n2);
x2.Set(0,CMath::RandomReal());
for(i=1; i<n2 ; i++)
x2.Set(i,x2[i-1]+CMath::RandomReal()/n2);
ArrayResize(needy,n0*n1*n2);
v=MathPow(10,-(3*CMath::RandomReal()));
for(i=0; i<n0*n1*n2; i++)
needy[i]=CMath::RandomReal()<v;
//--- Test at grid
x=vector<double>::Zeros(nx);
CRBF::RBFGridCalc3V(s,x0,n0,x1,n1,x2,n2,yv);
for(i0=0; i0<n0 ; i0++)
{
for(i1=0; i1<n1; i1++)
for(i2=0; i2<n2 ; i2++)
{
x.Set(0,x0[i0]);
x.Set(1,x1[i1]);
x.Set(2,x2[i2]);
CRBF::RBFCalc(s,x,y);
for(i=0; i<ny; i++)
CAp::SetErrorFlag(result,(double)(MathAbs(y[i]-yv[i+ny*(i0+i1*n0+i2*n0*n1)]))>errtol,"testrbfunit.ap:4052");
}
}
//--- Test calculation on subset of regular grid:
//--- * Test 1: compare full and subset versions
//--- * Test 2: check sparsity. Subset function may perform additional
//--- evaluations because it processes data micro-row by micro-row.
//--- So, we can't check that all elements which were not flagged
//--- are zero - some of them may become non-zero. However, if entire
//--- row is empty, we can reasonably expect (informal guarantee)
//--- that it is not processed. So, we check empty (completely
//--- unflagged) rows
ArrayResize(rowflags,n1*n2);
for(i=0; i<=n1*n2-1; i++)
rowflags[i]=false;
for(i=0; i<=n0*n1*n2-1; i++)
{
if(needy[i])
rowflags[i/n0]=true;
}
CRBF::RBFGridCalc3VSubset(s,x0,n0,x1,n1,x2,n2,needy,yv2);
for(i=0; i<ny*n0*n1*n2; i++)
{
CAp::SetErrorFlag(result,needy[i/ny] && MathAbs(yv[i]-yv2[i])>errtol,"testrbfunit.ap:4077");
CAp::SetErrorFlag(result,!rowflags[i/(ny*n0)] && yv2[i]!=0.0,"testrbfunit.ap:4078");
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing basic functionality of RBF module with |
//| hierarchical algorithm. |
//+------------------------------------------------------------------+
bool CTestRBFUnit::BasicHRBFTest(void)
{
//--- create variables
bool result=false;
CRBFModel s;
CRBFModel s2;
CRBFReport rep;
CRBFCalcBuffer tsbuf;
int nx=0;
int ny=0;
int linterm=0;
int bf=0;
double rbase=0;
int nlayers=0;
double errtol=0;
double scalefactor=0;
CMatrixDouble xy;
CMatrixDouble xytest;
CMatrixDouble uxwr;
CMatrixDouble uv;
CMatrixDouble xy2;
CRowDouble x;
CRowDouble y;
CRowDouble y2;
CRowDouble xzero;
CRowDouble yref;
CRowDouble scalevec;
int n=0;
int ntest=0;
int gridsize=0;
int i=0;
int j=0;
int k=0;
double r0=0;
int margin=0;
double threshold=0;
double v=0;
int functype=0;
int densitytype=0;
double width=0;
double lowprec=0;
double highprec=0;
int modeltype=0;
int shaketype=0;
double maxerr=0;
bool fractionalerror;
int unx=0;
int uny=0;
int unc=0;
int modelversion=0;
bool hasscale;
CHighQualityRandState rs;
CHighQualityRand::HQRndRandomize(rs);
//--- First test - random problem, ability to build model
//--- which reproduces function value in all points with
//--- good precision.
//--- We also test properties of the linear term - that
//--- model value in far away points is either constant
//--- or exactly zero (for corresponding kinds of linear
//--- term).
//--- All dataset points are located in unit cube on
//--- regular grid. We do not use smoothing for this test.
//--- We use/test following functions:
//--- * RBFCalc()
//--- * RBFCalc2()
//--- * RBFCalc3()
//--- * RBFCalcBuf()
//--- * RBFTsCalcBuf()
errtol=1.0E-6;
for(nx=1; nx<=4; nx++)
{
//--- problem setup
n=150;
rbase=3.0;
nlayers=10;
gridsize=(int)MathRound(MathPow(n,(double)1/(double)nx))+1;
linterm=1+CMath::RandomInteger(3);
ny=1+CMath::RandomInteger(2);
bf=CMath::RandomInteger(2);
n=(int)MathRound(MathPow(gridsize,nx));
xy=matrix<double>::Zeros(n,nx+ny);
if(!CAp::Assert(gridsize>1))
return(true);
if(!CAp::Assert(n==MathPow(gridsize,nx)))
return(true);
for(i=0; i<n; i++)
{
k=i;
for(j=0; j<nx ; j++)
{
xy.Set(i,j,k%gridsize);
k/=gridsize;
}
for(j=0; j<ny; j++)
xy.Set(i,nx+j,CMath::RandomReal()-0.5);
}
//--- Build model
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetV2BF(s,bf);
CRBF::RBFSetAlgoHierarchical(s,rbase,nlayers,0.0);
if(linterm==1)
CRBF::RBFSetLinTerm(s);
if(linterm==2)
CRBF::RBFSetConstTerm(s);
if(linterm==3)
CRBF::RBFSetZeroTerm(s);
CRBF::RBFSetPoints(s,xy,n);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4197");
return(result);
}
CRBF::RBFCreateCalcBuffer(s,tsbuf);
//--- Test ability to reproduce function value
//--- NOTE: we use RBFCalc(XZero) to guarantee that internal state of
//--- RBF model is "reset" between subsequent calls of different
//--- functions. It allows us to make sure that we have no bug
//--- like function simply returning latest result
x=vector<double>::Zeros(nx);
xzero=vector<double>::Zeros(nx);
y=vector<double>::Zeros(ny);
for(j=0; j<nx ; j++)
xzero.Set(j,0);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
x.Set(j,xy.Get(i,j));
CRBF::RBFCalc(s,xzero,y);
if(CMath::RandomReal()>0.5)
yref=vector<double>::Zeros(ny+1);
CRBF::RBFCalc(s,x,yref);
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,MathAbs(yref[j]-xy.Get(i,nx+j))>errtol,"testrbfunit.ap:4226");
CAp::SetErrorFlag(result,CAp::Len(yref)!=ny,"testrbfunit.ap:4227");
if(nx==1 && ny==1)
{
CRBF::RBFCalc(s,xzero,y);
CAp::SetErrorFlag(result,CRBF::RBFCalc1(s,x[0])!=yref[0],"testrbfunit.ap:4233");
}
if(nx==2 && ny==1)
{
CRBF::RBFCalc(s,xzero,y);
CAp::SetErrorFlag(result,CRBF::RBFCalc2(s,x[0],x[1])!=yref[0],"testrbfunit.ap:4240");
}
if(nx==3 && ny==1)
{
CRBF::RBFCalc(s,xzero,y);
CAp::SetErrorFlag(result,CRBF::RBFCalc3(s,x[0],x[1],x[2])!=yref[0],"testrbfunit.ap:4247");
}
CRBF::RBFCalc(s,xzero,y);
if(CMath::RandomReal()>0.5)
{
y=vector<double>::Zeros(ny+1);
CRBF::RBFCalcBuf(s,x,y);
CAp::SetErrorFlag(result,CAp::Len(y)!=ny+1,"testrbfunit.ap:4256");
}
else
{
y=vector<double>::Zeros(ny-1);
CRBF::RBFCalcBuf(s,x,y);
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:4262");
}
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,y[j]!=yref[j],"testrbfunit.ap:4265");
CRBF::RBFCalc(s,xzero,y);
if(CMath::RandomReal()>0.5)
{
y=vector<double>::Zeros(ny+1);
CRBF::RBFTSCalcBuf(s,tsbuf,x,y);
CAp::SetErrorFlag(result,CAp::Len(y)!=ny+1,"testrbfunit.ap:4273");
}
else
{
y=vector<double>::Zeros(ny-1);
CRBF::RBFTSCalcBuf(s,tsbuf,x,y);
CAp::SetErrorFlag(result,CAp::Len(y)!=ny,"testrbfunit.ap:4279");
}
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,y[j]!=yref[j],"testrbfunit.ap:4282");
}
//--- Test that:
//--- a) model with zero linear term is zero far away from dataset
//--- b) model with constant linear term is constant far away from dataset
x=vector<double>::Zeros(nx);
if(linterm==2)
{
for(j=0; j<nx ; j++)
{
if(CMath::RandomReal()>0.5)
x.Set(j,gridsize+1000*rbase);
else
x.Set(j,0-1000*rbase);
}
CRBF::RBFCalc(s,x,y);
for(j=0; j<nx ; j++)
{
if(CMath::RandomReal()>0.5)
x.Set(j,gridsize+1000*rbase);
else
x.Set(j,0-1000*rbase);
}
CRBF::RBFCalc(s,x,y2);
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,y[j]!=(double)(y2[j]),"testrbfunit.ap:4306");
}
if(linterm==3)
{
for(j=0; j<nx ; j++)
{
if(CMath::RandomReal()>0.5)
x.Set(j,gridsize+1000*rbase);
else
x.Set(j,0-1000*rbase);
}
CRBF::RBFCalc(s,x,y);
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,y[j]!=0.0,"testrbfunit.ap:4317");
}
}
//--- Test RBFUnpack()
for(nx=1; nx<=2; nx++)
{
for(ny=1; ny<=2; ny++)
{
//--- problem setup
n=150;
rbase=0.33;
nlayers=5;
gridsize=(int)MathRound(MathPow(n,(double)1/(double)nx))+1;
linterm=1+CMath::RandomInteger(3);
bf=CMath::RandomInteger(2);
n=(int)MathRound(MathPow(gridsize,nx));
xy=matrix<double>::Zeros(n,nx+ny);
if(!CAp::Assert(gridsize>1))
return(true);
if(!CAp::Assert(n==MathPow(gridsize,nx)))
return(true);
for(i=0; i<n; i++)
{
k=i;
for(j=0; j<nx ; j++)
{
xy.Set(i,j,(double)(k%gridsize)/(double)(gridsize-1));
k/=gridsize;
}
for(j=0; j<ny; j++)
xy.Set(i,nx+j,CMath::RandomReal()-0.5);
}
hasscale=CMath::RandomInteger(2)==0;
scalevec=vector<double>::Zeros(nx);
if(hasscale)
{
for(j=0; j<nx ; j++)
scalevec.Set(j,MathPow(2,2*CMath::RandomReal()-1));
}
//--- Build model
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetV2BF(s,bf);
CRBF::RBFSetAlgoHierarchical(s,rbase,nlayers,0.0);
if(linterm==1)
CRBF::RBFSetLinTerm(s);
if(linterm==2)
CRBF::RBFSetConstTerm(s);
if(linterm==3)
CRBF::RBFSetZeroTerm(s);
if(hasscale)
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
else
CRBF::RBFSetPoints(s,xy,n);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4378");
return(result);
}
//--- Test RBFUnpack()
CRBF::RBFUnpack(s,unx,uny,uxwr,unc,uv,modelversion);
if(modelversion!=2)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4388");
return(result);
}
if(unx!=nx || uny!=ny)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4393");
return(result);
}
if(CAp::Cols(uv)!=nx+1 || CAp::Rows(uv)!=ny)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4398");
return(result);
}
if(linterm==2)
{
for(i=0; i<ny; i++)
for(j=0; j<nx ; j++)
CAp::SetErrorFlag(result,(double)(uv.Get(i,j))!=0.0,"testrbfunit.ap:4405");
}
if(linterm==3)
{
for(i=0; i<ny; i++)
for(j=0; j<=nx; j++)
CAp::SetErrorFlag(result,(double)(uv.Get(i,j))!=0.0,"testrbfunit.ap:4411");
}
x=vector<double>::Zeros(nx);
y=vector<double>::Zeros(ny);
for(i=0; i<=9; i++)
{
for(j=0; j<nx ; j++)
x.Set(j,CMath::RandomReal());
CRBF::RBFCalc(s,x,yref);
for(j=0; j<ny; j++)
{
y.Set(j,uv.Get(j,nx));
for(k=0; k<nx ; k++)
y.Add(j,x[k]*uv.Get(j,k));
}
for(k=0; k<=unc-1; k++)
{
v=0;
for(j=0; j<nx ; j++)
v+=CMath::Sqr(uxwr.Get(k,j)-x[j])/CMath::Sqr(uxwr.Get(k,nx+ny+j));
if(v<(double)(CRBFV2::RBFV2FarRadius(bf)*CRBFV2::RBFV2FarRadius(bf)))
v=CRBFV2::RBFV2BasisFunc(bf,v);
else
v=0;
for(j=0; j<ny; j++)
y.Add(j,v*uxwr.Get(k,nx+j));
}
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,MathAbs(y[j]-yref[j])>(1.0E-9),"testrbfunit.ap:4446");
}
}
}
//--- Test that smooth 1D function is reproduced (between nodes)
//--- with good precision. We test two model types: model with
//--- three layers and moderate initial radius, and model with
//--- large initial radius and large number of layers.
//--- This test:
//--- * generates test function on [-Width,+Width]. Two sets of
//--- nodes are generated-"model" ones and "test" ones.
//---*builds RBF model using "model" dataset
//---*test model using "test" dataset. Test points are more
//--- dense and are spread in [-0.9*Width, +0.9*Width] (reduced
//--- interval is used because RBF models are too bad near the
//--- boundaries).
//--- NOTE: we calculate maximum error for given function type
//--- and grid density over all modifications of the task,
//--- and only after that we perform comparison with tolerance
//--- level. It allows easier debugging.
for(functype=0; functype<=2; functype++)
{
for(densitytype=0; densitytype<=1; densitytype++)
{
//--- Select tolerance
lowprec=-999999;
highprec=-999999;
switch(functype)
{
case 0:
lowprec=1.0E-2;
highprec=1.0E-3;
break;
case 1:
lowprec=1.0E-1;
highprec=1.0E-2;
break;
case 2:
lowprec=1.0E-3;
highprec=1.0E-4;
break;
default:
CAp::Assert(false);
return(true);
}
if(densitytype==0)
errtol=lowprec;
else
errtol=highprec;
//--- Test
maxerr=0;
for(modeltype=0; modeltype<=1; modeltype++)
{
for(shaketype=0; shaketype<=1; shaketype++)
{
//--- Generate grid
width=1;
fractionalerror=false;
switch(functype)
{
case 0:
//--- sin(x) on [-2*pi,+2*pi]
n=17*(int)MathRound(MathPow(4,densitytype));
width=M_PI;
fractionalerror=false;
break;
case 1:
//--- exp(x) on [-3,+3]
n=50*(int)MathRound(MathPow(4,densitytype));
width=3;
fractionalerror=true;
break;
case 2:
//--- 1/(1+x^2) on [-3,+3]
n=20*(int)MathRound(MathPow(4,densitytype));
width=3;
fractionalerror=false;
break;
default:
CAp::Assert(false);
return(true);
}
xy=matrix<double>::Zeros(n,2);
for(i=0; i<n; i++)
{
v=shaketype*0.25*(CMath::RandomReal()-0.5);
v=(i+v)/(n-1);
v=2*v-1;
xy.Set(i,0,width*v);
}
ntest=n*10;
xytest=matrix<double>::Zeros(ntest,2);
for(i=0; i<ntest ; i++)
xytest.Set(i,0,0.9*width*((double)(2*i)/(double)(ntest-1)-1));
//--- Evaluate function
switch(functype)
{
case 0:
//--- sin(x)
for(i=0; i<n; i++)
xy.Set(i,1,MathSin(xy.Get(i,0)));
for(i=0; i<ntest ; i++)
xytest.Set(i,1,MathSin(xytest.Get(i,0)));
break;
case 1:
//--- exp(x)
for(i=0; i<n; i++)
xy.Set(i,1,MathExp(xy.Get(i,0)));
for(i=0; i<ntest ; i++)
xytest.Set(i,1,MathExp(xytest.Get(i,0)));
break;
case 2:
//--- 1/(1+x^2)
for(i=0; i<n; i++)
xy.Set(i,1,1/(1+CMath::Sqr(xy.Get(i,0))));
for(i=0; i<ntest ; i++)
xytest.Set(i,1,1/(1+CMath::Sqr(xytest.Get(i,0))));
break;
default:
CAp::Assert(false);
return(true);
}
//--- Select model properties and precision
switch(modeltype)
{
case 0:
rbase=4.0*(2*width/n);
nlayers=3;
break;
case 1:
rbase=16.0*(2*width/n);
nlayers=6;
break;
default:
CAp::Assert(false);
return(true);
}
//--- Build model
bf=CMath::RandomInteger(2);
CRBF::RBFCreate(1,1,s);
CRBF::RBFSetV2BF(s,bf);
CRBF::RBFSetAlgoHierarchical(s,rbase,nlayers,0.0);
CRBF::RBFSetPoints(s,xy,n);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4620");
return(result);
}
//--- Check
x=vector<double>::Zeros(1);
for(i=0; i<ntest ; i++)
{
x.Set(0,xytest.Get(i,0));
CRBF::RBFCalc(s,x,y);
if(fractionalerror)
maxerr=MathMax(maxerr,MathAbs(y[0]-xytest.Get(i,1))/MathAbs(xytest.Get(i,1)));
else
maxerr=MathMax(maxerr,MathAbs(y[0]-xytest.Get(i,1)));
}
}
}
//--- Check error
CAp::SetErrorFlag(result,maxerr>errtol,"testrbfunit.ap:4642");
}
}
//--- Scaling test - random problem, we test that after
//--- scaling of all variables and radius by 2^K (for some K)
//--- we will get exactly same results (up to the last bit of
//--- mantissa).
//--- It is very strong requirement for algorithm stability,
//--- but it is satisfiable in most implementations of RBFs,
//--- because all operations involving spatial values are usually
//--- followed by division by radius, and using multiplier which
//--- is exactly power of 2 results in no changes in numbers
//--- being returned.
//--- It allows to test different scale-related bugs
//--- (say, situation when deep in kd-tree search code we compare
//--- against R instead of R^2).
//--- All dataset points are located in unit cube on
//--- regular grid.
//--- We do not use smoothing for this test.
for(nx=1; nx<=4; nx++)
{
//--- problem setup
n=150;
rbase=0.33;
nlayers=CMath::RandomInteger(4);
scalefactor=MathPow(1024,2*CMath::RandomInteger(2)-1);
gridsize=(int)MathRound(MathPow(n,(double)1/(double)nx))+1;
ny=1+CMath::RandomInteger(3);
linterm=1+CMath::RandomInteger(3);
bf=CMath::RandomInteger(2);
n=(int)MathRound(MathPow(gridsize,nx));
xy=matrix<double>::Zeros(n,nx+ny);
xy2=matrix<double>::Zeros(n,nx+ny);
if(!CAp::Assert(gridsize>1))
return(true);
if(!CAp::Assert((double)(n)==(double)(MathPow(gridsize,nx))))
return(true);
for(i=0; i<n; i++)
{
k=i;
for(j=0; j<nx ; j++)
{
xy.Set(i,j,(double)(k%gridsize)/(double)(gridsize-1));
xy2.Set(i,j,xy.Get(i,j)*scalefactor);
k/=gridsize;
}
for(j=0; j<ny; j++)
{
xy.Set(i,nx+j,CMath::RandomReal()-0.5);
xy2.Set(i,nx+j,xy.Get(i,nx+j));
}
}
//--- Build model 1
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetV2BF(s,bf);
CRBF::RBFSetAlgoHierarchical(s,rbase,nlayers,0.0);
if(linterm==1)
CRBF::RBFSetLinTerm(s);
if(linterm==2)
CRBF::RBFSetConstTerm(s);
if(linterm==3)
CRBF::RBFSetZeroTerm(s);
CRBF::RBFSetPoints(s,xy,n);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4718");
return(result);
}
//--- Build model 2
CRBF::RBFCreate(nx,ny,s2);
CRBF::RBFSetV2BF(s2,bf);
CRBF::RBFSetAlgoHierarchical(s2,rbase*scalefactor,nlayers,0.0);
if(linterm==1)
CRBF::RBFSetLinTerm(s2);
if(linterm==2)
CRBF::RBFSetConstTerm(s2);
if(linterm==3)
CRBF::RBFSetZeroTerm(s2);
CRBF::RBFSetPoints(s2,xy2,n);
CRBF::RBFBuildModel(s2,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4738");
return(result);
}
//--- Compare models
x=vector<double>::Zeros(nx);
y=vector<double>::Zeros(ny);
y2=vector<double>::Zeros(ny);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
x.Set(j,xy.Get(i,j));
CRBF::RBFCalc(s,x,y);
for(j=0; j<nx ; j++)
x.Set(j,xy2.Get(i,j));
CRBF::RBFCalc(s2,x,y2);
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,y[j]!=y2[j],"testrbfunit.ap:4757");
}
}
//--- Test smoothing properties of RBF model: model with just one layer
//--- and large initial radius will produce "average" value of neighbors.
//--- In order to test it we create regular grid, fill it with regular
//--- +1/-1 pattern, and test model values in the inner points. Model
//--- values should be around zero (we use handcrafted threshold to test
//--- it). Radius is chosen to be several times larger than grid step.
//--- We perform test for 2D model, because same behavior is expected
//--- regardless of dimensionality.
r0=6;
margin=5;
threshold=0.05;
gridsize=2*margin+5;
nx=2;
ny=1;
for(bf=0; bf<=1; bf++)
{
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetV2BF(s,bf);
CRBF::RBFSetAlgoHierarchical(s,r0,1,1.0E-1);
CRBF::RBFSetZeroTerm(s);
xy=matrix<double>::Zeros(gridsize*gridsize,nx+ny);
for(i=0; i<gridsize; i++)
for(j=0; j<gridsize; j++)
{
xy.Set(i*gridsize+j,0,i);
xy.Set(i*gridsize+j,1,j);
xy.Set(i*gridsize+j,2,0.01*(CMath::RandomReal()-0.5)+(2*((i+j)%2)-1));
}
CRBF::RBFSetPoints(s,xy,gridsize*gridsize);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4798");
return(result);
}
v=0.0;
for(i=margin; i<gridsize-margin ; i++)
{
for(j=margin; j<gridsize-margin ; j++)
v=MathMax(v,MathAbs(CRBF::RBFCalc2(s,i,j)));
}
CAp::SetErrorFlag(result,v>threshold,"testrbfunit.ap:4805");
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing scaling - related functionality of RBF |
//| module with hierarchical algorithm. |
//| Returns True on failure(error flag is set). |
//+------------------------------------------------------------------+
bool CTestRBFUnit::ScaleHRBFTtest(void)
{
//--- create variables
bool result=false;
CRBFModel s;
CRBFModel s2;
CRBFReport rep;
CRBFCalcBuffer tsbuf;
int nx=0;
int ny=0;
int linterm=0;
int bf=0;
double rbase=0;
int nlayers=0;
double errtol=0;
CMatrixDouble xy;
CMatrixDouble xy2;
CRowDouble x;
CRowDouble y;
CRowDouble y2;
CRowDouble xzero;
CRowDouble yref;
CRowDouble scalex;
CRowDouble scaley;
CRowDouble c0;
CRowDouble c1;
int n=0;
int gridsize=0;
int i=0;
int j=0;
int k=0;
int strictness=0;
double lambdav=0;
//--- First test - random problem, test that using scaling
//--- does not change model significantly (except for
//--- rounding-related errors).
//--- We test two kinds of scaling:
//---*"strict", which is scaling by some power of 2, and
//--- which should result in bit-to-bit equivalence of results
//---*"non-strict", which is scaling by random number, and
//--- which should result in approximate equivalence
//--- We also apply scaling to Y, in order to test that it
//--- is correctly handled too.
for(strictness=0; strictness<=1; strictness++)
{
for(nx=1; nx<=2; nx++)
{
for(ny=1; ny<=2; ny++)
{
//--- problem setup
if(strictness==1)
{
errtol=0;
scalex=vector<double>::Zeros(nx);
for(i=0; i<nx ; i++)
scalex.Set(i,MathPow(16,CMath::RandomInteger(3)-1));
scaley=vector<double>::Zeros(ny);
for(i=0; i<ny; i++)
scaley.Set(i,MathPow(16,CMath::RandomInteger(3)-1));
}
else
{
errtol=1.0E-3;
scalex=vector<double>::Zeros(nx);
for(i=0; i<nx ; i++)
scalex.Set(i,MathPow(4,2*CMath::RandomReal()-1));
scaley=vector<double>::Zeros(ny);
for(i=0; i<ny; i++)
scaley.Set(i,MathPow(4,2*CMath::RandomReal()-1));
}
n=150;
rbase=0.33;
nlayers=2;
gridsize=(int)MathRound(MathPow(n,(double)1/(double)nx))+1;
linterm=1+CMath::RandomInteger(3);
bf=CMath::RandomInteger(2);
lambdav=1.0E-3*CMath::RandomInteger(2);
n=(int)MathRound(MathPow(gridsize,nx));
xy=matrix<double>::Zeros(n,nx+ny);
CAp::Assert(gridsize>1);
CAp::Assert((double)(n)==MathPow(gridsize,nx));
c0=vector<double>::Zeros(nx);
for(j=0; j<nx ; j++)
c0.Set(j,CMath::RandomReal()-0.5);
c1=vector<double>::Zeros(ny);
for(j=0; j<ny; j++)
c1.Set(j,CMath::RandomReal()-0.5);
for(i=0; i<n; i++)
{
k=i;
for(j=0; j<nx ; j++)
{
xy.Set(i,j,(double)(k%gridsize)/(double)(gridsize-1));
k/=gridsize;
}
for(j=0; j<ny; j++)
{
xy.Set(i,nx+j,0);
for(k=0; k<nx ; k++)
xy.Add(i,nx+j,c0[k]*MathCos(M_PI*(1+k)*xy.Get(i,k)));
xy.Mul(i,nx+j,c1[j]);
}
}
xy2=matrix<double>::Zeros(n,nx+ny);
for(j=0; j<nx ; j++)
xy2.Col(j,xy.Col(j)*scalex[j]);
for(j=0; j<ny; j++)
xy2.Col(nx+j,xy.Col(nx+j)*scaley[j]);
//--- Build models
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetV2BF(s,bf);
CRBF::RBFSetAlgoHierarchical(s,rbase,nlayers,lambdav);
if(linterm==1)
CRBF::RBFSetLinTerm(s);
if(linterm==2)
CRBF::RBFSetConstTerm(s);
if(linterm==3)
CRBF::RBFSetZeroTerm(s);
CRBF::RBFSetPoints(s,xy,n);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4945");
return(result);
}
CRBF::RBFCreate(nx,ny,s2);
CRBF::RBFSetV2BF(s2,bf);
CRBF::RBFSetAlgoHierarchical(s2,rbase,nlayers,lambdav);
if(linterm==1)
CRBF::RBFSetLinTerm(s2);
if(linterm==2)
CRBF::RBFSetConstTerm(s2);
if(linterm==3)
CRBF::RBFSetZeroTerm(s2);
CRBF::RBFSetPointsAndScales(s2,xy2,n,scalex);
CRBF::RBFBuildModel(s2,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:4961");
return(result);
}
//--- Compare model values in grid points
x=vector<double>::Zeros(nx);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
x.Set(j,xy.Get(i,j));
CRBF::RBFCalc(s,x,y);
for(j=0; j<nx ; j++)
x.Set(j,xy2.Get(i,j));
CRBF::RBFCalc(s2,x,y2);
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,MathAbs(y[j]-y2[j]/scaley[j])>errtol,"testrbfunit.ap:4978"+" j="+string(j)+" y[j]="+string(y[j])+" y2[j]="+string(y2[j])+" scaley[j]="+string(scaley[j])+" errtol="+string(errtol));
}
//--- Compare model values in random points
x=vector<double>::Zeros(nx);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
x.Set(j,CMath::RandomReal());
CRBF::RBFCalc(s,x,y);
for(j=0; j<nx ; j++)
x.Mul(j,scalex[j]);
CRBF::RBFCalc(s2,x,y2);
for(j=0; j<ny; j++)
CAp::SetErrorFlag(result,MathAbs(y[j]-y2[j]/scaley[j])>errtol,"testrbfunit.ap:4994"+" j="+string(j)+" y[j]="+string(y[j])+" y2[j]="+string(y2[j])+" scaley[j]="+string(scaley[j])+" errtol="+string(errtol));
}
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Test special properties of hierarchical RBFs. |
//| Returns True on errors. |
//+------------------------------------------------------------------+
bool CTestRBFUnit::SpecHRBFTest(void)
{
//--- create variables
bool result=false;
int n=0;
int nx=0;
int ny=0;
double rbase=0;
CRBFModel s0;
CRBFModel s1;
CRBFReport rep;
int nlayers=0;
CMatrixDouble xy;
int i=0;
int j=0;
double vdiff=0;
double d2=0;
int v2its=0;
double vref=0;
double vfunc=0;
double maxerr=0;
CHighQualityRandState rs;
CHighQualityRand::HQRndRandomize(rs);
//--- Test properties of RBF basis functions - we compare values
//--- returned by RBFV2BasisFunc() against analytic expressions
//--- which are approximately modeled by RBFV2BasisFunc().
d2=0.0;
while(d2<100.0)
{
vref=MathExp(-d2);
vfunc=CRBFV2::RBFV2BasisFunc(0,d2);
CAp::SetErrorFlag(result,MathAbs(vref-vfunc)>(1.0E-9*vref),"testrbfunit.ap:5036");
d2+=1.0/64.0;
}
d2=0.0;
maxerr=0.0;
while(d2<16.0)
{
vref=MathMax(1-d2/9,0)*MathExp(-d2);
vfunc=CRBFV2::RBFV2BasisFunc(1,d2);
maxerr=MathMax(maxerr,MathAbs(vref-vfunc));
CAp::SetErrorFlag(result,MathAbs(vref-vfunc)>0.005,"testrbfunit.ap:5046");
d2+=1.0/64.0;
}
//--- Test that tiny changes in dataset points introduce tiny
//--- numerical noise. The noise magnitude depends on the
//--- properties of the linear solver being used. We compare
//--- noise magnitude against hard-coded values.
//--- Test sequence:
//--- * create model #1
//--- * create model #2 using slightly modified dataset
nx=2;
ny=1;
n=20;
rbase=2.0;
nlayers=1;
v2its=50;
xy=matrix<double>::Zeros(n*n,nx+ny);
for(i=0; i<=n*n-1; i++)
{
xy.Set(i,0,i%n);
xy.Set(i,1,i/n);
xy.Set(i,2,MathSin(i));
}
CRBF::RBFCreate(nx,ny,s0);
CRBF::RBFSetAlgoHierarchical(s0,rbase,nlayers,0.0);
CRBF::RBFSetV2Its(s0,v2its);
CRBF::RBFSetPoints(s0,xy,n*n);
CRBF::RBFBuildModel(s0,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:5081");
return(result);
}
for(i=0; i<=n*n-1; i++)
{
xy.Add(i,0,1.0E-14*MathSin(3*i));
xy.Add(i,1,1.0E-14*MathSin(7*i*i));
}
CRBF::RBFCreate(nx,ny,s1);
CRBF::RBFSetAlgoHierarchical(s1,rbase,nlayers,0.0);
CRBF::RBFSetV2Its(s1,v2its);
CRBF::RBFSetPoints(s1,xy,n*n);
CRBF::RBFBuildModel(s1,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:5096");
return(result);
}
vdiff=0;
for(i=0; i<=n-2; i++)
{
for(j=0; j<=n-2; j++)
vdiff+=MathAbs(CRBF::RBFCalc2(s0,0.5+i,0.5+j)-CRBF::RBFCalc2(s1,0.5+i,0.5+j))/CMath::Sqr(n-1);
}
CAp::SetErrorFlag(result,vdiff>0.002 || vdiff<0.00001,"testrbfunit.ap:5103");
//--- Test progress reports: progress before model construction is
//--- zero, progress after model is built is 1. More detailed tests
//--- are performed in the multithreaded TestHRBFProgress().
nx=2;
ny=1;
n=20;
rbase=1.0;
nlayers=3;
xy=matrix<double>::Zeros(n,nx+ny);
for(i=0; i<n; i++)
{
for(j=0; j<=nx; j++)
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs));
}
CRBF::RBFCreate(nx,ny,s0);
CRBF::RBFSetAlgoHierarchical(s0,rbase,nlayers,0.0);
CAp::SetErrorFlag(result,CRBF::RBFPeekProgress(s0)!=0.0,"testrbfunit.ap:5125");
CRBF::RBFBuildModel(s0,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testrbfunit.ap:5127");
CAp::SetErrorFlag(result,CRBF::RBFPeekProgress(s0)!=1.0,"testrbfunit.ap:5128");
CRBF::RBFCreate(nx,ny,s0);
CRBF::RBFSetPoints(s0,xy,n);
CRBF::RBFSetAlgoHierarchical(s0,rbase,0,0.0);
CAp::SetErrorFlag(result,CRBF::RBFPeekProgress(s0)!=0.0,"testrbfunit.ap:5134");
CRBF::RBFBuildModel(s0,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testrbfunit.ap:5136");
CAp::SetErrorFlag(result,CRBF::RBFPeekProgress(s0)!=1.0,"testrbfunit.ap:5137");
CRBF::RBFCreate(nx,ny,s0);
CRBF::RBFSetPoints(s0,xy,n);
CRBF::RBFSetAlgoHierarchical(s0,rbase,nlayers,0.0);
CAp::SetErrorFlag(result,CRBF::RBFPeekProgress(s0)!=0.0,"testrbfunit.ap:5143");
CRBF::RBFBuildModel(s0,rep);
CAp::SetErrorFlag(result,rep.m_terminationtype<=0,"testrbfunit.ap:5145");
CAp::SetErrorFlag(result,CRBF::RBFPeekProgress(s0)!=1.0,"testrbfunit.ap:5146");
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Test gridded evaluation of hierarchical RBFs. |
//| Returns True on errors. |
//+------------------------------------------------------------------+
bool CTestRBFUnit::GridHRBFTest(void)
{
//--- create variables
bool result=false;
CRBFModel s;
CRBFReport rep;
int linterm=0;
int bf=0;
double rbase=0;
int nlayers=0;
int nx=0;
int ny=0;
bool hasscale;
double errtol=0;
int n=0;
CMatrixDouble xy;
CMatrixDouble xy2;
CMatrixDouble y2;
CRowDouble x0;
CRowDouble x1;
CRowDouble x2;
CRowDouble x02;
CRowDouble x12;
CRowDouble x22;
CRowDouble scalevec;
CRowDouble scalevec2;
bool needy[];
bool rowflags[];
int n0=0;
int n1=0;
int n2=0;
int nkind=0;
double v=0;
int i=0;
int j=0;
int k=0;
int i0=0;
int i1=0;
int i2=0;
CRowDouble x;
CRowDouble y;
CRowDouble yv;
CRowDouble yv2;
double scalefactor=0;
double lambdav=0;
//--- Test 2-dimensional grid calculation
errtol=1.0E-12;
nx=2;
for(ny=1; ny<=4; ny++)
{
for(nkind=0; nkind<=2; nkind++)
{
//--- problem setup
n=150;
rbase=0.10;
nlayers=CMath::RandomInteger(3);
linterm=1+CMath::RandomInteger(3);
lambdav=1.0E-3*CMath::RandomInteger(2);
bf=CMath::RandomInteger(2);
xy=matrix<double>::Zeros(n,nx+ny);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
xy.Set(i,j,CMath::RandomReal());
for(j=0; j<ny; j++)
xy.Set(i,nx+j,CMath::RandomReal()-0.5);
}
hasscale=CMath::RandomInteger(2)==0;
if(hasscale)
{
scalevec=vector<double>::Zeros(nx);
for(j=0; j<nx ; j++)
scalevec.Set(j,MathPow(2,2*CMath::RandomReal()-1));
}
//--- Build model
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetV2BF(s,bf);
CRBF::RBFSetAlgoHierarchical(s,rbase,nlayers,lambdav);
if(linterm==1)
CRBF::RBFSetLinTerm(s);
if(linterm==2)
CRBF::RBFSetConstTerm(s);
if(linterm==3)
CRBF::RBFSetZeroTerm(s);
if(hasscale)
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
else
CRBF::RBFSetPoints(s,xy,n);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:5238");
return(result);
}
//--- Prepare grid to test
n0=1+CMath::RandomInteger(50);
n1=1+CMath::RandomInteger(50);
if(nkind==1)
{
k=CMath::RandomInteger(2);
if(k==0)
n0=1;
if(k==1)
n1=1;
}
else
{
if(nkind==2)
{
n0=1;
n1=1;
}
else
{
if(!CAp::Assert(nkind==0))
return(true);
}
}
x0=vector<double>::Zeros(n0);
x0.Set(0,CMath::RandomReal());
for(i=1; i<n0 ; i++)
x0.Set(i,x0[i-1]+CMath::RandomReal()/n0);
x1=vector<double>::Zeros(n1);
x1.Set(0,CMath::RandomReal());
for(i=1; i<n1; i++)
x1.Set(i,x1[i-1]+CMath::RandomReal()/n1);
ArrayResize(needy,n0*n1);
v=MathPow(10,-(3*CMath::RandomReal()));
for(i=0; i<n0*n1 ; i++)
needy[i]=CMath::RandomReal()<v;
//--- Test at grid
x=vector<double>::Zeros(nx);
CRBF::RBFGridCalc2V(s,x0,n0,x1,n1,yv);
for(i0=0; i0<n0 ; i0++)
{
for(i1=0; i1<n1; i1++)
{
x.Set(0,x0[i0]);
x.Set(1,x1[i1]);
CRBF::RBFCalc(s,x,y);
for(i=0; i<ny; i++)
CAp::SetErrorFlag(result,MathAbs(y[i]-yv[i+ny*(i0+i1*n0)])>errtol,"testrbfunit.ap:5287");
}
}
//--- Test calculation on subset of regular grid:
//--- * Test 1: compare full and subset versions
//--- * Test 2: check sparsity. Subset function may perform additional
//--- evaluations because it processes data micro-row by micro-row.
//--- So, we can't check that all elements which were not flagged
//--- are zero - some of them may become non-zero. However, if entire
//--- row is empty, we can reasonably expect (informal guarantee)
//--- that it is not processed. So, we check empty (completely
//--- unflagged) rows
CRBF::RBFGridCalc2VSubset(s,x0,n0,x1,n1,needy,yv2);
for(i=0; i<ny*n0*n1; i++)
CAp::SetErrorFlag(result,needy[i/ny] && MathAbs(yv[i]-yv2[i])>errtol,"testrbfunit.ap:5306");
//--- Test legacy function
CRBF::RBFGridCalc2(s,x0,n0,x1,n1,y2);
for(i=0; i<n0*n1 ; i++)
{
if(ny==1)
CAp::SetErrorFlag(result,MathAbs(yv[i]-y2.Get(i%n0,i/n0))>errtol,"testrbfunit.ap:5316");
else
CAp::SetErrorFlag(result,y2.Get(i%n0,i/n0)!=0.0,"testrbfunit.ap:5318");
}
//--- Test that scaling RBase, XY, X0, X1 by some power of 2
//--- does not change values at grid (quite a strict requirement, but
//--- ALGLIB implementation of RBF may deal with it).
scalefactor=MathPow(1024,2*CMath::RandomInteger(2)-1);
xy2=matrix<double>::Zeros(n,nx+ny);
x02=vector<double>::Zeros(n0);
x12=vector<double>::Zeros(n1);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
xy2.Set(i,j,xy.Get(i,j)*scalefactor);
for(j=0; j<ny; j++)
xy2.Set(i,nx+j,xy.Get(i,nx+j));
}
for(i=0; i<n0 ; i++)
x02.Set(i,x0[i]*scalefactor);
for(i=0; i<n1; i++)
x12.Set(i,x1[i]*scalefactor);
if(hasscale)
CRBF::RBFSetPointsAndScales(s,xy2,n,scalevec);
else
CRBF::RBFSetPoints(s,xy2,n);
CRBF::RBFSetV2BF(s,bf);
CRBF::RBFSetAlgoHierarchical(s,rbase*scalefactor,nlayers,lambdav);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:5350");
return(result);
}
CRBF::RBFGridCalc2V(s,x02,n0,x12,n1,yv2);
for(i=0; i<ny*n0*n1; i++)
CAp::SetErrorFlag(result,yv[i]!=yv2[i],"testrbfunit.ap:5356");
//--- Test that scaling RBase and scale vector by some power of 2
//--- (increase RBase and decreasing scale, or vice versa) does not
//--- change values at grid (quite a strict requirement, but
//--- ALGLIB implementation of RBF may deal with it).
scalefactor=MathPow(1024,2*CMath::RandomInteger(2)-1);
scalevec2=vector<double>::Zeros(nx);
for(i=0; i<nx ; i++)
{
if(hasscale)
scalevec2.Set(i,scalevec[i]);
else
scalevec2.Set(i,1.0);
scalevec2.Mul(i,1.0/scalefactor);
}
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec2);
CRBF::RBFSetV2BF(s,bf);
CRBF::RBFSetAlgoHierarchical(s,rbase*scalefactor,nlayers,lambdav);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:5380");
return(result);
}
CRBF::RBFGridCalc2V(s,x0,n0,x1,n1,yv2);
for(i=0; i<ny*n0*n1; i++)
CAp::SetErrorFlag(result,(double)(MathAbs(yv[i]-yv2[i]))>(100.0*CMath::m_machineepsilon*CApServ::RMaxAbs3(yv[i],yv2[i],1.0)),"testrbfunit.ap:5386");
}
}
//--- Test 3-dimensional grid calculation
errtol=1.0E-12;
nx=3;
for(ny=1; ny<=4; ny++)
{
for(nkind=0; nkind<=2; nkind++)
{
//--- problem setup
n=150;
rbase=0.10;
lambdav=1.0E-3*CMath::RandomInteger(2);
nlayers=CMath::RandomInteger(3);
linterm=1+CMath::RandomInteger(3);
bf=CMath::RandomInteger(2);
xy=matrix<double>::Zeros(n,nx+ny);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
xy.Set(i,j,CMath::RandomReal());
for(j=0; j<ny; j++)
xy.Set(i,nx+j,CMath::RandomReal()-0.5);
}
hasscale=CMath::RandomInteger(2)==0;
if(hasscale)
{
scalevec=vector<double>::Zeros(nx);
for(j=0; j<nx ; j++)
scalevec.Set(j,MathPow(2,2*CMath::RandomReal()-1));
}
//--- Build model
CRBF::RBFCreate(nx,ny,s);
CRBF::RBFSetV2BF(s,bf);
CRBF::RBFSetAlgoHierarchical(s,rbase,nlayers,lambdav);
if(linterm==1)
CRBF::RBFSetLinTerm(s);
if(linterm==2)
CRBF::RBFSetConstTerm(s);
if(linterm==3)
CRBF::RBFSetZeroTerm(s);
if(hasscale)
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
else
CRBF::RBFSetPoints(s,xy,n);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:5441");
return(result);
}
//--- Prepare grid to test
n0=1+CMath::RandomInteger(50);
n1=1+CMath::RandomInteger(50);
n2=1+CMath::RandomInteger(50);
if(nkind==1)
{
k=CMath::RandomInteger(3);
if(k==0)
n0=1;
if(k==1)
n1=1;
if(k==2)
n2=1;
}
else
{
if(nkind==2)
{
n0=1;
n1=1;
n2=1;
}
else
{
if(!CAp::Assert(nkind==0))
return(true);
}
}
x0=vector<double>::Zeros(n0);
x0.Set(0,CMath::RandomReal());
for(i=1; i<n0 ; i++)
x0.Set(i,x0[i-1]+CMath::RandomReal()/n0);
x1=vector<double>::Zeros(n1);
x1.Set(0,CMath::RandomReal());
for(i=1; i<n1; i++)
x1.Set(i,x1[i-1]+CMath::RandomReal()/n1);
x2=vector<double>::Zeros(n2);
x2.Set(0,CMath::RandomReal());
for(i=1; i<n2 ; i++)
x2.Set(i,x2[i-1]+CMath::RandomReal()/n2);
ArrayResize(needy,n0*n1*n2);
v=MathPow(10,-(3*CMath::RandomReal()));
for(i=0; i<n0*n1*n2; i++)
needy[i]=CMath::RandomReal()<v;
//--- Test at grid
x=vector<double>::Zeros(nx);
CRBF::RBFGridCalc3V(s,x0,n0,x1,n1,x2,n2,yv);
for(i0=0; i0<n0 ; i0++)
{
for(i1=0; i1<n1; i1++)
for(i2=0; i2<n2 ; i2++)
{
x.Set(0,x0[i0]);
x.Set(1,x1[i1]);
x.Set(2,x2[i2]);
CRBF::RBFCalc(s,x,y);
for(i=0; i<ny; i++)
CAp::SetErrorFlag(result,MathAbs(y[i]-yv[i+ny*(i0+i1*n0+i2*n0*n1)])>errtol,"testrbfunit.ap:5500");
}
}
//--- Test calculation on subset of regular grid:
//--- * Test 1: compare full and subset versions
//--- * Test 2: check sparsity. Subset function may perform additional
//--- evaluations because it processes data micro-row by micro-row.
//--- So, we can't check that all elements which were not flagged
//--- are zero - some of them may become non-zero. However, if entire
//--- row is empty, we can reasonably expect (informal guarantee)
//--- that it is not processed. So, we check empty (completely
//--- unflagged) rows
ArrayResize(rowflags,n1*n2);
ArrayInitialize(rowflags,false);
for(i=0; i<=n0*n1*n2-1; i++)
{
if(needy[i])
rowflags[i/n0]=true;
}
CRBF::RBFGridCalc3VSubset(s,x0,n0,x1,n1,x2,n2,needy,yv2);
for(i=0; i<ny*n0*n1*n2; i++)
{
CAp::SetErrorFlag(result,needy[i/ny] && MathAbs(yv[i]-yv2[i])>errtol,"testrbfunit.ap:5527");
CAp::SetErrorFlag(result,!rowflags[i/(ny*n0)] && yv2[i]!=0.0,"testrbfunit.ap:5528");
}
//--- Test that scaling RBase, XY, X0, X1 and X2 by some power of 2
//--- does not change values at grid (quite a strict requirement, but
//--- ALGLIB implementation of RBF may deal with it).
scalefactor=MathPow(1024,2*CMath::RandomInteger(2)-1);
xy2=matrix<double>::Zeros(n,nx+ny);
x02=vector<double>::Zeros(n0);
x12=vector<double>::Zeros(n1);
x22=vector<double>::Zeros(n2);
for(i=0; i<n; i++)
{
for(j=0; j<nx ; j++)
xy2.Set(i,j,xy.Get(i,j)*scalefactor);
for(j=0; j<ny; j++)
xy2.Set(i,nx+j,xy.Get(i,nx+j));
}
for(i=0; i<n0 ; i++)
x02.Set(i,x0[i]*scalefactor);
for(i=0; i<n1; i++)
x12.Set(i,x1[i]*scalefactor);
for(i=0; i<n2 ; i++)
x22.Set(i,x2[i]*scalefactor);
if(hasscale)
CRBF::RBFSetPointsAndScales(s,xy2,n,scalevec);
else
CRBF::RBFSetPoints(s,xy2,n);
CRBF::RBFSetV2BF(s,bf);
CRBF::RBFSetAlgoHierarchical(s,rbase*scalefactor,nlayers,lambdav);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:5563");
return(result);
}
CRBF::RBFGridCalc3V(s,x02,n0,x12,n1,x22,n2,yv2);
for(i=0; i<ny*n0*n1*n2; i++)
CAp::SetErrorFlag(result,yv[i]!=yv2[i],"testrbfunit.ap:5569");
//--- Test that scaling RBase and scale vector by some power of 2
//--- (increase RBase and decreasing scale, or vice versa) does not
//--- change values at grid (quite a strict requirement, but
//--- ALGLIB implementation of RBF may deal with it).
scalefactor=MathPow(1024,2*CMath::RandomInteger(2)-1);
scalevec2=vector<double>::Ones(nx);
if(hasscale)
scalevec2=scalevec;
scalevec2/=scalefactor;
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec2);
CRBF::RBFSetV2BF(s,bf);
CRBF::RBFSetAlgoHierarchical(s,rbase*scalefactor,nlayers,lambdav);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(result,true,"testrbfunit.ap:5593");
return(result);
}
CRBF::RBFGridCalc3V(s,x0,n0,x1,n1,x2,n2,yv2);
for(i=0; i<ny*n0*n1*n2; i++)
CAp::SetErrorFlag(result,MathAbs(yv[i]-yv2[i])>(100.0*CMath::m_machineepsilon*CApServ::RMaxAbs3(yv[i],yv2[i],1.0)),"testrbfunit.ap:5599");
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Function for testing gradient evaluation |
//+------------------------------------------------------------------+
void CTestRBFUnit::GradTest(bool &err)
{
//--- create variables
int algoidx=0;
int nkind=0;
int n=0;
int nx=0;
int ny=0;
bool hasscale;
double errtol=0;
double errtol2=0;
double stp=0;
double stp2=0;
CMatrixDouble xy;
CRowDouble scalevec;
CRowDouble x;
CRowDouble x1;
CRowDouble yref;
CRowDouble y;
CRowDouble y1;
CRowDouble dy;
CRowDouble d2y;
CRowDouble dyref;
CRowDouble d2yref;
CRowDouble dy0;
CRowDouble dy1;
CRowDouble dy2;
CRowDouble dy3;
CRowDouble dy4;
CRBFModel s;
CRBFReport rep;
CRBFCalcBuffer tsbuf;
int i=0;
int j=0;
int k=0;
int trialidx=0;
double v0=0;
double v1=0;
double v3=0;
double v4=0;
double df=0;
double d2f=0;
double vy=0;
double vdy0=0;
double vdy1=0;
double vdy2=0;
CHighQualityRandState rs;
bool islonger;
bool hasgradientatnodes;
bool hashessianatnodes;
bool trialatnode;
double meanseparation=0;
CHighQualityRand::HQRndRandomize(rs);
//--- Generate good random grid, build model, check derivatives at random points and at grid nodes
stp=0.000001;
stp2=0.000001;
errtol=0.005;
errtol2=0.005;
for(algoidx=0; algoidx<m_algorithmscnt; algoidx++)
{
for(nx=1; nx<=4; nx++)
{
for(nkind=0; nkind<=1; nkind++)
{
//--- problem setup
if(!CAp::Assert(nkind==0 || nkind==1,"rbf: nkind"))
{
err=true;
return;
}
ny=1+CHighQualityRand::HQRndUniformI(rs,2);
n=100*nkind;
hasscale=CHighQualityRand::HQRndNormal(rs)>0.0;
CRBF::RBFCreate(nx,ny,s);
if(n!=0)
{
GenerateNearlyRegularGrid(n,nx,ny,rs,xy,meanseparation);
if(hasscale)
{
scalevec=vector<double>::Zeros(nx);
for(i=0; i<nx ; i++)
scalevec.Set(i,MathPow(2,0.5*CHighQualityRand::HQRndNormal(rs)));
CRBF::RBFSetPointsAndScales(s,xy,n,scalevec);
}
else
CRBF::RBFSetPoints(s,xy,n);
}
else
meanseparation=1.0;
SetAlgorithm(s,n,nx,ny,hasscale,algoidx,meanseparation,hasgradientatnodes,hashessianatnodes);
CRBF::RBFBuildModel(s,rep);
if(rep.m_terminationtype<=0)
{
CAp::SetErrorFlag(err,true,"testrbfunit.ap:5676");
return;
}
CRBF::RBFCreateCalcBuffer(s,tsbuf);
//--- Check first derivatives - perform N randomized trials
for(trialidx=0; trialidx<=n; trialidx++)
{
//--- Check RBFDiff vs numerical derivatives. RBFCalc() is used to provide reference values.
//--- After this block we assume that RBFDiff is correct reference implementation.
x=vector<double>::Zeros(nx);
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
{
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
trialatnode=true;
}
else
{
GenerateSeparatedWithInbox(xy,n,nx,MathMax(100*stp,MathMax(100*stp2,(n>0?0.01/n:0))),rs,x);
trialatnode=false;
}
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
yref=vector<double>::Zeros(ny+1);
y=vector<double>::Zeros(ny+1);
dy=vector<double>::Zeros(ny*nx+1);
}
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFCalc(s,x1,yref);
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFDiff(s,x1,y,dy);
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5716");
CAp::SetErrorFlag(err,CAp::Len(y)!=ny,"testrbfunit.ap:5717");
CAp::SetErrorFlag(err,CAp::Len(dy)!=ny*nx,"testrbfunit.ap:5718");
if(hasgradientatnodes || !trialatnode)
{
//--- Gradient is well defined, check
for(j=0; j<ny; j++)
{
CAp::SetErrorFlag(err,MathAbs(y[j]-yref[j])>errtol,"testrbfunit.ap:5726");
for(k=0; k<nx ; k++)
{
CAblasF::RCopyAllocV(nx,x,x1);
x1.Add(k,- 2*stp);
CRBF::RBFCalc(s,x1,y1);
v0=y1[j];
CAblasF::RCopyAllocV(nx,x,x1);
x1.Add(k,-stp);
CRBF::RBFCalc(s,x1,y1);
v1=y1[j];
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFCalc(s,x1,y1);
CAblasF::RCopyAllocV(nx,x,x1);
x1.Add(k,stp);
CRBF::RBFCalc(s,x1,y1);
v3=y1[j];
CAblasF::RCopyAllocV(nx,x,x1);
x1.Add(k,2*stp);
CRBF::RBFCalc(s,x1,y1);
v4=y1[j];
df=(-v4+8*v3-8*v1+v0)/(12*stp);
CAp::SetErrorFlag(err,(MathAbs(dy[j*nx+k]-df)/(1+MathAbs(df)))>errtol,"testrbfunit.ap:5750");
}
}
}
else
{
//--- Gradient is undefined at the trial point, zero must be returned
CAp::SetErrorFlag(err,CAblasF::RMaxAbsV(ny*nx,dy)!=0.0,"testrbfunit.ap:5760");
}
//--- Check RBFDiffBuf vs RBFDiff.
x=vector<double>::Zeros(nx);
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
else
CHighQualityRand::HQRndNormalV(rs,nx,x);
//---
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
yref=vector<double>::Zeros(ny+1);
y=vector<double>::Zeros(ny+1);
dy=vector<double>::Zeros(ny*nx+1);
dyref=vector<double>::Zeros(ny*nx+1);
islonger=true;
}
else
{
yref.Resize(0);
y.Resize(0);
dy.Resize(0);
dyref.Resize(0);
islonger=false;
}
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFDiff(s,x1,yref,dyref);
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFDiffBuf(s,x1,y,dy);
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5788");
CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5789");
CAp::SetErrorFlag(err,(islonger && CAp::Len(y)<=ny) || (!islonger && CAp::Len(y)!=ny),"testrbfunit.ap:5790");
CAp::SetErrorFlag(err,(islonger && CAp::Len(dy)<=ny*nx) || (!islonger && CAp::Len(dy)!=ny*nx),"testrbfunit.ap:5791");
for(j=0; j<ny; j++)
{
CAp::SetErrorFlag(err,MathAbs(y[j]-yref[j])>errtol,"testrbfunit.ap:5794");
for(k=0; k<nx ; k++)
CAp::SetErrorFlag(err,MathAbs(dy[j*nx+k]-dyref[j*nx+k])>errtol,"testrbfunit.ap:5796");
}
//--- Check RBFTsDiffBuf vs RBFDiff.
x=vector<double>::Zeros(nx);
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
else
CHighQualityRand::HQRndNormalV(rs,nx,x);
//---
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
yref=vector<double>::Zeros(ny+1);
y=vector<double>::Zeros(ny+1);
dy=vector<double>::Zeros(ny*nx+1);
dyref=vector<double>::Zeros(ny*nx+1);
islonger=true;
}
else
{
yref.Resize(0);
y.Resize(0);
dy.Resize(0);
dyref.Resize(0);
islonger=false;
}
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFDiff(s,x1,yref,dyref);
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFTSDiffBuf(s,tsbuf,x1,y,dy);
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5824");
CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5825");
CAp::SetErrorFlag(err,(islonger && CAp::Len(y)<=ny) || (!islonger && CAp::Len(y)!=ny),"testrbfunit.ap:5826");
CAp::SetErrorFlag(err,(islonger && CAp::Len(dy)<=ny*nx) || (!islonger && CAp::Len(dy)!=ny*nx),"testrbfunit.ap:5827");
for(j=0; j<ny; j++)
{
CAp::SetErrorFlag(err,MathAbs(y[j]-yref[j])>errtol,"testrbfunit.ap:5830");
for(k=0; k<nx ; k++)
CAp::SetErrorFlag(err,MathAbs(dy[j*nx+k]-dyref[j*nx+k])>errtol,"testrbfunit.ap:5832");
}
//--- Check RBFDiff1/RBFDiff2/RBFDiff3 vs RBFDiff.
x=vector<double>::Zeros(nx);
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
else
CHighQualityRand::HQRndNormalV(rs,nx,x);
if(nx==1 && ny==1)
{
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFDiff(s,x1,yref,dyref);
CRBF::RBFDiff1(s,x[0],vy,vdy0);
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5850");
CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5851");
CAp::SetErrorFlag(err,MathAbs(vy-yref[0])>errtol,"testrbfunit.ap:5852");
CAp::SetErrorFlag(err,MathAbs(vdy0-dyref[0])>errtol,"testrbfunit.ap:5853");
}
else
{
CRBF::RBFDiff1(s,1.2,vy,vdy0);
CAp::SetErrorFlag(err,vy!=0.0,"testrbfunit.ap:5858");
CAp::SetErrorFlag(err,vdy0!=0.0,"testrbfunit.ap:5859");
}
x=vector<double>::Zeros(nx);
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
else
CHighQualityRand::HQRndNormalV(rs,nx,x);
if(nx==2 && ny==1)
{
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFDiff(s,x1,yref,dyref);
CRBF::RBFDiff2(s,x[0],x[1],vy,vdy0,vdy1);
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5871");
CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5872");
CAp::SetErrorFlag(err,MathAbs(vy-yref[0])>errtol,"testrbfunit.ap:5873");
CAp::SetErrorFlag(err,MathAbs(vdy0-dyref[0])>errtol,"testrbfunit.ap:5874");
CAp::SetErrorFlag(err,MathAbs(vdy1-dyref[1])>errtol,"testrbfunit.ap:5875");
}
else
{
CRBF::RBFDiff2(s,1.2,3.4,vy,vdy0,vdy1);
CAp::SetErrorFlag(err,vy!=0.0,"testrbfunit.ap:5880");
CAp::SetErrorFlag(err,vdy0!=0.0,"testrbfunit.ap:5881");
CAp::SetErrorFlag(err,vdy1!=0.0,"testrbfunit.ap:5882");
}
x=vector<double>::Zeros(nx);
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
else
CHighQualityRand::HQRndNormalV(rs,nx,x);
if(nx==3 && ny==1)
{
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFDiff(s,x1,yref,dyref);
CRBF::RBFDiff3(s,x[0],x[1],x[2],vy,vdy0,vdy1,vdy2);
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5894");
CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5895");
CAp::SetErrorFlag(err,MathAbs(vy-yref[0])>errtol,"testrbfunit.ap:5896");
CAp::SetErrorFlag(err,MathAbs(vdy0-dyref[0])>errtol,"testrbfunit.ap:5897");
CAp::SetErrorFlag(err,MathAbs(vdy1-dyref[1])>errtol,"testrbfunit.ap:5898");
CAp::SetErrorFlag(err,MathAbs(vdy2-dyref[2])>errtol,"testrbfunit.ap:5899");
}
else
{
CRBF::RBFDiff3(s,1.2,3.4,5.6,vy,vdy0,vdy1,vdy2);
CAp::SetErrorFlag(err,vy!=0.0,"testrbfunit.ap:5904");
CAp::SetErrorFlag(err,vdy0!=0.0,"testrbfunit.ap:5905");
CAp::SetErrorFlag(err,vdy1!=0.0,"testrbfunit.ap:5906");
CAp::SetErrorFlag(err,vdy2!=0.0,"testrbfunit.ap:5907");
}
}
//--- Check second derivatives - perform N randomized trials
for(trialidx=0; trialidx<=n; trialidx++)
{
//--- Check RBFHess vs numerical derivatives.
//--- RBFDiff() is assumed to be already tested and is used to provide exact first derivatives.
//--- In this test we have to handle situation when the Hessian is undefined at the trial node.
x=vector<double>::Zeros(nx);
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
{
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
trialatnode=true;
}
else
{
if(n<1)
CHighQualityRand::HQRndNormalV(rs,nx,x);
else
GenerateSeparatedWithInbox(xy,n,nx,MathMax(100*stp,MathMax(100*stp2,0.01/(double)n)),rs,x);
trialatnode=false;
}
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
dyref=vector<double>::Zeros(ny+1);
yref=vector<double>::Zeros(ny+1);
y=vector<double>::Zeros(ny+1);
dy=vector<double>::Zeros(ny*nx+1);
d2y=vector<double>::Zeros(ny*nx*nx+1);
}
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFDiff(s,x1,yref,dyref);
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFHess(s,x1,y,dy,d2y);
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:5950");
CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:5951");
CAp::SetErrorFlag(err,CAp::Len(y)!=ny,"testrbfunit.ap:5952");
CAp::SetErrorFlag(err,CAp::Len(dy)!=ny*nx,"testrbfunit.ap:5953");
CAp::SetErrorFlag(err,CAp::Len(d2y)!=ny*nx*nx,"testrbfunit.ap:5954");
for(k=0; k<nx ; k++)
CAp::SetErrorFlag(err,(MathAbs(dy[k]-dyref[k])/(1+MathAbs(dyref[k])))>errtol,"testrbfunit.ap:5956");
if(hashessianatnodes || !trialatnode)
{
//--- The Hessian is well defined at the trial point.
for(k=0; k<nx ; k++)
{
//--- Test K-th column of the Hessian, using RBFDiff() as a reference function for the first derivatives
CAblasF::RCopyAllocV(nx,x,x1);
x1.Add(k,-2*stp2);
CRBF::RBFDiff(s,x1,y1,dy0);
CAblasF::RCopyAllocV(nx,x,x1);
x1.Add(k,-stp2);
CRBF::RBFDiff(s,x1,y1,dy1);
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFDiff(s,x1,y1,dy2);
CAblasF::RCopyAllocV(nx,x,x1);
x1.Add(k,stp2);
CRBF::RBFDiff(s,x1,y1,dy3);
CAblasF::RCopyAllocV(nx,x,x1);
x1.Add(k,2*stp2);
CRBF::RBFDiff(s,x1,y1,dy4);
for(i=0; i<ny; i++)
for(j=0; j<nx ; j++)
{
double check1=8*dy3[i*nx+j]-8*dy1[i*nx+j];
double check2=dy0[i*nx+j]-dy4[i*nx+j];
d2f=(-dy4[i*nx+j]+8*dy3[i*nx+j]-8*dy1[i*nx+j]+dy0[i*nx+j])/(12*stp2);
CAp::SetErrorFlag(err,(MathAbs(d2y[i*nx*nx+j*nx+k]-d2f)/(1+MathAbs(d2f)))>errtol2,"testrbfunit.ap:5986");
}
}
}
else
{
//--- The Hessian is undefined at the trial point, exact zero must be returned
CAp::SetErrorFlag(err,CAblasF::RMaxAbsV(ny*nx*nx,d2y)!=0.0,"testrbfunit.ap:5996");
}
//--- Check RBFHessBuf vs RBFHess
x=vector<double>::Zeros(nx);
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
else
if(n<1)
CHighQualityRand::HQRndNormalV(rs,nx,x);
else
GenerateSeparatedWithInbox(xy,n,nx,1.0/(double)n,rs,x);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
y=vector<double>::Zeros(ny+1);
yref=vector<double>::Zeros(ny+1);
dy=vector<double>::Zeros(ny*nx+1);
dyref=vector<double>::Zeros(ny*nx+1);
d2y=vector<double>::Zeros(ny*nx*nx+1);
d2yref=vector<double>::Zeros(ny*nx*nx+1);
islonger=true;
}
else
{
yref.Resize(0);
y.Resize(0);
dy.Resize(0);
dyref.Resize(0);
d2y.Resize(0);
d2yref.Resize(0);
islonger=false;
}
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFHess(s,x1,yref,dyref,d2yref);
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFHessBuf(s,x1,y,dy,d2y);
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:6029");
CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:6030");
CAp::SetErrorFlag(err,CAp::Len(d2yref)!=ny*nx*nx,"testrbfunit.ap:6031");
CAp::SetErrorFlag(err,(islonger && CAp::Len(y)<=ny) || (!islonger && CAp::Len(y)!=ny),"testrbfunit.ap:6032");
CAp::SetErrorFlag(err,(islonger && CAp::Len(dy)<=ny*nx) || (!islonger && CAp::Len(dy)!=ny*nx),"testrbfunit.ap:6033");
CAp::SetErrorFlag(err,(islonger && CAp::Len(d2y)<=ny*nx*nx) || (!islonger && CAp::Len(d2y)!=ny*nx*nx),"testrbfunit.ap:6034");
for(k=0; k<ny; k++)
CAp::SetErrorFlag(err,y[k]!=yref[k],"testrbfunit.ap:6036");
for(k=0; k<ny*nx; k++)
CAp::SetErrorFlag(err,dy[k]!=dyref[k],"testrbfunit.ap:6038");
for(k=0; k<ny*nx*nx; k++)
CAp::SetErrorFlag(err,d2y[k]!=d2yref[k],"testrbfunit.ap:6040");
//--- Check RBFTsHessBuf vs RBFHess
x=vector<double>::Zeros(nx);
if(n>0 && CHighQualityRand::HQRndNormal(rs)>0.0)
CAblasF::RCopyRV(nx,xy,CHighQualityRand::HQRndUniformI(rs,n),x);
else
if(n<1)
CHighQualityRand::HQRndNormalV(rs,nx,x);
else
GenerateSeparatedWithInbox(xy,n,nx,1.0/(double)n,rs,x);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
y=vector<double>::Zeros(ny+1);
yref=vector<double>::Zeros(ny+1);
dy=vector<double>::Zeros(ny*nx+1);
dyref=vector<double>::Zeros(ny*nx+1);
d2y=vector<double>::Zeros(ny*nx*nx+1);
d2yref=vector<double>::Zeros(ny*nx*nx+1);
islonger=true;
}
else
{
islonger=false;
y.Resize(0);
yref.Resize(0);
dy.Resize(0);
dyref.Resize(0);
d2y.Resize(0);
d2yref.Resize(0);
}
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFHess(s,x1,yref,dyref,d2yref);
CAblasF::RCopyAllocV(nx,x,x1);
CRBF::RBFTSHessBuf(s,tsbuf,x1,y,dy,d2y);
CAp::SetErrorFlag(err,CAp::Len(yref)!=ny,"testrbfunit.ap:6071");
CAp::SetErrorFlag(err,CAp::Len(dyref)!=ny*nx,"testrbfunit.ap:6072");
CAp::SetErrorFlag(err,CAp::Len(d2yref)!=ny*nx*nx,"testrbfunit.ap:6073");
CAp::SetErrorFlag(err,(islonger && CAp::Len(y)<=ny) || (!islonger && CAp::Len(y)!=ny),"testrbfunit.ap:6074");
CAp::SetErrorFlag(err,(islonger && CAp::Len(dy)<=ny*nx) || (!islonger && CAp::Len(dy)!=ny*nx),"testrbfunit.ap:6075");
CAp::SetErrorFlag(err,(islonger && CAp::Len(d2y)<=ny*nx*nx) || (!islonger && CAp::Len(d2y)!=ny*nx*nx),"testrbfunit.ap:6076");
for(k=0; k<ny; k++)
CAp::SetErrorFlag(err,y[k]!=yref[k],"testrbfunit.ap:6078");
for(k=0; k<=ny*nx-1; k++)
CAp::SetErrorFlag(err,dy[k]!=dyref[k],"testrbfunit.ap:6080");
for(k=0; k<=ny*nx*nx-1; k++)
CAp::SetErrorFlag(err,d2y[k]!=d2yref[k],"testrbfunit.ap:6082");
}
}
}
}
}
//+------------------------------------------------------------------+
//| Generates nearly regular grid |
//+------------------------------------------------------------------+
void CTestRBFUnit::GenerateNearlyRegularGrid(int n,int nx,int ny,
CHighQualityRandState &rs,
CMatrixDouble &xy,
double &meanseparation)
{
//--- create variables
int k=0;
int griddim=0;
meanseparation=0;
if(!CAp::Assert(n>=MathPow(2,nx),"GenerateNearlyRegularGrid: N<2^NX"))
return;
meanseparation=1.0;
griddim=(int)MathRound(MathPow(n,1.0/(double)nx))+1;
xy=matrix<double>::Zeros(n,nx+ny);
for(int i=0; i<n; i++)
{
k=i;
for(int j=0; j<nx ; j++)
{
xy.Set(i,j,k%griddim+0.05*CHighQualityRand::HQRndNormal(rs));
k/=griddim;
}
for(k=nx; k<nx+ny; k++)
xy.Set(i,k,CHighQualityRand::HQRndNormal(rs));
}
}
//+------------------------------------------------------------------+
//| Generates set of normally distributed points(mean = 0, sigma = 1)|
//| with good separation between nodes. |
//+------------------------------------------------------------------+
void CTestRBFUnit::GenerateGoodRandomGrid(int n,int nx,int ny,
CHighQualityRandState &rs,
CMatrixDouble &xy,
double &meanseparation)
{
//--- create variables
int i=0;
int j=0;
int k=0;
double mindist=0;
double d=0;
double v=0;
meanseparation=0;
if(!CAp::Assert(n>=0,"GenerateGoodRandomGrid: N<1"))
return;
if(n==0)
{
meanseparation=1;
return;
}
xy=matrix<double>::Zeros(n,nx+ny);
d=1.0/MathPow(n,1.0/(double)nx);
for(i=0; i<n; i++)
{
do
{
for(k=0; k<nx+ny; k++)
xy.Set(i,k,CHighQualityRand::HQRndNormal(rs));
mindist=1.0E50;
for(j=0; j<=i-1; j++)
{
v=0;
for(k=0; k<nx ; k++)
v+=CMath::Sqr(xy.Get(i,k)-xy.Get(j,k));
mindist=MathMin(mindist,MathSqrt(v));
}
}
while(mindist<=d);
}
if(n>1)
{
meanseparation=0;
for(i=0; i<n; i++)
{
mindist=1.0E50;
for(j=0; j<n; j++)
{
if(j!=i)
{
v=0;
for(k=0; k<nx ; k++)
v+=CMath::Sqr(xy.Get(i,k)-xy.Get(j,k));
mindist=MathMin(mindist,MathSqrt(v));
}
}
meanseparation+=mindist/n;
}
meanseparation=CApServ::Coalesce(meanseparation,1);
}
else
meanseparation=1;
}
//+------------------------------------------------------------------+
//| Generates random point within box surrounding dataset, with good |
//| separation from dataset points. |
//+------------------------------------------------------------------+
void CTestRBFUnit::GenerateSeparatedWithInbox(CMatrixDouble &xy,int n,
int nx,double mindist,
CHighQualityRandState &rs,
CRowDouble &x)
{
//--- create variables
int i=0;
int j=0;
CRowDouble minx;
CRowDouble maxx;
double d=0;
double mind=0;
if(n<1)
{
CHighQualityRand::HQRndNormalV(rs,nx,x);
return;
}
CAblasF::RAllocV(nx,minx);
CAblasF::RAllocV(nx,maxx);
CAblasF::RAllocV(nx,x);
CAblasF::RCopyRV(nx,xy,0,minx);
CAblasF::RCopyRV(nx,xy,0,maxx);
for(i=1; i<n; i++)
{
CAblasF::RMergeMinRV(nx,xy,i,minx);
CAblasF::RMergeMaxRV(nx,xy,i,maxx);
}
for(i=0; i<nx ; i++)
{
minx.Add(i,-2*mindist);
maxx.Add(i,2*mindist);
}
do
{
for(j=0; j<nx ; j++)
x.Set(j,minx[j]+(maxx[j]-minx[j])*CHighQualityRand::HQRndUniformR(rs));
mind=1.0E50+mindist;
for(i=0; i<n; i++)
{
d=0;
for(j=0; j<nx ; j++)
d+=CMath::Sqr(xy.Get(i,j)-x[j]);
d=MathSqrt(d);
if(d<mind)
mind=d;
}
}
while(mind<mindist);
}
//+------------------------------------------------------------------+
//| Sets algorithm according to its index. If problem has unsupported|
//| dimensions or other properties(e.m_g. variable scales vector is |
//| present), this function assigns some other algorithm. |
//| This function outputs information about properties of the |
//| algorithm that was actually chosen: |
//| * whether second derivatives are defined at the nodes |
//+------------------------------------------------------------------+
void CTestRBFUnit::SetAlgorithm(CRBFModel &s,int n,int nx,int ny,
bool hasscale,int algoidx,
double meanpointsseparation,
bool &hasgradientatnodes,
bool &hashessianatnodes)
{
hasgradientatnodes=true;
hashessianatnodes=true;
if(algoidx==0)
{
if((nx>=2 && nx<=3) && !hasscale)
CRBF::RBFSetAlgoQNN(s,meanpointsseparation,5.0);
else
CRBF::RBFSetAlgoHierarchical(s,16*meanpointsseparation,8,0.0);
return;
}
if(algoidx==1)
{
if((nx>=2 && nx<=3) && !hasscale)
CRBF::RBFSetAlgoMultilayer(s,16*meanpointsseparation,8,0.0);
else
CRBF::RBFSetAlgoHierarchical(s,16*meanpointsseparation,8,0.0);
return;
}
if(algoidx==2)
{
CRBF::RBFSetAlgoHierarchical(s,16*meanpointsseparation,8,0.0);
return;
}
if(algoidx==3)
{
CRBF::RBFSetAlgoThinPlateSpline(s,CMath::RandomInteger(2)*MathPow(10,-(6*CMath::RandomReal())));
hashessianatnodes=false;
return;
}
if(algoidx==4)
{
if(CMath::RandomReal()>0.5)
CRBF::RBFSetAlgoMultiQuadricManual(s,meanpointsseparation*(0.5+CMath::RandomReal()),CMath::RandomInteger(2)*MathPow(10,-(6*CMath::RandomReal())));
else
CRBF::RBFSetAlgoMultiQuadricAuto(s,CMath::RandomInteger(2)*MathPow(10,-(6*CMath::RandomReal())));
return;
}
if(algoidx==5)
{
CRBF::RBFSetAlgoBiharmonic(s,CMath::RandomInteger(2)*MathPow(10,-(6*CMath::RandomReal())));
hasgradientatnodes=false;
hashessianatnodes=false;
return;
}
CAp::Assert(false,"SetAlgorithm: unknown AlgoIdx");
}
//+------------------------------------------------------------------+
//| Sets one of XRBF algorithms (version 3 RBFs introduced in 2022) |
//| according to its index. |
//+------------------------------------------------------------------+
void CTestRBFUnit::SetXRBFAlgorithmExact(CRBFModel &s,int algoidx,
double meanpointsseparation)
{
if(algoidx==0)
{
CRBF::RBFSetAlgoThinPlateSpline(s,0.0);
return;
}
if(algoidx==1)
{
if(CMath::RandomReal()>0.5)
CRBF::RBFSetAlgoMultiQuadricManual(s,meanpointsseparation*(0.5+CMath::RandomReal()),0.0);
else
CRBF::RBFSetAlgoMultiQuadricAuto(s,0.0);
return;
}
if(algoidx==2)
{
CRBF::RBFSetAlgoBiharmonic(s,0.0);
return;
}
CAp::Assert(false,"SetXRBFAlgorithm: unknown AlgoIdx");
}
//+------------------------------------------------------------------+
//| Sets one of smoothing XRBF algorithms(version 3 RBFs introduced |
//| in 2022) according to its index. |
//+------------------------------------------------------------------+
void CTestRBFUnit::SetXRBFAlgorithmSmoothing(CRBFModel &s,int algoidx,
double lambdav,
double meanpointsseparation)
{
if(algoidx==0)
{
CRBF::RBFSetAlgoThinPlateSpline(s,lambdav);
return;
}
if(algoidx==1)
{
if(CMath::RandomReal()>0.5)
CRBF::RBFSetAlgoMultiQuadricManual(s,meanpointsseparation*(0.5+CMath::RandomReal()),lambdav);
else
CRBF::RBFSetAlgoMultiQuadricAuto(s,lambdav);
return;
}
if(algoidx==2)
{
CRBF::RBFSetAlgoBiharmonic(s,lambdav);
return;
}
CAp::Assert(false,"SetXRBFAlgorithmSmoothing: unknown AlgoIdx");
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestMLPBaseUnit
{
public:
static bool TestMLPBase(bool silent);
private:
static double VectorDiff(CRowDouble &g0,CRowDouble &g1,int n,double s);
static void CreateNetwork(CMultilayerPerceptron &network,int nkind,double a1,double a2,int nin,int nhid1,int nhid2,int nout);
static void UnsetNetwork(CMultilayerPerceptron &network);
static void TestInformational(int nkind,int nin,int nhid1,int nhid2,int nout,int passcount,bool &err);
static void TestProcessing(int nkind,int nin,int nhid1,int nhid2,int nout,int passcount,bool &err);
static void TestGradient(int nkind,int nin,int nhid1,int nhid2,int nout,int passcount,int sizemin,int sizemax,bool &err);
static void TestHessian(int nkind,int nin,int nhid1,int nhid2,int nout,int passcount,bool &err);
static void TestErr(int nkind,int nin,int nhid1,int nhid2,int nout,int passcount,int sizemin,int sizemax,bool &err);
static void SpecTests(bool &inferrors,bool &procerrors,bool &graderrors,bool &hesserrors,bool &errerrors);
static bool TestMLPGBSubset(void);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestMLPBaseUnit::TestMLPBase(bool silent)
{
//--- create variables
bool waserrors=false;
bool inferrors=false;
bool procerrors=false;
bool graderrors=false;
bool hesserrors=false;
bool errerrors=false;
int passcount=5;
int maxn=3;
int maxhid=3;
bool result;
int sizemin=0;
int sizemax=0;
int nf=0;
int nl=0;
int nhid1=0;
int nhid2=0;
int nkind=0;
CMultilayerPerceptron network;
CMultilayerPerceptron network2;
CMatrixDouble xy;
CMatrixDouble valxy;
//--- Special tests
SpecTests(inferrors,procerrors,graderrors,hesserrors,errerrors);
//--- General multilayer network tests.
//--- These tests are performed with small dataset, whose size is in [0,10].
//--- We test correctness of functions on small sets, but do not test code
//--- which splits large dataset into smaller chunks.
sizemin=0;
sizemax=10;
for(nf=1; nf<=maxn; nf++)
{
for(nl=1; nl<=maxn; nl++)
{
for(nhid1=0; nhid1<=maxhid; nhid1++)
{
for(nhid2=0; nhid2<=maxhid; nhid2++)
{
for(nkind=0; nkind<=3; nkind++)
{
//--- Skip meaningless parameters combinations
if(nkind==1 && nl<2)
continue;
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,sizemin,sizemax,graderrors);
TestHessian(nkind,nf,nhid1,nhid2,nl,passcount,hesserrors);
TestErr(nkind,nf,nhid1,nhid2,nl,passcount,sizemin,sizemax,errerrors);
}
}
}
}
}
//--- Special tests on large datasets: test ability to correctly split
//--- work into smaller chunks.
nf=2;
nhid1=20;
nhid2=20;
nl=2;
sizemin=50000;
sizemax=50000;
TestErr(0,nf,nhid1,nhid2,nl,1,sizemin,sizemax,errerrors);
TestGradient(0,nf,nhid1,nhid2,nl,1,sizemin,sizemax,graderrors);
//--- Test for MLPGradBatch____Subset()
graderrors=graderrors||TestMLPGBSubset();
//--- Final report
waserrors=(((inferrors||procerrors)||graderrors)||hesserrors)||errerrors;
if(!silent)
{
Print("MLP TEST");
PrintResult("INFORMATIONAL FUNCTIONS",!inferrors);
PrintResult("BASIC PROCESSING",!procerrors);
PrintResult("GRADIENT CALCULATION",!graderrors);
PrintResult("HESSIAN CALCULATION",!hesserrors);
PrintResult("ERROR FUNCTIONS",!errerrors);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| This function compares vectors G0 and G1 and returns |
//| ||G0-G1||/max(||G0||,||G1||,S) |
//| For zero G0, G1 and S (all three quantities are zero) it returns |
//| zero. |
//+------------------------------------------------------------------+
double CTestMLPBaseUnit::VectorDiff(CRowDouble &g0,
CRowDouble &g1,
int n,double s)
{
//--- create variables
double norm0=0;
double norm1=0;
double diff=0;
for(int i=0; i<n; i++)
{
norm0+=CMath::Sqr(g0[i]);
norm1+=CMath::Sqr(g1[i]);
diff+=CMath::Sqr(g0[i]-g1[i]);
}
norm0=MathSqrt(norm0);
norm1=MathSqrt(norm1);
diff=MathSqrt(diff);
if(norm0!=0.0 || norm1!=0.0 || s!=0.0)
diff/=MathMax(MathMax(norm0,norm1),s);
else
diff=0;
return(diff);
}
//+------------------------------------------------------------------+
//| Network creation |
//| This function creates network with desired structure. |
//| Network is created using one of the three methods: |
//| a) straightforward 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 at random. |
//+------------------------------------------------------------------+
void CTestMLPBaseUnit::CreateNetwork(CMultilayerPerceptron &network,
int nkind,double a1,double a2,
int nin,int nhid1,int nhid2,
int nout)
{
//--- create variables
int mkind=0;
CMultilayerPerceptron tmp;
//--- check
if(!CAp::Assert(((nin>0 && nhid1>=0) && nhid2>=0) && nout>0,"CreateNetwork error"))
return;
if(!CAp::Assert(nhid1!=0 || nhid2==0,"CreateNetwork error"))
return;
if(!CAp::Assert(nkind!=1 || nout>=2,"CreateNetwork error"))
return;
mkind=CMath::RandomInteger(3);
if(nhid1==0)
{
//--- No hidden layers
if(nkind==0)
{
if(mkind==0)
CMLPBase::MLPCreate0(nin,nout,network);
if(mkind==1)
{
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;
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//---
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
if(mkind==2)
{
CMLPBase::MLPCreate0(nin,nout,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
if(nkind==1)
{
if(mkind==0)
CMLPBase::MLPCreateC0(nin,nout,network);
if(mkind==1)
{
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;
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//---
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
if(mkind==2)
{
CMLPBase::MLPCreateC0(nin,nout,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
if(nkind==2)
{
if(mkind==0)
CMLPBase::MLPCreateB0(nin,nout,a1,a2,network);
if(mkind==1)
{
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;
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//---
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
if(mkind==2)
{
CMLPBase::MLPCreateB0(nin,nout,a1,a2,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
if(nkind==3)
{
if(mkind==0)
CMLPBase::MLPCreateR0(nin,nout,a1,a2,network);
if(mkind==1)
{
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;
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//---
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
if(mkind==2)
{
CMLPBase::MLPCreateR0(nin,nout,a1,a2,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
}
}
}
CMLPBase::MLPRandomizeFull(network);
return;
}
if(nhid2==0)
{
//--- One hidden layer
if(nkind==0)
{
if(mkind==0)
CMLPBase::MLPCreate1(nin,nhid1,nout,network);
if(mkind==1)
{
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;
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//---
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
if(mkind==2)
{
CMLPBase::MLPCreate1(nin,nhid1,nout,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
if(nkind==1)
{
if(mkind==0)
{
CMLPBase::MLPCreateC1(nin,nhid1,nout,network);
}
if(mkind==1)
{
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;
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//---
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
if(mkind==2)
{
CMLPBase::MLPCreateC1(nin,nhid1,nout,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
if(nkind==2)
{
if(mkind==0)
CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,network);
if(mkind==1)
{
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;
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//---
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
if(mkind==2)
{
CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
if(nkind==3)
{
if(mkind==0)
CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,network);
if(mkind==1)
{
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;
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//---
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
if(mkind==2)
{
CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
}
}
}
CMLPBase::MLPRandomizeFull(network);
return;
}
//--- Two hidden layers
if(nkind==0)
{
if(mkind==0)
CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,network);
if(mkind==1)
{
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;
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//---
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
if(mkind==2)
{
CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
if(nkind==1)
{
if(mkind==0)
CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,network);
if(mkind==1)
{
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;
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//---
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
if(mkind==2)
{
CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
if(nkind==2)
{
if(mkind==0)
CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,network);
if(mkind==1)
{
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;
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//---
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
if(mkind==2)
{
CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
else
{
if(nkind==3)
{
if(mkind==0)
CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,network);
if(mkind==1)
{
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;
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,tmp);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,tmp);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//---
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network);
_local_serializer.Stop();
}
}
if(mkind==2)
{
CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,tmp);
CMLPBase::MLPCopy(tmp,network);
}
}
}
}
}
CMLPBase::MLPRandomizeFull(network);
}
//+------------------------------------------------------------------+
//| Unsets network (initialize it to smallest network possible |
//+------------------------------------------------------------------+
void CTestMLPBaseUnit::UnsetNetwork(CMultilayerPerceptron &network)
{
CMLPBase::MLPCreate0(1,1,network);
}
//+------------------------------------------------------------------+
//| Informational functions test |
//+------------------------------------------------------------------+
void CTestMLPBaseUnit::TestInformational(int nkind,int nin,int nhid1,
int nhid2,int nout,
int passcount,bool &err)
{
//--- create variables
CMultilayerPerceptron network;
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;
CMatrixDouble neurons;
CRowDouble x;
CRowDouble y;
double mean=0;
double sigma=0;
int fkind=0;
double c=0;
double f=0;
double df=0;
double d2f=0;
double s=0;
threshold=100000*CMath::m_machineepsilon;
CreateNetwork(network,nkind,0.0,0.0,nin,nhid1,nhid2,nout);
//--- test MLPProperties()
CMLPBase::MLPProperties(network,n1,n2,wcount);
err=(err||n1!=nin||n2!=nout||wcount<=0);
err=(err||CMLPBase::MLPGetInputsCount(network)!=nin||CMLPBase::MLPGetOutputsCount(network)!=nout ||
CMLPBase::MLPGetWeightsCount(network)!=wcount);
//--- 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);
if(nhid1!=0)
{
nlayers=3;
nmax=MathMax(nmax,nhid1);
}
if(nhid2!=0)
{
nlayers=4;
nmax=MathMax(nmax,nhid2);
}
neurons=matrix<double>::Zeros(nlayers,nmax);
x=vector<double>::Zeros(nin);
for(i=0; i<nin ; i++)
x.Set(i,2*CMath::RandomReal()-1);
y=vector<double>::Zeros(nout);
for(i=0; i<nout; i++)
y.Set(i,2*CMath::RandomReal()-1);
for(j=0; j<nin ; j++)
{
CMLPBase::MLPGetInputScaling(network,j,mean,sigma);
neurons.Set(0,j,(x[j]-mean)/sigma);
}
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.Add(i,j,CMLPBase::MLPGetWeight(network,i-1,k,i,j)*neurons.Get(i-1,k));
CMLPBase::MLPGetNeuronInfo(network,i,j,fkind,c);
CMLPBase::MLPActivationFunction(neurons.Get(i,j)-c,fkind,f,df,d2f);
neurons.Set(i,j,f);
}
}
if(nkind==1)
{
s=0;
for(j=0; j<nout; j++)
s+=MathExp(neurons.Get(nlayers-1,j));
for(j=0; j<nout; j++)
neurons.Set(nlayers-1,j,MathExp(neurons.Get(nlayers-1,j))/s);
}
for(j=0; j<nout; j++)
{
CMLPBase::MLPGetOutputScaling(network,j,mean,sigma);
neurons.Set(nlayers-1,j,neurons.Get(nlayers-1,j)*sigma+mean);
}
CMLPBase::MLPProcess(network,x,y);
for(j=0; j<nout; j++)
err=err||MathAbs(neurons.Get(nlayers-1,j)-y[j])>threshold;
}
//+------------------------------------------------------------------+
//| Processing functions test |
//+------------------------------------------------------------------+
void CTestMLPBaseUnit::TestProcessing(int nkind,int nin,int nhid1,
int nhid2,int nout,
int passcount,bool &err)
{
//--- create variables
CMultilayerPerceptron network;
CMultilayerPerceptron network2;
CSparseMatrix sparsexy;
CMatrixDouble densexy;
int npoints=0;
int subnp=0;
bool iscls;
int n1=0;
int n2=0;
int wcount=0;
bool zeronet;
double a1=0;
double a2=0;
int pass=0;
int i=0;
int j=0;
bool allsame;
CRowDouble x1;
CRowDouble x2;
CRowDouble y1;
CRowDouble y2;
CRowDouble p0;
CRowDouble p1;
int pcount=0;
double v=0;
int i_=0;
//--- check
if(!CAp::Assert(passcount>=2,"PassCount<2!"))
{
err=true;
return;
}
//--- Prepare network
a1=0;
a2=0;
if(nkind==2)
{
a1=1000*CMath::RandomReal()-500;
a2=2*CMath::RandomReal()-1;
}
if(nkind==3)
{
a1=1000*CMath::RandomReal()-500;
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
}
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
CMLPBase::MLPProperties(network,n1,n2,wcount);
iscls=CMLPBase::MLPIsSoftMax(network);
//--- Initialize arrays
x1=vector<double>::Zeros(nin);
x2=vector<double>::Zeros(nin);
y1=vector<double>::Zeros(nout);
y2=vector<double>::Zeros(nout);
//--- Initialize sets
npoints=CMath::RandomInteger(11)+10;
if(iscls)
{
densexy=matrix<double>::Zeros(npoints,nin+1);
CSparse::SparseCreate(npoints,nin+1,npoints,sparsexy);
}
else
{
densexy=matrix<double>::Zeros(npoints,nin+nout);
CSparse::SparseCreate(npoints,nin+nout,npoints,sparsexy);
}
CSparse::SparseConvertToCRS(sparsexy);
//--- Main cycle
for(pass=1; pass<=passcount; pass++)
{
//--- Last run is made on zero network
CMLPBase::MLPRandomizeFull(network);
zeronet=false;
if(pass==passcount)
{
network.m_weights.Fill(0);
zeronet=true;
}
//--- Same inputs leads to same outputs
for(i=0; i<nin ; i++)
{
x1.Set(i,2*CMath::RandomReal()-1);
x2.Set(i,x1[i]);
}
for(i=0; i<nout; i++)
{
y1.Set(i,2*CMath::RandomReal()-1);
y2.Set(i,2*CMath::RandomReal()-1);
}
CMLPBase::MLPProcess(network,x1,y1);
CMLPBase::MLPProcess(network,x2,y2);
CAp::SetErrorFlag(err,VectorDiff(y1,y2,nout,1.0)!=0.0,"testmlpbaseunit.ap:513");
//--- Same inputs on original network leads to same outputs
//--- on copy created:
//--- * using MLPCopy
for(i=0; i<nin ; i++)
{
x1.Set(i,2*CMath::RandomReal()-1);
x2.Set(i,x1[i]);
}
for(i=0; i<nout; i++)
{
y1.Set(i,2*CMath::RandomReal()-1);
y2.Set(i,2*CMath::RandomReal()-1);
}
UnsetNetwork(network2);
CMLPBase::MLPCopy(network,network2);
CMLPBase::MLPProcess(network,x1,y1);
CMLPBase::MLPProcess(network2,x2,y2);
CAp::SetErrorFlag(err,VectorDiff(y1,y2,nout,1.0)!=0.0,"testmlpbaseunit.ap:534");
//--- Additionally we tests functions for copying of tunable
//--- parameters by:
//--- * copying network using MLPCopy
//--- * randomizing tunable parameters with MLPRandomizeFull()
//--- * copying tunable parameters with:
//--- a) MLPCopyTunableParameters
//--- b) combination of MLPExportTunableParameters and
//--- MLPImportTunableParameters - we export parameters
//--- to P1, copy PCount elements to P2, then test import.
for(i=0; i<nin ; i++)
{
x1.Set(i,2*CMath::RandomReal()-1);
x2.Set(i,x1[i]);
}
for(i=0; i<nout; i++)
{
y1.Set(i,2*CMath::RandomReal()-1);
y2.Set(i,2*CMath::RandomReal()-1);
}
UnsetNetwork(network2);
CMLPBase::MLPCopy(network,network2);
CMLPBase::MLPRandomizeFull(network2);
CMLPBase::MLPCopyTunableParameters(network,network2);
CMLPBase::MLPProcess(network,x1,y1);
CMLPBase::MLPProcess(network2,x2,y2);
CAp::SetErrorFlag(err,VectorDiff(y1,y2,nout,1.0)!=0.0,"testmlpbaseunit.ap:571");
for(i=0; i<nout; i++)
y2.Set(i,2*CMath::RandomReal()-1);
UnsetNetwork(network2);
CMLPBase::MLPCopy(network,network2);
CMLPBase::MLPRandomizeFull(network2);
CMLPBase::MLPExportTunableParameters(network,p0,pcount);
p1=p0;
CMLPBase::MLPImportTunableParameters(network2,p1);
CMLPBase::MLPProcess(network,x1,y1);
CMLPBase::MLPProcess(network2,x2,y2);
CAp::SetErrorFlag(err,VectorDiff(y1,y2,nout,1.0)!=0.0,"testmlpbaseunit.ap:585");
//--- 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;
_local_serializer.Alloc_Start();
CMLPBase::MLPAlloc(_local_serializer,network);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CMLPBase::MLPSerialize(_local_serializer,network);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//---
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CMLPBase::MLPUnserialize(_local_serializer,network2);
_local_serializer.Stop();
}
for(i=0; i<nin ; i++)
{
x1.Set(i,2*CMath::RandomReal()-1);
x2.Set(i,x1[i]);
}
for(i=0; i<nout; i++)
{
y1.Set(i,2*CMath::RandomReal()-1);
y2.Set(i,2*CMath::RandomReal()-1);
}
CMLPBase::MLPProcess(network,x1,y1);
CMLPBase::MLPProcess(network2,x2,y2);
allsame=true;
for(i=0; i<nout; 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 ; i++)
{
x1.Set(i,2*CMath::RandomReal()-1);
x2.Set(i,2*CMath::RandomReal()-1);
}
for(i=0; i<nout; i++)
{
y1.Set(i,2*CMath::RandomReal()-1);
y2.Set(i,y1[i]);
}
CMLPBase::MLPProcess(network,x1,y1);
CMLPBase::MLPProcess(network,x2,y2);
allsame=true;
for(i=0; i<nout; 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 ; i++)
{
x1.Set(i,2*CMath::RandomReal()-1);
x2.Set(i,2*CMath::RandomReal()-1);
}
for(i=0; i<nout; i++)
{
y1.Set(i,2*CMath::RandomReal()-1);
y2.Set(i,y1[i]);
}
CMLPBase::MLPCopy(network,network2);
CMLPBase::MLPRandomize(network2);
CMLPBase::MLPProcess(network,x1,y1);
CMLPBase::MLPProcess(network2,x1,y2);
allsame=true;
for(i=0; i<nout; 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 ; i++)
{
x1.Set(i,2*CMath::RandomReal()-1);
x2.Set(i,2*CMath::RandomReal()-1);
}
for(i=0; i<nout; i++)
{
y1.Set(i,2*CMath::RandomReal()-1);
y2.Set(i,y1[i]);
}
CMLPBase::MLPCopy(network,network2);
CMLPBase::MLPRandomizeFull(network2);
CMLPBase::MLPProcess(network,x1,y1);
CMLPBase::MLPProcess(network2,x1,y2);
allsame=true;
for(i=0; i<nout; 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 ; i++)
x1.Set(i,2*CMath::RandomReal()-1);
CMLPBase::MLPProcess(network,x1,y1);
v=0;
for(i=0; i<nout; i++)
{
v+=y1[i];
err=err||y1[i]<0.0;
}
err=err||MathAbs(v-1)>(1000.0*CMath::m_machineepsilon);
}
if(nkind==2)
{
//--- B-type network outputs are bounded from above/below
for(i=0; i<nin ; i++)
x1.Set(i,2*CMath::RandomReal()-1);
CMLPBase::MLPProcess(network,x1,y1);
for(i=0; i<nout; i++)
{
if(a2>=0.0)
err=err||y1[i]<a1;
else
err=err||y1[i]>a1;
}
}
if(nkind==3)
{
//--- R-type network outputs are within [A1,A2] (or [A2,A1])
for(i=0; i<nin ; i++)
x1.Set(i,2*CMath::RandomReal()-1);
CMLPBase::MLPProcess(network,x1,y1);
for(i=0; i<nout; i++)
err=(err||y1[i]<MathMin(a1,a2)||y1[i]>MathMax(a1,a2));
}
//--- Comperison MLPInitPreprocessor results with
//--- MLPInitPreprocessorSparse results
CSparse::SparseConvertToHash(sparsexy);
if(iscls)
{
for(i=0; i<npoints ; i++)
{
for(j=0; j<nin ; j++)
{
densexy.Set(i,j,2*CMath::RandomReal()-1);
CSparse::SparseSet(sparsexy,i,j,densexy.Get(i,j));
}
densexy.Set(i,nin,CMath::RandomInteger(nout));
CSparse::SparseSet(sparsexy,i,j,densexy.Get(i,nin));
}
}
else
{
for(i=0; i<npoints ; i++)
for(j=0; j<nin+nout ; j++)
{
densexy.Set(i,j,2*CMath::RandomReal()-1);
CSparse::SparseSet(sparsexy,i,j,densexy.Get(i,j));
}
}
CSparse::SparseConvertToCRS(sparsexy);
CMLPBase::MLPCopy(network,network2);
CMLPBase::MLPInitPreprocessor(network,densexy,npoints);
CMLPBase::MLPInitPreprocessorSparse(network2,sparsexy,npoints);
subnp=CMath::RandomInteger(npoints);
for(i=0; i<=subnp-1; i++)
{
for(j=0; j<nin ; j++)
x1.Set(j,2*CMath::RandomReal()-1);
CMLPBase::MLPProcess(network,x1,y1);
CMLPBase::MLPProcess(network2,x1,y2);
for(j=0; j<nout; j++)
err=err||(double)(MathAbs(y1[j]-y2[j]))>1.0E-6;
}
}
}
//+------------------------------------------------------------------+
//| Gradient functions test |
//+------------------------------------------------------------------+
void CTestMLPBaseUnit::TestGradient(int nkind,int nin,int nhid1,
int nhid2,int nout,int passcount,
int sizemin,int sizemax,bool &err)
{
//--- create variables
CMultilayerPerceptron network;
CSparseMatrix sparsexy;
CSparseMatrix sparsexy2;
int n1=0;
int n2=0;
int wcount=0;
double h=0;
double etol=0;
double escale=0;
double gscale=0;
double nonstricttolerance=0;
double a1=0;
double a2=0;
int pass=0;
int i=0;
int j=0;
int k=0;
int ssize=0;
int subsetsize=0;
int rowsize=0;
CMatrixDouble xy;
CMatrixDouble xy2;
CRowDouble grad1;
CRowDouble grad2;
CRowDouble gradsp;
CRowDouble x;
CRowDouble y;
CRowDouble x1;
CRowDouble x2;
CRowDouble y1;
CRowDouble y2;
CRowInt idx;
double v=0;
double e=0;
double e1=0;
double e2=0;
double esp=0;
double v1=0;
double v2=0;
double v3=0;
double v4=0;
double wprev=0;
double referencee=0;
CRowDouble referenceg;
int i_=0;
int i1_=0;
if(nkind==2)
{
a1=1000*CMath::RandomReal()-500;
a2=2*CMath::RandomReal()-1;
}
if(nkind==3)
{
a1=1000*CMath::RandomReal()-500;
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
}
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
CMLPBase::MLPProperties(network,n1,n2,wcount);
h=0.0001;
etol=1.0E-2;
escale=1.0E-2;
gscale=1.0E-2;
nonstricttolerance=0.01;
//--- Initialize
x=vector<double>::Zeros(nin);
x1=vector<double>::Zeros(nin);
x2=vector<double>::Zeros(nin);
y=vector<double>::Zeros(nout);
y1=vector<double>::Zeros(nout);
y2=vector<double>::Zeros(nout);
referenceg=vector<double>::Zeros(wcount);
grad1=vector<double>::Zeros(wcount);
grad2=vector<double>::Zeros(wcount);
//--- Process
for(pass=1; pass<=passcount; pass++)
{
//--- Randomize network, then re-randomaze weights manually.
//--- NOTE: weights magnitude is chosen to be small, about 0.1,
//--- which allows us to avoid oversaturated network.
//--- In 10% of cases we use zero weights.
CMLPBase::MLPRandomizeFull(network);
if(CMath::RandomReal()<=0.1)
network.m_weights.Fill(0.0);
else
{
for(i=0; i<wcount; i++)
network.m_weights.Set(i,0.2*CMath::RandomReal()-0.1);
}
//--- Test MLPError(), MLPErrorSparse(), MLPGrad() for single-element dataset:
//--- * generate input X, output Y, combine them in dataset XY
//---*calculate "reference" error on dataset manually (call MLPProcess and evaluate sum-of-squared errors)
//---*calculate "reference" gradient by performing numerical differentiation of "reference" error
//--- using 4-point differentiation formula
//--- * test error/gradient returned by MLPGrad(), MLPError(), MLPErrorSparse()
xy=matrix<double>::Zeros(1,nin+nout);
CSparse::SparseCreate(1,nin+nout,nin+nout,sparsexy);
for(i=0; i<nin ; i++)
x.Set(i,4*CMath::RandomReal()-2);
for(i_=0; i_<nin ; i_++)
xy.Set(0,i_,x[i_]);
for(i=0; i<nin ; i++)
CSparse::SparseSet(sparsexy,0,i,x[i]);
if(CMLPBase::MLPIsSoftMax(network))
{
for(i=0; i<nout; i++)
y.Set(i,0);
xy.Set(0,nin,CMath::RandomInteger(nout));
CSparse::SparseSet(sparsexy,0,nin,xy.Get(0,nin));
y.Set((int)MathRound(xy.Get(0,nin)),1);
}
else
{
for(i=0; i<nout; i++)
{
y.Set(i,4*CMath::RandomReal()-2);
CSparse::SparseSet(sparsexy,0,nin+i,y[i]);
}
i1_= -nin;
for(i_=nin; i_<nin+nout ; i_++)
xy.Set(0,i_,y[i_+i1_]);
}
CSparse::SparseConvertToCRS(sparsexy);
CMLPBase::MLPProcess(network,x,y2);
y2-=y;
referencee=y2.Dot(y2)/2.0;
for(i=0; i<wcount; i++)
{
wprev=network.m_weights[i];
network.m_weights.Set(i,wprev-2*h);
CMLPBase::MLPProcess(network,x,y1);
y1-=y;
v1=y1.Dot(y1)/2.0;
network.m_weights.Set(i,wprev-h);
CMLPBase::MLPProcess(network,x,y1);
y1-=y;
v2=y1.Dot(y1)/2.0;
network.m_weights.Set(i,wprev+h);
CMLPBase::MLPProcess(network,x,y1);
y1-=y;
v3=y1.Dot(y1)/2.0;
network.m_weights.Set(i,wprev+2*h);
CMLPBase::MLPProcess(network,x,y1);
y1-=y;
v4=y1.Dot(y1)/2.0;
network.m_weights.Set(i,wprev);
referenceg.Set(i,(v1-8*v2+8*v3-v4)/(12*h));
}
CMLPBase::MLPGrad(network,x,y,e,grad2);
CAp::SetErrorFlagDiff(err,e,referencee,etol,escale);
CAp::SetErrorFlagDiff(err,CMLPBase::MLPError(network,xy,1),referencee,etol,escale);
CAp::SetErrorFlagDiff(err,CMLPBase::MLPErrorSparse(network,sparsexy,1),referencee,etol,escale);
CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>etol,"testmlpbaseunit.ap:948");
//--- Test MLPErrorN(), MLPGradN() for single-element dataset:
//--- * generate input X, output Y, combine them in dataset XY
//---*calculate "reference" error on dataset manually (call MLPProcess and evaluate sum-of-squared errors)
//---*calculate "reference" gradient by performing numerical differentiation of "reference" error
//--- * test error/gradient returned by MLPGradN(), MLPErrorN()
//--- NOTE: because we use inexact 2-point formula, we perform gradient test with NonStrictTolerance
xy=matrix<double>::Zeros(1,nin+nout);
for(i=0; i<nin ; i++)
x.Set(i,4*CMath::RandomReal()-2);
for(i_=0; i_<nin ; i_++)
xy.Set(0,i_,x[i_]);
if(CMLPBase::MLPIsSoftMax(network))
{
for(i=0; i<nout; i++)
y.Set(i,0);
xy.Set(0,nin,CMath::RandomInteger(nout));
y.Set((int)MathRound(xy.Get(0,nin)),1);
}
else
{
for(i=0; i<nout; i++)
y.Set(i,4*CMath::RandomReal()-2);
i1_=-nin;
for(i_=nin; i_<nin+nout ; i_++)
xy.Set(0,i_,y[i_+i1_]);
}
CMLPBase::MLPProcess(network,x,y2);
referencee=0;
if(nkind!=1)
{
for(i=0; i<nout; i++)
referencee+=0.5*CMath::Sqr(y2[i]-y[i]);
}
else
{
for(i=0; i<nout; i++)
if(y[i]!=0.0)
{
if(y2[i]==0.0)
referencee+=y[i]*MathLog(CMath::m_maxrealnumber);
else
referencee+=y[i]*MathLog(y[i]/y2[i]);
}
}
for(i=0; i<wcount; i++)
{
wprev=network.m_weights[i];
network.m_weights.Set(i,wprev+h);
CMLPBase::MLPProcess(network,x,y2);
network.m_weights.Set(i,wprev-h);
CMLPBase::MLPProcess(network,x,y1);
network.m_weights.Set(i,wprev);
v=0;
if(nkind!=1)
{
for(j=0; j<nout; j++)
v+=0.5*(CMath::Sqr(y2[j]-y[j])-CMath::Sqr(y1[j]-y[j]))/(2*h);
}
else
{
for(j=0; j<nout; j++)
if(y[j]!=0.0)
{
if((double)(y2[j])==0.0)
v+=y[j]*MathLog(CMath::m_maxrealnumber);
else
v+=y[j]*MathLog(y[j]/y2[j]);
if(y1[j]==0.0)
v-=y[j]*MathLog(CMath::m_maxrealnumber);
else
v-=y[j]*MathLog(y[j]/y1[j]);
}
v=v/(2*h);
}
referenceg.Set(i,v);
}
CMLPBase::MLPGradN(network,x,y,e,grad2);
CAp::SetErrorFlagDiff(err,e,referencee,etol,escale);
CAp::SetErrorFlagDiff(err,CMLPBase::MLPErrorN(network,xy,1),referencee,etol,escale);
CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>nonstricttolerance,"testmlpbaseunit.ap:1029");
//--- Test that gradient calculation functions automatically allocate
//--- space for gradient, if needed.
//--- NOTE: we perform test with empty dataset.
CSparse::SparseCreate(1,nin+nout,0,sparsexy);
CSparse::SparseConvertToCRS(sparsexy);
grad1=vector<double>::Zeros(1);
CMLPBase::MLPGradBatch(network,xy,0,e1,grad1);
CAp::SetErrorFlag(err,CAp::Len(grad1)!=wcount,"testmlpbaseunit.ap:1042");
grad1=vector<double>::Zeros(1);
CMLPBase::MLPGradBatchSparse(network,sparsexy,0,e1,grad1);
CAp::SetErrorFlag(err,CAp::Len(grad1)!=wcount,"testmlpbaseunit.ap:1045");
grad1=vector<double>::Zeros(1);
CMLPBase::MLPGradBatchSubset(network,xy,0,idx,0,e1,grad1);
CAp::SetErrorFlag(err,CAp::Len(grad1)!=wcount,"testmlpbaseunit.ap:1048");
grad1=vector<double>::Zeros(1);
CMLPBase::MLPGradBatchSparseSubset(network,sparsexy,0,idx,0,e1,grad1);
CAp::SetErrorFlag(err,CAp::Len(grad1)!=wcount,"testmlpbaseunit.ap:1051");
//--- Test MLPError(), MLPErrorSparse(), MLPGradBatch(), MLPGradBatchSparse() for many-element dataset:
//--- * generate random dataset XY
//---*calculate "reference" error/gradient using MLPGrad(), which was tested in previous
//--- section and is assumed to work correctly
//--- * test results returned by MLPGradBatch/MLPGradBatchSparse against reference ones
//--- NOTE: about 10% of tests are performed with zero SSize
ssize=sizemin+CMath::RandomInteger(sizemax-sizemin+1);
xy=matrix<double>::Zeros(MathMax(ssize,1),nin+nout);
CSparse::SparseCreate(MathMax(ssize,1),nin+nout,ssize*(nin+nout),sparsexy);
referenceg.Fill(0);
referencee=0;
for(i=0; i<ssize; i++)
{
for(j=0; j<nin ; j++)
{
x1.Set(j,4*CMath::RandomReal()-2);
CSparse::SparseSet(sparsexy,i,j,x1[j]);
}
for(i_=0; i_<nin ; i_++)
xy.Set(i,i_,x1[i_]);
if(CMLPBase::MLPIsSoftMax(network))
{
y1.Fill(0);
xy.Set(i,nin,CMath::RandomInteger(nout));
CSparse::SparseSet(sparsexy,i,nin,xy.Get(i,nin));
y1.Set((int)MathRound(xy.Get(i,nin)),1);
}
else
{
for(j=0; j<nout; j++)
{
y1.Set(j,4*CMath::RandomReal()-2);
CSparse::SparseSet(sparsexy,i,nin+j,y1[j]);
}
i1_=-nin;
for(i_=nin; i_<nin+nout ; i_++)
xy.Set(i,i_,y1[i_+i1_]);
}
CMLPBase::MLPGrad(network,x1,y1,v,grad2);
referencee+=v;
for(i_=0; i_<wcount; i_++)
referenceg.Add(i_,grad2[i_]);
}
CSparse::SparseConvertToCRS(sparsexy);
e2=CMLPBase::MLPError(network,xy,ssize);
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
e2=CMLPBase::MLPErrorSparse(network,sparsexy,ssize);
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
CMLPBase::MLPGradBatch(network,xy,ssize,e2,grad2);
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>etol,"testmlpbaseunit.ap:1104");
CMLPBase::MLPGradBatchSparse(network,sparsexy,ssize,esp,gradsp);
CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale);
CAp::SetErrorFlag(err,VectorDiff(referenceg,gradsp,wcount,gscale)>etol,"testmlpbaseunit.ap:1107");
//--- Test MLPErrorSubset(), MLPGradBatchSubset(), MLPErrorSparseSubset(), MLPGradBatchSparseSubset()
//--- for many-element dataset with different types of subsets:
//--- * generate random dataset XY
//---*"reference" error/gradient are calculated with MLPGradBatch(),
//--- which was tested in previous section and is assumed to work correctly
//--- * we perform tests for different subsets:
//--- * SubsetSize<0 - subset is a full dataset
//--- * SubsetSize=0 - subset is empty
//--- * SubsetSize>0 - random subset
ssize=sizemin+CMath::RandomInteger(sizemax-sizemin+1);
xy=matrix<double>::Zeros(MathMax(ssize,1),nin+nout);
CSparse::SparseCreate(MathMax(ssize,1),nin+nout,ssize*(nin+nout),sparsexy);
for(i=0; i<ssize; i++)
{
for(j=0; j<nin ; j++)
{
x1.Set(j,4*CMath::RandomReal()-2);
CSparse::SparseSet(sparsexy,i,j,x1[j]);
}
for(i_=0; i_<nin ; i_++)
xy.Set(i,i_,x1[i_]);
if(CMLPBase::MLPIsSoftMax(network))
{
for(j=0; j<nout; j++)
y1.Set(j,0);
xy.Set(i,nin,CMath::RandomInteger(nout));
CSparse::SparseSet(sparsexy,i,nin,xy.Get(i,nin));
y1.Set((int)MathRound(xy.Get(i,nin)),1);
}
else
{
for(j=0; j<nout; j++)
{
y1.Set(j,4*CMath::RandomReal()-2);
CSparse::SparseSet(sparsexy,i,nin+j,y1[j]);
}
i1_=-nin;
for(i_=nin; i_<nin+nout ; i_++)
xy.Set(i,i_,y1[i_+i1_]);
}
}
CSparse::SparseConvertToCRS(sparsexy);
if(ssize>0)
{
subsetsize=1+CMath::RandomInteger(10);
xy2=matrix<double>::Zeros(subsetsize,nin+nout);
idx.Resize(subsetsize);
CSparse::SparseCreate(subsetsize,nin+nout,subsetsize*(nin+nout),sparsexy2);
if(CMLPBase::MLPIsSoftMax(network))
rowsize=nin+1;
else
rowsize=nin+nout;
for(i=0; i<=subsetsize-1; i++)
{
k=CMath::RandomInteger(ssize);
idx.Set(i,k);
for(j=0; j<=rowsize-1; j++)
{
xy2.Set(i,j,xy.Get(k,j));
CSparse::SparseSet(sparsexy2,i,j,CSparse::SparseGet(sparsexy,k,j));
}
}
CSparse::SparseConvertToCRS(sparsexy2);
}
else
{
subsetsize=0;
CSparse::SparseCreate(1,nin+nout,0,sparsexy2);
CSparse::SparseConvertToCRS(sparsexy2);
}
CMLPBase::MLPGradBatch(network,xy,ssize,referencee,referenceg);
e2=CMLPBase::MLPErrorSubset(network,xy,ssize,idx,-1);
esp=CMLPBase::MLPErrorSparseSubset(network,sparsexy,ssize,idx,-1);
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale);
CMLPBase::MLPGradBatchSubset(network,xy,ssize,idx,-1,e2,grad2);
CMLPBase::MLPGradBatchSparseSubset(network,sparsexy,ssize,idx,-1,esp,gradsp);
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale);
CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>etol,"testmlpbaseunit.ap:1192");
CAp::SetErrorFlag(err,VectorDiff(referenceg,gradsp,wcount,gscale)>etol,"testmlpbaseunit.ap:1193");
CMLPBase::MLPGradBatch(network,xy,0,referencee,referenceg);
e2=CMLPBase::MLPErrorSubset(network,xy,ssize,idx,0);
esp=CMLPBase::MLPErrorSparseSubset(network,sparsexy,ssize,idx,0);
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale);
CMLPBase::MLPGradBatchSubset(network,xy,ssize,idx,0,e2,grad2);
CMLPBase::MLPGradBatchSparseSubset(network,sparsexy,ssize,idx,0,esp,gradsp);
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale);
CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>etol,"testmlpbaseunit.ap:1205");
CAp::SetErrorFlag(err,VectorDiff(referenceg,gradsp,wcount,gscale)>etol,"testmlpbaseunit.ap:1206");
CMLPBase::MLPGradBatch(network,xy2,subsetsize,referencee,referenceg);
e2=CMLPBase::MLPErrorSubset(network,xy,ssize,idx,subsetsize);
esp=CMLPBase::MLPErrorSparseSubset(network,sparsexy,ssize,idx,subsetsize);
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale);
CMLPBase::MLPGradBatchSubset(network,xy,ssize,idx,subsetsize,e2,grad2);
CMLPBase::MLPGradBatchSparseSubset(network,sparsexy,ssize,idx,subsetsize,esp,gradsp);
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
CAp::SetErrorFlagDiff(err,esp,referencee,etol,escale);
CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>etol,"testmlpbaseunit.ap:1218");
CAp::SetErrorFlag(err,VectorDiff(referenceg,gradsp,wcount,gscale)>etol,"testmlpbaseunit.ap:1219");
//--- Test MLPGradNBatch() for many-element dataset:
//--- * generate random dataset XY
//---*calculate "reference" error/gradient using MLPGrad(), which was tested in previous
//--- section and is assumed to work correctly
//--- * test results returned by MLPGradNBatch against reference ones
ssize=sizemin+CMath::RandomInteger(sizemax-sizemin+1);
xy=matrix<double>::Zeros(ssize,nin+nout);
for(i=0; i<wcount; i++)
referenceg.Set(i,0);
referencee=0;
for(i=0; i<ssize; i++)
{
for(j=0; j<nin ; j++)
x1.Set(j,4*CMath::RandomReal()-2);
for(i_=0; i_<nin ; i_++)
xy.Set(i,i_,x1[i_]);
if(CMLPBase::MLPIsSoftMax(network))
{
for(j=0; j<nout; j++)
y1.Set(j,0);
xy.Set(i,nin,CMath::RandomInteger(nout));
y1.Set((int)MathRound(xy.Get(i,nin)),1);
}
else
{
for(j=0; j<nout; j++)
y1.Set(j,4*CMath::RandomReal()-2);
i1_=-nin;
for(i_=nin; i_<nin+nout ; i_++)
xy.Set(i,i_,y1[i_+i1_]);
}
CMLPBase::MLPGradN(network,x1,y1,v,grad2);
referencee+=v;
for(i_=0; i_<wcount; i_++)
referenceg.Add(i_,grad2[i_]);
}
CMLPBase::MLPGradNBatch(network,xy,ssize,e2,grad2);
CAp::SetErrorFlagDiff(err,e2,referencee,etol,escale);
CAp::SetErrorFlag(err,VectorDiff(referenceg,grad2,wcount,gscale)>etol,"testmlpbaseunit.ap:1258");
}
}
//+------------------------------------------------------------------+
//| Hessian functions test
//+------------------------------------------------------------------+
void CTestMLPBaseUnit::TestHessian(int nkind,int nin,int nhid1,
int nhid2,int nout,
int passcount,bool &err)
{
//--- create variables
CMultilayerPerceptron network;
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;
CMatrixDouble xy;
CMatrixDouble h1;
CMatrixDouble h2;
CRowDouble grad1;
CRowDouble grad2;
CRowDouble grad3;
CRowDouble x;
CRowDouble y;
CRowDouble x1;
CRowDouble x2;
CRowDouble y1;
CRowDouble y2;
double v=0;
double e1=0;
double e2=0;
double wprev=0;
int i_=0;
int i1_=0;
//--- check
if(!CAp::Assert(passcount>=2,"PassCount<2!"))
{
err=true;
return;
}
if(nkind==2)
{
a1=1000*CMath::RandomReal()-500;
a2=2*CMath::RandomReal()-1;
}
if(nkind==3)
{
a1=1000*CMath::RandomReal()-500;
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
}
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
CMLPBase::MLPProperties(network,n1,n2,wcount);
h=0.00001;
etol=0.05;
//--- Initialize
x=vector<double>::Zeros(nin);
x1=vector<double>::Zeros(nin);
x2=vector<double>::Zeros(nin);
y=vector<double>::Zeros(nout);
y1=vector<double>::Zeros(nout);
y2=vector<double>::Zeros(nout);
grad1=vector<double>::Zeros(wcount);
grad2=vector<double>::Zeros(wcount);
grad3=vector<double>::Zeros(wcount);
h1=matrix<double>::Zeros(wcount,wcount);
h2=matrix<double>::Zeros(wcount,wcount);
//--- Process
for(pass=1; pass<=passcount; pass++)
{
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=matrix<double>::Zeros(ssize,nin+nout-1+1);
for(i=0; i<wcount; i++)
grad1.Set(i,0);
for(i=0; i<wcount; i++)
{
for(j=0; j<wcount; j++)
h1.Set(i,j,0);
}
e1=0;
for(i=0; i<ssize; i++)
{
//--- X, Y
for(j=0; j<nin ; j++)
x1.Set(j,4*CMath::RandomReal()-2);
for(i_=0; i_<nin ; i_++)
xy.Set(i,i_,x1[i_]);
if(CMLPBase::MLPIsSoftMax(network))
{
for(j=0; j<nout; j++)
y1.Set(j,0);
xy.Set(i,nin,CMath::RandomInteger(nout));
y1.Set((int)MathRound(xy.Get(i,nin)),1);
}
else
{
for(j=0; j<nout; j++)
y1.Set(j,4*CMath::RandomReal()-2);
i1_=-nin;
for(i_=nin; i_<nin+nout ; i_++)
xy.Set(i,i_,y1[i_+i1_]);
}
//--- E1, Grad1
if(hkind==0)
CMLPBase::MLPGrad(network,x1,y1,v,grad2);
else
CMLPBase::MLPGradN(network,x1,y1,v,grad2);
e1+=v;
for(i_=0; i_<wcount; i_++)
grad1.Add(i_,grad2[i_]);
//--- H1
for(j=0; j<wcount; j++)
{
wprev=network.m_weights[j];
network.m_weights.Set(j,wprev-2*h);
if(hkind==0)
CMLPBase::MLPGrad(network,x1,y1,v,grad2);
else
CMLPBase::MLPGradN(network,x1,y1,v,grad2);
network.m_weights.Set(j,wprev-h);
if(hkind==0)
CMLPBase::MLPGrad(network,x1,y1,v,grad3);
else
CMLPBase::MLPGradN(network,x1,y1,v,grad3);
for(i_=0; i_<wcount; i_++)
grad2.Add(i_,-8*grad3[i_]);
network.m_weights.Set(j,wprev+h);
if(hkind==0)
CMLPBase::MLPGrad(network,x1,y1,v,grad3);
else
CMLPBase::MLPGradN(network,x1,y1,v,grad3);
for(i_=0; i_<wcount; i_++)
grad2.Add(i_,8*grad3[i_]);
network.m_weights.Set(j,wprev+2*h);
if(hkind==0)
CMLPBase::MLPGrad(network,x1,y1,v,grad3);
else
CMLPBase::MLPGradN(network,x1,y1,v,grad3);
for(i_=0; i_<wcount; i_++)
grad2.Add(i_,-grad3[i_]);
v=1.0/(12.0*h);
for(i_=0; i_<wcount; i_++)
h1.Add(j,i_,v*grad2[i_]);
network.m_weights.Set(j,wprev);
}
}
if(hkind==0)
CMLPBase::MLPHessianBatch(network,xy,ssize,e2,grad2,h2);
else
CMLPBase::MLPHessianNBatch(network,xy,ssize,e2,grad2,h2);
CAp::SetErrorFlag(err,(MathAbs(e1-e2)/e1)>etol,"testmlpbaseunit.ap:1440");
for(i=0; i<wcount; i++)
{
if(MathAbs(grad1[i])>1.0E-2)
CAp::SetErrorFlag(err,MathAbs((grad2[i]-grad1[i])/grad1[i])>etol,"testmlpbaseunit.ap:1443");
else
CAp::SetErrorFlag(err,MathAbs(grad2[i]-grad1[i])>etol,"testmlpbaseunit.ap:1445");
}
for(i=0; i<wcount; i++)
for(j=0; j<wcount; j++)
{
if(MathAbs(h1.Get(i,j))>5.0E-2)
CAp::SetErrorFlag(err,MathAbs((h1.Get(i,j)-h2.Get(i,j))/h1.Get(i,j))>etol,"testmlpbaseunit.ap:1449");
else
CAp::SetErrorFlag(err,MathAbs(h2.Get(i,j)-h1.Get(i,j))>etol,"testmlpbaseunit.ap:1451");
}
}
}
}
//+------------------------------------------------------------------+
//| Error functions (other than MLPError and MLPErrorN) test. |
//| Network of type NKind is created, with NIn inputs, NHid1*NHid2 |
//| hidden layers (one layer if NHid2 = 0), NOut outputs. PassCount |
//| random passes is performed. Dataset has random size in |
//| [SizeMin, SizeMax]. |
//+------------------------------------------------------------------+
void CTestMLPBaseUnit::TestErr(int nkind,int nin,int nhid1,
int nhid2,int nout,int passcount,
int sizemin,int sizemax,bool &err)
{
//--- create variables
CMultilayerPerceptron network;
CSparseMatrix sparsexy;
int n1=0;
int n2=0;
int wcount=0;
double etol=0;
double escale=0;
double a1=0;
double a2=0;
int pass=0;
int i=0;
int j=0;
int k=0;
int ssize=0;
int subsetsize=0;
CMatrixDouble xy;
CRowDouble y;
CRowDouble x1;
CRowDouble y1;
CRowInt idx;
CRowInt dummy;
double refrmserror=0;
double refclserror=0;
double refrelclserror=0;
double refavgce=0;
double refavgerror=0;
double refavgrelerror=0;
int avgrelcnt=0;
CModelErrors allerrors;
int nnmax=0;
int dsmax=0;
double relclstolerance=0;
int i_=0;
int i1_=0;
if(nkind==2)
{
a1=1000*CMath::RandomReal()-500;
a2=2*CMath::RandomReal()-1;
}
if(nkind==3)
{
a1=1000*CMath::RandomReal()-500;
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
}
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
CMLPBase::MLPProperties(network,n1,n2,wcount);
etol=1.0E-4;
escale=1.0E-2;
//--- Initialize
x1=vector<double>::Zeros(nin);
y=vector<double>::Zeros(nout);
y1=vector<double>::Zeros(nout);
//--- Process
for(pass=1; pass<=passcount; pass++)
{
//--- Randomize network, then re-randomaze weights manually.
//--- NOTE: weights magnitude is chosen to be small, about 0.1,
//--- which allows us to avoid oversaturated network.
//--- In 10% of cases we use zero weights.
CMLPBase::MLPRandomizeFull(network);
if(CMath::RandomReal()<=0.1)
network.m_weights.Fill(0.0);
else
{
for(i=0; i<wcount; i++)
network.m_weights.Set(i,0.2*CMath::RandomReal()-0.1);
}
//--- Generate random dataset.
//--- Calculate reference errors.
//--- NOTE: about 10% of tests are performed with zero SSize
ssize=sizemin+CMath::RandomInteger(sizemax-sizemin+1);
if(CMLPBase::MLPIsSoftMax(network))
{
xy=matrix<double>::Zeros(MathMax(ssize,1),nin+1);
CSparse::SparseCreate(MathMax(ssize,1),nin+1,0,sparsexy);
}
else
{
xy=matrix<double>::Zeros(MathMax(ssize,1),nin+nout);
CSparse::SparseCreate(MathMax(ssize,1),nin+nout,0,sparsexy);
}
refrmserror=0.0;
refclserror=0.0;
refavgce=0.0;
refavgerror=0.0;
refavgrelerror=0.0;
avgrelcnt=0;
for(i=0; i<ssize; i++)
{
//--- Fill I-th row
for(j=0; j<nin ; j++)
{
x1.Set(j,4*CMath::RandomReal()-2);
CSparse::SparseSet(sparsexy,i,j,x1[j]);
}
for(i_=0; i_<nin ; i_++)
xy.Set(i,i_,x1[i_]);
if(CMLPBase::MLPIsSoftMax(network))
{
for(j=0; j<nout; j++)
y1.Set(j,0);
xy.Set(i,nin,CMath::RandomInteger(nout));
CSparse::SparseSet(sparsexy,i,nin,xy.Get(i,nin));
y1.Set((int)MathRound(xy.Get(i,nin)),1);
}
else
{
for(j=0; j<nout; j++)
{
y1.Set(j,4*CMath::RandomReal()-2);
if(y1[j]>=0.0)
y1.Add(j,0.1);
else
y1.Add(j,-0.1);
CSparse::SparseSet(sparsexy,i,nin+j,y1[j]);
}
i1_=-nin;
for(i_=nin; i_<nin+nout ; i_++)
xy.Set(i,i_,y1[i_+i1_]);
}
//--- Process
CMLPBase::MLPProcess(network,x1,y);
//--- Update reference errors
nnmax=0;
if(CMLPBase::MLPIsSoftMax(network))
{
if(y[(int)MathRound(xy.Get(i,nin))]>0.0)
refavgce+=MathLog(1.0/y[(int)MathRound(xy.Get(i,nin))]);
else
refavgce+=MathLog(CMath::m_maxrealnumber);
}
if(CMLPBase::MLPIsSoftMax(network))
dsmax=(int)MathRound(xy.Get(i,nin));
else
dsmax=0;
for(j=0; j<nout; j++)
{
refrmserror+=CMath::Sqr(y[j]-y1[j]);
refavgerror+=MathAbs(y[j]-y1[j]);
if(y1[j]!=0.0)
{
refavgrelerror+=MathAbs(y[j]-y1[j])/MathAbs(y1[j]);
avgrelcnt++;
}
if(y[j]>y[nnmax])
nnmax=j;
if(!CMLPBase::MLPIsSoftMax(network) && y1[j]>y1[dsmax])
dsmax=j;
}
if(nnmax!=dsmax)
refclserror++;
}
CSparse::SparseConvertToCRS(sparsexy);
if(ssize>0)
{
refrmserror=MathSqrt(refrmserror/(ssize*nout));
refavgerror/=(double)(ssize*nout);
refrelclserror=refclserror/ssize;
refavgce/=(ssize*MathLog(2.0));
}
else
refrelclserror=0.0;
if(avgrelcnt>0)
refavgrelerror/=(double)avgrelcnt;
//--- Test "continuous" errors on full dataset
CAp::SetErrorFlagDiff(err,CMLPBase::MLPRMSError(network,xy,ssize),refrmserror,etol,escale);
CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgCE(network,xy,ssize),refavgce,etol,escale);
CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgError(network,xy,ssize),refavgerror,etol,escale);
CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgRelError(network,xy,ssize),refavgrelerror,etol,escale);
CAp::SetErrorFlagDiff(err,CMLPBase::MLPRMSErrorSparse(network,sparsexy,ssize),refrmserror,etol,escale);
CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgCESparse(network,sparsexy,ssize),refavgce,etol,escale);
CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgErrorSparse(network,sparsexy,ssize),refavgerror,etol,escale);
CAp::SetErrorFlagDiff(err,CMLPBase::MLPAvgRelErrorSparse(network,sparsexy,ssize),refavgrelerror,etol,escale);
CMLPBase::MLPAllErrorsSubset(network,xy,ssize,dummy,-1,allerrors);
CAp::SetErrorFlagDiff(err,allerrors.m_AvgCE,refavgce,etol,escale);
CAp::SetErrorFlagDiff(err,allerrors.m_RMSError,refrmserror,etol,escale);
CAp::SetErrorFlagDiff(err,allerrors.m_AvgError,refavgerror,etol,escale);
CAp::SetErrorFlagDiff(err,allerrors.m_AvgRelError,refavgrelerror,etol,escale);
CMLPBase::MLPAllErrorsSparseSubset(network,sparsexy,ssize,dummy,-1,allerrors);
CAp::SetErrorFlagDiff(err,allerrors.m_AvgCE,refavgce,etol,escale);
CAp::SetErrorFlagDiff(err,allerrors.m_RMSError,refrmserror,etol,escale);
CAp::SetErrorFlagDiff(err,allerrors.m_AvgError,refavgerror,etol,escale);
CAp::SetErrorFlagDiff(err,allerrors.m_AvgRelError,refavgrelerror,etol,escale);
//--- Test errors on dataset given by subset.
//--- We perform only limited test for RMS error, assuming that either all errors
//--- are calculated correctly (subject to subset given by Idx) - or none of them.
if(ssize>0)
subsetsize=CMath::RandomInteger(10);
else
subsetsize=0;
idx.Resize(subsetsize);
refrmserror=0.0;
for(i=0; i<=subsetsize-1; i++)
{
k=CMath::RandomInteger(ssize);
idx.Set(i,k);
for(i_=0; i_<nin ; i_++)
x1.Set(i_,xy.Get(k,i_));
if(CMLPBase::MLPIsSoftMax(network))
{
for(j=0; j<nout; j++)
y1.Set(j,0);
y1.Set((int)MathRound(xy.Get(k,nin)),1);
}
else
{
for(j=0; j<nout; j++)
y1.Set(j,xy.Get(k,nin+j));
}
CMLPBase::MLPProcess(network,x1,y);
for(j=0; j<nout; j++)
refrmserror+=CMath::Sqr(y[j]-y1[j]);
}
if(subsetsize>0)
refrmserror=MathSqrt(refrmserror/(subsetsize*nout));
CMLPBase::MLPAllErrorsSubset(network,xy,ssize,idx,subsetsize,allerrors);
CAp::SetErrorFlagDiff(err,allerrors.m_RMSError,refrmserror,etol,escale);
CMLPBase::MLPAllErrorsSparseSubset(network,sparsexy,ssize,idx,subsetsize,allerrors);
CAp::SetErrorFlagDiff(err,allerrors.m_RMSError,refrmserror,etol,escale);
//--- Test "discontinuous" error function.
//--- Even slight changes in the network output may force these functions
//--- to change by 1. So, we test them with relaxed criteria, corresponding to
//--- difference in classification of two samples.
if(ssize>0)
{
relclstolerance=2.5/ssize;
CAp::SetErrorFlag(err,MathAbs(CMLPBase::MLPClsError(network,xy,ssize)-refclserror)>(ssize*relclstolerance),"testmlpbaseunit.ap:1732");
CAp::SetErrorFlag(err,MathAbs(CMLPBase::MLPRelClsError(network,xy,ssize)-refrelclserror)>relclstolerance,"testmlpbaseunit.ap:1733");
CAp::SetErrorFlag(err,MathAbs(CMLPBase::MLPRelClsErrorSparse(network,sparsexy,ssize)-refrelclserror)>relclstolerance,"testmlpbaseunit.ap:1734");
}
}
}
//+------------------------------------------------------------------+
//| Special tests |
//+------------------------------------------------------------------+
void CTestMLPBaseUnit::SpecTests(bool &inferrors,bool &procerrors,
bool &graderrors,bool &hesserrors,
bool &errerrors)
{
//--- create variables
CMultilayerPerceptron net;
CMatrixDouble xy;
double f=0;
CRowDouble g;
int i=0;
//--- Special test for overflow in TanH:
//--- * create 1x1x1 linear network
//--- * create dataset with 1 item: [x, y] = .Get(0, 1)
//--- * set network weights to [10000000, 10000000, 10000000, 10000000]
//--- * check that error function is finite
//--- * check that gradient is finite
CMLPBase::MLPCreate1(1,1,1,net);
xy=matrix<double>::Zeros(1,2);
xy.Set(0,1,1.0);
for(i=0; i<=CMLPBase::MLPGetWeightsCount(net)-1; i++)
net.m_weights.Set(i,10000000.0);
CMLPBase::MLPGradBatch(net,xy,1,f,g);
CAp::SetErrorFlag(graderrors,!MathIsValidNumber(f),"testmlpbaseunit.ap:1907");
CAp::SetErrorFlag(graderrors,!MathIsValidNumber(CMLPBase::MLPError(net,xy,1)),"testmlpbaseunit.ap:1908");
for(i=0; i<CMLPBase::MLPGetWeightsCount(net); i++)
CAp::SetErrorFlag(graderrors,!MathIsValidNumber(g[i]),"testmlpbaseunit.ap:1910");
//--- Special test for overflow in SOFTMAX layer:
//--- * create 1x1x2 classifier network
//--- * create dataset with 1 item: [x, y] = .Get(0, 1)
//--- * set network weights to [10000000, 10000000, 10000000, 10000000]
//--- * check that error function is finite
//--- * check that gradient is finite
CMLPBase::MLPCreateC1(1,1,2,net);
xy=matrix<double>::Zeros(1,2);
xy.Set(0,1,1.0);
for(i=0; i<CMLPBase::MLPGetWeightsCount(net); i++)
net.m_weights.Set(i,10000000.0);
CMLPBase::MLPGradBatch(net,xy,1,f,g);
CAp::SetErrorFlag(graderrors,!MathIsValidNumber(f),"testmlpbaseunit.ap:1928");
CAp::SetErrorFlag(graderrors,!MathIsValidNumber(CMLPBase::MLPError(net,xy,1)),"testmlpbaseunit.ap:1929");
for(i=0; i<=CMLPBase::MLPGetWeightsCount(net)-1; i++)
CAp::SetErrorFlag(graderrors,!MathIsValidNumber(g[i]),"testmlpbaseunit.ap:1931");
}
//+------------------------------------------------------------------+
//| The function test functions MLPGradBatchMasked and |
//| MLPGradBatchSparseMasked. |
//+------------------------------------------------------------------+
bool CTestMLPBaseUnit::TestMLPGBSubset(void)
{
//--- create variables
bool result;
CMultilayerPerceptron net;
CMatrixDouble a;
CMatrixDouble parta;
CSparseMatrix sa;
CSparseMatrix partsa;
CRowInt idx;
double e1=0;
double e2=0;
CRowDouble grad1;
CRowDouble grad2;
int nin=0;
int nout=0;
int w=0;
int wcount=0;
int nhid1=0;
int nhid2=0;
int nkind=0;
double a1=0;
double a2=0;
int n1=0;
int n2=0;
int ssize=0;
int maxssize=0;
int sbsize=0;
int nvar=0;
int variant=0;
int i=0;
int j=0;
int i_=0;
//--- Variant:
//--- * 1 - there are all rows;
//--- * 2 - there are no one rows;
//--- * 3 - there are some random rows.
nvar=3;
maxssize=96;
for(ssize=0; ssize<=maxssize; ssize++)
{
idx.Resize(ssize);
nkind=CMath::RandomInteger(4);
a1=0;
a2=0;
if(nkind==2)
{
a1=1000*CMath::RandomReal()-500;
a2=2*CMath::RandomReal()-1;
}
if(nkind==3)
{
a1=1000*CMath::RandomReal()-500;
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
}
nin=CMath::RandomInteger(20)+1;
nhid1=CMath::RandomInteger(5);
if(nhid1==0)
nhid2=0;
else
nhid2=CMath::RandomInteger(5);
nout=CMath::RandomInteger(20)+2;
CreateNetwork(net,nkind,a1,a2,nin,nhid1,nhid2,nout);
CMLPBase::MLPProperties(net,n1,n2,wcount);
if(CMLPBase::MLPIsSoftMax(net))
{
w=nin+1;
if(ssize>0)
{
a=matrix<double>::Zeros(ssize,w);
CSparse::SparseCreate(ssize,w,ssize*w,sa);
}
else
{
a.Resize(0,0);
CSparse::SparseCreate(1,1,0,sa);
}
for(i=0; i<ssize; i++)
{
for(j=0; j<w ; j++)
{
a.Set(i,j,2*CMath::RandomReal()-1);
CSparse::SparseSet(sa,i,j,a.Get(i,j));
}
}
for(i=0; i<ssize; i++)
{
a.Set(i,nin,CMath::RandomInteger(nout));
CSparse::SparseSet(sa,i,nin,a.Get(i,nin));
}
}
else
{
w=nin+nout;
if(ssize>0)
{
a=matrix<double>::Zeros(ssize,w);
CSparse::SparseCreate(ssize,w,ssize*w,sa);
}
else
{
a.Resize(0,0);
CSparse::SparseCreate(1,1,0,sa);
}
for(i=0; i<ssize; i++)
{
for(j=0; j<w ; j++)
{
a.Set(i,j,2*CMath::RandomReal()-1);
CSparse::SparseSet(sa,i,j,a.Get(i,j));
}
}
}
CSparse::SparseConvertToCRS(sa);
for(variant=1; variant<=nvar; variant++)
{
sbsize=-1;
if(variant==1)
{
sbsize=ssize;
for(i=0; i<sbsize ; i++)
idx.Set(i,i);
}
if(variant==2)
sbsize=0;
if(variant==3)
{
if(ssize==0)
sbsize=0;
else
sbsize=CMath::RandomInteger(ssize);
for(i=0; i<sbsize ; i++)
idx.Set(i,CMath::RandomInteger(ssize));
}
//--- check
if(!CAp::Assert(sbsize>=0,"mlpbase test: integrity check failed"))
return(true);
if(sbsize!=0)
{
parta=matrix<double>::Zeros(sbsize,w);
CSparse::SparseCreate(sbsize,w,sbsize*w,partsa);
}
else
{
parta.Resize(0,0);
CSparse::SparseCreate(1,1,0,partsa);
}
for(i=0; i<sbsize ; i++)
{
for(i_=0; i_<w ; i_++)
parta.Set(i,i_,a.Get(idx[i],i_));
for(j=0; j<w ; j++)
CSparse::SparseSet(partsa,i,j,parta.Get(i,j));
}
CSparse::SparseConvertToCRS(partsa);
CMLPBase::MLPGradBatch(net,parta,sbsize,e1,grad1);
CMLPBase::MLPGradBatchSubset(net,a,ssize,idx,sbsize,e2,grad2);
//--- Test for dense matrix
if(MathAbs(e1-e2)>1.0E-6)
{
result=true;
return(result);
}
for(i=0; i<wcount; i++)
{
if(MathAbs(grad1[i]-grad2[i])>1.0E-6)
{
result=true;
return(result);
}
}
//--- Test for sparse matrix
CMLPBase::MLPGradBatchSparse(net,partsa,sbsize,e1,grad1);
CMLPBase::MLPGradBatchSparseSubset(net,sa,ssize,idx,sbsize,e2,grad2);
if(MathAbs(e1-e2)>1.0E-6)
{
result=true;
return(result);
}
for(i=0; i<wcount; i++)
{
if(MathAbs(grad1[i]-grad2[i])>1.0E-6)
{
result=true;
return(result);
}
}
}
}
//--- return result
result=false;
return(result);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestFiltersUnit
{
public:
static bool CTestFiltersUnit::TestFilters(bool silent);
private:
static bool TestSMA(bool issilent);
static bool TestEMA(bool issilent);
static bool TestLRMA(bool issilent);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestFiltersUnit::TestFilters(bool silent)
{
//--- create variables
bool result;
bool waserrors;
bool smaerrors;
bool emaerrors;
bool lrmaerrors;
smaerrors=TestSMA(silent);
emaerrors=TestEMA(silent);
lrmaerrors=TestLRMA(silent);
//--- Final report
waserrors=(smaerrors||emaerrors)||lrmaerrors;
if(!silent)
{
Print("FILTERS TEST");
PrintResult("* SMA",!smaerrors);
PrintResult("* EMA",!emaerrors);
PrintResult("* LRMA",!lrmaerrors);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| This function tests SMA(k) filter. It returns True on error. |
//| Additional IsSilent parameter controls detailed error reporting. |
//+------------------------------------------------------------------+
bool CTestFiltersUnit::TestSMA(bool issilent)
{
//--- create variables
bool result;
CRowDouble x;
bool precomputederrors;
bool zerohandlingerrors;
double threshold=1000*CMath::m_machineepsilon;
if(!issilent)
Print("SMA(K) TEST");
//--- Test several pre-computed problems.
//--- NOTE: tests below rely on the fact that floating point
//--- additions and subtractions are exact when dealing
//--- with integer values.
precomputederrors=false;
x=vector<double>::Full(1,7);
CFilters::FilterSMA(x,1,1);
precomputederrors=precomputederrors||x[0]!=7.0;
x=vector<double>::Zeros(3);
x.Set(0,7);
x.Set(1,8);
x.Set(2,9);
CFilters::FilterSMA(x,3,1);
precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=8.0||x[2]!=9.0);
CFilters::FilterSMA(x,3,2);
precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=7.5||x[2]!=8.5);
x=vector<double>::Zeros(3);
x.Set(0,7);
x.Set(1,8);
x.Set(2,9);
CFilters::FilterSMA(x,3,4);
precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=7.5||x[2]!=8.0);
//--- Test zero-handling:
//--- a) when we have non-zero sequence (N1 elements) followed by zero sequence
//--- (N2 elements), then first N1+K-1 elements of the processed sequence are
//--- non-zero, but elements since (N1+K)th must be exactly zero.
//--- b) similar property holds for zero sequence followed by non-zero one
//--- Naive implementation of SMA does not have such property.
//--- NOTE: it is important to initialize X with non-integer elements with long
//--- binary mantissas, because this test tries to test behaviour in the presence
//--- of roundoff errors, and it will be useless when used with integer inputs.
zerohandlingerrors=false;
x=vector<double>::Zeros(10);
x.Set(0,MathSqrt(2));
x.Set(1,MathSqrt(3));
x.Set(2,MathSqrt(5));
x.Set(3,MathSqrt(6));
x.Set(4,MathSqrt(7));
CFilters::FilterSMA(x,10,3);
zerohandlingerrors=zerohandlingerrors||MathAbs(x[0]-MathSqrt(2))>threshold;
zerohandlingerrors=zerohandlingerrors||MathAbs(x[1]-(MathSqrt(2)+MathSqrt(3))/2)>threshold;
zerohandlingerrors=zerohandlingerrors||MathAbs(x[2]-(MathSqrt(2)+MathSqrt(3)+MathSqrt(5))/3)>threshold;
zerohandlingerrors=zerohandlingerrors||MathAbs(x[3]-(MathSqrt(3)+MathSqrt(5)+MathSqrt(6))/3)>threshold;
zerohandlingerrors=zerohandlingerrors||MathAbs(x[4]-(MathSqrt(5)+MathSqrt(6)+MathSqrt(7))/3)>threshold;
zerohandlingerrors=zerohandlingerrors||MathAbs(x[5]-(MathSqrt(6)+MathSqrt(7))/3)>threshold;
zerohandlingerrors=zerohandlingerrors||MathAbs(x[6]-MathSqrt(7)/3)>threshold;
zerohandlingerrors=zerohandlingerrors||x[7]!=0.0;
zerohandlingerrors=zerohandlingerrors||x[8]!=0.0;
zerohandlingerrors=zerohandlingerrors||x[9]!=0.0;
x.Set(0,0);
x.Set(1,0);
x.Set(2,0);
x.Set(3,0);
x.Set(4,0);
x.Set(5,MathSqrt(2));
x.Set(6,MathSqrt(3));
x.Set(7,MathSqrt(5));
x.Set(8,MathSqrt(6));
x.Set(9,MathSqrt(7));
CFilters::FilterSMA(x,10,3);
zerohandlingerrors=zerohandlingerrors||x[0]!=0.0;
zerohandlingerrors=zerohandlingerrors||x[1]!=0.0;
zerohandlingerrors=zerohandlingerrors||x[2]!=0.0;
zerohandlingerrors=zerohandlingerrors||x[3]!=0.0;
zerohandlingerrors=zerohandlingerrors||x[4]!=0.0;
zerohandlingerrors=zerohandlingerrors||MathAbs(x[5]-MathSqrt(2)/3)>threshold;
zerohandlingerrors=zerohandlingerrors||MathAbs(x[6]-(MathSqrt(2)+MathSqrt(3))/3)>threshold;
zerohandlingerrors=zerohandlingerrors||MathAbs(x[7]-(MathSqrt(2)+MathSqrt(3)+MathSqrt(5))/3)>threshold;
zerohandlingerrors=zerohandlingerrors||MathAbs(x[8]-(MathSqrt(3)+MathSqrt(5)+MathSqrt(6))/3)>threshold;
zerohandlingerrors=zerohandlingerrors||MathAbs(x[9]-(MathSqrt(5)+MathSqrt(6)+MathSqrt(7))/3)>threshold;
//--- Final result
result=precomputederrors||zerohandlingerrors;
return(result);
}
//+------------------------------------------------------------------+
//| This function tests EMA(alpha) filter. It returns True on error. |
//| Additional IsSilent parameter controls detailed error reporting. |
//+------------------------------------------------------------------+
bool CTestFiltersUnit::TestEMA(bool issilent)
{
//--- create variables
CRowDouble x;
bool precomputederrors;
if(!issilent)
Print("EMA(alpha) TEST");
//--- Test several pre-computed problems.
//--- NOTE: tests below rely on the fact that floating point
//--- additions and subtractions are exact when dealing
//--- with integer values.
precomputederrors=false;
x=vector<double>::Full(1,7);
CFilters::FilterEMA(x,1,1.0);
precomputederrors=precomputederrors||x[0]!=7.0;
CFilters::FilterEMA(x,1,0.5);
precomputederrors=precomputederrors||x[0]!=7.0;
x=vector<double>::Zeros(3);
x.Set(0,7);
x.Set(1,8);
x.Set(2,9);
CFilters::FilterEMA(x,3,1.0);
precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=8.0||x[2]!=9.0);
CFilters::FilterEMA(x,3,0.5);
precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=7.5||x[2]!=8.25);
//--- Final result
return(precomputederrors);
}
//+------------------------------------------------------------------+
//| This function tests LRMA(k) filter. It returns True on error. |
//| Additional IsSilent parameter controls detailed error reporting. |
//+------------------------------------------------------------------+
bool CTestFiltersUnit::TestLRMA(bool issilent)
{
//--- create variables
CRowDouble x;
bool precomputederrors=false;
double threshold=1000*CMath::m_machineepsilon;
if(!issilent)
Print("LRMA(K) TEST");
//--- First, check that filter does not changes points for K=1 or K=2
x=vector<double>::Full(1,7);
CFilters::FilterLRMA(x,1,1);
precomputederrors=precomputederrors||x[0]!=7.0;
x=vector<double>::Zeros(6);
x.Set(0,7);
x.Set(1,8);
x.Set(2,9);
x.Set(3,10);
x.Set(4,11);
x.Set(5,12);
CFilters::FilterLRMA(x,6,1);
precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=8.0||x[2]!=9.0||x[3]!=10.0||x[4]!=11.0||x[5]!=12.0);
CFilters::FilterLRMA(x,6,2);
precomputederrors=(precomputederrors||x[0]!=7.0||x[1]!=8.0||x[2]!=9.0||x[3]!=10.0||x[4]!=11.0||x[5]!=12.0);
//--- Check several precomputed problems
x=vector<double>::Zeros(6);
x.Set(0,7);
x.Set(1,8);
x.Set(2,9);
x.Set(3,10);
x.Set(4,11);
x.Set(5,12);
CFilters::FilterLRMA(x,6,3);
precomputederrors=precomputederrors||MathAbs(x[0]-7)>threshold;
precomputederrors=precomputederrors||MathAbs(x[1]-8)>threshold;
precomputederrors=precomputederrors||MathAbs(x[2]-9)>threshold;
precomputederrors=precomputederrors||MathAbs(x[3]-10)>threshold;
precomputederrors=precomputederrors||MathAbs(x[4]-11)>threshold;
precomputederrors=precomputederrors||MathAbs(x[5]-12)>threshold;
x=vector<double>::Zeros(6);
x.Set(0,7);
x.Set(1,8);
x.Set(2,8);
x.Set(3,9);
x.Set(4,12);
x.Set(5,12);
CFilters::FilterLRMA(x,6,3);
precomputederrors=precomputederrors||MathAbs(x[0]-7.0000000000)>(1.0E-5);
precomputederrors=precomputederrors||MathAbs(x[1]-8.0000000000)>(1.0E-5);
precomputederrors=precomputederrors||MathAbs(x[2]-8.1666666667)>(1.0E-5);
precomputederrors=precomputederrors||MathAbs(x[3]-8.8333333333)>(1.0E-5);
precomputederrors=precomputederrors||MathAbs(x[4]-11.6666666667)>(1.0E-5);
precomputederrors=precomputederrors||MathAbs(x[5]-12.5000000000)>(1.0E-5);
//--- Final result
return(precomputederrors);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestSSAUnit
{
public:
static bool CTestSSAUnit::TestSSA(bool silent);
private:
static void TestGeneral(bool &errorflag);
static void TestSpecial(bool&errorflag);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestSSAUnit::TestSSA(bool silent)
{
//--- create variables
bool result;
bool specerrors;
bool generrors;
bool waserrors;
specerrors=false;
generrors=false;
TestSpecial(specerrors);
TestGeneral(generrors);
//--- Final report
waserrors=specerrors||generrors;
if(!silent)
{
Print("SSA TEST");
PrintResult("* GENERAL TEST SUITE",!generrors)
PrintResult("* SPECIAL CASES",!specerrors);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| This function tests SSA on several general purpose analysis / |
//| prediction problems. |
//| On failure sets ErrorFlag, on success it is untouched. |
//+------------------------------------------------------------------+
void CTestSSAUnit::TestGeneral(bool &errorflag)
{
//--- create variables
int i=0;
int j=0;
int k=0;
double v=0;
double vv=0;
int pass=0;
CSSAModel state;
CSSAModel state2;
int ntracks=0;
int windowwidth=0;
int nlasttracklen=0;
int nzeros=0;
int nlinear=0;
double sinefreq=0;
double sineoffs=0;
double sineamp=0;
int windowwidth2=0;
int nbasis=0;
int nbasis2=0;
int ninitial=0;
CRowDouble x;
CRowDouble x2;
CRowDouble trend;
CRowDouble noise;
CRowDouble sv;
CRowDouble sv2;
CRowDouble tmp0;
CRowDouble trend2;
CRowDouble noise2;
int algotype=0;
int nticks=0;
int nnoise=0;
int navg=0;
CMatrixDouble a;
CMatrixDouble a2;
CMatrixDouble b;
double tol=0;
int datalen=0;
int forecastlen=0;
int mlimit=0;
int passcount=0;
double skipprob=0;
int i_=0;
int i1_=0;
CHighQualityRandState rs;
//--- Initialize RNG, test pass count and skip probability.
//--- When we perform several sequential tests on the same model, we may
//--- skip some of them with probability SkipProb in order to make sure
//--- that no carry-over effect is observed between tests.
CHighQualityRand::HQRndRandomize(rs);
passcount=500;
skipprob=0.50;
//--- Iterate over several algorithm types.
//--- Algorithms unsupported by tests are skipped within tests.
for(algotype=1; algotype<=3; algotype++)
{
//--- Test that on perfectly constant dataset SSA correctly predicts
//--- perfectly constant trend. Additionally test that analysis phase
//--- correctly returns nearly-zero noise and nearly-constant trend.
//--- Dataset is a one/few sequences with different constants; top-1
//--- algorithm is used (or precomputed unit-normalized vector of 1's).
for(pass=1; pass<=passcount; pass++)
{
nlasttracklen=-9999999;
ntracks=1+CHighQualityRand::HQRndUniformI(rs,3);
windowwidth=2+CHighQualityRand::HQRndUniformI(rs,3);
CSSA::SSACreate(state);
CSSA::SSASetWindow(state,windowwidth);
for(k=0; k<ntracks ; k++)
{
nlasttracklen=windowwidth+CHighQualityRand::HQRndUniformI(rs,windowwidth);
v=CHighQualityRand::HQRndNormal(rs);
x=vector<double>::Zeros(nlasttracklen);
for(i=0; i<nlasttracklen; i++)
x.Set(i,v);
CSSA::SSAAddSequence(state,x,nlasttracklen);
}
switch(algotype)
{
case 1:
b=matrix<double>::Zeros(windowwidth,1);
for(i=0; i<windowwidth ; i++)
b.Set(i,0,1.0/MathSqrt(windowwidth));
CSSA::SSASetAlgoPrecomputed(state,b,windowwidth,1);
break;
case 2:
CSSA::SSASetAlgoTopKDirect(state,1);
break;
case 3:
CSSA::SSASetAlgoTopKRealtime(state,1);
break;
default:
CAp::Assert(false);
errorflag=true;
return;
}
tol=1.0E-6;
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
CSSA::SSAAnalyzeLastWindow(state,trend,noise,nticks);
CAp::SetErrorFlag(errorflag,nticks!=windowwidth,"testssaunit.ap:143");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:144");
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:145");
if(errorflag)
return;
for(i=0; i<windowwidth ; i++)
{
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[nlasttracklen+i-windowwidth])>tol,"testssaunit.ap:150");
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:151");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
CSSA::SSAAnalyzeLast(state,nlasttracklen,trend,noise);
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nlasttracklen,"testssaunit.ap:157");
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nlasttracklen,"testssaunit.ap:158");
if(errorflag)
return;
for(i=0; i<nlasttracklen; i++)
{
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[i])>tol,"testssaunit.ap:163");
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:164");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
CSSA::SSAForecastLast(state,nticks,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:172");
if(errorflag)
return;
for(i=0; i<CAp::Len(trend) ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[nlasttracklen-1])>tol,"testssaunit.ap:176");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
navg=1+CHighQualityRand::HQRndUniformI(rs,windowwidth+3);
CSSA::SSAForecastAvgLast(state,navg,nticks,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:184");
if(errorflag)
return;
for(i=0; i<CAp::Len(trend) ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[nlasttracklen-1])>tol,"testssaunit.ap:188");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
datalen=windowwidth+CHighQualityRand::HQRndUniformI(rs,10);
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
x=vector<double>::Zeros(datalen);
x.Set(0,CHighQualityRand::HQRndNormal(rs));
for(i=1; i<datalen ; i++)
x.Set(i,x[i-1]);
CSSA::SSAForecastSequence(state,x,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:200");
if(errorflag)
return;
for(i=0; i<forecastlen; i++)
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[datalen-1])>tol,"testssaunit.ap:204");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
datalen=windowwidth+CHighQualityRand::HQRndUniformI(rs,10);
navg=1+CHighQualityRand::HQRndUniformI(rs,windowwidth+3);
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
x=vector<double>::Zeros(datalen);
x.Set(0,CHighQualityRand::HQRndNormal(rs));
for(i=1; i<datalen ; i++)
x.Set(i,x[i-1]);
CSSA::SSAForecastAvgSequence(state,x,datalen,navg,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:217");
if(errorflag)
return;
for(i=0; i<forecastlen ; i++)
CAp::SetErrorFlag(errorflag,(double)(MathAbs(trend[i]-x[datalen-1]))>tol,"testssaunit.ap:221");
}
}
//--- Test that on specially designed linear dataset SSA correctly
//--- predicts perfectly linear trend. Additionally test that analysis
//--- phase correctly returns nearly-zero noise and nearly-linear trend.
//--- Also test that correct basis vectors are returned.
//--- Dataset consists of many WindowWidth-sized (exactly) sequences with
//--- linear trend. Trend coefficients vary accross sequences. Top-2
//--- algorithm is used (or precomputed unit-normalized linear trend basis).
//--- NOTE: this test requires NTracks>=2, WindowWidth>=3 and TopK=2 to work;
//--- however, in order to improve numerical properties (diversity among
//--- samples, different slopes and offsets) we set NTracks to be
//--- at least 5.
//--- NOTE: one more version of this test verifies scaling properties
//--- by solving larger task (WindowWidth=100, NTracks=5,
//--- TrackLen=2*WindowWidth). This version makes just 5 checks because
//--- of higher cost.
for(pass=1; pass<=passcount; pass++)
{
ntracks=5+CHighQualityRand::HQRndUniformI(rs,10);
windowwidth=3+CHighQualityRand::HQRndUniformI(rs,3);
CSSA::SSACreate(state);
CSSA::SSASetWindow(state,windowwidth);
x=vector<double>::Zeros(windowwidth);
for(k=0; k<ntracks ; k++)
{
v=CHighQualityRand::HQRndNormal(rs);
x.Set(0,CHighQualityRand::HQRndNormal(rs));
for(i=1; i<windowwidth ; i++)
x.Set(i,x[i-1]+v);
CSSA::SSAAddSequence(state,x,windowwidth);
}
switch(algotype)
{
case 1:
b=matrix<double>::Zeros(windowwidth,2);
v=0.0;
for(i=0; i<windowwidth ; i++)
{
b.Set(i,0,1.0/MathSqrt(windowwidth));
b.Set(i,1,i);
v+=(double)i/(double)windowwidth;
}
vv=0.0;
for(i=0; i<windowwidth ; i++)
{
b.Add(i,1,-v);
vv+=CMath::Sqr(b.Get(i,1));
}
for(i=0; i<windowwidth ; i++)
b.Mul(i,1,1.0/MathSqrt(vv));
CSSA::SSASetAlgoPrecomputed(state,b,windowwidth,2);
break;
case 2:
CSSA::SSASetAlgoTopKDirect(state,2);
break;
case 3:
CSSA::SSASetAlgoTopKRealtime(state,2);
break;
default:
CAp::Assert(false);
errorflag=true;
return;
}
tol=1.0E-6;
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
//--- Basis vectors must be linear/constant functions
CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis);
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:293");
CAp::SetErrorFlag(errorflag,nbasis!=2,"testssaunit.ap:294");
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=nbasis,"testssaunit.ap:295");
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:296");
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=nbasis,"testssaunit.ap:297");
if(errorflag)
return;
for(j=0; j<nbasis ; j++)
{
v=a.Get(1,j)-a.Get(0,j);
for(i=2; i<windowwidth ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(a.Get(i,j)-a.Get(i-1,j)-v)>tol,"testssaunit.ap:304");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
CSSA::SSAAnalyzeLastWindow(state,trend,noise,nticks);
CAp::SetErrorFlag(errorflag,nticks!=windowwidth,"testssaunit.ap:310");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:311");
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:312");
if(errorflag)
return;
for(i=0; i<windowwidth ; i++)
{
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[i])>tol,"testssaunit.ap:317");
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:318");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
CSSA::SSAAnalyzeLast(state,windowwidth,trend,noise);
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:324");
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:325");
if(errorflag)
return;
for(i=0; i<windowwidth ; i++)
{
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[i])>tol,"testssaunit.ap:330");
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:331");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
CSSA::SSAForecastLast(state,nticks,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:338");
if(errorflag)
return;
v=x[windowwidth-1]-x[windowwidth-2];
CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[windowwidth-1]-v)>tol,"testssaunit.ap:342");
for(i=1; i<nticks ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend[i-1]-v)>tol,"testssaunit.ap:344");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
navg=1+CHighQualityRand::HQRndUniformI(rs,windowwidth+5);
CSSA::SSAForecastAvgLast(state,navg,nticks,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:352");
if(errorflag)
return;
v=x[windowwidth-1]-x[windowwidth-2];
CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[windowwidth-1]-v)>tol,"testssaunit.ap:356");
for(i=1; i<nticks ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend[i-1]-v)>tol,"testssaunit.ap:358");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
datalen=windowwidth+CHighQualityRand::HQRndUniformI(rs,10);
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
x=vector<double>::Zeros(datalen);
v=CHighQualityRand::HQRndNormal(rs);
x.Set(0,CHighQualityRand::HQRndNormal(rs));
for(i=1; i<datalen ; i++)
x.Set(i,x[i-1]+v);
CSSA::SSAForecastSequence(state,x,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:371");
if(errorflag)
return;
CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[datalen-1]-v)>tol,"testssaunit.ap:374");
for(i=1; i<forecastlen ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend[i-1]-v)>tol,"testssaunit.ap:376");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
datalen=windowwidth+CHighQualityRand::HQRndUniformI(rs,10);
navg=1+CHighQualityRand::HQRndUniformI(rs,windowwidth+5);
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
x=vector<double>::Zeros(datalen);
v=CHighQualityRand::HQRndNormal(rs);
x.Set(0,CHighQualityRand::HQRndNormal(rs));
for(i=1; i<datalen ; i++)
x.Set(i,x[i-1]+v);
CSSA::SSAForecastAvgSequence(state,x,datalen,navg,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:390");
if(errorflag)
return;
CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[datalen-1]-v)>tol,"testssaunit.ap:393");
for(i=1; i<forecastlen ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend[i-1]-v)>tol,"testssaunit.ap:395");
}
}
for(pass=1; pass<=5; pass++)
{
ntracks=5+CHighQualityRand::HQRndUniformI(rs,10);
windowwidth=100;
nlasttracklen=2*windowwidth;
CSSA::SSACreate(state);
CSSA::SSASetWindow(state,windowwidth);
x=vector<double>::Zeros(nlasttracklen);
for(k=0; k<ntracks ; k++)
{
v=CHighQualityRand::HQRndNormal(rs);
x.Set(0,CHighQualityRand::HQRndNormal(rs));
for(i=1; i<nlasttracklen; i++)
x.Set(i,x[i-1]+v);
CSSA::SSAAddSequence(state,x,nlasttracklen);
}
switch(algotype)
{
case 1:
b=matrix<double>::Zeros(windowwidth,2);
v=0.0;
for(i=0; i<windowwidth ; i++)
{
b.Set(i,0,1/MathSqrt(windowwidth));
b.Set(i,1,i);
v+=(double)i/(double)windowwidth;
}
vv=0.0;
for(i=0; i<windowwidth ; i++)
{
b.Add(i,1,-v);
vv+=CMath::Sqr(b.Get(i,1));
}
for(i=0; i<windowwidth ; i++)
b.Mul(i,1,1.0/MathSqrt(vv));
CSSA::SSASetAlgoPrecomputed(state,b,windowwidth,2);
break;
case 2:
CSSA::SSASetAlgoTopKDirect(state,2);
break;
case 3:
CSSA::SSASetAlgoTopKRealtime(state,2);
break;
default:
CAp::Assert(false);
errorflag=true;
return;
}
tol=1.0E-6;
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
CSSA::SSAAnalyzeLastWindow(state,trend,noise,nticks);
CAp::SetErrorFlag(errorflag,nticks!=windowwidth,"testssaunit.ap:444");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:445");
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:446");
if(errorflag)
return;
for(i=0; i<windowwidth ; i++)
{
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[nlasttracklen+i-windowwidth])>tol,"testssaunit.ap:451");
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:452");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
CSSA::SSAAnalyzeLast(state,nlasttracklen,trend,noise);
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nlasttracklen,"testssaunit.ap:458");
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nlasttracklen,"testssaunit.ap:459");
if(errorflag)
return;
for(i=0; i<nlasttracklen; i++)
{
CAp::SetErrorFlag(errorflag,(double)(MathAbs(trend[i]-x[i]))>tol,"testssaunit.ap:464");
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:465");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
CSSA::SSAForecastLast(state,nticks,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:472");
if(errorflag)
return;
v=x[nlasttracklen-1]-x[nlasttracklen-2];
CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[nlasttracklen-1]-v)>tol,"testssaunit.ap:476");
for(i=1; i<nticks ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend[i-1]-v)>tol,"testssaunit.ap:478");
}
}
//--- Test that on specially designed dataset with two sinusoidal components
//--- with significantly different amplitudes, whose periods are integer
//--- divisors of window width, SSA correctly separates leading sine from
//--- its smaller counterpart. Also test that we can correctly predict future
//--- values of the sequence.
//--- Dataset consists of many sequences, each of them featuring either
//--- sine with period=WindowWidth or sine with period=WindowWidth/2. Such
//--- dataset is necessary because sum of two sines is not well separated by
//--- SVD (it performs only approximate, asymptotic separation). But when
//--- every sequence is either one of the sines, but not two together, we
//--- can easily separate them.
//--- Sine coefficients are changed from sequence to sequence.
//--- NOTE: this test requires large WindowWidth and TopK=2 to work.
//--- NOTE: this test uses reduced number of passes because of higher computational complexity
for(pass=1; pass<=10; pass++)
{
//--- Skip "precomputed basis" algorithm
if(algotype==1)
continue;
//--- Generate dataset
ntracks=100+CHighQualityRand::HQRndUniformI(rs,2);
windowwidth=64;
nlasttracklen=windowwidth+CHighQualityRand::HQRndUniformI(rs,10);
CSSA::SSACreate(state);
x=vector<double>::Zeros(nlasttracklen);
for(k=0; k<ntracks ; k++)
{
sineoffs=CHighQualityRand::HQRndUniformI(rs,windowwidth);
if(k%2==0)
{
sineamp=1+CHighQualityRand::HQRndUniformR(rs);
sinefreq=1;
}
else
{
sineamp=0.1*(1+CHighQualityRand::HQRndUniformR(rs));
sinefreq=2;
}
for(i=0; i<nlasttracklen; i++)
x.Set(i,sineamp*MathSin((i+sineoffs)/windowwidth*2*M_PI*sinefreq));
CSSA::SSAAddSequence(state,x,nlasttracklen);
}
if(algotype==2)
CSSA::SSASetAlgoTopKDirect(state,2);
else
{
if(algotype==3)
CSSA::SSASetAlgoTopKRealtime(state,2);
else
{
CAp::Assert(false);
errorflag=true;
return;
}
}
tol=1.0E-6;
//--- Test analysis with WindowWidth=SinePeriod:
//--- * analyze sine with frequency=1, it must be recognized as trend
//--- * analyze sine with frequency=2, with smoothing enabled
//--- it must be discarded as noise;
CSSA::SSASetWindow(state,windowwidth);
nticks=windowwidth+1+CHighQualityRand::HQRndUniformI(rs,windowwidth);
sineoffs=CHighQualityRand::HQRndUniformI(rs,windowwidth);
sineamp=1+CHighQualityRand::HQRndUniformR(rs);
sinefreq=1;
x=vector<double>::Zeros(nticks);
for(i=0; i<nticks ; i++)
x.Set(i,sineamp*MathSin((i+sineoffs)/windowwidth*2*M_PI*sinefreq));
CSSA::SSAAnalyzeSequence(state,x,nticks,trend,noise);
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:558");
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:559");
if(errorflag)
return;
for(i=0; i<nticks ; i++)
{
v=sineamp*MathSin((i+sineoffs)/windowwidth*2*M_PI*sinefreq);
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-v)>tol,"testssaunit.ap:565");
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>tol,"testssaunit.ap:566");
}
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,nticks-windowwidth);
datalen=nticks-forecastlen;
x2=x;
x2.Resize(datalen);
CSSA::SSAForecastSequence(state,x2,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
for(i=0; i<forecastlen ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[datalen+i])>tol,"testssaunit.ap:575");
windowwidth2=-1;
CSSA::SSAGetLRR(state,tmp0,windowwidth2);
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:579");
CAp::SetErrorFlag(errorflag,CAp::Len(tmp0)!=windowwidth-1,"testssaunit.ap:580");
if(errorflag)
return;
for(i=windowwidth-1; i<nticks ; i++)
{
i1_= -(i-(windowwidth-1));
v=0.0;
for(i_=i-(windowwidth-1); i_<=i-1; i_++)
v+=x[i_]*tmp0[i_+i1_];
CAp::SetErrorFlag(errorflag,MathAbs(v-x[i])>tol,"testssaunit.ap:586");
}
nticks=windowwidth+1+CHighQualityRand::HQRndUniformI(rs,windowwidth);
sineoffs=CHighQualityRand::HQRndUniformI(rs,windowwidth);
sineamp=1+CHighQualityRand::HQRndUniformR(rs);
sinefreq=2;
x=vector<double>::Zeros(nticks);
for(i=0; i<nticks ; i++)
x.Set(i,sineamp*MathSin((i+sineoffs)/windowwidth*2*M_PI*sinefreq));
CSSA::SSAAnalyzeSequence(state,x,nticks,trend,noise);
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:597");
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:598");
if(errorflag)
return;
for(i=0; i<nticks ; i++)
{
v=sineamp*MathSin((i+sineoffs)/windowwidth*2*M_PI*sinefreq);
CAp::SetErrorFlag(errorflag,MathAbs(trend[i])>tol,"testssaunit.ap:604");
CAp::SetErrorFlag(errorflag,MathAbs(noise[i]-v)>tol,"testssaunit.ap:605");
}
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,nticks-windowwidth);
datalen=nticks-forecastlen;
x2=x;
x2.Resize(datalen);
CSSA::SSAForecastSequence(state,x2,datalen,forecastlen,true,trend);
for(i=0; i<forecastlen ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(trend[i])>tol,"testssaunit.ap:614");
}
//--- Test appendPoint() functionality.
//--- We have specially designed dataset:
//--- * NZeros ticks of exactly zero values
//--- * NLinear ticks of series which linearly grow from 0 to 1
//--- * NLinear ticks of series which linearly decrease from 1 to 0
//--- * NZeros ticks of exactly zero values
//--- * NZeros=100
//--- * NLinear=20
//--- SSA settings have following values:
//--- * WindowWidth=4 or 25 (large widths are more problematic for
//--- iterative solvers, so they help to debug incremental updates)
//--- * NBasis=2
//--- We choose number of initial values to start with NInitial
//--- in [1..2*WindowWidth], then add points one
//--- by one with SSAAppendPointAndUpdate(). In the end we compare
//--- results returned by SSAGetBasis() with that returned by model
//--- which was created from full dataset.
//--- NOTE: we perform limited amount of passes because this test
//--- has high cost.
for(pass=1; pass<=25; pass++)
{
//--- Generate dataset
tol=1.0E-6;
nzeros=100;
nlinear=50;
if(CHighQualityRand::HQRndNormal(rs)>0.0)
windowwidth=25;
else
windowwidth=4;
nbasis=2;
ninitial=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
x=vector<double>::Zeros(2*nzeros+2*nlinear);
for(i=0; i<=nlinear-1; i++)
{
x.Set(nzeros+i,(double)i/(double)nlinear);
x.Set(nzeros+nlinear+i,1-(double)i/(double)nlinear);
}
CMatGen::RMatrixRndOrthogonal(windowwidth,b);
//--- Build model using many sequential appends
//--- NOTE: for NInitial>=WindowWidth with probability 50%
//--- we enforce basis calculation before first
//--- append() call. It helps to debug different
//--- branches of algorithms.
//--- NOTE: we may also request delayed power-up of the
//--- algorithm. It also checks various branches of
//--- the algo, although with such settings delayed
//--- power-up is hard to check (initial dataset is
//--- just zeros, zeros, zeros..).
x2=x;
x2.Resize(ninitial);
CSSA::SSACreate(state);
CSSA::SSASetWindow(state,windowwidth);
CSSA::SSAAddSequence(state,x2,CAp::Len(x2));
switch(algotype)
{
case 1:
CSSA::SSASetAlgoPrecomputed(state,b,windowwidth,nbasis);
break;
case 2:
CSSA::SSASetAlgoTopKDirect(state,nbasis);
break;
case 3:
CSSA::SSASetAlgoTopKRealtime(state,nbasis);
if(CHighQualityRand::HQRndUniformR(rs)>0.5)
CSSA::SSASetPowerUpLength(state,10);
break;
default:
CAp::Assert(false);
errorflag=true;
return;
}
if(ninitial>=windowwidth && CHighQualityRand::HQRndUniformR(rs)>0.5)
{
a.Resize(0,0);
windowwidth2=-1;
nbasis2=-1;
CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis2);
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:705");
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:706");
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:707");
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=nbasis,"testssaunit.ap:708");
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=nbasis,"testssaunit.ap:709");
if(errorflag)
return;
}
for(i=CAp::Len(x2); i<CAp::Len(x); i++)
CSSA::SSAAppendPointAndUpdate(state,x[i],1.0);
a.Resize(0,0);
windowwidth2=-1;
nbasis2=-1;
CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis2);
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:720");
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:721");
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:722");
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=nbasis,"testssaunit.ap:723");
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=nbasis,"testssaunit.ap:724");
if(errorflag)
return;
//--- Build model using one big sequence
CSSA::SSACreate(state2);
CSSA::SSASetWindow(state2,windowwidth);
CSSA::SSAAddSequence(state2,x,CAp::Len(x));
switch(algotype)
{
case 1:
CSSA::SSASetAlgoPrecomputed(state2,b,windowwidth,nbasis);
break;
case 2:
CSSA::SSASetAlgoTopKDirect(state2,nbasis);
break;
case 3:
CSSA::SSASetAlgoTopKRealtime(state2,nbasis);
break;
default:
CAp::Assert(false);
errorflag=true;
return;
}
a2.Resize(0,0);
windowwidth2=-1;
nbasis2=-1;
CSSA::SSAGetBasis(state2,a2,sv2,windowwidth2,nbasis2);
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:747");
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:748");
CAp::SetErrorFlag(errorflag,CAp::Rows(a2)!=windowwidth,"testssaunit.ap:749");
CAp::SetErrorFlag(errorflag,CAp::Cols(a2)!=nbasis,"testssaunit.ap:750");
CAp::SetErrorFlag(errorflag,CAp::Len(sv2)!=nbasis,"testssaunit.ap:751");
if(errorflag)
return;
//--- Compare results
for(i=0; i<nbasis ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(sv[i]-sv2[i])>tol,"testssaunit.ap:759");
for(j=0; j<nbasis ; j++)
{
v=0.0;
for(i_=0; i_<windowwidth ; i_++)
v+=a.Get(i_,j)*a2.Get(i_,j);
for(i=0; i<windowwidth ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(a.Get(i,j)-MathSign(v)*a2.Get(i,j))>tol,"testssaunit.ap:764");
}
}
//--- Test appendSequence() functionality.
//--- We have specially designed dataset:
//--- * NNoise sequences of Gaussian noise
//--- * NLinear linear sequences partially corrupted by noise
//--- * NZeros exactly zero sequences (used to let incremental algo converge)
//--- * all sequences have random size in [WindowWidth-2,WindowWidth+2]
//--- * NNoise=20
//--- * NLinear=20
//--- SSA settings have following values:
//--- * WindowWidth=4 or 25 (large widths are more problematic for
//--- iterative solvers, so they help to debug incremental updates)
//--- * NBasis=2
//--- We have two solvers:
//--- * one is trained on complete dataset
//--- * another one starts from NNoise noisy sequences, linear/zero sequenes
//--- are incrementally appended with AppendSequence
//--- NOTE: we perform limited amount of passes because this test
//--- has high cost.
for(pass=1; pass<=25; pass++)
{
//--- Problem metrics
tol=1.0E-5;
nnoise=20;
nlinear=20;
nzeros=50;
if(CHighQualityRand::HQRndNormal(rs)>0.0)
windowwidth=25;
else
windowwidth=4;
nbasis=2;
//--- Initialize solvers
//--- NOTE: we set State.DefaultSubspaceIts to large value in order
//--- to ensure convergence to same basis.
CMatGen::RMatrixRndOrthogonal(windowwidth,b);
CSSA::SSACreate(state);
CSSA::SSACreate(state2);
state.m_DefaultSubspaceits=50;
CSSA::SSASetWindow(state,windowwidth);
CSSA::SSASetWindow(state2,windowwidth);
switch(algotype)
{
case 1:
CSSA::SSASetAlgoPrecomputed(state,b,windowwidth,nbasis);
CSSA::SSASetAlgoPrecomputed(state2,b,windowwidth,nbasis);
break;
case 2:
CSSA::SSASetAlgoTopKDirect(state,nbasis);
CSSA::SSASetAlgoTopKDirect(state2,nbasis);
break;
case 3:
CSSA::SSASetAlgoTopKRealtime(state,nbasis);
CSSA::SSASetAlgoTopKRealtime(state2,nbasis);
break;
default:
CAp::Assert(false);
errorflag=true;
return;
}
//--- Feed noisy sequences
for(i=0; i<nnoise ; i++)
{
k=windowwidth+(CHighQualityRand::HQRndUniformI(rs,5)-2);
x=vector<double>::Zeros(k);
for(j=0; j<k; j++)
x.Set(j,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAAddSequence(state,x,CAp::Len(x));
CSSA::SSAAddSequence(state2,x,CAp::Len(x));
}
//--- Feed linear and zero sequences.
//--- NOTE: with probability 50% we call SSAGetBasis(State2).
//--- Ideally, SSA should be able to handle appends correctly
//--- with or without preceeding call which requires basis
//--- to be evaluated.
if(CHighQualityRand::HQRndUniformR(rs)>0.5)
{
a2.Resize(0,0);
windowwidth2=-1;
nbasis2=-1;
CSSA::SSAGetBasis(state2,a2,sv2,windowwidth2,nbasis2);
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:865");
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:866");
CAp::SetErrorFlag(errorflag,CAp::Rows(a2)!=windowwidth,"testssaunit.ap:867");
CAp::SetErrorFlag(errorflag,CAp::Cols(a2)!=nbasis,"testssaunit.ap:868");
CAp::SetErrorFlag(errorflag,CAp::Len(sv2)!=nbasis,"testssaunit.ap:869");
if(errorflag)
return;
}
for(i=0; i<=nlinear-1; i++)
{
k=windowwidth+(CHighQualityRand::HQRndUniformI(rs,5)-2);
v=CHighQualityRand::HQRndNormal(rs);
vv=CHighQualityRand::HQRndNormal(rs);
x=vector<double>::Zeros(k);
x.Set(0,v);
for(j=1; j<k; j++)
x.Set(j,x[j-1]+vv+0.1*CHighQualityRand::HQRndNormal(rs));
CSSA::SSAAddSequence(state,x,CAp::Len(x));
CSSA::SSAAppendSequenceAndUpdate(state2,x,CAp::Len(x),1.0);
}
for(i=0; i<=nzeros-1; i++)
{
k=windowwidth+(CHighQualityRand::HQRndUniformI(rs,5)-2);
x=vector<double>::Zeros(k);
CSSA::SSAAddSequence(state,x,CAp::Len(x));
CSSA::SSAAppendSequenceAndUpdate(state2,x,CAp::Len(x),1.0);
}
//--- Compare results
a.Resize(0,0);
windowwidth2=-1;
nbasis2=-1;
CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis2);
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:903");
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:904");
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:905");
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=nbasis,"testssaunit.ap:906");
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=nbasis,"testssaunit.ap:907");
if(errorflag)
return;
a2.Resize(0,0);
windowwidth2=-1;
nbasis2=-1;
CSSA::SSAGetBasis(state2,a2,sv2,windowwidth2,nbasis2);
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:915");
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:916");
CAp::SetErrorFlag(errorflag,CAp::Rows(a2)!=windowwidth,"testssaunit.ap:917");
CAp::SetErrorFlag(errorflag,CAp::Cols(a2)!=nbasis,"testssaunit.ap:918");
CAp::SetErrorFlag(errorflag,CAp::Len(sv2)!=nbasis,"testssaunit.ap:919");
if(errorflag)
return;
for(i=0; i<nbasis ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(sv[i]-sv2[i])>tol,"testssaunit.ap:923");
for(j=0; j<nbasis ; j++)
{
v=0.0;
for(i_=0; i_<windowwidth ; i_++)
v+=a.Get(i_,j)*a2.Get(i_,j);
for(i=0; i<windowwidth ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(a.Get(i,j)-MathSign(v)*a2.Get(i,j))>tol,"testssaunit.ap:928");
}
}
//--- Test memory limit functionality.
//--- Compare results obtained with HUGE limit vs ones obtained with small limit.
for(windowwidth=1; windowwidth<=20; windowwidth++)
{
for(mlimit=-1; mlimit<=16; mlimit++)
{
//--- Not tested
if(algotype==1)
continue;
//--- Problem metrics
tol=1.0E-5;
nticks=1000+CHighQualityRand::HQRndUniformI(rs,1000);
if(windowwidth>5)
nbasis=1+CHighQualityRand::HQRndUniformI(rs,5);
else
nbasis=1;
//--- Create solvers, dataset, set limits
CSSA::SSACreate(state);
CSSA::SSACreate(state2);
if(mlimit>=0)
CSSA::SSASetMemoryLimit(state,(int)MathRound(MathPow(2,mlimit)));
else
CSSA::SSASetMemoryLimit(state,0);
CSSA::SSASetMemoryLimit(state2,999999999);
CSSA::SSASetWindow(state,windowwidth);
CSSA::SSASetWindow(state2,windowwidth);
if(algotype==2)
{
CSSA::SSASetAlgoTopKDirect(state,nbasis);
CSSA::SSASetAlgoTopKDirect(state2,nbasis);
}
else
{
if(algotype==3)
{
CSSA::SSASetAlgoTopKRealtime(state,nbasis);
CSSA::SSASetAlgoTopKRealtime(state2,nbasis);
}
else
{
CAp::Assert(false);
errorflag=true;
return;
}
}
x=vector<double>::Zeros(nticks);
for(i=0; i<nticks ; i++)
x.Set(i,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAAddSequence(state,x,CAp::Len(x));
CSSA::SSAAddSequence(state2,x,CAp::Len(x));
//--- Reset internal temporaries for this test.
//--- Implementation-dependent, may fail due to future changes in the core.
state.m_UxBatch.Resize(0,0);
state.m_AseqTrajectory.Resize(0,0);
state.m_AseqTbProduct.Resize(0,0);
//--- Test
CSSA::SSAAnalyzeLast(state,nticks,trend,noise);
CSSA::SSAAnalyzeLast(state2,nticks,trend2,noise2);
for(i=0; i<nticks ; i++)
{
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend2[i])>tol,"testssaunit.ap:1001");
CAp::SetErrorFlag(errorflag,MathAbs(noise[i]-noise2[i])>tol,"testssaunit.ap:1002");
}
//--- Additional tests for sizes of internal arrays.
//--- Implementation-dependent, may fail due to future changes in the core.
if(mlimit>=0)
{
k=MathMax(4*windowwidth*windowwidth,(int)MathRound(MathPow(2,mlimit)));
CAp::SetErrorFlag(errorflag,CAp::Cols(state.m_UxBatch)*CAp::Rows(state.m_UxBatch)>k,"testssaunit.ap:1012");
CAp::SetErrorFlag(errorflag,CAp::Cols(state.m_AseqTrajectory)*CAp::Rows(state.m_AseqTrajectory)>k,"testssaunit.ap:1013");
CAp::SetErrorFlag(errorflag,CAp::Cols(state.m_AseqTbProduct)*CAp::Rows(state.m_AseqTbProduct)>k,"testssaunit.ap:1014");
}
else
{
CAp::SetErrorFlag(errorflag,CAp::Rows(state.m_UxBatch)!=nticks-windowwidth+1,"testssaunit.ap:1018");
CAp::SetErrorFlag(errorflag,CAp::Rows(state.m_AseqTrajectory)!=nticks-windowwidth+1,"testssaunit.ap:1019");
CAp::SetErrorFlag(errorflag,CAp::Rows(state.m_AseqTbProduct)!=nticks-windowwidth+1,"testssaunit.ap:1020");
}
}
}
}
//--- Test power-up ability of the real-time algorithm.
//--- We have specially designed dataset:
//--- * NTicks ticks of linearly descending from 1 to 0 linear trend,
//--- corrupted by random Gaussian noise
//--- * NZeros ticks of exactly zero values
//--- * NTicks=100
//--- * NZeros=100
//--- SSA settings have following values:
//--- * WindowWidth=4 or 25 (large widths are more problematic for
//--- iterative solvers, so they help to debug incremental updates)
//--- * NBasis=1..2 (only top vectors converge stable enough for unit testing)
//--- * powerup length is 10
//--- We perform two SSAs:
//--- * one with full dataset and no powerup
//--- * one with powerup, NTicks+WindowWidth first elements, followed by appending of NZeros zeros
//--- We check that:
//--- * basis found by second model (right after initialization) is different
//--- from basis of the first one
//--- * basis found by second model after last append is same as the first one
//--- NOTE: we perform limited amount of passes because this test
//--- has high cost.
for(pass=1; pass<=25; pass++)
{
//--- Generate dataset
tol=1.0E-3;
nticks=100;
nzeros=50;
if(CHighQualityRand::HQRndNormal(rs)>0.0)
windowwidth=25;
else
windowwidth=4;
nbasis=1+CHighQualityRand::HQRndUniformI(rs,2);
x=vector<double>::Zeros(nticks+nzeros);
for(i=0; i<nticks ; i++)
x.Set(i,1-(double)i/(double)nticks+0.05*CHighQualityRand::HQRndNormal(rs));
//--- Build complete model
//--- NOTE: we tweak S.DefaultSubspaceIts in order to enforce convergence to
//--- same basis; for this test we need extra-precise convergence.
CSSA::SSACreate(state2);
CSSA::SSASetWindow(state2,windowwidth);
CSSA::SSAAddSequence(state2,x,CAp::Len(x));
CSSA::SSASetAlgoTopKRealtime(state2,nbasis);
a2.Resize(0,0);
windowwidth2=-1;
nbasis2=-1;
CSSA::SSAGetBasis(state2,a2,sv2,windowwidth2,nbasis2);
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:1088");
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:1089");
CAp::SetErrorFlag(errorflag,CAp::Rows(a2)!=windowwidth,"testssaunit.ap:1090");
CAp::SetErrorFlag(errorflag,CAp::Cols(a2)!=nbasis,"testssaunit.ap:1091");
CAp::SetErrorFlag(errorflag,CAp::Len(sv2)!=nbasis,"testssaunit.ap:1092");
if(errorflag)
return;
//--- Build model with power-up cycle
//--- NOTE: with probability 50% we enforce basis calculation
//--- before first append() call and compare basis with
//--- one returned by full analysis. We do it only in 50%
//--- of the cases because randomness helps to debug different
//--- branches of algorithms.
x2=x;
x2.Resize(nticks+windowwidth);
CSSA::SSACreate(state);
CSSA::SSASetWindow(state,windowwidth);
CSSA::SSAAddSequence(state,x2,CAp::Len(x2));
CSSA::SSASetAlgoTopKRealtime(state,nbasis);
CSSA::SSASetPowerUpLength(state,10);
if(CHighQualityRand::HQRndUniformR(rs)>0.5)
{
a.Resize(0,0);
windowwidth2=-1;
nbasis2=-1;
CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis2);
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:1120");
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:1121");
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:1122");
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=nbasis,"testssaunit.ap:1123");
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=nbasis,"testssaunit.ap:1124");
if(errorflag)
return;
vv=0.0;
for(i=0; i<nbasis ; i++)
vv=MathMax(vv,MathAbs(sv[i]-sv2[i]));
for(j=0; j<nbasis ; j++)
{
v=0.0;
for(i_=0; i_<windowwidth ; i_++)
v+=a.Get(i_,j)*a2.Get(i_,j);
for(i=0; i<windowwidth ; i++)
vv=MathMax(vv,MathAbs(a.Get(i,j)-MathSign(v)*a2.Get(i,j)));
}
CAp::SetErrorFlag(errorflag,MathAbs(vv)<tol,"testssaunit.ap:1136");
}
for(i=CAp::Len(x2); i<CAp::Len(x) ; i++)
CSSA::SSAAppendPointAndUpdate(state,x[i],1.0);
a.Resize(0,0);
windowwidth2=-1;
nbasis2=-1;
CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis2);
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:1145");
CAp::SetErrorFlag(errorflag,nbasis2!=nbasis,"testssaunit.ap:1146");
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:1147");
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=nbasis,"testssaunit.ap:1148");
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=nbasis,"testssaunit.ap:1149");
if(errorflag)
return;
vv=0.0;
for(i=0; i<nbasis ; i++)
vv=MathMax(vv,MathAbs(sv[i]-sv2[i]));
for(j=0; j<nbasis ; j++)
{
v=0.0;
for(i_=0; i_<windowwidth ; i_++)
v+=a.Get(i_,j)*a2.Get(i_,j);
for(i=0; i<windowwidth ; i++)
vv=MathMax(vv,MathAbs(a.Get(i,j)-MathSign(v)*a2.Get(i,j)));
}
CAp::SetErrorFlag(errorflag,MathAbs(vv)>tol,"testssaunit.ap:1161");
}
//--- Test that SSAForecastAvgLast/Sequence() actually performs averaging.
//--- We test it by comparing its results vs manually averaged predictions.
//--- Dataset is a small linear trend + Gaussian noise. We are not interested
//--- in getting meaningful components, we just want to check correctness of CMath::
for(pass=1; pass<=100; pass++)
{
nticks=75+CHighQualityRand::HQRndUniformI(rs,75);
windowwidth=5+CHighQualityRand::HQRndUniformI(rs,5);
navg=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
CSSA::SSACreate(state);
CSSA::SSASetWindow(state,windowwidth);
x=vector<double>::Zeros(nticks);
for(i=0; i<nticks ; i++)
x.Set(i,0.1*i+CHighQualityRand::HQRndNormal(rs));
CSSA::SSAAddSequence(state,x,nticks);
CSSA::SSASetAlgoTopKDirect(state,1+CHighQualityRand::HQRndUniformI(rs,3));
tol=1.0E-9;
CSSA::SSAForecastAvgLast(state,navg,forecastlen,trend);
trend2=vector<double>::Zeros(forecastlen);
for(i=0; i<forecastlen ; i++)
trend2.Set(i,0.0);
for(i=0; i<navg ; i++)
{
CSSA::SSAForecastSequence(state,x,nticks-i,forecastlen+i,true,tmp0);
for(j=0; j<forecastlen ; j++)
trend2.Add(j,tmp0[i+j]/navg);
}
for(i=0; i<forecastlen ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend2[i])>(tol*CApServ::RMaxAbs3(trend[i],trend2[i],1.0)),"testssaunit.ap:1198");
nticks=75+CHighQualityRand::HQRndUniformI(rs,75);
j=CHighQualityRand::HQRndUniformI(rs,150)-75;
x2=vector<double>::Zeros(nticks);
for(i=0; i<nticks ; i++)
x2.Set(i,0.1*(i+j)+CHighQualityRand::HQRndNormal(rs));
CSSA::SSAForecastAvgSequence(state,x2,nticks,navg,forecastlen,true,trend);
trend2=vector<double>::Zeros(forecastlen);
for(i=0; i<forecastlen ; i++)
trend2.Set(i,0.0);
for(i=0; i<navg ; i++)
{
CSSA::SSAForecastSequence(state,x2,nticks-i,forecastlen+i,true,tmp0);
for(j=0; j<forecastlen ; j++)
trend2.Add(j,tmp0[i+j]/navg);
}
for(i=0; i<forecastlen ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend2[i])>(tol*CApServ::RMaxAbs3(trend[i],trend2[i],1.0)),"testssaunit.ap:1217");
nticks=75+CHighQualityRand::HQRndUniformI(rs,75);
j=CHighQualityRand::HQRndUniformI(rs,150)-75;
x2=vector<double>::Zeros(nticks);
for(i=0; i<nticks ; i++)
x2.Set(i,0.1*(i+j)+CHighQualityRand::HQRndNormal(rs));
CSSA::SSAForecastAvgSequence(state,x2,nticks,navg,forecastlen,false,trend);
trend2=vector<double>::Zeros(forecastlen);
for(i=0; i<navg ; i++)
{
CSSA::SSAForecastSequence(state,x2,nticks-i,forecastlen+i,false,tmp0);
for(j=0; j<forecastlen ; j++)
trend2.Add(j,tmp0[i+j]/navg);
}
for(i=0; i<forecastlen ; i++)
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend2[i])>(tol*CApServ::RMaxAbs3(trend[i],trend2[i],1.0)),"testssaunit.ap:1236");
}
}
//+------------------------------------------------------------------+
//| This function tests different special cases(mostly - degenerate |
//| ones). |
//| On failure sets ErrorFlag, on success it is untouched. |
//+------------------------------------------------------------------+
void CTestSSAUnit::TestSpecial(bool&errorflag)
{
//--- create variables
int i=0;
int j=0;
int k=0;
double v=0;
int pass=0;
CSSAModel state;
CSSAModel state2;
CMatrixDouble a;
CMatrixDouble b;
CRowDouble x;
CRowDouble x2;
CRowDouble sv;
CRowDouble trend;
CRowDouble noise;
CRowDouble trend2;
CRowDouble noise2;
int algotype=0;
int nticks=0;
int nanalyzed=0;
int nlasttracklen=0;
int ntracks=0;
int maxtracklen=0;
int mintracklen=0;
int datalen=0;
int forecastlen=0;
int windowwidth=0;
int nbasis=0;
int windowwidth2=0;
CMatrixDouble tracksmatrix;
CRowInt trackssizes;
int passcount=0;
double skipprob=0;
int i_=0;
CHighQualityRandState rs;
//--- Initialize RNG, test pass count and skip probability.
//--- When we perform several sequential tests on the same model, we may
//--- skip some of them with probability SkipProb in order to make sure
//--- that no carry-over effect is observed between tests.
CHighQualityRand::HQRndRandomize(rs);
passcount=500;
skipprob=0.50;
//--- Test that for empty model in default state:
//--- * SSAGetBasis() returns zero 1x1 basis
//--- * SSAAnalyzeLastWindow() returns zeros as trend/noise
//--- * SSAAnalyzeLast() returns zeros as trend/noise
//--- * SSAAnalyzeSequence() returns zeros as trend, sequence as noise
//--- * SSAForecastLast() returns zero trend
//--- * SSAForecastSequence() returns zero trend
for(pass=1; pass<=passcount; pass++)
{
CSSA::SSACreate(state);
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
CSSA::SSAGetBasis(state,a,sv,windowwidth,nbasis);
CAp::SetErrorFlag(errorflag,windowwidth!=1,"testssaunit.ap:1299");
CAp::SetErrorFlag(errorflag,nbasis!=1,"testssaunit.ap:1300");
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=1,"testssaunit.ap:1301");
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=1,"testssaunit.ap:1302");
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=1,"testssaunit.ap:1303");
for(i=0; i<CAp::Rows(a) ; i++)
{
for(j=0; j<CAp::Cols(a) ; j++)
CAp::SetErrorFlag(errorflag,a.Get(i,j)!=0.0,"testssaunit.ap:1306");
}
for(i=0; i<CAp::Len(sv) ; i++)
CAp::SetErrorFlag(errorflag,sv[i]!=0.0,"testssaunit.ap:1308");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
windowwidth=-1;
CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth);
CAp::SetErrorFlag(errorflag,windowwidth!=1,"testssaunit.ap:1316");
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=1 || trend[0]!=0.0,"testssaunit.ap:1317");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=1 || noise[0]!=0.0,"testssaunit.ap:1318");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
CSSA::SSAAnalyzeLast(state,nticks,trend,noise);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1326");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:1327");
if(errorflag)
return;
for(i=0; i<nticks ; i++)
{
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1332");
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:1333");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
x=vector<double>::Zeros(nticks);
for(i=0; i<nticks ; i++)
x.Set(i,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAAnalyzeSequence(state,x,nticks,trend,noise);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1345");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:1346");
if(errorflag)
return;
for(i=0; i<nticks ; i++)
{
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1351");
CAp::SetErrorFlag(errorflag,noise[i]!=x[i],"testssaunit.ap:1352");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
CSSA::SSAForecastLast(state,nticks,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1360");
if(errorflag)
return;
for(i=0; i<nticks ; i++)
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1364");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
datalen=1+CHighQualityRand::HQRndUniformI(rs,10);
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
x=vector<double>::Zeros(datalen);
for(i=0; i<datalen ; i++)
x.Set(i,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAForecastSequence(state,x,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:1375");
if(errorflag)
return;
for(i=0; i<forecastlen ; i++)
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1379");
}
}
//--- Test that for empty model with non-default window, but default
//--- algorithm and no data:
//--- * SSAGetBasis() returns zero WINDOWx1 basis
//--- * SSAAnalyzeLastWindow() returns zeros as trend/noise
//--- * SSAAnalyzeLast() returns zeros as trend/noise
//--- * SSAAnalyzeSequence() returns zeros as trend, sequence as noise
//--- * SSAForecastLast() returns zero trend
//--- * SSAForecastSequence() returns zero trend
for(pass=1; pass<=passcount; pass++)
{
windowwidth2=1+CHighQualityRand::HQRndUniformI(rs,10);
CSSA::SSACreate(state);
CSSA::SSASetWindow(state,windowwidth2);
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
CSSA::SSAGetBasis(state,a,sv,windowwidth,nbasis);
CAp::SetErrorFlag(errorflag,windowwidth!=windowwidth2,"testssaunit.ap:1401");
CAp::SetErrorFlag(errorflag,nbasis!=1,"testssaunit.ap:1402");
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:1403");
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=1,"testssaunit.ap:1404");
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=1,"testssaunit.ap:1405");
for(i=0; i<CAp::Rows(a) ; i++)
{
for(j=0; j<CAp::Cols(a) ; j++)
CAp::SetErrorFlag(errorflag,a.Get(i,j)!=0.0,"testssaunit.ap:1408");
}
for(i=0; i<CAp::Len(sv) ; i++)
CAp::SetErrorFlag(errorflag,sv[i]!=0.0,"testssaunit.ap:1410");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
windowwidth=-1;
CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth);
CAp::SetErrorFlag(errorflag,windowwidth!=windowwidth2,"testssaunit.ap:1418");
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth2,"testssaunit.ap:1419");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth2,"testssaunit.ap:1420");
for(i=0; i<CAp::Len(trend) ; i++)
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1422");
for(i=0; i<=CAp::Len(noise)-1; i++)
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:1424");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
CSSA::SSAAnalyzeLast(state,nticks,trend,noise);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1432");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:1433");
if(errorflag)
return;
for(i=0; i<nticks ; i++)
{
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1438");
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:1439");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
x=vector<double>::Zeros(nticks);
for(i=0; i<nticks ; i++)
x.Set(i,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAAnalyzeSequence(state,x,nticks,trend,noise);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1451");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:1452");
if(errorflag)
return;
for(i=0; i<nticks ; i++)
{
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1457");
CAp::SetErrorFlag(errorflag,noise[i]!=x[i],"testssaunit.ap:1458");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
CSSA::SSAForecastLast(state,nticks,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1466");
if(errorflag)
return;
for(i=0; i<nticks ; i++)
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1470");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
datalen=1+CHighQualityRand::HQRndUniformI(rs,10);
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
x=vector<double>::Zeros(datalen);
for(i=0; i<datalen ; i++)
x.Set(i,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAForecastSequence(state,x,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:1481");
if(errorflag)
return;
for(i=0; i<forecastlen ; i++)
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1485");
}
}
//--- Test that for empty model with default algorithm and one/few tracks
//--- (which are sometimes shorter than window, sometimes longer than window)
//--- * SSAGetBasis() returns zero WINDOWx1 basis
//--- * SSAAnalyzeLastWindow() returns zeros as trend and correctly aligned
//--- cropped/padded copy of X in the noise
//--- * SSAAnalyzeLast() returns zeros as trend and correctly aligned
//--- cropped/padded copy of X in the noise
//--- * SSAAnalyzeSequence() returns zeros as trend, sequence as noise
//--- * SSAForecastLast() returns zero trend
//--- * SSAForecastSequence() returns zero trend
for(pass=1; pass<=passcount; pass++)
{
//--- Generate task; last track is stored in X, its length in NLastTrackLen
nlasttracklen=-999999;
windowwidth2=1+CHighQualityRand::HQRndUniformI(rs,10);
ntracks=1+CHighQualityRand::HQRndUniformI(rs,3);
CSSA::SSACreate(state);
CSSA::SSASetWindow(state,windowwidth2);
tracksmatrix=matrix<double>::Zeros(ntracks,2*windowwidth2);
trackssizes.Resize(ntracks);
for(k=0; k<ntracks ; k++)
{
nlasttracklen=CHighQualityRand::HQRndUniformI(rs,2*windowwidth2);
x=vector<double>::Zeros(nlasttracklen);
for(i=0; i<nlasttracklen; i++)
{
x.Set(i,CHighQualityRand::HQRndNormal(rs));
tracksmatrix.Set(k,i,x[i]);
}
trackssizes.Set(k,nlasttracklen);
CSSA::SSAAddSequence(state,x,nlasttracklen);
}
//--- Test
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
CSSA::SSAGetBasis(state,a,sv,windowwidth,nbasis);
CAp::SetErrorFlag(errorflag,windowwidth!=windowwidth2,"testssaunit.ap:1532");
CAp::SetErrorFlag(errorflag,nbasis!=1,"testssaunit.ap:1533");
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:1534");
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=1,"testssaunit.ap:1535");
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=1,"testssaunit.ap:1536");
for(i=0; i<CAp::Rows(a) ; i++)
{
for(j=0; j<CAp::Cols(a) ; j++)
CAp::SetErrorFlag(errorflag,a.Get(i,j)!=0.0,"testssaunit.ap:1539");
}
for(i=0; i<CAp::Len(sv) ; i++)
CAp::SetErrorFlag(errorflag,sv[i]!=0.0,"testssaunit.ap:1541");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
windowwidth=-1;
CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth);
CAp::SetErrorFlag(errorflag,windowwidth!=windowwidth2,"testssaunit.ap:1549");
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth2,"testssaunit.ap:1550");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth2,"testssaunit.ap:1551");
if(errorflag)
return;
for(i=0; i<windowwidth ; i++)
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1555");
for(i=0; i<=MathMax(windowwidth-nlasttracklen,0)-1; i++)
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:1557");
for(i=MathMax(windowwidth-nlasttracklen,0); i<windowwidth ; i++)
CAp::SetErrorFlag(errorflag,noise[i]!=x[nlasttracklen+(i-windowwidth)],"testssaunit.ap:1559");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nanalyzed=1+CHighQualityRand::HQRndUniformI(rs,10);
CSSA::SSAAnalyzeLast(state,nanalyzed,trend,noise);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nanalyzed,"testssaunit.ap:1567");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nanalyzed,"testssaunit.ap:1568");
if(errorflag)
return;
for(i=0; i<nanalyzed ; i++)
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1572");
for(i=0; i<MathMax(nanalyzed-nlasttracklen,0) ; i++)
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:1574");
for(i=MathMax(nanalyzed-nlasttracklen,0); i<nanalyzed ; i++)
CAp::SetErrorFlag(errorflag,noise[i]!=x[nlasttracklen+(i-nanalyzed)],"testssaunit.ap:1576");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
x=vector<double>::Zeros(nticks);
for(i=0; i<nticks ; i++)
x.Set(i,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAAnalyzeSequence(state,x,nticks,trend,noise);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1587");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:1588");
if(errorflag)
return;
for(i=0; i<nticks ; i++)
{
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1593");
CAp::SetErrorFlag(errorflag,noise[i]!=x[i],"testssaunit.ap:1594");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
CSSA::SSAForecastLast(state,nticks,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1602");
if(errorflag)
return;
for(i=0; i<nticks ; i++)
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1606");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
datalen=1+CHighQualityRand::HQRndUniformI(rs,10);
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
x=vector<double>::Zeros(datalen);
for(i=0; i<datalen ; i++)
x.Set(i,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAForecastSequence(state,x,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:1617");
if(errorflag)
return;
for(i=0; i<forecastlen ; i++)
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1621");
}
}
//--- Tests below are performed for all algorithms supported.
for(algotype=1; algotype<=3; algotype++)
{
//--- Test that SSAClearData() actually clears data
for(pass=1; pass<=passcount; pass++)
{
if(algotype==1)
continue;
//--- Create two models, one is created after cleardata() call
//--- which should erase all traces of the previous dataset.
CSSA::SSACreate(state);
CSSA::SSACreate(state2);
CSSA::SSASetWindow(state,5);
CSSA::SSASetWindow(state2,5);
switch(algotype)
{
case 2:
CSSA::SSASetAlgoTopKDirect(state,2);
CSSA::SSASetAlgoTopKDirect(state2,2);
break;
case 3:
CSSA::SSASetAlgoTopKRealtime(state,2);
CSSA::SSASetAlgoTopKRealtime(state2,2);
break;
default:
CAp::Assert(false);
errorflag=true;
return;
}
x=vector<double>::Zeros(10);
for(i=0; i<CAp::Len(x) ; i++)
x.Set(i,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAAddSequence(state,x,CAp::Len(x));
CSSA::SSAClearData(state);
for(i=0; i<CAp::Len(x) ; i++)
x.Set(i,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAAddSequence(state,x,CAp::Len(x));
CSSA::SSAAddSequence(state2,x,CAp::Len(x));
//--- Test
CSSA::SSAAnalyzeLast(state,CAp::Len(x),trend,noise);
CSSA::SSAAnalyzeLast(state2,CAp::Len(x),trend2,noise2);
for(i=0; i<CAp::Len(x) ; i++)
{
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-trend2[i])>(1.0E-5),"testssaunit.ap:1675");
CAp::SetErrorFlag(errorflag,MathAbs(noise[i]-noise2[i])>(1.0E-5),"testssaunit.ap:1676");
}
}
//--- Test that for model with some algo being set, one/few tracks and unit window length:
//--- * SSAGetBasis() returns 1x1 basis (unit matrix)
//--- * SSAAnalyzeLastWindow() returns last element of track as trend and zeros
//--- as noise (with minor rounding error)
//--- * SSAAnalyzeLast() returns tracks as trend and zeros as noise (with
//--- minor rounding error possible). For NTicks>TrackLength result is correctly
//--- prepended with zeros.
//--- * SSAAnalyzeSequence() returns sequence as trend, zeros as noise (up to
//--- machine precision)
//--- * SSAForecastLast() returns just copies of the last element
//--- * SSAForecastSequence() returns just copies of the last element
for(pass=1; pass<=passcount; pass++)
{
//--- Generate task; last track is stored in X, its length in NLastTrackLen
nlasttracklen=-999999;
maxtracklen=10;
ntracks=1+CHighQualityRand::HQRndUniformI(rs,3);
CSSA::SSACreate(state);
CSSA::SSASetWindow(state,1);
tracksmatrix=matrix<double>::Zeros(ntracks,maxtracklen);
trackssizes.Resize(ntracks);
for(k=0; k<ntracks ; k++)
{
nlasttracklen=1+CHighQualityRand::HQRndUniformI(rs,maxtracklen);
x=vector<double>::Zeros(nlasttracklen);
for(i=0; i<nlasttracklen; i++)
{
x.Set(i,CHighQualityRand::HQRndNormal(rs));
tracksmatrix.Set(k,i,x[i]);
}
trackssizes.Set(k,nlasttracklen);
CSSA::SSAAddSequence(state,x,nlasttracklen);
}
switch(algotype)
{
case 1:
b=matrix<double>::Zeros(1,1);
b.Set(0,0,2*CHighQualityRand::HQRndUniformI(rs,2)-1);
CSSA::SSASetAlgoPrecomputed(state,b,1,1);
break;
case 2:
CSSA::SSASetAlgoTopKDirect(state,1);
break;
case 3:
CSSA::SSASetAlgoTopKRealtime(state,1);
break;
default:
CAp::Assert(false);
errorflag=true;
return;
}
//--- Test
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
CSSA::SSAGetBasis(state,a,sv,windowwidth,nbasis);
CAp::SetErrorFlag(errorflag,windowwidth!=1,"testssaunit.ap:1736");
CAp::SetErrorFlag(errorflag,nbasis!=1,"testssaunit.ap:1737");
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=1,"testssaunit.ap:1738");
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=1,"testssaunit.ap:1739");
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=1,"testssaunit.ap:1740");
CAp::SetErrorFlag(errorflag,MathAbs(MathAbs(a.Get(0,0))-1)>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1741");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
windowwidth=-1;
CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth);
CAp::SetErrorFlag(errorflag,windowwidth!=1,"testssaunit.ap:1749");
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=1,"testssaunit.ap:1750");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=1,"testssaunit.ap:1751");
if(errorflag)
return;
CAp::SetErrorFlag(errorflag,MathAbs(trend[0]-x[nlasttracklen-1])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1754");
CAp::SetErrorFlag(errorflag,MathAbs(noise[0])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1755");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nanalyzed=1+CHighQualityRand::HQRndUniformI(rs,10);
CSSA::SSAAnalyzeLast(state,nanalyzed,trend,noise);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nanalyzed,"testssaunit.ap:1763");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nanalyzed,"testssaunit.ap:1764");
if(errorflag)
return;
for(i=0; i<MathMax(nanalyzed-nlasttracklen,0) ; i++)
{
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1769");
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:1770");
}
for(i=MathMax(nanalyzed-nlasttracklen,0); i<nanalyzed ; i++)
{
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[i-nanalyzed+nlasttracklen])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1774");
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1775");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
x2=vector<double>::Zeros(nticks);
for(i=0; i<nticks ; i++)
x2.Set(i,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAAnalyzeSequence(state,x2,nticks,trend,noise);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1787");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:1788");
if(errorflag)
return;
for(i=0; i<nticks ; i++)
{
CAp::SetErrorFlag(errorflag,(double)(MathAbs(trend[i]-x2[i]))>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1793");
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1794");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
CSSA::SSAForecastLast(state,nticks,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1802");
if(errorflag)
return;
for(i=0; i<nticks ; i++)
CAp::SetErrorFlag(errorflag,trend[i]!=x[nlasttracklen-1],"testssaunit.ap:1806");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
datalen=1+CHighQualityRand::HQRndUniformI(rs,10);
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
x=vector<double>::Zeros(datalen);
for(i=0; i<datalen ; i++)
x.Set(i,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAForecastSequence(state,x,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:1817");
if(errorflag)
return;
for(i=0; i<forecastlen ; i++)
CAp::SetErrorFlag(errorflag,trend[i]!=x[datalen-1],"testssaunit.ap:1821");
}
}
//--- Test that for model with non-unit window, some algo being set to calculate ALL
//--- components, and one/few tracks which have at least window length ticks:
//--- * SSAGetBasis() returns full orthonormal basis
//--- * SSAAnalyzeLastWindow() returns last elements of track as trend and zeros
//--- as noise (with minor rounding error)
//--- * SSAAnalyzeLast() returns track as trend and zeros as noise (with
//--- minor rounding error possible). For NTicks>TrackLength result is correctly
//--- prepended with zeros.
//--- * SSAAnalyzeSequence() returns:
//--- * for sequences with length at least window width - sequence as trend,
//--- zeros as noise (up to machine precision)
//--- * for sequences shorter than window width - zeros as trend,
//--- sequence as noise (exactly)
//--- * SSAForecastLast() returns just copies of the last element
//--- * SSAForecastSequence() returns just copies of the last element for long enough
//--- sequences, zeros for shorter sequences
for(pass=1; pass<=passcount; pass++)
{
//--- Generate task; last track is stored in X, its length in NLastTrackLen
windowwidth=1+CHighQualityRand::HQRndUniformI(rs,5);
ntracks=1+CHighQualityRand::HQRndUniformI(rs,3);
mintracklen=windowwidth;
maxtracklen=mintracklen+2*windowwidth;
CSSA::SSACreate(state);
CSSA::SSASetWindow(state,windowwidth);
tracksmatrix=matrix<double>::Zeros(ntracks,maxtracklen);
trackssizes.Resize(ntracks);
nlasttracklen=0;
for(k=0; k<ntracks ; k++)
{
nlasttracklen=mintracklen+CHighQualityRand::HQRndUniformI(rs,maxtracklen-mintracklen+1);
x=vector<double>::Zeros(nlasttracklen);
for(i=0; i<nlasttracklen; i++)
{
x.Set(i,CHighQualityRand::HQRndNormal(rs));
tracksmatrix.Set(k,i,x[i]);
}
trackssizes.Set(k,nlasttracklen);
CSSA::SSAAddSequence(state,x,nlasttracklen);
}
switch(algotype)
{
case 1:
CMatGen::RMatrixRndOrthogonal(windowwidth,b);
CSSA::SSASetAlgoPrecomputed(state,b,windowwidth,windowwidth);
break;
case 2:
CSSA::SSASetAlgoTopKDirect(state,windowwidth);
break;
case 3:
CSSA::SSASetAlgoTopKRealtime(state,windowwidth);
break;
default:
CAp::Assert(false);
errorflag=true;
return;
}
//--- Test
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
CSSA::SSAGetBasis(state,a,sv,windowwidth2,nbasis);
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:1888");
CAp::SetErrorFlag(errorflag,nbasis!=windowwidth,"testssaunit.ap:1889");
CAp::SetErrorFlag(errorflag,CAp::Rows(a)!=windowwidth,"testssaunit.ap:1890");
CAp::SetErrorFlag(errorflag,CAp::Cols(a)!=windowwidth,"testssaunit.ap:1891");
CAp::SetErrorFlag(errorflag,CAp::Len(sv)!=windowwidth,"testssaunit.ap:1892");
for(i=0; i<windowwidth ; i++)
{
for(j=0; j<windowwidth ; j++)
{
v=CAblasF::RDotRR(windowwidth,a,i,a,j);
if(i==j)
v-=1;
CAp::SetErrorFlag(errorflag,MathAbs(v)>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1899");
}
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
windowwidth2=-1;
CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth2);
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:1908");
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:1909");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:1910");
if(errorflag)
return;
for(i=0; i<windowwidth ; i++)
{
CAp::SetErrorFlag(errorflag,(double)(MathAbs(trend[i]-x[i-windowwidth+nlasttracklen]))>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1915");
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1916");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nanalyzed=1+CHighQualityRand::HQRndUniformI(rs,2*maxtracklen);
CSSA::SSAAnalyzeLast(state,nanalyzed,trend,noise);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nanalyzed,"testssaunit.ap:1925");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nanalyzed,"testssaunit.ap:1926");
if(errorflag)
return;
for(i=0; i<MathMax(nanalyzed-nlasttracklen,0) ; i++)
{
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1931");
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:1932");
}
for(i=MathMax(nanalyzed-nlasttracklen,0); i<nanalyzed ; i++)
{
CAp::SetErrorFlag(errorflag,MathAbs(trend[i]-x[i-nanalyzed+nlasttracklen])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1936");
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1937");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
x2=vector<double>::Zeros(nticks);
for(i=0; i<nticks ; i++)
x2.Set(i,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAAnalyzeSequence(state,x2,nticks,trend,noise);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1949");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nticks,"testssaunit.ap:1950");
if(errorflag)
return;
for(i=0; i<nticks ; i++)
{
if(nticks>=windowwidth)
{
CAp::SetErrorFlag(errorflag,(double)(MathAbs(trend[i]-x2[i]))>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1957");
CAp::SetErrorFlag(errorflag,MathAbs(noise[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1958");
}
else
{
CAp::SetErrorFlag(errorflag,MathAbs(trend[i])>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1962");
CAp::SetErrorFlag(errorflag,(double)(MathAbs(noise[i]-x2[i]))>(100.0*CMath::m_machineepsilon),"testssaunit.ap:1963");
}
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
CSSA::SSAForecastLast(state,nticks,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:1972");
if(errorflag)
return;
for(i=0; i<nticks ; i++)
CAp::SetErrorFlag(errorflag,trend[i]!=x[nlasttracklen-1],"testssaunit.ap:1976");
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
if(windowwidth>2)
{
datalen=1+CHighQualityRand::HQRndUniformI(rs,windowwidth-1);
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
x=vector<double>::Zeros(datalen);
for(i=0; i<datalen ; i++)
x.Set(i,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAForecastSequence(state,x,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:1990");
if(errorflag)
return;
for(i=0; i<forecastlen ; i++)
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:1994");
}
datalen=windowwidth+CHighQualityRand::HQRndUniformI(rs,windowwidth);
forecastlen=1+CHighQualityRand::HQRndUniformI(rs,10);
x=vector<double>::Zeros(datalen);
for(i=0; i<datalen ; i++)
x.Set(i,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAForecastSequence(state,x,datalen,forecastlen,CHighQualityRand::HQRndNormal(rs)>0.0,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=forecastlen,"testssaunit.ap:2005");
if(errorflag)
return;
for(i=0; i<forecastlen ; i++)
CAp::SetErrorFlag(errorflag,trend[i]!=x[datalen-1],"testssaunit.ap:2009");
}
}
//--- Test that for model with
//--- * strictly non-unit window
//--- * some algo being set to calculate one leading component
//--- * a few tracks which have at least window length ticks
//--- * and last track which has LESS than window length minus 1 ticks
//--- we have:
//--- * SSAAnalyzeLastWindow() returns zero trend and sequence as noise,
//--- correctly padded by zeros
//--- * SSAAnalyzeLast() returns zero trend and sequence as noise,
//--- correctly padded by zeros
//--- * SSAForecastLast() returns zero trend
//--- This test checks correct handling of the situatuon when last sequence
//--- stored in the dataset is too short.
for(pass=1; pass<=passcount; pass++)
{
//--- Generate task; last track is stored in X, its length in NLastTrackLen
windowwidth=3+CHighQualityRand::HQRndUniformI(rs,5);
ntracks=1+CHighQualityRand::HQRndUniformI(rs,3);
mintracklen=windowwidth;
maxtracklen=mintracklen+2*windowwidth;
CSSA::SSACreate(state);
CSSA::SSASetWindow(state,windowwidth);
for(k=0; k<ntracks ; k++)
{
nlasttracklen=mintracklen+CHighQualityRand::HQRndUniformI(rs,maxtracklen-mintracklen+1);
x=vector<double>::Zeros(nlasttracklen);
for(i=0; i<nlasttracklen; i++)
x.Set(i,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAAddSequence(state,x,nlasttracklen);
}
nlasttracklen=windowwidth-2;
x=vector<double>::Zeros(nlasttracklen);
for(i=0; i<nlasttracklen; i++)
x.Set(i,CHighQualityRand::HQRndNormal(rs));
CSSA::SSAAddSequence(state,x,nlasttracklen);
switch(algotype)
{
case 1:
CMatGen::RMatrixRndOrthogonal(windowwidth,b);
CSSA::SSASetAlgoPrecomputed(state,b,windowwidth,1);
break;
case 2:
CSSA::SSASetAlgoTopKDirect(state,1);
break;
case 3:
CSSA::SSASetAlgoTopKRealtime(state,1);
break;
default:
CAp::Assert(false);
errorflag=true;
return;
}
//--- Test
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
windowwidth2=-1;
CSSA::SSAAnalyzeLastWindow(state,trend,noise,windowwidth2);
CAp::SetErrorFlag(errorflag,windowwidth2!=windowwidth,"testssaunit.ap:2078");
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=windowwidth,"testssaunit.ap:2079");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=windowwidth,"testssaunit.ap:2080");
if(errorflag)
return;
for(i=0; i<windowwidth ; i++)
{
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:2085");
j=nlasttracklen+(i-windowwidth);
if(j>=0)
CAp::SetErrorFlag(errorflag,noise[i]!=x[j],"testssaunit.ap:2088");
else
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:2090");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nanalyzed=1+CHighQualityRand::HQRndUniformI(rs,2*windowwidth);
CSSA::SSAAnalyzeLast(state,nanalyzed,trend,noise);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nanalyzed,"testssaunit.ap:2099");
CAp::SetErrorFlag(errorflag,CAp::Len(noise)!=nanalyzed,"testssaunit.ap:2100");
if(errorflag)
return;
for(i=0; i<MathMax(nanalyzed-nlasttracklen,0) ; i++)
{
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:2105");
CAp::SetErrorFlag(errorflag,noise[i]!=0.0,"testssaunit.ap:2106");
}
for(i=MathMax(nanalyzed-nlasttracklen,0); i<nanalyzed ; i++)
{
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:2110");
CAp::SetErrorFlag(errorflag,noise[i]!=(double)(x[nlasttracklen+(i-nanalyzed)]),"testssaunit.ap:2111");
}
}
if(CHighQualityRand::HQRndUniformR(rs)>skipprob)
{
nticks=1+CHighQualityRand::HQRndUniformI(rs,10);
CSSA::SSAForecastLast(state,nticks,trend);
CAp::SetErrorFlag(errorflag,CAp::Len(trend)!=nticks,"testssaunit.ap:2119");
if(errorflag)
return;
for(i=0; i<nticks ; i++)
CAp::SetErrorFlag(errorflag,trend[i]!=0.0,"testssaunit.ap:2123");
}
}
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class CTestKNNUnit
{
public:
static bool TestKNN(bool silent);
private:
static void TestKNNAlgo(bool&err);
static void UnsetKNN(CKNNModel &model);
static void CTestKNNUnit::TestSetErrors(CKNNModel &model,int nvars,int nout,bool iscls,CMatrixDouble &xy,int npoints,double &avgce,double &relcls0,double &relcls1,double &rms,double &avg,double &avgrel);
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool CTestKNNUnit::TestKNN(bool silent)
{
bool result;
bool knnerrors=false;
bool waserrors=false;
//--- Tests
TestKNNAlgo(knnerrors);
//--- Final report
waserrors=knnerrors;
if(!silent)
{
Print("KNN CLASSIFIER/REGRESSION TEST");
PrintResult("TOTAL RESULTS",!waserrors);
PrintResult("* KNN (AKNN) ALGORITHM",!knnerrors);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| Processing functions test |
//+------------------------------------------------------------------+
void CTestKNNUnit::TestKNNAlgo(bool&err)
{
//--- create variables
int nvars=0;
int nout=0;
bool iscls;
int ny=0;
int k=0;
double eps=0;
CKNNModel model1;
CKNNModel model2;
CKNNModel modelus;
CKNNBuilder builder;
CKNNReport rep;
CKNNReport rep2;
CKNNReport tstrep;
CKNNBuffer buf;
CMatrixDouble xy;
int npoints=0;
CMatrixDouble testxy;
int testnpoints=0;
int pass=0;
int i=0;
int j=0;
bool allsame;
CRowDouble x1;
CRowDouble x2;
CRowDouble y1;
CRowDouble y2;
CRowDouble y3;
double v=0;
double refavgce=0;
double refcls0=0;
double refcls1=0;
double refrms=0;
double refavg=0;
double refavgrel=0;
double maxerr=0;
double avgerr=0;
int cnt=0;
CHighQualityRandState rs;
CHighQualityRand::HQRndRandomize(rs);
//--- Test on randomly generated sets with randomly generated algo settings
//--- We test:
//--- * process
//--- * processi
//--- * process0
//--- * classify
//--- * serialization/unserialization
//--- * reports and test set error evaluation
//--- * KNNRewriteKEps()
for(pass=1; pass<=100; pass++)
{
//--- initialize parameters
nvars=1+CHighQualityRand::HQRndUniformI(rs,5);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
iscls=true;
nout=2+CHighQualityRand::HQRndUniformI(rs,3);
ny=1;
}
else
{
iscls=false;
nout=1+CHighQualityRand::HQRndUniformI(rs,3);
ny=nout;
}
k=1+CHighQualityRand::HQRndUniformI(rs,3);
eps=CHighQualityRand::HQRndUniformI(rs,2)*0.1*CHighQualityRand::HQRndUniformR(rs);
//--- Initialize arrays and data: training and test sets
npoints=10+CHighQualityRand::HQRndUniformI(rs,100);
xy=matrix<double>::Zeros(npoints,nvars+ny);
for(i=0; i<npoints ; i++)
{
for(j=0; j<nvars ; j++)
{
if(j%2==0)
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs));
else
xy.Set(i,j,CHighQualityRand::HQRndUniformI(rs,2));
}
if(iscls)
xy.Set(i,nvars,CHighQualityRand::HQRndUniformI(rs,nout));
else
for(j=0; j<nout; j++)
xy.Set(i,nvars+j,CHighQualityRand::HQRndNormal(rs));
}
testnpoints=10+CHighQualityRand::HQRndUniformI(rs,100);
testxy=matrix<double>::Zeros(testnpoints,nvars+ny);
for(i=0; i<testnpoints ; i++)
{
for(j=0; j<nvars ; j++)
{
if(j%2==0)
testxy.Set(i,j,CHighQualityRand::HQRndNormal(rs));
else
testxy.Set(i,j,CHighQualityRand::HQRndUniformI(rs,2));
}
if(iscls)
testxy.Set(i,nvars,CHighQualityRand::HQRndUniformI(rs,nout));
else
for(j=0; j<nout; j++)
testxy.Set(i,nvars+j,CHighQualityRand::HQRndNormal(rs));
}
//--- create model
UnsetKNN(model1);
CKNN::KNNBuilderCreate(builder);
if(iscls)
CKNN::KNNBuilderSetDatasetCLS(builder,xy,npoints,nvars,nout);
else
CKNN::KNNBuilderSetDatasetReg(builder,xy,npoints,nvars,nout);
CKNN::KNNBuilderSetNorm(builder,CHighQualityRand::HQRndUniformI(rs,3));
CKNN::KNNBuilderBuildKNNModel(builder,k,eps,model1,rep);
CKNN::KNNCreateBuffer(model1,buf);
//--- Check that:
//--- * KNNProcess() does not shrink output array, only increases
//--- * KNNTsProcess() does not shrink output array, only increases
//--- * KNNProcessI() fits length exactly to NOut
x1=vector<double>::Zeros(nvars);
for(i=0; i<nvars ; i++)
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
y1=vector<double>::Zeros(nout+1);
CKNN::KNNProcess(model1,x1,y1);
CAp::SetErrorFlag(err,CAp::Len(y1)!=nout+1,"testknnunit.ap:200");
y1.Resize(nout-1);
CKNN::KNNProcess(model1,x1,y1);
CAp::SetErrorFlag(err,CAp::Len(y1)!=nout,"testknnunit.ap:203");
y1=vector<double>::Zeros(nout+1);
CKNN::KNNTsProcess(model1,buf,x1,y1);
CAp::SetErrorFlag(err,CAp::Len(y1)!=nout+1,"testknnunit.ap:207");
y1.Resize(nout-1);
CKNN::KNNTsProcess(model1,buf,x1,y1);
CAp::SetErrorFlag(err,CAp::Len(y1)!=nout,"testknnunit.ap:210");
y1=vector<double>::Zeros(nout+1);
CKNN::KNNProcessI(model1,x1,y1);
CAp::SetErrorFlag(err,CAp::Len(y1)!=nout,"testknnunit.ap:214");
y1.Resize(nout-1);
CKNN::KNNProcessI(model1,x1,y1);
CAp::SetErrorFlag(err,CAp::Len(y1)!=nout,"testknnunit.ap:217");
if(err)
return;
//--- KNNProcess(), KNNProcessI() and KNNTsProcess() return same results
x1=vector<double>::Zeros(nvars);
for(i=0; i<nvars ; i++)
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
CKNN::KNNProcess(model1,x1,y1);
CKNN::KNNProcessI(model1,x1,y2);
CKNN::KNNTsProcess(model1,buf,x1,y3);
for(i=0; i<nout; i++)
{
CAp::SetErrorFlag(err,MathAbs(y2[i]-y1[i])>(100.0*CMath::m_machineepsilon),"testknnunit.ap:232");
CAp::SetErrorFlag(err,MathAbs(y3[i]-y1[i])>(100.0*CMath::m_machineepsilon),"testknnunit.ap:233");
}
//--- Same inputs leads to same outputs
x1=vector<double>::Zeros(nvars);
x2=vector<double>::Zeros(nvars);
y1=vector<double>::Zeros(nout);
y2=vector<double>::Zeros(nout);
for(i=0; i<nvars ; i++)
{
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
x2.Set(i,x1[i]);
}
for(i=0; i<nout; i++)
{
y1.Set(i,CHighQualityRand::HQRndNormal(rs));
y2.Set(i,CHighQualityRand::HQRndNormal(rs));
}
CKNN::KNNProcess(model1,x1,y1);
CKNN::KNNProcess(model1,x2,y2);
allsame=true;
for(i=0; i<nout; i++)
allsame=allsame && y1[i]==y2[i];
CAp::SetErrorFlag(err,!allsame,"testknnunit.ap:259");
//--- Same inputs on original forest leads to same outputs
//--- on copy created using KNNSerialize
x1=vector<double>::Zeros(nvars);
x2=vector<double>::Zeros(nvars);
y1=vector<double>::Zeros(nout);
y2=vector<double>::Zeros(nout);
UnsetKNN(modelus);
{
//--- This code passes data structure through serializers
//--- (serializes it to string and loads back)
CSerializer _local_serializer;
string _local_str;
_local_serializer.Alloc_Start();
CKNN::KNNAlloc(_local_serializer,model1);
_local_serializer.Alloc_Entry();
_local_serializer.SStart_Str();
CKNN::KNNSerialize(_local_serializer,model1);
_local_serializer.Stop();
_local_str=_local_serializer.Get_String();
//---
_local_serializer.Reset();
_local_serializer.UStart_Str(_local_str);
CKNN::KNNUnserialize(_local_serializer,modelus);
_local_serializer.Stop();
}
for(i=0; i<nvars ; i++)
{
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
x2.Set(i,x1[i]);
}
for(i=0; i<nout; i++)
{
y1.Set(i,CHighQualityRand::HQRndNormal(rs));
y2.Set(i,CHighQualityRand::HQRndNormal(rs));
}
CKNN::KNNProcess(model1,x1,y1);
CKNN::KNNProcess(modelus,x2,y2);
allsame=true;
for(i=0; i<nout; i++)
allsame=allsame && y1[i]==y2[i];
CAp::SetErrorFlag(err,!allsame,"testknnunit.ap:287");
for(i=0; i<nvars ; i++)
{
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
x2.Set(i,x1[i]);
}
CAp::SetErrorFlag(err,MathAbs(CKNN::KNNProcess0(model1,x1)-CKNN::KNNProcess0(modelus,x2))>(100.0*CMath::m_machineepsilon),"testknnunit.ap:293");
for(i=0; i<nvars ; i++)
{
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
x2.Set(i,x1[i]);
}
CAp::SetErrorFlag(err,CKNN::KNNClassify(model1,x1)!=CKNN::KNNClassify(modelus,x2),"testknnunit.ap:299");
//--- KNNProcess0 works as expected
x1=vector<double>::Zeros(nvars);
x2=vector<double>::Zeros(nvars);
y1=vector<double>::Zeros(nout);
for(i=0; i<nvars ; i++)
{
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
x2.Set(i,x1[i]);
}
for(i=0; i<nout; i++)
y1.Set(i,CHighQualityRand::HQRndNormal(rs));
CKNN::KNNProcess(model1,x1,y1);
CAp::SetErrorFlag(err,MathAbs(y1[0]-CKNN::KNNProcess0(model1,x2))>(100.0*CMath::m_machineepsilon),"testknnunit.ap:318");
//--- KNNClassify works as expected
x1=vector<double>::Zeros(nvars);
x2=vector<double>::Zeros(nvars);
y1=vector<double>::Zeros(nout);
for(i=0; i<nvars ; i++)
{
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
x2.Set(i,x1[i]);
}
if(iscls)
{
for(i=0; i<nout; i++)
y1.Set(i,CHighQualityRand::HQRndNormal(rs));
CKNN::KNNProcess(model1,x1,y1);
j=CKNN::KNNClassify(model1,x2);
for(i=0; i<nout; i++)
CAp::SetErrorFlag(err,y1[i]>y1[j],"testknnunit.ap:341");
}
else
CAp::SetErrorFlag(err,CKNN::KNNClassify(model1,x2)!=-1,"testknnunit.ap:344");
//--- Normalization properties
if(iscls)
{
for(i=0; i<nvars ; i++)
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
CKNN::KNNProcess(model1,x1,y1);
v=0;
for(i=0; i<nout; i++)
{
v+=y1[i];
CAp::SetErrorFlag(err,y1[i]<0.0,"testknnunit.ap:359");
}
CAp::SetErrorFlag(err,MathAbs(v-1)>(1000.0*CMath::m_machineepsilon),"testknnunit.ap:361");
}
//--- Test various error metrics reported by algorithm:
//--- * training set errors included in the report
//--- * errors on the test set
//--- NOTE: cross-entropy metric is tested only for training set.
//--- because model can produce exactly zero probabilities
//--- for completely unknown values
TestSetErrors(model1,nvars,nout,iscls,xy,npoints,refavgce,refcls0,refcls1,refrms,refavg,refavgrel);
if(iscls)
{
CAp::SetErrorFlag(err,MathAbs(rep.m_AvgCE-refavgce)>1.0E-6,"testknnunit.ap:377");
CAp::SetErrorFlag(err,MathAbs(CKNN::KNNAvgCE(model1,xy,npoints)-refavgce)>1.0E-6,"testknnunit.ap:378");
CAp::SetErrorFlag(err,rep.m_RelCLSError<(refcls0-1.0E-6),"testknnunit.ap:381");
CAp::SetErrorFlag(err,rep.m_RelCLSError>(refcls1+1.0E-6),"testknnunit.ap:382");
CAp::SetErrorFlag(err,CKNN::KNNRelClsError(model1,xy,npoints)<(refcls0-1.0E-6),"testknnunit.ap:383");
CAp::SetErrorFlag(err,CKNN::KNNRelClsError(model1,xy,npoints)>(refcls1+1.0E-6),"testknnunit.ap:384");
}
else
{
CAp::SetErrorFlag(err,rep.m_AvgCE!=0.0,"testknnunit.ap:389");
CAp::SetErrorFlag(err,CKNN::KNNAvgCE(model1,xy,npoints)!=0.0,"testknnunit.ap:390");
CAp::SetErrorFlag(err,rep.m_RelCLSError!=0.0,"testknnunit.ap:393");
CAp::SetErrorFlag(err,CKNN::KNNRelClsError(model1,xy,npoints)!=0.0,"testknnunit.ap:394");
}
CAp::SetErrorFlag(err,MathAbs(rep.m_RMSError-refrms)>1.0E-6,"testknnunit.ap:396");
CAp::SetErrorFlag(err,MathAbs(rep.m_AvgError-refavg)>1.0E-6,"testknnunit.ap:397");
CAp::SetErrorFlag(err,MathAbs(rep.m_AvgRelError-refavgrel)>1.0E-6,"testknnunit.ap:398");
CAp::SetErrorFlag(err,MathAbs(CKNN::KNNRMSError(model1,xy,npoints)-refrms)>1.0E-6,"testknnunit.ap:399");
CAp::SetErrorFlag(err,MathAbs(CKNN::KNNAvgError(model1,xy,npoints)-refavg)>1.0E-6,"testknnunit.ap:400");
CAp::SetErrorFlag(err,MathAbs(CKNN::KNNAvgRelError(model1,xy,npoints)-refavgrel)>1.0E-6,"testknnunit.ap:401");
TestSetErrors(model1,nvars,nout,iscls,testxy,testnpoints,refavgce,refcls0,refcls1,refrms,refavg,refavgrel);
CKNN::KNNAllErrors(model1,testxy,testnpoints,tstrep);
if(iscls)
{
CAp::SetErrorFlag(err,tstrep.m_RelCLSError<(refcls0-1.0E-6),"testknnunit.ap:406");
CAp::SetErrorFlag(err,tstrep.m_RelCLSError>(refcls1+1.0E-6),"testknnunit.ap:407");
}
else
{
CAp::SetErrorFlag(err,tstrep.m_AvgCE!=0.0,"testknnunit.ap:411");
CAp::SetErrorFlag(err,tstrep.m_RelCLSError!=0.0,"testknnunit.ap:412");
}
CAp::SetErrorFlag(err,MathAbs(tstrep.m_RMSError-refrms)>1.0E-6,"testknnunit.ap:414");
CAp::SetErrorFlag(err,MathAbs(tstrep.m_AvgError-refavg)>1.0E-6,"testknnunit.ap:415");
CAp::SetErrorFlag(err,MathAbs(tstrep.m_AvgRelError-refavgrel)>1.0E-6,"testknnunit.ap:416");
}
//--- Test that model built with K=1 and Eps=0 remembers all training cases
for(nvars=1; nvars<=4; nvars++)
{
for(pass=0; pass<=10; pass++)
{
//--- Prepare task
npoints=1+CHighQualityRand::HQRndUniformI(rs,20);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
iscls=true;
nout=2+CHighQualityRand::HQRndUniformI(rs,3);
ny=1;
}
else
{
iscls=false;
nout=1+CHighQualityRand::HQRndUniformI(rs,3);
ny=nout;
}
xy=matrix<double>::Zeros(npoints,nvars+ny);
for(i=0; i<npoints ; i++)
{
xy.Set(i,0,i*0.000001);
for(j=1; j<nvars ; j++)
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs));
if(iscls)
xy.Set(i,nvars,CHighQualityRand::HQRndUniformI(rs,nout));
else
for(j=0; j<nout; j++)
xy.Set(i,nvars+j,CHighQualityRand::HQRndNormal(rs));
}
//--- Create model
UnsetKNN(model1);
CKNN::KNNBuilderCreate(builder);
if(iscls)
CKNN::KNNBuilderSetDatasetCLS(builder,xy,npoints,nvars,nout);
else
CKNN::KNNBuilderSetDatasetReg(builder,xy,npoints,nvars,nout);
CKNN::KNNBuilderSetNorm(builder,CHighQualityRand::HQRndUniformI(rs,3));
CKNN::KNNBuilderBuildKNNModel(builder,1,0.0,model1,rep);
CAp::SetErrorFlag(err,rep.m_AvgCE>0.0,"testknnunit.ap:467");
CAp::SetErrorFlag(err,rep.m_RelCLSError>0.0,"testknnunit.ap:468");
CAp::SetErrorFlag(err,rep.m_RMSError>1.0E-6,"testknnunit.ap:469");
CAp::SetErrorFlag(err,rep.m_AvgError>1.0E-6,"testknnunit.ap:470");
CAp::SetErrorFlag(err,rep.m_AvgRelError>1.0E-6,"testknnunit.ap:471");
x1=vector<double>::Zeros(nvars);
for(i=0; i<npoints ; i++)
{
for(j=0; j<nvars ; j++)
x1.Set(j,xy.Get(i,j));
CKNN::KNNProcess(model1,x1,y1);
if(iscls)
CAp::SetErrorFlag(err,MathAbs(y1[(int)MathRound(xy.Get(i,nvars))]-1.0)>(10.0*CMath::m_machineepsilon),"testknnunit.ap:479");
else
{
for(j=0; j<nout; j++)
CAp::SetErrorFlag(err,MathAbs(y1[j]-xy.Get(i,nvars+j))>(10.0*CMath::m_machineepsilon),"testknnunit.ap:483");
}
}
}
}
//--- Test that model rewrite works as expected
for(nvars=1; nvars<=20; nvars++)
{
//--- Prepare task
npoints=50+5*nvars;
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
iscls=true;
nout=2+CHighQualityRand::HQRndUniformI(rs,3);
ny=1;
}
else
{
iscls=false;
nout=1+CHighQualityRand::HQRndUniformI(rs,3);
ny=nout;
}
xy=matrix<double>::Zeros(npoints,nvars+ny);
for(i=0; i<npoints ; i++)
{
for(j=0; j<nvars ; j++)
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs));
if(iscls)
xy.Set(i,nvars,CHighQualityRand::HQRndUniformI(rs,nout));
else
for(j=0; j<nout; j++)
xy.Set(i,nvars+j,CHighQualityRand::HQRndNormal(rs));
}
//--- Select K, Eps
k=1+CHighQualityRand::HQRndUniformI(rs,10);
eps=CHighQualityRand::HQRndUniformI(rs,2)*(1+3*CHighQualityRand::HQRndUniformR(rs));
//--- Create model 1 using K, EPS.
//--- Create model 2 using other (K,Eps), then rewrite search settings
CKNN::KNNBuilderCreate(builder);
if(iscls)
CKNN::KNNBuilderSetDatasetCLS(builder,xy,npoints,nvars,nout);
else
CKNN::KNNBuilderSetDatasetReg(builder,xy,npoints,nvars,nout);
UnsetKNN(model1);
CKNN::KNNBuilderBuildKNNModel(builder,k,eps,model1,rep);
UnsetKNN(model2);
CKNN::KNNBuilderBuildKNNModel(builder,1+CHighQualityRand::HQRndUniformI(rs,10),CHighQualityRand::HQRndUniformI(rs,2)*(1+3*CHighQualityRand::HQRndUniformR(rs)),model2,rep2);
CKNN::KNNRewriteKEps(model2,k,eps);
x1=vector<double>::Zeros(nvars);
x2=vector<double>::Zeros(nvars);
for(i=0; i<nvars ; i++)
{
x1.Set(i,CHighQualityRand::HQRndNormal(rs));
x2.Set(i,x1[i]);
}
CKNN::KNNProcess(model1,x1,y1);
CKNN::KNNProcess(model2,x2,y2);
for(i=0; i<nout; i++)
CAp::SetErrorFlag(err,MathAbs(y1[i]-y2[i])>(1000.0*CMath::m_machineepsilon),"testknnunit.ap:553");
}
//--- Test 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
//--- An algorithm with K=100 and Eps={0,0.1} is tested
for(pass=0; pass<=1; pass++)
{
//--- Prepare task
npoints=3000;
xy=matrix<double>::Zeros(npoints,2);
for(i=0; i<npoints ; i++)
{
xy.Set(i,0,3*CHighQualityRand::HQRndUniformR(rs));
if(xy.Get(i,0)<=1.0)
xy.Set(i,1,0);
else
{
if(xy.Get(i,0)<=2.0)
{
if(CHighQualityRand::HQRndUniformR(rs)<(xy.Get(i,0)-1.0))
xy.Set(i,1,1.0);
else
xy.Set(i,1,0);
}
else
xy.Set(i,1,1);
}
}
//--- Select K and Eps, create model
k=100;
eps=0.1*(pass%2);
UnsetKNN(model1);
CKNN::KNNBuilderCreate(builder);
CKNN::KNNBuilderSetDatasetCLS(builder,xy,npoints,1,2);
CKNN::KNNBuilderSetNorm(builder,CHighQualityRand::HQRndUniformI(rs,3));
CKNN::KNNBuilderBuildKNNModel(builder,k,eps,model1,rep);
//--- Check model quality
x1=vector<double>::Zeros(1);
while(x1[0]<=3.0)
{
CKNN::KNNProcess(model1,x1,y1);
CAp::SetErrorFlag(err,y1[0]<0.0,"testknnunit.ap:606");
CAp::SetErrorFlag(err,y1[1]<0.0,"testknnunit.ap:607");
CAp::SetErrorFlag(err,MathAbs(y1[0]+y1[1]-1)>(1000.0*CMath::m_machineepsilon),"testknnunit.ap:608");
if(x1[0]<1.0)
CAp::SetErrorFlag(err,y1[0]<0.8,"testknnunit.ap:610");
if(x1[0]>=1.0 && x1[0]<=2.0)
CAp::SetErrorFlag(err,MathAbs(y1[1]-(x1[0]-1))>0.5,"testknnunit.ap:612");
if(x1[0]>2.0)
CAp::SetErrorFlag(err,y1[1]<0.8,"testknnunit.ap:614");
x1.Add(0,0.01);
}
}
//--- Test simple regression task without noise:
//--- * |x|<1, |y|<1
//--- * F(x,y) = x^2+y
//--- A model with K=50 is used, we check that
//--- * Eps=0 is good enough
//--- * having Eps>0 results in worse error metrics, but not too bad
//--- Prepare task
npoints=5000;
xy=matrix<double>::Zeros(npoints,3);
for(i=0; i<npoints ; i++)
{
xy.Set(i,0,2*CHighQualityRand::HQRndUniformR(rs)-1);
xy.Set(i,1,2*CHighQualityRand::HQRndUniformR(rs)-1);
xy.Set(i,2,CMath::Sqr(xy.Get(i,0))+xy.Get(i,1));
}
//--- Build model with Eps=0, check model quality
k=50;
UnsetKNN(model1);
CKNN::KNNBuilderCreate(builder);
CKNN::KNNBuilderSetDatasetReg(builder,xy,npoints,2,1);
CKNN::KNNBuilderSetNorm(builder,CHighQualityRand::HQRndUniformI(rs,3));
CKNN::KNNBuilderBuildKNNModel(builder,k,0.0,model1,rep);
x1=vector<double>::Zeros(2);
maxerr=0;
avgerr=0;
cnt=0;
for(i=0; i<=1000; i++)
{
//--- NOTE: we test model in the inner points, it deteriorates near the bounds
x1.Set(0,0.8*(2*CHighQualityRand::HQRndUniformR(rs)-1));
x1.Set(1,0.8*(2*CHighQualityRand::HQRndUniformR(rs)-1));
v=CMath::Sqr(x1[0])+x1[1];
CKNN::KNNProcess(model1,x1,y1);
v=MathAbs(y1[0]-v);
maxerr=MathMax(maxerr,v);
avgerr+=v;
cnt++;
}
avgerr/=cnt;
CAp::SetErrorFlag(err,maxerr>0.15,"testknnunit.ap:669");
CAp::SetErrorFlag(err,avgerr>0.05,"testknnunit.ap:670");
//--- Build model with Eps=1.0, compare error metrics with Eps=0
CKNN::KNNBuilderBuildKNNModel(builder,k,1.0,model2,rep2);
CAp::SetErrorFlag(err,rep2.m_RMSError<=rep.m_RMSError,"testknnunit.ap:676");
CAp::SetErrorFlag(err,rep2.m_RMSError<=(rep.m_RMSError+0.001),"testknnunit.ap:677");
CAp::SetErrorFlag(err,rep2.m_RMSError>=(rep.m_RMSError+0.020),"testknnunit.ap:678");
}
//+------------------------------------------------------------------+
//| Unsets model |
//+------------------------------------------------------------------+
void CTestKNNUnit::UnsetKNN(CKNNModel &model)
{
//--- create variables
CMatrixDouble xy;
CKNNBuilder builder;
CKNNReport rep;
xy=matrix<double>::Zeros(1,2);
xy.Set(0,1,CMath::RandomReal()-0.5);
CKNN::KNNBuilderCreate(builder);
CKNN::KNNBuilderSetDatasetReg(builder,xy,1,1,1);
CKNN::KNNBuilderBuildKNNModel(builder,1,0,model,rep);
model.m_nvars=-1;
model.m_nout=-1;
model.m_k=-1;
model.m_eps=0;
model.m_isdummy=true;
}
//+------------------------------------------------------------------+
//| Test set errors. |
//| Computes test set errors: |
//| * average cross-entropy |
//| * relative classification error (a range [RelCls0,RelCls1] is |
//| returned because classification error can be computed |
//| differently in the presence of the ties). All possible values|
//| of the classification error, no matter how ties are resolved,|
//| are guaranteed to be within [RelCls0,RelCls1]. |
//+------------------------------------------------------------------+
void CTestKNNUnit::TestSetErrors(CKNNModel &model,int nvars,
int nout,bool iscls,
CMatrixDouble &xy,int npoints,
double &avgce,double &relcls0,
double &relcls1,double &rms,
double &avg,double &avgrel)
{
//--- create variables
CRowDouble x;
CRowDouble y;
CRowDouble ey;
int relcnt=0;
int i=0;
int j=0;
double v=0;
double mxy=0;
avgce=0;
relcls0=0;
relcls1=0;
rms=0;
avg=0;
avgrel=0;
x=vector<double>::Zeros(nvars);
y=vector<double>::Zeros(nout);
ey=vector<double>::Zeros(nout);
avgce=0;
relcls0=0;
relcls1=0;
rms=0;
avg=0;
avgrel=0;
for(i=0; i<npoints ; i++)
{
for(j=0; j<nvars ; j++)
x.Set(j,xy.Get(i,j));
CKNN::KNNProcess(model,x,y);
if(iscls)
{
for(j=0; j<nout; j++)
ey.Set(j,0);
ey.Set((int)MathRound(xy.Get(i,nvars)),1);
}
else
{
for(j=0; j<nout; j++)
ey.Set(j,xy.Get(i,nvars+j));
}
for(j=0; j<nout; j++)
{
v=y[j]-ey[j];
if(iscls)
avgce-=ey[j]*MathLog(y[j]+CMath::m_minrealnumber);
rms+=CMath::Sqr(v);
avg+=MathAbs(v);
if(ey[j]!=0.0)
{
avgrel+=MathAbs(v/ey[j]);
relcnt ++;
}
}
if(iscls)
{
mxy=0;
for(j=0; j<nout; j++)
mxy=MathMax(mxy,y[j]);
if(y[(int)MathRound(xy.Get(i,nvars))]==mxy)
{
//--- RelCls0 is NOT increased because correct value achieves maximum.
//--- However, if ties are present, we have to increase RelCls1, upper bound
//--- of the uncertainty interval for the classification error.
for(j=0; j<nout; j++)
{
if(j!=(int)MathRound(xy.Get(i,nvars)) && y[j]==mxy)
{
relcls1=relcls1+1;
break;
}
}
}
else
{
//--- Both bounds of the error range are increased by 1
relcls0++;
relcls1++;
}
}
}
relcls0/=npoints;
relcls1/=npoints;
avgce/=npoints;
rms=MathSqrt(rms/(npoints*nout));
avg/=(npoints*nout);
avgrel/=CApServ::Coalesce(relcnt,1.0);
}
//+------------------------------------------------------------------+
//| Testing clustering |
//+------------------------------------------------------------------+
class CTestClusteringUnit
{
public:
static bool TestClustering(bool silent);
private:
static bool BasicAHCTests(void);
static bool AdvancedAHCTests(void);
static void KMeansSimpleTest1(int nvars,int nc,int passcount,bool &converrors,bool &othererrors,bool &simpleerrors);
static void KMeansSpecialTests(bool &othererrors);
static void KMeansInfiniteLoopTest(bool &othererrors);
static void KMeansRestartsTest(bool &converrors,bool &restartserrors);
static double RNormal(void);
static void RSphere(CMatrixDouble &xy,int n,int i);
static double DistFunc(CRowDouble &x0,CRowDouble &x1,int d,int disttype);
static bool ErrorsInMerges(CMatrixDouble &d,CMatrixDouble &xy,int npoints,int nf,CAHCReport &rep,int ahcalgo);
static void KMeansReferenceUpdateDistances(CMatrixDouble &xy,int npoints,int nvars,CMatrixDouble &ct,int k,CRowInt &xyc,CRowDouble &xydist2);
};
//+------------------------------------------------------------------+
//| Testing clustering |
//+------------------------------------------------------------------+
bool CTestClusteringUnit::TestClustering(bool silent)
{
//--- create variables
bool result;
bool waserrors;
bool basicahcerrors;
bool ahcerrors;
bool kmeansconverrors;
bool kmeanssimpleerrors;
bool kmeansothererrors;
bool kmeansrestartserrors;
int passcount=0;
int nf=0;
int nc=0;
//--- AHC tests
basicahcerrors=BasicAHCTests();
ahcerrors=AdvancedAHCTests();
//
//--- k-means tests
//
passcount=10;
kmeansconverrors=false;
kmeansothererrors=false;
kmeanssimpleerrors=false;
kmeansrestartserrors=false;
KMeansSpecialTests(kmeansothererrors);
KMeansInfiniteLoopTest(kmeansothererrors);
KMeansRestartsTest(kmeansconverrors,kmeansrestartserrors);
for(nf=1; nf<=5; nf++)
for(nc=1; nc<=5; nc++)
KMeansSimpleTest1(nf,nc,passcount,kmeansconverrors,kmeansothererrors,kmeanssimpleerrors);
//--- Results
waserrors=false;
waserrors=waserrors||(basicahcerrors||ahcerrors);
waserrors=waserrors||(kmeansconverrors||kmeansothererrors||kmeanssimpleerrors||kmeansrestartserrors);
if(!silent)
{
Print("TESTING CLUSTERING");
Print("AHC:");
PrintResult("* BASIC TESTS",!basicahcerrors);
PrintResult("* GENERAL TESTS",!ahcerrors);
Print("K-MEANS:");
PrintResult("* CONVERGENCE",!kmeansconverrors);
PrintResult("* SIMPLE TASKS",!kmeanssimpleerrors);
PrintResult("* OTHER PROPERTIES",!kmeansothererrors);
PrintResult("* RESTARTS PROPERTIES",!kmeansrestartserrors);
PrintResult("TEST SUMMARY",!waserrors);
Print("\n");
}
//--- return result
result=!waserrors;
return(result);
}
//+------------------------------------------------------------------+
//| Basic agglomerative hierarchical clustering tests: |
//| returns True on failure, False on success. |
//| Basic tests study algorithm behavior on simple, hand-made |
//| datasets with small number of points (1..10). |
//+------------------------------------------------------------------+
bool CTestClusteringUnit::BasicAHCTests(void)
{
//--- create variables
bool result=true;
CClusterizerState s;
CAHCReport rep;
CMatrixDouble xy;
CMatrixDouble d;
CMatrixDouble c;
bool berr;
int ahcalgo=0;
int i=0;
int j=0;
int npoints=0;
int k=0;
CRowInt cidx;
CRowInt cz;
CRowInt cidx2;
CRowInt cz2;
//--- Test on empty problem
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerRunAHC(s,rep);
if(rep.m_NPoints!=0)
return(result);
//--- Test on problem with one point
xy=matrix<double>::Zeros(1,2);
xy.Set(0,0,CMath::RandomReal());
xy.Set(0,1,CMath::RandomReal());
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,1,2,0);
CClustering::ClusterizerRunAHC(s,rep);
if(rep.m_NPoints!=1)
return(result);
//--- Test on problem with two points
xy=matrix<double>::Zeros(2,2);
xy.Set(0,0,CMath::RandomReal());
xy.Set(0,1,CMath::RandomReal());
xy.Set(1,0,CMath::RandomReal());
xy.Set(1,1,CMath::RandomReal());
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,2,2,0);
CClustering::ClusterizerRunAHC(s,rep);
if((rep.m_NPoints!=2 || CAp::Rows(rep.m_z)!=1) || CAp::Cols(rep.m_z)!=2)
return(result);
if(rep.m_z.Get(0,0)!=0 || rep.m_z.Get(0,1)!=1)
return(result);
//--- Test on specially designed problem which should have
//--- following dendrogram:
//--- ------
//--- | |
//--- ---- ----
//--- | | | |
//--- 0 1 2 3
//--- ...with first merge performed on 0 and 1, second merge
//--- performed on 2 and 3. Complete linkage is used.
//--- Additionally we test ClusterizerSeparatedByDist() on this
//--- problem for different distances. Test is performed by
//--- comparing function result with ClusterizerGetKClusters()
//--- for known K.
xy=matrix<double>::Zeros(4,1);
xy.Set(1,0,1.0);
xy.Set(2,0,3.0);
xy.Set(3,0,4.1);
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,4,1,0);
CClustering::ClusterizerSetAHCAlgo(s,0);
CClustering::ClusterizerRunAHC(s,rep);
if(rep.m_NPoints!=4 || CAp::Rows(rep.m_z)!=3 || CAp::Cols(rep.m_z)!=2 || CAp::Rows(rep.m_pz)!=3 || CAp::Cols(rep.m_pz)!=2)
return(result);
berr=false;
berr=(berr||rep.m_z.Get(0,0)!=0||rep.m_z.Get(0,1)!=1);
berr=(berr||rep.m_z.Get(1,0)!=2||rep.m_z.Get(1,1)!=3);
berr=(berr||rep.m_z.Get(2,0)!=4||rep.m_z.Get(2,1)!=5);
berr=(berr||rep.m_p[0]!=0||rep.m_p[1]!=1||rep.m_p[2]!=2||rep.m_p[3]!=3);
berr=(berr||rep.m_pz.Get(0,0)!=0||rep.m_pz.Get(0,1)!=1);
berr=(berr||rep.m_pz.Get(1,0)!=2||rep.m_pz.Get(1,1)!=3);
berr=(berr||rep.m_pz.Get(2,0)!=4||rep.m_pz.Get(2,1)!=5);
berr=(berr||rep.m_pm.Get(0,0)!=0||rep.m_pm.Get(0,1)!=0||rep.m_pm.Get(0,2)!=1||rep.m_pm.Get(0,3)!=1);
berr=(berr||rep.m_pm.Get(1,0)!=2||rep.m_pm.Get(1,1)!=2||rep.m_pm.Get(1,2)!=3||rep.m_pm.Get(1,3)!=3);
berr=(berr||rep.m_pm.Get(2,0)!=0||rep.m_pm.Get(2,1)!=1||rep.m_pm.Get(2,2)!=2||rep.m_pm.Get(2,3)!=3);
if(berr)
return(result);
CClustering::ClusterizerSeparatedByDist(rep,0.5,k,cidx,cz);
CClustering::ClusterizerGetKClusters(rep,4,cidx2,cz2);
if(k!=4)
return(result);
if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3])
return(result);
if(cz[0]!=cz2[0] || cz[1]!=cz2[1] || cz[2]!=cz2[2] || cz[3]!=cz2[3])
return(result);
CClustering::ClusterizerSeparatedByDist(rep,1.05,k,cidx,cz);
CClustering::ClusterizerGetKClusters(rep,3,cidx2,cz2);
if(k!=3)
return(result);
if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3])
return(result);
if(cz[0]!=cz2[0] || cz[1]!=cz2[1] || cz[2]!=cz2[2])
return(result);
CClustering::ClusterizerSeparatedByDist(rep,1.15,k,cidx,cz);
CClustering::ClusterizerGetKClusters(rep,2,cidx2,cz2);
if(k!=2)
return(result);
if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3])
return(result);
if(cz[0]!=cz2[0] || cz[1]!=cz2[1])
return(result);
//--- Test on specially designed problem with Pearson distance
//--- which should have following dendrogram:
//--- ------
//--- | |
//--- ---- ----
//--- | | | |
//--- 0 1 2 3
//--- This problem is used to test ClusterizerSeparatedByDist().
//--- The test is performed by comparing function result with
//--- ClusterizerGetKClusters() for known K.
//--- NOTE:
//--- * corr(a0,a1) = 0.866
//--- * corr(a2,a3) = 0.990
//--- * corr(a0/a1, a2/a3)<=0.5
xy=matrix<double>::Zeros(4,3);
xy.Set(0,0,0.3);
xy.Set(0,1,0.5);
xy.Set(0,2,0.3);
xy.Set(1,0,0.3);
xy.Set(1,1,0.5);
xy.Set(1,2,0.4);
xy.Set(2,0,0.1);
xy.Set(2,1,0.5);
xy.Set(2,2,0.9);
xy.Set(3,0,0.1);
xy.Set(3,1,0.4);
xy.Set(3,2,0.9);
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,4,3,10);
CClustering::ClusterizerSetAHCAlgo(s,1);
CClustering::ClusterizerRunAHC(s,rep);
CClustering::ClusterizerSeparatedByCorr(rep,0.999,k,cidx,cz);
CClustering::ClusterizerGetKClusters(rep,4,cidx2,cz2);
if(k!=4)
return(result);
if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3])
return(result);
if(cz[0]!=cz2[0] || cz[1]!=cz2[1] || cz[2]!=cz2[2] || cz[3]!=cz2[3])
return(result);
CClustering::ClusterizerSeparatedByCorr(rep,0.900,k,cidx,cz);
CClustering::ClusterizerGetKClusters(rep,3,cidx2,cz2);
if(k!=3)
return(result);
if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3])
return(result);
if(cz[0]!=cz2[0] || cz[1]!=cz2[1] || cz[2]!=cz2[2])
return(result);
CClustering::ClusterizerSeparatedByCorr(rep,0.600,k,cidx,cz);
CClustering::ClusterizerGetKClusters(rep,2,cidx2,cz2);
if(k!=2)
return(result);
if(cidx[0]!=cidx2[0] || cidx[1]!=cidx2[1] || cidx[2]!=cidx2[2] || cidx[3]!=cidx2[3])
return(result);
if(cz[0]!=cz2[0] || cz[1]!=cz2[1])
return(result);
//--- Single linkage vs. complete linkage
xy=matrix<double>::Zeros(6,1);
xy.Set(0,0,0.0);
xy.Set(1,0,1.0);
xy.Set(2,0,2.1);
xy.Set(3,0,3.3);
xy.Set(4,0,6.0);
xy.Set(5,0,4.6);
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,6,1,0);
CClustering::ClusterizerSetAHCAlgo(s,0);
CClustering::ClusterizerRunAHC(s,rep);
if(rep.m_NPoints!=6 || CAp::Len(rep.m_p)!=6)
return(result);
if(CAp::Rows(rep.m_z)!=5 || CAp::Cols(rep.m_z)!=2 || CAp::Rows(rep.m_pz)!=5 || CAp::Cols(rep.m_pz)!=2)
return(result);
berr=false;
berr=berr||rep.m_p[0]!=2;
berr=berr||rep.m_p[1]!=3;
berr=berr||rep.m_p[2]!=4;
berr=berr||rep.m_p[3]!=5;
berr=berr||rep.m_p[4]!=0;
berr=berr||rep.m_p[5]!=1;
berr=(berr||rep.m_z.Get(0,0)!=0)||rep.m_z.Get(0,1)!=1;
berr=(berr||rep.m_z.Get(1,0)!=2)||rep.m_z.Get(1,1)!=3;
berr=(berr||rep.m_z.Get(2,0)!=4)||rep.m_z.Get(2,1)!=5;
berr=(berr||rep.m_z.Get(3,0)!=6)||rep.m_z.Get(3,1)!=7;
berr=(berr||rep.m_z.Get(4,0)!=8)||rep.m_z.Get(4,1)!=9;
berr=(berr||rep.m_pz.Get(0,0)!=2)||rep.m_pz.Get(0,1)!=3;
berr=(berr||rep.m_pz.Get(1,0)!=4)||rep.m_pz.Get(1,1)!=5;
berr=(berr||rep.m_pz.Get(2,0)!=0)||rep.m_pz.Get(2,1)!=1;
berr=(berr||rep.m_pz.Get(3,0)!=6)||rep.m_pz.Get(3,1)!=7;
berr=(berr||rep.m_pz.Get(4,0)!=8)||rep.m_pz.Get(4,1)!=9;
if(berr)
return(result);
CClustering::ClusterizerSetAHCAlgo(s,1);
CClustering::ClusterizerRunAHC(s,rep);
if(rep.m_NPoints!=6 || CAp::Rows(rep.m_z)!=5 || CAp::Cols(rep.m_z)!=2)
return(result);
berr=false;
berr=(berr||rep.m_z.Get(0,0)!=0)||rep.m_z.Get(0,1)!=1;
berr=(berr||rep.m_z.Get(1,0)!=2)||rep.m_z.Get(1,1)!=6;
berr=(berr||rep.m_z.Get(2,0)!=3)||rep.m_z.Get(2,1)!=7;
berr=(berr||rep.m_z.Get(3,0)!=5)||rep.m_z.Get(3,1)!=8;
berr=(berr||rep.m_z.Get(4,0)!=4)||rep.m_z.Get(4,1)!=9;
if(berr)
return(result);
//--- Test which differentiates complete linkage and average linkage from
//--- single linkage:
//--- * we have cluster C0={(-0.5), (0)},
//--- cluster C1={(19.0), (20.0), (21.0), (22.0), (23.0)},
//--- and point P between them - (10.0)
//--- * we try three different strategies - single linkage, complete
//--- linkage, average linkage.
//--- * any strategy will merge C0 first, then merge points of C1,
//--- and then merge P with C0 or C1 (depending on linkage type)
//--- * we test that:
//--- a) C0 is merged first
//--- b) after 5 merges (including merge of C0), P is merged with C0 or C1
//--- c) P is merged with C1 when we have single linkage, with C0 otherwise
xy=matrix<double>::Zeros(8,1);
xy.Set(0,0,-0.5);
xy.Set(1,0,0.0);
xy.Set(2,0,10.0);
xy.Set(3,0,19.0);
xy.Set(4,0,20.0);
xy.Set(5,0,21.0);
xy.Set(6,0,22.0);
xy.Set(7,0,23.0);
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,8,1,0);
for(ahcalgo=0; ahcalgo<=2; ahcalgo++)
{
CClustering::ClusterizerSetAHCAlgo(s,ahcalgo);
CClustering::ClusterizerRunAHC(s,rep);
if(rep.m_NPoints!=8 || CAp::Rows(rep.m_z)!=7 || CAp::Cols(rep.m_z)!=2)
return(result);
if(rep.m_z.Get(0,0)!=0 || rep.m_z.Get(0,1)!=1)
return(result);
if(rep.m_z.Get(5,0)!=2 && rep.m_z.Get(5,1)!=2)
return(result);
if(rep.m_z.Get(5,0)!=2 && rep.m_z.Get(5,1)!=2)
return(result);
if((ahcalgo==0 || ahcalgo==2) && rep.m_z.Get(5,0)!=8 && rep.m_z.Get(5,1)!=8)
return(result);
if(ahcalgo==1 && (rep.m_z.Get(5,0)==8 || rep.m_z.Get(5,1)==8))
return(result);
}
//--- Test which differentiates single linkage and average linkage from
//--- complete linkage:
//--- * we have cluster C0={(-2.5), (-2.0)},
//--- cluster C1={(19.0), (20.0), (21.0), (22.0), (23.0)},
//--- and point P between them - (10.0)
//--- * we try three different strategies - single linkage, complete
//--- linkage, average linkage.
//--- * any strategy will merge C0 first, then merge points of C1,
//--- and then merge P with C0 or C1 (depending on linkage type)
//--- * we test that:
//--- a) C0 is merged first
//--- b) after 5 merges (including merge of C0), P is merged with C0 or C1
//--- c) P is merged with C0 when we have complete linkage, with C1 otherwise
xy=matrix<double>::Zeros(8,1);
xy.Set(0,0,-2.5);
xy.Set(1,0,-2.0);
xy.Set(2,0,10.0);
xy.Set(3,0,19.0);
xy.Set(4,0,20.0);
xy.Set(5,0,21.0);
xy.Set(6,0,22.0);
xy.Set(7,0,23.0);
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,8,1,0);
for(ahcalgo=0; ahcalgo<=2; ahcalgo++)
{
CClustering::ClusterizerSetAHCAlgo(s,ahcalgo);
CClustering::ClusterizerRunAHC(s,rep);
if(rep.m_NPoints!=8 || CAp::Rows(rep.m_z)!=7 || CAp::Cols(rep.m_z)!=2)
return(result);
if(rep.m_z.Get(0,0)!=0 || rep.m_z.Get(0,1)!=1)
return(result);
if(rep.m_z.Get(5,0)!=2 && rep.m_z.Get(5,1)!=2)
return(result);
if(rep.m_z.Get(5,0)!=2 && rep.m_z.Get(5,1)!=2)
return(result);
if(ahcalgo==0 && rep.m_z.Get(5,0)!=8 && rep.m_z.Get(5,1)!=8)
return(result);
if((ahcalgo==1 || ahcalgo==2) && (rep.m_z.Get(5,0)==8 || rep.m_z.Get(5,1)==8))
return(result);
}
//--- Test which differentiates weighred average linkage from unweighted average linkage:
//--- * we have cluster C0={(0.0), (1.5), (2.5)},
//--- cluster C1={(7.5), (7.99)},
//--- and point P between them - (4.5)
//--- * we try two different strategies - weighted average linkage and unweighted average linkage
//--- * any strategy will merge C1 first, then merge points of C0,
//--- and then merge P with C0 or C1 (depending on linkage type)
//--- * we test that:
//--- a) C1 is merged first, C0 is merged after that
//--- b) after first 3 merges P is merged with C0 or C1
//--- c) P is merged with C1 when we have weighted average linkage, with C0 otherwise
xy=matrix<double>::Zeros(6,1);
xy.Set(0,0,0.0);
xy.Set(1,0,1.5);
xy.Set(2,0,2.5);
xy.Set(3,0,4.5);
xy.Set(4,0,7.5);
xy.Set(5,0,7.99);
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,6,1,0);
for(ahcalgo=2; ahcalgo<=3; ahcalgo++)
{
CClustering::ClusterizerSetAHCAlgo(s,ahcalgo);
CClustering::ClusterizerRunAHC(s,rep);
if(rep.m_NPoints!=6 || CAp::Rows(rep.m_z)!=5 || CAp::Cols(rep.m_z)!=2)
return(result);
if(rep.m_z.Get(0,0)!=4 || rep.m_z.Get(0,1)!=5)
return(result);
if(rep.m_z.Get(1,0)!=1 || rep.m_z.Get(1,1)!=2)
return(result);
if(rep.m_z.Get(2,0)!=0 || rep.m_z.Get(2,1)!=7)
return(result);
if(rep.m_z.Get(3,0)!=3)
return(result);
if(ahcalgo==2 && rep.m_z.Get(3,1)!=8)
return(result);
if(ahcalgo==3 && rep.m_z.Get(3,1)!=6)
return(result);
}
//--- Test which checks correctness of Ward's method on very basic problem
xy=matrix<double>::Zeros(4,1);
xy.Set(0,0,0.0);
xy.Set(1,0,1.0);
xy.Set(2,0,3.1);
xy.Set(3,0,4.0);
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,CAp::Rows(xy),CAp::Cols(xy),2);
CClustering::ClusterizerGetDistances(xy,CAp::Rows(xy),CAp::Cols(xy),2,d);
CClustering::ClusterizerSetAHCAlgo(s,4);
CClustering::ClusterizerRunAHC(s,rep);
if(ErrorsInMerges(d,xy,CAp::Rows(xy),CAp::Cols(xy),rep,4))
return(result);
//--- One more Ward's test
xy=matrix<double>::Zeros(8,2);
xy.Set(0,0,0.4700566262);
xy.Set(0,1,0.4565938448);
xy.Set(1,0,0.2394499506);
xy.Set(1,1,0.1750209592);
xy.Set(2,0,0.6518417019);
xy.Set(2,1,0.6151370746);
xy.Set(3,0,0.9863942841);
xy.Set(3,1,0.7855012189);
xy.Set(4,0,0.1517812919);
xy.Set(4,1,0.2600174758);
xy.Set(5,0,0.7840203638);
xy.Set(5,1,0.9023597604);
xy.Set(6,0,0.2604194835);
xy.Set(6,1,0.9792704661);
xy.Set(7,0,0.6353096042);
xy.Set(7,1,0.8252606906);
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,CAp::Rows(xy),CAp::Cols(xy),2);
CClustering::ClusterizerGetDistances(xy,CAp::Rows(xy),CAp::Cols(xy),2,d);
CClustering::ClusterizerSetAHCAlgo(s,4);
CClustering::ClusterizerRunAHC(s,rep);
if(rep.m_z.Get(0,0)!=1 || rep.m_z.Get(0,1)!=4)
return(result);
if(rep.m_z.Get(1,0)!=5 || rep.m_z.Get(1,1)!=7)
return(result);
if(rep.m_z.Get(2,0)!=0 || rep.m_z.Get(2,1)!=2)
return(result);
if(rep.m_z.Get(3,0)!=3 || rep.m_z.Get(3,1)!=9)
return(result);
if(rep.m_z.Get(4,0)!=10 || rep.m_z.Get(4,1)!=11)
return(result);
if(rep.m_z.Get(5,0)!=6 || rep.m_z.Get(5,1)!=12)
return(result);
if(rep.m_z.Get(6,0)!=8 || rep.m_z.Get(6,1)!=13)
return(result);
if(ErrorsInMerges(d,xy,CAp::Rows(xy),CAp::Cols(xy),rep,4))
return(result);
//--- Ability to solve problems with zero distance matrix
npoints=20;
d=matrix<double>::Zeros(npoints,npoints);
for(ahcalgo=0; ahcalgo<=4; ahcalgo++)
{
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetDistances(s,d,npoints,true);
CClustering::ClusterizerSetAHCAlgo(s,ahcalgo);
CClustering::ClusterizerRunAHC(s,rep);
if(rep.m_NPoints!=npoints || CAp::Rows(rep.m_z)!=(npoints-1) || CAp::Cols(rep.m_z)!=2)
return(result);
}
//--- Test GetKClusters()
xy=matrix<double>::Zeros(8,1);
xy.Set(0,0,-2.5);
xy.Set(1,0,-2.0);
xy.Set(2,0,10.0);
xy.Set(3,0,19.0);
xy.Set(4,0,20.0);
xy.Set(5,0,21.0);
xy.Set(6,0,22.0);
xy.Set(7,0,23.0);
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,8,1,0);
CClustering::ClusterizerSetAHCAlgo(s,0);
CClustering::ClusterizerRunAHC(s,rep);
CClustering::ClusterizerGetKClusters(rep,3,cidx,cz);
if(cidx[0]!=1 || cidx[1]!=1 || cidx[2]!=0 || cidx[3]!=2 || cidx[4]!=2 || cidx[5]!=2 || cidx[6]!=2 || cidx[7]!=2)
return(result);
if(cz[0]!=2 || cz[1]!=8 || cz[2]!=12)
return(result);
//--- Test is done
return(false);
}
//+------------------------------------------------------------------+
//| Advanced agglomerative hierarchical clustering tests: returns |
//| True on failure, False on success. |
//| Advanced testing subroutine perform several automatically |
//| generated tests. |
//+------------------------------------------------------------------+
bool CTestClusteringUnit::AdvancedAHCTests(void)
{
//--- create variables
bool result=false;
int euclidean=2;
CClusterizerState s;
CAHCReport rep;
CMatrixDouble xy;
CMatrixDouble dm;
CMatrixDouble dm2;
CRowInt idx;
CRowInt disttypes;
CRowDouble x0;
CRowDouble x1;
int d=0;
int n=0;
int npoints=0;
int ahcalgo=0;
int disttype=0;
int i=0;
int j=0;
int k=0;
double v=0;
int t=0;
int i_=0;
//--- Test on D-dimensional problem:
//--- * D = 2...5
//--- * D clusters, each has N points;
//--- centers are located at x=(0 ... 1 ... 0);
//--- cluster radii are approximately 0.1
//--- * single/complete/unweighted_average/weighted_average linkage/Ward's method are tested
//--- * Euclidean distance is used, either:
//--- a) one given by distance matrix (ClusterizerSetDistances)
//--- b) one calculated from dataset (ClusterizerSetPoints)
//--- * we have N*D points, and N*D-1 merges in total
//--- * points are randomly rearranged after generation
//--- For all kinds of linkage we perform following test:
//--- * for each point we remember index of its cluster
//--- (one which is determined during dataset generation)
//--- * we clusterize points with ALGLIB capabilities
//--- * we scan Rep.Z and perform first D*(N-1) merges
//--- * for each merge we check that it merges points
//--- from same cluster;
//--- Additonally, we call ErrorsInMerges(). See function comments
//--- for more information about specific tests performed. This function
//--- allows us to check that clusters are built exactly as specified by
//--- definition of the clustering algorithm.
for(d=2; d<=5; d++)
{
for(ahcalgo=0; ahcalgo<=4; ahcalgo++)
{
n=(int)MathRound(MathPow(3,CMath::RandomInteger(3)));
npoints=d*n;
//--- 1. generate dataset.
//--- 2. fill Idx (array of cluster indexes):
//--- * first N*D elements store cluster indexes
//--- * next D*(N-1) elements are filled during merges
//--- 3. build distance matrix DM
xy=matrix<double>::Zeros(n*d,d);
idx.Resize(n*d+d*(n-1));
for(i=0; i<n*d; i++)
{
for(j=0; j<d; j++)
xy.Set(i,j,0.2*CMath::RandomReal()-0.1);
xy.Add(i,i%d,1.0);
idx.Set(i,i%d);
}
for(i=0; i<n*d; i++)
{
k=CMath::RandomInteger(n*d);
if(k!=i)
{
xy.SwapRows(i,k);
idx.Swap(k,i);
}
}
dm=matrix<double>::Zeros(npoints,npoints);
x0=vector<double>::Zeros(d);
x1=vector<double>::Zeros(d);
for(i=0; i<npoints; i++)
for(j=0; j<npoints; j++)
{
x0=xy[i]+0;
x1=xy[j]+0;
dm.Set(i,j,DistFunc(x0,x1,d,euclidean));
}
//--- Clusterize with SetPoints()
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,n*d,d,euclidean);
CClustering::ClusterizerSetAHCAlgo(s,ahcalgo);
CClustering::ClusterizerRunAHC(s,rep);
//--- Tests:
//--- * replay first D*(N-1) merges; these merges should take place
//--- within clusters, intercluster merges will be performed at the
//--- last stages of the processing.
//--- * test with ErrorsInMerges()
if(rep.m_NPoints!=npoints)
{
result=true;
return(result);
}
for(i=0; i<d*(n-1); i++)
{
//--- Check correctness of I-th row of Z
if(rep.m_z.Get(i,0)<0 || rep.m_z.Get(i,0)>=rep.m_z.Get(i,1) || rep.m_z.Get(i,1)>=(d*n+i))
{
result=true;
return(result);
}
//--- Check that merge is performed within cluster
if(idx[rep.m_z.Get(i,0)]!=idx[rep.m_z.Get(i,1)])
{
result=true;
return(result);
}
//--- Write new entry of Idx.
//--- Both points from the same cluster, so result of the merge
//--- belongs to the same cluster
idx.Set(n*d+i,idx[rep.m_z.Get(i,1)]);
}
if(((ahcalgo==0 || ahcalgo==1) || ahcalgo==2) || ahcalgo==4)
{
if(ErrorsInMerges(dm,xy,d*n,d,rep,ahcalgo))
{
result=true;
return(result);
}
}
//--- Clusterize one more time, now with distance matrix
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetDistances(s,dm,n*d,CMath::RandomReal()>0.5);
CClustering::ClusterizerSetAHCAlgo(s,ahcalgo);
CClustering::ClusterizerRunAHC(s,rep);
//--- Tests:
//--- * replay first D*(N-1) merges; these merges should take place
//--- within clusters, intercluster merges will be performed at the
//--- last stages of the processing.
//--- * test with ErrorsInMerges()
if(rep.m_NPoints!=npoints)
{
result=true;
return(result);
}
for(i=0; i<(d*(n-1)); i++)
{
//--- Check correctness of I-th row of Z
if(rep.m_z.Get(i,0)<0 || rep.m_z.Get(i,0)>=rep.m_z.Get(i,1) || rep.m_z.Get(i,1)>=(d*n+i))
{
result=true;
return(result);
}
//--- Check that merge is performed within cluster
if(idx[rep.m_z.Get(i,0)]!=idx[rep.m_z.Get(i,1)])
{
result=true;
return(result);
}
//--- Write new entry of Idx.
//--- Both points from the same cluster, so result of the merge
//--- belongs to the same cluster
idx.Set(n*d+i,idx[rep.m_z.Get(i,1)]);
}
if(ahcalgo==0 || ahcalgo==1 || ahcalgo==2 || ahcalgo==4)
if(ErrorsInMerges(dm,xy,d*n,d,rep,ahcalgo))
{
result=true;
return(result);
}
}
}
//--- Test on random D-dimensional problem:
//--- * D = 2...5
//--- * N=1..16 random points from unit hypercube
//--- * single/complete/unweighted_average linkage/Ward's method are tested
//--- * different distance functions are tested
//--- * we call ErrorsInMerges() and we check distance matrix
//--- calculated by unit test against one returned by GetDistances()
disttypes.Resize(9);
disttypes.Set(0,0);
disttypes.Set(1,1);
disttypes.Set(2,2);
disttypes.Set(3,10);
disttypes.Set(4,11);
disttypes.Set(5,12);
disttypes.Set(6,13);
disttypes.Set(7,20);
disttypes.Set(8,21);
for(disttype=0; disttype<CAp::Len(disttypes); disttype++)
{
for(ahcalgo=0; ahcalgo<=4; ahcalgo++)
{
if(ahcalgo==3)
continue;
if(ahcalgo==4 && disttype!=2)
continue;
npoints=(int)MathRound(MathPow(2,CMath::RandomInteger(5)));
d=2+CMath::RandomInteger(4);
//--- Generate dataset and distance matrix
xy=matrix<double>::Zeros(npoints,d);
for(i=0; i<npoints; i++)
for(j=0; j<d; j++)
xy.Set(i,j,CMath::RandomReal());
dm=matrix<double>::Zeros(npoints,npoints);
x0=vector<double>::Zeros(d);
x1=vector<double>::Zeros(d);
for(i=0; i<npoints; i++)
for(j=0; j<npoints; j++)
{
x0=xy[i]+0;
x1=xy[j]+0;
dm.Set(i,j,DistFunc(x0,x1,d,disttypes[disttype]));
}
//--- Clusterize
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,npoints,d,disttypes[disttype]);
CClustering::ClusterizerSetAHCAlgo(s,ahcalgo);
CClustering::ClusterizerRunAHC(s,rep);
//--- Test with ErrorsInMerges()
if(ErrorsInMerges(dm,xy,npoints,d,rep,ahcalgo))
{
result=true;
return(result);
}
//--- Test distance matrix
CClustering::ClusterizerGetDistances(xy,npoints,d,disttypes[disttype],dm2);
for(i=0; i<npoints; i++)
for(j=0; j<npoints; j++)
if(!MathIsValidNumber(dm2.Get(i,j)) || MathAbs(dm.Get(i,j)-dm2.Get(i,j))>(1.0E5*CMath::m_machineepsilon))
{
result=true;
return(result);
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| Simple test 1: ellipsoid in NF-dimensional space. |
//| compare k-means centers with random centers |
//+------------------------------------------------------------------+
void CTestClusteringUnit::KMeansSimpleTest1(int nvars,int nc,
int passcount,
bool &converrors,
bool &othererrors,
bool &simpleerrors)
{
//--- create variables
int npoints=nc*100;
int restarts=5;
int majoraxis=0;
CMatrixDouble xy;
CRowDouble tmp;
double v=0;
int i=0;
int j=0;
int pass=0;
double ekmeans=0;
double erandom=0;
double dclosest=0;
int cclosest=0;
CClusterizerState s;
CKmeansReport rep;
int i_=0;
passcount=10;
tmp=vector<double>::Zeros(nvars);
for(pass=1; pass<=passcount; pass++)
{
//--- Fill
xy=matrix<double>::Zeros(npoints,nvars);
majoraxis=CMath::RandomInteger(nvars);
for(i=0; i<npoints; i++)
{
RSphere(xy,nvars,i);
xy.Mul(i,majoraxis,nc);
}
//--- Test
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,npoints,nvars,2);
CClustering::ClusterizerSetKMeansLimits(s,restarts,0);
CClustering::ClusterizerRunKMeans(s,nc,rep);
if(rep.m_terminationtype<=0)
{
converrors=true;
return;
}
//--- Test that XYC is correct mapping to cluster centers
for(i=0; i<npoints; i++)
{
cclosest=-1;
dclosest=CMath::m_maxrealnumber;
for(j=0; j<nc; j++)
{
for(i_=0; i_<nvars; i_++)
tmp.Set(i_,xy.Get(i,i_)-rep.m_c.Get(j,i_));
v=CAblasF::RDotV2(nvars,tmp);
if(v<dclosest)
{
cclosest=j;
dclosest=v;
}
}
if(cclosest!=rep.m_cidx[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; i++)
{
for(i_=0; i_<nvars; i_++)
tmp.Set(i_,xy.Get(i,i_)-rep.m_c.Get(rep.m_cidx[i],i_));
v=CAblasF::RDotV2(nvars,tmp);
ekmeans+=v;
}
erandom=0;
for(i=0; i<npoints; i++)
{
dclosest=CMath::m_maxrealnumber;
v=0;
for(j=0; j<nc; j++)
{
for(i_=0; i_<nvars; i_++)
tmp.Set(i_,xy.Get(i,i_)-xy.Get(j,i_));
v=CAblasF::RDotV2(nvars,tmp);
if(v<dclosest)
dclosest=v;
}
erandom+=v;
}
if(erandom<ekmeans)
{
simpleerrors=true;
return;
}
}
}
//+------------------------------------------------------------------+
//| This test perform several checks for special properties |
//| On failure sets error flag, on success leaves it unchanged. |
//+------------------------------------------------------------------+
void CTestClusteringUnit::KMeansSpecialTests(bool &othererrors)
{
//--- create variables
int npoints=0;
int nfeatures=0;
int nclusters=0;
int initalgo=0;
CMatrixDouble xy;
CMatrixDouble c;
int idx0=0;
int idx1=0;
int idx2=0;
int i=0;
int j=0;
int pass=0;
int passcount=0;
int separation=0;
CRowInt xyc;
CRowInt xycref;
CRowDouble xydist2;
CRowDouble xydist2ref;
CRowDouble energies;
CHighQualityRandState rs;
CClusterizerState s;
CKmeansReport rep;
CRowInt pointslist;
CRowInt featureslist;
CRowInt clusterslist;
bool allsame;
CHighQualityRand::HQRndRandomize(rs);
//--- Compare different initialization algorithms:
//--- * dataset is K balls, chosen at random gaussian points, with
//--- radius equal to 2^(-Separation).
//--- * we generate random sample, run k-means initialization algorithm
//--- and calculate mean energy for each initialization algorithm.
//--- In order to suppress Lloyd's iteration we use KmeansDbgNoIts
//--- debug flag.
//--- * then, we compare mean energies; kmeans++ must be best one,
//--- random initialization must be worst one.
energies=vector<double>::Zeros(4);
passcount=1000;
npoints=100;
nfeatures=3;
nclusters=6;
xy=matrix<double>::Zeros(npoints,nfeatures);
c=matrix<double>::Zeros(nclusters,nfeatures);
CClustering::ClusterizerCreate(s);
s.m_kmeansdbgnoits=true;
for(separation=2; separation<=5; separation++)
{
//--- Try different init algorithms
for(initalgo=1; initalgo<=3; initalgo++)
{
energies.Set(initalgo,0.0);
CClustering::ClusterizerSetKMeansInit(s,initalgo);
for(pass=1; pass<=passcount; pass++)
{
//--- Generate centers of balls
for(i=0; i<nclusters; i++)
{
for(j=0; j<nfeatures; j++)
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
}
//--- Generate points
for(i=0; i<npoints; i++)
{
for(j=0; j<nfeatures; j++)
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs)*MathPow(2,-separation)+c.Get(i%nclusters,j));
}
//--- Run clusterization
CClustering::ClusterizerSetPoints(s,xy,npoints,nfeatures,2);
CClustering::ClusterizerRunKMeans(s,nclusters,rep);
CAp::SetErrorFlag(othererrors,rep.m_terminationtype<=0,"testclusteringunit.ap:1069");
energies.Add(initalgo,rep.m_energy/passcount);
}
}
//--- Compare
CAp::SetErrorFlag(othererrors,!(energies[2]<energies[1]),"testclusteringunit.ap:1077");
CAp::SetErrorFlag(othererrors,!(energies[3]<energies[1]),"testclusteringunit.ap:1078");
}
//--- Test distance calculation algorithm
pointslist.Resize(6);
pointslist.Set(0,1);
pointslist.Set(1,10);
pointslist.Set(2,32);
pointslist.Set(3,100);
pointslist.Set(4,512);
pointslist.Set(5,8000);
featureslist.Resize(5);
featureslist.Set(0,1);
featureslist.Set(1,5);
featureslist.Set(2,32);
featureslist.Set(3,50);
featureslist.Set(4,96);
clusterslist.Resize(5);
clusterslist.Set(0,1);
clusterslist.Set(1,5);
clusterslist.Set(2,32);
clusterslist.Set(3,50);
clusterslist.Set(4,96);
for(idx0=0; idx0<CAp::Len(pointslist); idx0++)
{
for(idx1=0; idx1<CAp::Len(featureslist); idx1++)
{
for(idx2=0; idx2<CAp::Len(clusterslist); idx2++)
{
npoints=pointslist[idx0];
nfeatures=featureslist[idx1];
nclusters=clusterslist[idx2];
xy=matrix<double>::Zeros(npoints,nfeatures);
for(i=0; i<npoints; i++)
{
for(j=0; j<=nfeatures-1; j++)
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs));
}
c=matrix<double>::Zeros(nclusters,nfeatures);
for(i=0; i<nclusters; i++)
{
for(j=0; j<nfeatures; j++)
c.Set(i,j,CHighQualityRand::HQRndNormal(rs));
}
xyc.Resize(npoints);
xycref.Resize(npoints);
xydist2=vector<double>::Zeros(npoints);
xydist2ref=vector<double>::Zeros(npoints);
//--- Test
CClustering::KMeansUpdateDistances(xy,0,npoints,nfeatures,c,0,nclusters,xyc,xydist2);
KMeansReferenceUpdateDistances(xy,npoints,nfeatures,c,nclusters,xycref,xydist2ref);
for(i=0; i<npoints; i++)
{
CAp::SetErrorFlag(othererrors,xyc[i]!=xycref[i],"testclusteringunit.ap:1137");
CAp::SetErrorFlag(othererrors,MathAbs(xydist2[i]-xydist2ref[i])>(1.0E-6),"testclusteringunit.ap:1138");
}
}
}
}
//--- Test degenerate dataset (less than NClusters distinct points)
for(nclusters=2; nclusters<=10; nclusters++)
{
for(initalgo=0; initalgo<=3; initalgo++)
{
for(pass=1; pass<=10; pass++)
{
//--- Initialize points. Two algorithms are used:
//--- * initialization by small integers (no rounding problems)
//---*initialization by "long" fraction
npoints=100;
nfeatures=10;
xy=matrix<double>::Zeros(npoints,nfeatures);
if(CHighQualityRand::HQRndNormal(rs)>0.0)
{
for(i=0; i<=nclusters-2; i++)
for(j=0; j<=nfeatures-1; j++)
xy.Set(i,j,MathSin(CHighQualityRand::HQRndNormal(rs)));
}
else
{
for(i=0; i<nclusters-1; i++)
for(j=0; j<nfeatures; j++)
xy.Set(i,j,CHighQualityRand::HQRndUniformI(rs,50));
}
for(i=nclusters-1; i<npoints; i++)
{
idx0=CHighQualityRand::HQRndUniformI(rs,nclusters-1);
for(j=0; j<nfeatures; j++)
xy.Set(i,j,xy.Get(idx0,j));
}
//--- Clusterize with unlimited number of iterations.
//--- Correct error code must be returned.
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,npoints,nfeatures,2);
CClustering::ClusterizerSetKMeansLimits(s,1,0);
CClustering::ClusterizerSetKMeansInit(s,initalgo);
CClustering::ClusterizerRunKMeans(s,nclusters,rep);
CAp::SetErrorFlag(othererrors,rep.m_terminationtype!=-3,"testclusteringunit.ap:1185");
}
}
}
//--- Test deterministic seed:
//--- * specyfying zero seed, or not specyfind seed at all = nondeterministic algo
//--- * nonzero zeed = deterministic algo
for(initalgo=0; initalgo<=3; initalgo++)
{
//--- Initialize points.
npoints=100;
nfeatures=3;
xy=matrix<double>::Zeros(npoints,nfeatures);
for(i=0; i<npoints; i++)
{
for(j=0; j<nfeatures; j++)
xy.Set(i,j,CHighQualityRand::HQRndNormal(rs));
}
//--- Clusterize with negative seed.
//--- Perform multiple runs, compare results with
//--- first one returned - at least one result must
//--- be different.
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,npoints,nfeatures,2);
CClustering::ClusterizerSetKMeansLimits(s,1,1);
CClustering::ClusterizerSetKMeansInit(s,initalgo);
CClustering::ClusterizerSetSeed(s,-CMath::RandomInteger(3));
allsame=true;
for(pass=0; pass<=10; pass++)
{
CClustering::ClusterizerRunKMeans(s,nclusters,rep);
CAp::SetErrorFlag(othererrors,rep.m_terminationtype<=0,"testclusteringunit.ap:1221");
if(othererrors)
return;
if(pass==0)
{
//--- Save clusters
c=rep.m_c;
}
else
{
//--- Compare clusters with ones returned from first run.
for(i=0; i<CAp::Rows(rep.m_c); i++)
for(j=0; j<CAp::Cols(rep.m_c); j++)
allsame=allsame && (c.Get(i,j)==rep.m_c.Get(i,j));
}
}
CAp::SetErrorFlag(othererrors,allsame,"testclusteringunit.ap:1244");
//--- Clusterize with positive seed.
//--- Perform multiple runs, compare results with
//--- first one returned - all results must be same.
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,npoints,nfeatures,2);
CClustering::ClusterizerSetKMeansLimits(s,1,1);
CClustering::ClusterizerSetKMeansInit(s,initalgo);
CClustering::ClusterizerSetSeed(s,1+CMath::RandomInteger(3));
allsame=true;
for(pass=0; pass<=10; pass++)
{
CClustering::ClusterizerRunKMeans(s,nclusters,rep);
CAp::SetErrorFlag(othererrors,rep.m_terminationtype<=0,"testclusteringunit.ap:1261");
if(othererrors)
return;
if(pass==0)
{
//--- Save clusters
c=rep.m_c;
}
else
{
//--- Compare clusters with ones returned from first run.
for(i=0; i<CAp::Rows(rep.m_c); i++)
for(j=0; j<CAp::Cols(rep.m_c); j++)
allsame=allsame && (c.Get(i,j)==rep.m_c.Get(i,j));
}
}
CAp::SetErrorFlag(othererrors,!allsame,"testclusteringunit.ap:1284");
//--- Clusterize with default seed.
//--- Perform multiple runs, compare results with
//--- first one returned - all results must be same.
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,npoints,nfeatures,2);
CClustering::ClusterizerSetKMeansLimits(s,1,1);
CClustering::ClusterizerSetKMeansInit(s,initalgo);
allsame=true;
for(pass=0; pass<=10; pass++)
{
CClustering::ClusterizerRunKMeans(s,nclusters,rep);
CAp::SetErrorFlag(othererrors,rep.m_terminationtype<=0,"testclusteringunit.ap:1301");
if(othererrors)
return;
if(pass==0)
{
//--- Save clusters
c=rep.m_c;
}
else
{
//--- Compare clusters with ones returned from first run.
for(i=0; i<CAp::Rows(rep.m_c); i++)
for(j=0; j<CAp::Cols(rep.m_c); j++)
allsame=allsame && (c.Get(i,j)==rep.m_c.Get(i,j));
}
}
CAp::SetErrorFlag(othererrors,!allsame,"testclusteringunit.ap:1324");
}
}
//+------------------------------------------------------------------+
//| This test checks algorithm ability to handle degenerate problems |
//| without causing infinite loop. |
//+------------------------------------------------------------------+
void CTestClusteringUnit::KMeansInfiniteLoopTest(bool &othererrors)
{
//--- create variables
int npoints=0;
int nfeatures=0;
int nclusters=0;
int restarts=0;
CMatrixDouble xy;
int i=0;
int j=0;
CClusterizerState s;
CKmeansReport rep;
//--- Problem 1: all points are same.
//--- For NClusters=1 we must get correct solution, for NClusters>1 we must get failure.
npoints=100;
nfeatures=1;
restarts=5;
xy=matrix<double>::Zeros(npoints,nfeatures);
for(j=0; j<nfeatures; j++)
xy.Set(0,j,CMath::RandomReal());
for(i=1; i<npoints; i++)
{
for(j=0; j<nfeatures; j++)
xy.Set(i,j,xy.Get(0,j));
}
nclusters=1;
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,npoints,nfeatures,2);
CClustering::ClusterizerSetKMeansLimits(s,restarts,0);
CClustering::ClusterizerRunKMeans(s,nclusters,rep);
othererrors=othererrors||rep.m_terminationtype<=0;
for(i=0; i<nfeatures; i++)
othererrors=othererrors||(MathAbs(rep.m_c.Get(0,i)-xy.Get(0,i))>(1000*CMath::m_machineepsilon));
for(i=0; i<npoints; i++)
othererrors=othererrors||rep.m_cidx[i]!=0;
nclusters=5;
CClustering::ClusterizerRunKMeans(s,nclusters,rep);
othererrors=othererrors||rep.m_terminationtype>0;
//--- Problem 2: degenerate dataset (report by Andreas).
npoints=57;
nfeatures=1;
restarts=1;
nclusters=4;
xy=matrix<double>::Zeros(npoints,nfeatures);
xy.Set(0,0,12.244689632138986);
xy.Set(1,0,12.244689632138982);
xy.Set(2,0,12.244689632138986);
xy.Set(3,0,12.244689632138982);
xy.Set(4,0,12.244689632138986);
xy.Set(5,0,12.244689632138986);
xy.Set(6,0,12.244689632138986);
xy.Set(7,0,12.244689632138986);
xy.Set(8,0,12.244689632138986);
xy.Set(9,0,12.244689632138986);
xy.Set(10,0,12.244689632138989);
xy.Set(11,0,12.244689632138984);
xy.Set(12,0,12.244689632138986);
xy.Set(13,0,12.244689632138986);
xy.Set(14,0,12.244689632138989);
xy.Set(15,0,12.244689632138986);
xy.Set(16,0,12.244689632138986);
xy.Set(17,0,12.244689632138986);
xy.Set(18,0,12.244689632138986);
xy.Set(19,0,12.244689632138989);
xy.Set(20,0,12.244689632138972);
xy.Set(21,0,12.244689632138986);
xy.Set(22,0,12.244689632138986);
xy.Set(23,0,12.244689632138986);
xy.Set(24,0,12.244689632138984);
xy.Set(25,0,12.244689632138982);
xy.Set(26,0,12.244689632138986);
xy.Set(27,0,12.244689632138986);
xy.Set(28,0,12.244689632138986);
xy.Set(29,0,12.244689632138986);
xy.Set(30,0,12.244689632138986);
xy.Set(31,0,12.244689632138986);
xy.Set(32,0,12.244689632138986);
xy.Set(33,0,12.244689632138986);
xy.Set(34,0,12.244689632138986);
xy.Set(35,0,12.244689632138982);
xy.Set(36,0,12.244689632138989);
xy.Set(37,0,12.244689632138986);
xy.Set(38,0,12.244689632138986);
xy.Set(39,0,12.244689632138986);
xy.Set(40,0,12.244689632138986);
xy.Set(41,0,12.244689632138986);
xy.Set(42,0,12.244689632138986);
xy.Set(43,0,12.244689632138986);
xy.Set(44,0,12.244689632138986);
xy.Set(45,0,12.244689632138986);
xy.Set(46,0,12.244689632138986);
xy.Set(47,0,12.244689632138986);
xy.Set(48,0,12.244689632138986);
xy.Set(49,0,12.244689632138986);
xy.Set(50,0,12.244689632138984);
xy.Set(51,0,12.244689632138986);
xy.Set(52,0,12.244689632138986);
xy.Set(53,0,12.244689632138986);
xy.Set(54,0,12.244689632138986);
xy.Set(55,0,12.244689632138986);
xy.Set(56,0,12.244689632138986);
CClustering::ClusterizerCreate(s);
CClustering::ClusterizerSetPoints(s,xy,npoints,nfeatures,2);
CClustering::ClusterizerSetKMeansLimits(s,restarts,0);
CClustering::ClusterizerRunKMeans(s,nclusters,rep);
othererrors=othererrors||rep.m_terminationtype<=0;
}
//+------------------------------------------------------------------+
//| 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 solved. 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. |
//+------------------------------------------------------------------+
void CTestClusteringUnit::KMeansRestartsTest(bool &converrors,
bool &restartserrors)
{
//--- create variables
int restarts=5;
int passcount=1000;
int clustersize=20;
int nclusters=3;
int nvars=2;
int npoints=nclusters*clustersize;
double sigmathreshold=5;
double p=0;
double s=0;
CMatrixDouble xy;
CRowDouble tmp;
int i=0;
int j=0;
int pass=0;
double ea=0;
double eb=0;
double v=0;
CClusterizerState state;
CKmeansReport rep1;
CKmeansReport rep2;
int i_=0;
xy=matrix<double>::Zeros(npoints,nvars);
tmp=vector<double>::Zeros(nvars);
for(pass=1; pass<=passcount; pass++)
{
//--- Fill
for(i=0; i<npoints; i++)
{
RSphere(xy,nvars,i);
for(j=0; j<nvars; j++)
xy.Add(i,j,(double)i/(double)clustersize*5.0);
}
CClustering::ClusterizerCreate(state);
CClustering::ClusterizerSetPoints(state,xy,npoints,nvars,2);
//--- Test: Restarts=1
CClustering::ClusterizerSetKMeansLimits(state,1,0);
CClustering::ClusterizerRunKMeans(state,nclusters,rep1);
if(rep1.m_terminationtype<=0)
{
converrors=true;
return;
}
ea=0;
for(i=0; i<npoints; i++)
{
for(i_=0; i_<nvars; i_++)
tmp.Set(i_,xy.Get(i,i_)-rep1.m_c.Get(rep1.m_cidx[i],i_));
v=CAblasF::RDotV2(nvars,tmp);
ea+=v;
}
//--- Test: Restarts>1
CClustering::ClusterizerSetKMeansLimits(state,restarts,0);
CClustering::ClusterizerRunKMeans(state,nclusters,rep2);
if(rep2.m_terminationtype<=0)
{
converrors=true;
return;
}
eb=0;
for(i=0; i<npoints; i++)
{
for(i_=0; i_<nvars; i_++)
tmp.Set(i_,xy.Get(i,i_)-rep2.m_c.Get(rep2.m_cidx[i],i_));
v=CAblasF::RDotV2(nvars,tmp);
eb+=v;
}
//--- Calculate statistic.
if(ea<eb)
p++;
if(ea==eb)
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>(-sigmathreshold));
}
//+------------------------------------------------------------------+
//| Random normal number |
//+------------------------------------------------------------------+
double CTestClusteringUnit::RNormal(void)
{
//--- create variables
double result=0;
double u=0;
double v=0;
double s=0;
double x1=0;
while(true)
{
u=2*CMath::RandomReal()-1;
v=2*CMath::RandomReal()-1;
s=CMath::Sqr(u)+CMath::Sqr(v);
if(s>0.0 && s<1.0)
{
s=MathSqrt(-(2*MathLog(s)/s));
x1=u*s;
break;
}
}
//--- return result
result=x1;
return(result);
}
//+------------------------------------------------------------------+
//| Random point from sphere |
//+------------------------------------------------------------------+
void CTestClusteringUnit::RSphere(CMatrixDouble &xy,int n,int i)
{
double v=0;
for(int j=0; j<n; j++)
xy.Set(i,j,RNormal());
v=CAblasF::RDotRR(n,xy,i,xy,i);
v=CMath::RandomReal()/MathSqrt(v);
for(int i_=0; i_<n; i_++)
xy.Mul(i,i_,v);
}
//+------------------------------------------------------------------+
//| Distance function: distance between X0 and X1 |
//| X0, X1 - array[D], points |
//| DistType - distance type |
//+------------------------------------------------------------------+
double CTestClusteringUnit::DistFunc(CRowDouble &x0,CRowDouble &x1,
int d,int disttype)
{
//--- create variables
double result=0;
int i=0;
double s0=0;
double s1=0;
//--- check
if(!CAp::Assert(disttype==0 || disttype==1 || disttype==2 || disttype==10 ||
disttype==11 || disttype==12 || disttype==13 || disttype==20 ||
disttype==21))
return(result);
switch(disttype)
{
case 0:
for(i=0; i<d; i++)
result=MathMax(result,MathAbs(x0[i]-x1[i]));
break;
case 1:
for(i=0; i<d; i++)
result+=MathAbs(x0[i]-x1[i]);
break;
case 2:
for(i=0; i<d; i++)
result+=CMath::Sqr(x0[i]-x1[i]);
result=MathSqrt(result);
break;
case 10:
result=MathMax(1-CBaseStat::PearsonCorr2(x0,x1,d),0.0);
break;
case 11:
result=MathMax(1-MathAbs(CBaseStat::PearsonCorr2(x0,x1,d)),0.0);
break;;
case 12:
case 13:
s0=0.0;
s1=0.0;
for(i=0; i<d; i++)
{
s0+=CMath::Sqr(x0[i])/d;
s1+=CMath::Sqr(x1[i])/d;
}
s0=MathSqrt(s0);
s1=MathSqrt(s1);
result=0;
for(i=0; i<d; i++)
result+=x0[i]/s0*(x1[i]/s1)/d;
if(disttype==12)
result=MathMax(1-result,0.0);
else
result=MathMax(1-MathAbs(result),0.0);
break;
case 20:
result=MathMax(1-CBaseStat::SpearmanCorr2(x0,x1,d),0.0);
break;
case 21:
result=MathMax(1-MathAbs(CBaseStat::SpearmanCorr2(x0,x1,d)),0.0);
break;
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| This function replays merges and checks that: |
//| * Rep.NPoints, Rep.Z, Rep.PZ and Rep.PM are consistent and |
//| correct |
//| * Rep.MergeDist is consistent with distance between clusters |
//| being merged |
//| * clusters with minimal distance are merged at each step |
//| * GetKClusters() correctly unpacks clusters for each K |
//| NOTE: this algorithm correctly handle ties, i.e. situations where|
//| several pairs of clusters have same intercluster distance, |
//| and we can't unambiguously choose clusters to merge. |
//| INPUT PARAMETERS |
//| D - distance matrix, array[NPoints,NPoints], full |
//| matrix is given (including both triangles and |
//| zeros on the main diagonal) |
//| XY - dataset matrix, array[NPoints,NF] |
//| NPoints - dataset size |
//| NF - number of features |
//| Rep - clusterizer report |
//| AHCAlgo - AHC algorithm: |
//| * 0 - complete linkage |
//| * 1 - single linkage |
//| * 2 - unweighted average linkage |
//| This function returns True on failure, False on success. |
//+------------------------------------------------------------------+
bool CTestClusteringUnit::ErrorsInMerges(CMatrixDouble &d,
CMatrixDouble &xy,
int npoints,int nf,
CAHCReport &rep,
int ahcalgo)
{
//--- create variables
bool result;
CMatrixDouble dm;
CMatrixInt cm;
CRowInt clustersizes;
CRowInt clusterheights;
bool b[];
CRowDouble x0;
CRowDouble x1;
bool bflag;
int i=0;
int j=0;
int k=0;
int i0=0;
int i1=0;
int c0=0;
int c1=0;
int s0=0;
int s1=0;
double v=0;
int t=0;
int mergeidx=0;
CRowInt kidx;
CRowInt kidxz;
int currentelement=0;
//--- check
if(!CAp::Assert(ahcalgo!=3,"integrity error"))
return(true);
result=false;
x0=vector<double>::Zeros(nf);
x1=vector<double>::Zeros(nf);
//--- Basic checks:
//--- * positive completion code
//--- * sizes of arrays
//--- * Rep.P is correct permutation
//--- * Rep.Z contains correct cluster indexes
//--- * Rep.PZ is consistent with Rep.P/Rep.Z
//--- * Rep.PM contains consistent indexes
//--- * GetKClusters() for K=NPoints
bflag=false;
bflag=bflag||rep.m_terminationtype<=0;
if(bflag)
{
result=true;
return(result);
}
bflag=bflag||rep.m_NPoints!=npoints;
bflag=(bflag||CAp::Rows(rep.m_z)!=npoints-1)||(npoints>1&&CAp::Cols(rep.m_z)!=2);
bflag=(bflag||CAp::Rows(rep.m_pz)!=npoints-1)||(npoints>1&&CAp::Cols(rep.m_pz)!=2);
bflag=(bflag||CAp::Rows(rep.m_pm)!=npoints-1)||(npoints>1&&CAp::Cols(rep.m_pm)!=6);
bflag=bflag||CAp::Len(rep.m_mergedist)!=npoints-1;
bflag=bflag||CAp::Len(rep.m_p)!=npoints;
if(bflag)
{
result=true;
return(result);
}
CAblasF::BSetAllocV(npoints,false,b);
for(i=0; i<npoints; i++)
{
if(rep.m_p[i]<0 || rep.m_p[i]>=npoints || b[rep.m_p[i]])
{
result=true;
return(result);
}
b[rep.m_p[i]]=true;
}
for(i=0; i<npoints-1; i++)
{
if(rep.m_z.Get(i,0)<0 || rep.m_z.Get(i,0)>=rep.m_z.Get(i,1) || rep.m_z.Get(i,1)>=npoints+i)
{
result=true;
return(result);
}
if(rep.m_pz.Get(i,0)<0 || rep.m_pz.Get(i,0)>=rep.m_pz.Get(i,1) || rep.m_pz.Get(i,1)>=npoints+i)
{
result=true;
return(result);
}
}
for(i=0; i<npoints-1; i++)
{
c0=rep.m_z.Get(i,0);
c1=rep.m_z.Get(i,1);
s0=rep.m_pz.Get(i,0);
s1=rep.m_pz.Get(i,1);
if(c0<npoints)
c0=rep.m_p[c0];
if(c1<npoints)
c1=rep.m_p[c1];
if(c0!=s0 || c1!=s1)
{
result=true;
return(result);
}
}
CClustering::ClusterizerGetKClusters(rep,npoints,kidx,kidxz);
if(CAp::Len(kidx)!=npoints || CAp::Len(kidxz)!=npoints)
{
result=true;
return(result);
}
for(i=0; i<npoints; i++)
{
if(kidxz[i]!=i || kidx[i]!=i)
{
result=true;
return(result);
}
}
//--- Test description:
//--- * we generate (2*NPoints-1)x(2*NPoints-1) matrix of distances DM and
//--- (2*NPoints-1)xNPoints matrix of clusters CM (I-th row contains indexes
//--- of elements which belong to I-th cluster, negative indexes denote
//--- empty cells). Leading N*N square of DM is just a distance matrix,
//--- other elements are filled by some large number M (used to mark empty
//--- elements).
//--- * we replay all merges
//--- * every time we merge clusters I and J into K, we:
//--- * check that distance between I and J is equal to the smallest
//--- element of DM (note: we account for rounding errors when we
//--- decide on that)
//--- * check that distance is consistent with Rep.MergeDist
//--- * then, we enumerate all elements in clusters being merged,
//--- and check that after permutation their indexes fall into range
//--- prescribed by Rep.PM
//--- * fill K-th column/row of D by distances to cluster K
//--- * merge I-th and J-th rows of CM and store result into K-th row
//--- * clear DM and CM: fill I-th and J-th column/row of DM by large
//--- number M, fill I-th and J-th row of CM by -1.
//--- NOTE: DM is initialized by distance metric specific to AHC algorithm
//--- being used. CLINK, SLINK and average linkage use user-provided
//--- distance measure, say Euclidean one, without any modifications.
//--- Ward's method uses (and reports) squared and scaled Euclidean
//--- distances.
dm=matrix<double>::Zeros(2*npoints-1,2*npoints-1);
cm.Resize(2*npoints-1,npoints);
clustersizes.Resize(2*npoints-1);
for(i=0; i<2*npoints-1; i++)
{
for(j=0; j<2*npoints-1; j++)
if(i<npoints && j<npoints)
{
dm.Set(i,j,d.Get(i,j));
if(ahcalgo==4)
dm.Set(i,j,0.5*CMath::Sqr(dm.Get(i,j)));
}
else
dm.Set(i,j,CMath::m_maxrealnumber);
}
for(i=0; i<2*npoints-1; i++)
{
for(j=0; j<npoints; j++)
cm.Set(i,j,-1);
}
for(i=0; i<npoints; i++)
{
cm.Set(i,0,i);
clustersizes.Set(i,1);
}
for(i=npoints; i<2*npoints-1; i++)
clustersizes.Set(i,0);
clusterheights.Resize(2*npoints-1);
for(i=0; i<npoints; i++)
clusterheights.Set(i,0);
for(mergeidx=0; mergeidx<npoints-1; mergeidx++)
{
//--- Check that clusters with minimum distance are merged,
//--- and that MergeDist is consistent with results.
//--- NOTE: we do not check for specific cluster indexes,
//--- because it is possible to have a tie. We just
//--- check that distance between clusters is a true
//--- minimum over all possible clusters.
v=CMath::m_maxrealnumber;
for(i=0; i<2*npoints-1; i++)
{
for(j=0; j<2*npoints-1; j++)
if(i!=j)
v=MathMin(v,dm.Get(i,j));
}
c0=rep.m_z.Get(mergeidx,0);
c1=rep.m_z.Get(mergeidx,1);
if(dm.Get(c0,c1)>(v+10000*CMath::m_machineepsilon))
{
result=true;
return(result);
}
if(rep.m_mergedist[mergeidx]>(v+10000*CMath::m_machineepsilon))
{
result=true;
return(result);
}
//--- Check that indexes of elements fall into range prescribed by Rep.PM,
//--- and Rep.PM correctly described merge operation
s0=clustersizes[c0];
s1=clustersizes[c1];
for(j=0; j<clustersizes[c0]; j++)
{
if(rep.m_p[cm.Get(c0,j)]<rep.m_pm.Get(mergeidx,0) || rep.m_p[cm.Get(c0,j)]>rep.m_pm.Get(mergeidx,1))
{
//--- Element falls outside of range described by PM
result=true;
return(result);
}
}
for(j=0; j<clustersizes[c1]; j++)
{
if(rep.m_p[cm.Get(c1,j)]<rep.m_pm.Get(mergeidx,2) || rep.m_p[cm.Get(c1,j)]>rep.m_pm.Get(mergeidx,3))
{
//--- Element falls outside of range described by PM
result=true;
return(result);
}
}
if(rep.m_pm.Get(mergeidx,1)-rep.m_pm.Get(mergeidx,0)!=s0-1 || rep.m_pm.Get(mergeidx,3)-rep.m_pm.Get(mergeidx,2)!=s1-1 ||
rep.m_pm.Get(mergeidx,2)!=rep.m_pm.Get(mergeidx,1)+1)
{
//--- Cluster size (as given by PM) is inconsistent with its actual size.
result=true;
return(result);
}
if(rep.m_pm.Get(mergeidx,4)!=clusterheights[rep.m_z.Get(mergeidx,0)] || rep.m_pm.Get(mergeidx,5)!=clusterheights[rep.m_z.Get(mergeidx,1)])
{
//--- Heights of subdendrograms as returned by PM are inconsistent with heights
//--- calculated by us.
result=true;
return(result);
}
//--- Update cluster heights
clusterheights.Set(mergeidx+npoints,MathMax(clusterheights[rep.m_z.Get(mergeidx,0)],clusterheights[rep.m_z.Get(mergeidx,1)])+1);
//--- Update CM
t=0;
for(j=0; j<clustersizes[rep.m_z.Get(mergeidx,0)]; j++)
{
cm.Set(npoints+mergeidx,t,cm.Get(rep.m_z.Get(mergeidx,0),j));
t++;
}
for(j=0; j<clustersizes[rep.m_z.Get(mergeidx,1)]; j++)
{
cm.Set(npoints+mergeidx,t,cm.Get(rep.m_z.Get(mergeidx,1),j));
t++;
}
clustersizes.Set(npoints+mergeidx,t);
clustersizes.Set(rep.m_z.Get(mergeidx,0),0);
clustersizes.Set(rep.m_z.Get(mergeidx,1),0);
//--- Update distance matrix D
for(i=0; i<2*npoints-1; i++)
{
//--- "Remove" columns/rows corresponding to clusters being merged
dm.Set(i,rep.m_z.Get(mergeidx,0),CMath::m_maxrealnumber);
dm.Set(i,rep.m_z.Get(mergeidx,1),CMath::m_maxrealnumber);
dm.Set(rep.m_z.Get(mergeidx,0),i,CMath::m_maxrealnumber);
dm.Set(rep.m_z.Get(mergeidx,1),i,CMath::m_maxrealnumber);
}
for(i=0; i<npoints+mergeidx; i++)
{
if(clustersizes[i]>0)
{
//--- Calculate column/row corresponding to new cluster
switch(ahcalgo)
{
case 0:
//--- Calculate distance between clusters I and NPoints+MergeIdx for CLINK
v=0.0;
for(i0=0; i0<clustersizes[i]; i0++)
for(i1=0; i1<clustersizes[npoints+mergeidx]; i1++)
v=MathMax(v,d.Get(cm.Get(i,i0),cm.Get(npoints+mergeidx,i1)));
break;
case 1:
//--- Calculate distance between clusters I and NPoints+MergeIdx for SLINK
v=CMath::m_maxrealnumber;
for(i0=0; i0<clustersizes[i]; i0++)
for(i1=0; i1<clustersizes[npoints+mergeidx]; i1++)
v=MathMin(v,d.Get(cm.Get(i,i0),cm.Get(npoints+mergeidx,i1)));
break;
case 2:
//--- Calculate distance between clusters I and NPoints+MergeIdx for unweighted average
v=0.0;
t=0;
for(i0=0; i0<clustersizes[i]; i0++)
{
for(i1=0; i1<clustersizes[npoints+mergeidx]; i1++)
{
v+=d.Get(cm.Get(i,i0),cm.Get(npoints+mergeidx,i1));
t++;
}
}
v=v/(double)t;
break;
case 3:
CAp::Assert(false);
return(true);
case 4:
//--- Calculate distance between clusters I and NPoints+MergeIdx for Ward's method:
//--- * X0 = center of mass for cluster I
//--- * X1 = center of mass for cluster NPoints+MergeIdx
//--- * S0 = size of cluster I
//--- * S1 = size of cluster NPoints+MergeIdx
//--- * distance between clusters is S0*S1/(S0+S1)*|X0-X1|^2
for(j=0; j<nf; j++)
{
x0.Set(j,0.0);
x1.Set(j,0.0);
}
for(i0=0; i0<clustersizes[i]; i0++)
{
for(j=0; j<nf; j++)
x0.Add(j,xy.Get(cm.Get(i,i0),j)/clustersizes[i]);
}
for(i1=0; i1<clustersizes[npoints+mergeidx]; i1++)
{
for(j=0; j<nf; j++)
x1.Add(j,xy.Get(cm.Get(npoints+mergeidx,i1),j)/clustersizes[npoints+mergeidx]);
}
v=0.0;
for(j=0; j<nf; j++)
v+=CMath::Sqr(x0[j]-x1[j]);
v=v*clustersizes[i]*clustersizes[npoints+mergeidx]/(clustersizes[i]+clustersizes[npoints+mergeidx]);
}
dm.Set(i,npoints+mergeidx,v);
dm.Set(npoints+mergeidx,i,v);
}
}
//--- Check that GetKClusters() correctly unpacks clusters for K=NPoints-(MergeIdx+1):
//--- * check lengths of arays
//--- * check consistency of CIdx/CZ parameters
//--- * scan clusters (CZ parameter), for each cluster scan CM matrix which stores
//--- cluster elements (according to our replay of merges), for each element of
//--- the current cluster check that CIdx array correctly reflects its status.
k=npoints-(mergeidx+1);
CClustering::ClusterizerGetKClusters(rep,k,kidx,kidxz);
if(CAp::Len(kidx)!=npoints || CAp::Len(kidxz)!=k)
{
result=true;
return(result);
}
for(i=0; i<=k-2; i++)
{
if(kidxz[i]<0 || kidxz[i]>=kidxz[i+1] || kidxz[i+1]>2*npoints-2)
{
//--- CZ is inconsistent
result=true;
return(result);
}
}
for(i=0; i<npoints; i++)
{
if(kidx[i]<0 || kidx[i]>=k)
{
//--- CIdx is inconsistent
result=true;
return(result);
}
}
for(i=0; i<=k-1; i++)
{
for(j=0; j<clustersizes[kidxz[i]]; j++)
{
currentelement=cm.Get(kidxz[i],j);
if(kidx[currentelement]!=i)
{
//--- We've found element which belongs to I-th cluster (according to CM
//--- matrix, which reflects current status of agglomerative clustering),
//--- but this element does not belongs to I-th cluster according to
//--- results of ClusterizerGetKClusters()
result=true;
return(result);
}
}
}
}
//--- return result
return(result);
}
//+------------------------------------------------------------------+
//| This procedure is a reference version of KMeansUpdateDistances().|
//| INPUT PARAMETERS: |
//| XY - dataset, array [0..NPoints-1,0..NVars-1]. |
//| NPoints - dataset size, NPoints>=K |
//| NVars - number of variables, NVars>=1 |
//| CT - matrix of centers, centers are stored in rows |
//| K - number of centers, K>=1 |
//| XYC - preallocated output buffer |
//| XYDist2 - preallocated output buffer |
//| OUTPUT PARAMETERS: |
//| XYC - new assignment of points to centers |
//| XYDist2 - squared distances |
//+------------------------------------------------------------------+
void CTestClusteringUnit::KMeansReferenceUpdateDistances(CMatrixDouble &xy,
int npoints,
int nvars,
CMatrixDouble &ct,
int k,
CRowInt &xyc,
CRowDouble &xydist2)
{
//--- create variables
int cclosest=0;
double dclosest=0;
double v=0;
CRowDouble tmp;
tmp=vector<double>::Zeros(nvars);
for(int i=0; i<npoints; i++)
{
cclosest=-1;
dclosest=CMath::m_maxrealnumber;
for(int j=0; j<k; j++)
{
for(int i_=0; i_<nvars; i_++)
tmp.Set(i_,xy.Get(i,i_)-ct.Get(j,i_));
v=CAblasF::RDotV2(nvars,tmp);
if(v<dclosest)
{
cclosest=j;
dclosest=v;
}
}
//--- check
if(!CAp::Assert(cclosest>=0,"KMeansUpdateDistances: internal error"))
return;
xyc.Set(i,cclosest);
xydist2.Set(i,dclosest);
}
}
//+------------------------------------------------------------------+