43858 lines
1.3 MiB
Plaintext
43858 lines
1.3 MiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| TestClasses.mqh |
|
|
//| Copyright 2003-2012 Sergey Bochkanov (ALGLIB project) |
|
|
//| Copyright 2012-2017, MetaQuotes Software Corp. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
//| Implementation of ALGLIB library in MetaQuotes Language 5 |
|
|
//| |
|
|
//| The features of the library include: |
|
|
//| - Linear algebra (direct algorithms, EVD, SVD) |
|
|
//| - Solving systems of linear and non-linear equations |
|
|
//| - Interpolation |
|
|
//| - Optimization |
|
|
//| - FFT (Fast Fourier Transform) |
|
|
//| - Numerical integration |
|
|
//| - Linear and nonlinear least-squares fitting |
|
|
//| - Ordinary differential equations |
|
|
//| - Computation of special functions |
|
|
//| - Descriptive statistics and hypothesis testing |
|
|
//| - Data analysis - classification, regression |
|
|
//| - Implementing linear algebra algorithms, interpolation, etc. |
|
|
//| in high-precision arithmetic (using MPFR) |
|
|
//| |
|
|
//| This file is free software; you can redistribute it and/or |
|
|
//| modify it under the terms of the GNU General Public License as |
|
|
//| published by the Free Software Foundation (www.fsf.org); either |
|
|
//| version 2 of the License, or (at your option) any later version. |
|
|
//| |
|
|
//| This program is distributed in the hope that it will be useful, |
|
|
//| but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
//| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
//| GNU General Public License for more details. |
|
|
//+------------------------------------------------------------------+
|
|
#include <Math\Alglib\alglibmisc.mqh>
|
|
#include <Math\Alglib\ap.mqh>
|
|
#include <Math\Alglib\dataanalysis.mqh>
|
|
#include <Math\Alglib\diffequations.mqh>
|
|
#include <Math\Alglib\interpolation.mqh>
|
|
#include <Math\Alglib\fasttransforms.mqh>
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CHighQualityRand |
|
|
//+------------------------------------------------------------------+
|
|
class CTestHQRndUnit
|
|
{
|
|
public:
|
|
CTestHQRndUnit(void);
|
|
~CTestHQRndUnit(void);
|
|
|
|
static bool TestHQRnd(const bool silent);
|
|
|
|
private:
|
|
static void CalculateMV(double &x[],const int n,double &mean,double &means,double &stddev,double &stddevs);
|
|
static void UnsetState(CHighQualityRandState &state);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestHQRndUnit::CTestHQRndUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestHQRndUnit::~CTestHQRndUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CHighQualityRand |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestHQRndUnit::TestHQRnd(const bool silent)
|
|
{
|
|
bool waserrors;
|
|
int samplesize=0;
|
|
double sigmathreshold=0;
|
|
int passcount=0;
|
|
int n=0;
|
|
int i=0;
|
|
int pass=0;
|
|
int s1=0;
|
|
int s2=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
double r1=0;
|
|
double r2=0;
|
|
double mean=0;
|
|
double means=0;
|
|
double stddev=0;
|
|
double stddevs=0;
|
|
double lambdav=0;
|
|
bool seederrors;
|
|
bool urerrors;
|
|
double ursigmaerr=0;
|
|
bool uierrors;
|
|
double uisigmaerr=0;
|
|
bool normerrors;
|
|
double normsigmaerr=0;
|
|
bool experrors;
|
|
double expsigmaerr=0;
|
|
//--- create array
|
|
double x[];
|
|
//--- object of class
|
|
CHighQualityRandState state;
|
|
//--- initialization
|
|
waserrors=false;
|
|
sigmathreshold=7;
|
|
samplesize=100000;
|
|
passcount=50;
|
|
seederrors=false;
|
|
urerrors=false;
|
|
uierrors=false;
|
|
normerrors=false;
|
|
experrors=false;
|
|
//--- allocation
|
|
ArrayResize(x,samplesize);
|
|
//--- Test seed errors
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- change values
|
|
s1=1+CMath::RandomInteger(32000);
|
|
s2=1+CMath::RandomInteger(32000);
|
|
//--- function calls
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndSeed(s1,s2,state);
|
|
//--- change value
|
|
i1=CHighQualityRand::HQRndUniformI(state,100);
|
|
//--- function calls
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndSeed(s1,s2,state);
|
|
//--- change values
|
|
i2=CHighQualityRand::HQRndUniformI(state,100);
|
|
seederrors=seederrors || i1!=i2;
|
|
//--- function calls
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndSeed(s1,s2,state);
|
|
//--- change value
|
|
r1=CHighQualityRand::HQRndUniformR(state);
|
|
//--- function calls
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndSeed(s1,s2,state);
|
|
//--- change values
|
|
r2=CHighQualityRand::HQRndUniformR(state);
|
|
seederrors=seederrors || r1!=r2;
|
|
}
|
|
//--- Test HQRNDRandomize() and real uniform generator
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
//--- change values
|
|
ursigmaerr=0;
|
|
for(i=0;i<=samplesize-1;i++)
|
|
x[i]=CHighQualityRand::HQRndUniformR(state);
|
|
for(i=0;i<=samplesize-1;i++)
|
|
urerrors=(urerrors || x[i]<=0.0) || x[i]>=1.0;
|
|
//--- function call
|
|
CalculateMV(x,samplesize,mean,means,stddev,stddevs);
|
|
//--- check
|
|
if(means!=0.0)
|
|
ursigmaerr=MathMax(ursigmaerr,MathAbs((mean-0.5)/means));
|
|
else
|
|
urerrors=true;
|
|
//--- check
|
|
if(stddevs!=0.0)
|
|
ursigmaerr=MathMax(ursigmaerr,MathAbs((stddev-MathSqrt(1.0/12.0))/stddevs));
|
|
else
|
|
urerrors=true;
|
|
//--- change value
|
|
urerrors=urerrors || ursigmaerr>sigmathreshold;
|
|
//--- Test HQRNDRandomize() and integer uniform
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
//--- calculation
|
|
uisigmaerr=0;
|
|
for(n=2;n<=10;n++)
|
|
{
|
|
for(i=0;i<=samplesize-1;i++)
|
|
x[i]=CHighQualityRand::HQRndUniformI(state,n);
|
|
for(i=0;i<=samplesize-1;i++)
|
|
uierrors=(uierrors || x[i]<0.0) || x[i]>=n;
|
|
//--- function call
|
|
CalculateMV(x,samplesize,mean,means,stddev,stddevs);
|
|
//--- check
|
|
if(means!=0.0)
|
|
uisigmaerr=MathMax(uisigmaerr,MathAbs((mean-0.5*(n-1))/means));
|
|
else
|
|
uierrors=true;
|
|
//--- check
|
|
if(stddevs!=0.0)
|
|
uisigmaerr=MathMax(uisigmaerr,MathAbs((stddev-MathSqrt((CMath::Sqr(n)-1)/12))/stddevs));
|
|
else
|
|
uierrors=true;
|
|
}
|
|
//--- change values
|
|
uierrors=uierrors || uisigmaerr>sigmathreshold;
|
|
//--- Special 'close-to-limit' test on uniformity of integers
|
|
//--- (straightforward implementation like 'RND mod N' will return
|
|
//--- non-uniform numbers for N=2/3*LIMIT)
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
//--- change values
|
|
uisigmaerr=0;
|
|
n=1431655708;
|
|
//--- calculation
|
|
for(i=0;i<=samplesize-1;i++)
|
|
x[i]=CHighQualityRand::HQRndUniformI(state,n);
|
|
for(i=0;i<=samplesize-1;i++)
|
|
uierrors=(uierrors || x[i]<0.0) || x[i]>=n;
|
|
//--- function call
|
|
CalculateMV(x,samplesize,mean,means,stddev,stddevs);
|
|
//--- check
|
|
if(means!=0.0)
|
|
uisigmaerr=MathMax(uisigmaerr,MathAbs((mean-0.5*(n-1))/means));
|
|
else
|
|
uierrors=true;
|
|
//--- check
|
|
if(stddevs!=0.0)
|
|
uisigmaerr=MathMax(uisigmaerr,MathAbs((stddev-MathSqrt((CMath::Sqr(n)-1)/12))/stddevs));
|
|
else
|
|
uierrors=true;
|
|
uierrors=uierrors || uisigmaerr>sigmathreshold;
|
|
//--- Test normal
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
//--- change values
|
|
normsigmaerr=0;
|
|
i=0;
|
|
//--- cycle
|
|
while(i<samplesize)
|
|
{
|
|
//--- function call
|
|
CHighQualityRand::HQRndNormal2(state,r1,r2);
|
|
x[i]=r1;
|
|
//--- check
|
|
if(i+1<samplesize)
|
|
{
|
|
x[i+1]=r2;
|
|
}
|
|
i=i+2;
|
|
}
|
|
//--- function call
|
|
CalculateMV(x,samplesize,mean,means,stddev,stddevs);
|
|
//--- check
|
|
if(means!=0.0)
|
|
normsigmaerr=MathMax(normsigmaerr,MathAbs((mean-0)/means));
|
|
else
|
|
normerrors=true;
|
|
//--- check
|
|
if(stddevs!=0.0)
|
|
normsigmaerr=MathMax(normsigmaerr,MathAbs((stddev-1)/stddevs));
|
|
else
|
|
normerrors=true;
|
|
normerrors=normerrors || (double)(normsigmaerr)>sigmathreshold;
|
|
//--- Test exponential
|
|
UnsetState(state);
|
|
CHighQualityRand::HQRndRandomize(state);
|
|
//--- change values
|
|
expsigmaerr=0;
|
|
lambdav=2+5*CMath::RandomReal();
|
|
//--- calculation
|
|
for(i=0;i<=samplesize-1;i++)
|
|
x[i]=CHighQualityRand::HQRndExponential(state,lambdav);
|
|
for(i=0;i<=samplesize-1;i++)
|
|
uierrors=uierrors || x[i]<0.0;
|
|
//--- function call
|
|
CalculateMV(x,samplesize,mean,means,stddev,stddevs);
|
|
//--- check
|
|
if(means!=0.0)
|
|
expsigmaerr=MathMax(expsigmaerr,MathAbs((mean-1.0/lambdav)/means));
|
|
else
|
|
experrors=true;
|
|
//--- check
|
|
if(stddevs!=0.0)
|
|
expsigmaerr=MathMax(expsigmaerr,MathAbs((stddev-1.0/lambdav)/stddevs));
|
|
else
|
|
experrors=true;
|
|
experrors=experrors || expsigmaerr>sigmathreshold;
|
|
//--- Final report
|
|
waserrors=(((seederrors || urerrors) || uierrors) || normerrors) || experrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("RNG TEST");
|
|
//--- check
|
|
if(!seederrors)
|
|
Print("SEED TEST: OK");
|
|
else
|
|
Print("SEED TEST: FAILED");
|
|
//--- check
|
|
if(!urerrors)
|
|
Print("UNIFORM CONTINUOUS: OK");
|
|
else
|
|
Print("UNIFORM CONTINUOUS: FAILED");
|
|
//--- check
|
|
if(!uierrors)
|
|
Print("UNIFORM INTEGER: OK");
|
|
else
|
|
Print("UNIFORM INTEGER: FAILED");
|
|
//--- check
|
|
if(!normerrors)
|
|
Print("NORMAL: OK");
|
|
else
|
|
Print("NORMAL: FAILED");
|
|
//--- check
|
|
if(!experrors)
|
|
Print("EXPONENTIAL: OK");
|
|
else
|
|
Print("EXPONENTIAL: FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST SUMMARY: FAILED");
|
|
else
|
|
Print("TEST SUMMARY: PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestHQRndUnit::CalculateMV(double &x[],const int n,double &mean,
|
|
double &means,double &stddev,
|
|
double &stddevs)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double variance=0;
|
|
//--- initialization
|
|
mean=0;
|
|
means=1;
|
|
stddev=0;
|
|
stddevs=1;
|
|
variance=0;
|
|
//--- check
|
|
if(n<=1)
|
|
return;
|
|
//--- Mean
|
|
for(i=0;i<=n-1;i++)
|
|
mean=mean+x[i];
|
|
mean=mean/n;
|
|
//--- Variance (using corrected two-pass algorithm)
|
|
if(n!=1)
|
|
{
|
|
//--- change value
|
|
v1=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v1=v1+CMath::Sqr(x[i]-mean);
|
|
//--- change value
|
|
v2=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v2=v2+(x[i]-mean);
|
|
//--- calculation
|
|
v2=CMath::Sqr(v2)/n;
|
|
variance=(v1-v2)/(n-1);
|
|
//--- check
|
|
if(variance<0.0)
|
|
variance=0;
|
|
stddev=MathSqrt(variance);
|
|
}
|
|
//--- Errors
|
|
means=stddev/MathSqrt(n);
|
|
stddevs=stddev*MathSqrt(2)/MathSqrt(n-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets HQRNDState structure |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestHQRndUnit::UnsetState(CHighQualityRandState &state)
|
|
{
|
|
state.m_s1=0;
|
|
state.m_s2=0;
|
|
state.m_v=0;
|
|
state.m_magicv=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CTSort |
|
|
//+------------------------------------------------------------------+
|
|
class CTestTSortUnit
|
|
{
|
|
public:
|
|
CTestTSortUnit(void);
|
|
~CTestTSortUnit(void);
|
|
|
|
static bool TestTSort(const bool silent);
|
|
|
|
private:
|
|
static void Unset2D(CMatrixComplex &a);
|
|
static void Unset1D(double &a[]);
|
|
static void Unset1DI(int &a[]);
|
|
static void TestSortResults(double &asorted[],int &p1[],int &p2[],double &aoriginal[],const int n,bool &waserrors);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestTSortUnit::CTestTSortUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestTSortUnit::~CTestTSortUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CTSort |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestTSortUnit::TestTSort(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
int n=0;
|
|
int i=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
int maxn=0;
|
|
//--- create arrays
|
|
double a[];
|
|
double a0[];
|
|
double a1[];
|
|
double a2[];
|
|
double a3[];
|
|
double ar[];
|
|
int ai[];
|
|
int p1[];
|
|
int p2[];
|
|
double bufr1[];
|
|
double bufr2[];
|
|
int bufi1[];
|
|
//--- initialization
|
|
waserrors=false;
|
|
maxn=100;
|
|
passcount=10;
|
|
//--- Test tagsort
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- (probably) distinct sort:
|
|
//--- * first sort A0 using TagSort and test sort results
|
|
//--- * now we can use A0 as reference point and test other functions
|
|
Unset1DI(p1);
|
|
Unset1DI(p2);
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(a0,n);
|
|
ArrayResize(a1,n);
|
|
ArrayResize(a2,n);
|
|
ArrayResize(a3,n);
|
|
ArrayResize(ar,n);
|
|
ArrayResize(ai,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i]=2*CMath::RandomReal()-1;
|
|
a0[i]=a[i];
|
|
a1[i]=a[i];
|
|
a2[i]=a[i];
|
|
a3[i]=a[i];
|
|
ar[i]=i;
|
|
ai[i]=i;
|
|
}
|
|
//--- function call
|
|
CTSort::TagSort(a0,n,p1,p2);
|
|
//--- function call
|
|
TestSortResults(a0,p1,p2,a,n,waserrors);
|
|
//--- function call
|
|
CTSort::TagSortFastI(a1,ai,bufr1,bufi1,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=(waserrors || a1[i]!=a0[i]) || ai[i]!=p1[i];
|
|
//--- function call
|
|
CTSort::TagSortFastR(a2,ar,bufr1,bufr2,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=(waserrors || a2[i]!=a0[i]) || ar[i]!=p1[i];
|
|
//--- function call
|
|
CTSort::TagSortFast(a3,bufr1,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=waserrors || a3[i]!=a0[i];
|
|
//--- non-distinct sort
|
|
Unset1DI(p1);
|
|
Unset1DI(p2);
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(a0,n);
|
|
ArrayResize(a1,n);
|
|
ArrayResize(a2,n);
|
|
ArrayResize(a3,n);
|
|
ArrayResize(ar,n);
|
|
ArrayResize(ai,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i]=i/2;
|
|
a0[i]=a[i];
|
|
a1[i]=a[i];
|
|
a2[i]=a[i];
|
|
a3[i]=a[i];
|
|
ar[i]=i;
|
|
ai[i]=i;
|
|
}
|
|
//--- function call
|
|
CTSort::TagSort(a0,n,p1,p2);
|
|
//--- function call
|
|
TestSortResults(a0,p1,p2,a,n,waserrors);
|
|
//--- function call
|
|
CTSort::TagSortFastI(a1,ai,bufr1,bufi1,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=(waserrors || a1[i]!=a0[i]) || ai[i]!=p1[i];
|
|
//--- function call
|
|
CTSort::TagSortFastR(a2,ar,bufr1,bufr2,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=(waserrors || a2[i]!=a0[i]) || ar[i]!=p1[i];
|
|
//--- function call
|
|
CTSort::TagSortFast(a3,bufr1,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=waserrors || a3[i]!=a0[i];
|
|
//--- 'All same' sort
|
|
Unset1DI(p1);
|
|
Unset1DI(p2);
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(a0,n);
|
|
ArrayResize(a1,n);
|
|
ArrayResize(a2,n);
|
|
ArrayResize(a3,n);
|
|
ArrayResize(ar,n);
|
|
ArrayResize(ai,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i]=0;
|
|
a0[i]=a[i];
|
|
a1[i]=a[i];
|
|
a2[i]=a[i];
|
|
a3[i]=a[i];
|
|
ar[i]=i;
|
|
ai[i]=i;
|
|
}
|
|
//--- function call
|
|
CTSort::TagSort(a0,n,p1,p2);
|
|
//--- function call
|
|
TestSortResults(a0,p1,p2,a,n,waserrors);
|
|
//--- function call
|
|
CTSort::TagSortFastI(a1,ai,bufr1,bufi1,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=(waserrors || a1[i]!=a0[i]) || ai[i]!=p1[i];
|
|
//--- function call
|
|
CTSort::TagSortFastR(a2,ar,bufr1,bufr2,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=(waserrors || a2[i]!=a0[i]) || ar[i]!=p1[i];
|
|
//--- function call
|
|
CTSort::TagSortFast(a3,bufr1,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=waserrors || a3[i]!=a0[i];
|
|
//--- 0-1 sort
|
|
Unset1DI(p1);
|
|
Unset1DI(p2);
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(a0,n);
|
|
ArrayResize(a1,n);
|
|
ArrayResize(a2,n);
|
|
ArrayResize(a3,n);
|
|
ArrayResize(ar,n);
|
|
ArrayResize(ai,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i]=CMath::RandomInteger(2);
|
|
a0[i]=a[i];
|
|
a1[i]=a[i];
|
|
a2[i]=a[i];
|
|
a3[i]=a[i];
|
|
ar[i]=i;
|
|
ai[i]=i;
|
|
}
|
|
//--- function call
|
|
CTSort::TagSort(a0,n,p1,p2);
|
|
//--- function call
|
|
TestSortResults(a0,p1,p2,a,n,waserrors);
|
|
//--- function call
|
|
CTSort::TagSortFastI(a1,ai,bufr1,bufi1,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=(waserrors || a1[i]!=a0[i]) || ai[i]!=p1[i];
|
|
//--- function call
|
|
CTSort::TagSortFastR(a2,ar,bufr1,bufr2,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=(waserrors || a2[i]!=a0[i]) || ar[i]!=p1[i];
|
|
//--- function call
|
|
CTSort::TagSortFast(a3,bufr1,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=waserrors || a3[i]!=a0[i];
|
|
}
|
|
}
|
|
//--- report
|
|
if(!silent)
|
|
{
|
|
Print("TESTING TAGSORT");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 2D array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestTSortUnit::Unset2D(CMatrixComplex &a)
|
|
{
|
|
//--- allocation
|
|
a.Resize(1,1);
|
|
//--- change value
|
|
a[0].Set(0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 1D array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestTSortUnit::Unset1D(double &a[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,1);
|
|
//--- change value
|
|
a[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 1D array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestTSortUnit::Unset1DI(int &a[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,1);
|
|
//--- change value
|
|
a[0]=CMath::RandomInteger(3)-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestTSortUnit::TestSortResults(double &asorted[],int &p1[],
|
|
int &p2[],double &aoriginal[],
|
|
const int n,bool &waserrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double t=0;
|
|
//--- create arrays
|
|
double a2[];
|
|
int f[];
|
|
//--- allocation
|
|
ArrayResize(a2,n);
|
|
ArrayResize(f,n);
|
|
//--- is set ordered?
|
|
for(i=0;i<=n-2;i++)
|
|
waserrors=waserrors || asorted[i]>asorted[i+1];
|
|
//--- P1 correctness
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=waserrors || asorted[i]!=aoriginal[p1[i]];
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
f[i]=0;
|
|
for(i=0;i<=n-1;i++)
|
|
f[p1[i]]=f[p1[i]]+1;
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=waserrors || f[i]!=1;
|
|
//--- P2 correctness
|
|
for(i=0;i<=n-1;i++)
|
|
a2[i]=aoriginal[i];
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(p2[i]!=i)
|
|
{
|
|
t=a2[i];
|
|
a2[i]=a2[p2[i]];
|
|
a2[p2[i]]=t;
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=waserrors || asorted[i]!=a2[i];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CNearestNeighbor |
|
|
//+------------------------------------------------------------------+
|
|
class CTestNearestNeighborUnit
|
|
{
|
|
public:
|
|
CTestNearestNeighborUnit(void);
|
|
~CTestNearestNeighborUnit(void);
|
|
|
|
static bool TestNearestNeighbor(const bool silent);
|
|
static void Unset2D(CMatrixComplex &a);
|
|
static void Unset1D(double &a[]);
|
|
static bool KDTResultsDifferent(CMatrixDouble &refxy,const int ntotal,CMatrixDouble &qx,CMatrixDouble &qxy,int &qt[],const int n,const int nx,const int ny);
|
|
static double VNorm(double &x[],const int n,const int normtype);
|
|
static void TestKDTUniform(CMatrixDouble &xy,const int n,const int nx,const int ny,const int normtype,bool &kdterrors);
|
|
|
|
private:
|
|
static void TestKDTreeSerialization(bool &err);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestNearestNeighborUnit::CTestNearestNeighborUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestNearestNeighborUnit::~CTestNearestNeighborUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CNearestNeighbor |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestNearestNeighborUnit::TestNearestNeighbor(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int normtype=0;
|
|
int nx=0;
|
|
int ny=0;
|
|
int n=0;
|
|
int smalln=0;
|
|
int largen=0;
|
|
int passcount=0;
|
|
int pass=0;
|
|
bool waserrors;
|
|
bool kdterrors;
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- initialization
|
|
kdterrors=false;
|
|
passcount=2;
|
|
smalln=256;
|
|
largen=2048;
|
|
ny=3;
|
|
//--- function call
|
|
TestKDTreeSerialization(kdterrors);
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(normtype=0;normtype<=2;normtype++)
|
|
{
|
|
for(nx=1;nx<=3;nx++)
|
|
{
|
|
//--- Test in hypercube
|
|
xy.Resize(largen,nx+ny);
|
|
for(i=0;i<=largen-1;i++)
|
|
{
|
|
for(j=0;j<=nx+ny-1;j++)
|
|
xy[i].Set(j,10*CMath::RandomReal()-5);
|
|
}
|
|
//--- function calls
|
|
for(n=1;n<=10;n++)
|
|
TestKDTUniform(xy,n,nx,CMath::RandomInteger(ny+1),normtype,kdterrors);
|
|
TestKDTUniform(xy,largen,nx,CMath::RandomInteger(ny+1),normtype,kdterrors);
|
|
//--- Test clustered (2*N points,pairs of equal points)
|
|
xy.Resize(2*smalln,nx+ny);
|
|
for(i=0;i<=smalln-1;i++)
|
|
{
|
|
for(j=0;j<=nx+ny-1;j++)
|
|
{
|
|
xy[2*i].Set(j,10*CMath::RandomReal()-5);
|
|
xy[2*i+1].Set(j,xy[2*i][j]);
|
|
}
|
|
}
|
|
//--- function call
|
|
TestKDTUniform(xy,2*smalln,nx,CMath::RandomInteger(ny+1),normtype,kdterrors);
|
|
//--- Test degenerate case: all points are same except for one
|
|
xy.Resize(smalln,nx+ny);
|
|
v=CMath::RandomReal();
|
|
//--- change values
|
|
for(i=0;i<=smalln-2;i++)
|
|
{
|
|
for(j=0;j<=nx+ny-1;j++)
|
|
xy[i].Set(j,v);
|
|
}
|
|
for(j=0;j<=nx+ny-1;j++)
|
|
xy[smalln-1].Set(j,10*CMath::RandomReal()-5);
|
|
//--- function call
|
|
TestKDTUniform(xy,smalln,nx,CMath::RandomInteger(ny+1),normtype,kdterrors);
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=kdterrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING NEAREST NEIGHBOR SEARCH");
|
|
//--- check
|
|
if(!kdterrors)
|
|
Print("KD TREES: OK");
|
|
else
|
|
Print("KD TREES: FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 2D array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestNearestNeighborUnit::Unset2D(CMatrixComplex &a)
|
|
{
|
|
//--- allocation
|
|
a.Resize(1,1);
|
|
//--- change value
|
|
a[0].Set(0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 1D array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestNearestNeighborUnit::Unset1D(double &a[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,1);
|
|
//--- change value
|
|
a[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Compare results from different queries: |
|
|
//| * X just X-values |
|
|
//| * XY X-values and Y-values |
|
|
//| * XT X-values and tag values |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestNearestNeighborUnit::KDTResultsDifferent(CMatrixDouble &refxy,
|
|
const int ntotal,
|
|
CMatrixDouble &qx,
|
|
CMatrixDouble &qxy,
|
|
int &qt[],const int n,
|
|
const int nx,const int ny)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
//--- initialization
|
|
result=false;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(qt[i]<0 || qt[i]>=ntotal)
|
|
{
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- search errors
|
|
for(j=0;j<=nx-1;j++)
|
|
{
|
|
result=result || qx[i][j]!=refxy[qt[i]][j];
|
|
result=result || qxy[i][j]!=refxy[qt[i]][j];
|
|
}
|
|
for(j=0;j<=ny-1;j++)
|
|
result=result || qxy[i][nx+j]!=refxy[qt[i]][nx+j];
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns norm |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestNearestNeighborUnit::VNorm(double &x[],const int n,
|
|
const int normtype)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
//--- initialization
|
|
result=CMath::RandomReal();
|
|
//--- check
|
|
if(normtype==0)
|
|
{
|
|
//--- calculation
|
|
result=0;
|
|
for(i=0;i<=n-1;i++)
|
|
result=MathMax(result,MathAbs(x[i]));
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- check
|
|
if(normtype==1)
|
|
{
|
|
//--- calculation
|
|
result=0;
|
|
for(i=0;i<=n-1;i++)
|
|
result=result+MathAbs(x[i]);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- check
|
|
if(normtype==2)
|
|
{
|
|
//--- calculation
|
|
result=0;
|
|
for(i=0;i<=n-1;i++)
|
|
result=result+CMath::Sqr(x[i]);
|
|
result=MathSqrt(result);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing Nearest Neighbor Search on uniformly distributed |
|
|
//| hypercube |
|
|
//| NormType: 0,1,2 |
|
|
//| D: space dimension |
|
|
//| N: points count |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestNearestNeighborUnit::TestKDTUniform(CMatrixDouble &xy,
|
|
const int n,const int nx,
|
|
const int ny,const int normtype,
|
|
bool &kdterrors)
|
|
{
|
|
//--- create variables
|
|
double errtol=0;
|
|
int kx=0;
|
|
int kxy=0;
|
|
int kt=0;
|
|
double eps=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int task=0;
|
|
bool isequal;
|
|
double r=0;
|
|
int q=0;
|
|
int qcount=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
int tags[];
|
|
double ptx[];
|
|
double tmpx[];
|
|
bool tmpb[];
|
|
int qtags[];
|
|
double qr[];
|
|
//--- objects of classes
|
|
CKDTree treex;
|
|
CKDTree treexy;
|
|
CKDTree treext;
|
|
//--- create matrix
|
|
CMatrixDouble qx;
|
|
CMatrixDouble qxy;
|
|
//--- initialization
|
|
qcount=10;
|
|
//--- Tol - roundoff error tolerance (for '>=' comparisons)
|
|
errtol=100000*CMath::m_machineepsilon;
|
|
//--- fill tags
|
|
ArrayResize(tags,n);
|
|
for(i=0;i<=n-1;i++)
|
|
tags[i]=i;
|
|
//--- build trees
|
|
CNearestNeighbor::KDTreeBuild(xy,n,nx,0,normtype,treex);
|
|
CNearestNeighbor::KDTreeBuild(xy,n,nx,ny,normtype,treexy);
|
|
CNearestNeighbor::KDTreeBuildTagged(xy,tags,n,nx,0,normtype,treext);
|
|
//--- allocate arrays
|
|
ArrayResize(tmpx,nx);
|
|
ArrayResize(tmpb,n);
|
|
qx.Resize(n,nx);
|
|
qxy.Resize(n,nx+ny);
|
|
ArrayResize(qtags,n);
|
|
ArrayResize(qr,n);
|
|
ArrayResize(ptx,nx);
|
|
//--- test general K-NN queries (with self-matches):
|
|
//--- * compare results from different trees (must be equal) and
|
|
//--- check that correct (value,tag) pairs are returned
|
|
//--- * test results from XT tree - let R be radius of query result.
|
|
//--- then all points not in result must be not closer than R.
|
|
for(q=1;q<=qcount;q++)
|
|
{
|
|
//--- Select K: 1..N
|
|
if(CMath::RandomReal()>0.5)
|
|
k=1+CMath::RandomInteger(n);
|
|
else
|
|
k=1;
|
|
//--- Select point (either one of the points,or random)
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
i=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
ptx[i_]=xy[i][i_];
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=nx-1;i++)
|
|
ptx[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- Test:
|
|
//--- * consistency of results from different queries
|
|
//--- * points in query are IN the R-sphere (or at the boundary),
|
|
//--- and points not in query are outside of the R-sphere (or at the boundary)
|
|
//--- * distances are correct and are ordered
|
|
kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,k,true);
|
|
kxy=CNearestNeighbor::KDTreeQueryKNN(treexy,ptx,k,true);
|
|
kt=CNearestNeighbor::KDTreeQueryKNN(treext,ptx,k,true);
|
|
//--- check
|
|
if((kx!=k || kxy!=k) || kt!=k)
|
|
{
|
|
kdterrors=true;
|
|
return;
|
|
}
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsXI(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXYI(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTagsI(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistancesI(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsX(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
tmpb[i]=true;
|
|
r=0;
|
|
//--- calculation
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
tmpb[qtags[i]]=false;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
tmpx[i_]=ptx[i_];
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
tmpx[i_]=tmpx[i_]-qx[i][i_];
|
|
r=MathMax(r,VNorm(tmpx,nx,normtype));
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(tmpb[i])
|
|
{
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
tmpx[i_]=ptx[i_];
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
tmpx[i_]=tmpx[i_]-xy[i][i_];
|
|
//--- search errors
|
|
kdterrors=kdterrors || VNorm(tmpx,nx,normtype)<r*(1-errtol);
|
|
}
|
|
}
|
|
for(i=0;i<=k-2;i++)
|
|
{
|
|
//--- search errors
|
|
kdterrors=kdterrors || qr[i]>qr[i+1];
|
|
}
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
tmpx[i_]=ptx[i_];
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
tmpx[i_]=tmpx[i_]-xy[qtags[i]][i_];
|
|
//--- search errors
|
|
kdterrors=kdterrors || MathAbs(VNorm(tmpx,nx,normtype)-qr[i])>errtol;
|
|
}
|
|
//--- Test reallocation properties: buffered functions must automatically
|
|
//--- resize array which is too small,but leave unchanged array which is
|
|
//--- too large.
|
|
if(n>=2)
|
|
{
|
|
//--- First step: array is too small,two elements are required
|
|
k=2;
|
|
kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,k,true);
|
|
kxy=CNearestNeighbor::KDTreeQueryKNN(treexy,ptx,k,true);
|
|
kt=CNearestNeighbor::KDTreeQueryKNN(treext,ptx,k,true);
|
|
//--- check
|
|
if((kx!=k || kxy!=k) || kt!=k)
|
|
{
|
|
kdterrors=true;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
qx.Resize(1,1);
|
|
qxy.Resize(1,1);
|
|
ArrayResize(qtags,1);
|
|
ArrayResize(qr,1);
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsX(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
|
|
//--- Second step: array is one row larger than needed,so only first
|
|
//--- row is overwritten. Test it.
|
|
k=1;
|
|
kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,k,true);
|
|
kxy=CNearestNeighbor::KDTreeQueryKNN(treexy,ptx,k,true);
|
|
kt=CNearestNeighbor::KDTreeQueryKNN(treext,ptx,k,true);
|
|
//--- check
|
|
if((kx!=k || kxy!=k) || kt!=k)
|
|
{
|
|
kdterrors=true;
|
|
return;
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=nx-1;i++)
|
|
qx[1].Set(i,CInfOrNaN::NaN());
|
|
for(i=0;i<=nx+ny-1;i++)
|
|
qxy[1].Set(i,CInfOrNaN::NaN());
|
|
qtags[1]=999;
|
|
qr[1]=CInfOrNaN::NaN();
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsX(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
|
|
for(i=0;i<=nx-1;i++)
|
|
{
|
|
//--- search errors
|
|
kdterrors=kdterrors || !CInfOrNaN::IsNaN(qx[1][i]);
|
|
}
|
|
for(i=0;i<=nx+ny-1;i++)
|
|
{
|
|
//--- search errors
|
|
kdterrors=kdterrors || !CInfOrNaN::IsNaN(qxy[1][i]);
|
|
}
|
|
//--- search errors
|
|
kdterrors=kdterrors || !(qtags[1]==999);
|
|
kdterrors=kdterrors || !CInfOrNaN::IsNaN(qr[1]);
|
|
}
|
|
//--- Test reallocation properties: 'interactive' functions must allocate
|
|
//--- new array on each call.
|
|
if(n>=2)
|
|
{
|
|
//--- On input array is either too small or too large
|
|
for(k=1;k<=2;k++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(k==1 || k==2,"KNN: internal error (unexpected K)!"))
|
|
return;
|
|
//--- change values
|
|
kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,k,true);
|
|
kxy=CNearestNeighbor::KDTreeQueryKNN(treexy,ptx,k,true);
|
|
kt=CNearestNeighbor::KDTreeQueryKNN(treext,ptx,k,true);
|
|
//--- check
|
|
if((kx!=k || kxy!=k) || kt!=k)
|
|
{
|
|
kdterrors=true;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
qx.Resize(3-k,3-k);
|
|
qxy.Resize(3-k,3-k);
|
|
ArrayResize(qtags,3-k);
|
|
ArrayResize(qr,3-k);
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsXI(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXYI(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTagsI(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistancesI(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
|
|
kdterrors=(kdterrors || CAp::Rows(qx)!=k) || CAp::Cols(qx)!=nx;
|
|
kdterrors=(kdterrors || CAp::Rows(qxy)!=k) || CAp::Cols(qxy)!=nx+ny;
|
|
kdterrors=kdterrors || CAp::Len(qtags)!=k;
|
|
kdterrors=kdterrors || CAp::Len(qr)!=k;
|
|
}
|
|
}
|
|
}
|
|
//--- test general approximate K-NN queries (with self-matches):
|
|
//--- * compare results from different trees (must be equal) and
|
|
//--- check that correct (value,tag) pairs are returned
|
|
//--- * test results from XT tree - let R be radius of query result.
|
|
//--- then all points not in result must be not closer than R/(1+Eps).
|
|
for(q=1;q<=qcount;q++)
|
|
{
|
|
//--- Select K: 1..N
|
|
if(CMath::RandomReal()>0.5)
|
|
k=1+CMath::RandomInteger(n);
|
|
else
|
|
k=1;
|
|
//--- Select Eps
|
|
eps=0.5+CMath::RandomReal();
|
|
//--- Select point (either one of the points,or random)
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
i=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
ptx[i_]=xy[i][i_];
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=nx-1;i++)
|
|
ptx[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- Test:
|
|
//--- * consistency of results from different queries
|
|
//--- * points in query are IN the R-sphere (or at the boundary),
|
|
//--- and points not in query are outside of the R-sphere (or at the boundary)
|
|
//--- * distances are correct and are ordered
|
|
kx=CNearestNeighbor::KDTreeQueryAKNN(treex,ptx,k,true,eps);
|
|
kxy=CNearestNeighbor::KDTreeQueryAKNN(treexy,ptx,k,true,eps);
|
|
kt=CNearestNeighbor::KDTreeQueryAKNN(treext,ptx,k,true,eps);
|
|
//--- check
|
|
if((kx!=k || kxy!=k) || kt!=k)
|
|
{
|
|
kdterrors=true;
|
|
return;
|
|
}
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsXI(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXYI(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTagsI(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistancesI(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsX(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,k,nx,ny);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
tmpb[i]=true;
|
|
r=0;
|
|
//--- calculation
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
tmpb[qtags[i]]=false;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
tmpx[i_]=ptx[i_];
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
tmpx[i_]=tmpx[i_]-qx[i][i_];
|
|
r=MathMax(r,VNorm(tmpx,nx,normtype));
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(tmpb[i])
|
|
{
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
tmpx[i_]=ptx[i_];
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
tmpx[i_]=tmpx[i_]-xy[i][i_];
|
|
//--- search errors
|
|
kdterrors=kdterrors || VNorm(tmpx,nx,normtype)<r*(1-errtol)/(1+eps);
|
|
}
|
|
}
|
|
for(i=0;i<=k-2;i++)
|
|
{
|
|
//--- search errors
|
|
kdterrors=kdterrors || qr[i]>qr[i+1];
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
tmpx[i_]=ptx[i_];
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
tmpx[i_]=tmpx[i_]-xy[qtags[i]][i_];
|
|
//--- search errors
|
|
kdterrors=kdterrors || MathAbs(VNorm(tmpx,nx,normtype)-qr[i])>errtol;
|
|
}
|
|
}
|
|
//--- test general R-NN queries (with self-matches):
|
|
//--- * compare results from different trees (must be equal) and
|
|
//--- check that correct (value,tag) pairs are returned
|
|
//--- * test results from XT tree - let R be radius of query result.
|
|
//--- then all points not in result must be not closer than R.
|
|
for(q=1;q<=qcount;q++)
|
|
{
|
|
//--- Select R
|
|
if(CMath::RandomReal()>0.3)
|
|
r=MathMax(CMath::RandomReal(),CMath::m_machineepsilon);
|
|
else
|
|
r=CMath::m_machineepsilon;
|
|
//--- Select point (either one of the points,or random)
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
i=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
ptx[i_]=xy[i][i_];
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=nx-1;i++)
|
|
ptx[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- Test:
|
|
//--- * consistency of results from different queries
|
|
//--- * points in query are IN the R-sphere (or at the boundary),
|
|
//--- and points not in query are outside of the R-sphere (or at the boundary)
|
|
//--- * distances are correct and are ordered
|
|
kx=CNearestNeighbor::KDTreeQueryRNN(treex,ptx,r,true);
|
|
kxy=CNearestNeighbor::KDTreeQueryRNN(treexy,ptx,r,true);
|
|
kt=CNearestNeighbor::KDTreeQueryRNN(treext,ptx,r,true);
|
|
//--- check
|
|
if(kxy!=kx || kt!=kx)
|
|
{
|
|
kdterrors=true;
|
|
return;
|
|
}
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsXI(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXYI(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTagsI(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistancesI(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,kx,nx,ny);
|
|
//--- function calls
|
|
CNearestNeighbor::KDTreeQueryResultsX(treex,qx);
|
|
CNearestNeighbor::KDTreeQueryResultsXY(treexy,qxy);
|
|
CNearestNeighbor::KDTreeQueryResultsTags(treext,qtags);
|
|
CNearestNeighbor::KDTreeQueryResultsDistances(treext,qr);
|
|
//--- search errors
|
|
kdterrors=kdterrors || KDTResultsDifferent(xy,n,qx,qxy,qtags,kx,nx,ny);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
tmpb[i]=true;
|
|
for(i=0;i<=kx-1;i++)
|
|
tmpb[qtags[i]]=false;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
tmpx[i_]=ptx[i_];
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
tmpx[i_]=tmpx[i_]-xy[i][i_];
|
|
//--- check
|
|
if(tmpb[i])
|
|
{
|
|
//--- search errors
|
|
kdterrors=kdterrors || VNorm(tmpx,nx,normtype)<r*(1-errtol);
|
|
}
|
|
else
|
|
{
|
|
//--- search errors
|
|
kdterrors=kdterrors || VNorm(tmpx,nx,normtype)>r*(1+errtol);
|
|
}
|
|
}
|
|
for(i=0;i<=kx-2;i++)
|
|
{
|
|
//--- search errors
|
|
kdterrors=kdterrors || qr[i]>qr[i+1];
|
|
}
|
|
}
|
|
//--- Test self-matching:
|
|
//--- * self-match - nearest neighbor of each point in XY is the point itself
|
|
//--- * no self-match - nearest neighbor is NOT the point itself
|
|
if(n>1)
|
|
{
|
|
//--- test for N=1 have non-general form,but it is not really needed
|
|
for(task=0;task<=1;task++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
ptx[i_]=xy[i][i_];
|
|
//--- function calls
|
|
kx=CNearestNeighbor::KDTreeQueryKNN(treex,ptx,1,task==0);
|
|
CNearestNeighbor::KDTreeQueryResultsXI(treex,qx);
|
|
//--- check
|
|
if(kx!=1)
|
|
{
|
|
kdterrors=true;
|
|
return;
|
|
}
|
|
//--- change value
|
|
isequal=true;
|
|
for(j=0;j<=nx-1;j++)
|
|
isequal=isequal && qx[0][j]==ptx[j];
|
|
//--- check
|
|
if(task==0)
|
|
kdterrors=kdterrors || !isequal;
|
|
else
|
|
kdterrors=kdterrors || isequal;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing serialization of KD trees |
|
|
//| This function sets Err to True on errors, but leaves it unchanged|
|
|
//| on success |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestNearestNeighborUnit::TestKDTreeSerialization(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int nx=0;
|
|
int ny=0;
|
|
int normtype=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int q=0;
|
|
double threshold=0;
|
|
int k0=0;
|
|
int k1=0;
|
|
//--- objects of classes
|
|
CKDTree tree0;
|
|
CKDTree tree1;
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
CMatrixDouble xy0;
|
|
CMatrixDouble xy1;
|
|
//--- create arrays
|
|
double x[];
|
|
int tags[];
|
|
int qsizes[];
|
|
int tags0[];
|
|
int tags1[];
|
|
int i_=0;
|
|
//--- initialization
|
|
threshold=100*CMath::m_machineepsilon;
|
|
//--- different N,NX,NY,NormType
|
|
n=1;
|
|
while(n<=51)
|
|
{
|
|
//--- prepare array with query sizes
|
|
ArrayResize(qsizes,4);
|
|
qsizes[0]=1;
|
|
qsizes[1]=(int)(MathMin(2,n));
|
|
qsizes[2]=(int)(MathMin(4,n));
|
|
qsizes[3]=n;
|
|
//--- different NX/NY/NormType
|
|
for(nx=1;nx<=2;nx++)
|
|
{
|
|
for(ny=0;ny<=2;ny++)
|
|
{
|
|
for(normtype=0;normtype<=2;normtype++)
|
|
{
|
|
//--- Prepare data
|
|
xy.Resize(n,nx+ny);
|
|
ArrayResize(tags,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=nx+ny-1;j++)
|
|
xy[i].Set(j,CMath::RandomReal());
|
|
tags[i]=CMath::RandomInteger(100);
|
|
}
|
|
//--- Build tree,pass it through serializer
|
|
CNearestNeighbor::KDTreeBuildTagged(xy,tags,n,nx,ny,normtype,tree0);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CNearestNeighbor::KDTreeAlloc(_local_serializer,tree0);
|
|
_local_serializer.SStart_Str();
|
|
CNearestNeighbor::KDTreeSerialize(_local_serializer,tree0);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CNearestNeighbor::KDTreeUnserialize(_local_serializer,tree1);
|
|
_local_serializer.Stop();
|
|
}
|
|
//--- For each point of XY we make queries with different sizes
|
|
ArrayResize(x,nx);
|
|
for(k=0;k<=n-1;k++)
|
|
{
|
|
for(q=0;q<=CAp::Len(qsizes)-1;q++)
|
|
{
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=xy[k][i_];
|
|
//--- change values
|
|
k0=CNearestNeighbor::KDTreeQueryKNN(tree0,x,qsizes[q],true);
|
|
k1=CNearestNeighbor::KDTreeQueryKNN(tree1,x,qsizes[q],true);
|
|
//--- check
|
|
if(k0!=k1)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsXY(tree0,xy0);
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsXY(tree1,xy1);
|
|
for(i=0;i<=k0-1;i++)
|
|
{
|
|
for(j=0;j<=nx+ny-1;j++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(xy0[i][j]-xy1[i][j])>threshold)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsTags(tree0,tags0);
|
|
//--- function call
|
|
CNearestNeighbor::KDTreeQueryResultsTags(tree1,tags1);
|
|
for(i=0;i<=k0-1;i++)
|
|
{
|
|
//--- check
|
|
if(tags0[i]!=tags1[i])
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Next N
|
|
n=n+25;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CAblas |
|
|
//+------------------------------------------------------------------+
|
|
class CTestAblasUnit
|
|
{
|
|
public:
|
|
CTestAblasUnit(void);
|
|
~CTestAblasUnit(void);
|
|
static bool TestAblas(const bool silent);
|
|
|
|
private:
|
|
static void NaiveMatrixMatrixMultiply(CMatrixDouble &a,const int ai1,const int ai2,const int aj1,const int aj2,const bool transa,CMatrixDouble &b,const int bi1,const int bi2,const int bj1,const int bj2,const bool transb,const double alpha,CMatrixDouble &c,const int ci1,const int ci2,const int cj1,const int cj2,const double beta);
|
|
static bool TestTrsM(const int minn,const int maxn);
|
|
static bool TestSyrk(const int minn,const int maxn);
|
|
static bool TestGemm(const int minn,const int maxn);
|
|
static bool TestTrans(const int minn,const int maxn);
|
|
static bool TestRank1(const int minn,const int maxn);
|
|
static bool TestMV(const int minn,const int maxn);
|
|
static bool TestCopy(const int minn,const int maxn);
|
|
static void RefCMatrixRightTrsM(const int m,const int n,CMatrixComplex &a,const int i1,const int j1,const bool isupper,const bool isunit,const int optype,CMatrixComplex &x,const int i2,const int j2);
|
|
static void RefCMatrixLeftTrsM(const int m,const int n,CMatrixComplex &a,const int i1,const int j1,const bool isupper,const bool isunit,const int optype,CMatrixComplex &x,const int i2,const int j2);
|
|
static void RefRMatrixRightTrsM(const int m,const int n,CMatrixDouble &a,const int i1,const int j1,const bool isupper,const bool isunit,const int optype,CMatrixDouble &x,const int i2,const int j2);
|
|
static void RefRMatrixLeftTrsM(const int m,const int n,CMatrixDouble &a,const int i1,const int j1,const bool isupper,const bool isunit,const int optype,CMatrixDouble &x,const int i2,const int j2);
|
|
static bool InternalCMatrixTrInverse(CMatrixComplex &a,const int n,const bool isupper,const bool isunittriangular);
|
|
static bool InternalRMatrixTrInverse(CMatrixDouble &a,const int n,const bool isupper,const bool isunittriangular);
|
|
static void RefCMatrixSyrk(const int n,const int k,const double alpha,CMatrixComplex &a,const int ia,const int ja,const int optypea,const double beta,CMatrixComplex &c,const int ic,const int jc,const bool isupper);
|
|
static void RefRMatrixSyrk(const int n,const int k,const double alpha,CMatrixDouble &a,const int ia,const int ja,const int optypea,const double beta,CMatrixDouble &c,const int ic,const int jc,const bool isupper);
|
|
static void RefCMatrixGemm(const int m,const int n,const int k,complex &alpha,CMatrixComplex &a,const int ia,const int ja,const int optypea,CMatrixComplex &b,const int ib,const int jb,const int optypeb,complex &beta,CMatrixComplex &c,const int ic,const int jc);
|
|
static void RefRMatrixGemm(const int m,const int n,const int k,const double alpha,CMatrixDouble &a,const int ia,const int ja,const int optypea,CMatrixDouble &b,const int ib,const int jb,const int optypeb,const double beta,CMatrixDouble &c,const int ic,const int jc);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestAblasUnit::CTestAblasUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestAblasUnit::~CTestAblasUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CAblas |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestAblasUnit::TestAblas(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double threshold=0;
|
|
bool trsmerrors;
|
|
bool syrkerrors;
|
|
bool gemmerrors;
|
|
bool transerrors;
|
|
bool rank1errors;
|
|
bool mverrors;
|
|
bool copyerrors;
|
|
bool waserrors;
|
|
//--- create array
|
|
CMatrixDouble ra;
|
|
//--- initialization
|
|
trsmerrors=false;
|
|
syrkerrors=false;
|
|
gemmerrors=false;
|
|
transerrors=false;
|
|
rank1errors=false;
|
|
mverrors=false;
|
|
copyerrors=false;
|
|
waserrors=false;
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
//--- search errors
|
|
trsmerrors=trsmerrors || TestTrsM(1,3*CAblas::AblasBlockSize()+1);
|
|
syrkerrors=syrkerrors || TestSyrk(1,3*CAblas::AblasBlockSize()+1);
|
|
gemmerrors=gemmerrors || TestGemm(1,3*CAblas::AblasBlockSize()+1);
|
|
transerrors=transerrors || TestTrans(1,3*CAblas::AblasBlockSize()+1);
|
|
rank1errors=rank1errors || TestRank1(1,3*CAblas::AblasBlockSize()+1);
|
|
mverrors=mverrors || TestMV(1,3*CAblas::AblasBlockSize()+1);
|
|
copyerrors=copyerrors || TestCopy(1,3*CAblas::AblasBlockSize()+1);
|
|
//--- report
|
|
waserrors=(((((trsmerrors || syrkerrors) || gemmerrors) || transerrors) || rank1errors) || mverrors) || copyerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING ABLAS");
|
|
Print("* TRSM: ");
|
|
//--- check
|
|
if(trsmerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* SYRK: ");
|
|
//--- check
|
|
if(syrkerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* GEMM: ");
|
|
//--- check
|
|
if(gemmerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* TRANS: ");
|
|
//--- check
|
|
if(transerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* RANK1: ");
|
|
//--- check
|
|
if(rank1errors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* MV: ");
|
|
//--- check
|
|
if(mverrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* COPY: ");
|
|
//--- check
|
|
if(copyerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestAblasUnit::NaiveMatrixMatrixMultiply(CMatrixDouble &a,
|
|
const int ai1,
|
|
const int ai2,
|
|
const int aj1,
|
|
const int aj2,
|
|
const bool transa,
|
|
CMatrixDouble &b,
|
|
const int bi1,
|
|
const int bi2,
|
|
const int bj1,
|
|
const int bj2,
|
|
const bool transb,
|
|
const double alpha,
|
|
CMatrixDouble &c,
|
|
const int ci1,
|
|
const int ci2,
|
|
const int cj1,
|
|
const int cj2,
|
|
const double beta)
|
|
{
|
|
//--- create variables
|
|
int arows=0;
|
|
int acols=0;
|
|
int brows=0;
|
|
int bcols=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int l=0;
|
|
int r=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double x1[];
|
|
double x2[];
|
|
//--- Setup
|
|
if(!transa)
|
|
{
|
|
arows=ai2-ai1+1;
|
|
acols=aj2-aj1+1;
|
|
}
|
|
else
|
|
{
|
|
arows=aj2-aj1+1;
|
|
acols=ai2-ai1+1;
|
|
}
|
|
//--- check
|
|
if(!transb)
|
|
{
|
|
brows=bi2-bi1+1;
|
|
bcols=bj2-bj1+1;
|
|
}
|
|
else
|
|
{
|
|
brows=bj2-bj1+1;
|
|
bcols=bi2-bi1+1;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(acols==brows,"NaiveMatrixMatrixMultiply: incorrect matrix sizes!"))
|
|
return;
|
|
//--- check
|
|
if(((arows<=0 || acols<=0) || brows<=0) || bcols<=0)
|
|
return;
|
|
//--- change values
|
|
l=arows;
|
|
r=bcols;
|
|
k=acols;
|
|
//--- allocation
|
|
ArrayResize(x1,k+1);
|
|
ArrayResize(x2,k+1);
|
|
//--- calculation
|
|
for(i=1;i<=l;i++)
|
|
{
|
|
for(j=1;j<=r;j++)
|
|
{
|
|
//--- check
|
|
if(!transa)
|
|
{
|
|
//--- check
|
|
if(!transb)
|
|
{
|
|
//--- change values
|
|
i1_=aj1-bi1;
|
|
v=0.0;
|
|
for(i_=bi1;i_<=bi2;i_++)
|
|
v+=b[i_][bj1+j-1]*a[ai1+i-1][i_+i1_];
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
i1_=aj1-bj1;
|
|
v=0.0;
|
|
for(i_=bj1;i_<=bj2;i_++)
|
|
v+=b[bi1+j-1][i_]*a[ai1+i-1][i_+i1_];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(!transb)
|
|
{
|
|
//--- change values
|
|
i1_=ai1-bi1;
|
|
v=0.0;
|
|
for(i_=bi1;i_<=bi2;i_++)
|
|
v+=b[i_][bj1+j-1]*a[i_+i1_][aj1+i-1];
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
i1_=ai1-bj1;
|
|
v=0.0;
|
|
for(i_=bj1;i_<=bj2;i_++)
|
|
v+=b[bi1+j-1][i_]*a[i_+i1_][aj1+i-1];
|
|
}
|
|
}
|
|
//--- check
|
|
if(beta==0.0)
|
|
c[ci1+i-1].Set(cj1+j-1,alpha*v);
|
|
else
|
|
c[ci1+i-1].Set(cj1+j-1,beta*c[ci1+i-1][cj1+j-1]+alpha*v);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| ?Matrix????TRSM tests |
|
|
//| Returns False for passed test,True - for failed |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestAblasUnit::TestTrsM(const int minn,const int maxn)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int m=0;
|
|
int mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int optype=0;
|
|
int uppertype=0;
|
|
int unittype=0;
|
|
int xoffsi=0;
|
|
int xoffsj=0;
|
|
int aoffsitype=0;
|
|
int aoffsjtype=0;
|
|
int aoffsi=0;
|
|
int aoffsj=0;
|
|
double threshold=0;
|
|
//--- create matrix
|
|
CMatrixDouble refra;
|
|
CMatrixDouble refrxl;
|
|
CMatrixDouble refrxr;
|
|
CMatrixComplex refca;
|
|
CMatrixComplex refcxl;
|
|
CMatrixComplex refcxr;
|
|
CMatrixDouble ra;
|
|
CMatrixComplex ca;
|
|
CMatrixDouble rxr1;
|
|
CMatrixDouble rxl1;
|
|
CMatrixComplex cxr1;
|
|
CMatrixComplex cxl1;
|
|
CMatrixDouble rxr2;
|
|
CMatrixDouble rxl2;
|
|
CMatrixComplex cxr2;
|
|
CMatrixComplex cxl2;
|
|
//--- initialization
|
|
threshold=CMath::Sqr(maxn)*100*CMath::m_machineepsilon;
|
|
result=false;
|
|
//--- calculation
|
|
for(mx=minn;mx<=maxn;mx++)
|
|
{
|
|
//--- Select random M/N in [1,MX] such that max(M,N)=MX
|
|
m=1+CMath::RandomInteger(mx);
|
|
n=1+CMath::RandomInteger(mx);
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
m=mx;
|
|
else
|
|
n=mx;
|
|
//--- Initialize RefRA/RefCA by random matrices whose upper
|
|
//--- and lower triangle submatrices are non-degenerate
|
|
//--- well-conditioned matrices.
|
|
//--- Matrix size is 2Mx2M (four copies of same MxM matrix
|
|
//--- to test different offsets)
|
|
refra.Resize(2*m,2*m);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
refra[i].Set(j,0.2*CMath::RandomReal()-0.1);
|
|
}
|
|
for(i=0;i<=m-1;i++)
|
|
refra[i].Set(i,(2*CMath::RandomInteger(1)-1)*(2*m+CMath::RandomReal()));
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
refra[i+m].Set(j,refra[i][j]);
|
|
refra[i].Set(j+m,refra[i][j]);
|
|
refra[i+m].Set(j+m,refra[i][j]);
|
|
}
|
|
}
|
|
//--- allocation
|
|
refca.Resize(2*m,2*m);
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
refca[i].SetRe(j,0.2*CMath::RandomReal()-0.1);
|
|
refca[i].SetIm(j,0.2*CMath::RandomReal()-0.1);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
refca[i].SetRe(i,(2*CMath::RandomInteger(2)-1)*(2*m+CMath::RandomReal()));
|
|
refca[i].SetIm(i,(2*CMath::RandomInteger(2)-1)*(2*m+CMath::RandomReal()));
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
refca[i+m].Set(j,refca[i][j]);
|
|
refca[i].Set(j+m,refca[i][j]);
|
|
refca[i+m].Set(j+m,refca[i][j]);
|
|
}
|
|
}
|
|
//--- Generate random XL/XR.
|
|
//--- XR is NxM matrix (matrix for 'Right' subroutines)
|
|
//--- XL is MxN matrix (matrix for 'Left' subroutines)
|
|
refrxr.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
refrxr[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- allocation
|
|
refrxl.Resize(m,n);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
refrxl[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- allocation
|
|
refcxr.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
refcxr[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
refcxr[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- allocation
|
|
refcxl.Resize(m,n);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
refcxl[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
refcxl[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- test different types of operations,offsets,and so on...
|
|
//--- to avoid unnecessary slowdown we don't test ALL possible
|
|
//--- combinations of operation types. We just generate one random
|
|
//--- set of parameters and test it.
|
|
ra.Resize(2*m,2*m);
|
|
rxr1.Resize(n,m);
|
|
rxr2.Resize(n,m);
|
|
rxl1.Resize(m,n);
|
|
rxl2.Resize(m,n);
|
|
ca.Resize(2*m,2*m);
|
|
cxr1.Resize(n,m);
|
|
cxr2.Resize(n,m);
|
|
cxl1.Resize(m,n);
|
|
cxl2.Resize(m,n);
|
|
//--- initialization
|
|
optype=CMath::RandomInteger(3);
|
|
uppertype=CMath::RandomInteger(2);
|
|
unittype=CMath::RandomInteger(2);
|
|
xoffsi=CMath::RandomInteger(2);
|
|
xoffsj=CMath::RandomInteger(2);
|
|
aoffsitype=CMath::RandomInteger(2);
|
|
aoffsjtype=CMath::RandomInteger(2);
|
|
aoffsi=m*aoffsitype;
|
|
aoffsj=m*aoffsjtype;
|
|
//--- copy A,XR,XL (fill unused parts with random garbage)
|
|
for(i=0;i<=2*m-1;i++)
|
|
{
|
|
for(j=0;j<=2*m-1;j++)
|
|
{
|
|
//--- check
|
|
if(((i>=aoffsi && i<aoffsi+m) && j>=aoffsj) && j<aoffsj+m)
|
|
{
|
|
ca[i].Set(j,refca[i][j]);
|
|
ra[i].Set(j,refra[i][j]);
|
|
}
|
|
else
|
|
{
|
|
ca[i].Set(j,CMath::RandomReal());
|
|
ra[i].Set(j,CMath::RandomReal());
|
|
}
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- check
|
|
if(i>=xoffsi && j>=xoffsj)
|
|
{
|
|
cxr1[i].Set(j,refcxr[i][j]);
|
|
cxr2[i].Set(j,refcxr[i][j]);
|
|
rxr1[i].Set(j,refrxr[i][j]);
|
|
rxr2[i].Set(j,refrxr[i][j]);
|
|
}
|
|
else
|
|
{
|
|
cxr1[i].Set(j,CMath::RandomReal());
|
|
cxr2[i].Set(j,cxr1[i][j]);
|
|
rxr1[i].Set(j,CMath::RandomReal());
|
|
rxr2[i].Set(j,rxr1[i][j]);
|
|
}
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(i>=xoffsi && j>=xoffsj)
|
|
{
|
|
cxl1[i].Set(j,refcxl[i][j]);
|
|
cxl2[i].Set(j,refcxl[i][j]);
|
|
rxl1[i].Set(j,refrxl[i][j]);
|
|
rxl2[i].Set(j,refrxl[i][j]);
|
|
}
|
|
else
|
|
{
|
|
cxl1[i].Set(j,CMath::RandomReal());
|
|
cxl2[i].Set(j,cxl1[i][j]);
|
|
rxl1[i].Set(j,CMath::RandomReal());
|
|
rxl2[i].Set(j,rxl1[i][j]);
|
|
}
|
|
}
|
|
}
|
|
//--- Test CXR
|
|
CAblas::CMatrixRightTrsM(n-xoffsi,m-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxr1,xoffsi,xoffsj);
|
|
RefCMatrixRightTrsM(n-xoffsi,m-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxr2,xoffsi,xoffsj);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
result=result || CMath::AbsComplex(cxr1[i][j]-cxr2[i][j])>threshold;
|
|
}
|
|
//--- Test CXL
|
|
CAblas::CMatrixLeftTrsM(m-xoffsi,n-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxl1,xoffsi,xoffsj);
|
|
RefCMatrixLeftTrsM(m-xoffsi,n-xoffsj,ca,aoffsi,aoffsj,uppertype==0,unittype==0,optype,cxl2,xoffsi,xoffsj);
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
result=result || CMath::AbsComplex(cxl1[i][j]-cxl2[i][j])>threshold;
|
|
}
|
|
//--- check
|
|
if(optype<2)
|
|
{
|
|
//--- Test RXR
|
|
CAblas::RMatrixRightTrsM(n-xoffsi,m-xoffsj,ra,aoffsi,aoffsj,uppertype==0,unittype==0,optype,rxr1,xoffsi,xoffsj);
|
|
RefRMatrixRightTrsM(n-xoffsi,m-xoffsj,ra,aoffsi,aoffsj,uppertype==0,unittype==0,optype,rxr2,xoffsi,xoffsj);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
result=result || MathAbs(rxr1[i][j]-rxr2[i][j])>threshold;
|
|
}
|
|
//--- Test RXL
|
|
CAblas::RMatrixLeftTrsM(m-xoffsi,n-xoffsj,ra,aoffsi,aoffsj,uppertype==0,unittype==0,optype,rxl1,xoffsi,xoffsj);
|
|
RefRMatrixLeftTrsM(m-xoffsi,n-xoffsj,ra,aoffsi,aoffsj,uppertype==0,unittype==0,optype,rxl2,xoffsi,xoffsj);
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
result=result || MathAbs(rxl1[i][j]-rxl2[i][j])>threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| SYRK tests |
|
|
//| Returns False for passed test,True - for failed |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestAblasUnit::TestSyrk(const int minn,const int maxn)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int k=0;
|
|
int mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int uppertype=0;
|
|
int xoffsi=0;
|
|
int xoffsj=0;
|
|
int aoffsitype=0;
|
|
int aoffsjtype=0;
|
|
int aoffsi=0;
|
|
int aoffsj=0;
|
|
int alphatype=0;
|
|
int betatype=0;
|
|
double alpha=0;
|
|
double beta=0;
|
|
double threshold=0;
|
|
//--- create matrix
|
|
CMatrixDouble refra;
|
|
CMatrixDouble refrc;
|
|
CMatrixComplex refca;
|
|
CMatrixComplex refcc;
|
|
CMatrixDouble ra1;
|
|
CMatrixDouble ra2;
|
|
CMatrixComplex ca1;
|
|
CMatrixComplex ca2;
|
|
CMatrixDouble rc;
|
|
CMatrixDouble rct;
|
|
CMatrixComplex cc;
|
|
CMatrixComplex cct;
|
|
//--- initialization
|
|
threshold=maxn*100*CMath::m_machineepsilon;
|
|
result=false;
|
|
for(mx=minn;mx<=maxn;mx++)
|
|
{
|
|
//--- Select random M/N in [1,MX] such that max(M,N)=MX
|
|
k=1+CMath::RandomInteger(mx);
|
|
n=1+CMath::RandomInteger(mx);
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
k=mx;
|
|
else
|
|
n=mx;
|
|
//--- Initialize RefRA/RefCA by random Hermitian matrices,
|
|
//--- RefRC/RefCC by random matrices
|
|
//--- RA/CA size is 2Nx2N (four copies of same NxN matrix
|
|
//--- to test different offsets)
|
|
refra.Resize(2*n,2*n);
|
|
refca.Resize(2*n,2*n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
refra[i].Set(i,2*CMath::RandomReal()-1);
|
|
refca[i].Set(i,2*CMath::RandomReal()-1);
|
|
for(j=i+1;j<=n-1;j++)
|
|
{
|
|
refra[i].Set(j,2*CMath::RandomReal()-1);
|
|
refca[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
refca[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
refra[j].Set(i,refra[i][j]);
|
|
refca[j].Set(i,CMath::Conj(refca[i][j]));
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
refra[i+n].Set(j,refra[i][j]);
|
|
refra[i].Set(j+n,refra[i][j]);
|
|
refra[i+n].Set(j+n,refra[i][j]);
|
|
refca[i+n].Set(j,refca[i][j]);
|
|
refca[i].Set(j+n,refca[i][j]);
|
|
refca[i+n].Set(j+n,refca[i][j]);
|
|
}
|
|
}
|
|
//--- allocation
|
|
refrc.Resize(n,k);
|
|
refcc.Resize(n,k);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=k-1;j++)
|
|
{
|
|
refrc[i].Set(j,2*CMath::RandomReal()-1);
|
|
refcc[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
refcc[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- test different types of operations,offsets,and so on...
|
|
//--- to avoid unnecessary slowdown we don't test ALL possible
|
|
//--- combinations of operation types. We just generate one random
|
|
//--- set of parameters and test it.
|
|
ra1.Resize(2*n,2*n);
|
|
ra2.Resize(2*n,2*n);
|
|
ca1.Resize(2*n,2*n);
|
|
ca2.Resize(2*n,2*n);
|
|
rc.Resize(n,k);
|
|
rct.Resize(k,n);
|
|
cc.Resize(n,k);
|
|
cct.Resize(k,n);
|
|
//--- initialization
|
|
uppertype=CMath::RandomInteger(2);
|
|
xoffsi=CMath::RandomInteger(2);
|
|
xoffsj=CMath::RandomInteger(2);
|
|
aoffsitype=CMath::RandomInteger(2);
|
|
aoffsjtype=CMath::RandomInteger(2);
|
|
alphatype=CMath::RandomInteger(2);
|
|
betatype=CMath::RandomInteger(2);
|
|
aoffsi=n*aoffsitype;
|
|
aoffsj=n*aoffsjtype;
|
|
alpha=alphatype*(2*CMath::RandomReal()-1);
|
|
beta=betatype*(2*CMath::RandomReal()-1);
|
|
//--- copy A,C (fill unused parts with random garbage)
|
|
for(i=0;i<=2*n-1;i++)
|
|
{
|
|
for(j=0;j<=2*n-1;j++)
|
|
{
|
|
//--- check
|
|
if(((i>=aoffsi && i<aoffsi+n) && j>=aoffsj) && j<aoffsj+n)
|
|
{
|
|
ca1[i].Set(j,refca[i][j]);
|
|
ca2[i].Set(j,refca[i][j]);
|
|
ra1[i].Set(j,refra[i][j]);
|
|
ra2[i].Set(j,refra[i][j]);
|
|
}
|
|
else
|
|
{
|
|
ca1[i].Set(j,CMath::RandomReal());
|
|
ca2[i].Set(j,ca1[i][j]);
|
|
ra1[i].Set(j,CMath::RandomReal());
|
|
ra2[i].Set(j,ra1[i][j]);
|
|
}
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=k-1;j++)
|
|
{
|
|
//--- check
|
|
if(i>=xoffsi && j>=xoffsj)
|
|
{
|
|
rc[i].Set(j,refrc[i][j]);
|
|
rct[j].Set(i,refrc[i][j]);
|
|
cc[i].Set(j,refcc[i][j]);
|
|
cct[j].Set(i,refcc[i][j]);
|
|
}
|
|
else
|
|
{
|
|
rc[i].Set(j,CMath::RandomReal());
|
|
rct[j].Set(i,rc[i][j]);
|
|
cc[i].Set(j,CMath::RandomReal());
|
|
cct[j].Set(i,cct[j][i]);
|
|
}
|
|
}
|
|
}
|
|
//--- Test complex
|
|
//--- Only one of transform types is selected and tested
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
CAblas::CMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cc,xoffsi,xoffsj,0,beta,ca1,aoffsi,aoffsj,uppertype==0);
|
|
RefCMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cc,xoffsi,xoffsj,0,beta,ca2,aoffsi,aoffsj,uppertype==0);
|
|
}
|
|
else
|
|
{
|
|
CAblas::CMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cct,xoffsj,xoffsi,2,beta,ca1,aoffsi,aoffsj,uppertype==0);
|
|
RefCMatrixSyrk(n-xoffsi,k-xoffsj,alpha,cct,xoffsj,xoffsi,2,beta,ca2,aoffsi,aoffsj,uppertype==0);
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
result=result || CMath::AbsComplex(ca1[i][j]-ca2[i][j])>threshold;
|
|
}
|
|
//--- Test real
|
|
//--- Only one of transform types is selected and tested
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
CAblas::RMatrixSyrk(n-xoffsi,k-xoffsj,alpha,rc,xoffsi,xoffsj,0,beta,ra1,aoffsi,aoffsj,uppertype==0);
|
|
RefRMatrixSyrk(n-xoffsi,k-xoffsj,alpha,rc,xoffsi,xoffsj,0,beta,ra2,aoffsi,aoffsj,uppertype==0);
|
|
}
|
|
else
|
|
{
|
|
CAblas::RMatrixSyrk(n-xoffsi,k-xoffsj,alpha,rct,xoffsj,xoffsi,1,beta,ra1,aoffsi,aoffsj,uppertype==0);
|
|
RefRMatrixSyrk(n-xoffsi,k-xoffsj,alpha,rct,xoffsj,xoffsi,1,beta,ra2,aoffsi,aoffsj,uppertype==0);
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
result=result || MathAbs(ra1[i][j]-ra2[i][j])>threshold;
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| GEMM tests |
|
|
//| Returns False for passed test,True - for failed |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestAblasUnit::TestGemm(const int minn,const int maxn)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int m=0;
|
|
int n=0;
|
|
int k=0;
|
|
int mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int aoffsi=0;
|
|
int aoffsj=0;
|
|
int aoptype=0;
|
|
int aoptyper=0;
|
|
int boffsi=0;
|
|
int boffsj=0;
|
|
int boptype=0;
|
|
int boptyper=0;
|
|
int coffsi=0;
|
|
int coffsj=0;
|
|
double alphar=0;
|
|
double betar=0;
|
|
complex alphac=0;
|
|
complex betac=0;
|
|
double threshold=0;
|
|
//--- create matrix
|
|
CMatrixDouble refra;
|
|
CMatrixDouble refrb;
|
|
CMatrixDouble refrc;
|
|
CMatrixComplex refca;
|
|
CMatrixComplex refcb;
|
|
CMatrixComplex refcc;
|
|
CMatrixDouble rc1;
|
|
CMatrixDouble rc2;
|
|
CMatrixComplex cc1;
|
|
CMatrixComplex cc2;
|
|
//--- initialization
|
|
threshold=maxn*100*CMath::m_machineepsilon;
|
|
result=false;
|
|
//--- calculation
|
|
for(mx=minn;mx<=maxn;mx++)
|
|
{
|
|
//--- Select random M/N/K in [1,MX] such that max(M,N,K)=MX
|
|
m=1+CMath::RandomInteger(mx);
|
|
n=1+CMath::RandomInteger(mx);
|
|
k=1+CMath::RandomInteger(mx);
|
|
i=CMath::RandomInteger(3);
|
|
//--- check
|
|
if(i==0)
|
|
m=mx;
|
|
//--- check
|
|
if(i==1)
|
|
n=mx;
|
|
//--- check
|
|
if(i==2)
|
|
k=mx;
|
|
//--- Initialize A/B/C by random matrices with size (MaxN+1)*(MaxN+1)
|
|
refra.Resize(maxn+1,maxn+1);
|
|
refrb.Resize(maxn+1,maxn+1);
|
|
refrc.Resize(maxn+1,maxn+1);
|
|
refca.Resize(maxn+1,maxn+1);
|
|
refcb.Resize(maxn+1,maxn+1);
|
|
refcc.Resize(maxn+1,maxn+1);
|
|
//--- change values
|
|
for(i=0;i<=maxn;i++)
|
|
{
|
|
for(j=0;j<=maxn;j++)
|
|
{
|
|
refra[i].Set(j,2*CMath::RandomReal()-1);
|
|
refrb[i].Set(j,2*CMath::RandomReal()-1);
|
|
refrc[i].Set(j,2*CMath::RandomReal()-1);
|
|
refca[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
refca[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
refcb[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
refcb[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
refcc[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
refcc[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- test different types of operations,offsets,and so on...
|
|
//--- to avoid unnecessary slowdown we don't test ALL possible
|
|
//--- combinations of operation types. We just generate one random
|
|
//--- set of parameters and test it.
|
|
rc1.Resize(maxn+1,maxn+1);
|
|
rc2.Resize(maxn+1,maxn+1);
|
|
cc1.Resize(maxn+1,maxn+1);
|
|
cc2.Resize(maxn+1,maxn+1);
|
|
//--- initialization
|
|
aoffsi=CMath::RandomInteger(2);
|
|
aoffsj=CMath::RandomInteger(2);
|
|
aoptype=CMath::RandomInteger(3);
|
|
aoptyper=CMath::RandomInteger(2);
|
|
boffsi=CMath::RandomInteger(2);
|
|
boffsj=CMath::RandomInteger(2);
|
|
boptype=CMath::RandomInteger(3);
|
|
boptyper=CMath::RandomInteger(2);
|
|
coffsi=CMath::RandomInteger(2);
|
|
coffsj=CMath::RandomInteger(2);
|
|
alphar=CMath::RandomInteger(2)*(2*CMath::RandomReal()-1);
|
|
betar=CMath::RandomInteger(2)*(2*CMath::RandomReal()-1);
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
alphac.re=2*CMath::RandomReal()-1;
|
|
alphac.im=2*CMath::RandomReal()-1;
|
|
}
|
|
else
|
|
alphac=0;
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
betac.re=2*CMath::RandomReal()-1;
|
|
betac.im=2*CMath::RandomReal()-1;
|
|
}
|
|
else
|
|
betac=0;
|
|
//--- copy C
|
|
for(i=0;i<=maxn;i++)
|
|
{
|
|
for(j=0;j<=maxn;j++)
|
|
{
|
|
rc1[i].Set(j,refrc[i][j]);
|
|
rc2[i].Set(j,refrc[i][j]);
|
|
cc1[i].Set(j,refcc[i][j]);
|
|
cc2[i].Set(j,refcc[i][j]);
|
|
}
|
|
}
|
|
//--- Test complex
|
|
CAblas::CMatrixGemm(m,n,k,alphac,refca,aoffsi,aoffsj,aoptype,refcb,boffsi,boffsj,boptype,betac,cc1,coffsi,coffsj);
|
|
RefCMatrixGemm(m,n,k,alphac,refca,aoffsi,aoffsj,aoptype,refcb,boffsi,boffsj,boptype,betac,cc2,coffsi,coffsj);
|
|
//--- search errors
|
|
for(i=0;i<=maxn;i++)
|
|
{
|
|
for(j=0;j<=maxn;j++)
|
|
result=result || CMath::AbsComplex(cc1[i][j]-cc2[i][j])>threshold;
|
|
}
|
|
//--- Test real
|
|
CAblas::RMatrixGemm(m,n,k,alphar,refra,aoffsi,aoffsj,aoptyper,refrb,boffsi,boffsj,boptyper,betar,rc1,coffsi,coffsj);
|
|
RefRMatrixGemm(m,n,k,alphar,refra,aoffsi,aoffsj,aoptyper,refrb,boffsi,boffsj,boptyper,betar,rc2,coffsi,coffsj);
|
|
//--- search errors
|
|
for(i=0;i<=maxn;i++)
|
|
{
|
|
for(j=0;j<=maxn;j++)
|
|
result=result || MathAbs(rc1[i][j]-rc2[i][j])>threshold;
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| transpose tests |
|
|
//| Returns False for passed test,True - for failed |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestAblasUnit::TestTrans(const int minn,const int maxn)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int m=0;
|
|
int n=0;
|
|
int mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int aoffsi=0;
|
|
int aoffsj=0;
|
|
int boffsi=0;
|
|
int boffsj=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double threshold=0;
|
|
//--- create matrix
|
|
CMatrixDouble refra;
|
|
CMatrixDouble refrb;
|
|
CMatrixComplex refca;
|
|
CMatrixComplex refcb;
|
|
//--- initialization
|
|
result=false;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- calculation
|
|
for(mx=minn;mx<=maxn;mx++)
|
|
{
|
|
//--- Select random M/N in [1,MX] such that max(M,N)=MX
|
|
//--- Generate random V1 and V2 which are used to fill
|
|
//--- RefRB/RefCB with control values.
|
|
m=1+CMath::RandomInteger(mx);
|
|
n=1+CMath::RandomInteger(mx);
|
|
//--- check
|
|
if(CMath::RandomInteger(2)==0)
|
|
m=mx;
|
|
else
|
|
n=mx;
|
|
//--- change values
|
|
v1=CMath::RandomReal();
|
|
v2=CMath::RandomReal();
|
|
//--- Initialize A by random matrix with size (MaxN+1)*(MaxN+1)
|
|
//--- Fill B with control values
|
|
refra.Resize(maxn+1,maxn+1);
|
|
refrb.Resize(maxn+1,maxn+1);
|
|
refca.Resize(maxn+1,maxn+1);
|
|
refcb.Resize(maxn+1,maxn+1);
|
|
//--- change values
|
|
for(i=0;i<=maxn;i++)
|
|
{
|
|
for(j=0;j<=maxn;j++)
|
|
{
|
|
refra[i].Set(j,2*CMath::RandomReal()-1);
|
|
refca[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
refca[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
refrb[i].Set(j,i*v1+j*v2);
|
|
refcb[i].Set(j,i*v1+j*v2);
|
|
}
|
|
}
|
|
//--- test different offsets (zero or one)
|
|
//--- to avoid unnecessary slowdown we don't test ALL possible
|
|
//--- combinations of operation types. We just generate one random
|
|
//--- set of parameters and test it.
|
|
aoffsi=CMath::RandomInteger(2);
|
|
aoffsj=CMath::RandomInteger(2);
|
|
boffsi=CMath::RandomInteger(2);
|
|
boffsj=CMath::RandomInteger(2);
|
|
//--- function call
|
|
CAblas::RMatrixTranspose(m,n,refra,aoffsi,aoffsj,refrb,boffsi,boffsj);
|
|
//--- search errors
|
|
for(i=0;i<=maxn;i++)
|
|
{
|
|
for(j=0;j<=maxn;j++)
|
|
{
|
|
//--- check
|
|
if(((i<boffsi || i>=boffsi+n) || j<boffsj) || j>=boffsj+m)
|
|
result=result || MathAbs(refrb[i][j]-(v1*i+v2*j))>threshold;
|
|
else
|
|
result=result || MathAbs(refrb[i][j]-refra[aoffsi+j-boffsj][aoffsj+i-boffsi])>threshold;
|
|
}
|
|
}
|
|
//--- function call
|
|
CAblas::CMatrixTranspose(m,n,refca,aoffsi,aoffsj,refcb,boffsi,boffsj);
|
|
//--- search errors
|
|
for(i=0;i<=maxn;i++)
|
|
{
|
|
for(j=0;j<=maxn;j++)
|
|
{
|
|
//--- check
|
|
if(((i<boffsi || i>=boffsi+n) || j<boffsj) || j>=boffsj+m)
|
|
result=result || CMath::AbsComplex(refcb[i][j]-(v1*i+v2*j))>threshold;
|
|
else
|
|
result=result || CMath::AbsComplex(refcb[i][j]-refca[aoffsi+j-boffsj][aoffsj+i-boffsi])>threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| rank-1tests |
|
|
//| Returns False for passed test,True - for failed |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestAblasUnit::TestRank1(const int minn,const int maxn)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int m=0;
|
|
int n=0;
|
|
int mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int aoffsi=0;
|
|
int aoffsj=0;
|
|
int uoffs=0;
|
|
int voffs=0;
|
|
double threshold=0;
|
|
//--- create arrays
|
|
double ru[];
|
|
double rv[];
|
|
complex cu[];
|
|
complex cv[];
|
|
//--- create matrix
|
|
CMatrixDouble refra;
|
|
CMatrixDouble refrb;
|
|
CMatrixComplex refca;
|
|
CMatrixComplex refcb;
|
|
//--- initialization
|
|
result=false;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- calculation
|
|
for(mx=minn;mx<=maxn;mx++)
|
|
{
|
|
//--- Select random M/N in [1,MX] such that max(M,N)=MX
|
|
m=1+CMath::RandomInteger(mx);
|
|
n=1+CMath::RandomInteger(mx);
|
|
//--- check
|
|
if(CMath::RandomInteger(2)==0)
|
|
m=mx;
|
|
else
|
|
n=mx;
|
|
//--- Initialize A by random matrix with size (MaxN+1)*(MaxN+1)
|
|
//--- Fill B with control values
|
|
refra.Resize(maxn+maxn,maxn+maxn);
|
|
refrb.Resize(maxn+maxn,maxn+maxn);
|
|
refca.Resize(maxn+maxn,maxn+maxn);
|
|
refcb.Resize(maxn+maxn,maxn+maxn);
|
|
//--- change values
|
|
for(i=0;i<=2*maxn-1;i++)
|
|
{
|
|
for(j=0;j<=2*maxn-1;j++)
|
|
{
|
|
refra[i].Set(j,2*CMath::RandomReal()-1);
|
|
refca[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
refca[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
refrb[i].Set(j,refra[i][j]);
|
|
refcb[i].Set(j,refca[i][j]);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResize(ru,2*m);
|
|
ArrayResize(cu,2*m);
|
|
//--- change values
|
|
for(i=0;i<=2*m-1;i++)
|
|
{
|
|
ru[i]=2*CMath::RandomReal()-1;
|
|
cu[i].re=2*CMath::RandomReal()-1;
|
|
cu[i].im=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(rv,2*n);
|
|
ArrayResize(cv,2*n);
|
|
//--- change values
|
|
for(i=0;i<=2*n-1;i++)
|
|
{
|
|
rv[i]=2*CMath::RandomReal()-1;
|
|
cv[i].re=2*CMath::RandomReal()-1;
|
|
cv[i].im=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- test different offsets (zero or one)
|
|
//--- to avoid unnecessary slowdown we don't test ALL possible
|
|
//--- combinations of operation types. We just generate one random
|
|
//--- set of parameters and test it.
|
|
aoffsi=CMath::RandomInteger(maxn);
|
|
aoffsj=CMath::RandomInteger(maxn);
|
|
uoffs=CMath::RandomInteger(m);
|
|
voffs=CMath::RandomInteger(n);
|
|
//--- function call
|
|
CAblas::CMatrixRank1(m,n,refca,aoffsi,aoffsj,cu,uoffs,cv,voffs);
|
|
//--- search errors
|
|
for(i=0;i<=2*maxn-1;i++)
|
|
{
|
|
for(j=0;j<=2*maxn-1;j++)
|
|
{
|
|
//--- check
|
|
if(((i<aoffsi || i>=aoffsi+m) || j<aoffsj) || j>=aoffsj+n)
|
|
result=result || CMath::AbsComplex(refca[i][j]-refcb[i][j])>threshold;
|
|
else
|
|
result=result || CMath::AbsComplex(refca[i][j]-(refcb[i][j]+cu[i-aoffsi+uoffs]*cv[j-aoffsj+voffs]))>threshold;
|
|
}
|
|
}
|
|
//--- function call
|
|
CAblas::RMatrixRank1(m,n,refra,aoffsi,aoffsj,ru,uoffs,rv,voffs);
|
|
//--- search errors
|
|
for(i=0;i<=2*maxn-1;i++)
|
|
{
|
|
for(j=0;j<=2*maxn-1;j++)
|
|
{
|
|
//--- check
|
|
if(((i<aoffsi || i>=aoffsi+m) || j<aoffsj) || j>=aoffsj+n)
|
|
result=result || MathAbs(refra[i][j]-refrb[i][j])>threshold;
|
|
else
|
|
result=result || MathAbs(refra[i][j]-(refrb[i][j]+ru[i-aoffsi+uoffs]*rv[j-aoffsj+voffs]))>threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| MV tests |
|
|
//| Returns False for passed test,True - for failed |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestAblasUnit::TestMV(const int minn,const int maxn)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int m=0;
|
|
int n=0;
|
|
int mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int aoffsi=0;
|
|
int aoffsj=0;
|
|
int xoffs=0;
|
|
int yoffs=0;
|
|
int opca=0;
|
|
int opra=0;
|
|
double threshold=0;
|
|
double rv1=0;
|
|
double rv2=0;
|
|
complex cv1=0;
|
|
complex cv2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double rx[];
|
|
double ry[];
|
|
complex cx[];
|
|
complex cy[];
|
|
//--- create matrix
|
|
CMatrixDouble refra;
|
|
CMatrixComplex refca;
|
|
//--- initialization
|
|
result=false;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- calculation
|
|
for(mx=minn;mx<=maxn;mx++)
|
|
{
|
|
//--- Select random M/N in [1,MX] such that max(M,N)=MX
|
|
m=1+CMath::RandomInteger(mx);
|
|
n=1+CMath::RandomInteger(mx);
|
|
//--- check
|
|
if(CMath::RandomInteger(2)==0)
|
|
m=mx;
|
|
else
|
|
n=mx;
|
|
//--- Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN)
|
|
//--- Initialize X by random vector with size (MaxN+MaxN)
|
|
//--- Fill Y by control values
|
|
refra.Resize(maxn+maxn,maxn+maxn);
|
|
refca.Resize(maxn+maxn,maxn+maxn);
|
|
//--- change values
|
|
for(i=0;i<=2*maxn-1;i++)
|
|
{
|
|
for(j=0;j<=2*maxn-1;j++)
|
|
{
|
|
refra[i].Set(j,2*CMath::RandomReal()-1);
|
|
refca[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
refca[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResize(rx,2*maxn);
|
|
ArrayResize(cx,2*maxn);
|
|
ArrayResize(ry,2*maxn);
|
|
ArrayResize(cy,2*maxn);
|
|
//--- change values
|
|
for(i=0;i<=2*maxn-1;i++)
|
|
{
|
|
rx[i]=2*CMath::RandomReal()-1;
|
|
cx[i].re=2*CMath::RandomReal()-1;
|
|
cx[i].im=2*CMath::RandomReal()-1;
|
|
ry[i]=i;
|
|
cy[i]=i;
|
|
}
|
|
//--- test different offsets (zero or one)
|
|
//--- to avoid unnecessary slowdown we don't test ALL possible
|
|
//--- combinations of operation types. We just generate one random
|
|
//--- set of parameters and test it.
|
|
aoffsi=CMath::RandomInteger(maxn);
|
|
aoffsj=CMath::RandomInteger(maxn);
|
|
xoffs=CMath::RandomInteger(maxn);
|
|
yoffs=CMath::RandomInteger(maxn);
|
|
opca=CMath::RandomInteger(3);
|
|
opra=CMath::RandomInteger(2);
|
|
//--- function call
|
|
CAblas::CMatrixMVect(m,n,refca,aoffsi,aoffsj,opca,cx,xoffs,cy,yoffs);
|
|
//--- search errors
|
|
for(i=0;i<=2*maxn-1;i++)
|
|
{
|
|
//--- check
|
|
if(i<yoffs || i>=yoffs+m)
|
|
result=result || cy[i]!=i;
|
|
else
|
|
{
|
|
cv1=cy[i];
|
|
cv2=0.0;
|
|
//--- check
|
|
if(opca==0)
|
|
{
|
|
//--- change values
|
|
i1_=xoffs-aoffsj;
|
|
cv2=0.0;
|
|
for(i_=aoffsj;i_<=aoffsj+n-1;i_++)
|
|
cv2+=refca[aoffsi+i-yoffs][i_]*cx[i_+i1_];
|
|
}
|
|
//--- check
|
|
if(opca==1)
|
|
{
|
|
//--- change values
|
|
i1_=xoffs-aoffsi;
|
|
cv2=0.0;
|
|
for(i_=aoffsi;i_<=aoffsi+n-1;i_++)
|
|
cv2+=refca[i_][aoffsj+i-yoffs]*cx[i_+i1_];
|
|
}
|
|
//--- check
|
|
if(opca==2)
|
|
{
|
|
//--- change values
|
|
i1_=xoffs-aoffsi;
|
|
cv2=0.0;
|
|
for(i_=aoffsi;i_<=aoffsi+n-1;i_++)
|
|
cv2+=CMath::Conj(refca[i_][aoffsj+i-yoffs])*cx[i_+i1_];
|
|
}
|
|
result=result || CMath::AbsComplex(cv1-cv2)>threshold;
|
|
}
|
|
}
|
|
//--- function call
|
|
CAblas::RMatrixMVect(m,n,refra,aoffsi,aoffsj,opra,rx,xoffs,ry,yoffs);
|
|
//--- function call
|
|
for(i=0;i<=2*maxn-1;i++)
|
|
{
|
|
//--- check
|
|
if(i<yoffs || i>=yoffs+m)
|
|
result=result || ry[i]!=i;
|
|
else
|
|
{
|
|
rv1=ry[i];
|
|
rv2=0;
|
|
//--- check
|
|
if(opra==0)
|
|
{
|
|
//--- change values
|
|
i1_=xoffs-aoffsj;
|
|
rv2=0.0;
|
|
for(i_=aoffsj;i_<=aoffsj+n-1;i_++)
|
|
rv2+=refra[aoffsi+i-yoffs][i_]*rx[i_+i1_];
|
|
}
|
|
//--- check
|
|
if(opra==1)
|
|
{
|
|
//--- change values
|
|
i1_=xoffs-aoffsi;
|
|
rv2=0.0;
|
|
for(i_=aoffsi;i_<=aoffsi+n-1;i_++)
|
|
rv2+=refra[i_][aoffsj+i-yoffs]*rx[i_+i1_];
|
|
}
|
|
result=result || MathAbs(rv1-rv2)>threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| COPY tests |
|
|
//| Returns False for passed test,True - for failed |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestAblasUnit::TestCopy(const int minn,const int maxn)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int m=0;
|
|
int n=0;
|
|
int mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int aoffsi=0;
|
|
int aoffsj=0;
|
|
int boffsi=0;
|
|
int boffsj=0;
|
|
double threshold=0;
|
|
//--- create matrix
|
|
CMatrixDouble ra;
|
|
CMatrixDouble rb;
|
|
CMatrixComplex ca;
|
|
CMatrixComplex cb;
|
|
//--- initialization
|
|
result=false;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- calculation
|
|
for(mx=minn;mx<=maxn;mx++)
|
|
{
|
|
//--- Select random M/N in [1,MX] such that max(M,N)=MX
|
|
m=1+CMath::RandomInteger(mx);
|
|
n=1+CMath::RandomInteger(mx);
|
|
//--- check
|
|
if(CMath::RandomInteger(2)==0)
|
|
m=mx;
|
|
else
|
|
n=mx;
|
|
//--- Initialize A by random matrix with size (MaxN+MaxN)*(MaxN+MaxN)
|
|
//--- Initialize X by random vector with size (MaxN+MaxN)
|
|
//--- Fill Y by control values
|
|
ra.Resize(maxn+maxn,maxn+maxn);
|
|
ca.Resize(maxn+maxn,maxn+maxn);
|
|
rb.Resize(maxn+maxn,maxn+maxn);
|
|
cb.Resize(maxn+maxn,maxn+maxn);
|
|
//--- change values
|
|
for(i=0;i<=2*maxn-1;i++)
|
|
{
|
|
for(j=0;j<=2*maxn-1;j++)
|
|
{
|
|
ra[i].Set(j,2*CMath::RandomReal()-1);
|
|
ca[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
ca[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
rb[i].Set(j,1+2*i+3*j);
|
|
cb[i].Set(j,1+2*i+3*j);
|
|
}
|
|
}
|
|
//--- test different offsets (zero or one)
|
|
//--- to avoid unnecessary slowdown we don't test ALL possible
|
|
//--- combinations of operation types. We just generate one random
|
|
//--- set of parameters and test it.
|
|
aoffsi=CMath::RandomInteger(maxn);
|
|
aoffsj=CMath::RandomInteger(maxn);
|
|
boffsi=CMath::RandomInteger(maxn);
|
|
boffsj=CMath::RandomInteger(maxn);
|
|
//--- function call
|
|
CAblas::CMatrixCopy(m,n,ca,aoffsi,aoffsj,cb,boffsi,boffsj);
|
|
//--- search errors
|
|
for(i=0;i<=2*maxn-1;i++)
|
|
{
|
|
for(j=0;j<=2*maxn-1;j++)
|
|
{
|
|
//--- check
|
|
if(((i<boffsi || i>=boffsi+m) || j<boffsj) || j>=boffsj+n)
|
|
result=result || cb[i][j]!=1+2*i+3*j;
|
|
else
|
|
result=result || CMath::AbsComplex(ca[aoffsi+i-boffsi][aoffsj+j-boffsj]-cb[i][j])>threshold;
|
|
}
|
|
}
|
|
//--- function call
|
|
CAblas::RMatrixCopy(m,n,ra,aoffsi,aoffsj,rb,boffsi,boffsj);
|
|
//--- search errors
|
|
for(i=0;i<=2*maxn-1;i++)
|
|
{
|
|
for(j=0;j<=2*maxn-1;j++)
|
|
{
|
|
//--- check
|
|
if(((i<boffsi || i>=boffsi+m) || j<boffsj) || j>=boffsj+n)
|
|
result=result || rb[i][j]!=1+2*i+3*j;
|
|
else
|
|
result=result || MathAbs(ra[aoffsi+i-boffsi][aoffsj+j-boffsj]-rb[i][j])>threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestAblasUnit::RefCMatrixRightTrsM(const int m,const int n,
|
|
CMatrixComplex &a,
|
|
const int i1,const int j1,
|
|
const bool isupper,
|
|
const bool isunit,
|
|
const int optype,
|
|
CMatrixComplex &x,
|
|
const int i2,const int j2)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
complex vc=0;
|
|
bool rupper;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
complex tx[];
|
|
//--- create matrix
|
|
CMatrixComplex a1;
|
|
CMatrixComplex a2;
|
|
//--- check
|
|
if(n*m==0)
|
|
return;
|
|
//--- allocation
|
|
a1.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a1[i].Set(j,0);
|
|
}
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i;j<=n-1;j++)
|
|
a1[i].Set(j,a[i1+i][j1+j]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=i;j++)
|
|
a1[i].Set(j,a[i1+i][j1+j]);
|
|
}
|
|
}
|
|
//--- change value
|
|
rupper=isupper;
|
|
//--- check
|
|
if(isunit)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a1[i].Set(i,1);
|
|
}
|
|
}
|
|
//--- allocation
|
|
a2.Resize(n,n);
|
|
//--- check
|
|
if(optype==0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a2[i].Set(j,a1[i][j]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(optype==1)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a2[i].Set(j,a1[j][i]);
|
|
}
|
|
//--- change value
|
|
rupper=!rupper;
|
|
}
|
|
//--- check
|
|
if(optype==2)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a2[i].Set(j,CMath::Conj(a1[j][i]));
|
|
}
|
|
}
|
|
//--- change value
|
|
rupper=!rupper;
|
|
}
|
|
//--- function call
|
|
InternalCMatrixTrInverse(a2,n,rupper,false);
|
|
//--- allocation
|
|
ArrayResize(tx,n);
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
i1_=j2;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tx[i_]=x[i2+i][i_+i1_];
|
|
//--- change values
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
vc=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vc+=tx[i_]*a2[i_][j];
|
|
x[i2+i].Set(j2+j,vc);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestAblasUnit::RefCMatrixLeftTrsM(const int m,const int n,
|
|
CMatrixComplex &a,
|
|
const int i1,const int j1,
|
|
const bool isupper,
|
|
const bool isunit,
|
|
const int optype,
|
|
CMatrixComplex &x,
|
|
const int i2,const int j2)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
complex vc=0;
|
|
bool rupper;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
complex tx[];
|
|
//--- create matrix
|
|
CMatrixComplex a1;
|
|
CMatrixComplex a2;
|
|
//--- check
|
|
if(n*m==0)
|
|
return;
|
|
//--- allocation
|
|
a1.Resize(m,m);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
a1[i].Set(j,0);
|
|
}
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=i;j<=m-1;j++)
|
|
a1[i].Set(j,a[i1+i][j1+j]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=i;j++)
|
|
a1[i].Set(j,a[i1+i][j1+j]);
|
|
}
|
|
}
|
|
//--- change value
|
|
rupper=isupper;
|
|
//--- check
|
|
if(isunit)
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
a1[i].Set(i,1);
|
|
}
|
|
//--- allocation
|
|
a2.Resize(m,m);
|
|
//--- check
|
|
if(optype==0)
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
a2[i].Set(j,a1[i][j]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(optype==1)
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
a2[i].Set(j,a1[j][i]);
|
|
}
|
|
//--- change value
|
|
rupper=!rupper;
|
|
}
|
|
//--- check
|
|
if(optype==2)
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
a2[i].Set(j,CMath::Conj(a1[j][i]));
|
|
}
|
|
//--- change value
|
|
rupper=!rupper;
|
|
}
|
|
//--- function call
|
|
InternalCMatrixTrInverse(a2,m,rupper,false);
|
|
//--- allocation
|
|
ArrayResize(tx,m);
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
i1_=i2;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
tx[i_]=x[i_+i1_][j2+j];
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
vc=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
vc+=a2[i][i_]*tx[i_];
|
|
x[i2+i].Set(j2+j,vc);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestAblasUnit::RefRMatrixRightTrsM(const int m,const int n,
|
|
CMatrixDouble &a,
|
|
const int i1,const int j1,
|
|
const bool isupper,
|
|
const bool isunit,
|
|
const int optype,
|
|
CMatrixDouble &x,
|
|
const int i2,const int j2)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double vr=0;
|
|
bool rupper;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
double tx[];
|
|
//--- create matrix
|
|
CMatrixDouble a1;
|
|
CMatrixDouble a2;
|
|
//--- check
|
|
if(n*m==0)
|
|
return;
|
|
//--- allocation
|
|
a1.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a1[i].Set(j,0);
|
|
}
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i;j<=n-1;j++)
|
|
a1[i].Set(j,a[i1+i][j1+j]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=i;j++)
|
|
a1[i].Set(j,a[i1+i][j1+j]);
|
|
}
|
|
}
|
|
//--- change value
|
|
rupper=isupper;
|
|
//--- check
|
|
if(isunit)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
a1[i].Set(i,1);
|
|
}
|
|
//--- allocation
|
|
a2.Resize(n,n);
|
|
//--- check
|
|
if(optype==0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a2[i].Set(j,a1[i][j]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(optype==1)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a2[i].Set(j,a1[j][i]);
|
|
}
|
|
//--- change value
|
|
rupper=!rupper;
|
|
}
|
|
//--- function call
|
|
InternalRMatrixTrInverse(a2,n,rupper,false);
|
|
//--- allocation
|
|
ArrayResize(tx,n);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
i1_=j2;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tx[i_]=x[i2+i][i_+i1_];
|
|
//--- change values
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
vr=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vr+=tx[i_]*a2[i_][j];
|
|
x[i2+i].Set(j2+j,vr);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestAblasUnit::RefRMatrixLeftTrsM(const int m,const int n,
|
|
CMatrixDouble &a,
|
|
const int i1,const int j1,
|
|
const bool isupper,
|
|
const bool isunit,
|
|
const int optype,
|
|
CMatrixDouble &x,
|
|
const int i2,const int j2)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double vr=0;
|
|
bool rupper;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
double tx[];
|
|
//--- create matrix
|
|
CMatrixDouble a1;
|
|
CMatrixDouble a2;
|
|
//--- check
|
|
if(n*m==0)
|
|
return;
|
|
//--- allocation
|
|
a1.Resize(m,m);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
a1[i].Set(j,0);
|
|
}
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=i;j<=m-1;j++)
|
|
a1[i].Set(j,a[i1+i][j1+j]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=i;j++)
|
|
a1[i].Set(j,a[i1+i][j1+j]);
|
|
}
|
|
}
|
|
//--- change value
|
|
rupper=isupper;
|
|
//--- check
|
|
if(isunit)
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
a1[i].Set(i,1);
|
|
}
|
|
//--- allocation
|
|
a2.Resize(m,m);
|
|
//--- check
|
|
if(optype==0)
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
a2[i].Set(j,a1[i][j]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(optype==1)
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
a2[i].Set(j,a1[j][i]);
|
|
}
|
|
//--- change value
|
|
rupper=!rupper;
|
|
}
|
|
//--- function call
|
|
InternalRMatrixTrInverse(a2,m,rupper,false);
|
|
//--- allocation
|
|
ArrayResize(tx,m);
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
i1_=i2;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
tx[i_]=x[i_+i1_][j2+j];
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
vr=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
vr+=a2[i][i_]*tx[i_];
|
|
x[i2+i].Set(j2+j,vr);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine. |
|
|
//| Triangular matrix inversion |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee,Univ. of California Berkeley,NAG Ltd., |
|
|
//| Courant Institute,Argonne National Lab,and Rice University |
|
|
//| February 29,1992 |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestAblasUnit::InternalCMatrixTrInverse(CMatrixComplex &a,
|
|
const int n,
|
|
const bool isupper,
|
|
const bool isunittriangular)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool nounit;
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
complex ajj=0;
|
|
complex one=1;
|
|
int i_=0;
|
|
//--- create array
|
|
complex t[];
|
|
//--- initialization
|
|
result=true;
|
|
//--- allocation
|
|
ArrayResize(t,n);
|
|
//--- Test the input parameters.
|
|
nounit=!isunittriangular;
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute inverse of upper triangular matrix.
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
a[j].Set(j,one/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- Compute elements 1:j-1 of j-th column.
|
|
if(j>0)
|
|
{
|
|
for(i_=0;i_<=j-1;i_++)
|
|
t[i_]=a[i_][j];
|
|
for(i=0;i<=j-1;i++)
|
|
{
|
|
//--- check
|
|
if(i+1<j)
|
|
{
|
|
v=0.0;
|
|
for(i_=i+1;i_<=j-1;i_++)
|
|
v+=a[i][i_]*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a[i].Set(j,v+a[i][i]*t[i]);
|
|
else
|
|
a[i].Set(j,v+t[i]);
|
|
}
|
|
//--- change values
|
|
for(i_=0;i_<=j-1;i_++)
|
|
a[i_].Set(j,ajj*a[i_][j]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute inverse of lower triangular matrix.
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
a[j].Set(j,one/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- check
|
|
if(j+1<n)
|
|
{
|
|
//--- Compute elements j+1:n of j-th column.
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
t[i_]=a[i_][j];
|
|
for(i=j+1;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i>j+1)
|
|
{
|
|
v=0.0;
|
|
for(i_=j+1;i_<=i-1;i_++)
|
|
v+=a[i][i_]*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a[i].Set(j,v+a[i][i]*t[i]);
|
|
else
|
|
a[i].Set(j,v+t[i]);
|
|
}
|
|
//--- change values
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
a[i_].Set(j,ajj*a[i_][j]);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal subroutine. |
|
|
//| Triangular matrix inversion |
|
|
//| -- LAPACK routine (version 3.0) -- |
|
|
//| Univ. of Tennessee,Univ. of California Berkeley,NAG Ltd., |
|
|
//| Courant Institute,Argonne National Lab,and Rice University |
|
|
//| February 29,1992 |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestAblasUnit::InternalRMatrixTrInverse(CMatrixDouble &a,
|
|
const int n,
|
|
const bool isupper,
|
|
const bool isunittriangular)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool nounit;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double ajj=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double t[];
|
|
//--- initialization
|
|
result=true;
|
|
//--- allocation
|
|
ArrayResize(t,n);
|
|
//--- Test the input parameters.
|
|
nounit=!isunittriangular;
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute inverse of upper triangular matrix.
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0.0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
a[j].Set(j,1/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- Compute elements 1:j-1 of j-th column.
|
|
if(j>0)
|
|
{
|
|
for(i_=0;i_<=j-1;i_++)
|
|
t[i_]=a[i_][j];
|
|
for(i=0;i<=j-1;i++)
|
|
{
|
|
//--- check
|
|
if(i<j-1)
|
|
{
|
|
v=0.0;
|
|
for(i_=i+1;i_<=j-1;i_++)
|
|
v+=a[i][i_]*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a[i].Set(j,v+a[i][i]*t[i]);
|
|
else
|
|
a[i].Set(j,v+t[i]);
|
|
}
|
|
//--- change values
|
|
for(i_=0;i_<=j-1;i_++)
|
|
a[i_].Set(j,ajj*a[i_][j]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute inverse of lower triangular matrix.
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0.0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
a[j].Set(j,1/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- check
|
|
if(j<n-1)
|
|
{
|
|
//--- Compute elements j+1:n of j-th column.
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
t[i_]=a[i_][j];
|
|
for(i=j+1;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i>j+1)
|
|
{
|
|
v=0.0;
|
|
for(i_=j+1;i_<=i-1;i_++)
|
|
v+=a[i][i_]*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a[i].Set(j,v+a[i][i]*t[i]);
|
|
else
|
|
a[i].Set(j,v+t[i]);
|
|
}
|
|
//--- change values
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
a[i_].Set(j,ajj*a[i_][j]);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference SYRK subroutine. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestAblasUnit::RefCMatrixSyrk(const int n,const int k,
|
|
const double alpha,CMatrixComplex &a,
|
|
const int ia,const int ja,
|
|
const int optypea,const double beta,
|
|
CMatrixComplex &c,const int ic,
|
|
const int jc,const bool isupper)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
complex vc=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixComplex ae;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if((isupper && j>=i) || (!isupper && j<=i))
|
|
{
|
|
//--- check
|
|
if(beta==0.0)
|
|
c[i+ic].Set(j+jc,0);
|
|
else
|
|
c[i+ic].Set(j+jc,c[i+ic][j+jc]*beta);
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(alpha==0.0)
|
|
return;
|
|
//--- check
|
|
if(n*k>0)
|
|
ae.Resize(n,k);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=k-1;j++)
|
|
{
|
|
//--- check
|
|
if(optypea==0)
|
|
ae[i].Set(j,a[ia+i][ja+j]);
|
|
//--- check
|
|
if(optypea==2)
|
|
ae[i].Set(j,CMath::Conj(a[ia+j][ja+i]));
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
vc=0;
|
|
//--- check
|
|
if(k>0)
|
|
{
|
|
vc=0.0;
|
|
for(i_=0;i_<=k-1;i_++)
|
|
vc+=ae[i][i_]*CMath::Conj(ae[j][i_]);
|
|
}
|
|
vc=vc*alpha;
|
|
//--- check
|
|
if(isupper && j>=i)
|
|
c[ic+i].Set(jc+j,vc+c[ic+i][jc+j]);
|
|
//--- check
|
|
if(!isupper && j<=i)
|
|
c[ic+i].Set(jc+j,vc+c[ic+i][jc+j]);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference SYRK subroutine. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestAblasUnit::RefRMatrixSyrk(const int n,const int k,
|
|
const double alpha,CMatrixDouble &a,
|
|
const int ia,const int ja,
|
|
const int optypea,const double beta,
|
|
CMatrixDouble &c,const int ic,
|
|
const int jc,const bool isupper)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double vr=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble ae;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if((isupper && j>=i) || (!isupper && j<=i))
|
|
{
|
|
//--- check
|
|
if(beta==0.0)
|
|
c[i+ic].Set(j+jc,0);
|
|
else
|
|
c[i+ic].Set(j+jc,c[i+ic][j+jc]*beta);
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(alpha==0.0)
|
|
return;
|
|
//--- check
|
|
if(n*k>0)
|
|
ae.Resize(n,k);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=k-1;j++)
|
|
{
|
|
//--- check
|
|
if(optypea==0)
|
|
ae[i].Set(j,a[ia+i][ja+j]);
|
|
//--- check
|
|
if(optypea==1)
|
|
ae[i].Set(j,a[ia+j][ja+i]);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
vr=0;
|
|
//--- check
|
|
if(k>0)
|
|
{
|
|
vr=0.0;
|
|
for(i_=0;i_<=k-1;i_++)
|
|
vr+=ae[i][i_]*ae[j][i_];
|
|
}
|
|
vr=alpha*vr;
|
|
//--- check
|
|
if(isupper && j>=i)
|
|
c[ic+i].Set(jc+j,vr+c[ic+i][jc+j]);
|
|
//--- check
|
|
if(!isupper && j<=i)
|
|
c[ic+i].Set(jc+j,vr+c[ic+i][jc+j]);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference GEMM, |
|
|
//| ALGLIB subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestAblasUnit::RefCMatrixGemm(const int m,const int n,
|
|
const int k,complex &alpha,
|
|
CMatrixComplex &a,const int ia,
|
|
const int ja,const int optypea,
|
|
CMatrixComplex &b,const int ib,
|
|
const int jb,const int optypeb,
|
|
complex &beta,CMatrixComplex &c,
|
|
const int ic,const int jc)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
complex vc=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixComplex ae;
|
|
CMatrixComplex be;
|
|
//--- allocation
|
|
ae.Resize(m,k);
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=k-1;j++)
|
|
{
|
|
//--- check
|
|
if(optypea==0)
|
|
ae[i].Set(j,a[ia+i][ja+j]);
|
|
//--- check
|
|
if(optypea==1)
|
|
ae[i].Set(j,a[ia+j][ja+i]);
|
|
//--- check
|
|
if(optypea==2)
|
|
ae[i].Set(j,CMath::Conj(a[ia+j][ja+i]));
|
|
}
|
|
}
|
|
//--- allocation
|
|
be.Resize(k,n);
|
|
//--- change values
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(optypeb==0)
|
|
be[i].Set(j,b[ib+i][jb+j]);
|
|
//--- check
|
|
if(optypeb==1)
|
|
be[i].Set(j,b[ib+j][jb+i]);
|
|
//--- check
|
|
if(optypeb==2)
|
|
be[i].Set(j,CMath::Conj(b[ib+j][jb+i]));
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
vc=0.0;
|
|
for(i_=0;i_<=k-1;i_++)
|
|
vc+=ae[i][i_]*be[i_][j];
|
|
vc=alpha*vc;
|
|
//--- check
|
|
if(beta!=0)
|
|
vc=vc+beta*c[ic+i][jc+j];
|
|
c[ic+i].Set(jc+j,vc);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference GEMM, |
|
|
//| ALGLIB subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestAblasUnit::RefRMatrixGemm(const int m,const int n,
|
|
const int k,const double alpha,
|
|
CMatrixDouble &a,const int ia,
|
|
const int ja,const int optypea,
|
|
CMatrixDouble &b,const int ib,
|
|
const int jb,const int optypeb,
|
|
const double beta,CMatrixDouble &c,
|
|
const int ic,const int jc)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double vc=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble ae;
|
|
CMatrixDouble be;
|
|
//--- allocation
|
|
ae.Resize(m,k);
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=k-1;j++)
|
|
{
|
|
//--- check
|
|
if(optypea==0)
|
|
ae[i].Set(j,a[ia+i][ja+j]);
|
|
//--- check
|
|
if(optypea==1)
|
|
ae[i].Set(j,a[ia+j][ja+i]);
|
|
}
|
|
}
|
|
//--- allocation
|
|
be.Resize(k,n);
|
|
//--- change values
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(optypeb==0)
|
|
be[i].Set(j,b[ib+i][jb+j]);
|
|
//--- check
|
|
if(optypeb==1)
|
|
be[i].Set(j,b[ib+j][jb+i]);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
vc=0.0;
|
|
for(i_=0;i_<=k-1;i_++)
|
|
vc+=ae[i][i_]*be[i_][j];
|
|
vc=alpha*vc;
|
|
//--- check
|
|
if(beta!=0.0)
|
|
vc=vc+beta*c[ic+i][jc+j];
|
|
c[ic+i].Set(jc+j,vc);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CBaseStat |
|
|
//+------------------------------------------------------------------+
|
|
class CTestBaseStatUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestBaseStatUnit(void);
|
|
~CTestBaseStatUnit(void);
|
|
//--- public method
|
|
static bool TestBaseStat(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestBaseStatUnit::CTestBaseStatUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestBaseStatUnit::~CTestBaseStatUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CBaseStat |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestBaseStatUnit::TestBaseStat(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool s1errors;
|
|
bool covcorrerrors;
|
|
double threshold=0;
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
int kx=0;
|
|
int ky=0;
|
|
int ctype=0;
|
|
int cidxx=0;
|
|
int cidxy=0;
|
|
double mean=0;
|
|
double variance=0;
|
|
double skewness=0;
|
|
double kurtosis=0;
|
|
double adev=0;
|
|
double median=0;
|
|
double pv=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble mx;
|
|
CMatrixDouble my;
|
|
CMatrixDouble cc;
|
|
CMatrixDouble cp;
|
|
CMatrixDouble cs;
|
|
//--- Primary settings
|
|
waserrors=false;
|
|
s1errors=false;
|
|
covcorrerrors=false;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- * prepare X and Y - two test samples
|
|
//--- * test 1-sample coefficients
|
|
n=10;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=CMath::Sqr(i);
|
|
//--- function call
|
|
CBaseStat::SampleMoments(x,n,mean,variance,skewness,kurtosis);
|
|
//--- search errors
|
|
s1errors=s1errors || MathAbs(mean-28.5)>0.001;
|
|
s1errors=s1errors || MathAbs(variance-801.1667)>0.001;
|
|
s1errors=s1errors || MathAbs(skewness-0.5751)>0.001;
|
|
s1errors=s1errors || MathAbs(kurtosis+1.2666)>0.001;
|
|
//--- function call
|
|
CBaseStat::SampleAdev(x,n,adev);
|
|
//--- search errors
|
|
s1errors=s1errors || MathAbs(adev-23.2000)>0.001;
|
|
//--- function call
|
|
CBaseStat::SampleMedian(x,n,median);
|
|
//--- search errors
|
|
s1errors=s1errors || MathAbs(median-0.5*(16+25))>0.001;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- function call
|
|
CBaseStat::SamplePercentile(x,n,(double)i/(double)(n-1),pv);
|
|
//--- search errors
|
|
s1errors=s1errors || MathAbs(pv-x[i])>0.001;
|
|
}
|
|
//--- function call
|
|
CBaseStat::SamplePercentile(x,n,0.5,pv);
|
|
//--- search errors
|
|
s1errors=s1errors || MathAbs(pv-0.5*(16+25))>0.001;
|
|
//--- test covariance/correlation:
|
|
//--- * 2-sample coefficients
|
|
//--- We generate random matrices MX and MY
|
|
n=10;
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=CMath::Sqr(i);
|
|
y[i]=i;
|
|
}
|
|
//--- search errors
|
|
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::PearsonCorr2(x,y,n)-0.9627)>0.0001;
|
|
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::SpearmanCorr2(x,y,n)-1.0000)>0.0001;
|
|
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::Cov2(x,y,n)-82.5000)>0.0001;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=CMath::Sqr(i-0.5*n);
|
|
y[i]=i;
|
|
}
|
|
//--- search errors
|
|
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::PearsonCorr2(x,y,n)+0.3676)>0.0001;
|
|
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::SpearmanCorr2(x,y,n)+0.2761)>0.0001;
|
|
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::Cov2(x,y,n)+9.1667)>0.0001;
|
|
//--- test covariance/correlation:
|
|
//--- * matrix covariance/correlation
|
|
//--- * matrix cross-covariance/cross-correlation
|
|
//--- We generate random matrices MX and MY which contain KX (KY)
|
|
//--- columns,all except one are random,one of them is constant.
|
|
//--- We test that function (a) do not crash on constant column,
|
|
//--- and (b) return variances/correlations that are exactly zero
|
|
//--- for this column.
|
|
//--- CType control variable controls type of constant: 0 - no constant
|
|
//--- column,1 - zero column,2 - nonzero column with value whose
|
|
//--- binary representation contains many non-zero bits. Using such
|
|
//--- type of constant column we are able to ensure than even in the
|
|
//--- presense of roundoff error functions correctly detect constant
|
|
//--- columns.
|
|
for(n=0;n<=10;n++)
|
|
{
|
|
//--- check
|
|
if(n>0)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
}
|
|
//--- calculation
|
|
for(ctype=0;ctype<=2;ctype++)
|
|
{
|
|
for(kx=1;kx<=10;kx++)
|
|
{
|
|
for(ky=1;ky<=10;ky++)
|
|
{
|
|
//--- Fill matrices,add constant column (when CType=1 or=2)
|
|
cidxx=-1;
|
|
cidxy=-1;
|
|
//--- check
|
|
if(n>0)
|
|
{
|
|
//--- allocation
|
|
mx.Resize(n,kx);
|
|
my.Resize(n,ky);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=kx-1;j++)
|
|
mx[i].Set(j,2*CMath::RandomReal()-1);
|
|
for(j=0;j<=ky-1;j++)
|
|
my[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- check
|
|
if(ctype==1)
|
|
{
|
|
cidxx=CMath::RandomInteger(kx);
|
|
cidxy=CMath::RandomInteger(ky);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
mx[i].Set(cidxx,0.0);
|
|
my[i].Set(cidxy,0.0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(ctype==2)
|
|
{
|
|
cidxx=CMath::RandomInteger(kx);
|
|
cidxy=CMath::RandomInteger(ky);
|
|
//--- change values
|
|
v=MathSqrt((CMath::RandomInteger(kx)+1)/(double)kx);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
mx[i].Set(cidxx,v);
|
|
my[i].Set(cidxy,v);
|
|
}
|
|
}
|
|
}
|
|
//--- test covariance/correlation matrix using
|
|
//--- 2-sample functions as reference point.
|
|
//--- We also test that coefficients for constant variables
|
|
//--- are exactly zero.
|
|
CBaseStat::CovM(mx,n,kx,cc);
|
|
CBaseStat::PearsonCorrM(mx,n,kx,cp);
|
|
CBaseStat::SpearmanCorrM(mx,n,kx,cs);
|
|
for(i=0;i<=kx-1;i++)
|
|
{
|
|
for(j=0;j<=kx-1;j++)
|
|
{
|
|
//--- check
|
|
if(n>0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
{
|
|
x[i_]=mx[i_][i];
|
|
}
|
|
for(i_=0;i_<=n-1;i_++)
|
|
{
|
|
y[i_]=mx[i_][j];
|
|
}
|
|
}
|
|
//--- search errors
|
|
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::Cov2(x,y,n)-cc[i][j])>threshold;
|
|
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::PearsonCorr2(x,y,n)-cp[i][j])>threshold;
|
|
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::SpearmanCorr2(x,y,n)-cs[i][j])>threshold;
|
|
}
|
|
}
|
|
//--- check
|
|
if(ctype!=0 && n>0)
|
|
{
|
|
for(i=0;i<=kx-1;i++)
|
|
{
|
|
//--- search errors
|
|
covcorrerrors=covcorrerrors || cc[i][cidxx]!=0.0;
|
|
covcorrerrors=covcorrerrors || cc[cidxx][i]!=0.0;
|
|
covcorrerrors=covcorrerrors || cp[i][cidxx]!=0.0;
|
|
covcorrerrors=covcorrerrors || cp[cidxx][i]!=0.0;
|
|
covcorrerrors=covcorrerrors || cs[i][cidxx]!=0.0;
|
|
covcorrerrors=covcorrerrors || cs[cidxx][i]!=0.0;
|
|
}
|
|
}
|
|
//--- test cross-covariance/cross-correlation matrix using
|
|
//--- 2-sample functions as reference point.
|
|
//--- We also test that coefficients for constant variables
|
|
//--- are exactly zero.
|
|
CBaseStat::CovM2(mx,my,n,kx,ky,cc);
|
|
CBaseStat::PearsonCorrM2(mx,my,n,kx,ky,cp);
|
|
CBaseStat::SpearmanCorrM2(mx,my,n,kx,ky,cs);
|
|
for(i=0;i<=kx-1;i++)
|
|
{
|
|
for(j=0;j<=ky-1;j++)
|
|
{
|
|
//--- check
|
|
if(n>0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
x[i_]=mx[i_][i];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
y[i_]=my[i_][j];
|
|
}
|
|
//--- search errors
|
|
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::Cov2(x,y,n)-cc[i][j])>threshold;
|
|
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::PearsonCorr2(x,y,n)-cp[i][j])>threshold;
|
|
covcorrerrors=covcorrerrors || MathAbs(CBaseStat::SpearmanCorr2(x,y,n)-cs[i][j])>threshold;
|
|
}
|
|
}
|
|
//--- check
|
|
if(ctype!=0 && n>0)
|
|
{
|
|
for(i=0;i<=kx-1;i++)
|
|
{
|
|
//--- search errors
|
|
covcorrerrors=covcorrerrors || cc[i][cidxy]!=0.0;
|
|
covcorrerrors=covcorrerrors || cp[i][cidxy]!=0.0;
|
|
covcorrerrors=covcorrerrors || cs[i][cidxy]!=0.0;
|
|
}
|
|
for(j=0;j<=ky-1;j++)
|
|
{
|
|
//--- search errors
|
|
covcorrerrors=covcorrerrors || cc[cidxx][j]!=0.0;
|
|
covcorrerrors=covcorrerrors || cp[cidxx][j]!=0.0;
|
|
covcorrerrors=covcorrerrors || cs[cidxx][j]!=0.0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Final report
|
|
waserrors=s1errors || covcorrerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("DESC.STAT TEST");
|
|
Print("TOTAL RESULTS: ");
|
|
//--- check
|
|
if(!waserrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* 1-SAMPLE FUNCTIONALITY: ");
|
|
//--- check
|
|
if(!s1errors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* CORRELATION/COVARIATION: ");
|
|
//--- check
|
|
if(!covcorrerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST SUMMARY: FAILED");
|
|
else
|
|
Print("TEST SUMMARY: PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CBdSS |
|
|
//+------------------------------------------------------------------+
|
|
class CTestBdSSUnit
|
|
{
|
|
public:
|
|
CTestBdSSUnit(void);
|
|
~CTestBdSSUnit(void);
|
|
|
|
static bool TestBdSS(const bool silent);
|
|
|
|
private:
|
|
static void Unset2D(CMatrixComplex &a);
|
|
static void Unset1D(double &a[]);
|
|
static void Unset1DI(int &a[]);
|
|
static void TestSortResults(double &asorted[],int &p1[],int &p2[],double &aoriginal[],const int n,bool &waserrors);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestBdSSUnit::CTestBdSSUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestBdSSUnit::~CTestBdSSUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CBdSS |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestBdSSUnit::TestBdSS(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
int maxn=0;
|
|
int maxnq=0;
|
|
int tiecount=0;
|
|
int c1=0;
|
|
int c0=0;
|
|
int ni=0;
|
|
int nc=0;
|
|
double pal=0;
|
|
double pbl=0;
|
|
double par=0;
|
|
double pbr=0;
|
|
double cve=0;
|
|
double cvr=0;
|
|
int info=0;
|
|
double threshold=0;
|
|
double rms=0;
|
|
double cvrms=0;
|
|
bool waserrors;
|
|
bool tieserrors;
|
|
bool split2errors;
|
|
bool optimalsplitkerrors;
|
|
bool splitkerrors;
|
|
//--- create arrays
|
|
double a[];
|
|
double a0[];
|
|
double at[];
|
|
double thresholds[];
|
|
int c[];
|
|
int p1[];
|
|
int p2[];
|
|
int ties[];
|
|
int pt1[];
|
|
int pt2[];
|
|
double tmp[];
|
|
double sortrbuf[];
|
|
double sortrbuf2[];
|
|
int sortibuf[];
|
|
int tiebuf[];
|
|
int cntbuf[];
|
|
//--- create matrix
|
|
CMatrixDouble p;
|
|
//--- initialization
|
|
waserrors=false;
|
|
tieserrors=false;
|
|
split2errors=false;
|
|
splitkerrors=false;
|
|
optimalsplitkerrors=false;
|
|
maxn=100;
|
|
maxnq=49;
|
|
passcount=10;
|
|
//--- Test ties
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- untied data,test DSTie
|
|
Unset1DI(p1);
|
|
Unset1DI(p2);
|
|
Unset1DI(pt1);
|
|
Unset1DI(pt2);
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(a0,n);
|
|
ArrayResize(at,n);
|
|
ArrayResize(tmp,n);
|
|
//--- change values
|
|
a[0]=2*CMath::RandomReal()-1;
|
|
tmp[0]=CMath::RandomReal();
|
|
for(i=1;i<=n-1;i++)
|
|
{
|
|
//--- A is randomly permuted
|
|
a[i]=a[i-1]+0.1*CMath::RandomReal()+0.1;
|
|
tmp[i]=CMath::RandomReal();
|
|
}
|
|
//--- function call
|
|
CTSort::TagSortFastR(tmp,a,sortrbuf,sortrbuf2,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a0[i]=a[i];
|
|
at[i]=a[i];
|
|
}
|
|
//--- function call
|
|
CBdSS::DSTie(a0,n,ties,tiecount,p1,p2);
|
|
//--- function call
|
|
CTSort::TagSort(at,n,pt1,pt2);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
tieserrors=tieserrors || p1[i]!=pt1[i];
|
|
tieserrors=tieserrors || p2[i]!=pt2[i];
|
|
}
|
|
tieserrors=tieserrors || tiecount!=n;
|
|
//--- check
|
|
if(tiecount==n)
|
|
{
|
|
for(i=0;i<=n;i++)
|
|
tieserrors=tieserrors || ties[i]!=i;
|
|
}
|
|
//--- tied data,test DSTie
|
|
Unset1DI(p1);
|
|
Unset1DI(p2);
|
|
Unset1DI(pt1);
|
|
Unset1DI(pt2);
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(a0,n);
|
|
ArrayResize(at,n);
|
|
//--- change values
|
|
c1=0;
|
|
c0=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i]=CMath::RandomInteger(2);
|
|
//--- check
|
|
if(a[i]==0.0)
|
|
c0=c0+1;
|
|
else
|
|
c1=c1+1;
|
|
a0[i]=a[i];
|
|
at[i]=a[i];
|
|
}
|
|
//--- function call
|
|
CBdSS::DSTie(a0,n,ties,tiecount,p1,p2);
|
|
//--- function call
|
|
CTSort::TagSort(at,n,pt1,pt2);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
tieserrors=tieserrors || p1[i]!=pt1[i];
|
|
tieserrors=tieserrors || p2[i]!=pt2[i];
|
|
}
|
|
//--- check
|
|
if(c0==0 || c1==0)
|
|
{
|
|
//--- search errors
|
|
tieserrors=tieserrors || tiecount!=1;
|
|
//--- check
|
|
if(tiecount==1)
|
|
{
|
|
tieserrors=tieserrors || ties[0]!=0;
|
|
tieserrors=tieserrors || ties[1]!=n;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- search errors
|
|
tieserrors=tieserrors || tiecount!=2;
|
|
//--- check
|
|
if(tiecount==2)
|
|
{
|
|
tieserrors=tieserrors || ties[0]!=0;
|
|
tieserrors=tieserrors || ties[1]!=c0;
|
|
tieserrors=tieserrors || ties[2]!=n;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- split-2
|
|
//--- General tests for different N's
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(c,n);
|
|
//--- one-tie test
|
|
if(n%2==0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i]=n;
|
|
c[i]=i%2;
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplit2(a,c,n,info,threshold,pal,pbl,par,pbr,cve);
|
|
//--- check
|
|
if(info!=-3)
|
|
{
|
|
split2errors=true;
|
|
continue;
|
|
}
|
|
}
|
|
//--- two-tie test
|
|
//--- test #1
|
|
if(n>1)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i]=i/((n+1)/2);
|
|
c[i]=i/((n+1)/2);
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplit2(a,c,n,info,threshold,pal,pbl,par,pbr,cve);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
split2errors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
split2errors=split2errors || MathAbs(threshold-0.5)>100*CMath::m_machineepsilon;
|
|
split2errors=split2errors || MathAbs(pal-1)>100*CMath::m_machineepsilon;
|
|
split2errors=split2errors || MathAbs(pbl-0)>100*CMath::m_machineepsilon;
|
|
split2errors=split2errors || MathAbs(par-0)>100*CMath::m_machineepsilon;
|
|
split2errors=split2errors || MathAbs(pbr-1)>100*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
//--- Special "CREDIT"-test (transparency coefficient)
|
|
n=110;
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(c,n);
|
|
//--- initialization
|
|
a[0]=0.000;
|
|
c[0]=0;
|
|
a[1]=0.000;
|
|
c[1]=0;
|
|
a[2]=0.000;
|
|
c[2]=0;
|
|
a[3]=0.000;
|
|
c[3]=0;
|
|
a[4]=0.000;
|
|
c[4]=0;
|
|
a[5]=0.000;
|
|
c[5]=0;
|
|
a[6]=0.000;
|
|
c[6]=0;
|
|
a[7]=0.000;
|
|
c[7]=1;
|
|
a[8]=0.000;
|
|
c[8]=0;
|
|
a[9]=0.000;
|
|
c[9]=1;
|
|
a[10]=0.000;
|
|
c[10]=0;
|
|
a[11]=0.000;
|
|
c[11]=0;
|
|
a[12]=0.000;
|
|
c[12]=0;
|
|
a[13]=0.000;
|
|
c[13]=0;
|
|
a[14]=0.000;
|
|
c[14]=0;
|
|
a[15]=0.000;
|
|
c[15]=0;
|
|
a[16]=0.000;
|
|
c[16]=0;
|
|
a[17]=0.000;
|
|
c[17]=0;
|
|
a[18]=0.000;
|
|
c[18]=0;
|
|
a[19]=0.000;
|
|
c[19]=0;
|
|
a[20]=0.000;
|
|
c[20]=0;
|
|
a[21]=0.000;
|
|
c[21]=0;
|
|
a[22]=0.000;
|
|
c[22]=1;
|
|
a[23]=0.000;
|
|
c[23]=0;
|
|
a[24]=0.000;
|
|
c[24]=0;
|
|
a[25]=0.000;
|
|
c[25]=0;
|
|
a[26]=0.000;
|
|
c[26]=0;
|
|
a[27]=0.000;
|
|
c[27]=1;
|
|
a[28]=0.000;
|
|
c[28]=0;
|
|
a[29]=0.000;
|
|
c[29]=1;
|
|
a[30]=0.000;
|
|
c[30]=0;
|
|
a[31]=0.000;
|
|
c[31]=1;
|
|
a[32]=0.000;
|
|
c[32]=0;
|
|
a[33]=0.000;
|
|
c[33]=1;
|
|
a[34]=0.000;
|
|
c[34]=0;
|
|
a[35]=0.030;
|
|
c[35]=0;
|
|
a[36]=0.030;
|
|
c[36]=0;
|
|
a[37]=0.050;
|
|
c[37]=0;
|
|
a[38]=0.070;
|
|
c[38]=1;
|
|
a[39]=0.110;
|
|
c[39]=0;
|
|
a[40]=0.110;
|
|
c[40]=1;
|
|
a[41]=0.120;
|
|
c[41]=0;
|
|
a[42]=0.130;
|
|
c[42]=0;
|
|
a[43]=0.140;
|
|
c[43]=0;
|
|
a[44]=0.140;
|
|
c[44]=0;
|
|
a[45]=0.140;
|
|
c[45]=0;
|
|
a[46]=0.150;
|
|
c[46]=0;
|
|
a[47]=0.150;
|
|
c[47]=0;
|
|
a[48]=0.170;
|
|
c[48]=0;
|
|
a[49]=0.190;
|
|
c[49]=1;
|
|
a[50]=0.200;
|
|
c[50]=0;
|
|
a[51]=0.200;
|
|
c[51]=0;
|
|
a[52]=0.250;
|
|
c[52]=0;
|
|
a[53]=0.250;
|
|
c[53]=0;
|
|
a[54]=0.260;
|
|
c[54]=0;
|
|
a[55]=0.270;
|
|
c[55]=0;
|
|
a[56]=0.280;
|
|
c[56]=0;
|
|
a[57]=0.310;
|
|
c[57]=0;
|
|
a[58]=0.310;
|
|
c[58]=0;
|
|
a[59]=0.330;
|
|
c[59]=0;
|
|
a[60]=0.330;
|
|
c[60]=0;
|
|
a[61]=0.340;
|
|
c[61]=0;
|
|
a[62]=0.340;
|
|
c[62]=0;
|
|
a[63]=0.370;
|
|
c[63]=0;
|
|
a[64]=0.380;
|
|
c[64]=1;
|
|
a[65]=0.380;
|
|
c[65]=0;
|
|
a[66]=0.410;
|
|
c[66]=0;
|
|
a[67]=0.460;
|
|
c[67]=0;
|
|
a[68]=0.520;
|
|
c[68]=0;
|
|
a[69]=0.530;
|
|
c[69]=0;
|
|
a[70]=0.540;
|
|
c[70]=0;
|
|
a[71]=0.560;
|
|
c[71]=0;
|
|
a[72]=0.560;
|
|
c[72]=0;
|
|
a[73]=0.570;
|
|
c[73]=0;
|
|
a[74]=0.600;
|
|
c[74]=0;
|
|
a[75]=0.600;
|
|
c[75]=0;
|
|
a[76]=0.620;
|
|
c[76]=0;
|
|
a[77]=0.650;
|
|
c[77]=0;
|
|
a[78]=0.660;
|
|
c[78]=0;
|
|
a[79]=0.680;
|
|
c[79]=0;
|
|
a[80]=0.700;
|
|
c[80]=0;
|
|
a[81]=0.750;
|
|
c[81]=0;
|
|
a[82]=0.770;
|
|
c[82]=0;
|
|
a[83]=0.770;
|
|
c[83]=0;
|
|
a[84]=0.770;
|
|
c[84]=0;
|
|
a[85]=0.790;
|
|
c[85]=0;
|
|
a[86]=0.810;
|
|
c[86]=0;
|
|
a[87]=0.840;
|
|
c[87]=0;
|
|
a[88]=0.860;
|
|
c[88]=0;
|
|
a[89]=0.870;
|
|
c[89]=0;
|
|
a[90]=0.890;
|
|
c[90]=0;
|
|
a[91]=0.900;
|
|
c[91]=1;
|
|
a[92]=0.900;
|
|
c[92]=0;
|
|
a[93]=0.910;
|
|
c[93]=0;
|
|
a[94]=0.940;
|
|
c[94]=0;
|
|
a[95]=0.950;
|
|
c[95]=0;
|
|
a[96]=0.952;
|
|
c[96]=0;
|
|
a[97]=0.970;
|
|
c[97]=0;
|
|
a[98]=0.970;
|
|
c[98]=0;
|
|
a[99]=0.980;
|
|
c[99]=0;
|
|
a[100]=1.000;
|
|
c[100]=0;
|
|
a[101]=1.000;
|
|
c[101]=0;
|
|
a[102]=1.000;
|
|
c[102]=0;
|
|
a[103]=1.000;
|
|
c[103]=0;
|
|
a[104]=1.000;
|
|
c[104]=0;
|
|
a[105]=1.020;
|
|
c[105]=0;
|
|
a[106]=1.090;
|
|
c[106]=0;
|
|
a[107]=1.130;
|
|
c[107]=0;
|
|
a[108]=1.840;
|
|
c[108]=0;
|
|
a[109]=2.470;
|
|
c[109]=0;
|
|
//--- function call
|
|
CBdSS::DSOptimalSplit2(a,c,n,info,threshold,pal,pbl,par,pbr,cve);
|
|
//--- check
|
|
if(info!=1)
|
|
split2errors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
split2errors=split2errors || MathAbs(threshold-0.195)>100*CMath::m_machineepsilon;
|
|
split2errors=split2errors || MathAbs(pal-0.80)>0.02;
|
|
split2errors=split2errors || MathAbs(pbl-0.20)>0.02;
|
|
split2errors=split2errors || MathAbs(par-0.97)>0.02;
|
|
split2errors=split2errors || MathAbs(pbr-0.03)>0.02;
|
|
}
|
|
//--- split-2 fast
|
|
//--- General tests for different N's
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(c,n);
|
|
ArrayResize(tiebuf,n+1);
|
|
ArrayResize(cntbuf,4);
|
|
//--- one-tie test
|
|
if(n%2==0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i]=n;
|
|
c[i]=i%2;
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplit2Fast(a,c,tiebuf,cntbuf,sortrbuf,sortibuf,n,2,0.00,info,threshold,rms,cvrms);
|
|
//--- check
|
|
if(info!=-3)
|
|
{
|
|
split2errors=true;
|
|
continue;
|
|
}
|
|
}
|
|
//--- two-tie test
|
|
//--- test #1
|
|
if(n>1)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i]=i/((n+1)/2);
|
|
c[i]=i/((n+1)/2);
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplit2Fast(a,c,tiebuf,cntbuf,sortrbuf,sortibuf,n,2,0.00,info,threshold,rms,cvrms);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
split2errors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
split2errors=split2errors || MathAbs(threshold-0.5)>100*CMath::m_machineepsilon;
|
|
split2errors=split2errors || MathAbs(rms-0)>100*CMath::m_machineepsilon;
|
|
//--- check
|
|
if(n==2)
|
|
split2errors=split2errors || MathAbs(cvrms-0.5)>100*CMath::m_machineepsilon;
|
|
else
|
|
{
|
|
//--- check
|
|
if(n==3)
|
|
split2errors=split2errors || MathAbs(cvrms-MathSqrt((2*0+2*0+2*0.25)/6))>100*CMath::m_machineepsilon;
|
|
else
|
|
split2errors=split2errors || MathAbs(cvrms)>100*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
}
|
|
//--- special tests
|
|
n=10;
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(c,n);
|
|
ArrayResize(tiebuf,n+1);
|
|
ArrayResize(cntbuf,2*3);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i]=i;
|
|
//--- check
|
|
if(i<=n-3)
|
|
c[i]=0;
|
|
else
|
|
c[i]=i-(n-3);
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplit2Fast(a,c,tiebuf,cntbuf,sortrbuf,sortibuf,n,3,0.00,info,threshold,rms,cvrms);
|
|
//--- check
|
|
if(info!=1)
|
|
split2errors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
split2errors=split2errors || MathAbs(threshold-(n-2.5))>100*CMath::m_machineepsilon;
|
|
split2errors=split2errors || MathAbs(rms-MathSqrt((0.25+0.25+0.25+0.25)/(3*n)))>100*CMath::m_machineepsilon;
|
|
split2errors=split2errors || MathAbs(cvrms-MathSqrt((double)(1+1+1+1)/(double)(3*n)))>100*CMath::m_machineepsilon;
|
|
}
|
|
//--- Optimal split-K
|
|
//--- General tests for different N's
|
|
for(n=1;n<=maxnq;n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(c,n);
|
|
//--- one-tie test
|
|
if(n%2==0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i]=n;
|
|
c[i]=i%2;
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
|
|
//--- check
|
|
if(info!=-3)
|
|
{
|
|
optimalsplitkerrors=true;
|
|
continue;
|
|
}
|
|
}
|
|
//--- two-tie test
|
|
//--- test #1
|
|
if(n>1)
|
|
{
|
|
c0=0;
|
|
c1=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i]=i/((n+1)/2);
|
|
c[i]=i/((n+1)/2);
|
|
//--- check
|
|
if(c[i]==0)
|
|
c0=c0+1;
|
|
//--- check
|
|
if(c[i]==1)
|
|
c1=c1+1;
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
optimalsplitkerrors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
optimalsplitkerrors=optimalsplitkerrors || ni!=2;
|
|
optimalsplitkerrors=optimalsplitkerrors || MathAbs(thresholds[0]-0.5)>100*CMath::m_machineepsilon;
|
|
optimalsplitkerrors=optimalsplitkerrors || MathAbs(cve-(-(c0*MathLog((double)c0/(double)(c0+1)))-c1*MathLog((double)c1/(double)(c1+1))))>100*CMath::m_machineepsilon;
|
|
}
|
|
//--- test #2
|
|
if(n>2)
|
|
{
|
|
c0=1+CMath::RandomInteger(n-1);
|
|
c1=n-c0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i<c0)
|
|
{
|
|
a[i]=0;
|
|
c[i]=0;
|
|
}
|
|
else
|
|
{
|
|
a[i]=1;
|
|
c[i]=1;
|
|
}
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
optimalsplitkerrors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
optimalsplitkerrors=optimalsplitkerrors || ni!=2;
|
|
optimalsplitkerrors=optimalsplitkerrors || MathAbs(thresholds[0]-0.5)>100*CMath::m_machineepsilon;
|
|
optimalsplitkerrors=optimalsplitkerrors || MathAbs(cve-(-(c0*MathLog((double)c0/(double)(c0+1)))-c1*MathLog((double)c1/(double)(c1+1))))>100*CMath::m_machineepsilon;
|
|
}
|
|
//--- multi-tie test
|
|
if(n>=16)
|
|
{
|
|
//--- Multi-tie test.
|
|
//--- First NC-1 ties have C0 entries,remaining NC-th tie
|
|
//--- have C1 entries.
|
|
nc=(int)MathRound(MathSqrt(n));
|
|
c0=n/nc;
|
|
c1=n-c0*(nc-1);
|
|
for(i=0;i<=nc-2;i++)
|
|
{
|
|
for(j=c0*i;j<=c0*(i+1)-1;j++)
|
|
{
|
|
a[j]=j;
|
|
c[j]=i;
|
|
}
|
|
}
|
|
//--- change values
|
|
for(j=c0*(nc-1);j<=n-1;j++)
|
|
{
|
|
a[j]=j;
|
|
c[j]=nc-1;
|
|
}
|
|
//--- function call
|
|
CBdSS::DSOptimalSplitK(a,c,n,nc,nc+CMath::RandomInteger(nc),info,thresholds,ni,cve);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
optimalsplitkerrors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
optimalsplitkerrors=optimalsplitkerrors || ni!=nc;
|
|
//--- check
|
|
if(ni==nc)
|
|
{
|
|
for(i=0;i<=nc-2;i++)
|
|
optimalsplitkerrors=optimalsplitkerrors || MathAbs(thresholds[i]-(c0*(i+1)-1+0.5))>100*CMath::m_machineepsilon;
|
|
cvr=-((nc-1)*c0*MathLog((double)c0/(double)(c0+nc-1))+c1*MathLog((double)c1/(double)(c1+nc-1)));
|
|
optimalsplitkerrors=optimalsplitkerrors || MathAbs(cve-cvr)>100*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
}
|
|
//--- Non-optimal split-K
|
|
//--- General tests for different N's
|
|
for(n=1;n<=maxnq;n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,n);
|
|
ArrayResize(c,n);
|
|
//--- one-tie test
|
|
if(n%2==0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i]=pass;
|
|
c[i]=i%2;
|
|
}
|
|
//--- function call
|
|
CBdSS::DSSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
|
|
//--- check
|
|
if(info!=-3)
|
|
{
|
|
splitkerrors=true;
|
|
continue;
|
|
}
|
|
}
|
|
//--- two-tie test
|
|
//--- test #1
|
|
if(n>1)
|
|
{
|
|
c0=0;
|
|
c1=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i]=i/((n+1)/2);
|
|
c[i]=i/((n+1)/2);
|
|
//--- check
|
|
if(c[i]==0)
|
|
c0=c0+1;
|
|
//--- check
|
|
if(c[i]==1)
|
|
c1=c1+1;
|
|
}
|
|
//--- function call
|
|
CBdSS::DSSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
splitkerrors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
splitkerrors=splitkerrors || ni!=2;
|
|
//--- check
|
|
if(ni==2)
|
|
{
|
|
splitkerrors=splitkerrors || MathAbs(thresholds[0]-0.5)>100*CMath::m_machineepsilon;
|
|
splitkerrors=splitkerrors || MathAbs(cve-(-(c0*MathLog((double)c0/(double)(c0+1)))-c1*MathLog((double)c1/(double)(c1+1))))>100*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
//--- test #2
|
|
if(n>2)
|
|
{
|
|
c0=1+CMath::RandomInteger(n-1);
|
|
c1=n-c0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i<c0)
|
|
{
|
|
a[i]=0;
|
|
c[i]=0;
|
|
}
|
|
else
|
|
{
|
|
a[i]=1;
|
|
c[i]=1;
|
|
}
|
|
}
|
|
//--- function call
|
|
CBdSS::DSSplitK(a,c,n,2,2+CMath::RandomInteger(5),info,thresholds,ni,cve);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
splitkerrors=true;
|
|
continue;
|
|
}
|
|
splitkerrors=splitkerrors || ni!=2;
|
|
//--- check
|
|
if(ni==2)
|
|
{
|
|
splitkerrors=splitkerrors || MathAbs(thresholds[0]-0.5)>100*CMath::m_machineepsilon;
|
|
splitkerrors=splitkerrors || MathAbs(cve-(-(c0*MathLog((double)c0/(double)(c0+1)))-c1*MathLog((double)c1/(double)(c1+1))))>100*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
//--- multi-tie test
|
|
for(c0=4;c0<=n;c0++)
|
|
{
|
|
//--- check
|
|
if((n%c0==0 && n/c0<=c0) && n/c0>1)
|
|
{
|
|
nc=n/c0;
|
|
for(i=0;i<=nc-1;i++)
|
|
{
|
|
for(j=c0*i;j<=c0*(i+1)-1;j++)
|
|
{
|
|
a[j]=j;
|
|
c[j]=i;
|
|
}
|
|
}
|
|
//--- function call
|
|
CBdSS::DSSplitK(a,c,n,nc,nc+CMath::RandomInteger(nc),info,thresholds,ni,cve);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
splitkerrors=true;
|
|
continue;
|
|
}
|
|
splitkerrors=splitkerrors || ni!=nc;
|
|
//--- check
|
|
if(ni==nc)
|
|
{
|
|
for(i=0;i<=nc-2;i++)
|
|
{
|
|
splitkerrors=splitkerrors || MathAbs(thresholds[i]-(c0*(i+1)-1+0.5))>100*CMath::m_machineepsilon;
|
|
}
|
|
cvr=-(nc*c0*MathLog((double)c0/(double)(c0+nc-1)));
|
|
splitkerrors=splitkerrors || MathAbs(cve-cvr)>100*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=((tieserrors || split2errors) || optimalsplitkerrors) || splitkerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING BASIC DATASET SUBROUTINES");
|
|
Print("TIES: ");
|
|
//--- check
|
|
if(!tieserrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("SPLIT-2: ");
|
|
//--- check
|
|
if(!split2errors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("OPTIMAL SPLIT-K: ");
|
|
//--- check
|
|
if(!optimalsplitkerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("SPLIT-K: ");
|
|
//--- check
|
|
if(!splitkerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 2D array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestBdSSUnit::Unset2D(CMatrixComplex &a)
|
|
{
|
|
//--- allocation
|
|
a.Resize(1,1);
|
|
//--- change value
|
|
a[0].Set(0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 1D array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestBdSSUnit::Unset1D(double &a[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,1);
|
|
//--- change value
|
|
a[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 1D array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestBdSSUnit::Unset1DI(int &a[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,1);
|
|
//--- change value
|
|
a[0]=CMath::RandomInteger(3)-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestBdSSUnit::TestSortResults(double &asorted[],int &p1[],
|
|
int &p2[],double &aoriginal[],
|
|
const int n,bool &waserrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double t=0;
|
|
//--- create arrays
|
|
double a2[];
|
|
int f[];
|
|
//--- allocation
|
|
ArrayResize(a2,n);
|
|
ArrayResize(f,n);
|
|
//--- is set ordered?
|
|
for(i=0;i<=n-2;i++)
|
|
waserrors=waserrors || asorted[i]>asorted[i+1];
|
|
//--- P1 correctness
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=waserrors || asorted[i]!=aoriginal[p1[i]];
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
f[i]=0;
|
|
for(i=0;i<=n-1;i++)
|
|
f[p1[i]]=f[p1[i]]+1;
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=waserrors || f[i]!=1;
|
|
//--- P2 correctness
|
|
for(i=0;i<=n-1;i++)
|
|
a2[i]=aoriginal[i];
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(p2[i]!=i)
|
|
{
|
|
t=a2[i];
|
|
a2[i]=a2[p2[i]];
|
|
a2[p2[i]]=t;
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
waserrors=waserrors || asorted[i]!=a2[i];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CDForest |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDForestUnit
|
|
{
|
|
public:
|
|
CTestDForestUnit(void);
|
|
~CTestDForestUnit(void);
|
|
|
|
static bool TestDForest(const bool silent);
|
|
|
|
private:
|
|
static void TestProcessing(bool &err);
|
|
static void BasicTest1(const int nvars,const int nclasses,const int passcount,bool &err);
|
|
static void BasicTest2(bool &err);
|
|
static void BasicTest3(bool &err);
|
|
static void BasicTest4(bool &err);
|
|
static void BasicTest5(bool &err);
|
|
static double RNormal(void);
|
|
static void RSphere(CMatrixDouble &xy,const int n,const int i);
|
|
static void UnsetDF(CDecisionForest &df);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestDForestUnit::CTestDForestUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestDForestUnit::~CTestDForestUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CDForest |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestDForestUnit::TestDForest(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int ncmax=0;
|
|
int nvmax=0;
|
|
int passcount=0;
|
|
int nvars=0;
|
|
int nclasses=0;
|
|
bool waserrors;
|
|
bool basicerrors;
|
|
bool procerrors;
|
|
//--- Primary settings
|
|
nvmax=4;
|
|
ncmax=3;
|
|
passcount=10;
|
|
basicerrors=false;
|
|
procerrors=false;
|
|
waserrors=false;
|
|
//--- Tests
|
|
TestProcessing(procerrors);
|
|
for(nvars=1;nvars<=nvmax;nvars++)
|
|
{
|
|
for(nclasses=1;nclasses<=ncmax;nclasses++)
|
|
BasicTest1(nvars,nclasses,passcount,basicerrors);
|
|
}
|
|
//--- function calls
|
|
BasicTest2(basicerrors);
|
|
BasicTest3(basicerrors);
|
|
BasicTest4(basicerrors);
|
|
BasicTest5(basicerrors);
|
|
//--- Final report
|
|
waserrors=basicerrors || procerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("RANDOM FOREST TEST");
|
|
Print("");
|
|
Print("TOTAL RESULTS: ");
|
|
//--- check
|
|
if(!waserrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* PROCESSING FUNCTIONS: ");
|
|
//--- check
|
|
if(!procerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* BASIC TESTS: ");
|
|
//--- check
|
|
if(!basicerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST SUMMARY: FAILED");
|
|
else
|
|
Print("TEST SUMMARY: PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Processing functions test |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDForestUnit::TestProcessing(bool &err)
|
|
{
|
|
//--- create variables
|
|
int nvars=0;
|
|
int nclasses=0;
|
|
int nsample=0;
|
|
int ntrees=0;
|
|
int nfeatures=0;
|
|
int flags=0;
|
|
int npoints=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
int i=0;
|
|
int j=0;
|
|
bool allsame;
|
|
int info=0;
|
|
double v=0;
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- create arrays
|
|
double x1[];
|
|
double x2[];
|
|
double y1[];
|
|
double y2[];
|
|
//--- objects of classes
|
|
CDecisionForest df1;
|
|
CDecisionForest df2;
|
|
CDFReport rep;
|
|
//--- initialization
|
|
passcount=100;
|
|
//--- Main cycle
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- initialize parameters
|
|
nvars=1+CMath::RandomInteger(5);
|
|
nclasses=1+CMath::RandomInteger(3);
|
|
ntrees=1+CMath::RandomInteger(4);
|
|
nfeatures=1+CMath::RandomInteger(nvars);
|
|
flags=0;
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
flags=flags+2;
|
|
//--- Initialize arrays and data
|
|
npoints=10+CMath::RandomInteger(50);
|
|
nsample=(int)(MathMax(10,CMath::RandomInteger(npoints)));
|
|
//--- allocation
|
|
ArrayResize(x1,nvars);
|
|
ArrayResize(x2,nvars);
|
|
ArrayResize(y1,nclasses);
|
|
ArrayResize(y2,nclasses);
|
|
xy.Resize(npoints,nvars+1);
|
|
//--- change values
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
for(j=0;j<=nvars-1;j++)
|
|
{
|
|
//--- check
|
|
if(j%2==0)
|
|
xy[i].Set(j,2*CMath::RandomReal()-1);
|
|
else
|
|
xy[i].Set(j,CMath::RandomInteger(2));
|
|
}
|
|
//--- check
|
|
if(nclasses==1)
|
|
xy[i].Set(nvars,2*CMath::RandomReal()-1);
|
|
else
|
|
xy[i].Set(nvars,CMath::RandomInteger(nclasses));
|
|
}
|
|
//--- create forest
|
|
CDForest::DFBuildInternal(xy,npoints,nvars,nclasses,ntrees,nsample,nfeatures,flags,info,df1,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- Same inputs leads to same outputs
|
|
for(i=0;i<=nvars-1;i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
for(i=0;i<=nclasses-1;i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function call
|
|
CDForest::DFProcess(df1,x1,y1);
|
|
//--- function call
|
|
CDForest::DFProcess(df1,x2,y2);
|
|
allsame=true;
|
|
//--- search errors
|
|
for(i=0;i<=nclasses-1;i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err || !allsame;
|
|
//--- Same inputs on original forest leads to same outputs
|
|
//--- on copy created using DFCopy
|
|
UnsetDF(df2);
|
|
//--- function call
|
|
CDForest::DFCopy(df1,df2);
|
|
for(i=0;i<=nvars-1;i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
for(i=0;i<=nclasses-1;i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function call
|
|
CDForest::DFProcess(df1,x1,y1);
|
|
//--- function call
|
|
CDForest::DFProcess(df2,x2,y2);
|
|
allsame=true;
|
|
//--- search errors
|
|
for(i=0;i<=nclasses-1;i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err || !allsame;
|
|
//--- Same inputs on original forest leads to same outputs
|
|
//--- on copy created using DFSerialize
|
|
UnsetDF(df2);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CDForest::DFAlloc(_local_serializer,df1);
|
|
_local_serializer.SStart_Str();
|
|
CDForest::DFSerialize(_local_serializer,df1);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CDForest::DFUnserialize(_local_serializer,df2);
|
|
_local_serializer.Stop();
|
|
}
|
|
for(i=0;i<=nvars-1;i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
for(i=0;i<=nclasses-1;i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function call
|
|
CDForest::DFProcess(df1,x1,y1);
|
|
//--- function call
|
|
CDForest::DFProcess(df2,x2,y2);
|
|
allsame=true;
|
|
//--- search errors
|
|
for(i=0;i<=nclasses-1;i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err || !allsame;
|
|
//--- Normalization properties
|
|
if(nclasses>1)
|
|
{
|
|
for(i=0;i<=nvars-1;i++)
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CDForest::DFProcess(df1,x1,y1);
|
|
v=0;
|
|
//--- search errors
|
|
for(i=0;i<=nclasses-1;i++)
|
|
{
|
|
v=v+y1[i];
|
|
err=err || y1[i]<0.0;
|
|
}
|
|
err=err || MathAbs(v-1)>1000*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic test: one-tree forest built using full sample must |
|
|
//| remember all the training cases |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDForestUnit::BasicTest1(const int nvars,const int nclasses,
|
|
const int passcount,bool &err)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int npoints=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double s=0;
|
|
int info=0;
|
|
bool hassame;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CDecisionForest df;
|
|
CDFReport rep;
|
|
//--- check
|
|
if(nclasses==1)
|
|
{
|
|
//--- only classification tasks
|
|
return;
|
|
}
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- select number of points
|
|
if(pass<=3 && passcount>3)
|
|
npoints=pass;
|
|
else
|
|
npoints=100+CMath::RandomInteger(100);
|
|
//--- Prepare task
|
|
xy.Resize(npoints,nvars+1);
|
|
ArrayResize(x,nvars);
|
|
ArrayResize(y,nclasses);
|
|
//--- change values
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
for(j=0;j<=nvars-1;j++)
|
|
xy[i].Set(j,2*CMath::RandomReal()-1);
|
|
xy[i].Set(nvars,CMath::RandomInteger(nclasses));
|
|
}
|
|
//--- Test
|
|
CDForest::DFBuildInternal(xy,npoints,nvars,nclasses,1,npoints,1,1,info,df,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
x[i_]=xy[i][i_];
|
|
//--- function call
|
|
CDForest::DFProcess(df,x,y);
|
|
s=0;
|
|
for(j=0;j<=nclasses-1;j++)
|
|
{
|
|
//--- check
|
|
if(y[j]<0.0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
s=s+y[j];
|
|
}
|
|
//--- check
|
|
if(MathAbs(s-1)>1000*CMath::m_machineepsilon)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(MathAbs(y[(int)MathRound(xy[i][nvars])]-1)>1000*CMath::m_machineepsilon)
|
|
{
|
|
//--- not an error if there exists such K,J that XY[k].Set(j,XY[I,J]
|
|
//--- (may be we just can't distinguish two tied values).
|
|
//--- definitely error otherwise.
|
|
hassame=false;
|
|
for(k=0;k<=npoints-1;k++)
|
|
{
|
|
//--- check
|
|
if(k!=i)
|
|
{
|
|
for(j=0;j<=nvars-1;j++)
|
|
{
|
|
//--- check
|
|
if(xy[k][j]==xy[i][j])
|
|
hassame=true;
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(!hassame)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic test: tests generalization ability on a simple noisy |
|
|
//| classification task: |
|
|
//| * 0<x<1 - P(class=0)=1 |
|
|
//| * 1<x<2 - P(class=0)=2-x |
|
|
//| * 2<x<3 - P(class=0)=0 |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDForestUnit::BasicTest2(bool &err)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int passcount=0;
|
|
int npoints=0;
|
|
int ntrees=0;
|
|
int i=0;
|
|
int j=0;
|
|
double s=0;
|
|
int info=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CDecisionForest df;
|
|
CDFReport rep;
|
|
//--- initialization
|
|
passcount=1;
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- select npoints and ntrees
|
|
npoints=3000;
|
|
ntrees=50;
|
|
//--- Prepare task
|
|
xy.Resize(npoints,2);
|
|
ArrayResize(x,1);
|
|
ArrayResize(y,2);
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
xy[i].Set(0,3*CMath::RandomReal());
|
|
//--- check
|
|
if(xy[i][0]<=1.0)
|
|
xy[i].Set(1,0);
|
|
else
|
|
{
|
|
//--- check
|
|
if(xy[i][0]<=2.0)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()<xy[i][0]-1)
|
|
xy[i].Set(1,1);
|
|
else
|
|
xy[i].Set(1,0);
|
|
}
|
|
else
|
|
xy[i].Set(1,1);
|
|
}
|
|
}
|
|
//--- Test
|
|
CDForest::DFBuildInternal(xy,npoints,1,2,ntrees,(int)MathRound(0.05*npoints),1,0,info,df,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
x[0]=0.0;
|
|
//--- cycle
|
|
while(x[0]<=3.0)
|
|
{
|
|
//--- function call
|
|
CDForest::DFProcess(df,x,y);
|
|
//--- Test for basic properties
|
|
s=0;
|
|
for(j=0;j<=1;j++)
|
|
{
|
|
//--- check
|
|
if(y[j]<0.0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
s=s+y[j];
|
|
}
|
|
//--- check
|
|
if(MathAbs(s-1)>1000*CMath::m_machineepsilon)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- test for good correlation with results
|
|
if(x[0]<1.0)
|
|
err=err || y[0]<0.8;
|
|
//--- check
|
|
if(x[0]>=1.0 && x[0]<=2.0)
|
|
err=err || MathAbs(y[1]-(x[0]-1))>0.5;
|
|
//--- check
|
|
if(x[0]>2.0)
|
|
err=err || y[1]<0.8;
|
|
x[0]=x[0]+0.01;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic test: tests generalization ability on a simple |
|
|
//| classification task (no noise): |
|
|
//| * ||x||<1,||y||<1 |
|
|
//| * x^2+y^2<=0.25 - P(class=0)=1 |
|
|
//| * x^2+y^2>0.25 - P(class=0)=0 |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDForestUnit::BasicTest3(bool &err)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int passcount=0;
|
|
int npoints=0;
|
|
int ntrees=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double s=0;
|
|
int info=0;
|
|
int testgridsize=0;
|
|
double r=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CDecisionForest df;
|
|
CDFReport rep;
|
|
//--- initialization
|
|
passcount=1;
|
|
testgridsize=50;
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- select npoints and ntrees
|
|
npoints=2000;
|
|
ntrees=100;
|
|
//--- Prepare task
|
|
xy.Resize(npoints,3);
|
|
ArrayResize(x,2);
|
|
ArrayResize(y,2);
|
|
//--- change values
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
xy[i].Set(0,2*CMath::RandomReal()-1);
|
|
xy[i].Set(1,2*CMath::RandomReal()-1);
|
|
//--- check
|
|
if(CMath::Sqr(xy[i][0])+CMath::Sqr(xy[i][1])<=0.25)
|
|
xy[i].Set(2,0);
|
|
else
|
|
xy[i].Set(2,1);
|
|
}
|
|
//--- Test
|
|
CDForest::DFBuildInternal(xy,npoints,2,2,ntrees,(int)MathRound(0.1*npoints),1,0,info,df,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- calculation
|
|
for(i=-(testgridsize/2);i<=testgridsize/2;i++)
|
|
{
|
|
for(j=-(testgridsize/2);j<=testgridsize/2;j++)
|
|
{
|
|
x[0]=(double)i/(double)(testgridsize/2);
|
|
x[1]=(double)j/(double)(testgridsize/2);
|
|
//--- function call
|
|
CDForest::DFProcess(df,x,y);
|
|
//--- Test for basic properties
|
|
s=0;
|
|
for(k=0;k<=1;k++)
|
|
{
|
|
//--- check
|
|
if(y[k]<0.0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
s=s+y[k];
|
|
}
|
|
//--- check
|
|
if(MathAbs(s-1)>1000*CMath::m_machineepsilon)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- test for good correlation with results
|
|
r=MathSqrt(CMath::Sqr(x[0])+CMath::Sqr(x[1]));
|
|
//--- check
|
|
if(r<0.5*0.5)
|
|
err=err || y[0]<0.6;
|
|
//--- check
|
|
if(r>0.5*1.5)
|
|
err=err || y[1]<0.6;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic test: simple regression task without noise: |
|
|
//| * ||x||<1,||y||<1 |
|
|
//| * F(x,y)=x^2+y |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDForestUnit::BasicTest4(bool &err)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int passcount=0;
|
|
int npoints=0;
|
|
int ntrees=0;
|
|
int ns=0;
|
|
int strongc=0;
|
|
int i=0;
|
|
int j=0;
|
|
int info=0;
|
|
int testgridsize=0;
|
|
double maxerr=0;
|
|
double maxerr2=0;
|
|
double avgerr=0;
|
|
double avgerr2=0;
|
|
int cnt=0;
|
|
double ey=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CDecisionForest df;
|
|
CDecisionForest df2;
|
|
CDFReport rep;
|
|
CDFReport rep2;
|
|
//--- initialization
|
|
passcount=1;
|
|
testgridsize=50;
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- select npoints and ntrees
|
|
npoints=5000;
|
|
ntrees=100;
|
|
ns=(int)MathRound(0.1*npoints);
|
|
strongc=1;
|
|
//--- Prepare task
|
|
xy.Resize(npoints,3);
|
|
ArrayResize(x,2);
|
|
ArrayResize(y,1);
|
|
//--- change values
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
xy[i].Set(0,2*CMath::RandomReal()-1);
|
|
xy[i].Set(1,2*CMath::RandomReal()-1);
|
|
xy[i].Set(2,CMath::Sqr(xy[i][0])+xy[i][1]);
|
|
}
|
|
//--- Test
|
|
CDForest::DFBuildInternal(xy,npoints,2,1,ntrees,ns,1,0,info,df,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- function call
|
|
CDForest::DFBuildInternal(xy,npoints,2,1,ntrees,ns,1,strongc,info,df2,rep2);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- change values
|
|
maxerr=0;
|
|
maxerr2=0;
|
|
avgerr=0;
|
|
avgerr2=0;
|
|
cnt=0;
|
|
//--- calculation
|
|
for(i=(int)MathRound(-(0.7*testgridsize/2));i<=(int)MathRound(0.7*testgridsize/2);i++)
|
|
{
|
|
for(j=(int)MathRound(-(0.7*testgridsize/2));j<=(int)MathRound(0.7*testgridsize/2);j++)
|
|
{
|
|
x[0]=(double)i/(double)(testgridsize/2);
|
|
x[1]=(double)j/(double)(testgridsize/2);
|
|
ey=CMath::Sqr(x[0])+x[1];
|
|
//--- function call
|
|
CDForest::DFProcess(df,x,y);
|
|
maxerr=MathMax(maxerr,MathAbs(y[0]-ey));
|
|
avgerr=avgerr+MathAbs(y[0]-ey);
|
|
//--- function call
|
|
CDForest::DFProcess(df2,x,y);
|
|
maxerr2=MathMax(maxerr2,MathAbs(y[0]-ey));
|
|
avgerr2=avgerr2+MathAbs(y[0]-ey);
|
|
cnt=cnt+1;
|
|
}
|
|
}
|
|
//--- search errors
|
|
avgerr=avgerr/cnt;
|
|
avgerr2=avgerr2/cnt;
|
|
err=err || maxerr>0.2;
|
|
err=err || maxerr2>0.2;
|
|
err=err || avgerr>0.1;
|
|
err=err || avgerr2>0.1;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Basic test: extended variable selection leads to better results. |
|
|
//| Next task CAN be solved without EVS but it is very unlikely. |
|
|
//| With EVS it can be easily and exactly solved. |
|
|
//| Task matrix: |
|
|
//| 1 0 0 0 ... 0 0 |
|
|
//| 0 1 0 0 ... 0 1 |
|
|
//| 0 0 1 0 ... 0 2 |
|
|
//| 0 0 0 1 ... 0 3 |
|
|
//| 0 0 0 0 ... 1 N-1 |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDForestUnit::BasicTest5(bool &err)
|
|
{
|
|
//--- create variables
|
|
int nvars=0;
|
|
int npoints=0;
|
|
int nfeatures=0;
|
|
int nsample=0;
|
|
int ntrees=0;
|
|
int evs=0;
|
|
int i=0;
|
|
int j=0;
|
|
bool eflag;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CDecisionForest df;
|
|
CDFReport rep;
|
|
//--- select npoints and ntrees
|
|
npoints=50;
|
|
nvars=npoints;
|
|
ntrees=1;
|
|
nsample=npoints;
|
|
evs=2;
|
|
nfeatures=1;
|
|
//--- Prepare task
|
|
xy.Resize(npoints,nvars+1);
|
|
ArrayResize(x,nvars);
|
|
ArrayResize(y,1);
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
for(j=0;j<=nvars-1;j++)
|
|
{
|
|
xy[i].Set(j,0);
|
|
}
|
|
xy[i].Set(i,1);
|
|
xy[i].Set(nvars,i);
|
|
}
|
|
//--- Without EVS
|
|
CDForest::DFBuildInternal(xy,npoints,nvars,1,ntrees,nsample,nfeatures,0,info,df,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- calculation
|
|
eflag=false;
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
x[i_]=xy[i][i_];
|
|
//--- function call
|
|
CDForest::DFProcess(df,x,y);
|
|
//--- check
|
|
if(MathAbs(y[0]-xy[i][nvars])>1000*CMath::m_machineepsilon)
|
|
eflag=true;
|
|
}
|
|
//--- check
|
|
if(!eflag)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- With EVS
|
|
CDForest::DFBuildInternal(xy,npoints,nvars,1,ntrees,nsample,nfeatures,evs,info,df,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- calculation
|
|
eflag=false;
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
{
|
|
x[i_]=xy[i][i_];
|
|
}
|
|
//--- function call
|
|
CDForest::DFProcess(df,x,y);
|
|
//--- check
|
|
if(MathAbs(y[0]-xy[i][nvars])>1000*CMath::m_machineepsilon)
|
|
{
|
|
eflag=true;
|
|
}
|
|
}
|
|
//--- check
|
|
if(eflag)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random normal number |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestDForestUnit::RNormal(void)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double u=0;
|
|
double v=0;
|
|
double s=0;
|
|
double x1=0;
|
|
double x2=0;
|
|
//--- calculation
|
|
while(true)
|
|
{
|
|
u=2*CMath::RandomReal()-1;
|
|
v=2*CMath::RandomReal()-1;
|
|
s=CMath::Sqr(u)+CMath::Sqr(v);
|
|
//--- check
|
|
if(s>0.0 && s<1.0)
|
|
{
|
|
s=MathSqrt(-(2*MathLog(s)/s));
|
|
x1=u*s;
|
|
x2=v*s;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
}
|
|
//--- check
|
|
if(x1!=0)
|
|
result=x1;
|
|
else
|
|
result=x2;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random point from sphere |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDForestUnit::RSphere(CMatrixDouble &xy,const int n,
|
|
const int i)
|
|
{
|
|
//--- create variables
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- change values
|
|
for(j=0;j<=n-1;j++)
|
|
xy[i].Set(j,RNormal());
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=xy[i][i_]*xy[i][i_];
|
|
//--- calculation
|
|
v=CMath::RandomReal()/MathSqrt(v);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xy[i].Set(i_,v*xy[i][i_]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets DF |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDForestUnit::UnsetDF(CDecisionForest &df)
|
|
{
|
|
//--- create a variable
|
|
int info=0;
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- object of class
|
|
CDFReport rep;
|
|
//--- allocation
|
|
xy.Resize(1,2);
|
|
//--- change values
|
|
xy[0].Set(0,0);
|
|
xy[0].Set(1,0);
|
|
//--- function call
|
|
CDForest::DFBuildInternal(xy,1,1,1,1,1,1,0,info,df,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CBlas |
|
|
//+------------------------------------------------------------------+
|
|
class CTestBlasUnit
|
|
{
|
|
private:
|
|
//--- private method
|
|
static void NaiveMatrixMatrixMultiply(CMatrixDouble &a,const int ai1,const int ai2,const int aj1,const int aj2,const bool transa,CMatrixDouble &b,const int bi1,const int bi2,const int bj1,const int bj2,const bool transb,const double alpha,CMatrixDouble &c,const int ci1,const int ci2,const int cj1,const int cj2,const double beta);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestBlasUnit(void);
|
|
~CTestBlasUnit(void);
|
|
//--- public method
|
|
static bool TestBlas(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestBlasUnit::CTestBlasUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestBlasUnit::~CTestBlasUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CBlas |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestBlasUnit::TestBlas(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int passcount=0;
|
|
int n=0;
|
|
int i=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
int j=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
int l=0;
|
|
int k=0;
|
|
int r=0;
|
|
int i3=0;
|
|
int j3=0;
|
|
int col1=0;
|
|
int col2=0;
|
|
int row1=0;
|
|
int row2=0;
|
|
double err=0;
|
|
double e1=0;
|
|
double e2=0;
|
|
double e3=0;
|
|
double v=0;
|
|
double scl1=0;
|
|
double scl2=0;
|
|
double scl3=0;
|
|
bool was1;
|
|
bool was2;
|
|
bool trans1;
|
|
bool trans2;
|
|
double threshold=0;
|
|
bool n2errors;
|
|
bool hsnerrors;
|
|
bool amaxerrors;
|
|
bool mverrors;
|
|
bool iterrors;
|
|
bool cterrors;
|
|
bool mmerrors;
|
|
bool waserrors;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x1[];
|
|
double x2[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble b;
|
|
CMatrixDouble c1;
|
|
CMatrixDouble c2;
|
|
//--- initialization
|
|
n2errors=false;
|
|
amaxerrors=false;
|
|
hsnerrors=false;
|
|
mverrors=false;
|
|
iterrors=false;
|
|
cterrors=false;
|
|
mmerrors=false;
|
|
waserrors=false;
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
//--- Test Norm2
|
|
passcount=1000;
|
|
e1=0;
|
|
e2=0;
|
|
e3=0;
|
|
scl2=0.5*CMath::m_maxrealnumber;
|
|
scl3=2*CMath::m_minrealnumber;
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
n=1+CMath::RandomInteger(1000);
|
|
i1=CMath::RandomInteger(10);
|
|
i2=n+i1-1;
|
|
//--- allocation
|
|
ArrayResize(x1,i2+1);
|
|
ArrayResize(x2,i2+1);
|
|
//--- change values
|
|
for(i=i1;i<=i2;i++)
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
v=0;
|
|
for(i=i1;i<=i2;i++)
|
|
v=v+CMath::Sqr(x1[i]);
|
|
v=MathSqrt(v);
|
|
//--- calculation
|
|
e1=MathMax(e1,MathAbs(v-CBlas::VectorNorm2(x1,i1,i2)));
|
|
for(i=i1;i<=i2;i++)
|
|
x2[i]=scl2*x1[i];
|
|
//--- calculation
|
|
e2=MathMax(e2,MathAbs(v*scl2-CBlas::VectorNorm2(x2,i1,i2)));
|
|
for(i=i1;i<=i2;i++)
|
|
x2[i]=scl3*x1[i];
|
|
//--- calculation
|
|
e3=MathMax(e3,MathAbs(v*scl3-CBlas::VectorNorm2(x2,i1,i2)));
|
|
}
|
|
e2=e2/scl2;
|
|
e3=e3/scl3;
|
|
//--- search errors
|
|
n2errors=(e1>=threshold || e2>=threshold) || e3>=threshold;
|
|
//--- Testing VectorAbsMax,Column/Row AbsMax
|
|
ArrayResize(x1,6);
|
|
x1[1]=2.0;
|
|
x1[2]=0.2;
|
|
x1[3]=-1.3;
|
|
x1[4]=0.7;
|
|
x1[5]=-3.0;
|
|
//--- search errors
|
|
amaxerrors=(CBlas::VectorIdxAbsMax(x1,1,5)!=5 || CBlas::VectorIdxAbsMax(x1,1,4)!=1) || CBlas::VectorIdxAbsMax(x1,2,4)!=3;
|
|
n=30;
|
|
//--- allocation
|
|
ArrayResize(x1,n+1);
|
|
a.Resize(n+1,n+1);
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- calculation
|
|
was1=false;
|
|
was2=false;
|
|
for(pass=1;pass<=1000;pass++)
|
|
{
|
|
//--- change values
|
|
j=1+CMath::RandomInteger(n);
|
|
i1=1+CMath::RandomInteger(n);
|
|
i2=i1+CMath::RandomInteger(n+1-i1);
|
|
for(i_=i1;i_<=i2;i_++)
|
|
x1[i_]=a[i_][j];
|
|
//--- check
|
|
if(CBlas::VectorIdxAbsMax(x1,i1,i2)!=CBlas::ColumnIdxAbsMax(a,i1,i2,j))
|
|
was1=true;
|
|
//--- change values
|
|
i=1+CMath::RandomInteger(n);
|
|
j1=1+CMath::RandomInteger(n);
|
|
j2=j1+CMath::RandomInteger(n+1-j1);
|
|
for(i_=j1;i_<=j2;i_++)
|
|
x1[i_]=a[i][i_];
|
|
//--- check
|
|
if(CBlas::VectorIdxAbsMax(x1,j1,j2)!=CBlas::RowIdxAbsMax(a,j1,j2,i))
|
|
was2=true;
|
|
}
|
|
//--- search errors
|
|
amaxerrors=(amaxerrors || was1) || was2;
|
|
//--- Testing upper Hessenberg 1-norm
|
|
a.Resize(4,4);
|
|
ArrayResize(x1,4);
|
|
a[1].Set(1,2);
|
|
a[1].Set(2,3);
|
|
a[1].Set(3,1);
|
|
a[2].Set(1,4);
|
|
a[2].Set(2,-5);
|
|
a[2].Set(3,8);
|
|
a[3].Set(1,99);
|
|
a[3].Set(2,3);
|
|
a[3].Set(3,1);
|
|
//--- search errors
|
|
hsnerrors=MathAbs(CBlas::UpperHessenberg1Norm(a,1,3,1,3,x1)-11)>threshold;
|
|
//--- Testing MatrixVectorMultiply
|
|
a.Resize(4,6);
|
|
ArrayResize(x1,4);
|
|
ArrayResize(x2,3);
|
|
a[2].Set(3,2);
|
|
a[2].Set(4,-1);
|
|
a[2].Set(5,-1);
|
|
a[3].Set(3,1);
|
|
a[3].Set(4,-2);
|
|
a[3].Set(5,2);
|
|
x1[1]=1;
|
|
x1[2]=2;
|
|
x1[3]=1;
|
|
x2[1]=-1;
|
|
x2[2]=-1;
|
|
//--- function calls
|
|
CBlas::MatrixVectorMultiply(a,2,3,3,5,false,x1,1,3,1.0,x2,1,2,1.0);
|
|
CBlas::MatrixVectorMultiply(a,2,3,3,5,true,x2,1,2,1.0,x1,1,3,1.0);
|
|
//--- calculation
|
|
e1=MathAbs(x1[1]+5)+MathAbs(x1[2]-8)+MathAbs(x1[3]+1)+MathAbs(x2[1]+2)+MathAbs(x2[2]+2);
|
|
x1[1]=1;
|
|
x1[2]=2;
|
|
x1[3]=1;
|
|
x2[1]=-1;
|
|
x2[2]=-1;
|
|
//--- function calls
|
|
CBlas::MatrixVectorMultiply(a,2,3,3,5,false,x1,1,3,1.0,x2,1,2,0.0);
|
|
CBlas::MatrixVectorMultiply(a,2,3,3,5,true,x2,1,2,1.0,x1,1,3,0.0);
|
|
//--- calculation
|
|
e2=MathAbs(x1[1]+3)+MathAbs(x1[2]-3)+MathAbs(x1[3]+1)+MathAbs(x2[1]+1)+MathAbs(x2[2]+1);
|
|
mverrors=e1+e2>=threshold;
|
|
//--- testing inplace transpose
|
|
n=10;
|
|
a.Resize(n+1,n+1);
|
|
b.Resize(n+1,n+1);
|
|
ArrayResize(x1,n);
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
a[i].Set(j,CMath::RandomReal());
|
|
}
|
|
//--- calculation
|
|
passcount=10000;
|
|
was1=false;
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- change values
|
|
i1=1+CMath::RandomInteger(n);
|
|
i2=i1+CMath::RandomInteger(n-i1+1);
|
|
j1=1+CMath::RandomInteger(n-(i2-i1));
|
|
j2=j1+(i2-i1);
|
|
//--- function calls
|
|
CBlas::CopyMatrix(a,i1,i2,j1,j2,b,i1,i2,j1,j2);
|
|
CBlas::InplaceTranspose(b,i1,i2,j1,j2,x1);
|
|
for(i=i1;i<=i2;i++)
|
|
{
|
|
for(j=j1;j<=j2;j++)
|
|
{
|
|
//--- check
|
|
if(a[i][j]!=b[i1+(j-j1)][j1+(i-i1)])
|
|
was1=true;
|
|
}
|
|
}
|
|
}
|
|
//--- change value
|
|
iterrors=was1;
|
|
//--- testing copy and transpose
|
|
n=10;
|
|
a.Resize(n+1,n+1);
|
|
b.Resize(n+1,n+1);
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
a[i].Set(j,CMath::RandomReal());
|
|
}
|
|
//--- calculation
|
|
passcount=10000;
|
|
was1=false;
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- change values
|
|
i1=1+CMath::RandomInteger(n);
|
|
i2=i1+CMath::RandomInteger(n-i1+1);
|
|
j1=1+CMath::RandomInteger(n);
|
|
j2=j1+CMath::RandomInteger(n-j1+1);
|
|
//--- function call
|
|
CBlas::CopyAndTranspose(a,i1,i2,j1,j2,b,j1,j2,i1,i2);
|
|
for(i=i1;i<=i2;i++)
|
|
{
|
|
for(j=j1;j<=j2;j++)
|
|
{
|
|
//--- check
|
|
if(a[i][j]!=b[j][i])
|
|
was1=true;
|
|
}
|
|
}
|
|
}
|
|
//--- change values
|
|
cterrors=was1;
|
|
//--- Testing MatrixMatrixMultiply
|
|
n=10;
|
|
a.Resize(2*n+1,2*n+1);
|
|
b.Resize(2*n+1,2*n+1);
|
|
c1.Resize(2*n+1,2*n+1);
|
|
c2.Resize(2*n+1,2*n+1);
|
|
ArrayResize(x1,n+1);
|
|
ArrayResize(x2,n+1);
|
|
for(i=1;i<=2*n;i++)
|
|
{
|
|
for(j=1;j<=2*n;j++)
|
|
{
|
|
a[i].Set(j,CMath::RandomReal());
|
|
b[i].Set(j,CMath::RandomReal());
|
|
}
|
|
}
|
|
//--- calculation
|
|
passcount=1000;
|
|
was1=false;
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(i=1;i<=2*n;i++)
|
|
{
|
|
for(j=1;j<=2*n;j++)
|
|
{
|
|
c1[i].Set(j,2.1*i+3.1*j);
|
|
c2[i].Set(j,c1[i][j]);
|
|
}
|
|
}
|
|
//--- change values
|
|
l=1+CMath::RandomInteger(n);
|
|
k=1+CMath::RandomInteger(n);
|
|
r=1+CMath::RandomInteger(n);
|
|
i1=1+CMath::RandomInteger(n);
|
|
j1=1+CMath::RandomInteger(n);
|
|
i2=1+CMath::RandomInteger(n);
|
|
j2=1+CMath::RandomInteger(n);
|
|
i3=1+CMath::RandomInteger(n);
|
|
j3=1+CMath::RandomInteger(n);
|
|
trans1=CMath::RandomReal()>0.5;
|
|
trans2=CMath::RandomReal()>0.5;
|
|
//--- check
|
|
if(trans1)
|
|
{
|
|
col1=l;
|
|
row1=k;
|
|
}
|
|
else
|
|
{
|
|
col1=k;
|
|
row1=l;
|
|
}
|
|
//--- check
|
|
if(trans2)
|
|
{
|
|
col2=k;
|
|
row2=r;
|
|
}
|
|
else
|
|
{
|
|
col2=r;
|
|
row2=k;
|
|
}
|
|
//--- change values
|
|
scl1=CMath::RandomReal();
|
|
scl2=CMath::RandomReal();
|
|
//--- function calls
|
|
CBlas::MatrixMatrixMultiply(a,i1,i1+row1-1,j1,j1+col1-1,trans1,b,i2,i2+row2-1,j2,j2+col2-1,trans2,scl1,c1,i3,i3+l-1,j3,j3+r-1,scl2,x1);
|
|
NaiveMatrixMatrixMultiply(a,i1,i1+row1-1,j1,j1+col1-1,trans1,b,i2,i2+row2-1,j2,j2+col2-1,trans2,scl1,c2,i3,i3+l-1,j3,j3+r-1,scl2);
|
|
//--- search errors
|
|
err=0;
|
|
for(i=1;i<=l;i++)
|
|
{
|
|
for(j=1;j<=r;j++)
|
|
err=MathMax(err,MathAbs(c1[i3+i-1][j3+j-1]-c2[i3+i-1][j3+j-1]));
|
|
}
|
|
//--- check
|
|
if(err>threshold)
|
|
{
|
|
was1=true;
|
|
break;
|
|
}
|
|
}
|
|
//--- change value
|
|
mmerrors=was1;
|
|
//--- report
|
|
waserrors=(((((n2errors || amaxerrors) || hsnerrors) || mverrors) || iterrors) || cterrors) || mmerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING BLAS");
|
|
Print("VectorNorm2: ");
|
|
//--- check
|
|
if(n2errors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("AbsMax (vector/row/column): ");
|
|
//--- check
|
|
if(amaxerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("UpperHessenberg1Norm: ");
|
|
//--- check
|
|
if(hsnerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("MatrixVectorMultiply: ");
|
|
//--- check
|
|
if(mverrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("InplaceTranspose: ");
|
|
//--- check
|
|
if(iterrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("CopyAndTranspose: ");
|
|
//--- check
|
|
if(cterrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("MatrixMatrixMultiply: ");
|
|
//--- check
|
|
if(mmerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestBlasUnit::NaiveMatrixMatrixMultiply(CMatrixDouble &a,
|
|
const int ai1,
|
|
const int ai2,
|
|
const int aj1,
|
|
const int aj2,
|
|
const bool transa,
|
|
CMatrixDouble &b,
|
|
const int bi1,
|
|
const int bi2,
|
|
const int bj1,
|
|
const int bj2,
|
|
const bool transb,
|
|
const double alpha,
|
|
CMatrixDouble &c,
|
|
const int ci1,
|
|
const int ci2,
|
|
const int cj1,
|
|
const int cj2,
|
|
const double beta)
|
|
{
|
|
//--- create variables
|
|
int arows=0;
|
|
int acols=0;
|
|
int brows=0;
|
|
int bcols=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int l=0;
|
|
int r=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double x1[];
|
|
double x2[];
|
|
//--- Setup
|
|
if(!transa)
|
|
{
|
|
arows=ai2-ai1+1;
|
|
acols=aj2-aj1+1;
|
|
}
|
|
else
|
|
{
|
|
arows=aj2-aj1+1;
|
|
acols=ai2-ai1+1;
|
|
}
|
|
//--- check
|
|
if(!transb)
|
|
{
|
|
brows=bi2-bi1+1;
|
|
bcols=bj2-bj1+1;
|
|
}
|
|
else
|
|
{
|
|
brows=bj2-bj1+1;
|
|
bcols=bi2-bi1+1;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(acols==brows,"NaiveMatrixMatrixMultiply: incorrect matrix sizes!"))
|
|
return;
|
|
//--- check
|
|
if(((arows<=0 || acols<=0) || brows<=0) || bcols<=0)
|
|
return;
|
|
//--- change values
|
|
l=arows;
|
|
r=bcols;
|
|
k=acols;
|
|
//--- allocation
|
|
ArrayResize(x1,k+1);
|
|
ArrayResize(x2,k+1);
|
|
//--- calculation
|
|
for(i=1;i<=l;i++)
|
|
{
|
|
for(j=1;j<=r;j++)
|
|
{
|
|
//--- check
|
|
if(!transa)
|
|
{
|
|
//--- check
|
|
if(!transb)
|
|
{
|
|
//--- change values
|
|
i1_=aj1-bi1;
|
|
v=0.0;
|
|
for(i_=bi1;i_<=bi2;i_++)
|
|
v+=b[i_][bj1+j-1]*a[ai1+i-1][i_+i1_];
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
i1_=aj1-bj1;
|
|
v=0.0;
|
|
for(i_=bj1;i_<=bj2;i_++)
|
|
v+=b[bi1+j-1][i_]*a[ai1+i-1][i_+i1_];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(!transb)
|
|
{
|
|
//--- change values
|
|
i1_=ai1-bi1;
|
|
v=0.0;
|
|
for(i_=bi1;i_<=bi2;i_++)
|
|
v+=b[i_][bj1+j-1]*a[i_+i1_][aj1+i-1];
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
i1_=ai1-bj1;
|
|
v=0.0;
|
|
for(i_=bj1;i_<=bj2;i_++)
|
|
v+=b[bi1+j-1][i_]*a[i_+i1_][aj1+i-1];
|
|
}
|
|
}
|
|
//--- check
|
|
if(beta==0.0)
|
|
c[ci1+i-1].Set(cj1+j-1,alpha*v);
|
|
else
|
|
c[ci1+i-1].Set(cj1+j-1,beta*c[ci1+i-1][cj1+j-1]+alpha*v);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CKMeans |
|
|
//+------------------------------------------------------------------+
|
|
class CTestKMeansUnit
|
|
{
|
|
public:
|
|
CTestKMeansUnit(void);
|
|
~CTestKMeansUnit(void);
|
|
|
|
static bool TestKMeans(const bool silent);
|
|
|
|
private:
|
|
static void SimpleTest1(const int nvars,const int nc,int passcount,bool &converrors,bool &othererrors,bool &simpleerrors);
|
|
static void RestartsTest(bool &converrors,bool &restartserrors);
|
|
static double RNormal(void);
|
|
static void RSphere(CMatrixDouble &xy,const int n,const int i);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestKMeansUnit::CTestKMeansUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestKMeansUnit::~CTestKMeansUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CKMeans |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestKMeansUnit::TestKMeans(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int nf=0;
|
|
int maxnf=0;
|
|
int nc=0;
|
|
int maxnc=0;
|
|
int passcount=0;
|
|
bool waserrors;
|
|
bool converrors;
|
|
bool simpleerrors;
|
|
bool complexerrors;
|
|
bool othererrors;
|
|
bool restartserrors;
|
|
//--- Primary settings
|
|
maxnf=5;
|
|
maxnc=5;
|
|
passcount=10;
|
|
waserrors=false;
|
|
converrors=false;
|
|
othererrors=false;
|
|
simpleerrors=false;
|
|
complexerrors=false;
|
|
restartserrors=false;
|
|
//--- calculation
|
|
for(nf=1;nf<=maxnf;nf++)
|
|
{
|
|
for(nc=1;nc<=maxnc;nc++)
|
|
SimpleTest1(nf,nc,passcount,converrors,othererrors,simpleerrors);
|
|
}
|
|
RestartsTest(converrors,restartserrors);
|
|
//--- Final report
|
|
waserrors=(((converrors || othererrors) || simpleerrors) || complexerrors) || restartserrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("K-MEANS TEST");
|
|
Print("TOTAL RESULTS: ");
|
|
//--- check
|
|
if(!waserrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* CONVERGENCE: ");
|
|
//--- check
|
|
if(!converrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* SIMPLE TASKS: ");
|
|
//--- check
|
|
if(!simpleerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* COMPLEX TASKS: ");
|
|
//--- check
|
|
if(!complexerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* OTHER PROPERTIES: ");
|
|
//--- check
|
|
if(!othererrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* RESTARTS PROPERTIES: ");
|
|
//--- check
|
|
if(!restartserrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST SUMMARY: FAILED");
|
|
else
|
|
Print("TEST SUMMARY: PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Simple test 1: ellipsoid in NF-dimensional space. |
|
|
//| compare k-means centers with random centers |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestKMeansUnit::SimpleTest1(const int nvars,const int nc,
|
|
int passcount,bool &converrors,
|
|
bool &othererrors,bool &simpleerrors)
|
|
{
|
|
//--- create variables
|
|
int npoints=0;
|
|
int majoraxis=0;
|
|
double v=0;
|
|
int i=0;
|
|
int j=0;
|
|
int info=0;
|
|
int pass=0;
|
|
int restarts=0;
|
|
double ekmeans=0;
|
|
double erandom=0;
|
|
double dclosest=0;
|
|
int cclosest=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double tmp[];
|
|
int xyc[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
CMatrixDouble c;
|
|
//--- initialization
|
|
npoints=nc*100;
|
|
restarts=5;
|
|
passcount=10;
|
|
//--- allocation
|
|
ArrayResize(tmp,nvars);
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Fill
|
|
xy.Resize(npoints,nvars);
|
|
majoraxis=CMath::RandomInteger(nvars);
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
RSphere(xy,nvars,i);
|
|
xy[i].Set(majoraxis,nc*xy[i][majoraxis]);
|
|
}
|
|
//--- Test
|
|
CKMeans::KMeansGenerate(xy,npoints,nvars,nc,restarts,info,c,xyc);
|
|
//--- check
|
|
if(info<0)
|
|
{
|
|
converrors=true;
|
|
return;
|
|
}
|
|
//--- Test that XYC is correct mapping to cluster centers
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
cclosest=-1;
|
|
dclosest=CMath::m_maxrealnumber;
|
|
//--- calculation
|
|
for(j=0;j<=nc-1;j++)
|
|
{
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
tmp[i_]=xy[i][i_];
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
tmp[i_]=tmp[i_]-c[i_][j];
|
|
v=0.0;
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
v+=tmp[i_]*tmp[i_];
|
|
//--- check
|
|
if(v<dclosest)
|
|
{
|
|
cclosest=j;
|
|
dclosest=v;
|
|
}
|
|
}
|
|
//--- check
|
|
if(cclosest!=xyc[i])
|
|
{
|
|
othererrors=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- Use first NC rows of XY as random centers
|
|
//--- (XY is totally random,so it is as good as any other choice).
|
|
//--- Compare potential functions.
|
|
ekmeans=0;
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
tmp[i_]=xy[i][i_];
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
tmp[i_]=tmp[i_]-c[i_][xyc[i]];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
v+=tmp[i_]*tmp[i_];
|
|
ekmeans=ekmeans+v;
|
|
}
|
|
//--- calculation
|
|
erandom=0;
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
dclosest=CMath::m_maxrealnumber;
|
|
v=0;
|
|
for(j=0;j<=nc-1;j++)
|
|
{
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
tmp[i_]=xy[i][i_];
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
tmp[i_]=tmp[i_]-xy[j][i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
v+=tmp[i_]*tmp[i_];
|
|
//--- check
|
|
if(v<dclosest)
|
|
dclosest=v;
|
|
}
|
|
erandom=erandom+v;
|
|
}
|
|
//--- check
|
|
if(erandom<ekmeans)
|
|
{
|
|
simpleerrors=true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This non-deterministic test checks that Restarts>1 significantly |
|
|
//| improves quality of results. |
|
|
//| Subroutine generates random task 3 unit balls in 2D,each with 20 |
|
|
//| points, separated by 5 units wide gaps,and solves it with |
|
|
//| Restarts=1 and with Restarts=5. Potential functions are compared,|
|
|
//| outcome of the trial is either 0 or 1 (depending on what is |
|
|
//| better). |
|
|
//| Sequence of 1000 such tasks is olved. If Restarts>1 actually |
|
|
//| improve quality of solution,sum of outcome will be non-binomial. |
|
|
//| If it doesn't matter,it will be binomially distributed. |
|
|
//| P.S. This test was added after report from Gianluca Borello who |
|
|
//| noticed error in the handling of multiple restarts. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestKMeansUnit::RestartsTest(bool &converrors,bool &restartserrors)
|
|
{
|
|
//--- create variables
|
|
int npoints=0;
|
|
int nvars=0;
|
|
int nclusters=0;
|
|
int clustersize=0;
|
|
int restarts=0;
|
|
int passcount=0;
|
|
double sigmathreshold=0;
|
|
double p=0;
|
|
double s=0;
|
|
int i=0;
|
|
int j=0;
|
|
int info=0;
|
|
int pass=0;
|
|
double ea=0;
|
|
double eb=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
CMatrixDouble ca;
|
|
CMatrixDouble cb;
|
|
//--- create arrays
|
|
int xyca[];
|
|
int xycb[];
|
|
double tmp[];
|
|
//--- initialization
|
|
restarts=5;
|
|
passcount=1000;
|
|
clustersize=20;
|
|
nclusters=3;
|
|
nvars=2;
|
|
npoints=nclusters*clustersize;
|
|
sigmathreshold=5;
|
|
//--- allocation
|
|
xy.Resize(npoints,nvars);
|
|
ArrayResize(tmp,nvars);
|
|
p=0;
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Fill
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
RSphere(xy,nvars,i);
|
|
for(j=0;j<=nvars-1;j++)
|
|
xy[i].Set(j,xy[i][j]+(double)i/(double)clustersize*5);
|
|
}
|
|
//--- Test: Restarts=1
|
|
CKMeans::KMeansGenerate(xy,npoints,nvars,nclusters,1,info,ca,xyca);
|
|
//--- check
|
|
if(info<0)
|
|
{
|
|
converrors=true;
|
|
return;
|
|
}
|
|
//--- calculation
|
|
ea=0;
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
tmp[i_]=xy[i][i_];
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
tmp[i_]=tmp[i_]-ca[i_][xyca[i]];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
v+=tmp[i_]*tmp[i_];
|
|
ea=ea+v;
|
|
}
|
|
//--- Test: Restarts>1
|
|
CKMeans::KMeansGenerate(xy,npoints,nvars,nclusters,restarts,info,cb,xycb);
|
|
//--- check
|
|
if(info<0)
|
|
{
|
|
converrors=true;
|
|
return;
|
|
}
|
|
//--- calculation
|
|
eb=0;
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
tmp[i_]=xy[i][i_];
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
tmp[i_]=tmp[i_]-cb[i_][xycb[i]];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=nvars-1;i_++)
|
|
v+=tmp[i_]*tmp[i_];
|
|
eb=eb+v;
|
|
}
|
|
//--- Calculate statistic.
|
|
if(ea<eb)
|
|
p=p+1;
|
|
//--- check
|
|
if(ea==eb)
|
|
p=p+0.5;
|
|
}
|
|
//--- If Restarts doesn't influence quality of centers found,P must be
|
|
//--- binomially distributed random value with mean 0.5*PassCount and
|
|
//--- standard deviation Sqrt(PassCount/4).
|
|
//--- If Restarts do influence quality of solution,P must be significantly
|
|
//--- lower than 0.5*PassCount.
|
|
s=(p-0.5*passcount)/MathSqrt((double)passcount/4.0);
|
|
restartserrors=restartserrors || s>(double)(-sigmathreshold);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random normal number |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestKMeansUnit::RNormal(void)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double u=0;
|
|
double v=0;
|
|
double s=0;
|
|
double x1=0;
|
|
double x2=0;
|
|
//--- calculation
|
|
while(true)
|
|
{
|
|
//--- change values
|
|
u=2*CMath::RandomReal()-1;
|
|
v=2*CMath::RandomReal()-1;
|
|
s=CMath::Sqr(u)+CMath::Sqr(v);
|
|
//--- check
|
|
if(s>0.0 && s<1.0)
|
|
{
|
|
s=MathSqrt(-(2*MathLog(s)/s));
|
|
x1=u*s;
|
|
x2=v*s;
|
|
//--- break the cycle
|
|
break;
|
|
}
|
|
}
|
|
//--- check
|
|
if(x1==0)
|
|
result=x2;
|
|
else
|
|
result=x1;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Random point from sphere |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestKMeansUnit::RSphere(CMatrixDouble &xy,const int n,const int i)
|
|
{
|
|
//--- create variables
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- change values
|
|
for(j=0;j<=n-1;j++)
|
|
xy[i].Set(j,RNormal());
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=xy[i][i_]*xy[i][i_];
|
|
//--- calculation
|
|
v=CMath::RandomReal()/MathSqrt(v);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xy[i].Set(i_,v*xy[i][i_]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CHblas |
|
|
//+------------------------------------------------------------------+
|
|
class CTestHblasUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestHblasUnit(void);
|
|
~CTestHblasUnit(void);
|
|
//--- public method
|
|
static bool TestHblas(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestHblasUnit::CTestHblasUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestHblasUnit::~CTestHblasUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CHblas |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestHblasUnit::TestHblas(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int maxn=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
bool waserrors;
|
|
double mverr=0;
|
|
double threshold=0;
|
|
complex alpha=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
complex x[];
|
|
complex y1[];
|
|
complex y2[];
|
|
complex y3[];
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex ua;
|
|
CMatrixComplex la;
|
|
//--- initialization
|
|
mverr=0;
|
|
waserrors=false;
|
|
maxn=10;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- Test MV
|
|
for(n=2;n<=maxn;n++)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n+1,n+1);
|
|
ua.Resize(n+1,n+1);
|
|
la.Resize(n+1,n+1);
|
|
ArrayResize(x,n+1);
|
|
ArrayResize(y1,n+1);
|
|
ArrayResize(y2,n+1);
|
|
ArrayResize(y3,n+1);
|
|
//--- fill A,UA,LA
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
a[i].SetRe(i,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(i,0);
|
|
//--- change values
|
|
for(j=i+1;j<=n;j++)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
a[j].Set(i,CMath::Conj(a[i][j]));
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
ua[i].Set(j,0);
|
|
}
|
|
//--- change values
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=i;j<=n;j++)
|
|
ua[i].Set(j,a[i][j]);
|
|
}
|
|
//--- change values
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
la[i].Set(j,0);
|
|
}
|
|
//--- change values
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=i;j++)
|
|
la[i].Set(j,a[i][j]);
|
|
}
|
|
//--- test on different I1,I2
|
|
for(i1=1;i1<=n;i1++)
|
|
{
|
|
for(i2=i1;i2<=n;i2++)
|
|
{
|
|
//--- Fill X,choose Alpha
|
|
for(i=1;i<=i2-i1+1;i++)
|
|
{
|
|
x[i].re=2*CMath::RandomReal()-1;
|
|
x[i].im=2*CMath::RandomReal()-1;
|
|
}
|
|
alpha.re=2*CMath::RandomReal()-1;
|
|
alpha.im=2*CMath::RandomReal()-1;
|
|
//--- calculate A*x,UA*x,LA*x
|
|
for(i=i1;i<=i2;i++)
|
|
{
|
|
i1_=1-i1;
|
|
v=0.0;
|
|
for(i_=i1;i_<=i2;i_++)
|
|
v+=a[i][i_]*x[i_+i1_];
|
|
y1[i-i1+1]=alpha*v;
|
|
}
|
|
//--- function call
|
|
CHblas::HermitianMatrixVectorMultiply(ua,true,i1,i2,x,alpha,y2);
|
|
//--- function call
|
|
CHblas::HermitianMatrixVectorMultiply(la,false,i1,i2,x,alpha,y3);
|
|
//--- Calculate error
|
|
for(i_=1;i_<=i2-i1+1;i_++)
|
|
y2[i_]=y2[i_]-y1[i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=1;i_<=i2-i1+1;i_++)
|
|
v+=y2[i_]*CMath::Conj(y2[i_]);
|
|
//--- search errors
|
|
mverr=MathMax(mverr,MathSqrt(CMath::AbsComplex(v)));
|
|
for(i_=1;i_<=i2-i1+1;i_++)
|
|
y3[i_]=y3[i_]-y1[i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=1;i_<=i2-i1+1;i_++)
|
|
v+=y3[i_]*CMath::Conj(y3[i_]);
|
|
//--- search errors
|
|
mverr=MathMax(mverr,MathSqrt(CMath::AbsComplex(v)));
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=mverr>threshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING HERMITIAN BLAS");
|
|
Print("MV error: ");
|
|
Print("{0,5:E3}",mverr);
|
|
Print("Threshold: ");
|
|
Print("{0,5:E3}",threshold);
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CReflections |
|
|
//+------------------------------------------------------------------+
|
|
class CTestReflectionsUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestReflectionsUnit(void);
|
|
~CTestReflectionsUnit(void);
|
|
//--- public method
|
|
static bool TestReflections(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestReflectionsUnit::CTestReflectionsUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestReflectionsUnit::~CTestReflectionsUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CReflections |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestReflectionsUnit::TestReflections(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
int m=0;
|
|
int maxmn=0;
|
|
double tmp=0;
|
|
double beta=0;
|
|
double tau=0;
|
|
double err=0;
|
|
double mer=0;
|
|
double mel=0;
|
|
double meg=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
double threshold=0;
|
|
int tasktype=0;
|
|
double xscale=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double v[];
|
|
double work[];
|
|
//--- create matrix
|
|
CMatrixDouble h;
|
|
CMatrixDouble a;
|
|
CMatrixDouble b;
|
|
CMatrixDouble c;
|
|
//--- initialization
|
|
passcount=10;
|
|
threshold=100*CMath::m_machineepsilon;
|
|
mer=0;
|
|
mel=0;
|
|
meg=0;
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(n=1;n<=10;n++)
|
|
{
|
|
for(m=1;m<=10;m++)
|
|
{
|
|
//--- Task
|
|
n=1+CMath::RandomInteger(10);
|
|
m=1+CMath::RandomInteger(10);
|
|
maxmn=MathMax(m,n);
|
|
//--- Initialize
|
|
ArrayResize(x,maxmn+1);
|
|
ArrayResize(v,maxmn+1);
|
|
ArrayResize(work,maxmn+1);
|
|
h.Resize(maxmn+1,maxmn+1);
|
|
a.Resize(maxmn+1,maxmn+1);
|
|
b.Resize(maxmn+1,maxmn+1);
|
|
c.Resize(maxmn+1,maxmn+1);
|
|
//--- GenerateReflection,three tasks are possible:
|
|
//--- * random X
|
|
//--- * zero X
|
|
//--- * non-zero X[1],all other are zeros
|
|
//--- * random X,near underflow scale
|
|
//--- * random X,near overflow scale
|
|
for(tasktype=0;tasktype<=4;tasktype++)
|
|
{
|
|
xscale=1;
|
|
//--- check
|
|
if(tasktype==0)
|
|
{
|
|
for(i=1;i<=n;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- check
|
|
if(tasktype==1)
|
|
{
|
|
for(i=1;i<=n;i++)
|
|
x[i]=0;
|
|
}
|
|
//--- check
|
|
if(tasktype==2)
|
|
{
|
|
x[1]=2*CMath::RandomReal()-1;
|
|
for(i=2;i<=n;i++)
|
|
x[i]=0;
|
|
}
|
|
//--- check
|
|
if(tasktype==3)
|
|
{
|
|
for(i=1;i<=n;i++)
|
|
x[i]=(CMath::RandomInteger(21)-10)*CMath::m_minrealnumber;
|
|
xscale=10*CMath::m_minrealnumber;
|
|
}
|
|
//--- check
|
|
if(tasktype==4)
|
|
{
|
|
for(i=1;i<=n;i++)
|
|
x[i]=(2*CMath::RandomReal()-1)*CMath::m_maxrealnumber;
|
|
xscale=CMath::m_maxrealnumber;
|
|
}
|
|
for(i_=1;i_<=n;i_++)
|
|
{
|
|
v[i_]=x[i_];
|
|
}
|
|
//--- function call
|
|
CReflections::GenerateReflection(v,n,tau);
|
|
//--- change values
|
|
beta=v[1];
|
|
v[1]=1;
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
h[i].Set(j,1-tau*v[i]*v[j]);
|
|
else
|
|
h[i].Set(j,-(tau*v[i]*v[j]));
|
|
}
|
|
}
|
|
//--- calculation
|
|
err=0;
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
tmp=0.0;
|
|
for(i_=1;i_<=n;i_++)
|
|
tmp+=h[i][i_]*x[i_];
|
|
//--- check
|
|
if(i==1)
|
|
err=MathMax(err,MathAbs(tmp-beta));
|
|
else
|
|
err=MathMax(err,MathAbs(tmp));
|
|
}
|
|
meg=MathMax(meg,err/xscale);
|
|
}
|
|
//--- ApplyReflectionFromTheLeft
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
v[i]=x[i];
|
|
}
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CReflections::GenerateReflection(v,m,tau);
|
|
beta=v[1];
|
|
v[1]=1;
|
|
//--- function call
|
|
CReflections::ApplyReflectionFromTheLeft(b,tau,v,1,m,1,n,work);
|
|
//--- change values
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
for(j=1;j<=m;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
h[i].Set(j,1-tau*v[i]*v[j]);
|
|
else
|
|
h[i].Set(j,-(tau*v[i]*v[j]));
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
tmp=0.0;
|
|
for(i_=1;i_<=m;i_++)
|
|
tmp+=h[i][i_]*a[i_][j];
|
|
c[i].Set(j,tmp);
|
|
}
|
|
}
|
|
//--- change value
|
|
err=0;
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
err=MathMax(err,MathAbs(b[i][j]-c[i][j]));
|
|
}
|
|
mel=MathMax(mel,err);
|
|
//--- ApplyReflectionFromTheRight
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
v[i]=x[i];
|
|
}
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CReflections::GenerateReflection(v,n,tau);
|
|
beta=v[1];
|
|
v[1]=1;
|
|
//--- function call
|
|
CReflections::ApplyReflectionFromTheRight(b,tau,v,1,m,1,n,work);
|
|
//--- change value
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
h[i].Set(j,1-tau*v[i]*v[j]);
|
|
else
|
|
h[i].Set(j,-(tau*v[i]*v[j]));
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
tmp=0.0;
|
|
for(i_=1;i_<=n;i_++)
|
|
tmp+=a[i][i_]*h[i_][j];
|
|
c[i].Set(j,tmp);
|
|
}
|
|
}
|
|
err=0;
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
err=MathMax(err,MathAbs(b[i][j]-c[i][j]));
|
|
}
|
|
mer=MathMax(mer,err);
|
|
}
|
|
}
|
|
}
|
|
//--- Overflow crash test
|
|
ArrayResize(x,11);
|
|
ArrayResize(v,11);
|
|
for(i=1;i<=10;i++)
|
|
v[i]=CMath::m_maxrealnumber*0.01*(2*CMath::RandomReal()-1);
|
|
//--- function call
|
|
CReflections::GenerateReflection(v,10,tau);
|
|
result=(meg<=threshold && mel<=threshold) && mer<=threshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING REFLECTIONS");
|
|
Print("Pass count is ");
|
|
Print("{0,0:d}",passcount);
|
|
Print("Generate absolute error is ");
|
|
Print("{0,5:E3}",meg);
|
|
Print("Apply(Left) absolute error is ");
|
|
Print("{0,5:E3}",mel);
|
|
Print("Apply(Right) absolute error is ");
|
|
Print("{0,5:E3}",mer);
|
|
Print("Overflow crash test passed");
|
|
//--- check
|
|
if(result)
|
|
Print("TEST PASSED");
|
|
else
|
|
Print("TEST FAILED");
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CComplexReflections |
|
|
//+------------------------------------------------------------------+
|
|
class CTestCReflectionsUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestCReflectionsUnit(void);
|
|
~CTestCReflectionsUnit(void);
|
|
//--- public method
|
|
static bool TestCReflections(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestCReflectionsUnit::CTestCReflectionsUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestCReflectionsUnit::~CTestCReflectionsUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CComplexReflections |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestCReflectionsUnit::TestCReflections(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
int m=0;
|
|
int maxmn=0;
|
|
complex tmp=0;
|
|
complex beta=0;
|
|
complex tau=0;
|
|
double err=0;
|
|
double mer=0;
|
|
double mel=0;
|
|
double meg=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
bool waserrors;
|
|
double threshold=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
complex x[];
|
|
complex v[];
|
|
complex work[];
|
|
//--- create matrix
|
|
CMatrixComplex h;
|
|
CMatrixComplex a;
|
|
CMatrixComplex b;
|
|
CMatrixComplex c;
|
|
//--- initialization
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
passcount=1000;
|
|
mer=0;
|
|
mel=0;
|
|
meg=0;
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Task
|
|
n=1+CMath::RandomInteger(10);
|
|
m=1+CMath::RandomInteger(10);
|
|
maxmn=MathMax(m,n);
|
|
//--- Initialize
|
|
ArrayResize(x,maxmn+1);
|
|
ArrayResize(v,maxmn+1);
|
|
ArrayResize(work,maxmn+1);
|
|
h.Resize(maxmn+1,maxmn+1);
|
|
a.Resize(maxmn+1,maxmn+1);
|
|
b.Resize(maxmn+1,maxmn+1);
|
|
c.Resize(maxmn+1,maxmn+1);
|
|
//--- GenerateReflection
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
x[i].re=2*CMath::RandomReal()-1;
|
|
x[i].im=2*CMath::RandomReal()-1;
|
|
v[i]=x[i];
|
|
}
|
|
//--- function call
|
|
CComplexReflections::ComplexGenerateReflection(v,n,tau);
|
|
//--- change values
|
|
beta=v[1];
|
|
v[1]=1;
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
h[i].Set(j,-tau*v[i]*CMath::Conj(v[j])+1);
|
|
else
|
|
h[i].Set(j,-(tau*v[i]*CMath::Conj(v[j])));
|
|
}
|
|
}
|
|
//--- calculation
|
|
err=0;
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
tmp=0.0;
|
|
for(i_=1;i_<=n;i_++)
|
|
tmp+=CMath::Conj(h[i_][i])*x[i_];
|
|
//--- check
|
|
if(i==1)
|
|
err=MathMax(err,CMath::AbsComplex(tmp-beta));
|
|
else
|
|
err=MathMax(err,CMath::AbsComplex(tmp));
|
|
}
|
|
err=MathMax(err,MathAbs(beta.im));
|
|
meg=MathMax(meg,err);
|
|
//--- ApplyReflectionFromTheLeft
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
x[i].re=2*CMath::RandomReal()-1;
|
|
x[i].im=2*CMath::RandomReal()-1;
|
|
v[i]=x[i];
|
|
}
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CComplexReflections::ComplexGenerateReflection(v,m,tau);
|
|
beta=v[1];
|
|
v[1]=1;
|
|
//--- function call
|
|
CComplexReflections::ComplexApplyReflectionFromTheLeft(b,tau,v,1,m,1,n,work);
|
|
//--- change values
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
for(j=1;j<=m;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
h[i].Set(j,-tau*v[i]*CMath::Conj(v[j])+1);
|
|
else
|
|
h[i].Set(j,-(tau*v[i]*CMath::Conj(v[j])));
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
tmp=0.0;
|
|
for(i_=1;i_<=m;i_++)
|
|
tmp+=h[i][i_]*a[i_][j];
|
|
c[i].Set(j,tmp);
|
|
}
|
|
}
|
|
err=0;
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
err=MathMax(err,CMath::AbsComplex(b[i][j]-c[i][j]));
|
|
}
|
|
mel=MathMax(mel,err);
|
|
//--- ApplyReflectionFromTheRight
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
v[i]=x[i];
|
|
}
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CComplexReflections::ComplexGenerateReflection(v,n,tau);
|
|
beta=v[1];
|
|
v[1]=1;
|
|
//--- function call
|
|
CComplexReflections::ComplexApplyReflectionFromTheRight(b,tau,v,1,m,1,n,work);
|
|
//--- change values
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
h[i].Set(j,-tau*v[i]*CMath::Conj(v[j])+1);
|
|
else
|
|
h[i].Set(j,-(tau*v[i]*CMath::Conj(v[j])));
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
tmp=0.0;
|
|
for(i_=1;i_<=n;i_++)
|
|
tmp+=a[i][i_]*h[i_][j];
|
|
c[i].Set(j,tmp);
|
|
}
|
|
}
|
|
err=0;
|
|
for(i=1;i<=m;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
err=MathMax(err,CMath::AbsComplex(b[i][j]-c[i][j]));
|
|
}
|
|
mer=MathMax(mer,err);
|
|
}
|
|
//--- Overflow crash test
|
|
ArrayResize(x,11);
|
|
ArrayResize(v,11);
|
|
for(i=1;i<=10;i++)
|
|
v[i]=CMath::m_maxrealnumber*0.01*(2*CMath::RandomReal()-1);
|
|
//--- function call
|
|
CComplexReflections::ComplexGenerateReflection(v,10,tau);
|
|
//--- report
|
|
waserrors=(meg>threshold || mel>threshold) || mer>threshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING COMPLEX REFLECTIONS");
|
|
Print("Generate error: ");
|
|
Print("{0,5:E3}",meg);
|
|
Print("Apply(L) error: ");
|
|
Print("{0,5:E3}",mel);
|
|
Print("Apply(R) error: ");
|
|
Print("{0,5:E3}",mer);
|
|
Print("Threshold: ");
|
|
Print("{0,5:E3}",threshold);
|
|
Print("Overflow crash test: PASSED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSblas |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSblasUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestSblasUnit(void);
|
|
~CTestSblasUnit(void);
|
|
//--- public method
|
|
static bool TestSblas(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestSblasUnit::CTestSblasUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestSblasUnit::~CTestSblasUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSblas |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestSblasUnit::TestSblas(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int maxn=0;
|
|
int i=0;
|
|
int j=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
bool waserrors;
|
|
double mverr=0;
|
|
double threshold=0;
|
|
double alpha=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y1[];
|
|
double y2[];
|
|
double y3[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble ua;
|
|
CMatrixDouble la;
|
|
//--- initialization
|
|
mverr=0;
|
|
waserrors=false;
|
|
maxn=10;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- Test MV
|
|
for(n=2;n<=maxn;n++)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n+1,n+1);
|
|
ua.Resize(n+1,n+1);
|
|
la.Resize(n+1,n+1);
|
|
ArrayResize(x,n+1);
|
|
ArrayResize(y1,n+1);
|
|
ArrayResize(y2,n+1);
|
|
ArrayResize(y3,n+1);
|
|
//--- fill A,UA,LA
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
a[i].Set(i,2*CMath::RandomReal()-1);
|
|
for(j=i+1;j<=n;j++)
|
|
{
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
a[j].Set(i,a[i][j]);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
ua[i].Set(j,0);
|
|
}
|
|
//--- change values
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=i;j<=n;j++)
|
|
ua[i].Set(j,a[i][j]);
|
|
}
|
|
//--- change values
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
la[i].Set(j,0);
|
|
}
|
|
//--- change values
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=i;j++)
|
|
la[i].Set(j,a[i][j]);
|
|
}
|
|
//--- test on different I1,I2
|
|
for(i1=1;i1<=n;i1++)
|
|
{
|
|
for(i2=i1;i2<=n;i2++)
|
|
{
|
|
//--- Fill X,choose Alpha
|
|
for(i=1;i<=i2-i1+1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
alpha=2*CMath::RandomReal()-1;
|
|
//--- calculate A*x,UA*x,LA*x
|
|
for(i=i1;i<=i2;i++)
|
|
{
|
|
i1_=1-i1;
|
|
v=0.0;
|
|
for(i_=i1;i_<=i2;i_++)
|
|
v+=a[i][i_]*x[i_+i1_];
|
|
y1[i-i1+1]=alpha*v;
|
|
}
|
|
//--- function call
|
|
CSblas::SymmetricMatrixVectorMultiply(ua,true,i1,i2,x,alpha,y2);
|
|
//--- function call
|
|
CSblas::SymmetricMatrixVectorMultiply(la,false,i1,i2,x,alpha,y3);
|
|
//--- Calculate error
|
|
for(i_=1;i_<=i2-i1+1;i_++)
|
|
y2[i_]=y2[i_]-y1[i_];
|
|
v=0.0;
|
|
for(i_=1;i_<=i2-i1+1;i_++)
|
|
v+=y2[i_]*y2[i_];
|
|
//--- search errors
|
|
mverr=MathMax(mverr,MathSqrt(v));
|
|
for(i_=1;i_<=i2-i1+1;i_++)
|
|
y3[i_]=y3[i_]-y1[i_];
|
|
v=0.0;
|
|
for(i_=1;i_<=i2-i1+1;i_++)
|
|
v+=y3[i_]*y3[i_];
|
|
//--- search errors
|
|
mverr=MathMax(mverr,MathSqrt(v));
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=mverr>threshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING SYMMETRIC BLAS");
|
|
Print("MV error: ");
|
|
Print("{0,5:E3}",mverr);
|
|
Print("Threshold: ");
|
|
Print("{0,5:E3}",threshold);
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class COrtFac |
|
|
//+------------------------------------------------------------------+
|
|
class CTestOrtFacUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static double RMatrixDiff(CMatrixDouble &a,CMatrixDouble &b,const int m,const int n);
|
|
static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
|
|
static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b);
|
|
static void RMatrixFillSparseA(CMatrixDouble &a,const int m,const int n,const double sparcity);
|
|
static void CMatrixFillSparseA(CMatrixComplex &a,const int m,const int n,const double sparcity);
|
|
static void InternalMatrixMatrixMultiply(CMatrixDouble &a,const int ai1,const int ai2,const int aj1,const int aj2,const bool transa,CMatrixDouble &b,const int bi1,const int bi2,const int bj1,const int bj2,const bool transb,CMatrixDouble &c,const int ci1,const int ci2,const int cj1,const int cj2);
|
|
static void TestRQRProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &qrerrors);
|
|
static void TestCQRProblem(CMatrixComplex &a,const int m,const int n,const double threshold,bool &qrerrors);
|
|
static void TestRLQProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &lqerrors);
|
|
static void TestCLQProblem(CMatrixComplex &a,const int m,const int n,const double threshold,bool &lqerrors);
|
|
static void TestRBdProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &bderrors);
|
|
static void TestRHessProblem(CMatrixDouble &a,const int n,const double threshold,bool &hesserrors);
|
|
static void TestRTdProblem(CMatrixDouble &a,const int n,const double threshold,bool &tderrors);
|
|
static void TestCTdProblem(CMatrixComplex &a,const int n,const double threshold,bool &tderrors);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestOrtFacUnit(void);
|
|
~CTestOrtFacUnit(void);
|
|
//--- public method
|
|
static bool TestOrtFac(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestOrtFacUnit::CTestOrtFacUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestOrtFacUnit::~CTestOrtFacUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Main unittest subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestOrtFacUnit::TestOrtFac(const bool silent)
|
|
{
|
|
int maxmn=0;
|
|
double threshold=0;
|
|
int passcount=0;
|
|
int mx=0;
|
|
int m=0;
|
|
int n=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
bool rqrerrors;
|
|
bool rlqerrors;
|
|
bool cqrerrors;
|
|
bool clqerrors;
|
|
bool rbderrors;
|
|
bool rhesserrors;
|
|
bool rtderrors;
|
|
bool ctderrors;
|
|
bool waserrors;
|
|
//--- create matrix
|
|
CMatrixDouble ra;
|
|
CMatrixComplex ca;
|
|
//--- initialization
|
|
waserrors=false;
|
|
rqrerrors=false;
|
|
rlqerrors=false;
|
|
cqrerrors=false;
|
|
clqerrors=false;
|
|
rbderrors=false;
|
|
rhesserrors=false;
|
|
rtderrors=false;
|
|
ctderrors=false;
|
|
maxmn=3*CAblas::AblasBlockSize()+1;
|
|
passcount=1;
|
|
threshold=5*1000*CMath::m_machineepsilon;
|
|
//--- Different problems
|
|
for(mx=1;mx<=maxmn;mx++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Rectangular factorizations: QR,LQ,bidiagonal
|
|
//--- Matrix types: zero,dense,sparse
|
|
n=1+CMath::RandomInteger(mx);
|
|
m=1+CMath::RandomInteger(mx);
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
n=mx;
|
|
else
|
|
m=mx;
|
|
//--- allocation
|
|
ra.Resize(m,n);
|
|
ca.Resize(m,n);
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
ra[i].Set(j,0);
|
|
ca[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- function calls
|
|
TestRQRProblem(ra,m,n,threshold,rqrerrors);
|
|
TestRLQProblem(ra,m,n,threshold,rlqerrors);
|
|
TestCQRProblem(ca,m,n,threshold,cqrerrors);
|
|
TestCLQProblem(ca,m,n,threshold,clqerrors);
|
|
TestRBdProblem(ra,m,n,threshold,rbderrors);
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
ra[i].Set(j,2*CMath::RandomReal()-1);
|
|
ca[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
ca[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- function calls
|
|
TestRQRProblem(ra,m,n,threshold,rqrerrors);
|
|
TestRLQProblem(ra,m,n,threshold,rlqerrors);
|
|
TestCQRProblem(ca,m,n,threshold,cqrerrors);
|
|
TestCLQProblem(ca,m,n,threshold,clqerrors);
|
|
TestRBdProblem(ra,m,n,threshold,rbderrors);
|
|
//--- function calls
|
|
RMatrixFillSparseA(ra,m,n,0.95);
|
|
CMatrixFillSparseA(ca,m,n,0.95);
|
|
//--- function calls
|
|
TestRQRProblem(ra,m,n,threshold,rqrerrors);
|
|
TestRLQProblem(ra,m,n,threshold,rlqerrors);
|
|
TestCQRProblem(ca,m,n,threshold,cqrerrors);
|
|
TestCLQProblem(ca,m,n,threshold,clqerrors);
|
|
TestRBdProblem(ra,m,n,threshold,rbderrors);
|
|
//--- Square factorizations: Hessenberg,tridiagonal
|
|
//--- Matrix types: zero,dense,sparse
|
|
ra.Resize(mx,mx);
|
|
ca.Resize(mx,mx);
|
|
//--- change values
|
|
for(i=0;i<=mx-1;i++)
|
|
{
|
|
for(j=0;j<=mx-1;j++)
|
|
{
|
|
ra[i].Set(j,0);
|
|
ca[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- function call
|
|
TestRHessProblem(ra,mx,threshold,rhesserrors);
|
|
//--- change values
|
|
for(i=0;i<=mx-1;i++)
|
|
{
|
|
for(j=0;j<=mx-1;j++)
|
|
{
|
|
ra[i].Set(j,2*CMath::RandomReal()-1);
|
|
ca[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
ca[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- function calls
|
|
TestRHessProblem(ra,mx,threshold,rhesserrors);
|
|
RMatrixFillSparseA(ra,mx,mx,0.95);
|
|
CMatrixFillSparseA(ca,mx,mx,0.95);
|
|
TestRHessProblem(ra,mx,threshold,rhesserrors);
|
|
//--- Symetric factorizations: tridiagonal
|
|
//--- Matrix types: zero,dense,sparse
|
|
ra.Resize(mx,mx);
|
|
ca.Resize(mx,mx);
|
|
//--- change values
|
|
for(i=0;i<=mx-1;i++)
|
|
{
|
|
for(j=0;j<=mx-1;j++)
|
|
{
|
|
ra[i].Set(j,0);
|
|
ca[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- function calls
|
|
TestRTdProblem(ra,mx,threshold,rtderrors);
|
|
TestCTdProblem(ca,mx,threshold,ctderrors);
|
|
//--- change values
|
|
for(i=0;i<=mx-1;i++)
|
|
{
|
|
for(j=i;j<=mx-1;j++)
|
|
{
|
|
ra[i].Set(j,2*CMath::RandomReal()-1);
|
|
ca[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
ca[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
ra[j].Set(i,ra[i][j]);
|
|
ca[j].Set(i,CMath::Conj(ca[i][j]));
|
|
}
|
|
}
|
|
for(i=0;i<=mx-1;i++)
|
|
ca[i].Set(i,2*CMath::RandomReal()-1);
|
|
//--- function calls
|
|
TestRTdProblem(ra,mx,threshold,rtderrors);
|
|
TestCTdProblem(ca,mx,threshold,ctderrors);
|
|
RMatrixFillSparseA(ra,mx,mx,0.95);
|
|
CMatrixFillSparseA(ca,mx,mx,0.95);
|
|
//--- change values
|
|
for(i=0;i<=mx-1;i++)
|
|
{
|
|
for(j=i;j<=mx-1;j++)
|
|
{
|
|
ra[j].Set(i,ra[i][j]);
|
|
ca[j].Set(i,CMath::Conj(ca[i][j]));
|
|
}
|
|
}
|
|
for(i=0;i<=mx-1;i++)
|
|
ca[i].Set(i,2*CMath::RandomReal()-1);
|
|
//--- function calls
|
|
TestRTdProblem(ra,mx,threshold,rtderrors);
|
|
TestCTdProblem(ca,mx,threshold,ctderrors);
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=((((((rqrerrors || rlqerrors) || cqrerrors) || clqerrors) || rbderrors) || rhesserrors) || rtderrors) || ctderrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING ORTFAC UNIT");
|
|
Print("RQR ERRORS: ");
|
|
//--- check
|
|
if(!rqrerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("RLQ ERRORS: ");
|
|
//--- check
|
|
if(!rlqerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("CQR ERRORS: ");
|
|
//--- check
|
|
if(!cqrerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("CLQ ERRORS: ");
|
|
//--- check
|
|
if(!clqerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("RBD ERRORS: ");
|
|
//--- check
|
|
if(!rbderrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("RHESS ERRORS: ");
|
|
//--- check
|
|
if(!rhesserrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("RTD ERRORS: ");
|
|
//--- check
|
|
if(!rtderrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("CTD ERRORS: ");
|
|
//--- check
|
|
if(!ctderrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Diff |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestOrtFacUnit::RMatrixDiff(CMatrixDouble &a,CMatrixDouble &b,
|
|
const int m,const int n)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
result=MathMax(result,MathAbs(b[i][j]-a[i][j]));
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestOrtFacUnit::RMatrixMakeACopy(CMatrixDouble &a,const int m,
|
|
const int n,CMatrixDouble &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestOrtFacUnit::CMatrixMakeACopy(CMatrixComplex &a,const int m,
|
|
const int n,CMatrixComplex &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sparse fill |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestOrtFacUnit::RMatrixFillSparseA(CMatrixDouble &a,const int m,
|
|
const int n,const double sparcity)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>=sparcity)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
else
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sparse fill |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestOrtFacUnit::CMatrixFillSparseA(CMatrixComplex &a,const int m,
|
|
const int n,const double sparcity)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>=sparcity)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
else
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Matrix multiplication |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestOrtFacUnit::InternalMatrixMatrixMultiply(CMatrixDouble &a,
|
|
const int ai1,
|
|
const int ai2,
|
|
const int aj1,
|
|
const int aj2,
|
|
const bool transa,
|
|
CMatrixDouble &b,
|
|
const int bi1,
|
|
const int bi2,
|
|
const int bj1,
|
|
const int bj2,
|
|
const bool transb,
|
|
CMatrixDouble &c,
|
|
const int ci1,
|
|
const int ci2,
|
|
const int cj1,
|
|
const int cj2)
|
|
{
|
|
//--- create variables
|
|
int arows=0;
|
|
int acols=0;
|
|
int brows=0;
|
|
int bcols=0;
|
|
int crows=0;
|
|
int ccols=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int l=0;
|
|
int r=0;
|
|
double v=0;
|
|
double beta=0;
|
|
double alpha=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
double work[];
|
|
//--- Pre-setup
|
|
k=MathMax(ai2-ai1+1,aj2-aj1+1);
|
|
k=MathMax(k,bi2-bi1+1);
|
|
k=MathMax(k,bj2-bj1+1);
|
|
//--- allocation
|
|
ArrayResize(work,k+1);
|
|
//--- initialization
|
|
beta=0;
|
|
alpha=1;
|
|
//--- Setup
|
|
if(!transa)
|
|
{
|
|
arows=ai2-ai1+1;
|
|
acols=aj2-aj1+1;
|
|
}
|
|
else
|
|
{
|
|
arows=aj2-aj1+1;
|
|
acols=ai2-ai1+1;
|
|
}
|
|
//--- check
|
|
if(!transb)
|
|
{
|
|
brows=bi2-bi1+1;
|
|
bcols=bj2-bj1+1;
|
|
}
|
|
else
|
|
{
|
|
brows=bj2-bj1+1;
|
|
bcols=bi2-bi1+1;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(acols==brows,"MatrixMatrixMultiply: incorrect matrix sizes!"))
|
|
return;
|
|
//--- check
|
|
if(((arows<=0 || acols<=0) || brows<=0) || bcols<=0)
|
|
return;
|
|
//--- change values
|
|
crows=arows;
|
|
ccols=bcols;
|
|
//--- Test WORK
|
|
i=MathMax(arows,acols);
|
|
i=MathMax(brows,i);
|
|
i=MathMax(i,bcols);
|
|
work[1]=0;
|
|
work[i]=0;
|
|
//--- Prepare C
|
|
if(beta==0.0)
|
|
{
|
|
for(i=ci1;i<=ci2;i++)
|
|
{
|
|
for(j=cj1;j<=cj2;j++)
|
|
c[i].Set(j,0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=ci1;i<=ci2;i++)
|
|
{
|
|
for(i_=cj1;i_<=cj2;i_++)
|
|
c[i].Set(i_,beta*c[i][i_]);
|
|
}
|
|
}
|
|
//--- A*B
|
|
if(!transa && !transb)
|
|
{
|
|
for(l=ai1;l<=ai2;l++)
|
|
{
|
|
for(r=bi1;r<=bi2;r++)
|
|
{
|
|
//--- change values
|
|
v=alpha*a[l][aj1+r-bi1];
|
|
k=ci1+l-ai1;
|
|
i1_=bj1-cj1;
|
|
//--- calculation
|
|
for(i_=cj1;i_<=cj2;i_++)
|
|
c[k].Set(i_,c[k][i_]+v*b[r][i_+i1_]);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
//--- A*B'
|
|
if(!transa && transb)
|
|
{
|
|
//--- check
|
|
if(arows*acols<brows*bcols)
|
|
{
|
|
for(r=bi1;r<=bi2;r++)
|
|
{
|
|
for(l=ai1;l<=ai2;l++)
|
|
{
|
|
//--- change values
|
|
i1_=bj1-aj1;
|
|
v=0.0;
|
|
for(i_=aj1;i_<=aj2;i_++)
|
|
v+=a[l][i_]*b[r][i_+i1_];
|
|
//--- calculation
|
|
c[ci1+l-ai1].Set(cj1+r-bi1,c[ci1+l-ai1][cj1+r-bi1]+alpha*v);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
for(l=ai1;l<=ai2;l++)
|
|
{
|
|
for(r=bi1;r<=bi2;r++)
|
|
{
|
|
//--- change values
|
|
i1_=bj1-aj1;
|
|
v=0.0;
|
|
for(i_=aj1;i_<=aj2;i_++)
|
|
v+=a[l][i_]*b[r][i_+i1_];
|
|
//--- calculation
|
|
c[ci1+l-ai1].Set(cj1+r-bi1,c[ci1+l-ai1][cj1+r-bi1]+alpha*v);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
//--- A'*B
|
|
if(transa && !transb)
|
|
{
|
|
for(l=aj1;l<=aj2;l++)
|
|
{
|
|
for(r=bi1;r<=bi2;r++)
|
|
{
|
|
//--- change values
|
|
v=alpha*a[ai1+r-bi1][l];
|
|
k=ci1+l-aj1;
|
|
i1_=bj1-cj1;
|
|
//--- calculation
|
|
for(i_=cj1;i_<=cj2;i_++)
|
|
c[k].Set(i_,c[k][i_]+v*b[r][i_+i1_]);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
//--- A'*B'
|
|
if(transa && transb)
|
|
{
|
|
//--- check
|
|
if(arows*acols<brows*bcols)
|
|
{
|
|
for(r=bi1;r<=bi2;r++)
|
|
{
|
|
for(i=1;i<=crows;i++)
|
|
work[i]=0.0;
|
|
for(l=ai1;l<=ai2;l++)
|
|
{
|
|
//--- change values
|
|
v=alpha*b[r][bj1+l-ai1];
|
|
k=cj1+r-bi1;
|
|
i1_=aj1-1;
|
|
//--- calculation
|
|
for(i_=1;i_<=crows;i_++)
|
|
work[i_]=work[i_]+v*a[l][i_+i1_];
|
|
}
|
|
//--- calculation
|
|
i1_=1-ci1;
|
|
for(i_=ci1;i_<=ci2;i_++)
|
|
c[i_].Set(k,c[i_][k]+work[i_+i1_]);
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
for(l=aj1;l<=aj2;l++)
|
|
{
|
|
//--- change values
|
|
k=ai2-ai1+1;
|
|
i1_=ai1-1;
|
|
for(i_=1;i_<=k;i_++)
|
|
work[i_]=a[i_+i1_][l];
|
|
//--- calculation
|
|
for(r=bi1;r<=bi2;r++)
|
|
{
|
|
//--- change values
|
|
i1_=bj1-1;
|
|
v=0.0;
|
|
for(i_=1;i_<=k;i_++)
|
|
v+=work[i_]*b[r][i_+i1_];
|
|
c[ci1+l-aj1].Set(cj1+r-bi1,c[ci1+l-aj1][cj1+r-bi1]+alpha*v);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Problem testing |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestOrtFacUnit::TestRQRProblem(CMatrixDouble &a,const int m,
|
|
const int n,const double threshold,
|
|
bool &qrerrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double taub[];
|
|
//--- create matrix
|
|
CMatrixDouble b;
|
|
CMatrixDouble q;
|
|
CMatrixDouble r;
|
|
CMatrixDouble q2;
|
|
//--- Test decompose-and-unpack error
|
|
RMatrixMakeACopy(a,m,n,b);
|
|
//--- function call
|
|
COrtFac::RMatrixQR(b,m,n,taub);
|
|
COrtFac::RMatrixQRUnpackQ(b,m,n,taub,m,q);
|
|
COrtFac::RMatrixQRUnpackR(b,m,n,r);
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=q[i][i_]*r[i_][j];
|
|
qrerrors=qrerrors || MathAbs(v-a[i][j])>threshold;
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=MathMin(i,n-1)-1;j++)
|
|
qrerrors=qrerrors || r[i][j]!=0.0;
|
|
}
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=q[i][i_]*q[j][i_];
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
qrerrors=qrerrors || MathAbs(v)>=threshold;
|
|
}
|
|
}
|
|
//--- Test for other errors
|
|
k=1+CMath::RandomInteger(m);
|
|
//--- function call
|
|
COrtFac::RMatrixQRUnpackQ(b,m,n,taub,k,q2);
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=k-1;j++)
|
|
qrerrors=qrerrors || MathAbs(q2[i][j]-q[i][j])>10*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Problem testing |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestOrtFacUnit::TestCQRProblem(CMatrixComplex &a,const int m,
|
|
const int n,const double threshold,
|
|
bool &qrerrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
complex taub[];
|
|
//--- create matrix
|
|
CMatrixComplex b;
|
|
CMatrixComplex q;
|
|
CMatrixComplex r;
|
|
CMatrixComplex q2;
|
|
//--- Test decompose-and-unpack error
|
|
CMatrixMakeACopy(a,m,n,b);
|
|
//--- function calls
|
|
COrtFac::CMatrixQR(b,m,n,taub);
|
|
COrtFac::CMatrixQRUnpackQ(b,m,n,taub,m,q);
|
|
COrtFac::CMatrixQRUnpackR(b,m,n,r);
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=q[i][i_]*r[i_][j];
|
|
qrerrors=qrerrors || CMath::AbsComplex(v-a[i][j])>threshold;
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=MathMin(i,n-1)-1;j++)
|
|
qrerrors=qrerrors || r[i][j]!=0;
|
|
}
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=q[i][i_]*CMath::Conj(q[j][i_]);
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
qrerrors=qrerrors || CMath::AbsComplex(v)>=threshold;
|
|
}
|
|
}
|
|
//--- Test for other errors
|
|
k=1+CMath::RandomInteger(m);
|
|
//--- function call
|
|
COrtFac::CMatrixQRUnpackQ(b,m,n,taub,k,q2);
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=k-1;j++)
|
|
qrerrors=qrerrors || CMath::AbsComplex(q2[i][j]-q[i][j])>10*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Problem testing |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestOrtFacUnit::TestRLQProblem(CMatrixDouble &a,const int m,
|
|
const int n,const double threshold,
|
|
bool &lqerrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double taub[];
|
|
//--- create matrix
|
|
CMatrixDouble b;
|
|
CMatrixDouble q;
|
|
CMatrixDouble l;
|
|
CMatrixDouble q2;
|
|
//--- Test decompose-and-unpack error
|
|
RMatrixMakeACopy(a,m,n,b);
|
|
//--- function calls
|
|
COrtFac::RMatrixLQ(b,m,n,taub);
|
|
COrtFac::RMatrixLQUnpackQ(b,m,n,taub,n,q);
|
|
COrtFac::RMatrixLQUnpackL(b,m,n,l);
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=l[i][i_]*q[i_][j];
|
|
lqerrors=lqerrors || MathAbs(v-a[i][j])>=threshold;
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=MathMin(i,n-1)+1;j<=n-1;j++)
|
|
lqerrors=lqerrors || l[i][j]!=0.0;
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=q[i][i_]*q[j][i_];
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
lqerrors=lqerrors || MathAbs(v)>=threshold;
|
|
}
|
|
}
|
|
//--- Test for other errors
|
|
k=1+CMath::RandomInteger(n);
|
|
//--- function call
|
|
COrtFac::RMatrixLQUnpackQ(b,m,n,taub,k,q2);
|
|
//--- search errors
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
lqerrors=lqerrors || MathAbs(q2[i][j]-q[i][j])>10*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Problem testing |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestOrtFacUnit::TestCLQProblem(CMatrixComplex &a,const int m,
|
|
const int n,const double threshold,
|
|
bool &lqerrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
complex taub[];
|
|
//--- create matrix
|
|
CMatrixComplex b;
|
|
CMatrixComplex q;
|
|
CMatrixComplex l;
|
|
CMatrixComplex q2;
|
|
//--- Test decompose-and-unpack error
|
|
CMatrixMakeACopy(a,m,n,b);
|
|
//--- function calls
|
|
COrtFac::CMatrixLQ(b,m,n,taub);
|
|
COrtFac::CMatrixLQUnpackQ(b,m,n,taub,n,q);
|
|
COrtFac::CMatrixLQUnpackL(b,m,n,l);
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=l[i][i_]*q[i_][j];
|
|
lqerrors=lqerrors || CMath::AbsComplex(v-a[i][j])>=threshold;
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=MathMin(i,n-1)+1;j<=n-1;j++)
|
|
lqerrors=lqerrors || l[i][j]!=0;
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=q[i][i_]*CMath::Conj(q[j][i_]);
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
lqerrors=lqerrors || CMath::AbsComplex(v)>=threshold;
|
|
}
|
|
}
|
|
//--- Test for other errors
|
|
k=1+CMath::RandomInteger(n);
|
|
//--- function call
|
|
COrtFac::CMatrixLQUnpackQ(b,m,n,taub,k,q2);
|
|
//--- search errors
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
lqerrors=lqerrors || CMath::AbsComplex(q2[i][j]-q[i][j])>10*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Problem testing |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestOrtFacUnit::TestRBdProblem(CMatrixDouble &a,const int m,
|
|
const int n,const double threshold,
|
|
bool &bderrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
bool up;
|
|
double v=0;
|
|
int mtsize=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double taup[];
|
|
double tauq[];
|
|
double d[];
|
|
double e[];
|
|
//--- create matrix
|
|
CMatrixDouble t;
|
|
CMatrixDouble pt;
|
|
CMatrixDouble q;
|
|
CMatrixDouble r;
|
|
CMatrixDouble bd;
|
|
CMatrixDouble x;
|
|
CMatrixDouble r1;
|
|
CMatrixDouble r2;
|
|
//--- Bidiagonal decomposition error
|
|
RMatrixMakeACopy(a,m,n,t);
|
|
//--- function calls
|
|
COrtFac::RMatrixBD(t,m,n,tauq,taup);
|
|
COrtFac::RMatrixBDUnpackQ(t,m,n,tauq,m,q);
|
|
COrtFac::RMatrixBDUnpackPT(t,m,n,taup,n,pt);
|
|
COrtFac::RMatrixBDUnpackDiagonals(t,m,n,up,d,e);
|
|
//--- allocation
|
|
bd.Resize(m,n);
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
bd[i].Set(j,0);
|
|
}
|
|
for(i=0;i<=MathMin(m,n)-1;i++)
|
|
bd[i].Set(i,d[i]);
|
|
//--- check
|
|
if(up)
|
|
{
|
|
for(i=0;i<=MathMin(m,n)-2;i++)
|
|
bd[i].Set(i+1,e[i]);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=MathMin(m,n)-2;i++)
|
|
bd[i+1].Set(i,e[i]);
|
|
}
|
|
//--- allocation
|
|
r.Resize(m,n);
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=q[i][i_]*bd[i_][j];
|
|
r[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=r[i][i_]*pt[i_][j];
|
|
bderrors=bderrors || MathAbs(v-a[i][j])>threshold;
|
|
}
|
|
}
|
|
//--- Orthogonality test for Q/PT
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=q[i_][i]*q[i_][j];
|
|
//--- check
|
|
if(i==j)
|
|
bderrors=bderrors || MathAbs(v-1)>threshold;
|
|
else
|
|
bderrors=bderrors || MathAbs(v)>threshold;
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=pt[i][i_]*pt[j][i_];
|
|
//--- check
|
|
if(i==j)
|
|
bderrors=bderrors || MathAbs(v-1)>threshold;
|
|
else
|
|
bderrors=bderrors || MathAbs(v)>threshold;
|
|
}
|
|
}
|
|
//--- Partial unpacking test
|
|
k=1+CMath::RandomInteger(m);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackQ(t,m,n,tauq,k,r);
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=k-1;j++)
|
|
bderrors=bderrors || MathAbs(r[i][j]-q[i][j])>10*CMath::m_machineepsilon;
|
|
}
|
|
k=1+CMath::RandomInteger(n);
|
|
//--- function call
|
|
COrtFac::RMatrixBDUnpackPT(t,m,n,taup,k,r);
|
|
//--- search errors
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
bderrors=bderrors || r[i][j]-pt[i][j]!=0.0;
|
|
}
|
|
//--- Multiplication test
|
|
x.Resize(MathMax(m,n),MathMax(m,n));
|
|
r.Resize(MathMax(m,n),MathMax(m,n));
|
|
r1.Resize(MathMax(m,n),MathMax(m,n));
|
|
r2.Resize(MathMax(m,n),MathMax(m,n));
|
|
//--- change values
|
|
for(i=0;i<=MathMax(m,n)-1;i++)
|
|
{
|
|
for(j=0;j<=MathMax(m,n)-1;j++)
|
|
x[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
mtsize=1+CMath::RandomInteger(MathMax(m,n));
|
|
//--- function calls
|
|
RMatrixMakeACopy(x,mtsize,m,r);
|
|
InternalMatrixMatrixMultiply(r,0,mtsize-1,0,m-1,false,q,0,m-1,0,m-1,false,r1,0,mtsize-1,0,m-1);
|
|
RMatrixMakeACopy(x,mtsize,m,r2);
|
|
COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,mtsize,m,true,false);
|
|
//--- search errors
|
|
bderrors=bderrors || RMatrixDiff(r1,r2,mtsize,m)>threshold;
|
|
//--- function calls
|
|
RMatrixMakeACopy(x,mtsize,m,r);
|
|
InternalMatrixMatrixMultiply(r,0,mtsize-1,0,m-1,false,q,0,m-1,0,m-1,true,r1,0,mtsize-1,0,m-1);
|
|
RMatrixMakeACopy(x,mtsize,m,r2);
|
|
COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,mtsize,m,true,true);
|
|
//--- search errors
|
|
bderrors=bderrors || RMatrixDiff(r1,r2,mtsize,m)>threshold;
|
|
//--- function calls
|
|
RMatrixMakeACopy(x,m,mtsize,r);
|
|
InternalMatrixMatrixMultiply(q,0,m-1,0,m-1,false,r,0,m-1,0,mtsize-1,false,r1,0,m-1,0,mtsize-1);
|
|
RMatrixMakeACopy(x,m,mtsize,r2);
|
|
COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,m,mtsize,false,false);
|
|
//--- search errors
|
|
bderrors=bderrors || RMatrixDiff(r1,r2,m,mtsize)>threshold;
|
|
//--- function calls
|
|
RMatrixMakeACopy(x,m,mtsize,r);
|
|
InternalMatrixMatrixMultiply(q,0,m-1,0,m-1,true,r,0,m-1,0,mtsize-1,false,r1,0,m-1,0,mtsize-1);
|
|
RMatrixMakeACopy(x,m,mtsize,r2);
|
|
COrtFac::RMatrixBDMultiplyByQ(t,m,n,tauq,r2,m,mtsize,false,true);
|
|
//--- search errors
|
|
bderrors=bderrors || RMatrixDiff(r1,r2,m,mtsize)>threshold;
|
|
//--- function calls
|
|
RMatrixMakeACopy(x,mtsize,n,r);
|
|
InternalMatrixMatrixMultiply(r,0,mtsize-1,0,n-1,false,pt,0,n-1,0,n-1,true,r1,0,mtsize-1,0,n-1);
|
|
RMatrixMakeACopy(x,mtsize,n,r2);
|
|
COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,mtsize,n,true,false);
|
|
//--- search errors
|
|
bderrors=bderrors || RMatrixDiff(r1,r2,mtsize,n)>threshold;
|
|
//--- function calls
|
|
RMatrixMakeACopy(x,mtsize,n,r);
|
|
InternalMatrixMatrixMultiply(r,0,mtsize-1,0,n-1,false,pt,0,n-1,0,n-1,false,r1,0,mtsize-1,0,n-1);
|
|
RMatrixMakeACopy(x,mtsize,n,r2);
|
|
COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,mtsize,n,true,true);
|
|
//--- search errors
|
|
bderrors=bderrors || RMatrixDiff(r1,r2,mtsize,n)>threshold;
|
|
//--- function calls
|
|
RMatrixMakeACopy(x,n,mtsize,r);
|
|
InternalMatrixMatrixMultiply(pt,0,n-1,0,n-1,true,r,0,n-1,0,mtsize-1,false,r1,0,n-1,0,mtsize-1);
|
|
RMatrixMakeACopy(x,n,mtsize,r2);
|
|
COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,n,mtsize,false,false);
|
|
//--- search errors
|
|
bderrors=bderrors || RMatrixDiff(r1,r2,n,mtsize)>threshold;
|
|
//--- function calls
|
|
RMatrixMakeACopy(x,n,mtsize,r);
|
|
InternalMatrixMatrixMultiply(pt,0,n-1,0,n-1,false,r,0,n-1,0,mtsize-1,false,r1,0,n-1,0,mtsize-1);
|
|
RMatrixMakeACopy(x,n,mtsize,r2);
|
|
COrtFac::RMatrixBDMultiplyByP(t,m,n,taup,r2,n,mtsize,false,true);
|
|
//--- search errors
|
|
bderrors=bderrors || RMatrixDiff(r1,r2,n,mtsize)>threshold;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Problem testing |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestOrtFacUnit::TestRHessProblem(CMatrixDouble &a,const int n,
|
|
const double threshold,bool &hesserrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double tau[];
|
|
//--- create matrix
|
|
CMatrixDouble b;
|
|
CMatrixDouble h;
|
|
CMatrixDouble q;
|
|
CMatrixDouble t1;
|
|
CMatrixDouble t2;
|
|
//--- function call
|
|
RMatrixMakeACopy(a,n,n,b);
|
|
//--- Decomposition
|
|
COrtFac::RMatrixHessenberg(b,n,tau);
|
|
COrtFac::RMatrixHessenbergUnpackQ(b,n,tau,q);
|
|
COrtFac::RMatrixHessenbergUnpackH(b,n,h);
|
|
//--- Matrix properties
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=q[i_][i]*q[i_][j];
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
hesserrors=hesserrors || MathAbs(v)>threshold;
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=i-2;j++)
|
|
hesserrors=hesserrors || h[i][j]!=0.0;
|
|
}
|
|
//--- Decomposition error
|
|
t1.Resize(n,n);
|
|
t2.Resize(n,n);
|
|
//--- function calls
|
|
InternalMatrixMatrixMultiply(q,0,n-1,0,n-1,false,h,0,n-1,0,n-1,false,t1,0,n-1,0,n-1);
|
|
InternalMatrixMatrixMultiply(t1,0,n-1,0,n-1,false,q,0,n-1,0,n-1,true,t2,0,n-1,0,n-1);
|
|
//--- search errors
|
|
hesserrors=hesserrors || RMatrixDiff(t2,a,n,n)>threshold;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tridiagonal tester |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestOrtFacUnit::TestRTdProblem(CMatrixDouble &a,const int n,
|
|
const double threshold,bool &tderrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double tau[];
|
|
double d[];
|
|
double e[];
|
|
//--- create matrix
|
|
CMatrixDouble ua;
|
|
CMatrixDouble la;
|
|
CMatrixDouble t;
|
|
CMatrixDouble q;
|
|
CMatrixDouble t2;
|
|
CMatrixDouble t3;
|
|
//--- allocation
|
|
ua.Resize(n,n);
|
|
la.Resize(n,n);
|
|
t.Resize(n,n);
|
|
q.Resize(n,n);
|
|
t2.Resize(n,n);
|
|
t3.Resize(n,n);
|
|
//--- fill
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
ua[i].Set(j,0);
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i;j<=n-1;j++)
|
|
ua[i].Set(j,a[i][j]);
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
la[i].Set(j,0);
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=i;j++)
|
|
la[i].Set(j,a[i][j]);
|
|
}
|
|
//--- Test 2tridiagonal: upper
|
|
COrtFac::SMatrixTD(ua,n,true,tau,d,e);
|
|
COrtFac::SMatrixTDUnpackQ(ua,n,true,tau,q);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
t[i].Set(j,0);
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
t[i].Set(i,d[i]);
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
t[i].Set(i+1,e[i]);
|
|
t[i+1].Set(i,e[i]);
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=q[i_][i]*a[i_][j];
|
|
t2[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=t2[i][i_]*q[i_][j];
|
|
t3[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
tderrors=tderrors || MathAbs(t3[i][j]-t[i][j])>threshold;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=q[i][i_]*q[j][i_];
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
tderrors=tderrors || MathAbs(v)>threshold;
|
|
}
|
|
}
|
|
//--- Test 2tridiagonal: lower
|
|
COrtFac::SMatrixTD(la,n,false,tau,d,e);
|
|
COrtFac::SMatrixTDUnpackQ(la,n,false,tau,q);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
t[i].Set(j,0);
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
t[i].Set(i,d[i]);
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
t[i].Set(i+1,e[i]);
|
|
t[i+1].Set(i,e[i]);
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=q[i_][i]*a[i_][j];
|
|
t2[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=t2[i][i_]*q[i_][j];
|
|
t3[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
tderrors=tderrors || MathAbs(t3[i][j]-t[i][j])>threshold;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=q[i][i_]*q[j][i_];
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
tderrors=tderrors || MathAbs(v)>threshold;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Hermitian problem tester |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestOrtFacUnit::TestCTdProblem(CMatrixComplex &a,const int n,
|
|
const double threshold,bool &tderrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
complex tau[];
|
|
double d[];
|
|
double e[];
|
|
//--- create matrix
|
|
CMatrixComplex ua;
|
|
CMatrixComplex la;
|
|
CMatrixComplex t;
|
|
CMatrixComplex q;
|
|
CMatrixComplex t2;
|
|
CMatrixComplex t3;
|
|
//--- allocation
|
|
ua.Resize(n,n);
|
|
la.Resize(n,n);
|
|
t.Resize(n,n);
|
|
q.Resize(n,n);
|
|
t2.Resize(n,n);
|
|
t3.Resize(n,n);
|
|
//--- fill
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
ua[i].Set(j,0);
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i;j<=n-1;j++)
|
|
ua[i].Set(j,a[i][j]);
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
la[i].Set(j,0);
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=i;j++)
|
|
la[i].Set(j,a[i][j]);
|
|
}
|
|
//--- Test 2tridiagonal: upper
|
|
COrtFac::HMatrixTD(ua,n,true,tau,d,e);
|
|
COrtFac::HMatrixTDUnpackQ(ua,n,true,tau,q);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
t[i].Set(j,0);
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
t[i].Set(i,d[i]);
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
t[i].Set(i+1,e[i]);
|
|
t[i+1].Set(i,e[i]);
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=CMath::Conj(q[i_][i])*a[i_][j];
|
|
t2[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=t2[i][i_]*q[i_][j];
|
|
t3[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
tderrors=tderrors || CMath::AbsComplex(t3[i][j]-t[i][j])>threshold;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=q[i][i_]*CMath::Conj(q[j][i_]);
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
tderrors=tderrors || CMath::AbsComplex(v)>threshold;
|
|
}
|
|
}
|
|
//--- Test 2tridiagonal: lower
|
|
COrtFac::HMatrixTD(la,n,false,tau,d,e);
|
|
COrtFac::HMatrixTDUnpackQ(la,n,false,tau,q);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
t[i].Set(j,0);
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
t[i].Set(i,d[i]);
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
t[i].Set(i+1,e[i]);
|
|
t[i+1].Set(i,e[i]);
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=CMath::Conj(q[i_][i])*a[i_][j];
|
|
t2[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=t2[i][i_]*q[i_][j];
|
|
t3[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
tderrors=tderrors || CMath::AbsComplex(t3[i][j]-t[i][j])>threshold;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=q[i][i_]*CMath::Conj(q[j][i_]);
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
tderrors=tderrors || CMath::AbsComplex(v)>threshold;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CEigenVDetect |
|
|
//+------------------------------------------------------------------+
|
|
class CTestEVDUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void RMatrixFillSparseA(CMatrixDouble &a,const int m,const int n,const double sparcity);
|
|
static void CMatrixFillSparseA(CMatrixComplex &a,const int m,const int n,const double sparcity);
|
|
static void RMatrixSymmetricSplit(CMatrixDouble &a,const int n,CMatrixDouble &al,CMatrixDouble &au);
|
|
static void CMatrixHermitianSplit(CMatrixComplex &a,const int n,CMatrixComplex &al,CMatrixComplex &au);
|
|
static void Unset2D(CMatrixDouble &a);
|
|
static void CUnset2D(CMatrixComplex &a);
|
|
static void Unset1D(double &a[]);
|
|
static void CUnset1D(complex &a[]);
|
|
static double TdTestProduct(double &d[],double &e[],const int n,CMatrixDouble &z,double &lambdav[]);
|
|
static double TestProduct(CMatrixDouble &a,const int n,CMatrixDouble &z,double &lambdav[]);
|
|
static double TestOrt(CMatrixDouble &z,const int n);
|
|
static double TestCProduct(CMatrixComplex &a,const int n,CMatrixComplex &z,double &lambdav[]);
|
|
static double TestCOrt(CMatrixComplex &z,const int n);
|
|
static void TestSEVDProblem(CMatrixDouble &a,CMatrixDouble &al,CMatrixDouble &au,const int n,const double threshold,bool &serrors,int &failc,int &runs);
|
|
static void TestHEVDProblem(CMatrixComplex &a,CMatrixComplex &al,CMatrixComplex &au,const int n,const double threshold,bool &herrors,int &failc,int &runs);
|
|
static void TestSEVDBiProblem(CMatrixDouble &afull,CMatrixDouble &al,CMatrixDouble &au,const int n,const bool distvals,const double threshold,bool &serrors,int &failc,int &runs);
|
|
static void TestHEVDBiProblem(CMatrixComplex &afull,CMatrixComplex &al,CMatrixComplex &au,const int n,const bool distvals,const double threshold,bool &herrors,int &failc,int &runs);
|
|
static void TestTdEVDProblem(double &d[],double &e[],const int n,const double threshold,bool &tderrors,int &failc,int &runs);
|
|
static void TestTdEVDBiProblem(double &d[],double &e[],const int n,const bool distvals,const double threshold,bool &serrors,int &failc,int &runs);
|
|
static void TestNSEVDProblem(CMatrixDouble &a,const int n,const double threshold,bool &nserrors,int &failc,int &runs);
|
|
static void TestEVDSet(const int n,const double threshold,double bithreshold,int &failc,int &runs,bool &nserrors,bool &serrors,bool &herrors,bool &tderrors,bool &sbierrors,bool &hbierrors,bool &tdbierrors);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestEVDUnit(void);
|
|
~CTestEVDUnit(void);
|
|
//--- public method
|
|
static bool TestEVD(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestEVDUnit::CTestEVDUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestEVDUnit::~CTestEVDUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing symmetric EVD subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestEVDUnit::TestEVD(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int j=0;
|
|
int failc=0;
|
|
int runs=0;
|
|
double failthreshold=0;
|
|
double threshold=0;
|
|
double bithreshold=0;
|
|
bool waserrors;
|
|
bool nserrors;
|
|
bool serrors;
|
|
bool herrors;
|
|
bool tderrors;
|
|
bool sbierrors;
|
|
bool hbierrors;
|
|
bool tdbierrors;
|
|
bool wfailed;
|
|
//--- create matrix
|
|
CMatrixDouble ra;
|
|
//--- initialization
|
|
failthreshold=0.005;
|
|
threshold=100000*CMath::m_machineepsilon;
|
|
bithreshold=1.0E-6;
|
|
nserrors=false;
|
|
serrors=false;
|
|
herrors=false;
|
|
tderrors=false;
|
|
sbierrors=false;
|
|
hbierrors=false;
|
|
tdbierrors=false;
|
|
failc=0;
|
|
runs=0;
|
|
//--- Test problems
|
|
for(n=1;n<=CAblas::AblasBlockSize();n++)
|
|
TestEVDSet(n,threshold,bithreshold,failc,runs,nserrors,serrors,herrors,tderrors,sbierrors,hbierrors,tdbierrors);
|
|
for(j=2;j<=3;j++)
|
|
{
|
|
for(n=j*CAblas::AblasBlockSize()-1;n<=j*CAblas::AblasBlockSize()+1;n++)
|
|
TestEVDSet(n,threshold,bithreshold,failc,runs,nserrors,serrors,herrors,tderrors,sbierrors,hbierrors,tdbierrors);
|
|
}
|
|
//--- report
|
|
wfailed=(double)failc/(double)runs>failthreshold;
|
|
waserrors=((((((nserrors || serrors) || herrors) || tderrors) || sbierrors) || hbierrors) || tdbierrors) || wfailed;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING EVD UNIT");
|
|
Print("NS ERRORS: ");
|
|
//--- check
|
|
if(!nserrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("S ERRORS: ");
|
|
//--- check
|
|
if(!serrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("H ERRORS: ");
|
|
//--- check
|
|
if(!herrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("TD ERRORS: ");
|
|
//--- check
|
|
if(!tderrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("SBI ERRORS: ");
|
|
//--- check
|
|
if(!sbierrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("HBI ERRORS: ");
|
|
//--- check
|
|
if(!hbierrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("TDBI ERRORS: ");
|
|
//--- check
|
|
if(!tdbierrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("FAILURE THRESHOLD: ");
|
|
//--- check
|
|
if(!wfailed)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sparse fill |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestEVDUnit::RMatrixFillSparseA(CMatrixDouble &a,const int m,
|
|
const int n,const double sparcity)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>=sparcity)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
else
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Sparse fill |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestEVDUnit::CMatrixFillSparseA(CMatrixComplex &a,const int m,
|
|
const int n,const double sparcity)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>=sparcity)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
else
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copies A to AL (lower half) and AU (upper half),filling unused |
|
|
//| parts by random garbage. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestEVDUnit::RMatrixSymmetricSplit(CMatrixDouble &a,const int n,
|
|
CMatrixDouble &al,CMatrixDouble &au)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i+1;j<=n-1;j++)
|
|
{
|
|
al[i].Set(j,2*CMath::RandomReal()-1);
|
|
al[j].Set(i,a[i][j]);
|
|
au[i].Set(j,a[i][j]);
|
|
au[j].Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
al[i].Set(i,a[i][i]);
|
|
au[i].Set(i,a[i][i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copies A to AL (lower half) and AU (upper half),filling unused |
|
|
//| parts by random garbage. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestEVDUnit::CMatrixHermitianSplit(CMatrixComplex &a,const int n,
|
|
CMatrixComplex &al,CMatrixComplex &au)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i+1;j<=n-1;j++)
|
|
{
|
|
al[i].Set(j,2*CMath::RandomReal()-1);
|
|
al[j].Set(i,CMath::Conj(a[i][j]));
|
|
au[i].Set(j,a[i][j]);
|
|
au[j].Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
al[i].Set(i,a[i][i]);
|
|
au[i].Set(i,a[i][i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 2D array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestEVDUnit::Unset2D(CMatrixDouble &a)
|
|
{
|
|
//--- allocation
|
|
a.Resize(1,1);
|
|
//--- change value
|
|
a[0].Set(0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 2D array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestEVDUnit::CUnset2D(CMatrixComplex &a)
|
|
{
|
|
//--- allocation
|
|
a.Resize(1,1);
|
|
//--- change value
|
|
a[0].Set(0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 1D array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestEVDUnit::Unset1D(double &a[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,1);
|
|
//--- change value
|
|
a[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 1D array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestEVDUnit::CUnset1D(complex &a[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,1);
|
|
//--- change value
|
|
a[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests Z*Lambda*Z' against tridiag(D,E). |
|
|
//| Returns relative error. |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestEVDUnit::TdTestProduct(double &d[],double &e[],const int n,
|
|
CMatrixDouble &z,double &lambdav[])
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
double mx=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- Calculate V=A[i][j],A=Z*Lambda*Z'
|
|
v=0;
|
|
for(k=0;k<=n-1;k++)
|
|
v=v+z[i][k]*lambdav[k]*z[j][k];
|
|
//--- Compare
|
|
if(MathAbs(i-j)==0)
|
|
result=MathMax(result,MathAbs(v-d[i]));
|
|
//--- check
|
|
if(MathAbs(i-j)==1)
|
|
result=MathMax(result,MathAbs(v-e[MathMin(i,j)]));
|
|
//--- check
|
|
if(MathAbs(i-j)>1)
|
|
result=MathMax(result,MathAbs(v));
|
|
}
|
|
}
|
|
//--- change value
|
|
mx=0;
|
|
for(i=0;i<=n-1;i++)
|
|
mx=MathMax(mx,MathAbs(d[i]));
|
|
for(i=0;i<=n-2;i++)
|
|
mx=MathMax(mx,MathAbs(e[i]));
|
|
//--- check
|
|
if(mx==0.0)
|
|
mx=1;
|
|
//--- return result
|
|
return(result/mx);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests Z*Lambda*Z' against A |
|
|
//| Returns relative error. |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestEVDUnit::TestProduct(CMatrixDouble &a,const int n,
|
|
CMatrixDouble &z,double &lambdav[])
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
double mx=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- Calculate V=A[i][j],A=Z*Lambda*Z'
|
|
v=0;
|
|
for(k=0;k<=n-1;k++)
|
|
v=v+z[i][k]*lambdav[k]*z[j][k];
|
|
//--- Compare
|
|
result=MathMax(result,MathAbs(v-a[i][j]));
|
|
}
|
|
}
|
|
//--- change value
|
|
mx=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
mx=MathMax(mx,MathAbs(a[i][j]));
|
|
}
|
|
//--- check
|
|
if(mx==0.0)
|
|
mx=1;
|
|
//--- return result
|
|
return(result/mx);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests Z*Z' against diag(1...1) |
|
|
//| Returns absolute error. |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestEVDUnit::TestOrt(CMatrixDouble &z,const int n)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=z[i_][i]*z[i_][j];
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
result=MathMax(result,MathAbs(v));
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests Z*Lambda*Z' against A |
|
|
//| Returns relative error. |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestEVDUnit::TestCProduct(CMatrixComplex &a,const int n,
|
|
CMatrixComplex &z,double &lambdav[])
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
complex v=0;
|
|
double mx=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- Calculate V=A[i][j],A=Z*Lambda*Z'
|
|
v=0;
|
|
for(k=0;k<=n-1;k++)
|
|
v=v+z[i][k]*lambdav[k]*CMath::Conj(z[j][k]);
|
|
//--- Compare
|
|
result=MathMax(result,CMath::AbsComplex(v-a[i][j]));
|
|
}
|
|
}
|
|
//--- change value
|
|
mx=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
mx=MathMax(mx,CMath::AbsComplex(a[i][j]));
|
|
}
|
|
//--- check
|
|
if(mx==0.0)
|
|
mx=1;
|
|
//--- return result
|
|
return(result/mx);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests Z*Z' against diag(1...1) |
|
|
//| Returns absolute error. |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestEVDUnit::TestCOrt(CMatrixComplex &z,const int n)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=z[i_][i]*CMath::Conj(z[i_][j]);
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
result=MathMax(result,CMath::AbsComplex(v));
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests SEVD problem |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestEVDUnit::TestSEVDProblem(CMatrixDouble &a,CMatrixDouble &al,
|
|
CMatrixDouble &au,const int n,
|
|
const double threshold,
|
|
bool &serrors,int &failc,int &runs)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create arrays
|
|
double lambdav[];
|
|
double lambdaref[];
|
|
//--- create matrix
|
|
CMatrixDouble z;
|
|
//--- Test simple EVD: values and full vectors,lower A
|
|
Unset1D(lambdaref);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVD(al,n,1,false,lambdaref,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
serrors=serrors || TestProduct(a,n,z,lambdaref)>threshold;
|
|
serrors=serrors || TestOrt(z,n)>threshold;
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
//--- check
|
|
if(lambdaref[i+1]<lambdaref[i])
|
|
{
|
|
serrors=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- Test simple EVD: values and full vectors,upper A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVD(au,n,1,true,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
serrors=serrors || TestProduct(a,n,z,lambdav)>threshold;
|
|
serrors=serrors || TestOrt(z,n)>threshold;
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
//--- check
|
|
if(lambdav[i+1]<lambdav[i])
|
|
{
|
|
serrors=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- Test simple EVD: values only,lower A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVD(al,n,0,false,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
serrors=serrors || MathAbs(lambdav[i]-lambdaref[i])>threshold;
|
|
//--- Test simple EVD: values only,upper A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVD(au,n,0,true,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
serrors=serrors || MathAbs(lambdav[i]-lambdaref[i])>threshold;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests SEVD problem |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestEVDUnit::TestHEVDProblem(CMatrixComplex &a,CMatrixComplex &al,
|
|
CMatrixComplex &au,const int n,
|
|
const double threshold,bool &herrors,
|
|
int &failc,int &runs)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create arrays
|
|
double lambdav[];
|
|
double lambdaref[];
|
|
//--- create matrix
|
|
CMatrixComplex z;
|
|
//--- Test simple EVD: values and full vectors,lower A
|
|
Unset1D(lambdaref);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVD(al,n,1,false,lambdaref,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
herrors=herrors || TestCProduct(a,n,z,lambdaref)>threshold;
|
|
herrors=herrors || TestCOrt(z,n)>threshold;
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
//--- check
|
|
if(lambdaref[i+1]<lambdaref[i])
|
|
{
|
|
herrors=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- Test simple EVD: values and full vectors,upper A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVD(au,n,1,true,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
herrors=herrors || TestCProduct(a,n,z,lambdav)>threshold;
|
|
herrors=herrors || TestCOrt(z,n)>threshold;
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
//--- check
|
|
if(lambdav[i+1]<lambdav[i])
|
|
{
|
|
herrors=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- Test simple EVD: values only,lower A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVD(al,n,0,false,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
herrors=herrors || MathAbs(lambdav[i]-lambdaref[i])>threshold;
|
|
//--- Test simple EVD: values only,upper A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVD(au,n,0,true,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
herrors=herrors || MathAbs(lambdav[i]-lambdaref[i])>threshold;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests EVD problem |
|
|
//| DistVals - is True,when eigenvalues are distinct. Is False, |
|
|
//| when we are solving sparse task with lots of zero|
|
|
//| eigenvalues. In such cases some tests related to |
|
|
//| the eigenvectors are not performed. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestEVDUnit::TestSEVDBiProblem(CMatrixDouble &afull,
|
|
CMatrixDouble &al,CMatrixDouble &au,
|
|
const int n,const bool distvals,
|
|
const double threshold,bool &serrors,
|
|
int &failc,int &runs)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int m=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
double v=0;
|
|
double a=0;
|
|
double b=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double lambdav[];
|
|
double lambdaref[];
|
|
//--- create matrix
|
|
CMatrixDouble z;
|
|
CMatrixDouble zref;
|
|
CMatrixDouble a1;
|
|
CMatrixDouble a2;
|
|
CMatrixDouble ar;
|
|
//--- allocation
|
|
ArrayResize(lambdaref,n);
|
|
zref.Resize(n,n);
|
|
a1.Resize(n,n);
|
|
a2.Resize(n,n);
|
|
//--- Reference EVD
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVD(afull,n,1,true,lambdaref,zref))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- Select random interval boundaries.
|
|
//--- If there are non-distinct eigenvalues at the boundaries,
|
|
//--- we move indexes further until values splits. It is done to
|
|
//--- avoid situations where we can't get definite answer.
|
|
i1=CMath::RandomInteger(n);
|
|
i2=i1+CMath::RandomInteger(n-i1);
|
|
//--- calculation
|
|
while(i1>0)
|
|
{
|
|
//--- check
|
|
if(MathAbs(lambdaref[i1-1]-lambdaref[i1])>10*threshold)
|
|
break;
|
|
i1=i1-1;
|
|
}
|
|
while(i2<n-1)
|
|
{
|
|
//--- check
|
|
if(MathAbs(lambdaref[i2+1]-lambdaref[i2])>10*threshold)
|
|
break;
|
|
i2=i2+1;
|
|
}
|
|
//--- Select A,B
|
|
if(i1>0)
|
|
a=0.5*(lambdaref[i1]+lambdaref[i1-1]);
|
|
else
|
|
a=lambdaref[0]-1;
|
|
//--- check
|
|
if(i2<n-1)
|
|
b=0.5*(lambdaref[i2]+lambdaref[i2+1]);
|
|
else
|
|
b=lambdaref[n-1]+1;
|
|
//--- Test interval,no vectors,lower A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVDR(al,n,0,false,a,b,m,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test interval,no vectors,upper A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVDR(au,n,0,true,a,b,m,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test indexes,no vectors,lower A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVDI(al,n,0,false,i1,i2,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test indexes,no vectors,upper A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVDI(au,n,0,true,i1,i2,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test interval,vectors,lower A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVDR(al,n,1,false,a,b,m,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- Distinct eigenvalues,test vectors
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=z[i_][j]*zref[i_][i1+j];
|
|
//--- check
|
|
if(v<0.0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
z[i_].Set(j,-1*z[i_][j]);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
//--- Test interval,vectors,upper A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVDR(au,n,1,true,a,b,m,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- Distinct eigenvalues,test vectors
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=z[i_][j]*zref[i_][i1+j];
|
|
//--- check
|
|
if(v<0.0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
z[i_].Set(j,-1*z[i_][j]);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
//--- Test indexes,vectors,lower A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVDI(al,n,1,false,i1,i2,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- Distinct eigenvalues,test vectors
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=z[i_][j]*zref[i_][i1+j];
|
|
//--- check
|
|
if(v<0.0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
z[i_].Set(j,-1*z[i_][j]);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
//--- Test indexes,vectors,upper A
|
|
Unset1D(lambdav);
|
|
Unset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixEVDI(au,n,1,true,i1,i2,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- Distinct eigenvalues,test vectors
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=z[i_][j]*zref[i_][i1+j];
|
|
//--- check
|
|
if(v<0.0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
z[i_].Set(j,-1*z[i_][j]);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests EVD problem |
|
|
//| DistVals - is True,when eigenvalues are distinct. Is False, |
|
|
//| when we are solving sparse task with lots of zero|
|
|
//| eigenvalues. In such cases some tests related to |
|
|
//| the eigenvectors are not performed. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestEVDUnit::TestHEVDBiProblem(CMatrixComplex &afull,
|
|
CMatrixComplex &al,
|
|
CMatrixComplex &au,const int n,
|
|
const bool distvals,const double threshold,
|
|
bool &herrors,int &failc,int &runs)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int m=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
complex v=0;
|
|
double a=0;
|
|
double b=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double lambdav[];
|
|
double lambdaref[];
|
|
//--- create matrix
|
|
CMatrixComplex z;
|
|
CMatrixComplex zref;
|
|
CMatrixComplex a1;
|
|
CMatrixComplex a2;
|
|
CMatrixComplex ar;
|
|
//--- allocation
|
|
ArrayResize(lambdaref,n);
|
|
zref.Resize(n,n);
|
|
a1.Resize(n,n);
|
|
a2.Resize(n,n);
|
|
//--- Reference EVD
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVD(afull,n,1,true,lambdaref,zref))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- Select random interval boundaries.
|
|
//--- If there are non-distinct eigenvalues at the boundaries,
|
|
//--- we move indexes further until values splits. It is done to
|
|
//--- avoid situations where we can't get definite answer.
|
|
i1=CMath::RandomInteger(n);
|
|
i2=i1+CMath::RandomInteger(n-i1);
|
|
//--- calculation
|
|
while(i1>0)
|
|
{
|
|
//--- check
|
|
if(MathAbs(lambdaref[i1-1]-lambdaref[i1])>10*threshold)
|
|
break;
|
|
i1=i1-1;
|
|
}
|
|
while(i2<n-1)
|
|
{
|
|
//--- check
|
|
if(MathAbs(lambdaref[i2+1]-lambdaref[i2])>10*threshold)
|
|
break;
|
|
i2=i2+1;
|
|
}
|
|
//--- Select A,B
|
|
if(i1>0)
|
|
a=0.5*(lambdaref[i1]+lambdaref[i1-1]);
|
|
else
|
|
a=lambdaref[0]-1;
|
|
//--- check
|
|
if(i2<n-1)
|
|
b=0.5*(lambdaref[i2]+lambdaref[i2+1]);
|
|
else
|
|
b=lambdaref[n-1]+1;
|
|
//--- Test interval,no vectors,lower A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVDR(al,n,0,false,a,b,m,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test interval,no vectors,upper A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVDR(au,n,0,true,a,b,m,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test indexes,no vectors,lower A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVDI(al,n,0,false,i1,i2,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test indexes,no vectors,upper A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVDI(au,n,0,true,i1,i2,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test interval,vectors,lower A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVDR(al,n,1,false,a,b,m,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- Distinct eigenvalues,test vectors
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=z[i_][j]*CMath::Conj(zref[i_][i1+j]);
|
|
v=CMath::Conj(v/CMath::AbsComplex(v));
|
|
//--- calculation
|
|
for(i_=0;i_<=n-1;i_++)
|
|
z[i_].Set(j,v*z[i_][j]);
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
herrors=herrors || CMath::AbsComplex(z[i][j]-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
//--- Test interval,vectors,upper A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVDR(au,n,1,true,a,b,m,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- Distinct eigenvalues,test vectors
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=z[i_][j]*CMath::Conj(zref[i_][i1+j]);
|
|
v=CMath::Conj(v/CMath::AbsComplex(v));
|
|
//--- calculation
|
|
for(i_=0;i_<=n-1;i_++)
|
|
z[i_].Set(j,v*z[i_][j]);
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
herrors=herrors || CMath::AbsComplex(z[i][j]-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
//--- Test indexes,vectors,lower A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVDI(al,n,1,false,i1,i2,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- Distinct eigenvalues,test vectors
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=z[i_][j]*CMath::Conj(zref[i_][i1+j]);
|
|
v=CMath::Conj(v/CMath::AbsComplex(v));
|
|
//--- calculation
|
|
for(i_=0;i_<=n-1;i_++)
|
|
z[i_].Set(j,v*z[i_][j]);
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
herrors=herrors || CMath::AbsComplex(z[i][j]-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
//--- Test indexes,vectors,upper A
|
|
Unset1D(lambdav);
|
|
CUnset2D(z);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::HMatrixEVDI(au,n,1,true,i1,i2,lambdav,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
herrors=herrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- Distinct eigenvalues,test vectors
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=z[i_][j]*CMath::Conj(zref[i_][i1+j]);
|
|
v=CMath::Conj(v/CMath::AbsComplex(v));
|
|
//--- calculation
|
|
for(i_=0;i_<=n-1;i_++)
|
|
z[i_].Set(j,v*z[i_][j]);
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
herrors=herrors || CMath::AbsComplex(z[i][j]-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests EVD problem |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestEVDUnit::TestTdEVDProblem(double &d[],double &e[],
|
|
const int n,const double threshold,
|
|
bool &tderrors,int &failc,int &runs)
|
|
{
|
|
//--- create variables
|
|
bool wsucc;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double lambdav[];
|
|
double ee[];
|
|
double lambda2[];
|
|
//--- create matrix
|
|
CMatrixDouble z;
|
|
CMatrixDouble zref;
|
|
CMatrixDouble a1;
|
|
CMatrixDouble a2;
|
|
//--- allocation
|
|
ArrayResize(lambdav,n);
|
|
ArrayResize(lambda2,n);
|
|
zref.Resize(n,n);
|
|
a1.Resize(n,n);
|
|
a2.Resize(n,n);
|
|
//--- check
|
|
if(n>1)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(ee,n-1);
|
|
}
|
|
//--- Test simple EVD: values and full vectors
|
|
for(i=0;i<=n-1;i++)
|
|
lambdav[i]=d[i];
|
|
for(i=0;i<=n-2;i++)
|
|
ee[i]=e[i];
|
|
runs=runs+1;
|
|
wsucc=CEigenVDetect::SMatrixTdEVD(lambdav,ee,n,2,z);
|
|
//--- check
|
|
if(!wsucc)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
tderrors=tderrors || TdTestProduct(d,e,n,z,lambdav)>threshold;
|
|
tderrors=tderrors || TestOrt(z,n)>threshold;
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
//--- check
|
|
if(lambdav[i+1]<lambdav[i])
|
|
{
|
|
tderrors=true;
|
|
return;
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
zref[i].Set(j,z[i][j]);
|
|
}
|
|
//--- Test values only variant
|
|
for(i=0;i<=n-1;i++)
|
|
lambda2[i]=d[i];
|
|
for(i=0;i<=n-2;i++)
|
|
ee[i]=e[i];
|
|
runs=runs+1;
|
|
wsucc=CEigenVDetect::SMatrixTdEVD(lambda2,ee,n,0,z);
|
|
//--- check
|
|
if(!wsucc)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
tderrors=tderrors || MathAbs(lambda2[i]-lambdav[i])>threshold;
|
|
//--- Test multiplication variant
|
|
for(i=0;i<=n-1;i++)
|
|
lambda2[i]=d[i];
|
|
for(i=0;i<=n-2;i++)
|
|
ee[i]=e[i];
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a1[i].Set(j,2*CMath::RandomReal()-1);
|
|
a2[i].Set(j,a1[i][j]);
|
|
}
|
|
}
|
|
runs=runs+1;
|
|
wsucc=CEigenVDetect::SMatrixTdEVD(lambda2,ee,n,1,a1);
|
|
//--- check
|
|
if(!wsucc)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
tderrors=tderrors || MathAbs(lambda2[i]-lambdav[i])>threshold;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a2[i][i_]*zref[i_][j];
|
|
//--- next line is a bit complicated because
|
|
//--- depending on algorithm used we can get either
|
|
//--- z or -z as eigenvector. so we compare result
|
|
//--- with both A*ZRef and -A*ZRef
|
|
tderrors=tderrors || (MathAbs(v-a1[i][j])>threshold && MathAbs(v+a1[i][j])>threshold);
|
|
}
|
|
}
|
|
//--- Test first row variant
|
|
for(i=0;i<=n-1;i++)
|
|
lambda2[i]=d[i];
|
|
for(i=0;i<=n-2;i++)
|
|
ee[i]=e[i];
|
|
runs=runs+1;
|
|
wsucc=CEigenVDetect::SMatrixTdEVD(lambda2,ee,n,3,z);
|
|
//--- check
|
|
if(!wsucc)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
tderrors=tderrors || MathAbs(lambda2[i]-lambdav[i])>threshold;
|
|
//--- next line is a bit complicated because
|
|
//--- depending on algorithm used we can get either
|
|
//--- z or -z as eigenvector. so we compare result
|
|
//--- with both z and -z
|
|
tderrors=tderrors || (MathAbs(z[0][i]-zref[0][i])>threshold && MathAbs(z[0][i]+zref[0][i])>threshold);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests EVD problem |
|
|
//| DistVals - is True,when eigenvalues are distinct. Is False, |
|
|
//| when we are solving sparse task with lots of zero|
|
|
//| eigenvalues. In such cases some tests related to |
|
|
//| the eigenvectors are not performed. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestEVDUnit::TestTdEVDBiProblem(double &d[],double &e[],
|
|
const int n,const bool distvals,
|
|
const double threshold,bool &serrors,
|
|
int &failc,int &runs)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int m=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
double v=0;
|
|
double a=0;
|
|
double b=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double lambdav[];
|
|
double lambdaref[];
|
|
//--- create matrix
|
|
CMatrixDouble z;
|
|
CMatrixDouble zref;
|
|
CMatrixDouble a1;
|
|
CMatrixDouble a2;
|
|
CMatrixDouble ar;
|
|
//--- allocation
|
|
ArrayResize(lambdaref,n);
|
|
zref.Resize(n,n);
|
|
a1.Resize(n,n);
|
|
a2.Resize(n,n);
|
|
//--- Reference EVD
|
|
ArrayResize(lambdaref,n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
lambdaref[i_]=d[i_];
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixTdEVD(lambdaref,e,n,2,zref))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- Select random interval boundaries.
|
|
//--- If there are non-distinct eigenvalues at the boundaries,
|
|
//--- we move indexes further until values splits. It is done to
|
|
//--- avoid situations where we can't get definite answer.
|
|
i1=CMath::RandomInteger(n);
|
|
i2=i1+CMath::RandomInteger(n-i1);
|
|
//--- calculation
|
|
while(i1>0)
|
|
{
|
|
//--- check
|
|
if(MathAbs(lambdaref[i1-1]-lambdaref[i1])>10*threshold)
|
|
break;
|
|
i1=i1-1;
|
|
}
|
|
while(i2<n-1)
|
|
{
|
|
//--- check
|
|
if(MathAbs(lambdaref[i2+1]-lambdaref[i2])>10*threshold)
|
|
break;
|
|
i2=i2+1;
|
|
}
|
|
//--- Test different combinations
|
|
//--- Select A,B
|
|
if(i1>0)
|
|
a=0.5*(lambdaref[i1]+lambdaref[i1-1]);
|
|
else
|
|
a=lambdaref[0]-1;
|
|
//--- check
|
|
if(i2<n-1)
|
|
b=0.5*(lambdaref[i2]+lambdaref[i2+1]);
|
|
else
|
|
b=lambdaref[n-1]+1;
|
|
//--- Test interval,no vectors
|
|
ArrayResize(lambdav,n);
|
|
for(i=0;i<=n-1;i++)
|
|
lambdav[i]=d[i];
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixTdEVDR(lambdav,e,n,0,a,b,m,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test indexes,no vectors
|
|
ArrayResize(lambdav,n);
|
|
for(i=0;i<=n-1;i++)
|
|
lambdav[i]=d[i];
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixTdEVDI(lambdav,e,n,0,i1,i2,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- Test interval,transform vectors
|
|
ArrayResize(lambdav,n);
|
|
for(i=0;i<=n-1;i++)
|
|
lambdav[i]=d[i];
|
|
//--- allocation
|
|
a1.Resize(n,n);
|
|
a2.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a1[i].Set(j,2*CMath::RandomReal()-1);
|
|
a2[i].Set(j,a1[i][j]);
|
|
}
|
|
}
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixTdEVDR(lambdav,e,n,1,a,b,m,a1))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- allocation
|
|
ar.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a2[i][i_]*zref[i_][i1+j];
|
|
ar[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a1[i_][j]*ar[i_][j];
|
|
//--- check
|
|
if(v<0.0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ar[i_].Set(j,-1*ar[i_][j]);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
serrors=serrors || MathAbs(a1[i][j]-ar[i][j])>threshold;
|
|
}
|
|
}
|
|
//--- Test indexes,transform vectors
|
|
ArrayResize(lambdav,n);
|
|
for(i=0;i<=n-1;i++)
|
|
lambdav[i]=d[i];
|
|
//--- allocation
|
|
a1.Resize(n,n);
|
|
a2.Resize(n,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a1[i].Set(j,2*CMath::RandomReal()-1);
|
|
a2[i].Set(j,a1[i][j]);
|
|
}
|
|
}
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixTdEVDI(lambdav,e,n,1,i1,i2,a1))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
//--- allocation
|
|
ar.Resize(n,m);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a2[i][i_]*zref[i_][i1+j];
|
|
ar[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a1[i_][j]*ar[i_][j];
|
|
//--- check
|
|
if(v<0.0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ar[i_].Set(j,-1*ar[i_][j]);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
serrors=serrors || MathAbs(a1[i][j]-ar[i][j])>threshold;
|
|
}
|
|
}
|
|
//--- Test interval,do not transform vectors
|
|
ArrayResize(lambdav,n);
|
|
for(i=0;i<=n-1;i++)
|
|
lambdav[i]=d[i];
|
|
//--- allocation
|
|
z.Resize(1,1);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixTdEVDR(lambdav,e,n,2,a,b,m,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(m!=i2-i1+1)
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=z[i_][j]*zref[i_][i1+j];
|
|
//--- check
|
|
if(v<0.0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
z[i_].Set(j,-1*z[i_][j]);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
//--- Test indexes,do not transform vectors
|
|
ArrayResize(lambdav,n);
|
|
for(i=0;i<=n-1;i++)
|
|
lambdav[i]=d[i];
|
|
//--- allocation
|
|
z.Resize(1,1);
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::SMatrixTdEVDI(lambdav,e,n,2,i1,i2,z))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
m=i2-i1+1;
|
|
//--- search errors
|
|
for(k=0;k<=m-1;k++)
|
|
serrors=serrors || MathAbs(lambdav[k]-lambdaref[i1+k])>threshold;
|
|
//--- check
|
|
if(distvals)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=z[i_][j]*zref[i_][i1+j];
|
|
//--- check
|
|
if(v<0.0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
z[i_].Set(j,-1*z[i_][j]);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
serrors=serrors || MathAbs(z[i][j]-zref[i][i1+j])>threshold;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Non-symmetric problem |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestEVDUnit::TestNSEVDProblem(CMatrixDouble &a,const int n,
|
|
const double threshold,
|
|
bool &nserrors,int &failc,
|
|
int &runs)
|
|
{
|
|
//--- create variables
|
|
double mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int vjob=0;
|
|
bool needl;
|
|
bool needr;
|
|
double curwr=0;
|
|
double curwi=0;
|
|
double vt=0;
|
|
double tmp=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double wr0[];
|
|
double wi0[];
|
|
double wr1[];
|
|
double wi1[];
|
|
double wr0s[];
|
|
double wi0s[];
|
|
double wr1s[];
|
|
double wi1s[];
|
|
double vec1r[];
|
|
double vec1i[];
|
|
double vec2r[];
|
|
double vec2i[];
|
|
double vec3r[];
|
|
double vec3i[];
|
|
//--- create matrix
|
|
CMatrixDouble vl;
|
|
CMatrixDouble vr;
|
|
//--- allocation
|
|
ArrayResize(vec1r,n);
|
|
ArrayResize(vec2r,n);
|
|
ArrayResize(vec3r,n);
|
|
ArrayResize(vec1i,n);
|
|
ArrayResize(vec2i,n);
|
|
ArrayResize(vec3i,n);
|
|
ArrayResize(wr0s,n);
|
|
ArrayResize(wr1s,n);
|
|
ArrayResize(wi0s,n);
|
|
ArrayResize(wi1s,n);
|
|
//--- initialization
|
|
mx=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(a[i][j])>mx)
|
|
mx=MathAbs(a[i][j]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(mx==0.0)
|
|
mx=1;
|
|
//--- Load values-only
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::RMatrixEVD(a,n,0,wr0,wi0,vl,vr))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- Test different jobs
|
|
for(vjob=1;vjob<=3;vjob++)
|
|
{
|
|
needr=vjob==1 || vjob==3;
|
|
needl=vjob==2 || vjob==3;
|
|
runs=runs+1;
|
|
//--- check
|
|
if(!CEigenVDetect::RMatrixEVD(a,n,vjob,wr1,wi1,vl,vr))
|
|
{
|
|
failc=failc+1;
|
|
return;
|
|
}
|
|
//--- Test values:
|
|
//--- 1. sort by real part
|
|
//--- 2. test
|
|
for(i_=0;i_<=n-1;i_++)
|
|
wr0s[i_]=wr0[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
wi0s[i_]=wi0[i_];
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-2-i;j++)
|
|
{
|
|
//--- check
|
|
if(wr0s[j]>wr0s[j+1])
|
|
{
|
|
tmp=wr0s[j];
|
|
wr0s[j]=wr0s[j+1];
|
|
wr0s[j+1]=tmp;
|
|
tmp=wi0s[j];
|
|
wi0s[j]=wi0s[j+1];
|
|
wi0s[j+1]=tmp;
|
|
}
|
|
}
|
|
}
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
wr1s[i_]=wr1[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
wi1s[i_]=wi1[i_];
|
|
//--- swap
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-2-i;j++)
|
|
{
|
|
//--- check
|
|
if(wr1s[j]>wr1s[j+1])
|
|
{
|
|
tmp=wr1s[j];
|
|
wr1s[j]=wr1s[j+1];
|
|
wr1s[j+1]=tmp;
|
|
tmp=wi1s[j];
|
|
wi1s[j]=wi1s[j+1];
|
|
wi1s[j+1]=tmp;
|
|
}
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
nserrors=nserrors || MathAbs(wr0s[i]-wr1s[i])>threshold;
|
|
nserrors=nserrors || MathAbs(wi0s[i]-wi1s[i])>threshold;
|
|
}
|
|
//--- Test right vectors
|
|
if(needr)
|
|
{
|
|
k=0;
|
|
//--- calculation
|
|
while(k<=n-1)
|
|
{
|
|
//--- check
|
|
if(wi1[k]==0.0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec1r[i_]=vr[i_][k];
|
|
for(i=0;i<=n-1;i++)
|
|
vec1i[i]=0;
|
|
curwr=wr1[k];
|
|
curwi=0;
|
|
}
|
|
//--- check
|
|
if(wi1[k]>0.0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec1r[i_]=vr[i_][k];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec1i[i_]=vr[i_][k+1];
|
|
curwr=wr1[k];
|
|
curwi=wi1[k];
|
|
}
|
|
//--- check
|
|
if(wi1[k]<0.0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec1r[i_]=vr[i_][k-1];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec1i[i_]=-vr[i_][k];
|
|
curwr=wr1[k];
|
|
curwi=wi1[k];
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
vt=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vt+=a[i][i_]*vec1r[i_];
|
|
vec2r[i]=vt;
|
|
vt=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vt+=a[i][i_]*vec1i[i_];
|
|
vec2i[i]=vt;
|
|
}
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec3r[i_]=curwr*vec1r[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec3r[i_]=vec3r[i_]-curwi*vec1i[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec3i[i_]=curwi*vec1r[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec3i[i_]=vec3i[i_]+curwr*vec1i[i_];
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
nserrors=nserrors || MathAbs(vec2r[i]-vec3r[i])>threshold;
|
|
nserrors=nserrors || MathAbs(vec2i[i]-vec3i[i])>threshold;
|
|
}
|
|
k=k+1;
|
|
}
|
|
}
|
|
//--- Test left vectors
|
|
if(needl)
|
|
{
|
|
k=0;
|
|
//--- calculation
|
|
while(k<=n-1)
|
|
{
|
|
//--- check
|
|
if(wi1[k]==0.0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec1r[i_]=vl[i_][k];
|
|
for(i=0;i<=n-1;i++)
|
|
vec1i[i]=0;
|
|
curwr=wr1[k];
|
|
curwi=0;
|
|
}
|
|
//--- check
|
|
if(wi1[k]>0.0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec1r[i_]=vl[i_][k];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec1i[i_]=vl[i_][k+1];
|
|
curwr=wr1[k];
|
|
curwi=wi1[k];
|
|
}
|
|
//--- check
|
|
if(wi1[k]<0.0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec1r[i_]=vl[i_][k-1];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec1i[i_]=-vl[i_][k];
|
|
curwr=wr1[k];
|
|
curwi=wi1[k];
|
|
}
|
|
//--- calculation
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
vt=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vt+=vec1r[i_]*a[i_][j];
|
|
vec2r[j]=vt;
|
|
vt=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vt+=vec1i[i_]*a[i_][j];
|
|
vec2i[j]=-vt;
|
|
}
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec3r[i_]=curwr*vec1r[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec3r[i_]=vec3r[i_]+curwi*vec1i[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec3i[i_]=curwi*vec1r[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vec3i[i_]=vec3i[i_]-curwr*vec1i[i_];
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
nserrors=nserrors || MathAbs(vec2r[i]-vec3r[i])>threshold;
|
|
nserrors=nserrors || MathAbs(vec2i[i]-vec3i[i])>threshold;
|
|
}
|
|
k=k+1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing EVD subroutines for one N |
|
|
//| NOTES: |
|
|
//| * BIThreshold is a threshold for bisection-and-inverse-iteration |
|
|
//| subroutines. special threshold is needed because these |
|
|
//| subroutines may have much more larger error than QR-based |
|
|
//| algorithms. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestEVDUnit::TestEVDSet(const int n,const double threshold,
|
|
double bithreshold,int &failc,
|
|
int &runs,bool &nserrors,bool &serrors,
|
|
bool &herrors,bool &tderrors,bool &sbierrors,
|
|
bool &hbierrors,bool &tdbierrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int mkind=0;
|
|
//--- create arrays
|
|
double d[];
|
|
double e[];
|
|
//--- create matrix
|
|
CMatrixDouble ra;
|
|
CMatrixDouble ral;
|
|
CMatrixDouble rau;
|
|
CMatrixComplex ca;
|
|
CMatrixComplex cal;
|
|
CMatrixComplex cau;
|
|
//--- Test symmetric problems
|
|
ra.Resize(n,n);
|
|
ral.Resize(n,n);
|
|
rau.Resize(n,n);
|
|
ca.Resize(n,n);
|
|
cal.Resize(n,n);
|
|
cau.Resize(n,n);
|
|
//--- Zero matrices
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
ra[i].Set(j,0);
|
|
ca[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- function calls
|
|
RMatrixSymmetricSplit(ra,n,ral,rau);
|
|
CMatrixHermitianSplit(ca,n,cal,cau);
|
|
TestSEVDProblem(ra,ral,rau,n,threshold,serrors,failc,runs);
|
|
TestHEVDProblem(ca,cal,cau,n,threshold,herrors,failc,runs);
|
|
TestSEVDBiProblem(ra,ral,rau,n,false,bithreshold,sbierrors,failc,runs);
|
|
TestHEVDBiProblem(ca,cal,cau,n,false,bithreshold,hbierrors,failc,runs);
|
|
//--- Random matrix
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i+1;j<=n-1;j++)
|
|
{
|
|
ra[i].Set(j,2*CMath::RandomReal()-1);
|
|
ca[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
ca[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
ra[j].Set(i,ra[i][j]);
|
|
ca[j].Set(i,CMath::Conj(ca[i][j]));
|
|
}
|
|
//--- change values
|
|
ra[i].Set(i,2*CMath::RandomReal()-1);
|
|
ca[i].Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function calls
|
|
RMatrixSymmetricSplit(ra,n,ral,rau);
|
|
CMatrixHermitianSplit(ca,n,cal,cau);
|
|
TestSEVDProblem(ra,ral,rau,n,threshold,serrors,failc,runs);
|
|
TestHEVDProblem(ca,cal,cau,n,threshold,herrors,failc,runs);
|
|
//--- Random diagonally dominant matrix with distinct eigenvalues
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i+1;j<=n-1;j++)
|
|
{
|
|
ra[i].Set(j,0.1*(2*CMath::RandomReal()-1)/n);
|
|
ca[i].SetRe(j,0.1*(2*CMath::RandomReal()-1)/n);
|
|
ca[i].SetIm(j,0.1*(2*CMath::RandomReal()-1)/n);
|
|
ra[j].Set(i,ra[i][j]);
|
|
ca[j].Set(i,CMath::Conj(ca[i][j]));
|
|
}
|
|
//--- change values
|
|
ra[i].Set(i,0.1*(2*CMath::RandomReal()-1)+i);
|
|
ca[i].Set(i,0.1*(2*CMath::RandomReal()-1)+i);
|
|
}
|
|
//--- function calls
|
|
RMatrixSymmetricSplit(ra,n,ral,rau);
|
|
CMatrixHermitianSplit(ca,n,cal,cau);
|
|
TestSEVDProblem(ra,ral,rau,n,threshold,serrors,failc,runs);
|
|
TestHEVDProblem(ca,cal,cau,n,threshold,herrors,failc,runs);
|
|
TestSEVDBiProblem(ra,ral,rau,n,true,bithreshold,sbierrors,failc,runs);
|
|
TestHEVDBiProblem(ca,cal,cau,n,true,bithreshold,hbierrors,failc,runs);
|
|
//--- Sparse matrices
|
|
RMatrixFillSparseA(ra,n,n,0.995);
|
|
CMatrixFillSparseA(ca,n,n,0.995);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i+1;j<=n-1;j++)
|
|
{
|
|
ra[j].Set(i,ra[i][j]);
|
|
ca[j].Set(i,CMath::Conj(ca[i][j]));
|
|
}
|
|
ca[i].SetIm(i,0);
|
|
}
|
|
//--- function calls
|
|
RMatrixSymmetricSplit(ra,n,ral,rau);
|
|
CMatrixHermitianSplit(ca,n,cal,cau);
|
|
TestSEVDProblem(ra,ral,rau,n,threshold,serrors,failc,runs);
|
|
TestHEVDProblem(ca,cal,cau,n,threshold,herrors,failc,runs);
|
|
TestSEVDBiProblem(ra,ral,rau,n,false,bithreshold,sbierrors,failc,runs);
|
|
TestHEVDBiProblem(ca,cal,cau,n,false,bithreshold,hbierrors,failc,runs);
|
|
//--- testing tridiagonal problems
|
|
for(mkind=0;mkind<=7;mkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(d,n);
|
|
if(n>1)
|
|
ArrayResize(e,n-1);
|
|
//--- check
|
|
if(mkind==0)
|
|
{
|
|
//--- Zero matrix
|
|
for(i=0;i<=n-1;i++)
|
|
d[i]=0;
|
|
for(i=0;i<=n-2;i++)
|
|
e[i]=0;
|
|
}
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- Diagonal matrix
|
|
for(i=0;i<=n-1;i++)
|
|
d[i]=2*CMath::RandomReal()-1;
|
|
for(i=0;i<=n-2;i++)
|
|
e[i]=0;
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
//--- Off-diagonal matrix
|
|
for(i=0;i<=n-1;i++)
|
|
d[i]=0;
|
|
for(i=0;i<=n-2;i++)
|
|
e[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- check
|
|
if(mkind==3)
|
|
{
|
|
//--- Dense matrix with blocks
|
|
for(i=0;i<=n-1;i++)
|
|
d[i]=2*CMath::RandomReal()-1;
|
|
for(i=0;i<=n-2;i++)
|
|
e[i]=2*CMath::RandomReal()-1;
|
|
//--- change values
|
|
j=1;
|
|
i=2;
|
|
while(j<=n-2)
|
|
{
|
|
e[j]=0;
|
|
j=j+i;
|
|
i=i+1;
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==4)
|
|
{
|
|
//--- dense matrix
|
|
for(i=0;i<=n-1;i++)
|
|
d[i]=2*CMath::RandomReal()-1;
|
|
for(i=0;i<=n-2;i++)
|
|
e[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- check
|
|
if(mkind==5)
|
|
{
|
|
//--- Diagonal matrix with distinct eigenvalues
|
|
for(i=0;i<=n-1;i++)
|
|
d[i]=0.1*(2*CMath::RandomReal()-1)+i;
|
|
for(i=0;i<=n-2;i++)
|
|
e[i]=0;
|
|
}
|
|
//--- check
|
|
if(mkind==6)
|
|
{
|
|
//--- Off-diagonal matrix with distinct eigenvalues
|
|
for(i=0;i<=n-1;i++)
|
|
d[i]=0;
|
|
for(i=0;i<=n-2;i++)
|
|
e[i]=0.1*(2*CMath::RandomReal()-1)+i+1;
|
|
}
|
|
//--- check
|
|
if(mkind==7)
|
|
{
|
|
//--- dense matrix with distinct eigenvalues
|
|
for(i=0;i<=n-1;i++)
|
|
d[i]=0.1*(2*CMath::RandomReal()-1)+i+1;
|
|
for(i=0;i<=n-2;i++)
|
|
e[i]=0.1*(2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function calls
|
|
TestTdEVDProblem(d,e,n,threshold,tderrors,failc,runs);
|
|
TestTdEVDBiProblem(d,e,n,(mkind==5 || mkind==6) || mkind==7,bithreshold,tdbierrors,failc,runs);
|
|
}
|
|
//--- Test non-symmetric problems
|
|
//--- Test non-symmetric problems: zero,random,sparse matrices.
|
|
ra.Resize(n,n);
|
|
ca.Resize(n,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
ra[i].Set(j,0);
|
|
ca[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- function call
|
|
TestNSEVDProblem(ra,n,threshold,nserrors,failc,runs);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
ra[i].Set(j,2*CMath::RandomReal()-1);
|
|
ca[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
ca[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- function calls
|
|
TestNSEVDProblem(ra,n,threshold,nserrors,failc,runs);
|
|
RMatrixFillSparseA(ra,n,n,0.995);
|
|
CMatrixFillSparseA(ca,n,n,0.995);
|
|
TestNSEVDProblem(ra,n,threshold,nserrors,failc,runs);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMatGen |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMatGenUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void Unset2D(CMatrixDouble &a);
|
|
static void Unset2DC(CMatrixComplex &a);
|
|
static bool IsSPD(CMatrixDouble &ca,const int n,const bool isupper);
|
|
static bool IsHPD(CMatrixComplex &ca,const int n);
|
|
static double SVDCond(CMatrixDouble &a,const int n);
|
|
static bool ObsoleteSVDDecomposition(CMatrixDouble &a,const int m,const int n,double &w[],CMatrixDouble &v);
|
|
static double ExtSign(const double a,const double b);
|
|
static double MyMax(const double a,const double b);
|
|
static double PyThag(const double a,const double b);
|
|
public:
|
|
//--- class constant
|
|
static const int m_maxsvditerations;
|
|
//--- constructor, destructor
|
|
CTestMatGenUnit(void);
|
|
~CTestMatGenUnit(void);
|
|
//--- public method
|
|
static bool TestMatGen(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize constant |
|
|
//+------------------------------------------------------------------+
|
|
const int CTestMatGenUnit::m_maxsvditerations=60;
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestMatGenUnit::CTestMatGenUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestMatGenUnit::~CTestMatGenUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMatGen |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMatGenUnit::TestMatGen(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int maxn=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
int equal_number=0;
|
|
bool waserrors;
|
|
double cond=0;
|
|
double threshold=0;
|
|
double vt=0;
|
|
complex ct=0;
|
|
double minw=0;
|
|
double maxw=0;
|
|
bool serr;
|
|
bool herr;
|
|
bool spderr;
|
|
bool hpderr;
|
|
bool rerr;
|
|
bool cerr;
|
|
int i_=0;
|
|
//--- create array
|
|
double w[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble b;
|
|
CMatrixDouble u;
|
|
CMatrixDouble v;
|
|
CMatrixComplex ca;
|
|
CMatrixComplex cb;
|
|
CMatrixDouble r1;
|
|
CMatrixDouble r2;
|
|
CMatrixComplex c1;
|
|
CMatrixComplex c2;
|
|
//--- initialization
|
|
rerr=false;
|
|
cerr=false;
|
|
serr=false;
|
|
herr=false;
|
|
spderr=false;
|
|
hpderr=false;
|
|
waserrors=false;
|
|
maxn=20;
|
|
passcount=15;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- Testing orthogonal
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- allocation
|
|
r1.Resize(n,2*n);
|
|
r2.Resize(2*n,n);
|
|
c1.Resize(n,2*n);
|
|
c2.Resize(2*n,n);
|
|
//--- Random orthogonal,real
|
|
Unset2D(a);
|
|
Unset2D(b);
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonal(n,a);
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonal(n,b);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- orthogonality test
|
|
vt=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vt+=a[i][i_]*a[j][i_];
|
|
//--- check
|
|
if(i==j)
|
|
rerr=rerr || MathAbs(vt-1)>threshold;
|
|
else
|
|
rerr=rerr || MathAbs(vt)>threshold;
|
|
//--- change value
|
|
vt=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vt+=b[i][i_]*b[j][i_];
|
|
//--- check
|
|
if(i==j)
|
|
rerr=rerr || MathAbs(vt-1)>threshold;
|
|
else
|
|
rerr=rerr || MathAbs(vt)>threshold;
|
|
//--- test for difference in A and B
|
|
if(n>=2)
|
|
rerr=rerr || a[i][j]==b[i][j];
|
|
}
|
|
}
|
|
//--- Random orthogonal,complex
|
|
Unset2DC(ca);
|
|
Unset2DC(cb);
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonal(n,ca);
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonal(n,cb);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- orthogonality test
|
|
ct=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ct+=ca[i][i_]*CMath::Conj(ca[j][i_]);
|
|
//--- check
|
|
if(i==j)
|
|
cerr=cerr || CMath::AbsComplex(ct-1)>threshold;
|
|
else
|
|
cerr=cerr || CMath::AbsComplex(ct)>threshold;
|
|
//--- change value
|
|
ct=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ct+=cb[i][i_]*CMath::Conj(cb[j][i_]);
|
|
//--- check
|
|
if(i==j)
|
|
cerr=cerr || CMath::AbsComplex(ct-1)>threshold;
|
|
else
|
|
cerr=cerr || CMath::AbsComplex(ct)>threshold;
|
|
//--- test for difference in A and B
|
|
if(n>=2)
|
|
cerr=cerr || ca[i][j]==cb[i][j];
|
|
}
|
|
}
|
|
//--- From the right real tests:
|
|
//--- 1. E*Q is orthogonal
|
|
//--- 2. Q1<>Q2 (routine result is changing)
|
|
//--- 3. (E E)'*Q=(Q' Q')' (correct handling of non-square matrices)
|
|
Unset2D(a);
|
|
Unset2D(b);
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
b.Resize(n,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a[i].Set(j,0);
|
|
b[i].Set(j,0);
|
|
}
|
|
a[i].Set(i,1);
|
|
b[i].Set(i,1);
|
|
}
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonalFromTheRight(a,n,n);
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonalFromTheRight(b,n,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- orthogonality test
|
|
vt=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vt+=a[i][i_]*a[j][i_];
|
|
//--- check
|
|
if(i==j)
|
|
rerr=rerr || MathAbs(vt-1)>threshold;
|
|
else
|
|
rerr=rerr || MathAbs(vt)>threshold;
|
|
//--- change value
|
|
vt=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vt+=b[i][i_]*b[j][i_];
|
|
//--- check
|
|
if(i==j)
|
|
rerr=rerr || MathAbs(vt-1)>threshold;
|
|
else
|
|
rerr=rerr || MathAbs(vt)>threshold;
|
|
//--- test for difference in A and B
|
|
if(n>=2)
|
|
rerr=rerr || a[i][j]==b[i][j];
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
r2[i].Set(j,2*CMath::RandomReal()-1);
|
|
r2[i+n].Set(j,r2[i][j]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonalFromTheRight(r2,2*n,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
rerr=rerr || MathAbs(r2[i+n][j]-r2[i][j])>threshold;
|
|
}
|
|
//--- From the left real tests:
|
|
//--- 1. Q*E is orthogonal
|
|
//--- 2. Q1<>Q2 (routine result is changing)
|
|
//--- 3. Q*(E E)=(Q Q) (correct handling of non-square matrices)
|
|
Unset2D(a);
|
|
Unset2D(b);
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
b.Resize(n,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a[i].Set(j,0);
|
|
b[i].Set(j,0);
|
|
}
|
|
a[i].Set(i,1);
|
|
b[i].Set(i,1);
|
|
}
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonalFromTheLeft(a,n,n);
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonalFromTheLeft(b,n,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- orthogonality test
|
|
vt=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vt+=a[i][i_]*a[j][i_];
|
|
//--- check
|
|
if(i==j)
|
|
rerr=rerr || MathAbs(vt-1)>threshold;
|
|
else
|
|
rerr=rerr || MathAbs(vt)>threshold;
|
|
//--- change value
|
|
vt=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vt+=b[i][i_]*b[j][i_];
|
|
//--- check
|
|
if(i==j)
|
|
rerr=rerr || MathAbs(vt-1)>threshold;
|
|
else
|
|
rerr=rerr || MathAbs(vt)>threshold;
|
|
//--- test for difference in A and B
|
|
if(n>=2)
|
|
rerr=rerr || a[i][j]==b[i][j];
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
r1[i].Set(j,2*CMath::RandomReal()-1);
|
|
r1[i].Set(j+n,r1[i][j]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonalFromTheLeft(r1,n,2*n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
rerr=rerr || MathAbs(r1[i][j]-r1[i][j+n])>threshold;
|
|
}
|
|
//--- From the right complex tests:
|
|
//--- 1. E*Q is orthogonal
|
|
//--- 2. Q1<>Q2 (routine result is changing)
|
|
//--- 3. (E E)'*Q=(Q' Q')' (correct handling of non-square matrices)
|
|
Unset2DC(ca);
|
|
Unset2DC(cb);
|
|
//--- allocation
|
|
ca.Resize(n,n);
|
|
cb.Resize(n,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
ca[i].Set(j,0);
|
|
cb[i].Set(j,0);
|
|
}
|
|
ca[i].Set(i,1);
|
|
cb[i].Set(i,1);
|
|
}
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonalFromTheRight(ca,n,n);
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonalFromTheRight(cb,n,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- orthogonality test
|
|
ct=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ct+=ca[i][i_]*CMath::Conj(ca[j][i_]);
|
|
//--- check
|
|
if(i==j)
|
|
cerr=cerr || CMath::AbsComplex(ct-1)>threshold;
|
|
else
|
|
cerr=cerr || CMath::AbsComplex(ct)>threshold;
|
|
//--- change value
|
|
ct=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ct+=cb[i][i_]*CMath::Conj(cb[j][i_]);
|
|
//--- check
|
|
if(i==j)
|
|
cerr=cerr || CMath::AbsComplex(ct-1)>threshold;
|
|
else
|
|
cerr=cerr || CMath::AbsComplex(ct)>threshold;
|
|
//--- test for difference in A and B
|
|
cerr=cerr || ca[i][j]==cb[i][j];
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
c2[i].Set(j,2*CMath::RandomReal()-1);
|
|
c2[i+n].Set(j,c2[i][j]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonalFromTheRight(c2,2*n,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
cerr=cerr || CMath::AbsComplex(c2[i+n][j]-c2[i][j])>threshold;
|
|
}
|
|
//--- From the left complex tests:
|
|
//--- 1. Q*E is orthogonal
|
|
//--- 2. Q1<>Q2 (routine result is changing)
|
|
//--- 3. Q*(E E)=(Q Q) (correct handling of non-square matrices)
|
|
Unset2DC(ca);
|
|
Unset2DC(cb);
|
|
//--- allocation
|
|
ca.Resize(n,n);
|
|
cb.Resize(n,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
ca[i].Set(j,0);
|
|
cb[i].Set(j,0);
|
|
}
|
|
ca[i].Set(i,1);
|
|
cb[i].Set(i,1);
|
|
}
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonalFromTheLeft(ca,n,n);
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonalFromTheLeft(cb,n,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- orthogonality test
|
|
ct=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ct+=ca[i][i_]*CMath::Conj(ca[j][i_]);
|
|
//--- check
|
|
if(i==j)
|
|
cerr=cerr || CMath::AbsComplex(ct-1)>threshold;
|
|
else
|
|
cerr=cerr || CMath::AbsComplex(ct)>threshold;
|
|
//--- change value
|
|
ct=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ct+=cb[i][i_]*CMath::Conj(cb[j][i_]);
|
|
//--- check
|
|
if(i==j)
|
|
cerr=cerr || CMath::AbsComplex(ct-1)>threshold;
|
|
else
|
|
cerr=cerr || CMath::AbsComplex(ct)>threshold;
|
|
//--- test for difference in A and B
|
|
cerr=cerr || ca[i][j]==cb[i][j];
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
c1[i].Set(j,2*CMath::RandomReal()-1);
|
|
c1[i].Set(j+n,c1[i][j]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonalFromTheLeft(c1,n,2*n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
cerr=cerr || CMath::AbsComplex(c1[i][j]-c1[i][j+n])>threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- Testing GCond
|
|
for(n=2;n<=maxn;n++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- real test
|
|
Unset2D(a);
|
|
cond=MathExp(MathLog(1000)*CMath::RandomReal());
|
|
//--- function call
|
|
CMatGen::RMatrixRndCond(n,cond,a);
|
|
//--- allocation
|
|
b.Resize(n+1,n+1);
|
|
//--- change values
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
b[i].Set(j,a[i-1][j-1]);
|
|
}
|
|
//--- check
|
|
if(ObsoleteSVDDecomposition(b,n,n,w,v))
|
|
{
|
|
maxw=w[1];
|
|
minw=w[1];
|
|
for(i=2;i<=n;i++)
|
|
{
|
|
//--- check
|
|
if(w[i]>maxw)
|
|
maxw=w[i];
|
|
//--- check
|
|
if(w[i]<minw)
|
|
minw=w[i];
|
|
}
|
|
vt=maxw/minw/cond;
|
|
//--- check
|
|
if(MathAbs(MathLog(vt))>MathLog(1+threshold))
|
|
rerr=true;
|
|
}
|
|
}
|
|
}
|
|
//--- Symmetric/SPD
|
|
//--- N=2 .. 30
|
|
for(n=2;n<=maxn;n++)
|
|
{
|
|
//--- SPD matrices
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Generate A
|
|
Unset2D(a);
|
|
cond=MathExp(MathLog(1000)*CMath::RandomReal());
|
|
//--- function call
|
|
CMatGen::SPDMatrixRndCond(n,cond,a);
|
|
//--- test condition number
|
|
spderr=spderr || SVDCond(a,n)/cond-1>threshold;
|
|
//--- test SPD
|
|
spderr=spderr || !IsSPD(a,n,true);
|
|
//--- test that A is symmetic
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
spderr=spderr || MathAbs(a[i][j]-a[j][i])>threshold;
|
|
}
|
|
//--- test for difference between A and B (subsequent matrix)
|
|
Unset2D(b);
|
|
//--- function call
|
|
CMatGen::SPDMatrixRndCond(n,cond,b);
|
|
//--- check
|
|
if(n>=2)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
if(a[i][j]==b[i][j])
|
|
equal_number++;
|
|
}
|
|
}
|
|
if(equal_number>2)
|
|
spderr=true;
|
|
}
|
|
}
|
|
//--- HPD matrices
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Generate A
|
|
Unset2DC(ca);
|
|
cond=MathExp(MathLog(1000)*CMath::RandomReal());
|
|
//--- function call
|
|
CMatGen::HPDMatrixRndCond(n,cond,ca);
|
|
//--- test HPD
|
|
hpderr=hpderr || !IsHPD(ca,n);
|
|
//--- test that A is Hermitian
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
hpderr=hpderr || CMath::AbsComplex(ca[i][j]-CMath::Conj(ca[j][i]))>threshold;
|
|
}
|
|
//--- test for difference between A and B (subsequent matrix)
|
|
Unset2DC(cb);
|
|
//--- function call
|
|
CMatGen::HPDMatrixRndCond(n,cond,cb);
|
|
//--- check
|
|
if(n>=2)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
hpderr=hpderr || ca[i][j]==cb[i][j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Symmetric matrices
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- test condition number
|
|
Unset2D(a);
|
|
cond=MathExp(MathLog(1000)*CMath::RandomReal());
|
|
//--- function call
|
|
CMatGen::SMatrixRndCond(n,cond,a);
|
|
serr=serr || SVDCond(a,n)/cond-1>threshold;
|
|
//--- test for difference between A and B
|
|
Unset2D(b);
|
|
//--- function call
|
|
CMatGen::SMatrixRndCond(n,cond,b);
|
|
//--- check
|
|
if(n>=2)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
serr=serr || a[i][j]==b[i][j];
|
|
}
|
|
}
|
|
}
|
|
//--- Hermitian matrices
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Generate A
|
|
Unset2DC(ca);
|
|
cond=MathExp(MathLog(1000)*CMath::RandomReal());
|
|
//--- function call
|
|
CMatGen::HMatrixRndCond(n,cond,ca);
|
|
//--- test that A is Hermitian
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
herr=herr || CMath::AbsComplex(ca[i][j]-CMath::Conj(ca[j][i]))>threshold;
|
|
}
|
|
//--- test for difference between A and B (subsequent matrix)
|
|
Unset2DC(cb);
|
|
//--- function call
|
|
CMatGen::HMatrixRndCond(n,cond,cb);
|
|
//--- check
|
|
if(n>=2)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
herr=herr || ca[i][j]==cb[i][j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=((((rerr || cerr) || serr) || spderr) || herr) || hpderr;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING MATRIX GENERATOR");
|
|
Print("REAL TEST: ");
|
|
//--- check
|
|
if(!rerr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("COMPLEX TEST: ");
|
|
//--- check
|
|
if(!cerr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("SYMMETRIC TEST: ");
|
|
//--- check
|
|
if(!serr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("HERMITIAN TEST: ");
|
|
//--- check
|
|
if(!herr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("SPD TEST: ");
|
|
//--- check
|
|
if(!spderr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("HPD TEST: ");
|
|
//--- check
|
|
if(!hpderr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 2D array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatGenUnit::Unset2D(CMatrixDouble &a)
|
|
{
|
|
//--- allocation
|
|
a.Resize(1,1);
|
|
//--- change value
|
|
a[0].Set(0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 2D array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatGenUnit::Unset2DC(CMatrixComplex &a)
|
|
{
|
|
//--- allocation
|
|
a.Resize(1,1);
|
|
//--- change value
|
|
a[0].Set(0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test whether matrix is SPD |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMatGenUnit::IsSPD(CMatrixDouble &ca,const int n,const bool isupper)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
double ajj=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- copy
|
|
a=ca;
|
|
//--- Test the input parameters.
|
|
if(!CAp::Assert(n>=0,"Error in SMatrixCholesky: incorrect function arguments"))
|
|
return(false);
|
|
//--- Quick return if possible
|
|
result=true;
|
|
if(n<=0)
|
|
{
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute the Cholesky factorization A=U'*U.
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- Compute U(J,J) and test for non-positive-definiteness.
|
|
v=0.0;
|
|
for(i_=0;i_<=j-1;i_++)
|
|
v+=a[i_][j]*a[i_][j];
|
|
ajj=a[j][j]-v;
|
|
//--- check
|
|
if(ajj<=0.0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
ajj=MathSqrt(ajj);
|
|
a[j].Set(j,ajj);
|
|
//--- Compute elements J+1:N of row J.
|
|
if(j<n-1)
|
|
{
|
|
for(i=j+1;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=j-1;i_++)
|
|
v+=a[i_][i]*a[i_][j];
|
|
a[j].Set(i,a[j][i]-v);
|
|
}
|
|
v=1/ajj;
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
a[j].Set(i_,v*a[j][i_]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute the Cholesky factorization A=L*L'.
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- Compute L(J,J) and test for non-positive-definiteness.
|
|
v=0.0;
|
|
for(i_=0;i_<=j-1;i_++)
|
|
v+=a[j][i_]*a[j][i_];
|
|
ajj=a[j][j]-v;
|
|
//--- check
|
|
if(ajj<=0.0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
ajj=MathSqrt(ajj);
|
|
a[j].Set(j,ajj);
|
|
//--- Compute elements J+1:N of column J.
|
|
if(j<n-1)
|
|
{
|
|
for(i=j+1;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=j-1;i_++)
|
|
v+=a[i][i_]*a[j][i_];
|
|
a[i].Set(j,a[i][j]-v);
|
|
}
|
|
//--- change values
|
|
v=1/ajj;
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
a[i_].Set(j,v*a[i_][j]);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests whether A is HPD |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMatGenUnit::IsHPD(CMatrixComplex &ca,const int n)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int j=0;
|
|
double ajj=0;
|
|
complex v=0;
|
|
double r=0;
|
|
int i=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
complex t[];
|
|
complex t2[];
|
|
complex t3[];
|
|
//--- create matrix
|
|
CMatrixComplex a1;
|
|
CMatrixComplex a;
|
|
//--- copy
|
|
a=ca;
|
|
//--- allocation
|
|
ArrayResize(t,n);
|
|
ArrayResize(t2,n);
|
|
ArrayResize(t3,n);
|
|
//--- initialization
|
|
result=true;
|
|
//--- Compute the Cholesky factorization A=U'*U.
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- Compute U(J,J) and test for non-positive-definiteness.
|
|
v=0.0;
|
|
for(i_=0;i_<=j-1;i_++)
|
|
v+=CMath::Conj(a[i_][j])*a[i_][j];
|
|
ajj=(a[j][j]-v).re;
|
|
//--- check
|
|
if(ajj<=0.0)
|
|
{
|
|
a[j].Set(j,ajj);
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
ajj=MathSqrt(ajj);
|
|
a[j].Set(j,ajj);
|
|
//--- Compute elements J+1:N-1 of row J.
|
|
if(j<n-1)
|
|
{
|
|
for(i_=0;i_<=j-1;i_++)
|
|
t2[i_]=CMath::Conj(a[i_][j]);
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
t3[i_]=a[j][i_];
|
|
//--- calculation
|
|
for(i=j+1;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=j-1;i_++)
|
|
v+=a[i_][i]*t2[i_];
|
|
t3[i]=t3[i]-v;
|
|
}
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
a[j].Set(i_,t3[i_]);
|
|
//--- change values
|
|
r=1/ajj;
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
a[j].Set(i_,a[j][i_]*r);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| SVD condition number |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestMatGenUnit::SVDCond(CMatrixDouble &a,const int n)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
double minw=0;
|
|
double maxw=0;
|
|
//--- create array
|
|
double w[];
|
|
//--- create matrix
|
|
CMatrixDouble a1;
|
|
CMatrixDouble v;
|
|
//--- allocation
|
|
a1.Resize(n+1,n+1);
|
|
//--- change values
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
a1[i].Set(j,a[i-1][j-1]);
|
|
}
|
|
//--- check
|
|
if(!ObsoleteSVDDecomposition(a1,n,n,w,v))
|
|
{
|
|
//--- return result
|
|
return(0);
|
|
}
|
|
//--- change values
|
|
minw=w[1];
|
|
maxw=w[1];
|
|
for(i=2;i<=n;i++)
|
|
{
|
|
//--- check
|
|
if(w[i]<minw)
|
|
minw=w[i];
|
|
//--- check
|
|
if(w[i]>maxw)
|
|
maxw=w[i];
|
|
}
|
|
//--- return result
|
|
return(maxw/minw);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMatGenUnit::ObsoleteSVDDecomposition(CMatrixDouble &a,
|
|
const int m,
|
|
const int n,
|
|
double &w[],
|
|
CMatrixDouble &v)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int nm=0;
|
|
int minmn=0;
|
|
int l=0;
|
|
int k=0;
|
|
int j=0;
|
|
int jj=0;
|
|
int its=0;
|
|
int i=0;
|
|
double z=0;
|
|
double y=0;
|
|
double x=0;
|
|
double vscale=0;
|
|
double s=0;
|
|
double h=0;
|
|
double g=0;
|
|
double f=0;
|
|
double c=0;
|
|
double anorm=0;
|
|
bool flag;
|
|
//--- create array
|
|
double rv1[];
|
|
//--- allocation
|
|
ArrayResize(rv1,n+1);
|
|
ArrayResize(w,n+1);
|
|
v.Resize(n+1,n+1);
|
|
//--- initialization
|
|
result=true;
|
|
//--- check
|
|
if(m<n)
|
|
minmn=m;
|
|
else
|
|
minmn=n;
|
|
//--- initialization
|
|
g=0.0;
|
|
vscale=0.0;
|
|
anorm=0.0;
|
|
//--- calculation
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
l=i+1;
|
|
rv1[i]=vscale*g;
|
|
g=0;
|
|
s=0;
|
|
vscale=0;
|
|
//--- check
|
|
if(i<=m)
|
|
{
|
|
for(k=i;k<=m;k++)
|
|
vscale=vscale+MathAbs(a[k][i]);
|
|
//--- check
|
|
if(vscale!=0.0)
|
|
{
|
|
for(k=i;k<=m;k++)
|
|
{
|
|
a[k].Set(i,a[k][i]/vscale);
|
|
s=s+a[k][i]*a[k][i];
|
|
}
|
|
//--- calculation
|
|
f=a[i][i];
|
|
g=-ExtSign(MathSqrt(s),f);
|
|
h=f*g-s;
|
|
a[i].Set(i,f-g);
|
|
//--- check
|
|
if(i!=n)
|
|
{
|
|
for(j=l;j<=n;j++)
|
|
{
|
|
s=0.0;
|
|
for(k=i;k<=m;k++)
|
|
s=s+a[k][i]*a[k][j];
|
|
//--- calculation
|
|
f=s/h;
|
|
for(k=i;k<=m;k++)
|
|
a[k].Set(j,a[k][j]+f*a[k][i]);
|
|
}
|
|
}
|
|
for(k=i;k<=m;k++)
|
|
a[k].Set(i,vscale*a[k][i]);
|
|
}
|
|
}
|
|
//--- change values
|
|
w[i]=vscale*g;
|
|
g=0.0;
|
|
s=0.0;
|
|
vscale=0.0;
|
|
//--- check
|
|
if(i<=m && i!=n)
|
|
{
|
|
for(k=l;k<=n;k++)
|
|
vscale=vscale+MathAbs(a[i][k]);
|
|
//--- check
|
|
if(vscale!=0.0)
|
|
{
|
|
for(k=l;k<=n;k++)
|
|
{
|
|
a[i].Set(k,a[i][k]/vscale);
|
|
s=s+a[i][k]*a[i][k];
|
|
}
|
|
//--- calculation
|
|
f=a[i][l];
|
|
g=-ExtSign(MathSqrt(s),f);
|
|
h=f*g-s;
|
|
a[i].Set(l,f-g);
|
|
for(k=l;k<=n;k++)
|
|
rv1[k]=a[i][k]/h;
|
|
//--- check
|
|
if(i!=m)
|
|
{
|
|
//--- calculation
|
|
for(j=l;j<=m;j++)
|
|
{
|
|
s=0.0;
|
|
for(k=l;k<=n;k++)
|
|
s=s+a[j][k]*a[i][k];
|
|
for(k=l;k<=n;k++)
|
|
a[j].Set(k,a[j][k]+s*rv1[k]);
|
|
}
|
|
}
|
|
for(k=l;k<=n;k++)
|
|
a[i].Set(k,vscale*a[i][k]);
|
|
}
|
|
}
|
|
//--- change value
|
|
anorm=MyMax(anorm,MathAbs(w[i])+MathAbs(rv1[i]));
|
|
}
|
|
for(i=n;i>=1;i--)
|
|
{
|
|
//--- check
|
|
if(i<n)
|
|
{
|
|
//--- check
|
|
if(g!=0.0)
|
|
{
|
|
for(j=l;j<=n;j++)
|
|
v[j].Set(i,a[i][j]/a[i][l]/g);
|
|
//--- calculation
|
|
for(j=l;j<=n;j++)
|
|
{
|
|
s=0.0;
|
|
for(k=l;k<=n;k++)
|
|
s=s+a[i][k]*v[k][j];
|
|
for(k=l;k<=n;k++)
|
|
v[k].Set(j,v[k][j]+s*v[k][i]);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(j=l;j<=n;j++)
|
|
{
|
|
v[i].Set(j,0.0);
|
|
v[j].Set(i,0.0);
|
|
}
|
|
}
|
|
//--- change values
|
|
v[i].Set(i,1.0);
|
|
g=rv1[i];
|
|
l=i;
|
|
}
|
|
//--- calculation
|
|
for(i=minmn;i>=1;i--)
|
|
{
|
|
l=i+1;
|
|
g=w[i];
|
|
//--- check
|
|
if(i<n)
|
|
{
|
|
for(j=l;j<=n;j++)
|
|
a[i].Set(j,0.0);
|
|
}
|
|
//--- check
|
|
if(g!=0.0)
|
|
{
|
|
g=1.0/g;
|
|
//--- check
|
|
if(i!=n)
|
|
{
|
|
//--- calculation
|
|
for(j=l;j<=n;j++)
|
|
{
|
|
s=0.0;
|
|
for(k=l;k<=m;k++)
|
|
s=s+a[k][i]*a[k][j];
|
|
//--- change values
|
|
f=s/a[i][i]*g;
|
|
for(k=i;k<=m;k++)
|
|
a[k].Set(j,a[k][j]+f*a[k][i]);
|
|
}
|
|
}
|
|
for(j=i;j<=m;j++)
|
|
a[j].Set(i,a[j][i]*g);
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
for(j=i;j<=m;j++)
|
|
a[j].Set(i,0.0);
|
|
}
|
|
a[i].Set(i,a[i][i]+1.0);
|
|
}
|
|
//--- calculation
|
|
for(k=n;k>=1;k--)
|
|
{
|
|
for(its=1;its<=m_maxsvditerations;its++)
|
|
{
|
|
flag=true;
|
|
for(l=k;l>=1;l--)
|
|
{
|
|
nm=l-1;
|
|
//--- check
|
|
if(MathAbs(rv1[l])+anorm==anorm)
|
|
{
|
|
flag=false;
|
|
break;
|
|
}
|
|
//--- check
|
|
if(MathAbs(w[nm])+anorm==anorm)
|
|
break;
|
|
}
|
|
//--- check
|
|
if(flag)
|
|
{
|
|
c=0.0;
|
|
s=1.0;
|
|
//--- calculation
|
|
for(i=l;i<=k;i++)
|
|
{
|
|
f=s*rv1[i];
|
|
//--- check
|
|
if(MathAbs(f)+anorm!=anorm)
|
|
{
|
|
//--- change values
|
|
g=w[i];
|
|
h=PyThag(f,g);
|
|
w[i]=h;
|
|
h=1.0/h;
|
|
c=g*h;
|
|
s=-(f*h);
|
|
for(j=1;j<=m;j++)
|
|
{
|
|
y=a[j][nm];
|
|
z=a[j][i];
|
|
a[j].Set(nm,y*c+z*s);
|
|
a[j].Set(i,-(y*s)+z*c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
z=w[k];
|
|
//--- check
|
|
if(l==k)
|
|
{
|
|
//--- check
|
|
if(z<0.0)
|
|
{
|
|
w[k]=-z;
|
|
for(j=1;j<=n;j++)
|
|
v[j].Set(k,-v[j][k]);
|
|
}
|
|
break;
|
|
}
|
|
//--- check
|
|
if(its==m_maxsvditerations)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
x=w[l];
|
|
nm=k-1;
|
|
y=w[nm];
|
|
g=rv1[nm];
|
|
h=rv1[k];
|
|
f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);
|
|
g=PyThag(f,1);
|
|
f=((x-z)*(x+z)+h*(y/(f+ExtSign(g,f))-h))/x;
|
|
c=1.0;
|
|
s=1.0;
|
|
//--- calculation
|
|
for(j=l;j<=nm;j++)
|
|
{
|
|
i=j+1;
|
|
g=rv1[i];
|
|
y=w[i];
|
|
h=s*g;
|
|
g=c*g;
|
|
z=PyThag(f,h);
|
|
rv1[j]=z;
|
|
c=f/z;
|
|
s=h/z;
|
|
f=x*c+g*s;
|
|
g=-(x*s)+g*c;
|
|
h=y*s;
|
|
y=y*c;
|
|
for(jj=1;jj<=n;jj++)
|
|
{
|
|
x=v[jj][j];
|
|
z=v[jj][i];
|
|
v[jj].Set(j,x*c+z*s);
|
|
v[jj].Set(i,-(x*s)+z*c);
|
|
}
|
|
z=PyThag(f,h);
|
|
w[j]=z;
|
|
//--- check
|
|
if(z!=0.0)
|
|
{
|
|
z=1.0/z;
|
|
c=f*z;
|
|
s=h*z;
|
|
}
|
|
//--- calculation
|
|
f=c*g+s*y;
|
|
x=-(s*g)+c*y;
|
|
for(jj=1;jj<=m;jj++)
|
|
{
|
|
y=a[jj][j];
|
|
z=a[jj][i];
|
|
a[jj].Set(j,y*c+z*s);
|
|
a[jj].Set(i,-(y*s)+z*c);
|
|
}
|
|
}
|
|
//--- change values
|
|
rv1[l]=0.0;
|
|
rv1[k]=f;
|
|
w[k]=x;
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestMatGenUnit::ExtSign(const double a,const double b)
|
|
{
|
|
//--- create a variable
|
|
double result=0;
|
|
//--- check
|
|
if(b>=0.0)
|
|
result=MathAbs(a);
|
|
else
|
|
result=-MathAbs(a);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestMatGenUnit::MyMax(const double a,const double b)
|
|
{
|
|
//--- create a variable
|
|
double result=0;
|
|
//--- check
|
|
if(a>b)
|
|
result=a;
|
|
else
|
|
result=b;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestMatGenUnit::PyThag(const double a,const double b)
|
|
{
|
|
//--- create a variable
|
|
double result=0;
|
|
//--- check
|
|
if(MathAbs(a)<MathAbs(b))
|
|
result=MathAbs(b)*MathSqrt(1+CMath::Sqr(a/b));
|
|
else
|
|
result=MathAbs(a)*MathSqrt(1+CMath::Sqr(b/a));
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CTrFac |
|
|
//+------------------------------------------------------------------+
|
|
class CTestTrFacUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void TestCLUProblem(CMatrixComplex &a,const int m,const int n,const double threshold,bool &err,bool &properr);
|
|
static void TestRLUProblem(CMatrixDouble &a,const int m,const int n,const double threshold,bool &err,bool &properr);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestTrFacUnit(void);
|
|
~CTestTrFacUnit(void);
|
|
//--- public method
|
|
static bool TestTrFac(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestTrFacUnit::CTestTrFacUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestTrFacUnit::~CTestTrFacUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CTrFac |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestTrFacUnit::TestTrFac(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int m=0;
|
|
int n=0;
|
|
int mx=0;
|
|
int maxmn=0;
|
|
int i=0;
|
|
int j=0;
|
|
complex vc=0;
|
|
double vr=0;
|
|
bool waserrors;
|
|
bool spderr;
|
|
bool hpderr;
|
|
bool rerr;
|
|
bool cerr;
|
|
bool properr;
|
|
double threshold=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble ra;
|
|
CMatrixDouble ral;
|
|
CMatrixDouble rau;
|
|
CMatrixComplex ca;
|
|
CMatrixComplex cal;
|
|
CMatrixComplex cau;
|
|
//--- initialization
|
|
rerr=false;
|
|
spderr=false;
|
|
cerr=false;
|
|
hpderr=false;
|
|
properr=false;
|
|
waserrors=false;
|
|
maxmn=4*CAblas::AblasBlockSize()+1;
|
|
threshold=1000*CMath::m_machineepsilon*maxmn;
|
|
//--- test LU
|
|
for(mx=1;mx<=maxmn;mx++)
|
|
{
|
|
//--- Initialize N/M,both are <=MX,
|
|
//--- at least one of them is exactly equal to MX
|
|
n=1+CMath::RandomInteger(mx);
|
|
m=1+CMath::RandomInteger(mx);
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
n=mx;
|
|
else
|
|
m=mx;
|
|
//--- First,test on zero matrix
|
|
ra.Resize(m,n);
|
|
ca.Resize(m,n);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
ra[i].Set(j,0);
|
|
ca[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- function calls
|
|
TestCLUProblem(ca,m,n,threshold,cerr,properr);
|
|
TestRLUProblem(ra,m,n,threshold,rerr,properr);
|
|
//--- Second,random matrix with moderate condition number
|
|
ra.Resize(m,n);
|
|
ca.Resize(m,n);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
ra[i].Set(j,0);
|
|
ca[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=MathMin(m,n)-1;i++)
|
|
{
|
|
ra[i].Set(i,1+10*CMath::RandomReal());
|
|
ca[i].Set(i,1+10*CMath::RandomReal());
|
|
}
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonalFromTheLeft(ca,m,n);
|
|
//--- function call
|
|
CMatGen::CMatrixRndOrthogonalFromTheRight(ca,m,n);
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonalFromTheLeft(ra,m,n);
|
|
//--- function call
|
|
CMatGen::RMatrixRndOrthogonalFromTheRight(ra,m,n);
|
|
//--- function calls
|
|
TestCLUProblem(ca,m,n,threshold,cerr,properr);
|
|
TestRLUProblem(ra,m,n,threshold,rerr,properr);
|
|
}
|
|
//--- Test Cholesky
|
|
for(n=1;n<=maxmn;n++)
|
|
{
|
|
//--- Load CA (HPD matrix with low condition number),
|
|
//--- CAL and CAU - its lower and upper triangles
|
|
CMatGen::HPDMatrixRndCond(n,1+50*CMath::RandomReal(),ca);
|
|
//--- allocation
|
|
cal.Resize(n,n);
|
|
cau.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
cal[i].Set(j,i);
|
|
cau[i].Set(j,j);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=i;i_++)
|
|
cal[i].Set(i_,ca[i][i_]);
|
|
for(i_=i;i_<=n-1;i_++)
|
|
cau[i].Set(i_,ca[i][i_]);
|
|
}
|
|
//--- Test HPDMatrixCholesky:
|
|
//--- 1. it must leave upper (lower) part unchanged
|
|
//--- 2. max(A-L*L^H) must be small
|
|
if(CTrFac::HPDMatrixCholesky(cal,n,false))
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(j>i)
|
|
hpderr=hpderr || cal[i][j]!=i;
|
|
else
|
|
{
|
|
vc=0.0;
|
|
for(i_=0;i_<=j;i_++)
|
|
vc+=cal[i][i_]*CMath::Conj(cal[j][i_]);
|
|
//--- search errors
|
|
hpderr=hpderr || CMath::AbsComplex(ca[i][j]-vc)>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
hpderr=true;
|
|
//--- check
|
|
if(CTrFac::HPDMatrixCholesky(cau,n,true))
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(j<i)
|
|
hpderr=hpderr || cau[i][j]!=j;
|
|
else
|
|
{
|
|
vc=0.0;
|
|
for(i_=0;i_<=i;i_++)
|
|
vc+=CMath::Conj(cau[i_][i])*cau[i_][j];
|
|
//--- search errors
|
|
hpderr=hpderr || CMath::AbsComplex(ca[i][j]-vc)>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
hpderr=true;
|
|
//--- Load RA (SPD matrix with low condition number),
|
|
//--- RAL and RAU - its lower and upper triangles
|
|
CMatGen::SPDMatrixRndCond(n,1+50*CMath::RandomReal(),ra);
|
|
//--- allocation
|
|
ral.Resize(n,n);
|
|
rau.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
ral[i].Set(j,i);
|
|
rau[i].Set(j,j);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=i;i_++)
|
|
ral[i].Set(i_,ra[i][i_]);
|
|
for(i_=i;i_<=n-1;i_++)
|
|
rau[i].Set(i_,ra[i][i_]);
|
|
}
|
|
//--- Test SPDMatrixCholesky:
|
|
//--- 1. it must leave upper (lower) part unchanged
|
|
//--- 2. max(A-L*L^H) must be small
|
|
if(CTrFac::SPDMatrixCholesky(ral,n,false))
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(j>i)
|
|
spderr=spderr || ral[i][j]!=i;
|
|
else
|
|
{
|
|
vr=0.0;
|
|
for(i_=0;i_<=j;i_++)
|
|
vr+=ral[i][i_]*ral[j][i_];
|
|
//--- search errors
|
|
spderr=spderr || MathAbs(ra[i][j]-vr)>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
spderr=true;
|
|
//--- check
|
|
if(CTrFac::SPDMatrixCholesky(rau,n,true))
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(j<i)
|
|
spderr=spderr || rau[i][j]!=j;
|
|
else
|
|
{
|
|
vr=0.0;
|
|
for(i_=0;i_<=i;i_++)
|
|
vr+=rau[i_][i]*rau[i_][j];
|
|
//--- search errors
|
|
spderr=spderr || MathAbs(ra[i][j]-vr)>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
spderr=true;
|
|
}
|
|
//--- report
|
|
waserrors=(((rerr || spderr) || cerr) || hpderr) || properr;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING TRIANGULAR FACTORIZATIONS");
|
|
Print("* REAL: ");
|
|
//--- check
|
|
if(rerr)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* SPD: ");
|
|
//--- check
|
|
if(spderr)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* COMPLEX: ");
|
|
//--- check
|
|
if(cerr)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* HPD: ");
|
|
//--- check
|
|
if(hpderr)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* OTHER PROPERTIES: ");
|
|
//--- check
|
|
if(properr)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestTrFacUnit::TestCLUProblem(CMatrixComplex &a,const int m,
|
|
const int n,const double threshold,
|
|
bool &err,bool &properr)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int minmn=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
complex ct[];
|
|
int p[];
|
|
//--- create matrix
|
|
CMatrixComplex ca;
|
|
CMatrixComplex cl;
|
|
CMatrixComplex cu;
|
|
CMatrixComplex ca2;
|
|
//--- initialization
|
|
minmn=MathMin(m,n);
|
|
//--- PLU test
|
|
ca.Resize(m,n);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ca[i].Set(i_,a[i][i_]);
|
|
}
|
|
//--- function call
|
|
CTrFac::CMatrixPLU(ca,m,n,p);
|
|
for(i=0;i<=minmn-1;i++)
|
|
{
|
|
//--- check
|
|
if(p[i]<i || p[i]>=m)
|
|
{
|
|
properr=false;
|
|
return;
|
|
}
|
|
}
|
|
//--- allocation
|
|
cl.Resize(m,minmn);
|
|
for(j=0;j<=minmn-1;j++)
|
|
{
|
|
for(i=0;i<=j-1;i++)
|
|
cl[i].Set(j,0.0);
|
|
//--- change values
|
|
cl[j].Set(j,1.0);
|
|
for(i=j+1;i<=m-1;i++)
|
|
cl[i].Set(j,ca[i][j]);
|
|
}
|
|
//--- allocation
|
|
cu.Resize(minmn,n);
|
|
//--- change values
|
|
for(i=0;i<=minmn-1;i++)
|
|
{
|
|
for(j=0;j<=i-1;j++)
|
|
cu[i].Set(j,0.0);
|
|
for(j=i;j<=n-1;j++)
|
|
cu[i].Set(j,ca[i][j]);
|
|
}
|
|
//--- allocation
|
|
ca2.Resize(m,n);
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=minmn-1;i_++)
|
|
v+=cl[i][i_]*cu[i_][j];
|
|
ca2[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResize(ct,n);
|
|
//--- change values
|
|
for(i=minmn-1;i>=0;i--)
|
|
{
|
|
//--- check
|
|
if(i!=p[i])
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ct[i_]=ca2[i][i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ca2[i].Set(i_,ca2[p[i]][i_]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ca2[p[i]].Set(i_,ct[i_]);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
err=err || CMath::AbsComplex(a[i][j]-ca2[i][j])>threshold;
|
|
}
|
|
//--- LUP test
|
|
ca.Resize(m,n);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ca[i].Set(i_,a[i][i_]);
|
|
}
|
|
//--- function call
|
|
CTrFac::CMatrixLUP(ca,m,n,p);
|
|
for(i=0;i<=minmn-1;i++)
|
|
{
|
|
//--- check
|
|
if(p[i]<i || p[i]>=n)
|
|
{
|
|
properr=false;
|
|
return;
|
|
}
|
|
}
|
|
//--- allocation
|
|
cl.Resize(m,minmn);
|
|
//--- change values
|
|
for(j=0;j<=minmn-1;j++)
|
|
{
|
|
for(i=0;i<=j-1;i++)
|
|
cl[i].Set(j,0.0);
|
|
for(i=j;i<=m-1;i++)
|
|
cl[i].Set(j,ca[i][j]);
|
|
}
|
|
//--- allocation
|
|
cu.Resize(minmn,n);
|
|
//--- change values
|
|
for(i=0;i<=minmn-1;i++)
|
|
{
|
|
for(j=0;j<=i-1;j++)
|
|
cu[i].Set(j,0.0);
|
|
cu[i].Set(i,1.0);
|
|
for(j=i+1;j<=n-1;j++)
|
|
cu[i].Set(j,ca[i][j]);
|
|
}
|
|
//--- allocation
|
|
ca2.Resize(m,n);
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=minmn-1;i_++)
|
|
v+=cl[i][i_]*cu[i_][j];
|
|
ca2[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResize(ct,m);
|
|
//--- change values
|
|
for(i=minmn-1;i>=0;i--)
|
|
{
|
|
//--- check
|
|
if(i!=p[i])
|
|
{
|
|
for(i_=0;i_<=m-1;i_++)
|
|
ct[i_]=ca2[i_][i];
|
|
for(i_=0;i_<=m-1;i_++)
|
|
ca2[i_].Set(i,ca2[i_][p[i]]);
|
|
for(i_=0;i_<=m-1;i_++)
|
|
ca2[i_].Set(p[i],ct[i_]);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
err=err || CMath::AbsComplex(a[i][j]-ca2[i][j])>threshold;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestTrFacUnit::TestRLUProblem(CMatrixDouble &a,const int m,
|
|
const int n,const double threshold,
|
|
bool &err,bool &properr)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int minmn=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double ct[];
|
|
int p[];
|
|
//--- create matrix
|
|
CMatrixDouble ca;
|
|
CMatrixDouble cl;
|
|
CMatrixDouble cu;
|
|
CMatrixDouble ca2;
|
|
//--- initialization
|
|
minmn=MathMin(m,n);
|
|
//--- PLU test
|
|
ca.Resize(m,n);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ca[i].Set(i_,a[i][i_]);
|
|
}
|
|
//--- function call
|
|
CTrFac::RMatrixPLU(ca,m,n,p);
|
|
for(i=0;i<=minmn-1;i++)
|
|
{
|
|
//--- check
|
|
if(p[i]<i || p[i]>=m)
|
|
{
|
|
properr=false;
|
|
return;
|
|
}
|
|
}
|
|
//--- allocation
|
|
cl.Resize(m,minmn);
|
|
//--- change values
|
|
for(j=0;j<=minmn-1;j++)
|
|
{
|
|
for(i=0;i<=j-1;i++)
|
|
cl[i].Set(j,0.0);
|
|
cl[j].Set(j,1.0);
|
|
for(i=j+1;i<=m-1;i++)
|
|
cl[i].Set(j,ca[i][j]);
|
|
}
|
|
//--- allocation
|
|
cu.Resize(minmn,n);
|
|
//--- change values
|
|
for(i=0;i<=minmn-1;i++)
|
|
{
|
|
for(j=0;j<=i-1;j++)
|
|
cu[i].Set(j,0.0);
|
|
for(j=i;j<=n-1;j++)
|
|
cu[i].Set(j,ca[i][j]);
|
|
}
|
|
//--- allocation
|
|
ca2.Resize(m,n);
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=minmn-1;i_++)
|
|
v+=cl[i][i_]*cu[i_][j];
|
|
ca2[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResize(ct,n);
|
|
for(i=minmn-1;i>=0;i--)
|
|
{
|
|
//--- check
|
|
if(i!=p[i])
|
|
{
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ct[i_]=ca2[i][i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ca2[i].Set(i_,ca2[p[i]][i_]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ca2[p[i]].Set(i_,ct[i_]);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
err=err || MathAbs(a[i][j]-ca2[i][j])>threshold;
|
|
}
|
|
//--- LUP test
|
|
ca.Resize(m,n);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
ca[i].Set(i_,a[i][i_]);
|
|
}
|
|
//--- function call
|
|
CTrFac::RMatrixLUP(ca,m,n,p);
|
|
for(i=0;i<=minmn-1;i++)
|
|
{
|
|
//--- check
|
|
if(p[i]<i || p[i]>=n)
|
|
{
|
|
properr=false;
|
|
return;
|
|
}
|
|
}
|
|
//--- allocation
|
|
cl.Resize(m,minmn);
|
|
//--- change values
|
|
for(j=0;j<=minmn-1;j++)
|
|
{
|
|
for(i=0;i<=j-1;i++)
|
|
cl[i].Set(j,0.0);
|
|
for(i=j;i<=m-1;i++)
|
|
cl[i].Set(j,ca[i][j]);
|
|
}
|
|
//--- allocation
|
|
cu.Resize(minmn,n);
|
|
//--- change values
|
|
for(i=0;i<=minmn-1;i++)
|
|
{
|
|
for(j=0;j<=i-1;j++)
|
|
cu[i].Set(j,0.0);
|
|
cu[i].Set(i,1.0);
|
|
for(j=i+1;j<=n-1;j++)
|
|
cu[i].Set(j,ca[i][j]);
|
|
}
|
|
//--- allocation
|
|
ca2.Resize(m,n);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=minmn-1;i_++)
|
|
v+=cl[i][i_]*cu[i_][j];
|
|
ca2[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResize(ct,m);
|
|
for(i=minmn-1;i>=0;i--)
|
|
{
|
|
//--- check
|
|
if(i!=p[i])
|
|
{
|
|
//--- change values
|
|
for(i_=0;i_<=m-1;i_++)
|
|
ct[i_]=ca2[i_][i];
|
|
for(i_=0;i_<=m-1;i_++)
|
|
ca2[i_].Set(i,ca2[i_][p[i]]);
|
|
for(i_=0;i_<=m-1;i_++)
|
|
ca2[i_].Set(p[i],ct[i_]);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
err=err || MathAbs(a[i][j]-ca2[i][j])>threshold;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CTrLinSolve |
|
|
//+------------------------------------------------------------------+
|
|
class CTestTrLinSolveUnit
|
|
{
|
|
private:
|
|
//--- private method
|
|
static void MakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestTrLinSolveUnit(void);
|
|
~CTestTrLinSolveUnit(void);
|
|
//--- public method
|
|
static bool TestTrLinSolve(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestTrLinSolveUnit::CTestTrLinSolveUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestTrLinSolveUnit::~CTestTrLinSolveUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Main unittest subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestTrLinSolveUnit::TestTrLinSolve(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int maxmn=0;
|
|
int passcount=0;
|
|
double threshold=0;
|
|
int n=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int cnts=0;
|
|
int cntu=0;
|
|
int cntt=0;
|
|
int cntm=0;
|
|
bool waserrors;
|
|
bool isupper;
|
|
bool istrans;
|
|
bool isunit;
|
|
double v=0;
|
|
double s=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double xe[];
|
|
double b[];
|
|
//--- create matrix
|
|
CMatrixDouble aeffective;
|
|
CMatrixDouble aparam;
|
|
//--- initialization
|
|
waserrors=false;
|
|
maxmn=15;
|
|
passcount=15;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
//--- Different problems
|
|
for(n=1;n<=maxmn;n++)
|
|
{
|
|
//--- allocation
|
|
aeffective.Resize(n,n);
|
|
aparam.Resize(n,n);
|
|
ArrayResize(xe,n);
|
|
ArrayResize(b,n);
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(cnts=0;cnts<=1;cnts++)
|
|
{
|
|
for(cntu=0;cntu<=1;cntu++)
|
|
{
|
|
for(cntt=0;cntt<=1;cntt++)
|
|
{
|
|
for(cntm=0;cntm<=2;cntm++)
|
|
{
|
|
isupper=cnts==0;
|
|
isunit=cntu==0;
|
|
istrans=cntt==0;
|
|
//--- Skip meaningless combinations of parameters:
|
|
//--- (matrix is singular) AND (matrix is unit diagonal)
|
|
if(cntm==2 && isunit)
|
|
continue;
|
|
//--- Clear matrices
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
aeffective[i].Set(j,0);
|
|
aparam[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- Prepare matrices
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i;j<=n-1;j++)
|
|
{
|
|
aeffective[i].Set(j,0.9*(2*CMath::RandomReal()-1));
|
|
aparam[i].Set(j,aeffective[i][j]);
|
|
}
|
|
//--- change values
|
|
aeffective[i].Set(i,(2*CMath::RandomInteger(2)-1)*(0.8+CMath::RandomReal()));
|
|
aparam[i].Set(i,aeffective[i][i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=i;j++)
|
|
{
|
|
aeffective[i].Set(j,0.9*(2*CMath::RandomReal()-1));
|
|
aparam[i].Set(j,aeffective[i][j]);
|
|
}
|
|
//--- change values
|
|
aeffective[i].Set(i,(2*CMath::RandomInteger(2)-1)*(0.8+CMath::RandomReal()));
|
|
aparam[i].Set(i,aeffective[i][i]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(isunit)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
aeffective[i].Set(i,1);
|
|
aparam[i].Set(i,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(istrans)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i+1;j<=n-1;j++)
|
|
{
|
|
aeffective[j].Set(i,aeffective[i][j]);
|
|
aeffective[i].Set(j,0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i+1;j<=n-1;j++)
|
|
{
|
|
aeffective[i].Set(j,aeffective[j][i]);
|
|
aeffective[j].Set(i,0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Prepare task,solve,compare
|
|
for(i=0;i<=n-1;i++)
|
|
xe[i]=2*CMath::RandomReal()-1;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=aeffective[i][i_]*xe[i_];
|
|
b[i]=v;
|
|
}
|
|
//--- function call
|
|
CTrLinSolve::RMatrixTrSafeSolve(aparam,n,b,s,isupper,istrans,isunit);
|
|
//--- calculation
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xe[i_]=s*xe[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xe[i_]=xe[i_]-b[i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=xe[i_]*xe[i_];
|
|
v=MathSqrt(v);
|
|
//--- search errors
|
|
waserrors=waserrors || v>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
if(!silent)
|
|
{
|
|
Print("TESTING RMatrixTrSafeSolve");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestTrLinSolveUnit::MakeACopy(CMatrixDouble &a,const int m,
|
|
const int n,CMatrixDouble &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSafeSolve |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSafeSolveUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
|
|
static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestSafeSolveUnit(void);
|
|
~CTestSafeSolveUnit(void);
|
|
//--- public method
|
|
static bool TestSafeSolve(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestSafeSolveUnit::CTestSafeSolveUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestSafeSolveUnit::~CTestSafeSolveUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Main unittest subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestSafeSolveUnit::TestSafeSolve(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int maxmn=0;
|
|
double threshold=0;
|
|
bool rerrors;
|
|
bool cerrors;
|
|
bool waserrors;
|
|
bool isupper;
|
|
int trans=0;
|
|
bool isunit;
|
|
double scalea=0;
|
|
double growth=0;
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
complex cv=0;
|
|
double rv=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
complex cxs[];
|
|
complex cxe[];
|
|
double rxs[];
|
|
double rxe[];
|
|
//--- create matrix
|
|
CMatrixComplex ca;
|
|
CMatrixComplex cea;
|
|
CMatrixComplex ctmpa;
|
|
CMatrixDouble ra;
|
|
CMatrixDouble rea;
|
|
CMatrixDouble rtmpa;
|
|
//--- initialization
|
|
maxmn=30;
|
|
threshold=100000*CMath::m_machineepsilon;
|
|
rerrors=false;
|
|
cerrors=false;
|
|
waserrors=false;
|
|
//--- Different problems: general tests
|
|
for(n=1;n<=maxmn;n++)
|
|
{
|
|
//--- test complex solver with well-conditioned matrix:
|
|
//--- 1. generate A: fill off-diagonal elements with small values,
|
|
//--- diagonal elements are filled with larger values
|
|
//--- 2. generate 'effective' A
|
|
//--- 3. prepare task (exact X is stored in CXE,right part - in CXS),
|
|
//--- solve and compare CXS and CXE
|
|
isupper=CMath::RandomReal()>0.5;
|
|
trans=CMath::RandomInteger(3);
|
|
isunit=CMath::RandomReal()>0.5;
|
|
scalea=CMath::RandomReal()+0.5;
|
|
//--- allocation
|
|
ca.Resize(n,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
{
|
|
ca[i].SetRe(j,(2*CMath::RandomInteger(2)-1)*(5+CMath::RandomReal()));
|
|
ca[i].SetIm(j,(2*CMath::RandomInteger(2)-1)*(5+CMath::RandomReal()));
|
|
}
|
|
else
|
|
{
|
|
ca[i].SetRe(j,0.2*CMath::RandomReal()-0.1);
|
|
ca[i].SetIm(j,0.2*CMath::RandomReal()-0.1);
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMatrixMakeACopy(ca,n,n,ctmpa);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
j1=0;
|
|
j2=i-1;
|
|
}
|
|
else
|
|
{
|
|
j1=i+1;
|
|
j2=n-1;
|
|
}
|
|
for(j=j1;j<=j2;j++)
|
|
ctmpa[i].Set(j,0);
|
|
//--- check
|
|
if(isunit)
|
|
ctmpa[i].Set(i,1);
|
|
}
|
|
//--- allocation
|
|
cea.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(trans==0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
cea[i].Set(i_,ctmpa[i][i_]*scalea);
|
|
}
|
|
//--- check
|
|
if(trans==1)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
cea[i_].Set(i,ctmpa[i][i_]*scalea);
|
|
}
|
|
//--- check
|
|
if(trans==2)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
cea[i_].Set(i,CMath::Conj(ctmpa[i][i_])*scalea);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cxe,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
cxe[i].re=2*CMath::RandomReal()-1;
|
|
cxe[i].im=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cxs,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
cv=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
cv+=cea[i][i_]*cxe[i_];
|
|
cxs[i]=cv;
|
|
}
|
|
//--- check
|
|
if(CSafeSolve::CMatrixScaledTrSafeSolve(ca,scalea,n,cxs,isupper,trans,isunit,MathSqrt(CMath::m_maxrealnumber)))
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
cerrors=cerrors || CMath::AbsComplex(cxs[i]-cxe[i])>threshold;
|
|
}
|
|
else
|
|
cerrors=true;
|
|
//--- same with real
|
|
isupper=CMath::RandomReal()>0.5;
|
|
trans=CMath::RandomInteger(2);
|
|
isunit=CMath::RandomReal()>0.5;
|
|
scalea=CMath::RandomReal()+0.5;
|
|
//--- allocation
|
|
ra.Resize(n,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
ra[i].Set(j,(2*CMath::RandomInteger(2)-1)*(5+CMath::RandomReal()));
|
|
else
|
|
ra[i].Set(j,0.2*CMath::RandomReal()-0.1);
|
|
}
|
|
}
|
|
//--- function call
|
|
RMatrixMakeACopy(ra,n,n,rtmpa);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
j1=0;
|
|
j2=i-1;
|
|
}
|
|
else
|
|
{
|
|
j1=i+1;
|
|
j2=n-1;
|
|
}
|
|
for(j=j1;j<=j2;j++)
|
|
rtmpa[i].Set(j,0);
|
|
//--- check
|
|
if(isunit)
|
|
rtmpa[i].Set(i,1);
|
|
}
|
|
//--- allocation
|
|
rea.Resize(n,n);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(trans==0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
rea[i].Set(i_,scalea*rtmpa[i][i_]);
|
|
}
|
|
//--- check
|
|
if(trans==1)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
rea[i_].Set(i,scalea*rtmpa[i][i_]);
|
|
}
|
|
}
|
|
//--- allocation
|
|
ArrayResize(rxe,n);
|
|
for(i=0;i<=n-1;i++)
|
|
rxe[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(rxs,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
rv=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
rv+=rea[i][i_]*rxe[i_];
|
|
rxs[i]=rv;
|
|
}
|
|
//--- check
|
|
if(CSafeSolve::RMatrixScaledTrSafeSolve(ra,scalea,n,rxs,isupper,trans,isunit,MathSqrt(CMath::m_maxrealnumber)))
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
rerrors=rerrors || MathAbs(rxs[i]-rxe[i])>threshold;
|
|
}
|
|
else
|
|
rerrors=true;
|
|
}
|
|
//--- Special test with diagonal ill-conditioned matrix:
|
|
//--- * ability to solve it when resulting growth is less than threshold
|
|
//--- * ability to stop solve when resulting growth is greater than threshold
|
|
//--- A=diag(1,1/growth)
|
|
//--- b=(1,0.5)
|
|
n=2;
|
|
growth=10;
|
|
//--- allocation
|
|
ca.Resize(n,n);
|
|
//--- change values
|
|
ca[0].Set(0,1);
|
|
ca[0].Set(1,0);
|
|
ca[1].Set(0,0);
|
|
ca[1].Set(1,1/growth);
|
|
//--- allocation
|
|
ArrayResize(cxs,n);
|
|
//--- change values
|
|
cxs[0]=1.0;
|
|
cxs[1]=0.5;
|
|
//--- search errors
|
|
cerrors=cerrors || !CSafeSolve::CMatrixScaledTrSafeSolve(ca,1.0,n,cxs,CMath::RandomReal()>0.5,CMath::RandomInteger(3),false,1.05*MathMax(CMath::AbsComplex(cxs[1])*growth,1.0));
|
|
cerrors=cerrors || !CSafeSolve::CMatrixScaledTrSafeSolve(ca,1.0,n,cxs,CMath::RandomReal()>0.5,CMath::RandomInteger(3),false,0.95*MathMax(CMath::AbsComplex(cxs[1])*growth,1.0));
|
|
//--- allocation
|
|
ra.Resize(n,n);
|
|
//--- change values
|
|
ra[0].Set(0,1);
|
|
ra[0].Set(1,0);
|
|
ra[1].Set(0,0);
|
|
ra[1].Set(1,1/growth);
|
|
//--- allocation
|
|
ArrayResize(rxs,n);
|
|
//--- change values
|
|
rxs[0]=1.0;
|
|
rxs[1]=0.5;
|
|
//--- search errors
|
|
rerrors=rerrors || !CSafeSolve::RMatrixScaledTrSafeSolve(ra,1.0,n,rxs,CMath::RandomReal()>0.5,CMath::RandomInteger(2),false,1.05*MathMax(MathAbs(rxs[1])*growth,1.0));
|
|
rerrors=rerrors || !CSafeSolve::RMatrixScaledTrSafeSolve(ra,1.0,n,rxs,CMath::RandomReal()>0.5,CMath::RandomInteger(2),false,0.95*MathMax(MathAbs(rxs[1])*growth,1.0));
|
|
//--- Special test with diagonal degenerate matrix:
|
|
//--- * ability to solve it when resulting growth is less than threshold
|
|
//--- * ability to stop solve when resulting growth is greater than threshold
|
|
//--- A=diag(1,0)
|
|
//--- b=(1,0.5)
|
|
n=2;
|
|
ca.Resize(n,n);
|
|
//--- change values
|
|
ca[0].Set(0,1);
|
|
ca[0].Set(1,0);
|
|
ca[1].Set(0,0);
|
|
ca[1].Set(1,0);
|
|
//--- allocation
|
|
ArrayResize(cxs,n);
|
|
//--- change values
|
|
cxs[0]=1.0;
|
|
cxs[1]=0.5;
|
|
//--- search errors
|
|
cerrors=cerrors || CSafeSolve::CMatrixScaledTrSafeSolve(ca,1.0,n,cxs,CMath::RandomReal()>0.5,CMath::RandomInteger(3),false,MathSqrt(CMath::m_maxrealnumber));
|
|
//--- allocation
|
|
ra.Resize(n,n);
|
|
//--- change values
|
|
ra[0].Set(0,1);
|
|
ra[0].Set(1,0);
|
|
ra[1].Set(0,0);
|
|
ra[1].Set(1,0);
|
|
//--- allocation
|
|
ArrayResize(rxs,n);
|
|
//--- change values
|
|
rxs[0]=1.0;
|
|
rxs[1]=0.5;
|
|
//--- search errors
|
|
rerrors=rerrors || CSafeSolve::RMatrixScaledTrSafeSolve(ra,1.0,n,rxs,CMath::RandomReal()>0.5,CMath::RandomInteger(2),false,MathSqrt(CMath::m_maxrealnumber));
|
|
//--- report
|
|
waserrors=rerrors || cerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING SAFE TR SOLVER");
|
|
Print("REAL: ");
|
|
//--- check
|
|
if(!rerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("COMPLEX: ");
|
|
//--- check
|
|
if(!cerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestSafeSolveUnit::RMatrixMakeACopy(CMatrixDouble &a,
|
|
const int m,const int n,
|
|
CMatrixDouble &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestSafeSolveUnit::CMatrixMakeACopy(CMatrixComplex &a,
|
|
const int m,const int n,
|
|
CMatrixComplex &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CRCond |
|
|
//+------------------------------------------------------------------+
|
|
class CTestRCondUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
|
|
static void RMatrixDropHalf(CMatrixDouble &a,const int n,const bool droplower);
|
|
static void CMatrixDropHalf(CMatrixComplex &a,const int n,const bool droplower);
|
|
static void RMatrixGenZero(CMatrixDouble &a0,const int n);
|
|
static bool RMatrixInvMatTr(CMatrixDouble &a,const int n,const bool isupper,const bool isunittriangular);
|
|
static bool RMatrixInvMatLU(CMatrixDouble &a,int &pivots[],const int n);
|
|
static bool RMatrixInvMat(CMatrixDouble &a,const int n);
|
|
static void RMatrixRefRCond(CMatrixDouble &a,const int n,double &rc1,double &rcinf);
|
|
static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b);
|
|
static void CMatrixGenZero(CMatrixComplex &a0,const int n);
|
|
static bool CMatrixInvMatTr(CMatrixComplex &a,const int n,const bool isupper,const bool isunittriangular);
|
|
static bool CMatrixInvMatLU(CMatrixComplex &a,int &pivots[],const int n);
|
|
static bool CMatrixInvMat(CMatrixComplex &a,const int n);
|
|
static void CMatrixRefRCond(CMatrixComplex &a,const int n,double &rc1,double &rcinf);
|
|
static bool TestRMatrixTrRCond(const int maxn,const int passcount);
|
|
static bool TestCMatrixTrRCond(const int maxn,const int passcount);
|
|
static bool TestRMatrixRCond(const int maxn,const int passcount);
|
|
static bool TestSPDMatrixRCond(const int maxn,const int passcount);
|
|
static bool TestCMatrixRCond(const int maxn,const int passcount);
|
|
static bool TestHPDMatrixRCond(const int maxn,const int passcount);
|
|
public:
|
|
//--- class constants
|
|
static const double m_threshold50;
|
|
static const double m_threshold90;
|
|
//--- constructor, destructor
|
|
CTestRCondUnit(void);
|
|
~CTestRCondUnit(void);
|
|
//--- public method
|
|
static bool TestRCond(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize constants |
|
|
//+------------------------------------------------------------------+
|
|
const double CTestRCondUnit::m_threshold50=0.25;
|
|
const double CTestRCondUnit::m_threshold90=0.10;
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestRCondUnit::CTestRCondUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestRCondUnit::~CTestRCondUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CRCond |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestRCondUnit::TestRCond(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int maxn=0;
|
|
int passcount=0;
|
|
bool waserrors;
|
|
bool rtrerr;
|
|
bool ctrerr;
|
|
bool rerr;
|
|
bool cerr;
|
|
bool spderr;
|
|
bool hpderr;
|
|
//--- initialization
|
|
maxn=10;
|
|
passcount=100;
|
|
//--- report
|
|
rtrerr=!TestRMatrixTrRCond(maxn,passcount);
|
|
ctrerr=!TestCMatrixTrRCond(maxn,passcount);
|
|
rerr=!TestRMatrixRCond(maxn,passcount);
|
|
cerr=!TestCMatrixRCond(maxn,passcount);
|
|
spderr=!TestSPDMatrixRCond(maxn,passcount);
|
|
hpderr=!TestHPDMatrixRCond(maxn,passcount);
|
|
waserrors=((((rtrerr || ctrerr) || rerr) || cerr) || spderr) || hpderr;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING RCOND");
|
|
Print("REAL TRIANGULAR: ");
|
|
//--- check
|
|
if(!rtrerr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("COMPLEX TRIANGULAR: ");
|
|
//--- check
|
|
if(!ctrerr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("REAL: ");
|
|
//--- check
|
|
if(!rerr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("SPD: ");
|
|
//--- check
|
|
if(!spderr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("HPD: ");
|
|
//--- check
|
|
if(!hpderr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("COMPLEX: ");
|
|
//--- check
|
|
if(!cerr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestRCondUnit::RMatrixMakeACopy(CMatrixDouble &a,const int m,
|
|
const int n,CMatrixDouble &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Drops upper or lower half of the matrix - fills it by special |
|
|
//| pattern which may be used later to ensure that this part wasn't |
|
|
//| changed |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestRCondUnit::RMatrixDropHalf(CMatrixDouble &a,const int n,
|
|
const bool droplower)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if((droplower && i>j) || (!droplower && i<j))
|
|
a[i].Set(j,1+2*i+3*j);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Drops upper or lower half of the matrix - fills it by special |
|
|
//| pattern which may be used later to ensure that this part wasn't |
|
|
//| changed |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestRCondUnit::CMatrixDropHalf(CMatrixComplex &a,const int n,
|
|
const bool droplower)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if((droplower && i>j) || (!droplower && i<j))
|
|
a[i].Set(j,1+2*i+3*j);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generate matrix with given condition number C (2-norm) |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestRCondUnit::RMatrixGenZero(CMatrixDouble &a0,const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
a0.Resize(n,n);
|
|
//--- make zero
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a0[i].Set(j,0);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Triangular inverse |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestRCondUnit::RMatrixInvMatTr(CMatrixDouble &a,const int n,
|
|
const bool isupper,
|
|
const bool isunittriangular)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool nounit;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double ajj=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double t[];
|
|
//--- initialization
|
|
result=true;
|
|
//--- allocation
|
|
ArrayResize(t,n);
|
|
//--- Test the input parameters.
|
|
nounit=!isunittriangular;
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute inverse of upper triangular matrix.
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0.0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
a[j].Set(j,1/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- Compute elements 1:j-1 of j-th column.
|
|
if(j>0)
|
|
{
|
|
for(i_=0;i_<=j-1;i_++)
|
|
t[i_]=a[i_][j];
|
|
for(i=0;i<=j-1;i++)
|
|
{
|
|
//--- check
|
|
if(i<j-1)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=i+1;i_<=j-1;i_++)
|
|
v+=a[i][i_]*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a[i].Set(j,v+a[i][i]*t[i]);
|
|
else
|
|
a[i].Set(j,v+t[i]);
|
|
}
|
|
//--- calculation
|
|
for(i_=0;i_<=j-1;i_++)
|
|
a[i_].Set(j,ajj*a[i_][j]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute inverse of lower triangular matrix.
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0.0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
a[j].Set(j,1/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- check
|
|
if(j<n-1)
|
|
{
|
|
//--- Compute elements j+1:n of j-th column.
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
t[i_]=a[i_][j];
|
|
for(i=j+1;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i>j+1)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=j+1;i_<=i-1;i_++)
|
|
v+=a[i][i_]*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a[i].Set(j,v+a[i][i]*t[i]);
|
|
else
|
|
a[i].Set(j,v+t[i]);
|
|
}
|
|
//--- calculation
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
a[i_].Set(j,ajj*a[i_][j]);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LU inverse |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestRCondUnit::RMatrixInvMatLU(CMatrixDouble &a,int &pivots[],
|
|
const int n)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
int jp=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double work[];
|
|
//--- initialization
|
|
result=true;
|
|
//--- Quick return if possible
|
|
if(n==0)
|
|
{
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- allocation
|
|
ArrayResize(work,n);
|
|
//--- Form inv(U)
|
|
if(!RMatrixInvMatTr(a,n,true,false))
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- Solve the equation inv(A)*L=inv(U) for inv(A).
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
//--- Copy current column of L to WORK and replace with zeros.
|
|
for(i=j+1;i<=n-1;i++)
|
|
{
|
|
work[i]=a[i][j];
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- Compute current column of inv(A).
|
|
if(j<n-1)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
v+=a[i][i_]*work[i_];
|
|
a[i].Set(j,a[i][j]-v);
|
|
}
|
|
}
|
|
}
|
|
//--- Apply column interchanges.
|
|
for(j=n-2;j>=0;j--)
|
|
{
|
|
jp=pivots[j];
|
|
//--- check
|
|
if(jp!=j)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
work[i_]=a[i_][j];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(j,a[i_][jp]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(jp,work[i_]);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Matrix inverse |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestRCondUnit::RMatrixInvMat(CMatrixDouble &a,const int n)
|
|
{
|
|
//--- create array
|
|
int pivots[];
|
|
//--- function call
|
|
CTrFac::RMatrixLU(a,n,n,pivots);
|
|
//--- return result
|
|
return(RMatrixInvMatLU(a,pivots,n));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| reference RCond |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestRCondUnit::RMatrixRefRCond(CMatrixDouble &a,const int n,
|
|
double &rc1,double &rcinf)
|
|
{
|
|
//--- create variables
|
|
double nrm1a=0;
|
|
double nrminfa=0;
|
|
double nrm1inva=0;
|
|
double nrminfinva=0;
|
|
double v=0;
|
|
int k=0;
|
|
int i=0;
|
|
//--- create matrix
|
|
CMatrixDouble inva;
|
|
//--- initialization
|
|
rc1=0;
|
|
rcinf=0;
|
|
//--- inv A
|
|
RMatrixMakeACopy(a,n,n,inva);
|
|
//--- check
|
|
if(!RMatrixInvMat(inva,n))
|
|
{
|
|
rc1=0;
|
|
rcinf=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- norm A
|
|
nrm1a=0;
|
|
nrminfa=0;
|
|
//--- calculation
|
|
for(k=0;k<=n-1;k++)
|
|
{
|
|
//--- change values
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+MathAbs(a[i][k]);
|
|
nrm1a=MathMax(nrm1a,v);
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+MathAbs(a[k][i]);
|
|
nrminfa=MathMax(nrminfa,v);
|
|
}
|
|
//--- norm inv A
|
|
nrm1inva=0;
|
|
nrminfinva=0;
|
|
//--- calculation
|
|
for(k=0;k<=n-1;k++)
|
|
{
|
|
//--- change values
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+MathAbs(inva[i][k]);
|
|
nrm1inva=MathMax(nrm1inva,v);
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+MathAbs(inva[k][i]);
|
|
nrminfinva=MathMax(nrminfinva,v);
|
|
}
|
|
//--- result
|
|
rc1=nrm1inva*nrm1a;
|
|
rcinf=nrminfinva*nrminfa;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestRCondUnit::CMatrixMakeACopy(CMatrixComplex &a,const int m,
|
|
const int n,CMatrixComplex &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generate matrix with given condition number C (2-norm) |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestRCondUnit::CMatrixGenZero(CMatrixComplex &a0,const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
a0.Resize(n,n);
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a0[i].Set(j,0);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| triangular inverse |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestRCondUnit::CMatrixInvMatTr(CMatrixComplex &a,const int n,
|
|
const bool isupper,
|
|
const bool isunittriangular)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool nounit;
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
complex ajj=0;
|
|
complex one=1;
|
|
int i_=0;
|
|
//--- create array
|
|
complex t[];
|
|
//--- initialization
|
|
result=true;
|
|
//--- allocation
|
|
ArrayResize(t,n);
|
|
//--- Test the input parameters.
|
|
nounit=!isunittriangular;
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute inverse of upper triangular matrix.
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
a[j].Set(j,one/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- Compute elements 1:j-1 of j-th column.
|
|
if(j>0)
|
|
{
|
|
for(i_=0;i_<=j-1;i_++)
|
|
t[i_]=a[i_][j];
|
|
//--- calculation
|
|
for(i=0;i<=j-1;i++)
|
|
{
|
|
//--- check
|
|
if(i<j-1)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=i+1;i_<=j-1;i_++)
|
|
v+=a[i][i_]*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a[i].Set(j,v+a[i][i]*t[i]);
|
|
else
|
|
a[i].Set(j,v+t[i]);
|
|
}
|
|
for(i_=0;i_<=j-1;i_++)
|
|
a[i_].Set(j,ajj*a[i_][j]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute inverse of lower triangular matrix.
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
a[j].Set(j,one/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- check
|
|
if(j<n-1)
|
|
{
|
|
//--- Compute elements j+1:n of j-th column.
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
t[i_]=a[i_][j];
|
|
//--- calculation
|
|
for(i=j+1;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i>j+1)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=j+1;i_<=i-1;i_++)
|
|
v+=a[i][i_]*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a[i].Set(j,v+a[i][i]*t[i]);
|
|
else
|
|
a[i].Set(j,v+t[i]);
|
|
}
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
a[i_].Set(j,ajj*a[i_][j]);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LU inverse |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestRCondUnit::CMatrixInvMatLU(CMatrixComplex &a,int &pivots[],
|
|
const int n)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
int jp=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
complex work[];
|
|
//--- initialization
|
|
result=true;
|
|
//--- Quick return if possible
|
|
if(n==0)
|
|
{
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- allocation
|
|
ArrayResize(work,n);
|
|
//--- Form inv(U)
|
|
if(!CMatrixInvMatTr(a,n,true,false))
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- Solve the equation inv(A)*L=inv(U) for inv(A).
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
//--- Copy current column of L to WORK and replace with zeros.
|
|
for(i=j+1;i<=n-1;i++)
|
|
{
|
|
work[i]=a[i][j];
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- Compute current column of inv(A).
|
|
if(j<n-1)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
v+=a[i][i_]*work[i_];
|
|
a[i].Set(j,a[i][j]-v);
|
|
}
|
|
}
|
|
}
|
|
//--- Apply column interchanges.
|
|
for(j=n-2;j>=0;j--)
|
|
{
|
|
jp=pivots[j];
|
|
//--- check
|
|
if(jp!=j)
|
|
{
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
work[i_]=a[i_][j];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(j,a[i_][jp]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(jp,work[i_]);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Matrix inverse |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestRCondUnit::CMatrixInvMat(CMatrixComplex &a,const int n)
|
|
{
|
|
//--- create array
|
|
int pivots[];
|
|
//--- function call
|
|
CTrFac::CMatrixLU(a,n,n,pivots);
|
|
//--- return result
|
|
return(CMatrixInvMatLU(a,pivots,n));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| reference RCond |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestRCondUnit::CMatrixRefRCond(CMatrixComplex &a,const int n,
|
|
double &rc1,double &rcinf)
|
|
{
|
|
//--- create variables
|
|
double nrm1a=0;
|
|
double nrminfa=0;
|
|
double nrm1inva=0;
|
|
double nrminfinva=0;
|
|
double v=0;
|
|
int k=0;
|
|
int i=0;
|
|
//--- create matrix
|
|
CMatrixComplex inva;
|
|
//--- initialization
|
|
rc1=0;
|
|
rcinf=0;
|
|
//--- inv A
|
|
CMatrixMakeACopy(a,n,n,inva);
|
|
//--- check
|
|
if(!CMatrixInvMat(inva,n))
|
|
{
|
|
rc1=0;
|
|
rcinf=0;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- norm A
|
|
nrm1a=0;
|
|
nrminfa=0;
|
|
//--- calculation
|
|
for(k=0;k<=n-1;k++)
|
|
{
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+CMath::AbsComplex(a[i][k]);
|
|
nrm1a=MathMax(nrm1a,v);
|
|
//--- change value
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+CMath::AbsComplex(a[k][i]);
|
|
nrminfa=MathMax(nrminfa,v);
|
|
}
|
|
//--- norm inv A
|
|
nrm1inva=0;
|
|
nrminfinva=0;
|
|
//--- calculation
|
|
for(k=0;k<=n-1;k++)
|
|
{
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+CMath::AbsComplex(inva[i][k]);
|
|
nrm1inva=MathMax(nrm1inva,v);
|
|
//--- change value
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+CMath::AbsComplex(inva[k][i]);
|
|
nrminfinva=MathMax(nrminfinva,v);
|
|
}
|
|
//--- result
|
|
rc1=nrm1inva*nrm1a;
|
|
rcinf=nrminfinva*nrminfa;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns True for successful test,False - for failed test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestRCondUnit::TestRMatrixTrRCond(const int maxn,const int passcount)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
int pass=0;
|
|
bool err50;
|
|
bool err90;
|
|
bool errspec;
|
|
bool errless;
|
|
double erc1=0;
|
|
double ercinf=0;
|
|
double v=0;
|
|
bool isupper;
|
|
bool isunit;
|
|
//--- create arrays
|
|
int p[];
|
|
double q50[];
|
|
double q90[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble ea;
|
|
//--- initialization
|
|
err50=false;
|
|
err90=false;
|
|
errless=false;
|
|
errspec=false;
|
|
//--- allocation
|
|
ArrayResize(q50,2);
|
|
ArrayResize(q90,2);
|
|
//--- calculation
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- special test for zero matrix
|
|
RMatrixGenZero(a,n);
|
|
//--- search errors
|
|
errspec=errspec || CRCond::RMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
errspec=errspec || CRCond::RMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
//--- general test
|
|
a.Resize(n,n);
|
|
for(i=0;i<=1;i++)
|
|
{
|
|
q50[i]=0;
|
|
q90[i]=0;
|
|
}
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- change values
|
|
isupper=CMath::RandomReal()>0.5;
|
|
isunit=CMath::RandomReal()>0.5;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,CMath::RandomReal()-0.5);
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
a[i].Set(i,1+CMath::RandomReal());
|
|
//--- function call
|
|
RMatrixMakeACopy(a,n,n,ea);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
j1=0;
|
|
j2=i-1;
|
|
}
|
|
else
|
|
{
|
|
j1=i+1;
|
|
j2=n-1;
|
|
}
|
|
//--- change values
|
|
for(j=j1;j<=j2;j++)
|
|
ea[i].Set(j,0);
|
|
//--- check
|
|
if(isunit)
|
|
ea[i].Set(i,1);
|
|
}
|
|
//--- function call
|
|
RMatrixRefRCond(ea,n,erc1,ercinf);
|
|
//--- 1-norm
|
|
v=1/CRCond::RMatrixTrRCond1(a,n,isupper,isunit);
|
|
//--- check
|
|
if(v>=m_threshold50*erc1)
|
|
q50[0]=q50[0]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[0]=q90[0]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless || v>erc1*1.001;
|
|
//--- Inf-norm
|
|
v=1/CRCond::RMatrixTrRCondInf(a,n,isupper,isunit);
|
|
//--- check
|
|
if(v>=m_threshold50*ercinf)
|
|
q50[1]=q50[1]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=(double)(m_threshold90*ercinf))
|
|
q90[1]=q90[1]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless || v>ercinf*1.001;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=1;i++)
|
|
{
|
|
err50=err50 || q50[i]<0.5;
|
|
err90=err90 || q90[i]<0.9;
|
|
}
|
|
//--- degenerate matrix test
|
|
if(n>=3)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0.0);
|
|
}
|
|
a[0].Set(0,1);
|
|
a[n-1].Set(n-1,1);
|
|
//--- search errors
|
|
errspec=errspec || CRCond::RMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
errspec=errspec || CRCond::RMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
}
|
|
//--- near-degenerate matrix test
|
|
if(n>=2)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0.0);
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
a[i].Set(i,1);
|
|
i=CMath::RandomInteger(n);
|
|
a[i].Set(i,0.1*CMath::m_maxrealnumber);
|
|
//--- search errors
|
|
errspec=errspec || CRCond::RMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
errspec=errspec || CRCond::RMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
}
|
|
}
|
|
//--- report
|
|
result=!(((err50 || err90) || errless) || errspec);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns True for successful test,False - for failed test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestRCondUnit::TestCMatrixTrRCond(const int maxn,const int passcount)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int j1=0;
|
|
int j2=0;
|
|
int pass=0;
|
|
bool err50;
|
|
bool err90;
|
|
bool errspec;
|
|
bool errless;
|
|
double erc1=0;
|
|
double ercinf=0;
|
|
double v=0;
|
|
bool isupper;
|
|
bool isunit;
|
|
//--- create arrays
|
|
int p[];
|
|
double q50[];
|
|
double q90[];
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex ea;
|
|
//--- initialization
|
|
err50=false;
|
|
err90=false;
|
|
errless=false;
|
|
errspec=false;
|
|
//--- allocation
|
|
ArrayResize(q50,2);
|
|
ArrayResize(q90,2);
|
|
//--- calculation
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- special test for zero matrix
|
|
CMatrixGenZero(a,n);
|
|
//--- search errors
|
|
errspec=errspec || CRCond::CMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
errspec=errspec || CRCond::CMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
//--- general test
|
|
a.Resize(n,n);
|
|
for(i=0;i<=1;i++)
|
|
{
|
|
q50[i]=0;
|
|
q90[i]=0;
|
|
}
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- change values
|
|
isupper=CMath::RandomReal()>0.5;
|
|
isunit=CMath::RandomReal()>0.5;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a[i].SetRe(j,CMath::RandomReal()-0.5);
|
|
a[i].SetIm(j,CMath::RandomReal()-0.5);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i].SetRe(i,1+CMath::RandomReal());
|
|
a[i].SetIm(i,1+CMath::RandomReal());
|
|
}
|
|
//--- function call
|
|
CMatrixMakeACopy(a,n,n,ea);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
j1=0;
|
|
j2=i-1;
|
|
}
|
|
else
|
|
{
|
|
j1=i+1;
|
|
j2=n-1;
|
|
}
|
|
for(j=j1;j<=j2;j++)
|
|
ea[i].Set(j,0);
|
|
//--- check
|
|
if(isunit)
|
|
ea[i].Set(i,1);
|
|
}
|
|
//--- function call
|
|
CMatrixRefRCond(ea,n,erc1,ercinf);
|
|
//--- 1-norm
|
|
v=1/CRCond::CMatrixTrRCond1(a,n,isupper,isunit);
|
|
if(v>=m_threshold50*erc1)
|
|
q50[0]=q50[0]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=(double)(m_threshold90*erc1))
|
|
q90[0]=q90[0]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless || v>(double)(erc1*1.001);
|
|
//--- Inf-norm
|
|
v=1/CRCond::CMatrixTrRCondInf(a,n,isupper,isunit);
|
|
//--- check
|
|
if(v>=m_threshold50*ercinf)
|
|
q50[1]=q50[1]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=(double)(m_threshold90*ercinf))
|
|
q90[1]=q90[1]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless || v>(double)(ercinf*1.001);
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=1;i++)
|
|
{
|
|
err50=err50 || q50[i]<0.5;
|
|
err90=err90 || q90[i]<0.9;
|
|
}
|
|
//--- degenerate matrix test
|
|
if(n>=3)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0.0);
|
|
}
|
|
a[0].Set(0,1);
|
|
a[n-1].Set(n-1,1);
|
|
//--- search errors
|
|
errspec=errspec || CRCond::CMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
errspec=errspec || CRCond::CMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
}
|
|
//--- near-degenerate matrix test
|
|
if(n>=2)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0.0);
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
a[i].Set(i,1);
|
|
i=CMath::RandomInteger(n);
|
|
a[i].Set(i,0.1*CMath::m_maxrealnumber);
|
|
//--- search errors
|
|
errspec=errspec || CRCond::CMatrixTrRCond1(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
errspec=errspec || CRCond::CMatrixTrRCondInf(a,n,CMath::RandomReal()>0.5,false)!=0.0;
|
|
}
|
|
}
|
|
//--- report
|
|
result=!(((err50 || err90) || errless) || errspec);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns True for successful test,False - for failed test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestRCondUnit::TestRMatrixRCond(const int maxn,const int passcount)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
bool err50;
|
|
bool err90;
|
|
bool errspec;
|
|
bool errless;
|
|
double erc1=0;
|
|
double ercinf=0;
|
|
double v=0;
|
|
//--- create array
|
|
int p[];
|
|
double q50[];
|
|
double q90[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble lua;
|
|
//--- initialization
|
|
err50=false;
|
|
err90=false;
|
|
errless=false;
|
|
errspec=false;
|
|
//--- allocation
|
|
ArrayResize(q50,4);
|
|
ArrayResize(q90,4);
|
|
//--- calculation
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- special test for zero matrix
|
|
RMatrixGenZero(a,n);
|
|
RMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::RMatrixLU(lua,n,n,p);
|
|
//--- search errors
|
|
errspec=errspec || CRCond::RMatrixRCond1(a,n)!=0.0;
|
|
errspec=errspec || CRCond::RMatrixRCondInf(a,n)!=0.0;
|
|
errspec=errspec || CRCond::RMatrixLURCond1(lua,n)!=0.0;
|
|
errspec=errspec || CRCond::RMatrixLURCondInf(lua,n)!=0.0;
|
|
//--- general test
|
|
a.Resize(n,n);
|
|
for(i=0;i<=3;i++)
|
|
{
|
|
q50[i]=0;
|
|
q90[i]=0;
|
|
}
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- function call
|
|
CMatGen::RMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a);
|
|
RMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::RMatrixLU(lua,n,n,p);
|
|
RMatrixRefRCond(a,n,erc1,ercinf);
|
|
//--- 1-norm,normal
|
|
v=1/CRCond::RMatrixRCond1(a,n);
|
|
//--- check
|
|
if(v>=m_threshold50*erc1)
|
|
q50[0]=q50[0]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[0]=q90[0]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless || v>(double)(erc1*1.001);
|
|
//--- 1-norm,LU
|
|
v=1/CRCond::RMatrixLURCond1(lua,n);
|
|
//--- check
|
|
if(v>=m_threshold50*erc1)
|
|
q50[1]=q50[1]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[1]=q90[1]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless || v>erc1*1.001;
|
|
//--- Inf-norm,normal
|
|
v=1/CRCond::RMatrixRCondInf(a,n);
|
|
//--- check
|
|
if(v>=m_threshold50*ercinf)
|
|
q50[2]=q50[2]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*ercinf)
|
|
q90[2]=q90[2]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless || v>ercinf*1.001;
|
|
//--- Inf-norm,LU
|
|
v=1/CRCond::RMatrixLURCondInf(lua,n);
|
|
//--- check
|
|
if(v>=m_threshold50*ercinf)
|
|
q50[3]=q50[3]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*ercinf)
|
|
q90[3]=q90[3]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless || v>ercinf*1.001;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=3;i++)
|
|
{
|
|
err50=err50 || q50[i]<0.5;
|
|
err90=err90 || q90[i]<0.9;
|
|
}
|
|
//--- degenerate matrix test
|
|
if(n>=3)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0.0);
|
|
}
|
|
a[0].Set(0,1);
|
|
a[n-1].Set(n-1,1);
|
|
//--- search errors
|
|
errspec=errspec || CRCond::RMatrixRCond1(a,n)!=0.0;
|
|
errspec=errspec || CRCond::RMatrixRCondInf(a,n)!=0.0;
|
|
errspec=errspec || CRCond::RMatrixLURCond1(a,n)!=0.0;
|
|
errspec=errspec || CRCond::RMatrixLURCondInf(a,n)!=0.0;
|
|
}
|
|
//--- near-degenerate matrix test
|
|
if(n>=2)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0.0);
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
a[i].Set(i,1);
|
|
i=CMath::RandomInteger(n);
|
|
a[i].Set(i,0.1*CMath::m_maxrealnumber);
|
|
//--- search errors
|
|
errspec=errspec || CRCond::RMatrixRCond1(a,n)!=0.0;
|
|
errspec=errspec || CRCond::RMatrixRCondInf(a,n)!=0.0;
|
|
errspec=errspec || CRCond::RMatrixLURCond1(a,n)!=0.0;
|
|
errspec=errspec || CRCond::RMatrixLURCondInf(a,n)!=0.0;
|
|
}
|
|
}
|
|
//--- report
|
|
result=!(((err50 || err90) || errless) || errspec);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns True for successful test,False - for failed test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestRCondUnit::TestSPDMatrixRCond(const int maxn,const int passcount)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
bool err50;
|
|
bool err90;
|
|
bool errspec;
|
|
bool errless;
|
|
bool isupper;
|
|
double erc1=0;
|
|
double ercinf=0;
|
|
double v=0;
|
|
//--- create arrays
|
|
int p[];
|
|
double q50[];
|
|
double q90[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble cha;
|
|
//--- initialization
|
|
err50=false;
|
|
err90=false;
|
|
errless=false;
|
|
errspec=false;
|
|
//--- allocation
|
|
ArrayResize(q50,2);
|
|
ArrayResize(q90,2);
|
|
//--- calculation
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
isupper=CMath::RandomReal()>0.5;
|
|
//--- general test
|
|
a.Resize(n,n);
|
|
for(i=0;i<=1;i++)
|
|
{
|
|
q50[i]=0;
|
|
q90[i]=0;
|
|
}
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- function calls
|
|
CMatGen::SPDMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a);
|
|
RMatrixRefRCond(a,n,erc1,ercinf);
|
|
RMatrixDropHalf(a,n,isupper);
|
|
RMatrixMakeACopy(a,n,n,cha);
|
|
CTrFac::SPDMatrixCholesky(cha,n,isupper);
|
|
//--- normal
|
|
v=1/CRCond::SPDMatrixRCond(a,n,isupper);
|
|
//--- check
|
|
if(v>=m_threshold50*erc1)
|
|
q50[0]=q50[0]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[0]=q90[0]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless || v>erc1*1.001;
|
|
//--- Cholesky
|
|
v=1/CRCond::SPDMatrixCholeskyRCond(cha,n,isupper);
|
|
if(v>=m_threshold50*erc1)
|
|
q50[1]=q50[1]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[1]=q90[1]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless || v>erc1*1.001;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=1;i++)
|
|
{
|
|
err50=err50 || q50[i]<0.5;
|
|
err90=err90 || q90[i]<0.9;
|
|
}
|
|
//--- degenerate matrix test
|
|
if(n>=3)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0.0);
|
|
}
|
|
a[0].Set(0,1);
|
|
a[n-1].Set(n-1,1);
|
|
//--- search errors
|
|
errspec=errspec || CRCond::SPDMatrixRCond(a,n,isupper)!=-1.0;
|
|
errspec=errspec || CRCond::SPDMatrixCholeskyRCond(a,n,isupper)!=0.0;
|
|
}
|
|
//--- near-degenerate matrix test
|
|
if(n>=2)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0.0);
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
a[i].Set(i,1);
|
|
i=CMath::RandomInteger(n);
|
|
a[i].Set(i,0.1*CMath::m_maxrealnumber);
|
|
//--- search errors
|
|
errspec=errspec || CRCond::SPDMatrixRCond(a,n,isupper)!=0.0;
|
|
errspec=errspec || CRCond::SPDMatrixCholeskyRCond(a,n,isupper)!=0.0;
|
|
}
|
|
}
|
|
//--- report
|
|
result=!(((err50 || err90) || errless) || errspec);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns True for successful test,False - for failed test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestRCondUnit::TestCMatrixRCond(const int maxn,const int passcount)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
bool err50;
|
|
bool err90;
|
|
bool errless;
|
|
bool errspec;
|
|
double erc1=0;
|
|
double ercinf=0;
|
|
double v=0;
|
|
//--- create arrays
|
|
int p[];
|
|
double q50[];
|
|
double q90[];
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex lua;
|
|
//--- allocation
|
|
ArrayResize(q50,4);
|
|
ArrayResize(q90,4);
|
|
//--- initialization
|
|
err50=false;
|
|
err90=false;
|
|
errless=false;
|
|
errspec=false;
|
|
//--- process
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- special test for zero matrix
|
|
CMatrixGenZero(a,n);
|
|
CMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::CMatrixLU(lua,n,n,p);
|
|
//--- search errors
|
|
errspec=errspec || CRCond::CMatrixRCond1(a,n)!=0.0;
|
|
errspec=errspec || CRCond::CMatrixRCondInf(a,n)!=0.0;
|
|
errspec=errspec || CRCond::CMatrixLURCond1(lua,n)!=0.0;
|
|
errspec=errspec || CRCond::CMatrixLURCondInf(lua,n)!=0.0;
|
|
//--- general test
|
|
a.Resize(n,n);
|
|
for(i=0;i<=3;i++)
|
|
{
|
|
q50[i]=0;
|
|
q90[i]=0;
|
|
}
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- function calls
|
|
CMatGen::CMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a);
|
|
CMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::CMatrixLU(lua,n,n,p);
|
|
CMatrixRefRCond(a,n,erc1,ercinf);
|
|
//--- 1-norm,normal
|
|
v=1/CRCond::CMatrixRCond1(a,n);
|
|
//--- check
|
|
if(v>=m_threshold50*erc1)
|
|
q50[0]=q50[0]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[0]=q90[0]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless || v>erc1*1.001;
|
|
//--- 1-norm,LU
|
|
v=1/CRCond::CMatrixLURCond1(lua,n);
|
|
//--- check
|
|
if(v>=m_threshold50*erc1)
|
|
q50[1]=q50[1]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[1]=q90[1]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless || v>erc1*1.001;
|
|
//--- Inf-norm,normal
|
|
v=1/CRCond::CMatrixRCondInf(a,n);
|
|
//--- check
|
|
if(v>=m_threshold50*ercinf)
|
|
q50[2]=q50[2]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*ercinf)
|
|
q90[2]=q90[2]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless || v>ercinf*1.001;
|
|
//--- Inf-norm,LU
|
|
v=1/CRCond::CMatrixLURCondInf(lua,n);
|
|
//--- check
|
|
if(v>=m_threshold50*ercinf)
|
|
q50[3]=q50[3]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*ercinf)
|
|
q90[3]=q90[3]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless || v>ercinf*1.001;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=3;i++)
|
|
{
|
|
err50=err50 || q50[i]<0.5;
|
|
err90=err90 || q90[i]<0.9;
|
|
}
|
|
//--- degenerate matrix test
|
|
if(n>=3)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0.0);
|
|
}
|
|
a[0].Set(0,1);
|
|
a[n-1].Set(n-1,1);
|
|
//--- search errors
|
|
errspec=errspec || CRCond::CMatrixRCond1(a,n)!=0.0;
|
|
errspec=errspec || CRCond::CMatrixRCondInf(a,n)!=0.0;
|
|
errspec=errspec || CRCond::CMatrixLURCond1(a,n)!=0.0;
|
|
errspec=errspec || CRCond::CMatrixLURCondInf(a,n)!=0.0;
|
|
}
|
|
//--- near-degenerate matrix test
|
|
if(n>=2)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0.0);
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
a[i].Set(i,1);
|
|
i=CMath::RandomInteger(n);
|
|
a[i].Set(i,0.1*CMath::m_maxrealnumber);
|
|
//--- search errors
|
|
errspec=errspec || CRCond::CMatrixRCond1(a,n)!=0.0;
|
|
errspec=errspec || CRCond::CMatrixRCondInf(a,n)!=0.0;
|
|
errspec=errspec || CRCond::CMatrixLURCond1(a,n)!=0.0;
|
|
errspec=errspec || CRCond::CMatrixLURCondInf(a,n)!=0.0;
|
|
}
|
|
}
|
|
//--- report
|
|
result=!(((err50 || err90) || errless) || errspec);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns True for successful test,False - for failed test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestRCondUnit::TestHPDMatrixRCond(const int maxn,const int passcount)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
bool err50;
|
|
bool err90;
|
|
bool errspec;
|
|
bool errless;
|
|
bool isupper;
|
|
double erc1=0;
|
|
double ercinf=0;
|
|
double v=0;
|
|
//--- create arrays
|
|
int p[];
|
|
double q50[];
|
|
double q90[];
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex cha;
|
|
//--- initialization
|
|
err50=false;
|
|
err90=false;
|
|
errless=false;
|
|
errspec=false;
|
|
//--- allocation
|
|
ArrayResize(q50,2);
|
|
ArrayResize(q90,2);
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
isupper=CMath::RandomReal()>0.5;
|
|
//--- general test
|
|
a.Resize(n,n);
|
|
for(i=0;i<=1;i++)
|
|
{
|
|
q50[i]=0;
|
|
q90[i]=0;
|
|
}
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- function calls
|
|
CMatGen::HPDMatrixRndCond(n,MathExp(CMath::RandomReal()*MathLog(1000)),a);
|
|
CMatrixRefRCond(a,n,erc1,ercinf);
|
|
CMatrixDropHalf(a,n,isupper);
|
|
CMatrixMakeACopy(a,n,n,cha);
|
|
CTrFac::HPDMatrixCholesky(cha,n,isupper);
|
|
//--- normal
|
|
v=1/CRCond::HPDMatrixRCond(a,n,isupper);
|
|
//--- check
|
|
if(v>=m_threshold50*erc1)
|
|
q50[0]=q50[0]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[0]=q90[0]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless || v>erc1*1.001;
|
|
//--- Cholesky
|
|
v=1/CRCond::HPDMatrixCholeskyRCond(cha,n,isupper);
|
|
//--- check
|
|
if(v>=m_threshold50*erc1)
|
|
q50[1]=q50[1]+1.0/(double)passcount;
|
|
//--- check
|
|
if(v>=m_threshold90*erc1)
|
|
q90[1]=q90[1]+1.0/(double)passcount;
|
|
//--- search errors
|
|
errless=errless || v>erc1*1.001;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=1;i++)
|
|
{
|
|
err50=err50 || q50[i]<0.5;
|
|
err90=err90 || q90[i]<0.9;
|
|
}
|
|
//--- degenerate matrix test
|
|
if(n>=3)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0.0);
|
|
}
|
|
a[0].Set(0,1);
|
|
a[n-1].Set(n-1,1);
|
|
//--- search errors
|
|
errspec=errspec || CRCond::HPDMatrixRCond(a,n,isupper)!=-1.0;
|
|
errspec=errspec || CRCond::HPDMatrixCholeskyRCond(a,n,isupper)!=0.0;
|
|
}
|
|
//--- near-degenerate matrix test
|
|
if(n>=2)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0.0);
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
a[i].Set(i,1);
|
|
i=CMath::RandomInteger(n);
|
|
a[i].Set(i,0.1*CMath::m_maxrealnumber);
|
|
//--- search errors
|
|
errspec=errspec || CRCond::HPDMatrixRCond(a,n,isupper)!=0.0;
|
|
errspec=errspec || CRCond::HPDMatrixCholeskyRCond(a,n,isupper)!=0.0;
|
|
}
|
|
}
|
|
//--- report
|
|
result=!(((err50 || err90) || errless) || errspec);
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMatInv |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMatInvUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
|
|
static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b);
|
|
static bool RMatrixCheckInverse(CMatrixDouble &a,CMatrixDouble &inva,const int n,const double threshold,const int info,CMatInvReport &rep);
|
|
static bool SPDMatrixCheckInverse(CMatrixDouble &ca,CMatrixDouble &cinva,const bool isupper,const int n,const double threshold,const int info,CMatInvReport &rep);
|
|
static bool HPDMatrixCheckInverse(CMatrixComplex &ca,CMatrixComplex &cinva,const bool isupper,const int n,const double threshold,const int info,CMatInvReport &rep);
|
|
static bool RMatrixCheckInverseSingular(CMatrixDouble &inva,const int n,const double threshold,const int info,CMatInvReport &rep);
|
|
static bool CMatrixCheckInverse(CMatrixComplex &a,CMatrixComplex &inva,const int n,const double threshold,const int info,CMatInvReport &rep);
|
|
static bool CMatrixCheckInverseSingular(CMatrixComplex &inva,const int n,const double threshold,const int info,CMatInvReport &rep);
|
|
static void RMatrixDropHalf(CMatrixDouble &a,const int n,const bool droplower);
|
|
static void CMatrixDropHalf(CMatrixComplex &a,const int n,const bool droplower);
|
|
static void TestRTRInv(const int maxn,const int passcount,const double threshold,bool &rtrerrors);
|
|
static void TestCTRInv(const int maxn,const int passcount,const double threshold,bool &ctrerrors);
|
|
static void TesTrInv(const int maxn,const int passcount,const double threshold,bool &rerrors);
|
|
static void TestCInv(const int maxn,const int passcount,const double threshold,bool &cerrors);
|
|
static void TestSPDInv(const int maxn,const int passcount,const double threshold,bool &spderrors);
|
|
static void TestHPDInv(const int maxn,const int passcount,const double threshold,bool &hpderrors);
|
|
static void Unset2D(CMatrixDouble &x);
|
|
static void Unset1D(double &x[]);
|
|
static void CUnset2D(CMatrixComplex &x);
|
|
static void CUnset1D(double &x[]);
|
|
static void UnsetRep(CMatInvReport &r);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestMatInvUnit(void);
|
|
~CTestMatInvUnit(void);
|
|
//--- public method
|
|
static bool TestMatInv(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestMatInvUnit::CTestMatInvUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestMatInvUnit::~CTestMatInvUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMatInvUnit::TestMatInv(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int maxrn=0;
|
|
int maxcn=0;
|
|
int passcount=0;
|
|
double threshold=0;
|
|
double rcondtol=0;
|
|
bool rtrerrors;
|
|
bool ctrerrors;
|
|
bool rerrors;
|
|
bool cerrors;
|
|
bool spderrors;
|
|
bool hpderrors;
|
|
bool waserrors;
|
|
//--- create matrix
|
|
CMatrixDouble emptyra;
|
|
CMatrixDouble emptyca;
|
|
//--- initialization
|
|
maxrn=3*CAblas::AblasBlockSize()+1;
|
|
maxcn=3*CAblas::AblasBlockSize()+1;
|
|
passcount=1;
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
rcondtol=0.01;
|
|
rtrerrors=false;
|
|
ctrerrors=false;
|
|
rerrors=false;
|
|
cerrors=false;
|
|
spderrors=false;
|
|
hpderrors=false;
|
|
//--- function calls
|
|
TestRTRInv(maxrn,passcount,threshold,rtrerrors);
|
|
TestCTRInv(maxcn,passcount,threshold,ctrerrors);
|
|
TesTrInv(maxrn,passcount,threshold,rerrors);
|
|
TestSPDInv(maxrn,passcount,threshold,spderrors);
|
|
TestCInv(maxcn,passcount,threshold,cerrors);
|
|
TestHPDInv(maxcn,passcount,threshold,hpderrors);
|
|
//--- search errors
|
|
waserrors=((((rtrerrors || ctrerrors) || rerrors) || cerrors) || spderrors) || hpderrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING MATINV");
|
|
Print("* REAL TRIANGULAR: ");
|
|
//--- check
|
|
if(rtrerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* COMPLEX TRIANGULAR: ");
|
|
//--- check
|
|
if(ctrerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* REAL: ");
|
|
//--- check
|
|
if(rerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* COMPLEX: ");
|
|
//--- check
|
|
if(cerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* SPD: ");
|
|
//--- check
|
|
if(spderrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* HPD: ");
|
|
//--- check
|
|
if(hpderrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatInvUnit::RMatrixMakeACopy(CMatrixDouble &a,
|
|
const int m,const int n,
|
|
CMatrixDouble &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatInvUnit::CMatrixMakeACopy(CMatrixComplex &a,
|
|
const int m,const int n,
|
|
CMatrixComplex &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether inverse is correct |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMatInvUnit::RMatrixCheckInverse(CMatrixDouble &a,
|
|
CMatrixDouble &inva,
|
|
const int n,
|
|
const double threshold,
|
|
const int info,
|
|
CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
result=true;
|
|
//--- check
|
|
if(info<=0)
|
|
result=false;
|
|
else
|
|
{
|
|
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*inva[i_][j];
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
result=result && MathAbs(v)<=threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether inverse is correct |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMatInvUnit::SPDMatrixCheckInverse(CMatrixDouble &ca,
|
|
CMatrixDouble &cinva,
|
|
const bool isupper,
|
|
const int n,
|
|
const double threshold,
|
|
const int info,
|
|
CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble inva;
|
|
//--- copy
|
|
a=ca;
|
|
inva=cinva;
|
|
//--- calculation
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- change values
|
|
for(i_=i+1;i_<=n-1;i_++)
|
|
a[i_].Set(i,a[i][i_]);
|
|
for(i_=i+1;i_<=n-1;i_++)
|
|
inva[i_].Set(i,inva[i][i_]);
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
for(i_=i+1;i_<=n-1;i_++)
|
|
a[i].Set(i_,a[i_][i]);
|
|
for(i_=i+1;i_<=n-1;i_++)
|
|
inva[i].Set(i_,inva[i_][i]);
|
|
}
|
|
}
|
|
//--- change value
|
|
result=true;
|
|
//--- check
|
|
if(info<=0)
|
|
result=false;
|
|
else
|
|
{
|
|
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*inva[i_][j];
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
result=result && MathAbs(v)<=threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether inverse is correct |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMatInvUnit::HPDMatrixCheckInverse(CMatrixComplex &ca,
|
|
CMatrixComplex &cinva,
|
|
const bool isupper,
|
|
const int n,
|
|
const double threshold,
|
|
const int info,
|
|
CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex inva;
|
|
//--- copy
|
|
a=ca;
|
|
inva=cinva;
|
|
//--- calculation
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- change values
|
|
for(i_=i+1;i_<=n-1;i_++)
|
|
a[i_].Set(i,CMath::Conj(a[i][i_]));
|
|
for(i_=i+1;i_<=n-1;i_++)
|
|
inva[i_].Set(i,CMath::Conj(inva[i][i_]));
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
for(i_=i+1;i_<=n-1;i_++)
|
|
a[i].Set(i_,CMath::Conj(a[i_][i]));
|
|
for(i_=i+1;i_<=n-1;i_++)
|
|
inva[i].Set(i_,CMath::Conj(inva[i_][i]));
|
|
}
|
|
}
|
|
//--- change value
|
|
result=true;
|
|
//--- check
|
|
if(info<=0)
|
|
result=false;
|
|
else
|
|
{
|
|
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*inva[i_][j];
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
result=result && CMath::AbsComplex(v)<=threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether inversion result indicate singular matrix |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMatInvUnit::RMatrixCheckInverseSingular(CMatrixDouble &inva,
|
|
const int n,
|
|
const double threshold,
|
|
const int info,
|
|
CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
//--- initialization
|
|
result=true;
|
|
//--- check
|
|
if(info!=-3 && info!=1)
|
|
result=false;
|
|
else
|
|
{
|
|
result=result && !(rep.m_r1<0.0 || rep.m_r1>1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon);
|
|
//--- check
|
|
if(info==-3)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
result=result && inva[i][j]==0.0;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether inverse is correct |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMatInvUnit::CMatrixCheckInverse(CMatrixComplex &a,
|
|
CMatrixComplex &inva,
|
|
const int n,
|
|
const double threshold,
|
|
const int info,
|
|
CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
result=true;
|
|
//--- check
|
|
if(info<=0)
|
|
result=false;
|
|
else
|
|
{
|
|
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*inva[i_][j];
|
|
//--- check
|
|
if(i==j)
|
|
v=v-1;
|
|
result=result && CMath::AbsComplex(v)<=threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether inversion result indicate singular matrix |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMatInvUnit::CMatrixCheckInverseSingular(CMatrixComplex &inva,
|
|
const int n,
|
|
const double threshold,
|
|
const int info,
|
|
CMatInvReport &rep)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
//--- initialization
|
|
result=true;
|
|
//--- check
|
|
if(info!=-3 && info!=1)
|
|
result=false;
|
|
else
|
|
{
|
|
result=result && !(rep.m_r1<0.0 || rep.m_r1>1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon);
|
|
//--- check
|
|
if(info==-3)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
result=result && inva[i][j]==0;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Drops upper or lower half of the matrix - fills it by special |
|
|
//| pattern which may be used later to ensure that this part wasn't |
|
|
//| changed |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatInvUnit::RMatrixDropHalf(CMatrixDouble &a,const int n,
|
|
const bool droplower)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if((droplower && i>j) || (!droplower && i<j))
|
|
a[i].Set(j,1+2*i+3*j);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Drops upper or lower half of the matrix - fills it by special |
|
|
//| pattern which may be used later to ensure that this part wasn't |
|
|
//| changed |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatInvUnit::CMatrixDropHalf(CMatrixComplex &a,const int n,
|
|
const bool droplower)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if((droplower && i>j) || (!droplower && i<j))
|
|
a[i].Set(j,1+2*i+3*j);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Real TR inverse |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatInvUnit::TestRTRInv(const int maxn,const int passcount,
|
|
const double threshold,bool &rtrerrors)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int task=0;
|
|
bool isupper;
|
|
bool isunit;
|
|
double v=0;
|
|
bool waserrors;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble b;
|
|
//--- object of class
|
|
CMatInvReport rep;
|
|
//--- initialization
|
|
waserrors=false;
|
|
//--- Test
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
b.Resize(n,n);
|
|
//--- calculation
|
|
for(task=0;task<=3;task++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Determine task
|
|
isupper=task%2==0;
|
|
isunit=task/2%2==0;
|
|
//--- Generate matrix
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
a[i].Set(i,1+CMath::RandomReal());
|
|
else
|
|
a[i].Set(j,0.2*CMath::RandomReal()-0.1);
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//--- Inverse
|
|
CMatInv::RMatrixTrInverse(b,n,isupper,isunit,info,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
rtrerrors=true;
|
|
return;
|
|
}
|
|
//--- Structural test
|
|
if(isunit)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
rtrerrors=rtrerrors || a[i][i]!=b[i][i];
|
|
}
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=i-1;j++)
|
|
rtrerrors=rtrerrors || a[i][j]!=b[i][j];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i+1;j<=n-1;j++)
|
|
rtrerrors=rtrerrors || a[i][j]!=b[i][j];
|
|
}
|
|
}
|
|
//--- Inverse test
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if((j<i && isupper) || (j>i && !isupper))
|
|
{
|
|
a[i].Set(j,0);
|
|
b[i].Set(j,0);
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(isunit)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i].Set(i,1);
|
|
b[i].Set(i,1);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*b[i_][j];
|
|
//--- check
|
|
if(j!=i)
|
|
rtrerrors=rtrerrors || MathAbs(v)>threshold;
|
|
else
|
|
rtrerrors=rtrerrors || MathAbs(v-1)>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Complex TR inverse |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatInvUnit::TestCTRInv(const int maxn,const int passcount,
|
|
const double threshold,bool &ctrerrors)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int task=0;
|
|
bool isupper;
|
|
bool isunit;
|
|
complex v=0;
|
|
bool waserrors;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
CMatrixComplex a;
|
|
CMatrixComplex b;
|
|
//--- object of class
|
|
CMatInvReport rep;
|
|
//--- initialization
|
|
waserrors=false;
|
|
//--- Test
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
b.Resize(n,n);
|
|
//--- calculation
|
|
for(task=0;task<=3;task++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Determine task
|
|
isupper=task%2==0;
|
|
isunit=task/2%2==0;
|
|
//--- Generate matrix
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
{
|
|
a[i].SetRe(i,1+CMath::RandomReal());
|
|
a[i].SetIm(i,1+CMath::RandomReal());
|
|
}
|
|
else
|
|
{
|
|
a[i].SetRe(j,0.2*CMath::RandomReal()-0.1);
|
|
a[i].SetIm(j,0.2*CMath::RandomReal()-0.1);
|
|
}
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//--- Inverse
|
|
CMatInv::CMatrixTrInverse(b,n,isupper,isunit,info,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
ctrerrors=true;
|
|
return;
|
|
}
|
|
//--- Structural test
|
|
if(isunit)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
ctrerrors=ctrerrors || a[i][i]!=b[i][i];
|
|
}
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=i-1;j++)
|
|
ctrerrors=ctrerrors || a[i][j]!=b[i][j];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i+1;j<=n-1;j++)
|
|
ctrerrors=ctrerrors || a[i][j]!=b[i][j];
|
|
}
|
|
}
|
|
//--- Inverse test
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if((j<i && isupper) || (j>i && !isupper))
|
|
{
|
|
a[i].Set(j,0);
|
|
b[i].Set(j,0);
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(isunit)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i].Set(i,1);
|
|
b[i].Set(i,1);
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*b[i_][j];
|
|
//--- check
|
|
if(j!=i)
|
|
ctrerrors=ctrerrors || CMath::AbsComplex(v)>threshold;
|
|
else
|
|
ctrerrors=ctrerrors || CMath::AbsComplex(v-1)>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Real test |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatInvUnit::TesTrInv(const int maxn,const int passcount,
|
|
const double threshold,bool &rerrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int pass=0;
|
|
int taskkind=0;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create array
|
|
int p[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble lua;
|
|
CMatrixDouble inva;
|
|
CMatrixDouble invlua;
|
|
//--- object of class
|
|
CMatInvReport rep;
|
|
//--- General square matrices:
|
|
//--- * test general solvers
|
|
//--- * test least squares solver
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- ********************************************************
|
|
//--- WELL CONDITIONED TASKS
|
|
//--- ability to find correct solution is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods on original A
|
|
CMatGen::RMatrixRndCond(n,1000,a);
|
|
RMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::RMatrixLU(lua,n,n,p);
|
|
RMatrixMakeACopy(a,n,n,inva);
|
|
RMatrixMakeACopy(lua,n,n,invlua);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::RMatrixInverse(inva,n,info,rep);
|
|
//--- search errors
|
|
rerrors=rerrors || !RMatrixCheckInverse(a,inva,n,threshold,info,rep);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::RMatrixLUInverse(invlua,p,n,info,rep);
|
|
//--- search errors
|
|
rerrors=rerrors || !RMatrixCheckInverse(a,invlua,n,threshold,info,rep);
|
|
//--- ********************************************************
|
|
//--- EXACTLY SINGULAR MATRICES
|
|
//--- ability to detect singularity is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate different types of singular matrices:
|
|
//--- * zero
|
|
//--- * with zero columns
|
|
//--- * with zero rows
|
|
//--- * with equal rows/columns
|
|
//--- 2. test different methods
|
|
for(taskkind=0;taskkind<=4;taskkind++)
|
|
{
|
|
Unset2D(a);
|
|
//--- check
|
|
if(taskkind==0)
|
|
{
|
|
//--- all zeros
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(taskkind==1)
|
|
{
|
|
//--- there is zero column
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(k,0*a[i_][k]);
|
|
}
|
|
//--- check
|
|
if(taskkind==2)
|
|
{
|
|
//--- there is zero row
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[k].Set(i_,0*a[k][i_]);
|
|
}
|
|
//--- check
|
|
if(taskkind==3)
|
|
{
|
|
//--- equal columns
|
|
if(n<2)
|
|
continue;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(0,a[i_][k]);
|
|
}
|
|
//--- check
|
|
if(taskkind==4)
|
|
{
|
|
//--- equal rows
|
|
if(n<2)
|
|
continue;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[0].Set(i_,a[k][i_]);
|
|
}
|
|
//--- function calls
|
|
RMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::RMatrixLU(lua,n,n,p);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::RMatrixInverse(a,n,info,rep);
|
|
//--- search errors
|
|
rerrors=rerrors || !RMatrixCheckInverseSingular(a,n,threshold,info,rep);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::RMatrixLUInverse(lua,p,n,info,rep);
|
|
//--- search errors
|
|
rerrors=rerrors || !RMatrixCheckInverseSingular(lua,n,threshold,info,rep);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Complex test |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatInvUnit::TestCInv(const int maxn,const int passcount,
|
|
const double threshold,bool &cerrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int pass=0;
|
|
int taskkind=0;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create array
|
|
int p[];
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex lua;
|
|
CMatrixComplex inva;
|
|
CMatrixComplex invlua;
|
|
//--- object of class
|
|
CMatInvReport rep;
|
|
//--- General square matrices:
|
|
//--- * test general solvers
|
|
//--- * test least squares solver
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- ********************************************************
|
|
//--- WELL CONDITIONED TASKS
|
|
//--- ability to find correct solution is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods on original A
|
|
CMatGen::CMatrixRndCond(n,1000,a);
|
|
CMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::CMatrixLU(lua,n,n,p);
|
|
CMatrixMakeACopy(a,n,n,inva);
|
|
CMatrixMakeACopy(lua,n,n,invlua);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::CMatrixInverse(inva,n,info,rep);
|
|
//--- search errors
|
|
cerrors=cerrors || !CMatrixCheckInverse(a,inva,n,threshold,info,rep);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::CMatrixLUInverse(invlua,p,n,info,rep);
|
|
//--- search errors
|
|
cerrors=cerrors || !CMatrixCheckInverse(a,invlua,n,threshold,info,rep);
|
|
//--- ********************************************************
|
|
//--- EXACTLY SINGULAR MATRICES
|
|
//--- ability to detect singularity is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate different types of singular matrices:
|
|
//--- * zero
|
|
//--- * with zero columns
|
|
//--- * with zero rows
|
|
//--- * with equal rows/columns
|
|
//--- 2. test different methods
|
|
for(taskkind=0;taskkind<=4;taskkind++)
|
|
{
|
|
CUnset2D(a);
|
|
//--- check
|
|
if(taskkind==0)
|
|
{
|
|
//--- all zeros
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(taskkind==1)
|
|
{
|
|
//--- there is zero column
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(k,a[i_][k]*0);
|
|
}
|
|
//--- check
|
|
if(taskkind==2)
|
|
{
|
|
//--- there is zero row
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[k].Set(i_,a[k][i_]*0);
|
|
}
|
|
//--- check
|
|
if(taskkind==3)
|
|
{
|
|
//--- equal columns
|
|
if(n<2)
|
|
continue;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(0,a[i_][k]);
|
|
}
|
|
//--- check
|
|
if(taskkind==4)
|
|
{
|
|
//--- equal rows
|
|
if(n<2)
|
|
continue;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change value
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[0].Set(i_,a[k][i_]);
|
|
}
|
|
//--- function calls
|
|
CMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::CMatrixLU(lua,n,n,p);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::CMatrixInverse(a,n,info,rep);
|
|
//--- search errors
|
|
cerrors=cerrors || !CMatrixCheckInverseSingular(a,n,threshold,info,rep);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::CMatrixLUInverse(lua,p,n,info,rep);
|
|
//--- search errors
|
|
cerrors=cerrors || !CMatrixCheckInverseSingular(lua,n,threshold,info,rep);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| SPD test |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatInvUnit::TestSPDInv(const int maxn,const int passcount,
|
|
const double threshold,bool &spderrors)
|
|
{
|
|
//--- create variables
|
|
bool isupper;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int pass=0;
|
|
int taskkind=0;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble cha;
|
|
CMatrixDouble inva;
|
|
CMatrixDouble invcha;
|
|
//--- object of class
|
|
CMatInvReport rep;
|
|
//--- General square matrices:
|
|
//--- * test general solvers
|
|
//--- * test least squares solver
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
isupper=CMath::RandomReal()>0.5;
|
|
//--- ********************************************************
|
|
//--- WELL CONDITIONED TASKS
|
|
//--- ability to find correct solution is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods on original A
|
|
CMatGen::SPDMatrixRndCond(n,1000,a);
|
|
RMatrixDropHalf(a,n,isupper);
|
|
RMatrixMakeACopy(a,n,n,cha);
|
|
//--- check
|
|
if(!CTrFac::SPDMatrixCholesky(cha,n,isupper))
|
|
continue;
|
|
//--- function calls
|
|
RMatrixMakeACopy(a,n,n,inva);
|
|
RMatrixMakeACopy(cha,n,n,invcha);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::SPDMatrixInverse(inva,n,isupper,info,rep);
|
|
//--- search errors
|
|
spderrors=spderrors || !SPDMatrixCheckInverse(a,inva,isupper,n,threshold,info,rep);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::SPDMatrixCholeskyInverse(invcha,n,isupper,info,rep);
|
|
//--- search errors
|
|
spderrors=spderrors || !SPDMatrixCheckInverse(a,invcha,isupper,n,threshold,info,rep);
|
|
//--- ********************************************************
|
|
//--- EXACTLY SINGULAR MATRICES
|
|
//--- ability to detect singularity is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate different types of singular matrices:
|
|
//--- * zero
|
|
//--- * with zero columns
|
|
//--- * with zero rows
|
|
//--- 2. test different methods
|
|
for(taskkind=0;taskkind<=2;taskkind++)
|
|
{
|
|
Unset2D(a);
|
|
//--- check
|
|
if(taskkind==0)
|
|
{
|
|
//--- all zeros
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(taskkind==1)
|
|
{
|
|
//--- there is zero column
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(k,0*a[i_][k]);
|
|
}
|
|
//--- check
|
|
if(taskkind==2)
|
|
{
|
|
//--- there is zero row
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[k].Set(i_,0*a[k][i_]);
|
|
}
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::SPDMatrixCholeskyInverse(a,n,isupper,info,rep);
|
|
//--- check
|
|
if(info!=-3 && info!=1)
|
|
spderrors=true;
|
|
else
|
|
{
|
|
spderrors=(spderrors || rep.m_r1<0.0) || rep.m_r1>1000*CMath::m_machineepsilon;
|
|
spderrors=(spderrors || rep.m_rinf<0.0) || rep.m_rinf>1000*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| HPD test |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatInvUnit::TestHPDInv(const int maxn,const int passcount,
|
|
const double threshold,bool &hpderrors)
|
|
{
|
|
//--- create variables
|
|
bool isupper;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int pass=0;
|
|
int taskkind=0;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex cha;
|
|
CMatrixComplex inva;
|
|
CMatrixComplex invcha;
|
|
//--- object of class
|
|
CMatInvReport rep;
|
|
//--- General square matrices:
|
|
//--- * test general solvers
|
|
//--- * test least squares solver
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
isupper=CMath::RandomReal()>0.5;
|
|
//--- ********************************************************
|
|
//--- WELL CONDITIONED TASKS
|
|
//--- ability to find correct solution is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods on original A
|
|
CMatGen::HPDMatrixRndCond(n,1000,a);
|
|
CMatrixDropHalf(a,n,isupper);
|
|
CMatrixMakeACopy(a,n,n,cha);
|
|
//--- check
|
|
if(!CTrFac::HPDMatrixCholesky(cha,n,isupper))
|
|
continue;
|
|
//--- function calls
|
|
CMatrixMakeACopy(a,n,n,inva);
|
|
CMatrixMakeACopy(cha,n,n,invcha);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::HPDMatrixInverse(inva,n,isupper,info,rep);
|
|
//--- search errors
|
|
hpderrors=hpderrors || !HPDMatrixCheckInverse(a,inva,isupper,n,threshold,info,rep);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::HPDMatrixCholeskyInverse(invcha,n,isupper,info,rep);
|
|
//--- search errors
|
|
hpderrors=hpderrors || !HPDMatrixCheckInverse(a,invcha,isupper,n,threshold,info,rep);
|
|
//--- ********************************************************
|
|
//--- EXACTLY SINGULAR MATRICES
|
|
//--- ability to detect singularity is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate different types of singular matrices:
|
|
//--- * zero
|
|
//--- * with zero columns
|
|
//--- * with zero rows
|
|
//--- 2. test different methods
|
|
for(taskkind=0;taskkind<=2;taskkind++)
|
|
{
|
|
CUnset2D(a);
|
|
//--- check
|
|
if(taskkind==0)
|
|
{
|
|
//--- all zeros
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(taskkind==1)
|
|
{
|
|
//--- there is zero column
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(k,a[i_][k]*0);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[k].Set(i_,a[k][i_]*0);
|
|
}
|
|
//--- check
|
|
if(taskkind==2)
|
|
{
|
|
//--- there is zero row
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[k].Set(i_,a[k][i_]*0);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(k,a[i_][k]*0);
|
|
}
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CMatInv::HPDMatrixCholeskyInverse(a,n,isupper,info,rep);
|
|
//--- check
|
|
if(info!=-3 && info!=1)
|
|
hpderrors=true;
|
|
else
|
|
{
|
|
hpderrors=(hpderrors || rep.m_r1<0.0) || rep.m_r1>1000*CMath::m_machineepsilon;
|
|
hpderrors=(hpderrors || rep.m_rinf<0.0) || rep.m_rinf>1000*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real matrix |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatInvUnit::Unset2D(CMatrixDouble &x)
|
|
{
|
|
//--- allocation
|
|
x.Resize(1,1);
|
|
//--- change value
|
|
x[0].Set(0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real matrix |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatInvUnit::Unset1D(double &x[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
//--- change value
|
|
x[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real matrix |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatInvUnit::CUnset2D(CMatrixComplex &x)
|
|
{
|
|
//--- allocation
|
|
x.Resize(1,1);
|
|
//--- change value
|
|
x[0].Set(0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real vector |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatInvUnit::CUnset1D(double &x[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
//--- change value
|
|
x[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets report |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMatInvUnit::UnsetRep(CMatInvReport &r)
|
|
{
|
|
//--- change values
|
|
r.m_r1=-1;
|
|
r.m_rinf=-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLDA |
|
|
//+------------------------------------------------------------------+
|
|
class CTestLDAUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void GenSimpleSet(const int nfeatures,const int nclasses,const int nsamples,const int axis,CMatrixDouble &xy);
|
|
static void GenDeg1Set(const int nfeatures,const int nclasses,const int nsamples,int axis,CMatrixDouble &xy);
|
|
static double GenerateNormal(const double mean,const double sigma);
|
|
static bool TestWN(CMatrixDouble &xy,CMatrixDouble &wn,const int ns,const int nf,const int nc,const int ndeg);
|
|
static double CalcJ(const int nf,CMatrixDouble &st,CMatrixDouble &sw,double &w[],double &p,double &q);
|
|
static void Fishers(CMatrixDouble &xy,int npoints,const int nfeatures,const int nclasses,CMatrixDouble &st,CMatrixDouble &sw);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestLDAUnit(void);
|
|
~CTestLDAUnit(void);
|
|
//--- public method
|
|
static bool TestLDA(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestLDAUnit::CTestLDAUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestLDAUnit::~CTestLDAUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLDA |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestLDAUnit::TestLDA(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int maxnf=0;
|
|
int maxns=0;
|
|
int maxnc=0;
|
|
int passcount=0;
|
|
bool ldanerrors;
|
|
bool lda1errors;
|
|
bool waserrors;
|
|
int nf=0;
|
|
int nc=0;
|
|
int ns=0;
|
|
int i=0;
|
|
int info=0;
|
|
int pass=0;
|
|
int axis=0;
|
|
//--- create array
|
|
double w1[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
CMatrixDouble wn;
|
|
//--- Primary settings
|
|
maxnf=10;
|
|
maxns=1000;
|
|
maxnc=5;
|
|
passcount=1;
|
|
waserrors=false;
|
|
ldanerrors=false;
|
|
lda1errors=false;
|
|
//--- General tests
|
|
for(nf=1;nf<=maxnf;nf++)
|
|
{
|
|
for(nc=2;nc<=maxnc;nc++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Simple test for LDA-N/LDA-1
|
|
axis=CMath::RandomInteger(nf);
|
|
ns=maxns/2+CMath::RandomInteger(maxns/2);
|
|
//--- function calls
|
|
GenSimpleSet(nf,nc,ns,axis,xy);
|
|
CLDA::FisherLDAN(xy,ns,nf,nc,info,wn);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
ldanerrors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
ldanerrors=ldanerrors || !TestWN(xy,wn,ns,nf,nc,0);
|
|
ldanerrors=ldanerrors || MathAbs(wn[axis][0])<=0.75;
|
|
//--- function call
|
|
CLDA::FisherLDA(xy,ns,nf,nc,info,w1);
|
|
//--- search errors
|
|
for(i=0;i<=nf-1;i++)
|
|
lda1errors=lda1errors || w1[i]!=wn[i][0];
|
|
//--- Degenerate test for LDA-N
|
|
if(nf>=3)
|
|
{
|
|
ns=maxns/2+CMath::RandomInteger(maxns/2);
|
|
//--- there are two duplicate features,
|
|
//--- axis is oriented along non-duplicate feature
|
|
axis=CMath::RandomInteger(nf-2);
|
|
GenDeg1Set(nf,nc,ns,axis,xy);
|
|
CLDA::FisherLDAN(xy,ns,nf,nc,info,wn);
|
|
//--- check
|
|
if(info!=2)
|
|
{
|
|
ldanerrors=true;
|
|
continue;
|
|
}
|
|
//--- function calls
|
|
ldanerrors=ldanerrors || wn[axis][0]<=0.75;
|
|
CLDA::FisherLDA(xy,ns,nf,nc,info,w1);
|
|
//--- search errors
|
|
for(i=0;i<=nf-1;i++)
|
|
lda1errors=lda1errors || w1[i]!=wn[i][0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Final report
|
|
waserrors=ldanerrors || lda1errors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("LDA TEST");
|
|
Print("FISHER LDA-N: ");
|
|
//--- check
|
|
if(!ldanerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("FISHER LDA-1: ");
|
|
//--- check
|
|
if(!lda1errors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST SUMMARY: FAILED");
|
|
else
|
|
Print("TEST SUMMARY: PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generates 'simple' set - a sequence of unit 'balls' at |
|
|
//| (0,0),(1,0),(2,0) and so on. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestLDAUnit::GenSimpleSet(const int nfeatures,const int nclasses,
|
|
const int nsamples,const int axis,
|
|
CMatrixDouble &xy)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int c=0;
|
|
//--- check
|
|
if(!CAp::Assert(axis>=0 && axis<nfeatures,"GenSimpleSet: wrong Axis!"))
|
|
return;
|
|
//--- allocation
|
|
xy.Resize(nsamples,nfeatures+1);
|
|
//--- calculation
|
|
for(i=0;i<=nsamples-1;i++)
|
|
{
|
|
for(j=0;j<=nfeatures-1;j++)
|
|
xy[i].Set(j,GenerateNormal(0.0,1.0));
|
|
//--- change values
|
|
c=i%nclasses;
|
|
xy[i].Set(axis,xy[i][axis]+c);
|
|
xy[i].Set(nfeatures,c);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generates 'degenerate' set #1. |
|
|
//| NFeatures>=3. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestLDAUnit::GenDeg1Set(const int nfeatures,const int nclasses,
|
|
const int nsamples,int axis,
|
|
CMatrixDouble &xy)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int c=0;
|
|
//--- check
|
|
if(!CAp::Assert(axis>=0 && axis<nfeatures,"GenDeg1Set: wrong Axis!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(nfeatures>=3,"GenDeg1Set: wrong NFeatures!"))
|
|
return;
|
|
//--- allocation
|
|
xy.Resize(nsamples,nfeatures+1);
|
|
//--- check
|
|
if(axis>=nfeatures-2)
|
|
axis=nfeatures-3;
|
|
//--- calculation
|
|
for(i=0;i<=nsamples-1;i++)
|
|
{
|
|
for(j=0;j<=nfeatures-2;j++)
|
|
xy[i].Set(j,GenerateNormal(0.0,1.0));
|
|
//--- change values
|
|
xy[i].Set(nfeatures-1,xy[i][nfeatures-2]);
|
|
c=i%nclasses;
|
|
xy[i].Set(axis,xy[i][axis]+c);
|
|
xy[i].Set(nfeatures,c);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Normal random number |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestLDAUnit::GenerateNormal(const double mean,const double sigma)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double u=0;
|
|
double v=0;
|
|
double sum=0;
|
|
//--- initialization
|
|
result=mean;
|
|
//--- calculation
|
|
while(true)
|
|
{
|
|
//--- change values
|
|
u=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal();
|
|
v=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal();
|
|
sum=u*u+v*v;
|
|
//--- check
|
|
if(sum<1.0 && sum>0.0)
|
|
{
|
|
sum=MathSqrt(-(2*MathLog(sum)/sum));
|
|
result=sigma*u*sum+mean;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests WN for correctness |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestLDAUnit::TestWN(CMatrixDouble &xy,CMatrixDouble &wn,
|
|
const int ns,const int nf,
|
|
const int nc,const int ndeg)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double wprev=0;
|
|
double tol=0;
|
|
double p=0;
|
|
double q=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double tx[];
|
|
double jp[];
|
|
double jq[];
|
|
double work[];
|
|
//--- create matrix
|
|
CMatrixDouble st;
|
|
CMatrixDouble sw;
|
|
CMatrixDouble a;
|
|
CMatrixDouble z;
|
|
//--- initialization
|
|
tol=10000;
|
|
result=true;
|
|
Fishers(xy,ns,nf,nc,st,sw);
|
|
//--- Test for decreasing of J
|
|
ArrayResize(tx,nf);
|
|
ArrayResize(jp,nf);
|
|
ArrayResize(jq,nf);
|
|
//--- calculation
|
|
for(j=0;j<=nf-1;j++)
|
|
{
|
|
for(i_=0;i_<=nf-1;i_++)
|
|
tx[i_]=wn[i_][j];
|
|
v=CalcJ(nf,st,sw,tx,p,q);
|
|
jp[j]=p;
|
|
jq[j]=q;
|
|
}
|
|
//--- calculation
|
|
for(i=1;i<=nf-1-ndeg;i++)
|
|
result=result && jp[i-1]/jq[i-1]>=(1-tol*CMath::m_machineepsilon)*jp[i]/jq[i];
|
|
for(i=nf-1-ndeg+1;i<=nf-1;i++)
|
|
result=result && jp[i]<=tol*CMath::m_machineepsilon*jp[0];
|
|
//--- Test for J optimality
|
|
for(i_=0;i_<=nf-1;i_++)
|
|
tx[i_]=wn[i_][0];
|
|
v=CalcJ(nf,st,sw,tx,p,q);
|
|
//--- calculation
|
|
for(i=0;i<=nf-1;i++)
|
|
{
|
|
wprev=tx[i];
|
|
tx[i]=wprev+0.01;
|
|
result=result && v>=(1-tol*CMath::m_machineepsilon)*CalcJ(nf,st,sw,tx,p,q);
|
|
tx[i]=wprev-0.01;
|
|
result=result && v>=(1-tol*CMath::m_machineepsilon)*CalcJ(nf,st,sw,tx,p,q);
|
|
tx[i]=wprev;
|
|
}
|
|
//--- Test for linear independence of W
|
|
ArrayResize(work,nf+1);
|
|
a.Resize(nf,nf);
|
|
//--- function call
|
|
CBlas::MatrixMatrixMultiply(wn,0,nf-1,0,nf-1,false,wn,0,nf-1,0,nf-1,true,1.0,a,0,nf-1,0,nf-1,0.0,work);
|
|
//--- check
|
|
if(CEigenVDetect::SMatrixEVD(a,nf,1,true,tx,z))
|
|
result=result && tx[0]>tx[nf-1]*1000*CMath::m_machineepsilon;
|
|
//--- Test for other properties
|
|
for(j=0;j<=nf-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=nf-1;i_++)
|
|
v+=wn[i_][j]*wn[i_][j];
|
|
//--- change values
|
|
v=MathSqrt(v);
|
|
result=result && MathAbs(v-1)<=1000*CMath::m_machineepsilon;
|
|
v=0;
|
|
for(i=0;i<=nf-1;i++)
|
|
v=v+wn[i][j];
|
|
result=result && v>=0.0;
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculates J |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestLDAUnit::CalcJ(const int nf,CMatrixDouble &st,
|
|
CMatrixDouble &sw,double &w[],
|
|
double &p,double &q)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double tx[];
|
|
//--- initialization
|
|
p=0;
|
|
q=0;
|
|
//--- allocation
|
|
ArrayResize(tx,nf);
|
|
//--- calculation
|
|
for(i=0;i<=nf-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=nf-1;i_++)
|
|
v+=st[i][i_]*w[i_];
|
|
tx[i]=v;
|
|
}
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=nf-1;i_++)
|
|
v+=w[i_]*tx[i_];
|
|
p=v;
|
|
for(i=0;i<=nf-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=nf-1;i_++)
|
|
v+=sw[i][i_]*w[i_];
|
|
tx[i]=v;
|
|
}
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=nf-1;i_++)
|
|
v+=w[i_]*tx[i_];
|
|
q=v;
|
|
result=p/q;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculates ST/SW |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestLDAUnit::Fishers(CMatrixDouble &xy,int npoints,
|
|
const int nfeatures,const int nclasses,
|
|
CMatrixDouble &st,CMatrixDouble &sw)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
int c[];
|
|
double mu[];
|
|
int nc[];
|
|
double tf[];
|
|
double work[];
|
|
//--- create matrix
|
|
CMatrixDouble muc;
|
|
//--- Prepare temporaries
|
|
ArrayResize(tf,nfeatures);
|
|
ArrayResize(work,nfeatures+1);
|
|
//--- Convert class labels from reals to integers (just for convenience)
|
|
ArrayResize(c,npoints);
|
|
for(i=0;i<=npoints-1;i++)
|
|
c[i]=(int)MathRound(xy[i][nfeatures]);
|
|
//--- Calculate class sizes and means
|
|
ArrayResize(mu,nfeatures);
|
|
muc.Resize(nclasses,nfeatures);
|
|
ArrayResize(nc,nclasses);
|
|
//--- change values
|
|
for(j=0;j<=nfeatures-1;j++)
|
|
mu[j]=0;
|
|
for(i=0;i<=nclasses-1;i++)
|
|
{
|
|
nc[i]=0;
|
|
for(j=0;j<=nfeatures-1;j++)
|
|
muc[i].Set(j,0);
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
for(i_=0;i_<=nfeatures-1;i_++)
|
|
mu[i_]=mu[i_]+xy[i][i_];
|
|
for(i_=0;i_<=nfeatures-1;i_++)
|
|
muc[c[i]].Set(i_,muc[c[i]][i_]+xy[i][i_]);
|
|
nc[c[i]]=nc[c[i]]+1;
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=nclasses-1;i++)
|
|
{
|
|
v=1.0/(double)nc[i];
|
|
for(i_=0;i_<=nfeatures-1;i_++)
|
|
muc[i].Set(i_,v*muc[i][i_]);
|
|
}
|
|
//--- change values
|
|
v=1.0/(double)npoints;
|
|
for(i_=0;i_<=nfeatures-1;i_++)
|
|
mu[i_]=v*mu[i_];
|
|
//--- Create ST matrix
|
|
st.Resize(nfeatures,nfeatures);
|
|
//--- change values
|
|
for(i=0;i<=nfeatures-1;i++)
|
|
{
|
|
for(j=0;j<=nfeatures-1;j++)
|
|
st[i].Set(j,0);
|
|
}
|
|
//--- calculation
|
|
for(k=0;k<=npoints-1;k++)
|
|
{
|
|
for(i_=0;i_<=nfeatures-1;i_++)
|
|
tf[i_]=xy[k][i_];
|
|
for(i_=0;i_<=nfeatures-1;i_++)
|
|
tf[i_]=tf[i_]-mu[i_];
|
|
//--- calculation
|
|
for(i=0;i<=nfeatures-1;i++)
|
|
{
|
|
v=tf[i];
|
|
for(i_=0;i_<=nfeatures-1;i_++)
|
|
st[i].Set(i_,st[i][i_]+v*tf[i_]);
|
|
}
|
|
}
|
|
//--- Create SW matrix
|
|
sw.Resize(nfeatures,nfeatures);
|
|
for(i=0;i<=nfeatures-1;i++)
|
|
{
|
|
for(j=0;j<=nfeatures-1;j++)
|
|
sw[i].Set(j,0);
|
|
}
|
|
//--- calculation
|
|
for(k=0;k<=npoints-1;k++)
|
|
{
|
|
for(i_=0;i_<=nfeatures-1;i_++)
|
|
tf[i_]=xy[k][i_];
|
|
for(i_=0;i_<=nfeatures-1;i_++)
|
|
tf[i_]=tf[i_]-muc[c[k]][i_];
|
|
//--- calculation
|
|
for(i=0;i<=nfeatures-1;i++)
|
|
{
|
|
v=tf[i];
|
|
for(i_=0;i_<=nfeatures-1;i_++)
|
|
sw[i].Set(i_,sw[i][i_]+v*tf[i_]);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CGammaFunc |
|
|
//+------------------------------------------------------------------+
|
|
class CTestGammaFuncUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestGammaFuncUnit(void);
|
|
~CTestGammaFuncUnit(void);
|
|
//--- public method
|
|
static bool TestGammaFunc(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestGammaFuncUnit::CTestGammaFuncUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestGammaFuncUnit::~CTestGammaFuncUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CGammaFunc |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestGammaFuncUnit::TestGammaFunc(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double threshold=0;
|
|
double v=0;
|
|
double s=0;
|
|
bool waserrors;
|
|
bool gammaerrors;
|
|
bool lngammaerrors;
|
|
//--- initialization
|
|
gammaerrors=false;
|
|
lngammaerrors=false;
|
|
waserrors=false;
|
|
threshold=100*CMath::m_machineepsilon;
|
|
//--- search errors
|
|
gammaerrors=gammaerrors || MathAbs(CGammaFunc::GammaFunc(0.5)-MathSqrt(M_PI))>threshold;
|
|
gammaerrors=gammaerrors || MathAbs(CGammaFunc::GammaFunc(1.5)-0.5*MathSqrt(M_PI))>threshold;
|
|
//--- function call
|
|
v=CGammaFunc::LnGamma(0.5,s);
|
|
//--- search errors
|
|
lngammaerrors=(lngammaerrors || MathAbs(v-MathLog(MathSqrt(M_PI)))>threshold) || s!=1.0;
|
|
//--- function call
|
|
v=CGammaFunc::LnGamma(1.5,s);
|
|
//--- search errors
|
|
lngammaerrors=(lngammaerrors || MathAbs(v-MathLog(0.5*MathSqrt(M_PI)))>threshold) || s!=1.0;
|
|
//--- report
|
|
waserrors=gammaerrors || lngammaerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING GAMMA FUNCTION");
|
|
Print("GAMMA: ");
|
|
//--- check
|
|
if(gammaerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("LN GAMMA: ");
|
|
//--- check
|
|
if(lngammaerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- end
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CBdSingValueDecompose |
|
|
//+------------------------------------------------------------------+
|
|
class CTestBdSVDUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void FillIdentity(CMatrixDouble &a,const int n);
|
|
static void FillSparseDE(double &d[],double &e[],const int n,const double sparcity);
|
|
static void GetBdSVDError(double &d[],double &e[],const int n,const bool isupper,CMatrixDouble &u,CMatrixDouble &c,double &w[],CMatrixDouble &vt,double &materr,double &orterr,bool &wsorted);
|
|
static void TestBdSVDProblem(double &d[],double &e[],const int n,double &materr,double &orterr,bool &wsorted,bool &wfailed,int &failcount,int &succcount);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestBdSVDUnit(void);
|
|
~CTestBdSVDUnit(void);
|
|
//--- public method
|
|
static bool TestBdSVD(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestBdSVDUnit::CTestBdSVDUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestBdSVDUnit::~CTestBdSVDUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing bidiagonal SVD decomposition subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestBdSVDUnit::TestBdSVD(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int maxn=0;
|
|
int i=0;
|
|
int pass=0;
|
|
bool waserrors;
|
|
bool wsorted;
|
|
bool wfailed;
|
|
bool failcase;
|
|
double materr=0;
|
|
double orterr=0;
|
|
double threshold=0;
|
|
double failthreshold=0;
|
|
double failr=0;
|
|
int failcount=0;
|
|
int succcount=0;
|
|
//--- create arrays
|
|
double d[];
|
|
double e[];
|
|
//--- create matrix
|
|
CMatrixDouble mempty;
|
|
//--- initialization
|
|
failcount=0;
|
|
succcount=0;
|
|
materr=0;
|
|
orterr=0;
|
|
wsorted=true;
|
|
wfailed=false;
|
|
waserrors=false;
|
|
maxn=15;
|
|
threshold=5*100*CMath::m_machineepsilon;
|
|
failthreshold=1.0E-2;
|
|
//--- allocation
|
|
ArrayResize(d,maxn);
|
|
ArrayResize(e,maxn-1);
|
|
//--- special case: fail matrix
|
|
n=5;
|
|
d[0]=-8.27448347422711894000e-01;
|
|
d[1]=-8.16705832087160854600e-01;
|
|
d[2]=-2.53974358904729382800e-17;
|
|
d[3]=-1.24626684881972815700e+00;
|
|
d[4]=-4.64744131545637651000e-01;
|
|
e[0]=-3.25785088656270038800e-01;
|
|
e[1]=-1.03732413708914436580e-01;
|
|
e[2]=-9.57365642262031357700e-02;
|
|
e[3]=-2.71564153973817390400e-01;
|
|
failcase=CBdSingValueDecompose::RMatrixBdSVD(d,e,n,true,false,mempty,0,mempty,0,mempty,0);
|
|
//--- special case: zero divide matrix
|
|
//--- unfixed LAPACK routine should fail on this problem
|
|
n=7;
|
|
d[0]=-6.96462904751731892700e-01;
|
|
d[1]=0.00000000000000000000e+00;
|
|
d[2]=-5.73827770385971991400e-01;
|
|
d[3]=-6.62562624399371191700e-01;
|
|
d[4]=5.82737148001782223600e-01;
|
|
d[5]=3.84825263580925003300e-01;
|
|
d[6]=9.84087420830525472200e-01;
|
|
e[0]=-7.30307931760612871800e-02;
|
|
e[1]=-2.30079042939542843800e-01;
|
|
e[2]=-6.87824621739351216300e-01;
|
|
e[3]=-1.77306437707837570600e-02;
|
|
e[4]=1.78285126526551632000e-15;
|
|
e[5]=-4.89434737751289969400e-02;
|
|
CBdSingValueDecompose::RMatrixBdSVD(d,e,n,true,false,mempty,0,mempty,0,mempty,0);
|
|
//--- zero matrix,several cases
|
|
for(i=0;i<=maxn-1;i++)
|
|
d[i]=0;
|
|
for(i=0;i<=maxn-2;i++)
|
|
e[i]=0;
|
|
for(n=1;n<=maxn;n++)
|
|
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
|
|
//--- Dense matrix
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
for(pass=1;pass<=10;pass++)
|
|
{
|
|
for(i=0;i<=maxn-1;i++)
|
|
d[i]=2*CMath::RandomReal()-1;
|
|
for(i=0;i<=maxn-2;i++)
|
|
e[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
|
|
}
|
|
}
|
|
//--- Sparse matrices,very sparse matrices,incredible sparse matrices
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
for(pass=1;pass<=10;pass++)
|
|
{
|
|
//--- function calls
|
|
FillSparseDE(d,e,n,0.5);
|
|
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
|
|
FillSparseDE(d,e,n,0.8);
|
|
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
|
|
FillSparseDE(d,e,n,0.9);
|
|
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
|
|
FillSparseDE(d,e,n,0.95);
|
|
TestBdSVDProblem(d,e,n,materr,orterr,wsorted,wfailed,failcount,succcount);
|
|
}
|
|
}
|
|
//--- report
|
|
failr=(double)failcount/(double)(succcount+failcount);
|
|
waserrors=((materr>threshold || orterr>threshold) || !wsorted) || failr>failthreshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING BIDIAGONAL SVD DECOMPOSITION");
|
|
Print("SVD decomposition error: ");
|
|
Print("{0,5:E3}",materr);
|
|
Print("SVD orthogonality error: ");
|
|
Print("{0,5:E3}",orterr);
|
|
Print("Singular values order: ");
|
|
//--- check
|
|
if(wsorted)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("Always converged: ");
|
|
//--- check
|
|
if(!wfailed)
|
|
Print("YES");
|
|
else
|
|
{
|
|
Print("NO");
|
|
Print("Fail ratio: ");
|
|
Print("{0,5:F3}",failr);
|
|
}
|
|
Print("Fail matrix test: ");
|
|
//--- check
|
|
if(!failcase)
|
|
Print("AS EXPECTED");
|
|
else
|
|
Print("CONVERGED (UNEXPECTED)");
|
|
Print("Threshold: ");
|
|
Print("{0,5:E3}",threshold);
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestBdSVDUnit::FillIdentity(CMatrixDouble &a,const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
a[i].Set(j,1);
|
|
else
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestBdSVDUnit::FillSparseDE(double &d[],double &e[],
|
|
const int n,const double sparcity)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- allocation
|
|
ArrayResize(d,n);
|
|
ArrayResize(e,(int)(MathMax(0,n-2))+1);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>=sparcity)
|
|
d[i]=2*CMath::RandomReal()-1;
|
|
else
|
|
d[i]=0;
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>=sparcity)
|
|
e[i]=2*CMath::RandomReal()-1;
|
|
else
|
|
e[i]=0;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestBdSVDUnit::GetBdSVDError(double &d[],double &e[],
|
|
const int n,const bool isupper,
|
|
CMatrixDouble &u,CMatrixDouble &c,
|
|
double &w[],CMatrixDouble &vt,
|
|
double &materr,double &orterr,
|
|
bool &wsorted)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double locerr=0;
|
|
double sm=0;
|
|
int i_=0;
|
|
//--- decomposition error
|
|
locerr=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
sm=0;
|
|
for(k=0;k<=n-1;k++)
|
|
sm=sm+w[k]*u[i][k]*vt[k][j];
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
locerr=MathMax(locerr,MathAbs(d[i]-sm));
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==j-1)
|
|
locerr=MathMax(locerr,MathAbs(e[i]-sm));
|
|
else
|
|
locerr=MathMax(locerr,MathAbs(sm));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
locerr=MathMax(locerr,MathAbs(d[i]-sm));
|
|
else
|
|
{
|
|
//--- check
|
|
if(i-1==j)
|
|
locerr=MathMax(locerr,MathAbs(e[j]-sm));
|
|
else
|
|
locerr=MathMax(locerr,MathAbs(sm));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- change value
|
|
materr=MathMax(materr,locerr);
|
|
//--- check for C=U'
|
|
//--- we consider it as decomposition error
|
|
locerr=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
locerr=MathMax(locerr,MathAbs(u[i][j]-c[j][i]));
|
|
}
|
|
materr=MathMax(materr,locerr);
|
|
//--- orthogonality error
|
|
locerr=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
sm=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
sm+=u[i_][i]*u[i_][j];
|
|
//--- check
|
|
if(i!=j)
|
|
locerr=MathMax(locerr,MathAbs(sm));
|
|
else
|
|
locerr=MathMax(locerr,MathAbs(sm-1));
|
|
//--- change value
|
|
sm=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
sm+=vt[i][i_]*vt[j][i_];
|
|
//--- check
|
|
if(i!=j)
|
|
locerr=MathMax(locerr,MathAbs(sm));
|
|
else
|
|
locerr=MathMax(locerr,MathAbs(sm-1));
|
|
}
|
|
}
|
|
orterr=MathMax(orterr,locerr);
|
|
//--- values order error
|
|
for(i=1;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(w[i]>w[i-1])
|
|
wsorted=false;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestBdSVDUnit::TestBdSVDProblem(double &d[],double &e[],
|
|
const int n,double &materr,
|
|
double &orterr,bool &wsorted,
|
|
bool &wfailed,int &failcount,
|
|
int &succcount)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double mx=0;
|
|
//--- create array
|
|
double w[];
|
|
//--- create matrix
|
|
CMatrixDouble u;
|
|
CMatrixDouble vt;
|
|
CMatrixDouble c;
|
|
//--- change value
|
|
mx=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(d[i])>mx)
|
|
mx=MathAbs(d[i]);
|
|
}
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(e[i])>mx)
|
|
mx=MathAbs(e[i]);
|
|
}
|
|
//--- check
|
|
if(mx==0.0)
|
|
mx=1;
|
|
//--- Upper BDSVD tests
|
|
ArrayResize(w,n);
|
|
FillIdentity(u,n);
|
|
FillIdentity(vt,n);
|
|
FillIdentity(c,n);
|
|
for(i=0;i<=n-1;i++)
|
|
w[i]=d[i];
|
|
//--- check
|
|
if(!CBdSingValueDecompose::RMatrixBdSVD(w,e,n,true,false,u,n,c,n,vt,n))
|
|
{
|
|
failcount=failcount+1;
|
|
wfailed=true;
|
|
return;
|
|
}
|
|
//--- function calls
|
|
GetBdSVDError(d,e,n,true,u,c,w,vt,materr,orterr,wsorted);
|
|
FillIdentity(u,n);
|
|
FillIdentity(vt,n);
|
|
FillIdentity(c,n);
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
w[i]=d[i];
|
|
//--- check
|
|
if(!CBdSingValueDecompose::RMatrixBdSVD(w,e,n,true,true,u,n,c,n,vt,n))
|
|
{
|
|
failcount=failcount+1;
|
|
wfailed=true;
|
|
return;
|
|
}
|
|
//--- function call
|
|
GetBdSVDError(d,e,n,true,u,c,w,vt,materr,orterr,wsorted);
|
|
//--- Lower BDSVD tests
|
|
ArrayResize(w,n);
|
|
FillIdentity(u,n);
|
|
FillIdentity(vt,n);
|
|
FillIdentity(c,n);
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
w[i]=d[i];
|
|
//--- check
|
|
if(!CBdSingValueDecompose::RMatrixBdSVD(w,e,n,false,false,u,n,c,n,vt,n))
|
|
{
|
|
failcount=failcount+1;
|
|
wfailed=true;
|
|
return;
|
|
}
|
|
//--- function calls
|
|
GetBdSVDError(d,e,n,false,u,c,w,vt,materr,orterr,wsorted);
|
|
FillIdentity(u,n);
|
|
FillIdentity(vt,n);
|
|
FillIdentity(c,n);
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
w[i]=d[i];
|
|
//--- check
|
|
if(!CBdSingValueDecompose::RMatrixBdSVD(w,e,n,false,true,u,n,c,n,vt,n))
|
|
{
|
|
failcount=failcount+1;
|
|
wfailed=true;
|
|
return;
|
|
}
|
|
//--- function call
|
|
GetBdSVDError(d,e,n,false,u,c,w,vt,materr,orterr,wsorted);
|
|
//--- update counter
|
|
succcount=succcount+1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSingValueDecompose |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSVDUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void FillsParseA(CMatrixDouble &a,const int m,const int n,const double sparcity);
|
|
static void GetSVDError(CMatrixDouble &a,const int m,const int n,CMatrixDouble &u,double &w[],CMatrixDouble &vt,double &materr,double &orterr,bool &wsorted);
|
|
static void TestSVDProblem(CMatrixDouble &a,const int m,const int n,double &materr,double &orterr,double &othererr,bool &wsorted,bool &wfailed,int &failcount,int &succcount);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestSVDUnit(void);
|
|
~CTestSVDUnit(void);
|
|
//--- public method
|
|
static bool TestSVD(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestSVDUnit::CTestSVDUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestSVDUnit::~CTestSVDUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing SVD decomposition subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestSVDUnit::TestSVD(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int m=0;
|
|
int n=0;
|
|
int maxmn=0;
|
|
int i=0;
|
|
int j=0;
|
|
int gpass=0;
|
|
int pass=0;
|
|
bool waserrors;
|
|
bool wsorted;
|
|
bool wfailed;
|
|
double materr=0;
|
|
double orterr=0;
|
|
double othererr=0;
|
|
double threshold=0;
|
|
double failthreshold=0;
|
|
double failr=0;
|
|
int failcount=0;
|
|
int succcount=0;
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- initialization
|
|
failcount=0;
|
|
succcount=0;
|
|
materr=0;
|
|
orterr=0;
|
|
othererr=0;
|
|
wsorted=true;
|
|
wfailed=false;
|
|
waserrors=false;
|
|
maxmn=30;
|
|
threshold=5*100*CMath::m_machineepsilon;
|
|
failthreshold=5.0E-3;
|
|
//--- allocation
|
|
a.Resize(maxmn,maxmn);
|
|
//--- TODO: div by zero fail,convergence fail
|
|
for(gpass=1;gpass<=1;gpass++)
|
|
{
|
|
//--- zero matrix,several cases
|
|
for(i=0;i<=maxmn-1;i++)
|
|
{
|
|
for(j=0;j<=maxmn-1;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- function calls
|
|
for(i=1;i<=MathMin(5,maxmn);i++)
|
|
{
|
|
for(j=1;j<=MathMin(5,maxmn);j++)
|
|
TestSVDProblem(a,i,j,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
|
|
}
|
|
//--- Long dense matrix
|
|
for(i=0;i<=maxmn-1;i++)
|
|
{
|
|
for(j=0;j<=MathMin(5,maxmn)-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function calls
|
|
for(i=1;i<=maxmn;i++)
|
|
{
|
|
for(j=1;j<=MathMin(5,maxmn);j++)
|
|
TestSVDProblem(a,i,j,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=MathMin(5,maxmn)-1;i++)
|
|
{
|
|
for(j=0;j<=maxmn-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function calls
|
|
for(i=1;i<=MathMin(5,maxmn);i++)
|
|
{
|
|
for(j=1;j<=maxmn;j++)
|
|
TestSVDProblem(a,i,j,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
|
|
}
|
|
//--- Dense matrices
|
|
for(m=1;m<=MathMin(10,maxmn);m++)
|
|
{
|
|
for(n=1;n<=MathMin(10,maxmn);n++)
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function call
|
|
TestSVDProblem(a,m,n,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
|
|
}
|
|
}
|
|
//--- Sparse matrices,very sparse matrices,incredible sparse matrices
|
|
for(m=1;m<=10;m++)
|
|
{
|
|
for(n=1;n<=10;n++)
|
|
{
|
|
for(pass=1;pass<=2;pass++)
|
|
{
|
|
//--- function calls
|
|
FillsParseA(a,m,n,0.8);
|
|
TestSVDProblem(a,m,n,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
|
|
FillsParseA(a,m,n,0.9);
|
|
TestSVDProblem(a,m,n,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
|
|
FillsParseA(a,m,n,0.95);
|
|
TestSVDProblem(a,m,n,materr,orterr,othererr,wsorted,wfailed,failcount,succcount);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
failr=(double)failcount/(double)(succcount+failcount);
|
|
waserrors=(((materr>threshold || orterr>threshold) || othererr>threshold) || !wsorted) || failr>failthreshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING SVD DECOMPOSITION");
|
|
Print("SVD decomposition error: ");
|
|
Print("{0,5:E3}",materr);
|
|
Print("SVD orthogonality error: ");
|
|
Print("{0,5:E3}",orterr);
|
|
Print("SVD with different parameters error: ");
|
|
Print("{0,5:E3}",othererr);
|
|
Print("Singular values order: ");
|
|
//--- check
|
|
if(wsorted)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("Always converged: ");
|
|
//--- check
|
|
if(!wfailed)
|
|
Print("YES");
|
|
else
|
|
{
|
|
Print("NO");
|
|
Print("Fail ratio: ");
|
|
Print("{0,5:F3}",failr);
|
|
}
|
|
Print("Threshold: ");
|
|
Print("{0,5:E3}",threshold);
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestSVDUnit::FillsParseA(CMatrixDouble &a,const int m,
|
|
const int n,const double sparcity)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>=sparcity)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
else
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestSVDUnit::GetSVDError(CMatrixDouble &a,const int m,
|
|
const int n,CMatrixDouble &u,
|
|
double &w[],CMatrixDouble &vt,
|
|
double &materr,double &orterr,
|
|
bool &wsorted)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int minmn=0;
|
|
double locerr=0;
|
|
double sm=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
minmn=MathMin(m,n);
|
|
//--- decomposition error
|
|
locerr=0;
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
sm=0;
|
|
for(k=0;k<=minmn-1;k++)
|
|
sm=sm+w[k]*u[i][k]*vt[k][j];
|
|
locerr=MathMax(locerr,MathAbs(a[i][j]-sm));
|
|
}
|
|
}
|
|
materr=MathMax(materr,locerr);
|
|
//--- orthogonality error
|
|
locerr=0;
|
|
for(i=0;i<=minmn-1;i++)
|
|
{
|
|
for(j=i;j<=minmn-1;j++)
|
|
{
|
|
//--- change value
|
|
sm=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
sm+=u[i_][i]*u[i_][j];
|
|
//--- check
|
|
if(i!=j)
|
|
locerr=MathMax(locerr,MathAbs(sm));
|
|
else
|
|
locerr=MathMax(locerr,MathAbs(sm-1));
|
|
//--- change value
|
|
sm=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
sm+=vt[i][i_]*vt[j][i_];
|
|
//--- check
|
|
if(i!=j)
|
|
locerr=MathMax(locerr,MathAbs(sm));
|
|
else
|
|
locerr=MathMax(locerr,MathAbs(sm-1));
|
|
}
|
|
}
|
|
orterr=MathMax(orterr,locerr);
|
|
//--- values order error
|
|
for(i=1;i<=minmn-1;i++)
|
|
{
|
|
//--- check
|
|
if(w[i]>w[i-1])
|
|
wsorted=false;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestSVDUnit::TestSVDProblem(CMatrixDouble &a,const int m,
|
|
const int n,double &materr,
|
|
double &orterr,double &othererr,
|
|
bool &wsorted,bool &wfailed,
|
|
int &failcount,int &succcount)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int ujob=0;
|
|
int vtjob=0;
|
|
int memjob=0;
|
|
int ucheck=0;
|
|
int vtcheck=0;
|
|
//--- create arrays
|
|
double w[];
|
|
double w2[];
|
|
//--- create matrix
|
|
CMatrixDouble u;
|
|
CMatrixDouble vt;
|
|
CMatrixDouble u2;
|
|
CMatrixDouble vt2;
|
|
//--- Main SVD test
|
|
if(!CSingValueDecompose::RMatrixSVD(a,m,n,2,2,2,w,u,vt))
|
|
{
|
|
failcount=failcount+1;
|
|
wfailed=true;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- function call
|
|
GetSVDError(a,m,n,u,w,vt,materr,orterr,wsorted);
|
|
//--- Additional SVD tests
|
|
for(ujob=0;ujob<=2;ujob++)
|
|
{
|
|
for(vtjob=0;vtjob<=2;vtjob++)
|
|
{
|
|
for(memjob=0;memjob<=2;memjob++)
|
|
{
|
|
//--- check
|
|
if(!CSingValueDecompose::RMatrixSVD(a,m,n,ujob,vtjob,memjob,w2,u2,vt2))
|
|
{
|
|
failcount=failcount+1;
|
|
wfailed=true;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- change value
|
|
ucheck=0;
|
|
//--- check
|
|
if(ujob==1)
|
|
ucheck=MathMin(m,n);
|
|
//--- check
|
|
if(ujob==2)
|
|
ucheck=m;
|
|
//--- change value
|
|
vtcheck=0;
|
|
//--- check
|
|
if(vtjob==1)
|
|
vtcheck=MathMin(m,n);
|
|
//--- check
|
|
if(vtjob==2)
|
|
vtcheck=n;
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=ucheck-1;j++)
|
|
othererr=MathMax(othererr,MathAbs(u[i][j]-u2[i][j]));
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=vtcheck-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
othererr=MathMax(othererr,MathAbs(vt[i][j]-vt2[i][j]));
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=MathMin(m,n)-1;i++)
|
|
othererr=MathMax(othererr,MathAbs(w[i]-w2[i]));
|
|
}
|
|
}
|
|
}
|
|
//--- update counter
|
|
succcount=succcount+1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLinReg |
|
|
//+------------------------------------------------------------------+
|
|
class CTestLinRegUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestLinRegUnit(void);
|
|
~CTestLinRegUnit(void);
|
|
//--- public methods
|
|
static bool TestLinReg(const bool silent);
|
|
static void GenerateRandomTask(const double xl,const double xr,const bool randomx,const double ymin,const double ymax,const double smin,const double smax,const int n,CMatrixDouble &xy,double &s[]);
|
|
static void GenerateTask(const double a,const double b,const double xl,const double xr,const bool randomx,const double smin,const double smax,const int n,CMatrixDouble &xy,double &s[]);
|
|
static void FillTaskWithY(const double a,const double b,const int n,CMatrixDouble &xy,double &s[]);
|
|
static double GenerateNormal(const double mean,const double sigma);
|
|
static void CalculateMV(double &x[],const int n,double &mean,double &means,double &stddev,double &stddevs);
|
|
static void UnsetLR(CLinearModel &lr);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestLinRegUnit::CTestLinRegUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestLinRegUnit::~CTestLinRegUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLinReg |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestLinRegUnit::TestLinReg(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double sigmathreshold=0;
|
|
int maxn=0;
|
|
int maxm=0;
|
|
int passcount=0;
|
|
int estpasscount=0;
|
|
double threshold=0;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int tmpi=0;
|
|
int pass=0;
|
|
int epass=0;
|
|
int m=0;
|
|
int tasktype=0;
|
|
int modeltype=0;
|
|
int m1=0;
|
|
int m2=0;
|
|
int n1=0;
|
|
int n2=0;
|
|
int info=0;
|
|
int info2=0;
|
|
double y1=0;
|
|
double y2=0;
|
|
bool allsame;
|
|
double ea=0;
|
|
double eb=0;
|
|
double varatested=0;
|
|
double varbtested=0;
|
|
double a=0;
|
|
double b=0;
|
|
double vara=0;
|
|
double varb=0;
|
|
double a2=0;
|
|
double b2=0;
|
|
double covab=0;
|
|
double corrab=0;
|
|
double p=0;
|
|
int qcnt=0;
|
|
double f=0;
|
|
double fp=0;
|
|
double fm=0;
|
|
double v=0;
|
|
double vv=0;
|
|
double cvrmserror=0;
|
|
double cvavgerror=0;
|
|
double cvavgrelerror=0;
|
|
double rmserror=0;
|
|
double avgerror=0;
|
|
double avgrelerror=0;
|
|
bool nondefect;
|
|
double sinshift=0;
|
|
double tasklevel=0;
|
|
double noiselevel=0;
|
|
double hstep=0;
|
|
double sigma=0;
|
|
double mean=0;
|
|
double means=0;
|
|
double stddev=0;
|
|
double stddevs=0;
|
|
bool slcerrors;
|
|
bool slerrors;
|
|
bool grcoverrors;
|
|
bool gropterrors;
|
|
bool gresterrors;
|
|
bool grothererrors;
|
|
bool grconverrors;
|
|
bool waserrors;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double s[];
|
|
double s2[];
|
|
double w2[];
|
|
double x[];
|
|
double ta[];
|
|
double tb[];
|
|
double tc[];
|
|
double xy0[];
|
|
double tmpweights[];
|
|
double x1[];
|
|
double x2[];
|
|
double qtbl[];
|
|
double qvals[];
|
|
double qsigma[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
CMatrixDouble xy2;
|
|
//--- objects of classes
|
|
CLinearModel w;
|
|
CLinearModel wt;
|
|
CLinearModel wt2;
|
|
CLRReport ar;
|
|
CLRReport ar2;
|
|
//--- Primary settings
|
|
maxn=40;
|
|
maxm=5;
|
|
passcount=3;
|
|
estpasscount=1000;
|
|
sigmathreshold=7;
|
|
threshold=1000000*CMath::m_machineepsilon;
|
|
slerrors=false;
|
|
slcerrors=false;
|
|
grcoverrors=false;
|
|
gropterrors=false;
|
|
gresterrors=false;
|
|
grothererrors=false;
|
|
grconverrors=false;
|
|
waserrors=false;
|
|
//--- Quantiles table setup
|
|
qcnt=5;
|
|
ArrayResize(qtbl,qcnt);
|
|
ArrayResize(qvals,qcnt);
|
|
ArrayResize(qsigma,qcnt);
|
|
qtbl[0]=0.5;
|
|
qtbl[1]=0.25;
|
|
qtbl[2]=0.10;
|
|
qtbl[3]=0.05;
|
|
qtbl[4]=0.025;
|
|
for(i=0;i<=qcnt-1;i++)
|
|
qsigma[i]=MathSqrt(qtbl[i]*(1-qtbl[i])/estpasscount);
|
|
//--- Other setup
|
|
ArrayResize(ta,estpasscount);
|
|
ArrayResize(tb,estpasscount);
|
|
//--- Test straight line regression
|
|
for(n=2;n<=maxn;n++)
|
|
{
|
|
//--- Fail/pass test
|
|
GenerateRandomTask(-1,1,false,-1,1,1,2,n,xy,s);
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- search errors
|
|
slcerrors=slcerrors || info!=1;
|
|
//--- function calls
|
|
GenerateRandomTask(1,1,false,-1,1,1,2,n,xy,s);
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- search errors
|
|
slcerrors=slcerrors || info!=-3;
|
|
//--- function calls
|
|
GenerateRandomTask(-1,1,false,-1,1,-1,-1,n,xy,s);
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- search errors
|
|
slcerrors=slcerrors || info!=-2;
|
|
//--- function calls
|
|
GenerateRandomTask(-1,1,false,-1,1,2,1,2,xy,s);
|
|
CLinReg::LRLines(xy,s,1,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- search errors
|
|
slcerrors=slcerrors || info!=-1;
|
|
//--- Multipass tests
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Test S variant against non-S variant
|
|
ea=2*CMath::RandomReal()-1;
|
|
eb=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
GenerateTask(ea,eb,-(5*CMath::RandomReal()),5*CMath::RandomReal(),CMath::RandomReal()>0.5,1,1,n,xy,s);
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
CLinReg::LRLine(xy,n,info2,a2,b2);
|
|
//--- check
|
|
if(info!=1 || info2!=1)
|
|
slcerrors=true;
|
|
else
|
|
slerrors=(slerrors || MathAbs(a-a2)>threshold) || MathAbs(b-b2)>threshold;
|
|
//--- Test for A/B
|
|
//--- Generate task with exact,non-perturbed y[i],
|
|
//--- then make non-zero s[i]
|
|
ea=2*CMath::RandomReal()-1;
|
|
eb=2*CMath::RandomReal()-1;
|
|
GenerateTask(ea,eb,-(5*CMath::RandomReal()),5*CMath::RandomReal(),n>4,0.0,0.0,n,xy,s);
|
|
for(i=0;i<=n-1;i++)
|
|
s[i]=1+CMath::RandomReal();
|
|
//--- function call
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- check
|
|
if(info!=1)
|
|
slcerrors=true;
|
|
else
|
|
slerrors=(slerrors || MathAbs(a-ea)>0.001) || MathAbs(b-eb)>0.001;
|
|
//--- Test for VarA,VarB,P (P is being tested only for N>2)
|
|
for(i=0;i<=qcnt-1;i++)
|
|
qvals[i]=0;
|
|
ea=2*CMath::RandomReal()-1;
|
|
eb=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
GenerateTask(ea,eb,-(5*CMath::RandomReal()),5*CMath::RandomReal(),n>4,1.0,2.0,n,xy,s);
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
slcerrors=true;
|
|
continue;
|
|
}
|
|
//--- change values
|
|
varatested=vara;
|
|
varbtested=varb;
|
|
//--- calculation
|
|
for(epass=0;epass<=estpasscount-1;epass++)
|
|
{
|
|
//--- Generate
|
|
FillTaskWithY(ea,eb,n,xy,s);
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
slcerrors=true;
|
|
continue;
|
|
}
|
|
//--- A,B,P
|
|
//--- (P is being tested for uniformity,additional p-tests are below)
|
|
ta[epass]=a;
|
|
tb[epass]=b;
|
|
for(i=0;i<=qcnt-1;i++)
|
|
{
|
|
//--- check
|
|
if(p<=qtbl[i])
|
|
qvals[i]=qvals[i]+1.0/(double)estpasscount;
|
|
}
|
|
}
|
|
//--- function call
|
|
CalculateMV(ta,estpasscount,mean,means,stddev,stddevs);
|
|
//--- search errors
|
|
slerrors=slerrors || MathAbs(mean-ea)/means>=sigmathreshold;
|
|
slerrors=slerrors || MathAbs(stddev-MathSqrt(varatested))/stddevs>=sigmathreshold;
|
|
//--- function call
|
|
CalculateMV(tb,estpasscount,mean,means,stddev,stddevs);
|
|
//--- search errors
|
|
slerrors=slerrors || MathAbs(mean-eb)/means>=sigmathreshold;
|
|
slerrors=slerrors || MathAbs(stddev-MathSqrt(varbtested))/stddevs>=sigmathreshold;
|
|
//--- check
|
|
if(n>2)
|
|
{
|
|
for(i=0;i<=qcnt-1;i++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(qtbl[i]-qvals[i])/qsigma[i]>sigmathreshold)
|
|
slerrors=true;
|
|
}
|
|
}
|
|
//--- Additional tests for P: correlation with fit quality
|
|
if(n>2)
|
|
{
|
|
GenerateTask(ea,eb,-(5*CMath::RandomReal()),5*CMath::RandomReal(),false,0.0,0.0,n,xy,s);
|
|
for(i=0;i<=n-1;i++)
|
|
s[i]=1+CMath::RandomReal();
|
|
//--- function call
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
slcerrors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
slerrors=slerrors || p<(double)(0.999);
|
|
//--- function call
|
|
GenerateTask(0,0,-(5*CMath::RandomReal()),5*CMath::RandomReal(),false,1.0,1.0,n,xy,s);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i%2==0)
|
|
xy[i].Set(1,5.0);
|
|
else
|
|
xy[i].Set(1,-5.0);
|
|
}
|
|
//--- check
|
|
if(n%2!=0)
|
|
xy[n-1].Set(1,0);
|
|
//--- function call
|
|
CLinReg::LRLines(xy,s,n,info,a,b,vara,varb,covab,corrab,p);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
slcerrors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
slerrors=slerrors || p>0.001;
|
|
}
|
|
}
|
|
}
|
|
//--- General regression tests:
|
|
//--- Simple linear tests (small sample,optimum point,covariance)
|
|
for(n=3;n<=maxn;n++)
|
|
{
|
|
ArrayResize(s,n);
|
|
//--- Linear tests:
|
|
//--- a. random points,sigmas
|
|
//--- b. no sigmas
|
|
xy.Resize(n,2);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
xy[i].Set(0,2*CMath::RandomReal()-1);
|
|
xy[i].Set(1,2*CMath::RandomReal()-1);
|
|
s[i]=1+CMath::RandomReal();
|
|
}
|
|
//--- function call
|
|
CLinReg::LRBuildS(xy,s,n,1,info,wt,ar);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
grconverrors=true;
|
|
continue;
|
|
}
|
|
//--- function calls
|
|
CLinReg::LRUnpack(wt,tmpweights,tmpi);
|
|
//--- search errors
|
|
CLinReg::LRLines(xy,s,n,info2,a,b,vara,varb,covab,corrab,p);
|
|
gropterrors=gropterrors || MathAbs(a-tmpweights[1])>threshold;
|
|
gropterrors=gropterrors || MathAbs(b-tmpweights[0])>threshold;
|
|
grcoverrors=grcoverrors || MathAbs(vara-ar.m_c[1][1])>threshold;
|
|
grcoverrors=grcoverrors || MathAbs(varb-ar.m_c[0][0])>threshold;
|
|
grcoverrors=grcoverrors || MathAbs(covab-ar.m_c[1][0])>threshold;
|
|
grcoverrors=grcoverrors || MathAbs(covab-ar.m_c[0][1])>threshold;
|
|
//--- function call
|
|
CLinReg::LRBuild(xy,n,1,info,wt,ar);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
grconverrors=true;
|
|
continue;
|
|
}
|
|
//--- function calls
|
|
CLinReg::LRUnpack(wt,tmpweights,tmpi);
|
|
CLinReg::LRLine(xy,n,info2,a,b);
|
|
//--- search errors
|
|
gropterrors=gropterrors || MathAbs(a-tmpweights[1])>threshold;
|
|
gropterrors=gropterrors || MathAbs(b-tmpweights[0])>threshold;
|
|
}
|
|
//--- S covariance versus S-less covariance.
|
|
//--- Slightly skewed task,large sample size.
|
|
//--- Will S-less subroutine estimate covariance matrix good enough?
|
|
n=1000+CMath::RandomInteger(3000);
|
|
sigma=0.1+CMath::RandomReal()*1.9;
|
|
//--- allocation
|
|
xy.Resize(n,2);
|
|
ArrayResize(s,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
xy[i].Set(0,1.5*CMath::RandomReal()-0.5);
|
|
xy[i].Set(1,1.2*xy[i][0]-0.3+GenerateNormal(0,sigma));
|
|
s[i]=sigma;
|
|
}
|
|
//--- function calls
|
|
CLinReg::LRBuild(xy,n,1,info,wt,ar);
|
|
CLinReg::LRLines(xy,s,n,info2,a,b,vara,varb,covab,corrab,p);
|
|
//--- check
|
|
if(info!=1 || info2!=1)
|
|
grconverrors=true;
|
|
else
|
|
{
|
|
grcoverrors=grcoverrors || MathAbs(MathLog(ar.m_c[0][0]/varb))>MathLog(1.2);
|
|
grcoverrors=grcoverrors || MathAbs(MathLog(ar.m_c[1][1]/vara))>MathLog(1.2);
|
|
grcoverrors=grcoverrors || MathAbs(MathLog(ar.m_c[0][1]/covab))>MathLog(1.2);
|
|
grcoverrors=grcoverrors || MathAbs(MathLog(ar.m_c[1][0]/covab))>MathLog(1.2);
|
|
}
|
|
//--- General tests:
|
|
//--- * basis functions - up to cubic
|
|
//--- * task types:
|
|
//--- * data set is noisy sine half-period with random shift
|
|
//--- * tests:
|
|
//--- unpacking/packing
|
|
//--- optimality
|
|
//--- error estimates
|
|
//--- * tasks:
|
|
//--- 0=noised sine
|
|
//--- 1=degenerate task with 1-of-n encoded categorical variables
|
|
//--- 2=random task with large variation (for 1-type models)
|
|
//--- 3=random task with small variation (for 1-type models)
|
|
//--- Additional tasks TODO
|
|
//--- specially designed task with defective vectors which leads to
|
|
//--- the failure of the fast CV formula.
|
|
for(modeltype=0;modeltype<=1;modeltype++)
|
|
{
|
|
for(tasktype=0;tasktype<=3;tasktype++)
|
|
{
|
|
//--- check
|
|
if(tasktype==0)
|
|
{
|
|
m1=1;
|
|
m2=3;
|
|
}
|
|
//--- check
|
|
if(tasktype==1)
|
|
{
|
|
m1=9;
|
|
m2=9;
|
|
}
|
|
//--- check
|
|
if(tasktype==2 || tasktype==3)
|
|
{
|
|
m1=9;
|
|
m2=9;
|
|
}
|
|
//--- calculation
|
|
for(m=m1;m<=m2;m++)
|
|
{
|
|
//--- check
|
|
if(tasktype==0)
|
|
{
|
|
n1=m+3;
|
|
n2=m+20;
|
|
}
|
|
//--- check
|
|
if(tasktype==1)
|
|
{
|
|
n1=70+CMath::RandomInteger(70);
|
|
n2=n1;
|
|
}
|
|
//--- check
|
|
if(tasktype==2 || tasktype==3)
|
|
{
|
|
n1=100;
|
|
n2=n1;
|
|
}
|
|
for(n=n1;n<=n2;n++)
|
|
{
|
|
//--- allocation
|
|
xy.Resize(n,m+1);
|
|
ArrayResize(xy0,n);
|
|
ArrayResize(s,n);
|
|
hstep=0.001;
|
|
noiselevel=0.2;
|
|
//--- Prepare task
|
|
if(tasktype==0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
xy[i].Set(0,2*CMath::RandomReal()-1);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=1;j<=m-1;j++)
|
|
xy[i].Set(j,xy[i][0]*xy[i][j-1]);
|
|
}
|
|
sinshift=CMath::RandomReal()*M_PI;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
xy0[i]=MathSin(sinshift+M_PI*0.5*(xy[i][0]+1));
|
|
xy[i].Set(m,xy0[i]+noiselevel*GenerateNormal(0,1));
|
|
}
|
|
}
|
|
//--- check
|
|
if(tasktype==1)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(m==9))
|
|
return(false);
|
|
//--- allocation
|
|
ArrayResize(ta,9);
|
|
//--- change values
|
|
ta[0]=1;
|
|
ta[1]=2;
|
|
ta[2]=3;
|
|
ta[3]=0.25;
|
|
ta[4]=0.5;
|
|
ta[5]=0.75;
|
|
ta[6]=0.06;
|
|
ta[7]=0.12;
|
|
ta[8]=0.18;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
xy[i].Set(j,0);
|
|
xy[i].Set(i%3,1);
|
|
xy[i].Set(3+i/3%3,1);
|
|
xy[i].Set(6+i/9%3,1);
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=8;i_++)
|
|
v+=xy[i][i_]*ta[i_];
|
|
xy0[i]=v;
|
|
xy[i].Set(m,v+noiselevel*GenerateNormal(0,1));
|
|
}
|
|
}
|
|
//--- check
|
|
if(tasktype==2 || tasktype==3)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(m==9))
|
|
return(false);
|
|
//--- allocation
|
|
ArrayResize(ta,9);
|
|
//--- change values
|
|
ta[0]=1;
|
|
ta[1]=-2;
|
|
ta[2]=3;
|
|
ta[3]=0.25;
|
|
ta[4]=-0.5;
|
|
ta[5]=0.75;
|
|
ta[6]=-0.06;
|
|
ta[7]=0.12;
|
|
ta[8]=-0.18;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- check
|
|
if(tasktype==2)
|
|
xy[i].Set(j,1+GenerateNormal(0,3));
|
|
else
|
|
xy[i].Set(j,1+GenerateNormal(0,0.05));
|
|
}
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=8;i_++)
|
|
v+=xy[i][i_]*ta[i_];
|
|
xy0[i]=v;
|
|
xy[i].Set(m,v+noiselevel*GenerateNormal(0,1));
|
|
}
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
s[i]=1+CMath::RandomReal();
|
|
//--- Solve (using S-variant,non-S-variant is not tested)
|
|
if(modeltype==0)
|
|
CLinReg::LRBuildS(xy,s,n,m,info,wt,ar);
|
|
else
|
|
CLinReg::LRBuildZS(xy,s,n,m,info,wt,ar);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
grconverrors=true;
|
|
continue;
|
|
}
|
|
//--- function call
|
|
CLinReg::LRUnpack(wt,tmpweights,tmpi);
|
|
//--- LRProcess test
|
|
ArrayResize(x,m);
|
|
//--- change values
|
|
v=tmpweights[m];
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
v=v+tmpweights[i]*x[i];
|
|
}
|
|
//--- search errors
|
|
grothererrors=grothererrors || MathAbs(v-CLinReg::LRProcess(wt,x))/MathMax(MathAbs(v),1)>threshold;
|
|
//--- LRPack test
|
|
CLinReg::LRPack(tmpweights,m,wt2);
|
|
ArrayResize(x,m);
|
|
for(i=0;i<=m-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
v=CLinReg::LRProcess(wt,x);
|
|
grothererrors=grothererrors || MathAbs(v-CLinReg::LRProcess(wt2,x))/MathAbs(v)>threshold;
|
|
//--- Optimality test
|
|
for(k=0;k<=m;k++)
|
|
{
|
|
//--- check
|
|
if(modeltype==1 && k==m)
|
|
{
|
|
//--- 0-type models (with non-zero constant term)
|
|
//--- are tested for optimality of all coefficients.
|
|
//--- 1-type models (with zero constant term)
|
|
//--- are tested for optimality of non-constant terms only.
|
|
continue;
|
|
}
|
|
//--- change values
|
|
f=0;
|
|
fp=0;
|
|
fm=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v=tmpweights[m];
|
|
for(j=0;j<=m-1;j++)
|
|
v=v+xy[i][j]*tmpweights[j];
|
|
f=f+CMath::Sqr((v-xy[i][m])/s[i]);
|
|
//--- check
|
|
if(k<m)
|
|
vv=xy[i][k];
|
|
else
|
|
vv=1;
|
|
//--- calculation
|
|
fp=fp+CMath::Sqr((v+vv*hstep-xy[i][m])/s[i]);
|
|
fm=fm+CMath::Sqr((v-vv*hstep-xy[i][m])/s[i]);
|
|
}
|
|
//--- search errors
|
|
gropterrors=(gropterrors || f>fp) || f>fm;
|
|
}
|
|
//--- Covariance matrix test:
|
|
//--- generate random vector,project coefficients on it,
|
|
//--- compare variance of projection with estimate provided
|
|
//--- by cov.matrix
|
|
ArrayResize(ta,estpasscount);
|
|
ArrayResize(tb,m+1);
|
|
ArrayResize(tc,m+1);
|
|
xy2.Resize(n,m+1);
|
|
for(i=0;i<=m;i++)
|
|
tb[i]=GenerateNormal(0,1);
|
|
//--- calculation
|
|
for(epass=0;epass<=estpasscount-1;epass++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=m-1;i_++)
|
|
xy2[i].Set(i_,xy[i][i_]);
|
|
xy2[i].Set(m,xy0[i]+s[i]*GenerateNormal(0,1));
|
|
}
|
|
//--- check
|
|
if(modeltype==0)
|
|
CLinReg::LRBuildS(xy2,s,n,m,info,wt,ar2);
|
|
else
|
|
CLinReg::LRBuildZS(xy2,s,n,m,info,wt,ar2);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
ta[epass]=0;
|
|
grconverrors=true;
|
|
continue;
|
|
}
|
|
//--- function call
|
|
CLinReg::LRUnpack(wt,w2,tmpi);
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=m;i_++)
|
|
v+=tb[i_]*w2[i_];
|
|
ta[epass]=v;
|
|
}
|
|
//--- function call
|
|
CalculateMV(ta,estpasscount,mean,means,stddev,stddevs);
|
|
for(i=0;i<=m;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=m;i_++)
|
|
v+=tb[i_]*ar.m_c[i_][i];
|
|
tc[i]=v;
|
|
}
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=m;i_++)
|
|
v+=tc[i_]*tb[i_];
|
|
//--- search errors
|
|
grcoverrors=grcoverrors || MathAbs((MathSqrt(v)-stddev)/stddevs)>=sigmathreshold;
|
|
//--- Test for the fast CV error:
|
|
//--- calculate CV error by definition (leaving out N
|
|
//--- points and recalculating solution).
|
|
//--- Test for the training set error
|
|
cvrmserror=0;
|
|
cvavgerror=0;
|
|
cvavgrelerror=0;
|
|
rmserror=0;
|
|
avgerror=0;
|
|
avgrelerror=0;
|
|
xy2.Resize(n-1,m+1);
|
|
ArrayResize(s2,n-1);
|
|
//--- change values
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
for(i_=0;i_<=m;i_++)
|
|
xy2[i].Set(i_,xy[i+1][i_]);
|
|
s2[i]=s[i+1];
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- Trn
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=xy[i][i_]*tmpweights[i_];
|
|
v=v+tmpweights[m];
|
|
//--- search errors
|
|
rmserror=rmserror+CMath::Sqr(v-xy[i][m]);
|
|
avgerror=avgerror+MathAbs(v-xy[i][m]);
|
|
avgrelerror=avgrelerror+MathAbs((v-xy[i][m])/xy[i][m]);
|
|
//--- CV: non-defect vectors only
|
|
nondefect=true;
|
|
for(k=0;k<=ar.m_ncvdefects-1;k++)
|
|
{
|
|
//--- check
|
|
if(ar.m_cvdefects[k]==i)
|
|
nondefect=false;
|
|
}
|
|
//--- check
|
|
if(nondefect)
|
|
{
|
|
//--- check
|
|
if(modeltype==0)
|
|
CLinReg::LRBuildS(xy2,s2,n-1,m,info2,wt,ar2);
|
|
else
|
|
CLinReg::LRBuildZS(xy2,s2,n-1,m,info2,wt,ar2);
|
|
//--- check
|
|
if(info2!=1)
|
|
{
|
|
grconverrors=true;
|
|
continue;
|
|
}
|
|
//--- function call
|
|
CLinReg::LRUnpack(wt,w2,tmpi);
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=xy[i][i_]*w2[i_];
|
|
v=v+w2[m];
|
|
//--- search errors
|
|
cvrmserror=cvrmserror+CMath::Sqr(v-xy[i][m]);
|
|
cvavgerror=cvavgerror+MathAbs(v-xy[i][m]);
|
|
cvavgrelerror=cvavgrelerror+MathAbs((v-xy[i][m])/xy[i][m]);
|
|
}
|
|
//--- Next set
|
|
if(i!=n-1)
|
|
{
|
|
for(i_=0;i_<=m;i_++)
|
|
xy2[i].Set(i_,xy[i][i_]);
|
|
s2[i]=s[i];
|
|
}
|
|
}
|
|
//--- search errors
|
|
cvrmserror=MathSqrt(cvrmserror/(n-ar.m_ncvdefects));
|
|
cvavgerror=cvavgerror/(n-ar.m_ncvdefects);
|
|
cvavgrelerror=cvavgrelerror/(n-ar.m_ncvdefects);
|
|
rmserror=MathSqrt(rmserror/n);
|
|
avgerror=avgerror/n;
|
|
avgrelerror=avgrelerror/n;
|
|
gresterrors=gresterrors || MathAbs(MathLog(ar.m_cvrmserror/cvrmserror))>MathLog(1+1.0E-5);
|
|
gresterrors=gresterrors || MathAbs(MathLog(ar.m_cvavgerror/cvavgerror))>MathLog(1+1.0E-5);
|
|
gresterrors=gresterrors || MathAbs(MathLog(ar.m_cvavgrelerror/cvavgrelerror))>MathLog(1+1.0E-5);
|
|
gresterrors=gresterrors || MathAbs(MathLog(ar.m_rmserror/rmserror))>MathLog(1+1.0E-5);
|
|
gresterrors=gresterrors || MathAbs(MathLog(ar.m_avgerror/avgerror))>MathLog(1+1.0E-5);
|
|
gresterrors=gresterrors || MathAbs(MathLog(ar.m_avgrelerror/avgrelerror))>MathLog(1+1.0E-5);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Additional subroutines
|
|
for(pass=1;pass<=50;pass++)
|
|
{
|
|
n=2;
|
|
//--- cycle
|
|
do
|
|
{
|
|
noiselevel=CMath::RandomReal()+0.1;
|
|
tasklevel=2*CMath::RandomReal()-1;
|
|
}
|
|
while(MathAbs(noiselevel-tasklevel)<=0.05);
|
|
//--- allocation
|
|
xy.Resize(3*n,2);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
xy[3*i+0].Set(0,i);
|
|
xy[3*i+1].Set(0,i);
|
|
xy[3*i+2].Set(0,i);
|
|
xy[3*i+0].Set(1,tasklevel-noiselevel);
|
|
xy[3*i+1].Set(1,tasklevel);
|
|
xy[3*i+2].Set(1,tasklevel+noiselevel);
|
|
}
|
|
//--- function call
|
|
CLinReg::LRBuild(xy,3*n,1,info,wt,ar);
|
|
//--- check
|
|
if(info==1)
|
|
{
|
|
//--- function calls
|
|
CLinReg::LRUnpack(wt,tmpweights,tmpi);
|
|
v=CLinReg::LRRMSError(wt,xy,3*n);
|
|
//--- search errors
|
|
grothererrors=grothererrors || MathAbs(v-noiselevel*MathSqrt(2.0/3.0))>threshold;
|
|
//--- function call
|
|
v=CLinReg::LRAvgError(wt,xy,3*n);
|
|
//--- search errors
|
|
grothererrors=grothererrors || MathAbs(v-noiselevel*(2.0/3.0))>threshold;
|
|
//--- function call
|
|
v=CLinReg::LRAvgRelError(wt,xy,3*n);
|
|
vv=(MathAbs(noiselevel/(tasklevel-noiselevel))+MathAbs(noiselevel/(tasklevel+noiselevel)))/3;
|
|
//--- search errors
|
|
grothererrors=grothererrors || MathAbs(v-vv)>threshold*vv;
|
|
}
|
|
else
|
|
grothererrors=true;
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
xy[3*i+0].Set(0,i);
|
|
xy[3*i+1].Set(0,i);
|
|
xy[3*i+2].Set(0,i);
|
|
xy[3*i+0].Set(1,-noiselevel);
|
|
xy[3*i+1].Set(1,0);
|
|
xy[3*i+2].Set(1,noiselevel);
|
|
}
|
|
//--- function call
|
|
CLinReg::LRBuild(xy,3*n,1,info,wt,ar);
|
|
//--- check
|
|
if(info==1)
|
|
{
|
|
//--- function calls
|
|
CLinReg::LRUnpack(wt,tmpweights,tmpi);
|
|
v=CLinReg::LRAvgRelError(wt,xy,3*n);
|
|
//--- search errors
|
|
grothererrors=grothererrors || MathAbs(v-1)>threshold;
|
|
}
|
|
else
|
|
grothererrors=true;
|
|
}
|
|
//--- calculation
|
|
for(pass=1;pass<=10;pass++)
|
|
{
|
|
m=1+CMath::RandomInteger(5);
|
|
n=10+CMath::RandomInteger(10);
|
|
//--- allocation
|
|
xy.Resize(n,m+1);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m;j++)
|
|
xy[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function call
|
|
CLinReg::LRBuild(xy,n,m,info,w,ar);
|
|
//--- check
|
|
if(info<0)
|
|
{
|
|
grothererrors=true;
|
|
break;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(x1,m);
|
|
ArrayResize(x2,m);
|
|
//--- Same inputs on original leads to same outputs
|
|
//--- on copy created using LRCopy
|
|
UnsetLR(wt);
|
|
CLinReg::LRCopy(w,wt);
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
//--- change values
|
|
y1=CLinReg::LRProcess(w,x1);
|
|
y2=CLinReg::LRProcess(wt,x2);
|
|
allsame=y1==y2;
|
|
//--- search errors
|
|
grothererrors=grothererrors || !allsame;
|
|
}
|
|
//--- TODO: Degenerate tests (when design matrix and right part are zero)
|
|
//--- Final report
|
|
waserrors=(((((slerrors || slcerrors) || gropterrors) || grcoverrors) || gresterrors) || grothererrors) || grconverrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("REGRESSION TEST");
|
|
Print("STRAIGHT LINE REGRESSION: ");
|
|
//--- check
|
|
if(!slerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("STRAIGHT LINE REGRESSION CONVERGENCE: ");
|
|
//--- check
|
|
if(!slcerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("GENERAL LINEAR REGRESSION: ");
|
|
//--- check
|
|
if(!((((gropterrors || grcoverrors) || gresterrors) || grothererrors) || grconverrors))
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* OPTIMALITY: ");
|
|
//--- check
|
|
if(!gropterrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* COV. MATRIX: ");
|
|
//--- check
|
|
if(!grcoverrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* ERROR ESTIMATES: ");
|
|
//--- check
|
|
if(!gresterrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* CONVERGENCE: ");
|
|
//--- check
|
|
if(!grconverrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* OTHER SUBROUTINES: ");
|
|
//--- check
|
|
if(!grothererrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST SUMMARY: FAILED");
|
|
else
|
|
Print("TEST SUMMARY: PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Task generation. Meaningless task,just random numbers. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestLinRegUnit::GenerateRandomTask(const double xl,const double xr,
|
|
const bool randomx,const double ymin,
|
|
const double ymax,const double smin,
|
|
const double smax,const int n,
|
|
CMatrixDouble &xy,double &s[])
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- allocation
|
|
xy.Resize(n,2);
|
|
ArrayResize(s,n);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(randomx)
|
|
xy[i].Set(0,xl+(xr-xl)*CMath::RandomReal());
|
|
else
|
|
xy[i].Set(0,xl+(xr-xl)*i/(n-1));
|
|
//--- calculation
|
|
xy[i].Set(1,ymin+(ymax-ymin)*CMath::RandomReal());
|
|
s[i]=smin+(smax-smin)*CMath::RandomReal();
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Task generation. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestLinRegUnit::GenerateTask(const double a,const double b,
|
|
const double xl,const double xr,
|
|
const bool randomx,const double smin,
|
|
const double smax,const int n,
|
|
CMatrixDouble &xy,double &s[])
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- allocation
|
|
xy.Resize(n,2);
|
|
ArrayResize(s,n);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(randomx)
|
|
xy[i].Set(0,xl+(xr-xl)*CMath::RandomReal());
|
|
else
|
|
xy[i].Set(0,xl+(xr-xl)*i/(n-1));
|
|
//--- change values
|
|
s[i]=smin+(smax-smin)*CMath::RandomReal();
|
|
xy[i].Set(1,a+b*xy[i][0]+GenerateNormal(0,s[i]));
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Task generation. |
|
|
//| y[i] are filled based on A,B,X[I],S[I] |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestLinRegUnit::FillTaskWithY(const double a,const double b,
|
|
const int n,CMatrixDouble &xy,
|
|
double &s[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
xy[i].Set(1,a+b*xy[i][0]+GenerateNormal(0,s[i]));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Normal random numbers |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestLinRegUnit::GenerateNormal(const double mean,const double sigma)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
double u=0;
|
|
double v=0;
|
|
double sum=0;
|
|
//--- initialization
|
|
result=mean;
|
|
//--- calculation
|
|
while(true)
|
|
{
|
|
//--- change values
|
|
u=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal();
|
|
v=(2*CMath::RandomInteger(2)-1)*CMath::RandomReal();
|
|
sum=u*u+v*v;
|
|
//--- check
|
|
if(sum<1.0 && sum>0.0)
|
|
{
|
|
sum=MathSqrt(-(2*MathLog(sum)/sum));
|
|
result=sigma*u*sum+mean;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Moments estimates and their errors |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestLinRegUnit::CalculateMV(double &x[],const int n,double &mean,
|
|
double &means,double &stddev,
|
|
double &stddevs)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double variance=0;
|
|
//--- initialization
|
|
mean=0;
|
|
means=1;
|
|
stddev=0;
|
|
stddevs=1;
|
|
variance=0;
|
|
//--- check
|
|
if(n<=1)
|
|
return;
|
|
//--- Mean
|
|
for(i=0;i<=n-1;i++)
|
|
mean=mean+x[i];
|
|
mean=mean/n;
|
|
//--- Variance (using corrected two-pass algorithm)
|
|
if(n!=1)
|
|
{
|
|
//--- change value
|
|
v1=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v1=v1+CMath::Sqr(x[i]-mean);
|
|
//--- change value
|
|
v2=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v2=v2+(x[i]-mean);
|
|
v2=CMath::Sqr(v2)/n;
|
|
//--- calculation
|
|
variance=(v1-v2)/(n-1);
|
|
//--- check
|
|
if(variance<0.0)
|
|
variance=0;
|
|
stddev=MathSqrt(variance);
|
|
}
|
|
//--- Errors
|
|
means=stddev/MathSqrt(n);
|
|
stddevs=stddev*MathSqrt(2)/MathSqrt(n-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets LR |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestLinRegUnit::UnsetLR(CLinearModel &lr)
|
|
{
|
|
//--- create variables
|
|
int info=0;
|
|
int i=0;
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- object of class
|
|
CLRReport rep;
|
|
//--- allocation
|
|
xy.Resize(6,2);
|
|
//--- change values
|
|
for(i=0;i<=5;i++)
|
|
{
|
|
xy[i].Set(0,0);
|
|
xy[i].Set(1,0);
|
|
}
|
|
//--- function call
|
|
CLinReg::LRBuild(xy,6,1,info,lr,rep);
|
|
//--- check
|
|
if(!CAp::Assert(info>0))
|
|
return;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CXblas |
|
|
//+------------------------------------------------------------------+
|
|
class CTestXBlasUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestXBlasUnit(void);
|
|
~CTestXBlasUnit(void);
|
|
//--- public method
|
|
static bool TestXBlas(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestXBlasUnit::CTestXBlasUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestXBlasUnit::~CTestXBlasUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CXblas |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestXBlasUnit::TestXBlas(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool approxerrors;
|
|
bool exactnesserrors;
|
|
bool waserrors;
|
|
double approxthreshold=0;
|
|
int maxn=0;
|
|
int passcount=0;
|
|
int n=0;
|
|
int i=0;
|
|
int pass=0;
|
|
double rv1=0;
|
|
double rv2=0;
|
|
double rv2err=0;
|
|
complex cv1=0;
|
|
complex cv2=0;
|
|
double cv2err=0;
|
|
double s=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double rx[];
|
|
double ry[];
|
|
complex cx[];
|
|
complex cy[];
|
|
double temp[];
|
|
//--- initialization
|
|
approxerrors=false;
|
|
exactnesserrors=false;
|
|
waserrors=false;
|
|
approxthreshold=1000*CMath::m_machineepsilon;
|
|
maxn=1000;
|
|
passcount=10;
|
|
//--- tests:
|
|
//--- 1. ability to calculate dot product
|
|
//--- 2. higher precision
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- ability to approximately calculate real dot product
|
|
ArrayResize(rx,n);
|
|
ArrayResize(ry,n);
|
|
ArrayResize(temp,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>0.2)
|
|
rx[i]=2*CMath::RandomReal()-1;
|
|
else
|
|
rx[i]=0;
|
|
//--- check
|
|
if(CMath::RandomReal()>0.2)
|
|
ry[i]=2*CMath::RandomReal()-1;
|
|
else
|
|
ry[i]=0;
|
|
}
|
|
//--- change value
|
|
rv1=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
rv1+=rx[i_]*ry[i_];
|
|
//--- function call
|
|
CXblas::XDot(rx,ry,n,temp,rv2,rv2err);
|
|
//--- search errors
|
|
approxerrors=approxerrors || MathAbs(rv1-rv2)>approxthreshold;
|
|
//--- ability to approximately calculate complex dot product
|
|
ArrayResize(cx,n);
|
|
ArrayResize(cy,n);
|
|
ArrayResize(temp,2*n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>0.2)
|
|
{
|
|
cx[i].re=2*CMath::RandomReal()-1;
|
|
cx[i].im=2*CMath::RandomReal()-1;
|
|
}
|
|
else
|
|
cx[i]=0;
|
|
//--- check
|
|
if(CMath::RandomReal()>0.2)
|
|
{
|
|
cy[i].re=2*CMath::RandomReal()-1;
|
|
cy[i].im=2*CMath::RandomReal()-1;
|
|
}
|
|
else
|
|
cy[i]=0;
|
|
}
|
|
//--- change value
|
|
cv1=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
cv1+=cx[i_]*cy[i_];
|
|
//--- function call
|
|
CXblas::XCDot(cx,cy,n,temp,cv2,cv2err);
|
|
//--- search errors
|
|
approxerrors=approxerrors || CMath::AbsComplex(cv1-cv2)>approxthreshold;
|
|
}
|
|
}
|
|
//--- test of precision: real
|
|
n=50000;
|
|
ArrayResize(rx,n);
|
|
ArrayResize(ry,n);
|
|
ArrayResize(temp,n);
|
|
//--- calculation
|
|
for(pass=0;pass<=passcount-1;pass++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n%2==0))
|
|
return(false);
|
|
//--- First test: X + X + ... + X - X - X - ... - X=1*X
|
|
s=MathExp(MathMax(pass,50));
|
|
//--- check
|
|
if(pass==passcount-1 && pass>1)
|
|
s=1E300;
|
|
//--- change values
|
|
ry[0]=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
|
|
for(i=1;i<=n-1;i++)
|
|
ry[i]=ry[0];
|
|
for(i=0;i<=n/2-1;i++)
|
|
rx[i]=1;
|
|
for(i=n/2;i<=n-2;i++)
|
|
rx[i]=-1;
|
|
rx[n-1]=0;
|
|
//--- function call
|
|
CXblas::XDot(rx,ry,n,temp,rv2,rv2err);
|
|
//--- search errors
|
|
exactnesserrors=exactnesserrors || rv2err<0.0;
|
|
exactnesserrors=exactnesserrors || rv2err>4*CMath::m_machineepsilon*MathAbs(ry[0]);
|
|
exactnesserrors=exactnesserrors || MathAbs(rv2-ry[0])>rv2err;
|
|
//--- First test: X + X + ... + X=N*X
|
|
s=MathExp(MathMax(pass,50));
|
|
//--- check
|
|
if(pass==passcount-1 && pass>1)
|
|
s=1E300;
|
|
//--- change values
|
|
ry[0]=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
|
|
for(i=1;i<=n-1;i++)
|
|
ry[i]=ry[0];
|
|
for(i=0;i<=n-1;i++)
|
|
rx[i]=1;
|
|
//--- function call
|
|
CXblas::XDot(rx,ry,n,temp,rv2,rv2err);
|
|
//--- search errors
|
|
exactnesserrors=exactnesserrors || rv2err<0.0;
|
|
exactnesserrors=exactnesserrors || rv2err>4*CMath::m_machineepsilon*MathAbs(ry[0])*n;
|
|
exactnesserrors=exactnesserrors || MathAbs(rv2-n*ry[0])>rv2err;
|
|
}
|
|
//--- test of precision: complex
|
|
n=50000;
|
|
ArrayResize(cx,n);
|
|
ArrayResize(cy,n);
|
|
ArrayResize(temp,2*n);
|
|
//--- calculation
|
|
for(pass=0;pass<=passcount-1;pass++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n%2==0))
|
|
return(false);
|
|
//--- First test: X + X + ... + X - X - X - ... - X=1*X
|
|
s=MathExp(MathMax(pass,50));
|
|
//--- check
|
|
if(pass==passcount-1 && pass>1)
|
|
s=1E300;
|
|
//--- change values
|
|
cy[0].re=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
|
|
cy[0].im=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
|
|
for(i=1;i<=n-1;i++)
|
|
cy[i]=cy[0];
|
|
for(i=0;i<=n/2-1;i++)
|
|
cx[i]=1;
|
|
for(i=n/2;i<=n-2;i++)
|
|
cx[i]=-1;
|
|
cx[n-1]=0;
|
|
//--- function call
|
|
CXblas::XCDot(cx,cy,n,temp,cv2,cv2err);
|
|
//--- search errors
|
|
exactnesserrors=exactnesserrors || cv2err<0.0;
|
|
exactnesserrors=exactnesserrors || cv2err>4*CMath::m_machineepsilon*CMath::AbsComplex(cy[0]);
|
|
exactnesserrors=exactnesserrors || CMath::AbsComplex(cv2-cy[0])>cv2err;
|
|
//--- First test: X + X + ... + X=N*X
|
|
s=MathExp(MathMax(pass,50));
|
|
//--- check
|
|
if(pass==passcount-1 && pass>1)
|
|
s=1E300;
|
|
//--- change values
|
|
cy[0]=(2*CMath::RandomReal()-1)*s*MathSqrt(2*CMath::RandomReal());
|
|
for(i=1;i<=n-1;i++)
|
|
cy[i]=cy[0];
|
|
for(i=0;i<=n-1;i++)
|
|
cx[i]=1;
|
|
//--- function call
|
|
CXblas::XCDot(cx,cy,n,temp,cv2,cv2err);
|
|
//--- search errors
|
|
exactnesserrors=exactnesserrors || cv2err<0.0;
|
|
exactnesserrors=exactnesserrors || cv2err>4*CMath::m_machineepsilon*CMath::AbsComplex(cy[0])*n;
|
|
exactnesserrors=exactnesserrors || CMath::AbsComplex(cv2-cy[0]*n)>cv2err;
|
|
}
|
|
//--- report
|
|
waserrors=approxerrors || exactnesserrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING XBLAS");
|
|
Print("APPROX.TESTS: ");
|
|
//--- check
|
|
if(approxerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("EXACT TESTS: ");
|
|
//--- check
|
|
if(exactnesserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- end
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CDenseSolver |
|
|
//+------------------------------------------------------------------+
|
|
class CTestDenseSolverUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static bool RMatrixCheckSolutionM(CMatrixDouble &xe,const int n,const int m,const double threshold,const int info,CDenseSolverReport &rep,CMatrixDouble &xs);
|
|
static bool RMatrixCheckSolution(CMatrixDouble &xe,const int n,const double threshold,const int info,CDenseSolverReport &rep,double &xs[]);
|
|
static bool RMatrixCheckSingularM(const int n,const int m,const int info,CDenseSolverReport &rep,CMatrixDouble &xs);
|
|
static bool RMatrixCheckSingular(const int n,const int info,CDenseSolverReport &rep,double &xs[]);
|
|
static bool CMatrixCheckSolutionM(CMatrixComplex &xe,const int n,const int m,const double threshold,const int info,CDenseSolverReport &rep,CMatrixComplex &xs);
|
|
static bool CMatrixCheckSolution(CMatrixComplex &xe,const int n,const double threshold,const int info,CDenseSolverReport &rep,complex &xs[]);
|
|
static bool CMatrixCheckSingularM(const int n,const int m,const int info,CDenseSolverReport &rep,CMatrixComplex &xs);
|
|
static bool CMatrixCheckSingular(const int n,const int info,CDenseSolverReport &rep,complex &xs[]);
|
|
static void RMatrixMakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
|
|
static void CMatrixMakeACopy(CMatrixComplex &a,const int m,const int n,CMatrixComplex &b);
|
|
static void RMatrixDropHalf(CMatrixDouble &a,const int n,const bool droplower);
|
|
static void CMatrixDropHalf(CMatrixComplex &a,const int n,const bool droplower);
|
|
static void TestRSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &rerrors,bool &rfserrors);
|
|
static void TestSPDSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &spderrors,bool &rfserrors);
|
|
static void TestCSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &cerrors,bool &rfserrors);
|
|
static void TestHPDSolver(const int maxn,const int maxm,const int passcount,const double threshold,bool &hpderrors,bool &rfserrors);
|
|
static void Unset2D(CMatrixDouble &x);
|
|
static void Unset1D(double &x[]);
|
|
static void CUnset2D(CMatrixComplex &x);
|
|
static void CUnset1D(complex &x[]);
|
|
static void UnsetRep(CDenseSolverReport &r);
|
|
static void UnsetLSRep(CDenseSolverLSReport &r);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestDenseSolverUnit(void);
|
|
~CTestDenseSolverUnit(void);
|
|
//--- public method
|
|
static bool TestDenseSolver(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestDenseSolverUnit::CTestDenseSolverUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestDenseSolverUnit::~CTestDenseSolverUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestDenseSolverUnit::TestDenseSolver(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int maxn=0;
|
|
int maxm=0;
|
|
int passcount=0;
|
|
double threshold=0;
|
|
bool rerrors;
|
|
bool cerrors;
|
|
bool spderrors;
|
|
bool hpderrors;
|
|
bool rfserrors;
|
|
bool waserrors;
|
|
//--- initialization
|
|
maxn=10;
|
|
maxm=5;
|
|
passcount=5;
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
rfserrors=false;
|
|
rerrors=false;
|
|
cerrors=false;
|
|
spderrors=false;
|
|
hpderrors=false;
|
|
//--- function calls
|
|
TestRSolver(maxn,maxm,passcount,threshold,rerrors,rfserrors);
|
|
TestSPDSolver(maxn,maxm,passcount,threshold,spderrors,rfserrors);
|
|
TestCSolver(maxn,maxm,passcount,threshold,cerrors,rfserrors);
|
|
TestHPDSolver(maxn,maxm,passcount,threshold,hpderrors,rfserrors);
|
|
//--- search errors
|
|
waserrors=(((rerrors || cerrors) || spderrors) || hpderrors) || rfserrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING DENSE SOLVER");
|
|
Print("* REAL: ");
|
|
//--- check
|
|
if(rerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* COMPLEX: ");
|
|
//--- check
|
|
if(cerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* SPD: ");
|
|
//--- check
|
|
if(spderrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* HPD: ");
|
|
//--- check
|
|
if(hpderrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* ITERATIVE IMPROVEMENT: ");
|
|
//--- check
|
|
if(rfserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether solver results are correct solution. |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestDenseSolverUnit::RMatrixCheckSolutionM(CMatrixDouble &xe,
|
|
const int n,const int m,
|
|
const double threshold,
|
|
const int info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixDouble &xs)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
//--- initialization
|
|
result=true;
|
|
//--- check
|
|
if(info<=0)
|
|
result=false;
|
|
else
|
|
{
|
|
//--- calculation
|
|
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
result=result && MathAbs(xe[i][j]-xs[i][j])<=threshold;
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether solver results are correct solution. |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestDenseSolverUnit::RMatrixCheckSolution(CMatrixDouble &xe,
|
|
const int n,
|
|
const double threshold,
|
|
const int info,
|
|
CDenseSolverReport &rep,
|
|
double &xs[])
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble xsm;
|
|
//--- allocation
|
|
xsm.Resize(n,1);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xsm[i_].Set(0,xs[i_]);
|
|
//--- return result
|
|
return(RMatrixCheckSolutionM(xe,n,1,threshold,info,rep,xsm));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether solver results indicate singular matrix. |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestDenseSolverUnit::RMatrixCheckSingularM(const int n,
|
|
const int m,
|
|
const int info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixDouble &xs)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
//--- initialization
|
|
result=true;
|
|
//--- check
|
|
if(info!=-3 && info!=1)
|
|
result=false;
|
|
else
|
|
{
|
|
//--- calculation
|
|
result=result && !(rep.m_r1<0.0 || rep.m_r1>1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon);
|
|
//--- check
|
|
if(info==-3)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
result=result && xs[i][j]==0.0;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether solver results indicate singular matrix. |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestDenseSolverUnit::RMatrixCheckSingular(const int n,
|
|
const int info,
|
|
CDenseSolverReport &rep,
|
|
double &xs[])
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble xsm;
|
|
//--- allocation
|
|
xsm.Resize(n,1);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xsm[i_].Set(0,xs[i_]);
|
|
//--- return result
|
|
return(RMatrixCheckSingularM(n,1,info,rep,xsm));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether solver results are correct solution. |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestDenseSolverUnit::CMatrixCheckSolutionM(CMatrixComplex &xe,
|
|
const int n,const int m,
|
|
const double threshold,
|
|
const int info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixComplex &xs)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
//--- initialization
|
|
result=true;
|
|
//--- check
|
|
if(info<=0)
|
|
result=false;
|
|
else
|
|
{
|
|
//--- calculation
|
|
result=result && !(rep.m_r1<100*CMath::m_machineepsilon || rep.m_r1>1+1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<100*CMath::m_machineepsilon || rep.m_rinf>1+1000*CMath::m_machineepsilon);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
result=result && CMath::AbsComplex(xe[i][j]-xs[i][j])<=threshold;
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether solver results are correct solution. |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestDenseSolverUnit::CMatrixCheckSolution(CMatrixComplex &xe,
|
|
const int n,
|
|
const double threshold,
|
|
const int info,
|
|
CDenseSolverReport &rep,
|
|
complex &xs[])
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixComplex xsm;
|
|
//--- allocation
|
|
xsm.Resize(n,1);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xsm[i_].Set(0,xs[i_]);
|
|
//--- return result
|
|
return(CMatrixCheckSolutionM(xe,n,1,threshold,info,rep,xsm));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether solver results indicate singular matrix. |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestDenseSolverUnit::CMatrixCheckSingularM(const int n,
|
|
const int m,
|
|
const int info,
|
|
CDenseSolverReport &rep,
|
|
CMatrixComplex &xs)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
//--- initialization
|
|
result=true;
|
|
//--- check
|
|
if(info!=-3 && info!=1)
|
|
result=false;
|
|
else
|
|
{
|
|
//--- calculation
|
|
result=result && !(rep.m_r1<0.0 || rep.m_r1>1000*CMath::m_machineepsilon);
|
|
result=result && !(rep.m_rinf<0.0 || rep.m_rinf>1000*CMath::m_machineepsilon);
|
|
//--- check
|
|
if(info==-3)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
result=result && xs[i][j]==0;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks whether solver results indicate singular matrix. |
|
|
//| Returns True on success. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestDenseSolverUnit::CMatrixCheckSingular(const int n,
|
|
const int info,
|
|
CDenseSolverReport &rep,
|
|
complex &xs[])
|
|
{
|
|
//--- create variables
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixComplex xsm;
|
|
//--- allocation
|
|
xsm.Resize(n,1);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xsm[i_].Set(0,xs[i_]);
|
|
//--- return result
|
|
return(CMatrixCheckSingularM(n,1,info,rep,xsm));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDenseSolverUnit::RMatrixMakeACopy(CMatrixDouble &a,
|
|
const int m,
|
|
const int n,
|
|
CMatrixDouble &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDenseSolverUnit::CMatrixMakeACopy(CMatrixComplex &a,
|
|
const int m,
|
|
const int n,
|
|
CMatrixComplex &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Drops upper or lower half of the matrix - fills it by special |
|
|
//| pattern which may be used later to ensure that this part wasn't |
|
|
//| changed |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDenseSolverUnit::RMatrixDropHalf(CMatrixDouble &a,
|
|
const int n,
|
|
const bool droplower)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if((droplower && i>j) || (!droplower && i<j))
|
|
a[i].Set(j,1+2*i+3*j);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Drops upper or lower half of the matrix - fills it by special |
|
|
//| pattern which may be used later to ensure that this part wasn't |
|
|
//| changed |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDenseSolverUnit::CMatrixDropHalf(CMatrixComplex &a,
|
|
const int n,
|
|
const bool droplower)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if((droplower && i>j) || (!droplower && i<j))
|
|
a[i].Set(j,1+2*i+3*j);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Real test |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDenseSolverUnit::TestRSolver(const int maxn,const int maxm,
|
|
const int passcount,
|
|
const double threshold,
|
|
bool &rerrors,bool &rfserrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int m=0;
|
|
int pass=0;
|
|
int taskkind=0;
|
|
double v=0;
|
|
double verr=0;
|
|
int info=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
int p[];
|
|
double bv[];
|
|
double xv[];
|
|
double y[];
|
|
double tx[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble lua;
|
|
CMatrixDouble atmp;
|
|
CMatrixDouble xe;
|
|
CMatrixDouble b;
|
|
CMatrixDouble x;
|
|
//--- objects of classes
|
|
CDenseSolverReport rep;
|
|
CDenseSolverLSReport repls;
|
|
//--- General square matrices:
|
|
//--- * test general solvers
|
|
//--- * test least squares solver
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
for(m=1;m<=maxm;m++)
|
|
{
|
|
//--- ********************************************************
|
|
//--- WELL CONDITIONED TASKS
|
|
//--- ability to find correct solution is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods on original A
|
|
CMatGen::RMatrixRndCond(n,1000,a);
|
|
RMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::RMatrixLU(lua,n,n,p);
|
|
//--- allocation
|
|
xe.Resize(n,m);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
xe[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- allocation
|
|
b.Resize(n,m);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*xe[i_][j];
|
|
b[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- Test solvers
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::RMatrixSolveM(a,n,b,m,CMath::RandomReal()>0.5,info,rep,x);
|
|
//--- search errors
|
|
rerrors=rerrors || !RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::RMatrixSolve(a,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
rerrors=rerrors || !RMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::RMatrixLUSolveM(lua,p,n,b,m,info,rep,x);
|
|
//--- search errors
|
|
rerrors=rerrors || !RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::RMatrixLUSolve(lua,p,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
rerrors=rerrors || !RMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::RMatrixMixedSolveM(a,lua,p,n,b,m,info,rep,x);
|
|
//--- search errors
|
|
rerrors=rerrors || !RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::RMatrixMixedSolve(a,lua,p,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
rerrors=rerrors || !RMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- Test DenseSolverRLS():
|
|
//--- * test on original system A*x=b
|
|
//--- * test on overdetermined system with the same solution: (A' A')'*x=(b' b')'
|
|
//--- * test on underdetermined system with the same solution: (A 0 0 0 ) * z=b
|
|
info=0;
|
|
UnsetLSRep(repls);
|
|
Unset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::RMatrixSolveLS(a,n,n,bv,0.0,info,repls,xv);
|
|
//--- check
|
|
if(info<=0)
|
|
rerrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
rerrors=(rerrors || repls.m_r2<100*CMath::m_machineepsilon) || repls.m_r2>1+1000*CMath::m_machineepsilon;
|
|
rerrors=(rerrors || repls.m_n!=n) || repls.m_k!=0;
|
|
for(i=0;i<=n-1;i++)
|
|
rerrors=rerrors || MathAbs(xe[i][0]-xv[i])>threshold;
|
|
}
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetLSRep(repls);
|
|
Unset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,2*n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- change value
|
|
i1_=-n;
|
|
for(i_=n;i_<=2*n-1;i_++)
|
|
bv[i_]=b[i_+i1_][0];
|
|
//--- allocation
|
|
atmp.Resize(2*n,n);
|
|
//--- function calls
|
|
CBlas::CopyMatrix(a,0,n-1,0,n-1,atmp,0,n-1,0,n-1);
|
|
CBlas::CopyMatrix(a,0,n-1,0,n-1,atmp,n,2*n-1,0,n-1);
|
|
CDenseSolver::RMatrixSolveLS(atmp,2*n,n,bv,0.0,info,repls,xv);
|
|
//--- check
|
|
if(info<=0)
|
|
rerrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
rerrors=(rerrors || repls.m_r2<100*CMath::m_machineepsilon) || repls.m_r2>1+1000*CMath::m_machineepsilon;
|
|
rerrors=(rerrors || repls.m_n!=n) || repls.m_k!=0;
|
|
for(i=0;i<=n-1;i++)
|
|
rerrors=rerrors || MathAbs(xe[i][0]-xv[i])>threshold;
|
|
}
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetLSRep(repls);
|
|
Unset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- allocation
|
|
atmp.Resize(n,2*n);
|
|
CBlas::CopyMatrix(a,0,n-1,0,n-1,atmp,0,n-1,0,n-1);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=n;j<=2*n-1;j++)
|
|
atmp[i].Set(j,0);
|
|
}
|
|
//--- function call
|
|
CDenseSolver::RMatrixSolveLS(atmp,n,2*n,bv,0.0,info,repls,xv);
|
|
//--- check
|
|
if(info<=0)
|
|
rerrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
rerrors=rerrors || repls.m_r2!=0.0;
|
|
rerrors=(rerrors || repls.m_n!=2*n) || repls.m_k!=n;
|
|
for(i=0;i<=n-1;i++)
|
|
rerrors=rerrors || MathAbs(xe[i][0]-xv[i])>threshold;
|
|
for(i=n;i<=2*n-1;i++)
|
|
rerrors=rerrors || MathAbs(xv[i])>threshold;
|
|
}
|
|
//--- ********************************************************
|
|
//--- EXACTLY SINGULAR MATRICES
|
|
//--- ability to detect singularity is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate different types of singular matrices:
|
|
//--- * zero
|
|
//--- * with zero columns
|
|
//--- * with zero rows
|
|
//--- * with equal rows/columns
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods
|
|
//
|
|
for(taskkind=0;taskkind<=4;taskkind++)
|
|
{
|
|
Unset2D(a);
|
|
//--- check
|
|
if(taskkind==0)
|
|
{
|
|
//--- all zeros
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(taskkind==1)
|
|
{
|
|
//--- there is zero column
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(k,0*a[i_][k]);
|
|
}
|
|
//--- check
|
|
if(taskkind==2)
|
|
{
|
|
//--- there is zero row
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[k].Set(i_,0*a[k][i_]);
|
|
}
|
|
//--- check
|
|
if(taskkind==3)
|
|
{
|
|
//--- equal columns
|
|
if(n<2)
|
|
continue;
|
|
//--- change values
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(0,a[i_][k]);
|
|
}
|
|
//--- check
|
|
if(taskkind==4)
|
|
{
|
|
//--- equal rows
|
|
if(n<2)
|
|
continue;
|
|
//--- change values
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[0].Set(i_,a[k][i_]);
|
|
}
|
|
//--- allocation
|
|
xe.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
xe[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- allocation
|
|
b.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*xe[i_][j];
|
|
b[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- function calls
|
|
RMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::RMatrixLU(lua,n,n,p);
|
|
//--- Test RMatrixSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::RMatrixSolveM(a,n,b,m,CMath::RandomReal()>0.5,info,rep,x);
|
|
//--- search errors
|
|
rerrors=rerrors || !RMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test RMatrixSolve()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::RMatrixSolve(a,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
rerrors=rerrors || !RMatrixCheckSingular(n,info,rep,xv);
|
|
//--- Test RMatrixLUSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::RMatrixLUSolveM(lua,p,n,b,m,info,rep,x);
|
|
//--- search errors
|
|
rerrors=rerrors || !RMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test RMatrixLUSolve()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::RMatrixLUSolve(lua,p,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
rerrors=rerrors || !RMatrixCheckSingular(n,info,rep,xv);
|
|
//--- Test RMatrixMixedSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::RMatrixMixedSolveM(a,lua,p,n,b,m,info,rep,x);
|
|
//--- search errors
|
|
rerrors=rerrors || !RMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test RMatrixMixedSolve()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::RMatrixMixedSolve(a,lua,p,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
rerrors=rerrors || !RMatrixCheckSingular(n,info,rep,xv);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- test iterative improvement
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Test iterative improvement matrices
|
|
//--- A matrix/right part are constructed such that both matrix
|
|
//--- and solution components are within (-1,+1). Such matrix/right part
|
|
//--- have nice properties - system can be solved using iterative
|
|
//--- improvement with ||A*x-b|| about several ulps of max(1,||b||).
|
|
n=100;
|
|
a.Resize(n,n);
|
|
b.Resize(n,1);
|
|
ArrayResize(bv,n);
|
|
ArrayResize(tx,n);
|
|
ArrayResize(xv,n);
|
|
ArrayResize(y,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
xv[i]=2*CMath::RandomReal()-1;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
y[i_]=a[i][i_];
|
|
//--- function call
|
|
CXblas::XDot(y,xv,n,tx,v,verr);
|
|
bv[i]=v;
|
|
}
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
b[i_].Set(0,bv[i_]);
|
|
//--- Test RMatrixSolveM()
|
|
Unset2D(x);
|
|
CDenseSolver::RMatrixSolveM(a,n,b,1,true,info,rep,x);
|
|
//--- check
|
|
if(info<=0)
|
|
rfserrors=true;
|
|
else
|
|
{
|
|
//--- allocation
|
|
ArrayResize(xv,n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xv[i_]=x[i_][0];
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
y[i_]=a[i][i_];
|
|
//--- function call
|
|
CXblas::XDot(y,xv,n,tx,v,verr);
|
|
//--- search errors
|
|
rfserrors=rfserrors || MathAbs(v-b[i][0])>8*CMath::m_machineepsilon*MathMax(1,MathAbs(b[i][0]));
|
|
}
|
|
}
|
|
//--- Test RMatrixSolve()
|
|
Unset1D(xv);
|
|
CDenseSolver::RMatrixSolve(a,n,bv,info,rep,xv);
|
|
//--- check
|
|
if(info<=0)
|
|
rfserrors=true;
|
|
else
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
y[i_]=a[i][i_];
|
|
//--- function call
|
|
CXblas::XDot(y,xv,n,tx,v,verr);
|
|
//--- search errors
|
|
rfserrors=rfserrors || MathAbs(v-bv[i])>8*CMath::m_machineepsilon*MathMax(1,MathAbs(bv[i]));
|
|
}
|
|
}
|
|
//--- Test LS-solver on the same matrix
|
|
CDenseSolver::RMatrixSolveLS(a,n,n,bv,0.0,info,repls,xv);
|
|
//--- check
|
|
if(info<=0)
|
|
rfserrors=true;
|
|
else
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
y[i_]=a[i][i_];
|
|
//--- function call
|
|
CXblas::XDot(y,xv,n,tx,v,verr);
|
|
//--- search errors
|
|
rfserrors=rfserrors || MathAbs(v-bv[i])>8*CMath::m_machineepsilon*MathMax(1,MathAbs(bv[i]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| SPD test |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDenseSolverUnit::TestSPDSolver(const int maxn,const int maxm,
|
|
const int passcount,
|
|
const double threshold,
|
|
bool &spderrors,bool &rfserrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int m=0;
|
|
int pass=0;
|
|
int taskkind=0;
|
|
double v=0;
|
|
bool isupper;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
int p[];
|
|
double bv[];
|
|
double xv[];
|
|
double y[];
|
|
double tx[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble cha;
|
|
CMatrixDouble atmp;
|
|
CMatrixDouble xe;
|
|
CMatrixDouble b;
|
|
CMatrixDouble x;
|
|
//--- objects of class
|
|
CDenseSolverReport rep;
|
|
CDenseSolverLSReport repls;
|
|
//--- General square matrices:
|
|
//--- * test general solvers
|
|
//--- * test least squares solver
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
for(m=1;m<=maxm;m++)
|
|
{
|
|
//--- ********************************************************
|
|
//--- WELL CONDITIONED TASKS
|
|
//--- ability to find correct solution is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods on original A
|
|
isupper=CMath::RandomReal()>0.5;
|
|
CMatGen::SPDMatrixRndCond(n,1000,a);
|
|
RMatrixMakeACopy(a,n,n,cha);
|
|
//--- check
|
|
if(!CTrFac::SPDMatrixCholesky(cha,n,isupper))
|
|
{
|
|
spderrors=true;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
xe.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
xe[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- allocation
|
|
b.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*xe[i_][j];
|
|
b[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- function calls
|
|
RMatrixDropHalf(a,n,isupper);
|
|
RMatrixDropHalf(cha,n,isupper);
|
|
//--- Test solvers
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::SPDMatrixSolveM(a,n,isupper,b,m,info,rep,x);
|
|
//--- search errors
|
|
spderrors=spderrors || !RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::SPDMatrixSolve(a,n,isupper,bv,info,rep,xv);
|
|
//--- search errors
|
|
spderrors=spderrors || !RMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::SPDMatrixCholeskySolveM(cha,n,isupper,b,m,info,rep,x);
|
|
//--- search errors
|
|
spderrors=spderrors || !RMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change value
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::SPDMatrixCholeskySolve(cha,n,isupper,bv,info,rep,xv);
|
|
//--- search errors
|
|
spderrors=spderrors || !RMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- ********************************************************
|
|
//--- EXACTLY SINGULAR MATRICES
|
|
//--- ability to detect singularity is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate different types of singular matrices:
|
|
//--- * zero
|
|
//--- * with zero columns
|
|
//--- * with zero rows
|
|
//--- * with equal rows/columns
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods
|
|
for(taskkind=0;taskkind<=3;taskkind++)
|
|
{
|
|
Unset2D(a);
|
|
//--- check
|
|
if(taskkind==0)
|
|
{
|
|
//--- all zeros
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(taskkind==1)
|
|
{
|
|
//--- there is zero column
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i;j<=n-1;j++)
|
|
{
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
a[j].Set(i,a[i][j]);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(k,0*a[i_][k]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[k].Set(i_,0*a[k][i_]);
|
|
}
|
|
//--- check
|
|
if(taskkind==2)
|
|
{
|
|
//--- there is zero row
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i;j<=n-1;j++)
|
|
{
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
a[j].Set(i,a[i][j]);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[k].Set(i_,0*a[k][i_]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(k,0*a[i_][k]);
|
|
}
|
|
//--- check
|
|
if(taskkind==3)
|
|
{
|
|
//--- equal columns/rows
|
|
if(n<2)
|
|
continue;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i;j<=n-1;j++)
|
|
{
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
a[j].Set(i,a[i][j]);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(0,a[i_][k]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[0].Set(i_,a[k][i_]);
|
|
}
|
|
//--- allocation
|
|
xe.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
xe[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- allocation
|
|
b.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*xe[i_][j];
|
|
b[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- function calls
|
|
RMatrixMakeACopy(a,n,n,cha);
|
|
RMatrixDropHalf(a,n,isupper);
|
|
RMatrixDropHalf(cha,n,isupper);
|
|
//--- Test SPDMatrixSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::SPDMatrixSolveM(a,n,isupper,b,m,info,rep,x);
|
|
//--- search errors
|
|
spderrors=spderrors || !RMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test SPDMatrixSolve()
|
|
info=0;
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::SPDMatrixSolve(a,n,isupper,bv,info,rep,xv);
|
|
//--- search errors
|
|
spderrors=spderrors || !RMatrixCheckSingular(n,info,rep,xv);
|
|
//--- 'equal columns/rows' are degenerate,but
|
|
//--- Cholesky matrix with equal columns/rows IS NOT degenerate,
|
|
//--- so it is not used for testing purposes.
|
|
if(taskkind!=3)
|
|
{
|
|
//--- Test SPDMatrixLUSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
CDenseSolver::SPDMatrixCholeskySolveM(cha,n,isupper,b,m,info,rep,x);
|
|
//--- search errors
|
|
spderrors=spderrors || !RMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test SPDMatrixLUSolve()
|
|
info=0;
|
|
UnsetRep(rep);
|
|
Unset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::SPDMatrixCholeskySolve(cha,n,isupper,bv,info,rep,xv);
|
|
//--- search errors
|
|
spderrors=spderrors || !RMatrixCheckSingular(n,info,rep,xv);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Real test |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDenseSolverUnit::TestCSolver(const int maxn,const int maxm,
|
|
const int passcount,
|
|
const double threshold,
|
|
bool &cerrors,bool &rfserrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int m=0;
|
|
int pass=0;
|
|
int taskkind=0;
|
|
double verr=0;
|
|
complex v=0;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
int p[];
|
|
complex bv[];
|
|
complex xv[];
|
|
complex y[];
|
|
double tx[];
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex lua;
|
|
CMatrixComplex atmp;
|
|
CMatrixComplex xe;
|
|
CMatrixComplex b;
|
|
CMatrixComplex x;
|
|
//--- objects of classes
|
|
CDenseSolverReport rep;
|
|
CDenseSolverLSReport repls;
|
|
//--- General square matrices:
|
|
//--- * test general solvers
|
|
//--- * test least squares solver
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
for(m=1;m<=maxm;m++)
|
|
{
|
|
//--- ********************************************************
|
|
//--- WELL CONDITIONED TASKS
|
|
//--- ability to find correct solution is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods on original A
|
|
CMatGen::CMatrixRndCond(n,1000,a);
|
|
CMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::CMatrixLU(lua,n,n,p);
|
|
//--- allocation
|
|
xe.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
xe[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
xe[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- allocation
|
|
b.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*xe[i_][j];
|
|
b[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- Test solvers
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::CMatrixSolveM(a,n,b,m,CMath::RandomReal()>0.5,info,rep,x);
|
|
//--- search errors
|
|
cerrors=cerrors || !CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change values
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::CMatrixSolve(a,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
cerrors=cerrors || !CMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- change values
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::CMatrixLUSolveM(lua,p,n,b,m,info,rep,x);
|
|
//--- search errors
|
|
cerrors=cerrors || !CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change values
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::CMatrixLUSolve(lua,p,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
cerrors=cerrors || !CMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- change values
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::CMatrixMixedSolveM(a,lua,p,n,b,m,info,rep,x);
|
|
//--- search errors
|
|
cerrors=cerrors || !CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change values
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::CMatrixMixedSolve(a,lua,p,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
cerrors=cerrors || !CMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- ********************************************************
|
|
//--- EXACTLY SINGULAR MATRICES
|
|
//--- ability to detect singularity is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate different types of singular matrices:
|
|
//--- * zero
|
|
//--- * with zero columns
|
|
//--- * with zero rows
|
|
//--- * with equal rows/columns
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods
|
|
for(taskkind=0;taskkind<=4;taskkind++)
|
|
{
|
|
CUnset2D(a);
|
|
//--- check
|
|
if(taskkind==0)
|
|
{
|
|
//--- all zeros
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(taskkind==1)
|
|
{
|
|
//--- there is zero column
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(k,a[i_][k]*0);
|
|
}
|
|
//--- check
|
|
if(taskkind==2)
|
|
{
|
|
//--- there is zero row
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[k].Set(i_,a[k][i_]*0);
|
|
}
|
|
//--- check
|
|
if(taskkind==3)
|
|
{
|
|
//--- equal columns
|
|
if(n<2)
|
|
continue;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(0,a[i_][k]);
|
|
}
|
|
//--- check
|
|
if(taskkind==4)
|
|
{
|
|
//--- equal rows
|
|
if(n<2)
|
|
continue;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[0].Set(i_,a[k][i_]);
|
|
}
|
|
//--- allocation
|
|
xe.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
xe[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- allocation
|
|
b.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*xe[i_][j];
|
|
b[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- function calls
|
|
CMatrixMakeACopy(a,n,n,lua);
|
|
CTrFac::CMatrixLU(lua,n,n,p);
|
|
//--- Test CMatrixSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::CMatrixSolveM(a,n,b,m,CMath::RandomReal()>0.5,info,rep,x);
|
|
//--- search errors
|
|
cerrors=cerrors || !CMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test CMatrixSolve()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::CMatrixSolve(a,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
cerrors=cerrors || !CMatrixCheckSingular(n,info,rep,xv);
|
|
//--- Test CMatrixLUSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::CMatrixLUSolveM(lua,p,n,b,m,info,rep,x);
|
|
//--- search errors
|
|
cerrors=cerrors || !CMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test CMatrixLUSolve()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::CMatrixLUSolve(lua,p,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
cerrors=cerrors || !CMatrixCheckSingular(n,info,rep,xv);
|
|
//--- Test CMatrixMixedSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::CMatrixMixedSolveM(a,lua,p,n,b,m,info,rep,x);
|
|
//--- search errors
|
|
cerrors=cerrors || !CMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test CMatrixMixedSolve()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::CMatrixMixedSolve(a,lua,p,n,bv,info,rep,xv);
|
|
//--- search errors
|
|
cerrors=cerrors || !CMatrixCheckSingular(n,info,rep,xv);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- test iterative improvement
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Test iterative improvement matrices
|
|
//--- A matrix/right part are constructed such that both matrix
|
|
//--- and solution components magnitudes are within (-1,+1).
|
|
//--- Such matrix/right part have nice properties - system can
|
|
//--- be solved using iterative improvement with ||A*x-b|| about
|
|
//--- several ulps of max(1,||b||).
|
|
n=100;
|
|
a.Resize(n,n);
|
|
b.Resize(n,1);
|
|
ArrayResize(bv,n);
|
|
ArrayResize(tx,2*n);
|
|
ArrayResize(xv,n);
|
|
ArrayResize(y,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
xv[i].re=2*CMath::RandomReal()-1;
|
|
xv[i].im=2*CMath::RandomReal()-1;
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
for(i_=0;i_<=n-1;i_++)
|
|
y[i_]=a[i][i_];
|
|
//--- function call
|
|
CXblas::XCDot(y,xv,n,tx,v,verr);
|
|
bv[i]=v;
|
|
}
|
|
for(i_=0;i_<=n-1;i_++)
|
|
b[i_].Set(0,bv[i_]);
|
|
//--- Test CMatrixSolveM()
|
|
CUnset2D(x);
|
|
CDenseSolver::CMatrixSolveM(a,n,b,1,true,info,rep,x);
|
|
//--- check
|
|
if(info<=0)
|
|
rfserrors=true;
|
|
else
|
|
{
|
|
//--- allocation
|
|
ArrayResize(xv,n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xv[i_]=x[i_][0];
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
y[i_]=a[i][i_];
|
|
//--- function call
|
|
CXblas::XCDot(y,xv,n,tx,v,verr);
|
|
//--- search errors
|
|
rfserrors=rfserrors || CMath::AbsComplex(v-b[i][0])>8*CMath::m_machineepsilon*MathMax(1,CMath::AbsComplex(b[i][0]));
|
|
}
|
|
}
|
|
//--- Test CMatrixSolve()
|
|
CUnset1D(xv);
|
|
CDenseSolver::CMatrixSolve(a,n,bv,info,rep,xv);
|
|
//--- check
|
|
if(info<=0)
|
|
rfserrors=true;
|
|
else
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
y[i_]=a[i][i_];
|
|
//--- function call
|
|
CXblas::XCDot(y,xv,n,tx,v,verr);
|
|
//--- search errors
|
|
rfserrors=rfserrors || CMath::AbsComplex(v-bv[i])>8*CMath::m_machineepsilon*MathMax(1,CMath::AbsComplex(bv[i]));
|
|
}
|
|
}
|
|
//--- TODO: Test LS-solver on the same matrix
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| HPD test |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDenseSolverUnit::TestHPDSolver(const int maxn,const int maxm,
|
|
const int passcount,
|
|
const double threshold,
|
|
bool &hpderrors,bool &rfserrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int m=0;
|
|
int pass=0;
|
|
int taskkind=0;
|
|
complex v=0;
|
|
bool isupper;
|
|
int info=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
int p[];
|
|
complex bv[];
|
|
complex xv[];
|
|
complex y[];
|
|
complex tx[];
|
|
//--- create matrix
|
|
CMatrixComplex a;
|
|
CMatrixComplex cha;
|
|
CMatrixComplex atmp;
|
|
CMatrixComplex xe;
|
|
CMatrixComplex b;
|
|
CMatrixComplex x;
|
|
//--- objects of classes
|
|
CDenseSolverReport rep;
|
|
CDenseSolverLSReport repls;
|
|
//--- General square matrices:
|
|
//--- * test general solvers
|
|
//--- * test least squares solver
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
for(m=1;m<=maxm;m++)
|
|
{
|
|
//--- ********************************************************
|
|
//--- WELL CONDITIONED TASKS
|
|
//--- ability to find correct solution is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate random well conditioned matrix A.
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods on original A
|
|
isupper=CMath::RandomReal()>0.5;
|
|
CMatGen::HPDMatrixRndCond(n,1000,a);
|
|
CMatrixMakeACopy(a,n,n,cha);
|
|
//--- check
|
|
if(!CTrFac::HPDMatrixCholesky(cha,n,isupper))
|
|
{
|
|
hpderrors=true;
|
|
return;
|
|
}
|
|
//--- allocation
|
|
xe.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
xe[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
xe[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
//--- allocation
|
|
b.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*xe[i_][j];
|
|
b[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- function calls
|
|
CMatrixDropHalf(a,n,isupper);
|
|
CMatrixDropHalf(cha,n,isupper);
|
|
//--- Test solvers
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::HPDMatrixSolveM(a,n,isupper,b,m,info,rep,x);
|
|
//--- search errors
|
|
hpderrors=hpderrors || !CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change values
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::HPDMatrixSolve(a,n,isupper,bv,info,rep,xv);
|
|
//--- search errors
|
|
hpderrors=hpderrors || !CMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- change values
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::HPDMatrixCholeskySolveM(cha,n,isupper,b,m,info,rep,x);
|
|
//--- search errors
|
|
hpderrors=hpderrors || !CMatrixCheckSolutionM(xe,n,m,threshold,info,rep,x);
|
|
//--- change values
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset1D(xv);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::HPDMatrixCholeskySolve(cha,n,isupper,bv,info,rep,xv);
|
|
//--- search errors
|
|
hpderrors=hpderrors || !CMatrixCheckSolution(xe,n,threshold,info,rep,xv);
|
|
//--- ********************************************************
|
|
//--- EXACTLY SINGULAR MATRICES
|
|
//--- ability to detect singularity is tested
|
|
//--- ********************************************************
|
|
//--- 1. generate different types of singular matrices:
|
|
//--- * zero
|
|
//--- * with zero columns
|
|
//--- * with zero rows
|
|
//--- * with equal rows/columns
|
|
//--- 2. generate random solution vector xe
|
|
//--- 3. generate right part b=A*xe
|
|
//--- 4. test different methods
|
|
for(taskkind=0;taskkind<=3;taskkind++)
|
|
{
|
|
CUnset2D(a);
|
|
//--- check
|
|
if(taskkind==0)
|
|
{
|
|
//--- all zeros
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- check
|
|
if(taskkind==1)
|
|
{
|
|
//--- there is zero column
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i;j<=n-1;j++)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
//--- check
|
|
if(i==j)
|
|
a[i].SetIm(j,0);
|
|
a[j].Set(i,a[i][j]);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(k,a[i_][k]*0);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[k].Set(i_,a[k][i_]*0);
|
|
}
|
|
//--- check
|
|
if(taskkind==2)
|
|
{
|
|
//--- there is zero row
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i;j<=n-1;j++)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
//--- check
|
|
if(i==j)
|
|
a[i].SetIm(j,0);
|
|
a[j].Set(i,a[i][j]);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=CMath::RandomInteger(n);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[k].Set(i_,a[k][i_]*0);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(k,a[i_][k]*0);
|
|
}
|
|
//--- check
|
|
if(taskkind==3)
|
|
{
|
|
//--- equal columns/rows
|
|
if(n<2)
|
|
continue;
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i;j<=n-1;j++)
|
|
{
|
|
a[i].SetRe(j,2*CMath::RandomReal()-1);
|
|
a[i].SetIm(j,2*CMath::RandomReal()-1);
|
|
//--- check
|
|
if(i==j)
|
|
a[i].SetIm(j,0);
|
|
a[j].Set(i,a[i][j]);
|
|
}
|
|
}
|
|
//--- change values
|
|
k=1+CMath::RandomInteger(n-1);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(0,a[i_][k]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[0].Set(i_,a[k][i_]);
|
|
}
|
|
//--- allocation
|
|
xe.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
xe[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- allocation
|
|
b.Resize(n,m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*xe[i_][j];
|
|
b[i].Set(j,v);
|
|
}
|
|
}
|
|
//--- function calls
|
|
CMatrixMakeACopy(a,n,n,cha);
|
|
CMatrixDropHalf(a,n,isupper);
|
|
CMatrixDropHalf(cha,n,isupper);
|
|
//--- Test SPDMatrixSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::HPDMatrixSolveM(a,n,isupper,b,m,info,rep,x);
|
|
//--- search errors
|
|
hpderrors=hpderrors || !CMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test SPDMatrixSolve()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::HPDMatrixSolve(a,n,isupper,bv,info,rep,xv);
|
|
//--- search errors
|
|
hpderrors=hpderrors || !CMatrixCheckSingular(n,info,rep,xv);
|
|
//--- 'equal columns/rows' are degenerate,but
|
|
//--- Cholesky matrix with equal columns/rows IS NOT degenerate,
|
|
//--- so it is not used for testing purposes.
|
|
if(taskkind!=3)
|
|
{
|
|
//--- Test SPDMatrixLUSolveM()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
CDenseSolver::HPDMatrixCholeskySolveM(cha,n,isupper,b,m,info,rep,x);
|
|
//--- search errors
|
|
hpderrors=hpderrors || !CMatrixCheckSingularM(n,m,info,rep,x);
|
|
//--- Test SPDMatrixLUSolve()
|
|
info=0;
|
|
//--- function calls
|
|
UnsetRep(rep);
|
|
CUnset2D(x);
|
|
//--- allocation
|
|
ArrayResize(bv,n);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
bv[i_]=b[i_][0];
|
|
//--- function call
|
|
CDenseSolver::HPDMatrixCholeskySolve(cha,n,isupper,bv,info,rep,xv);
|
|
//--- search errors
|
|
hpderrors=hpderrors || !CMatrixCheckSingular(n,info,rep,xv);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real matrix |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDenseSolverUnit::Unset2D(CMatrixDouble &x)
|
|
{
|
|
//--- allocation
|
|
x.Resize(1,1);
|
|
//--- change value
|
|
x[0].Set(0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real vector |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDenseSolverUnit::Unset1D(double &x[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
//--- change value
|
|
x[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real matrix |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDenseSolverUnit::CUnset2D(CMatrixComplex &x)
|
|
{
|
|
//--- allocation
|
|
x.Resize(1,1);
|
|
//--- change value
|
|
x[0].Set(0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real vector |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDenseSolverUnit::CUnset1D(complex &x[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
//--- change value
|
|
x[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets report |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDenseSolverUnit::UnsetRep(CDenseSolverReport &r)
|
|
{
|
|
//--- change values
|
|
r.m_r1=-1;
|
|
r.m_rinf=-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets report |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestDenseSolverUnit::UnsetLSRep(CDenseSolverLSReport &r)
|
|
{
|
|
//--- change values
|
|
r.m_r2=-1;
|
|
r.m_n=-1;
|
|
r.m_k=-1;
|
|
//--- function call
|
|
Unset2D(r.m_cx);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLinMin |
|
|
//+------------------------------------------------------------------+
|
|
class CTestLinMinUnit
|
|
{
|
|
public:
|
|
static bool TestLinMin(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
//--- initialization
|
|
waserrors=false;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING LINMIN");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMinCG |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMinCGUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void TestFunc1(CMinCGState &state);
|
|
static void TestFunc2(CMinCGState &state);
|
|
static void TestFunc3(CMinCGState &state);
|
|
static void CalcIIP2(CMinCGState &state,const int n);
|
|
static void CalcLowRank(CMinCGState &state,const int n,const int vcnt,double &d[],CMatrixDouble &v,double &vd[],double &x0[]);
|
|
static void TestPreconditioning(bool &err);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestMinCGUnit(void);
|
|
~CTestMinCGUnit(void);
|
|
//--- public methods
|
|
static bool TestMinCG(const bool silent);
|
|
static void TestOther(bool &err);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestMinCGUnit::CTestMinCGUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestMinCGUnit::~CTestMinCGUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMinCG |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMinCGUnit::TestMinCG(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool referror;
|
|
bool eqerror;
|
|
bool linerror1;
|
|
bool linerror2;
|
|
bool restartserror;
|
|
bool precerror;
|
|
bool converror;
|
|
bool othererrors;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int cgtype=0;
|
|
int difftype=0;
|
|
double diffstep=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double xe[];
|
|
double b[];
|
|
double xlast[];
|
|
double diagh[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- objects of classes
|
|
CMinCGState state;
|
|
CMinCGReport rep;
|
|
//--- initialization
|
|
waserrors=false;
|
|
referror=false;
|
|
linerror1=false;
|
|
linerror2=false;
|
|
eqerror=false;
|
|
converror=false;
|
|
restartserror=false;
|
|
othererrors=false;
|
|
precerror=false;
|
|
//--- function calls
|
|
TestPreconditioning(precerror);
|
|
TestOther(othererrors);
|
|
//--- calculation
|
|
for(difftype=0;difftype<=1;difftype++)
|
|
{
|
|
for(cgtype=-1;cgtype<=1;cgtype++)
|
|
{
|
|
//--- Reference problem
|
|
ArrayResize(x,3);
|
|
//--- change values
|
|
n=3;
|
|
diffstep=1.0E-6;
|
|
x[0]=100*CMath::RandomReal()-50;
|
|
x[1]=100*CMath::RandomReal()-50;
|
|
x[2]=100*CMath::RandomReal()-50;
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function call
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(state.m_x[0]-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_g[0]=2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2]);
|
|
state.m_g[1]=2*state.m_x[1];
|
|
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
referror=(((referror || rep.m_terminationtype<=0) || MathAbs(x[0]-2)>0.001) || MathAbs(x[1])>0.001) || MathAbs(x[2]-2)>0.001;
|
|
//--- F2 problem with restarts:
|
|
//--- * make several iterations and restart BEFORE termination
|
|
//--- * iterate and restart AFTER termination
|
|
//--- NOTE: step is bounded from above to avoid premature convergence
|
|
ArrayResize(x,3);
|
|
//--- change values
|
|
n=3;
|
|
diffstep=1.0E-6;
|
|
x[0]=10+10*CMath::RandomReal();
|
|
x[1]=10+10*CMath::RandomReal();
|
|
x[2]=10+10*CMath::RandomReal();
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function calls
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
CMinCG::MinCGSetStpMax(state,0.1);
|
|
CMinCG::MinCGSetCond(state,0.0000001,0.0,0.0,0);
|
|
//--- calculation
|
|
for(i=0;i<=10;i++)
|
|
{
|
|
//--- check
|
|
if(!CMinCG::MinCGIteration(state))
|
|
break;
|
|
//--- function call
|
|
TestFunc2(state);
|
|
}
|
|
//--- change values
|
|
x[0]=10+10*CMath::RandomReal();
|
|
x[1]=10+10*CMath::RandomReal();
|
|
x[2]=10+10*CMath::RandomReal();
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
TestFunc2(state);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
restartserror=(((restartserror || rep.m_terminationtype<=0) || MathAbs(x[0]-MathLog(2))>0.01) || MathAbs(x[1])>0.01) || MathAbs(x[2]-MathLog(2))>0.01;
|
|
//--- change values
|
|
x[0]=10+10*CMath::RandomReal();
|
|
x[1]=10+10*CMath::RandomReal();
|
|
x[2]=10+10*CMath::RandomReal();
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
TestFunc2(state);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
restartserror=(((restartserror || rep.m_terminationtype<=0) || MathAbs(x[0]-MathLog(2))>0.01) || MathAbs(x[1])>0.01) || MathAbs(x[2]-MathLog(2))>0.01;
|
|
//--- 1D problem #1
|
|
ArrayResize(x,1);
|
|
//--- change values
|
|
n=1;
|
|
diffstep=1.0E-6;
|
|
x[0]=100*CMath::RandomReal()-50;
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function call
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=-MathCos(state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g[0]=MathSin(state.m_x[0]);
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
linerror1=(linerror1 || rep.m_terminationtype<=0) || MathAbs(x[0]/M_PI-(int)MathRound(x[0]/M_PI))>0.001;
|
|
//--- 1D problem #2
|
|
ArrayResize(x,1);
|
|
//--- change values
|
|
n=1;
|
|
diffstep=1.0E-6;
|
|
x[0]=100*CMath::RandomReal()-50;
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function call
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(state.m_x[0])/(1+CMath::Sqr(state.m_x[0]));
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g[0]=(2*state.m_x[0]*(1+CMath::Sqr(state.m_x[0]))-CMath::Sqr(state.m_x[0])*2*state.m_x[0])/CMath::Sqr(1+CMath::Sqr(state.m_x[0]));
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
linerror2=(linerror2 || rep.m_terminationtype<=0) || MathAbs(x[0])>0.001;
|
|
//--- Linear equations
|
|
diffstep=1.0E-6;
|
|
for(n=1;n<=10;n++)
|
|
{
|
|
//--- Prepare task
|
|
a.Resize(n,n);
|
|
ArrayResize(x,n);
|
|
ArrayResize(xe,n);
|
|
ArrayResize(b,n);
|
|
for(i=0;i<=n-1;i++)
|
|
xe[i]=2*CMath::RandomReal()-1;
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
a[i].Set(i,a[i][i]+3*MathSign(a[i][i]));
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*xe[i_];
|
|
b[i]=v;
|
|
}
|
|
//--- Solve task
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function call
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_g[i]=0;
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*state.m_x[i_];
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=state.m_f+CMath::Sqr(v-b[i]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
state.m_g[j]=state.m_g[j]+2*(v-b[i])*a[i][j];
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
eqerror=eqerror || rep.m_terminationtype<=0;
|
|
for(i=0;i<=n-1;i++)
|
|
eqerror=eqerror || MathAbs(x[i]-xe[i])>0.001;
|
|
}
|
|
//--- Testing convergence properties
|
|
diffstep=1.0E-6;
|
|
n=3;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=6*CMath::RandomReal()-3;
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function calls
|
|
CMinCG::MinCGSetCond(state,0.001,0.0,0.0,0);
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
TestFunc3(state);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
converror=converror || rep.m_terminationtype!=4;
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=6*CMath::RandomReal()-3;
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function calls
|
|
CMinCG::MinCGSetCond(state,0.0,0.001,0.0,0);
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
TestFunc3(state);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
converror=converror || rep.m_terminationtype!=1;
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=6*CMath::RandomReal()-3;
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function calls
|
|
CMinCG::MinCGSetCond(state,0.0,0.0,0.001,0);
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
TestFunc3(state);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
converror=converror || rep.m_terminationtype!=2;
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(difftype==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(difftype==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function calls
|
|
CMinCG::MinCGSetCond(state,0.0,0.0,0.0,10);
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
TestFunc3(state);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
converror=converror || !((rep.m_terminationtype==5 && rep.m_iterationscount==10) || rep.m_terminationtype==7);
|
|
}
|
|
}
|
|
//--- end
|
|
waserrors=((((((referror || eqerror) || linerror1) || linerror2) || converror) || othererrors) || restartserror) || precerror;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING CG OPTIMIZATION");
|
|
Print("REFERENCE PROBLEM: ");
|
|
//--- check
|
|
if(referror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("LIN-1 PROBLEM: ");
|
|
//--- check
|
|
if(linerror1)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("LIN-2 PROBLEM: ");
|
|
//--- check
|
|
if(linerror2)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("LINEAR EQUATIONS: ");
|
|
//--- check
|
|
if(eqerror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("RESTARTS: ");
|
|
//--- check
|
|
if(restartserror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("PRECONDITIONING: ");
|
|
//--- check
|
|
if(precerror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("CONVERGENCE PROPERTIES: ");
|
|
//--- check
|
|
if(converror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("OTHER PROPERTIES: ");
|
|
//--- check
|
|
if(othererrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Other properties |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinCGUnit::TestOther(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
double fprev=0;
|
|
double xprev=0;
|
|
double stpmax=0;
|
|
int i=0;
|
|
int cgtype=0;
|
|
double tmpeps=0;
|
|
double epsg=0;
|
|
double v=0;
|
|
double r=0;
|
|
bool hasxlast;
|
|
double lastscaledstep=0;
|
|
int pkind=0;
|
|
int ckind=0;
|
|
int mkind=0;
|
|
int dkind=0;
|
|
double diffstep=0;
|
|
double vc=0;
|
|
double vm=0;
|
|
bool wasf;
|
|
bool wasfg;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double s[];
|
|
double a[];
|
|
double h[];
|
|
double xlast[];
|
|
//--- objects of classes
|
|
CMinCGState state;
|
|
CMinCGReport rep;
|
|
//--- calculation
|
|
for(cgtype=-1;cgtype<=1;cgtype++)
|
|
{
|
|
//--- Test reports (F should form monotone sequence)
|
|
n=50;
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=1;
|
|
//--- function calls
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
CMinCG::MinCGSetCond(state,0,0,0,100);
|
|
CMinCG::MinCGSetXRep(state,true);
|
|
fprev=CMath::m_maxrealnumber;
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]);
|
|
state.m_g[i]=2*(1+i)*state.m_x[i];
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
err=err || state.m_f>fprev;
|
|
//--- check
|
|
if(fprev==CMath::m_maxrealnumber)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
err=err || state.m_x[i]!=x[i];
|
|
}
|
|
//--- change values
|
|
fprev=state.m_f;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xlast[i_]=state.m_x[i_];
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
err=err || x[i]!=xlast[i];
|
|
//--- Test differentiation vs. analytic gradient
|
|
//--- (first one issues NeedF requests,second one issues NeedFG requests)
|
|
n=50;
|
|
diffstep=1.0E-6;
|
|
for(dkind=0;dkind<=1;dkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=1;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinCG::MinCGCreateF(n,x,diffstep,state);
|
|
//--- function call
|
|
CMinCG::MinCGSetCond(state,0,0,0,n/2);
|
|
//--- change values
|
|
wasf=false;
|
|
wasfg=false;
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g[i]=2*(1+i)*state.m_x[i];
|
|
}
|
|
//--- search errors
|
|
wasf=wasf || state.m_needf;
|
|
wasfg=wasfg || state.m_needfg;
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- check
|
|
if(dkind==0)
|
|
err=(err || wasf) || !wasfg;
|
|
//--- check
|
|
if(dkind==1)
|
|
err=(err || !wasf) || wasfg;
|
|
}
|
|
//--- Test that numerical differentiation uses scaling.
|
|
//--- In order to test that we solve simple optimization
|
|
//--- problem: min(x^2) with initial x equal to 0.0.
|
|
//--- We choose random DiffStep and S,then we check that
|
|
//--- optimizer evaluates function at +-DiffStep*S only.
|
|
ArrayResize(x,1);
|
|
ArrayResize(s,1);
|
|
diffstep=CMath::RandomReal()*1.0E-6;
|
|
s[0]=MathExp(CMath::RandomReal()*4-2);
|
|
x[0]=0;
|
|
//--- function calls
|
|
CMinCG::MinCGCreateF(1,x,diffstep,state);
|
|
CMinCG::MinCGSetCond(state,1.0E-6,0,0,0);
|
|
CMinCG::MinCGSetScale(state,s);
|
|
v=0;
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
state.m_f=CMath::Sqr(state.m_x[0]);
|
|
v=MathMax(v,MathAbs(state.m_x[0]));
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
r=v/(s[0]*diffstep);
|
|
//--- search errors
|
|
err=err || MathAbs(MathLog(r))>MathLog(1+1000*CMath::m_machineepsilon);
|
|
//--- Test maximum step
|
|
n=1;
|
|
ArrayResize(x,n);
|
|
x[0]=100;
|
|
stpmax=0.05+0.05*CMath::RandomReal();
|
|
//--- function calls
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
CMinCG::MinCGSetCond(state,1.0E-9,0,0,0);
|
|
CMinCG::MinCGSetStpMax(state,stpmax);
|
|
CMinCG::MinCGSetXRep(state,true);
|
|
xprev=x[0];
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]);
|
|
state.m_g[0]=MathExp(state.m_x[0])-MathExp(-state.m_x[0]);
|
|
//--- search errors
|
|
err=err || MathAbs(state.m_x[0]-xprev)>(1+MathSqrt(CMath::m_machineepsilon))*stpmax;
|
|
}
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
//--- search errors
|
|
err=err || MathAbs(state.m_x[0]-xprev)>(1+MathSqrt(CMath::m_machineepsilon))*stpmax;
|
|
xprev=state.m_x[0];
|
|
}
|
|
}
|
|
//--- Test correctness of the scaling:
|
|
//--- * initial point is random point from [+1,+2]^N
|
|
//--- * f(x)=SUM(A[i]*x[i]^4),C[i] is random from [0.01,100]
|
|
//--- * we use random scaling matrix
|
|
//--- * we test different variants of the preconditioning:
|
|
//--- 0) unit preconditioner
|
|
//--- 1) random diagonal from [0.01,100]
|
|
//--- 2) scale preconditioner
|
|
//--- * we set stringent stopping conditions (we try EpsG and EpsX)
|
|
//--- * and we test that in the extremum stopping conditions are
|
|
//--- satisfied subject to the current scaling coefficients.
|
|
tmpeps=1.0E-10;
|
|
for(n=1;n<=10;n++)
|
|
{
|
|
for(pkind=0;pkind<=2;pkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
ArrayResize(a,n);
|
|
ArrayResize(s,n);
|
|
ArrayResize(h,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=CMath::RandomReal()+1;
|
|
a[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
s[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
h[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
}
|
|
//--- function calls
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
CMinCG::MinCGSetScale(state,s);
|
|
CMinCG::MinCGSetXRep(state,true);
|
|
//--- check
|
|
if(pkind==1)
|
|
CMinCG::MinCGSetPrecDiag(state,h);
|
|
//--- check
|
|
if(pkind==2)
|
|
CMinCG::MinCGSetPrecScale(state);
|
|
//--- Test gradient-based stopping condition
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=CMath::RandomReal()+1;
|
|
//--- function calls
|
|
CMinCG::MinCGSetCond(state,tmpeps,0,0,0);
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_f=state.m_f+a[i]*MathPow(state.m_x[i],4);
|
|
state.m_g[i]=4*a[i]*MathPow(state.m_x[i],3);
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- change value
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+CMath::Sqr(s[i]*4*a[i]*MathPow(x[i],3));
|
|
v=MathSqrt(v);
|
|
//--- search errors
|
|
err=err || v>tmpeps;
|
|
//--- Test step-based stopping condition
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=CMath::RandomReal()+1;
|
|
hasxlast=false;
|
|
//--- function calls
|
|
CMinCG::MinCGSetCond(state,0,0,tmpeps,0);
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_f=state.m_f+a[i]*MathPow(state.m_x[i],4);
|
|
state.m_g[i]=4*a[i]*MathPow(state.m_x[i],3);
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
//--- check
|
|
if(hasxlast)
|
|
{
|
|
lastscaledstep=0;
|
|
for(i=0;i<=n-1;i++)
|
|
lastscaledstep=lastscaledstep+CMath::Sqr(state.m_x[i]-xlast[i])/CMath::Sqr(s[i]);
|
|
lastscaledstep=MathSqrt(lastscaledstep);
|
|
}
|
|
else
|
|
lastscaledstep=0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xlast[i_]=state.m_x[i_];
|
|
hasxlast=true;
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
err=err || lastscaledstep>tmpeps;
|
|
}
|
|
}
|
|
//--- Check correctness of the "trimming".
|
|
//--- Trimming is a technique which is used to help algorithm
|
|
//--- cope with unbounded functions. In order to check this
|
|
//--- technique we will try to solve following optimization
|
|
//--- problem:
|
|
//--- min f(x) subject to no constraints on X
|
|
//--- { 1/(1-x) + 1/(1+x) + c*x,if -0.999999<x<0.999999
|
|
//--- f(x)={
|
|
//--- { M,if x<=-0.999999 or x>=0.999999
|
|
//--- where c is either 1.0 or 1.0E+6,M is either 1.0E8,1.0E20 or +INF
|
|
//--- (we try different combinations)
|
|
for(ckind=0;ckind<=1;ckind++)
|
|
{
|
|
for(mkind=0;mkind<=2;mkind++)
|
|
{
|
|
//--- Choose c and M
|
|
if(ckind==0)
|
|
vc=1.0;
|
|
//--- check
|
|
if(ckind==1)
|
|
vc=1.0E+6;
|
|
//--- check
|
|
if(mkind==0)
|
|
vm=1.0E+8;
|
|
//--- check
|
|
if(mkind==1)
|
|
vm=1.0E+20;
|
|
//--- check
|
|
if(mkind==2)
|
|
vm=CInfOrNaN::PositiveInfinity();
|
|
//--- Create optimizer,solve optimization problem
|
|
epsg=1.0E-6*vc;
|
|
ArrayResize(x,1);
|
|
x[0]=0.0;
|
|
//--- function calls
|
|
CMinCG::MinCGCreate(1,x,state);
|
|
CMinCG::MinCGSetCond(state,epsg,0,0,0);
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- check
|
|
if(-0.999999<state.m_x[0] && state.m_x[0]<0.999999)
|
|
{
|
|
state.m_f=1/(1-state.m_x[0])+1/(1+state.m_x[0])+vc*state.m_x[0];
|
|
state.m_g[0]=1/CMath::Sqr(1-state.m_x[0])-1/CMath::Sqr(1+state.m_x[0])+vc;
|
|
}
|
|
else
|
|
state.m_f=vm;
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
err=err || MathAbs(1/CMath::Sqr(1-x[0])-1/CMath::Sqr(1+x[0])+vc)>epsg;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function #1 |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinCGUnit::TestFunc1(CMinCGState &state)
|
|
{
|
|
//--- check
|
|
if(state.m_x[0]<100.0)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
|
|
state.m_g[1]=2*state.m_x[1];
|
|
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=MathSqrt(CMath::m_maxrealnumber);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_g[0]=MathSqrt(CMath::m_maxrealnumber);
|
|
state.m_g[1]=0;
|
|
state.m_g[2]=0;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function #2 |
|
|
//| Simple variation of #1,much more nonlinear,which makes unlikely |
|
|
//| premature convergence of algorithm . |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinCGUnit::TestFunc2(CMinCGState &state)
|
|
{
|
|
//--- check
|
|
if(state.m_x[0]<100.0)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1]))+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
|
|
state.m_g[1]=4*state.m_x[1]*CMath::Sqr(state.m_x[1]);
|
|
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=MathSqrt(CMath::m_maxrealnumber);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_g[0]=MathSqrt(CMath::m_maxrealnumber);
|
|
state.m_g[1]=0;
|
|
state.m_g[2]=0;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function #3 |
|
|
//| Simple variation of #1,much more nonlinear,with non-zero value at|
|
|
//| minimum. |
|
|
//| It achieve two goals: |
|
|
//| * makes unlikely premature convergence of algorithm. |
|
|
//| * solves some issues with EpsF stopping condition which arise |
|
|
//| when F(minimum) is zero |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinCGUnit::TestFunc3(CMinCGState &state)
|
|
{
|
|
//--- create a variable
|
|
double s=0;
|
|
//--- initialization
|
|
s=0.001;
|
|
//--- check
|
|
if(state.m_x[0]<100.0)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1])+s)+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
|
|
state.m_g[1]=2*(CMath::Sqr(state.m_x[1])+s)*2*state.m_x[1];
|
|
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=MathSqrt(CMath::m_maxrealnumber);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_g[0]=MathSqrt(CMath::m_maxrealnumber);
|
|
state.m_g[1]=0;
|
|
state.m_g[2]=0;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function IIP2 |
|
|
//| f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1) |
|
|
//| It has high condition number which makes fast convergence |
|
|
//| unlikely without good preconditioner. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinCGUnit::CalcIIP2(CMinCGState &state,const int n)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=state.m_f+CMath::Sqr(i*i+1)*CMath::Sqr(state.m_x[i]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g[i]=CMath::Sqr(i*i+1)*2*state.m_x[i];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function f(x)=0.5*(x-x0)'*A*(x-x0),A=D+V'*Vd*V |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinCGUnit::CalcLowRank(CMinCGState &state,const int n,
|
|
const int vcnt,double &d[],
|
|
CMatrixDouble &v,double &vd[],
|
|
double &x0[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double dx=0;
|
|
double t=0;
|
|
double t2=0;
|
|
int i_=0;
|
|
//--- change values
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_g[i]=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
dx=state.m_x[i]-x0[i];
|
|
state.m_f=state.m_f+0.5*dx*d[i]*dx;
|
|
state.m_g[i]=state.m_g[i]+d[i]*dx;
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=vcnt-1;i++)
|
|
{
|
|
t=0;
|
|
for(j=0;j<=n-1;j++)
|
|
t=t+v[i][j]*(state.m_x[j]-x0[j]);
|
|
//--- change values
|
|
state.m_f=state.m_f+0.5*t*vd[i]*t;
|
|
t2=t*vd[i];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_g[i_]=state.m_g[i_]+t2*v[i][i_];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests preconditioning |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinCGUnit::TestPreconditioning(bool &err)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int vs=0;
|
|
int cntb1=0;
|
|
int cntg1=0;
|
|
int cntb2=0;
|
|
int cntg2=0;
|
|
double epsg=0;
|
|
int cgtype=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double x0[];
|
|
double vd[];
|
|
double d[];
|
|
double s[];
|
|
double diagh[];
|
|
//--- create matrix
|
|
CMatrixDouble v;
|
|
//--- objects of classes
|
|
CMinCGState state;
|
|
CMinCGReport rep;
|
|
//--- initialization
|
|
k=50;
|
|
epsg=1.0E-10;
|
|
//--- calculation
|
|
for(cgtype=-1;cgtype<=1;cgtype++)
|
|
{
|
|
//--- Preconditioner test 1.
|
|
//--- If
|
|
//--- * B1 is default preconditioner
|
|
//--- * G1 is diagonal precomditioner based on approximate diagonal of Hessian matrix
|
|
//--- then "bad" preconditioner is worse than "good" one.
|
|
//--- "Worse" means more iterations to converge.
|
|
//--- We test it using f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1).
|
|
//--- N - problem size
|
|
//--- K - number of repeated passes (should be large enough to average out random factors)
|
|
for(n=10;n<=15;n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=0;
|
|
//--- function calls
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- Test it with default preconditioner
|
|
CMinCG::MinCGSetPrecDefault(state);
|
|
//--- change values
|
|
cntb1=0;
|
|
for(pass=0;pass<=k-1;pass++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
cntb1=cntb1+rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
}
|
|
//--- Test it with perturbed diagonal preconditioner
|
|
ArrayResize(diagh,n);
|
|
for(i=0;i<=n-1;i++)
|
|
diagh[i]=2*CMath::Sqr(i*i+1)*(0.8+0.4*CMath::RandomReal());
|
|
//--- function call
|
|
CMinCG::MinCGSetPrecDiag(state,diagh);
|
|
//--- change values
|
|
cntg1=0;
|
|
for(pass=0;pass<=k-1;pass++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
cntg1=cntg1+rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
}
|
|
//--- Compare
|
|
err=err || cntb1<cntg1;
|
|
}
|
|
//--- Preconditioner test 2.
|
|
//--- If
|
|
//--- * B1 is default preconditioner
|
|
//--- * G1 is low rank exact preconditioner
|
|
//--- then "bad" preconditioner is worse than "good" one.
|
|
//--- "Worse" means more iterations to converge.
|
|
//--- Target function is f(x)=0.5*(x-x0)'*A*(x-x0),A=D+V'*Vd*V
|
|
//--- N - problem size
|
|
//--- K - number of repeated passes (should be large enough to average out random factors)
|
|
for(n=10;n<=15;n++)
|
|
{
|
|
for(vs=0;vs<=5;vs++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(x0,n);
|
|
ArrayResize(d,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=0;
|
|
x0[i]=2*CMath::RandomReal()-1;
|
|
d[i]=MathExp(2*CMath::RandomReal());
|
|
}
|
|
//--- check
|
|
if(vs>0)
|
|
{
|
|
//--- allocation
|
|
v.Resize(vs,n);
|
|
ArrayResize(vd,vs);
|
|
//--- change values
|
|
for(i=0;i<=vs-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
v[i].Set(j,2*CMath::RandomReal()-1);
|
|
vd[i]=MathExp(2*CMath::RandomReal());
|
|
}
|
|
}
|
|
//--- function calls
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
CMinCG::MinCGSetCGType(state,cgtype);
|
|
//--- Test it with default preconditioner
|
|
CMinCG::MinCGSetPrecDefault(state);
|
|
//--- change values
|
|
cntb1=0;
|
|
for(pass=0;pass<=k-1;pass++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
CalcLowRank(state,n,vs,d,v,vd,x0);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
cntb1=cntb1+rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
}
|
|
//--- Test it with low rank preconditioner
|
|
CMinCG::MinCGSetPrecLowRankFast(state,d,vd,v,vs);
|
|
//--- change values
|
|
cntg1=0;
|
|
for(pass=0;pass<=k-1;pass++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
CalcLowRank(state,n,vs,d,v,vd,x0);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
cntg1=cntg1+rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
}
|
|
//--- Compare
|
|
err=err || cntb1<cntg1;
|
|
}
|
|
}
|
|
//--- Preconditioner test 3.
|
|
//--- If
|
|
//--- * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
|
|
//--- * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
|
|
//--- then B2 is worse than G2.
|
|
//--- "Worse" means more iterations to converge.
|
|
for(n=10;n<=15;n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=0;
|
|
//--- function calls
|
|
CMinCG::MinCGCreate(n,x,state);
|
|
ArrayResize(s,n);
|
|
for(i=0;i<=n-1;i++)
|
|
s[i]=1/MathSqrt(2*MathPow(i*i+1,2)*(0.8+0.4*CMath::RandomReal()));
|
|
//--- function calls
|
|
CMinCG::MinCGSetPrecDefault(state);
|
|
CMinCG::MinCGSetScale(state,s);
|
|
//--- change values
|
|
cntb2=0;
|
|
for(pass=0;pass<=k-1;pass++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
cntb2=cntb2+rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
}
|
|
//--- function calls
|
|
CMinCG::MinCGSetPrecScale(state);
|
|
CMinCG::MinCGSetScale(state,s);
|
|
//--- change values
|
|
cntg2=0;
|
|
for(pass=0;pass<=k-1;pass++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinCG::MinCGIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinCG::MinCGResults(state,x,rep);
|
|
cntg2=cntg2+rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
}
|
|
//--- search errors
|
|
err=err || cntb2<cntg2;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMinBLEIC |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMinBLEICUnit
|
|
{
|
|
private:
|
|
//--- private method
|
|
static void CheckBounds(double &x[],double &bndl[],double &bndu[],const int n,bool &err);
|
|
static void CalcIIP2(CMinBLEICState &state,const int n,const int fk);
|
|
static void TestFeasibility(bool &feaserr,bool &converr,bool &interr);
|
|
static void TestOther(bool &err);
|
|
static void TestConv(bool &err);
|
|
static void TestPreconditioning(bool &err);
|
|
static void SetRandomPreconditioner(CMinBLEICState &state,const int n,const int preckind);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestMinBLEICUnit(void);
|
|
~CTestMinBLEICUnit(void);
|
|
//--- public method
|
|
static bool TestMinBLEIC(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestMinBLEICUnit::CTestMinBLEICUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestMinBLEICUnit::~CTestMinBLEICUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMinBLEIC |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMinBLEICUnit::TestMinBLEIC(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool feasibilityerrors;
|
|
bool othererrors;
|
|
bool precerrors;
|
|
bool interrors;
|
|
bool converrors;
|
|
//--- initialization
|
|
waserrors=false;
|
|
feasibilityerrors=false;
|
|
othererrors=false;
|
|
precerrors=false;
|
|
interrors=false;
|
|
converrors=false;
|
|
//--- function calls
|
|
TestFeasibility(feasibilityerrors,converrors,interrors);
|
|
TestOther(othererrors);
|
|
TestConv(converrors);
|
|
TestPreconditioning(precerrors);
|
|
//--- end
|
|
waserrors=(((feasibilityerrors || othererrors) || converrors) || interrors) || precerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING BLEIC OPTIMIZATION");
|
|
Print("FEASIBILITY PROPERTIES: ");
|
|
//--- check
|
|
if(feasibilityerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("PRECONDITIONING: ");
|
|
//--- check
|
|
if(precerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("OTHER PROPERTIES: ");
|
|
//--- check
|
|
if(othererrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("CONVERGENCE PROPERTIES: ");
|
|
//--- check
|
|
if(converrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("INTERNAL ERRORS: ");
|
|
//--- check
|
|
if(interrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Checks that X is bounded with respect to BndL/BndU. |
|
|
//| If it is not,True is assigned to the Err variable (which is not |
|
|
//| changed otherwise). |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinBLEICUnit::CheckBounds(double &x[],double &bndl[],
|
|
double &bndu[],const int n,
|
|
bool &err)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(x[i]<bndl[i] || x[i]>bndu[i])
|
|
err=true;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function IIP2 |
|
|
//| f(x)=sum( ((i*i+1)^FK*x[i])^2,i=0..N-1) |
|
|
//| It has high condition number which makes fast convergence |
|
|
//| unlikely without good preconditioner. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinBLEICUnit::CalcIIP2(CMinBLEICState &state,const int n,
|
|
const int fk)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_f=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=state.m_f+MathPow(i*i+1,2*fk)*CMath::Sqr(state.m_x[i]);
|
|
state.m_g[i]=MathPow(i*i+1,2*fk)*2*state.m_x[i];
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function test feasibility properties. |
|
|
//| It launches a sequence of problems and examines their solutions. |
|
|
//| Most of the attention is directed towards feasibility properties,|
|
|
//| although we make some quick checks to ensure that actual solution|
|
|
//| is found. |
|
|
//| On failure sets FeasErr (or ConvErr,depending on failure type) |
|
|
//| to True, or leaves it unchanged otherwise. |
|
|
//| IntErr is set to True on internal errors (errors in the control |
|
|
//| flow). |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinBLEICUnit::TestFeasibility(bool &feaserr,bool &converr,
|
|
bool &interr)
|
|
{
|
|
//--- create variables
|
|
int pkind=0;
|
|
int preckind=0;
|
|
int passcount=0;
|
|
int pass=0;
|
|
int n=0;
|
|
int nmax=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int p=0;
|
|
double v=0;
|
|
double v2=0;
|
|
double v3=0;
|
|
double vv=0;
|
|
double epsc=0;
|
|
double epsg=0;
|
|
int dkind=0;
|
|
double diffstep=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double bl[];
|
|
double bu[];
|
|
double x[];
|
|
double g[];
|
|
double x0[];
|
|
double xs[];
|
|
int ct[];
|
|
//--- create matrix
|
|
CMatrixDouble c;
|
|
//--- objects of classes
|
|
CMinBLEICState state;
|
|
CMinBLEICReport rep;
|
|
//--- initialization
|
|
nmax=5;
|
|
epsc=1.0E-4;
|
|
epsg=1.0E-8;
|
|
passcount=10;
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Test problem 1:
|
|
//--- * no boundary and inequality constraints
|
|
//--- * randomly generated plane as equality constraint
|
|
//--- * random point (not necessarily on the plane)
|
|
//--- * f=||x||^P,P={2,4} is used as target function
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from converging
|
|
//--- to the feasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * either analytic gradient or numerical differentiation are used
|
|
//--- * we check that after work is over we are on the plane and
|
|
//--- that we are in the stationary point of constrained F
|
|
diffstep=1.0E-6;
|
|
for(dkind=0;dkind<=1;dkind++)
|
|
{
|
|
for(preckind=0;preckind<=2;preckind++)
|
|
{
|
|
for(pkind=1;pkind<=2;pkind++)
|
|
{
|
|
for(n=1;n<=nmax;n++)
|
|
{
|
|
//--- Generate X,BL,BU,CT and left part of C.
|
|
//--- Right part of C is generated using somewhat complex algo:
|
|
//--- * we generate random vector and multiply it by C.
|
|
//--- * result is used as the right part.
|
|
//--- * calculations are done on the fly,vector itself is not stored
|
|
//--- We use such algo to be sure that our system is consistent.
|
|
p=2*pkind;
|
|
ArrayResize(x,n);
|
|
ArrayResize(g,n);
|
|
c.Resize(1,n+1);
|
|
ArrayResize(ct,1);
|
|
c[0].Set(n,0);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
c[0].Set(i,2*CMath::RandomReal()-1);
|
|
v=2*CMath::RandomReal()-1;
|
|
c[0].Set(n,c[0][n]+c[0][i]*v);
|
|
}
|
|
ct[0]=0;
|
|
//--- Create and optimize
|
|
if(dkind==0)
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,1);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=state.m_f+MathPow(state.m_x[i],p);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g[i]=p*MathPow(state.m_x[i],p-1);
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
converr=true;
|
|
return;
|
|
}
|
|
//--- Test feasibility of solution
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=c[0][i_]*x[i_];
|
|
//--- search errors
|
|
feaserr=feaserr || MathAbs(v-c[0][n])>epsc;
|
|
//--- if C is nonzero,test that result is
|
|
//--- a stationary point of constrained F.
|
|
//--- NOTE: this check is done only if C is nonzero
|
|
vv=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
vv+=c[0][i_]*c[0][i_];
|
|
//--- check
|
|
if(vv!=0.0)
|
|
{
|
|
//--- Calculate gradient at the result
|
|
//--- Project gradient into C
|
|
//--- Check projected norm
|
|
for(i=0;i<=n-1;i++)
|
|
g[i]=p*MathPow(x[i],p-1);
|
|
v2=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v2+=c[0][i_]*c[0][i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=c[0][i_]*g[i_];
|
|
vv=v/v2;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
{
|
|
g[i_]=g[i_]-vv*c[0][i_];
|
|
}
|
|
v3=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v3+=g[i_]*g[i_];
|
|
//--- search errors
|
|
converr=converr || MathSqrt(v3)>0.001;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test problem 2 (multiple equality constraints):
|
|
//--- * 1<=N<=NMax,1<=K<=N
|
|
//--- * no boundary constraints
|
|
//--- * N-dimensional space
|
|
//--- * randomly generated point xs
|
|
//--- * K randomly generated hyperplanes which all pass through xs
|
|
//--- define K equality constraints: (a[k],x)=b[k]
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from converging
|
|
//--- to the feasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * f(x)=||x-x0||^2,x0=xs+a[0]
|
|
//--- * either analytic gradient or numerical differentiation are used
|
|
//--- * extremum of f(x) is exactly xs because:
|
|
//--- * xs is the closest point in the plane defined by (a[0],x)=b[0]
|
|
//--- * xs is feasible by definition
|
|
diffstep=1.0E-6;
|
|
for(dkind=0;dkind<=1;dkind++)
|
|
{
|
|
for(preckind=0;preckind<=2;preckind++)
|
|
{
|
|
for(n=2;n<=nmax;n++)
|
|
{
|
|
for(k=1;k<=n;k++)
|
|
{
|
|
//--- Generate X,X0,XS,BL,BU,CT and left part of C.
|
|
//--- Right part of C is generated using somewhat complex algo:
|
|
//--- * we generate random vector and multiply it by C.
|
|
//--- * result is used as the right part.
|
|
//--- * calculations are done on the fly,vector itself is not stored
|
|
//--- We use such algo to be sure that our system is consistent.
|
|
p=2*pkind;
|
|
ArrayResize(x,n);
|
|
ArrayResize(x0,n);
|
|
ArrayResize(xs,n);
|
|
ArrayResize(g,n);
|
|
c.Resize(k,n+1);
|
|
ArrayResize(ct,k);
|
|
c[0].Set(n,0);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
xs[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
c[i].Set(j,2*CMath::RandomReal()-1);
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=c[i][i_]*xs[i_];
|
|
c[i].Set(n,v);
|
|
ct[i]=0;
|
|
}
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
x0[i_]=xs[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
x0[i_]=x0[i_]+c[0][i_];
|
|
//--- Create and optimize
|
|
//
|
|
if(dkind==0)
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,k);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=state.m_f+CMath::Sqr(state.m_x[i]-x0[i]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g[i]=2*(state.m_x[i]-x0[i]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
converr=true;
|
|
return;
|
|
}
|
|
//--- check feasiblity properties
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=c[i][i_]*x[i_];
|
|
//--- search errors
|
|
feaserr=feaserr || MathAbs(v-c[i][n])>epsc;
|
|
}
|
|
//--- Compare with XS
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+CMath::Sqr(x[i]-xs[i]);
|
|
v=MathSqrt(v);
|
|
//--- search errors
|
|
converr=converr || MathAbs(v)>0.001;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Another simple problem:
|
|
//--- * bound constraints 0 <=x[i] <=1
|
|
//--- * no linear constraints
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from converging
|
|
//--- to the feasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * F(x)=||x-x0||^P,where P={2,4} and x0 is randomly selected from [-1,+2]^N
|
|
//--- * with such simple boundaries and function it is easy to find
|
|
//--- analytic form of solution: S[i]=bound(x0[i],0,1)
|
|
//--- * we also check that both final solution and subsequent iterates
|
|
//--- are strictly feasible
|
|
diffstep=1.0E-6;
|
|
for(dkind=0;dkind<=1;dkind++)
|
|
{
|
|
for(preckind=0;preckind<=2;preckind++)
|
|
{
|
|
for(pkind=1;pkind<=2;pkind++)
|
|
{
|
|
for(n=1;n<=nmax;n++)
|
|
{
|
|
//--- Generate X,BL,BU.
|
|
p=2*pkind;
|
|
ArrayResize(bl,n);
|
|
ArrayResize(bu,n);
|
|
ArrayResize(x,n);
|
|
ArrayResize(x0,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
bl[i]=0;
|
|
bu[i]=1;
|
|
x[i]=CMath::RandomReal();
|
|
x0[i]=3*CMath::RandomReal()-1;
|
|
}
|
|
//--- Create and optimize
|
|
if(dkind==0)
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=state.m_f+MathPow(state.m_x[i]-x0[i],p);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g[i]=p*MathPow(state.m_x[i]-x0[i],p-1);
|
|
//--- search errors
|
|
feaserr=feaserr || state.m_x[i]<0.0;
|
|
feaserr=feaserr || state.m_x[i]>1.0;
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
converr=true;
|
|
return;
|
|
}
|
|
//--- * compare solution with analytic one
|
|
//--- * check feasibility
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- search errors
|
|
converr=converr || MathAbs(x[i]-CApServ::BoundVal(x0[i],0.0,1.0))>0.01;
|
|
feaserr=feaserr || x[i]<0.0;
|
|
feaserr=feaserr || x[i]>1.0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Same as previous one,but with bound constraints posed
|
|
//--- as general linear ones:
|
|
//--- * no bound constraints
|
|
//--- * 2*N linear constraints 0 <=x[i] <=1
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from converging
|
|
//--- to the feasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * F(x)=||x-x0||^P,where P={2,4} and x0 is randomly selected from [-1,+2]^N
|
|
//--- * with such simple constraints and function it is easy to find
|
|
//--- analytic form of solution: S[i]=bound(x0[i],0,1).
|
|
//--- * however,we can't guarantee that solution is strictly feasible
|
|
//--- with respect to nonlinearity constraint,so we check
|
|
//--- for approximate feasibility.
|
|
for(preckind=0;preckind<=2;preckind++)
|
|
{
|
|
for(pkind=1;pkind<=2;pkind++)
|
|
{
|
|
for(n=1;n<=nmax;n++)
|
|
{
|
|
//--- Generate X,BL,BU.
|
|
p=2*pkind;
|
|
ArrayResize(x,n);
|
|
ArrayResize(x0,n);
|
|
c.Resize(2*n,n+1);
|
|
ArrayResize(ct,2*n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=CMath::RandomReal();
|
|
x0[i]=3*CMath::RandomReal()-1;
|
|
for(j=0;j<=n;j++)
|
|
{
|
|
c[2*i].Set(j,0);
|
|
c[2*i+1].Set(j,0);
|
|
}
|
|
c[2*i+0].Set(i,1);
|
|
c[2*i+0].Set(n,0);
|
|
ct[2*i+0]=1;
|
|
c[2*i+1].Set(i,1);
|
|
c[2*i+1].Set(n,1);
|
|
ct[2*i+1]=-1;
|
|
}
|
|
//--- Create and optimize
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,2*n);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_f=state.m_f+MathPow(state.m_x[i]-x0[i],p);
|
|
state.m_g[i]=p*MathPow(state.m_x[i]-x0[i],p-1);
|
|
}
|
|
continue;
|
|
}
|
|
//--- Unknown protocol specified
|
|
interr=true;
|
|
return;
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
converr=true;
|
|
return;
|
|
}
|
|
//--- * compare solution with analytic one
|
|
//--- * check feasibility
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- search errors
|
|
converr=converr || MathAbs(x[i]-CApServ::BoundVal(x0[i],0.0,1.0))>0.05;
|
|
feaserr=feaserr || x[i]<0.0-epsc;
|
|
feaserr=feaserr || x[i]>1.0+epsc;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Infeasible problem:
|
|
//--- * all bound constraints are 0 <=x[i] <=1 except for one
|
|
//--- * that one is 0 >=x[i] >=1
|
|
//--- * no linear constraints
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from detecting
|
|
//--- infeasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * F(x)=||x-x0||^P,where P={2,4} and x0 is randomly selected from [-1,+2]^N
|
|
//--- * algorithm must return correct error code on such problem
|
|
for(preckind=0;preckind<=2;preckind++)
|
|
{
|
|
for(pkind=1;pkind<=2;pkind++)
|
|
{
|
|
for(n=1;n<=nmax;n++)
|
|
{
|
|
//--- Generate X,BL,BU.
|
|
p=2*pkind;
|
|
ArrayResize(bl,n);
|
|
ArrayResize(bu,n);
|
|
ArrayResize(x,n);
|
|
ArrayResize(x0,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
bl[i]=0;
|
|
bu[i]=1;
|
|
x[i]=CMath::RandomReal();
|
|
x0[i]=3*CMath::RandomReal()-1;
|
|
}
|
|
i=CMath::RandomInteger(n);
|
|
bl[i]=1;
|
|
bu[i]=0;
|
|
//--- Create and optimize
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_f=state.m_f+MathPow(state.m_x[i]-x0[i],p);
|
|
state.m_g[i]=p*MathPow(state.m_x[i]-x0[i],p-1);
|
|
}
|
|
continue;
|
|
}
|
|
//--- Unknown protocol specified
|
|
interr=true;
|
|
return;
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- search errors
|
|
feaserr=feaserr || rep.m_terminationtype!=-3;
|
|
}
|
|
}
|
|
}
|
|
//--- Infeasible problem (2):
|
|
//--- * no bound and inequality constraints
|
|
//--- * 1<=K<=N arbitrary equality constraints
|
|
//--- * (K+1)th constraint which is equal to the first constraint a*x=c,
|
|
//--- but with c:=c+1. I.e. we have both a*x=c and a*x=c+1,which can't
|
|
//--- be true (other constraints may be inconsistent too,but we don't
|
|
//--- have to check it).
|
|
//--- * preconditioner is chosen at random (we just want to be
|
|
//--- sure that preconditioning won't prevent us from detecting
|
|
//--- infeasible point):
|
|
//--- * unit preconditioner
|
|
//--- * random diagonal-based preconditioner
|
|
//--- * random scale-based preconditioner
|
|
//--- * F(x)=||x||^P,where P={2,4}
|
|
//--- * algorithm must return correct error code on such problem
|
|
for(preckind=0;preckind<=2;preckind++)
|
|
{
|
|
for(pkind=1;pkind<=2;pkind++)
|
|
{
|
|
for(n=1;n<=nmax;n++)
|
|
{
|
|
for(k=1;k<=n;k++)
|
|
{
|
|
//--- Generate X,BL,BU.
|
|
p=2*pkind;
|
|
ArrayResize(x,n);
|
|
c.Resize(k+1,n+1);
|
|
ArrayResize(ct,k+1);
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=CMath::RandomReal();
|
|
//--- change values
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
for(j=0;j<=n;j++)
|
|
c[i].Set(j,2*CMath::RandomReal()-1);
|
|
ct[i]=0;
|
|
}
|
|
ct[k]=0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
c[k].Set(i_,c[0][i_]);
|
|
c[k].Set(n,c[0][n]+1);
|
|
//--- Create and optimize
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,k+1);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0.0,0.0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
|
|
SetRandomPreconditioner(state,n,preckind);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_f=state.m_f+MathPow(state.m_x[i],p);
|
|
state.m_g[i]=p*MathPow(state.m_x[i],p-1);
|
|
}
|
|
continue;
|
|
}
|
|
//--- Unknown protocol specified
|
|
interr=true;
|
|
return;
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- search errors
|
|
feaserr=feaserr || rep.m_terminationtype!=-3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function additional properties. |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinBLEICUnit::TestOther(bool &err)
|
|
{
|
|
//--- create variables
|
|
int passcount=0;
|
|
int pass=0;
|
|
int n=0;
|
|
int nmax=0;
|
|
int i=0;
|
|
double fprev=0;
|
|
double xprev=0;
|
|
double stpmax=0;
|
|
double v=0;
|
|
int pkind=0;
|
|
int ckind=0;
|
|
int mkind=0;
|
|
double vc=0;
|
|
double vm=0;
|
|
double epsc=0;
|
|
double epsg=0;
|
|
double tmpeps=0;
|
|
double diffstep=0;
|
|
int dkind=0;
|
|
bool wasf;
|
|
bool wasfg;
|
|
double r=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double bl[];
|
|
double bu[];
|
|
double x[];
|
|
double xf[];
|
|
double xlast[];
|
|
double a[];
|
|
double s[];
|
|
double h[];
|
|
int ct[];
|
|
//--- create matrix
|
|
CMatrixDouble c;
|
|
//--- objects of classes
|
|
CMinBLEICState state;
|
|
CMinBLEICReport rep;
|
|
//--- initialization
|
|
nmax=5;
|
|
epsc=1.0E-4;
|
|
epsg=1.0E-8;
|
|
passcount=10;
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Test reports:
|
|
//--- * first value must be starting point
|
|
//--- * last value must be last point
|
|
n=50;
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
ArrayResize(bl,n);
|
|
ArrayResize(bu,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=10;
|
|
bl[i]=2*CMath::RandomReal()-1;
|
|
bu[i]=CInfOrNaN::PositiveInfinity();
|
|
}
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,0,0,0);
|
|
CMinBLEIC::MinBLEICSetMaxIts(state,10);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,1.0E-64,1.0E-64);
|
|
CMinBLEIC::MinBLEICSetXRep(state,true);
|
|
fprev=CMath::m_maxrealnumber;
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]);
|
|
state.m_g[i]=2*(1+i)*state.m_x[i];
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
//--- check
|
|
if(fprev==CMath::m_maxrealnumber)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
err=err || state.m_x[i]!=x[i];
|
|
}
|
|
//--- change values
|
|
fprev=state.m_f;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xlast[i_]=state.m_x[i_];
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
err=err || x[i]!=xlast[i];
|
|
//--- Test differentiation vs. analytic gradient
|
|
//--- (first one issues NeedF requests,second one issues NeedFG requests)
|
|
n=50;
|
|
diffstep=1.0E-6;
|
|
for(dkind=0;dkind<=1;dkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=1;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinBLEIC::MinBLEICCreateF(n,x,diffstep,state);
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,1.0E-10,0,0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,1.0E-6,1.0E-6);
|
|
wasf=false;
|
|
wasfg=false;
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g[i]=2*(1+i)*state.m_x[i];
|
|
}
|
|
//--- search errors
|
|
wasf=wasf || state.m_needf;
|
|
wasfg=wasfg || state.m_needfg;
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- check
|
|
if(dkind==0)
|
|
err=(err || wasf) || !wasfg;
|
|
//--- check
|
|
if(dkind==1)
|
|
err=(err || !wasf) || wasfg;
|
|
}
|
|
//--- Test that numerical differentiation uses scaling.
|
|
//--- In order to test that we solve simple optimization
|
|
//--- problem: min(x^2) with initial x equal to 0.0.
|
|
//--- We choose random DiffStep and S,then we check that
|
|
//--- optimizer evaluates function at +-DiffStep*S only.
|
|
ArrayResize(x,1);
|
|
ArrayResize(s,1);
|
|
diffstep=CMath::RandomReal()*1.0E-6;
|
|
s[0]=MathExp(CMath::RandomReal()*4-2);
|
|
x[0]=0;
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICCreateF(1,x,diffstep,state);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,1.0E-6,0,0);
|
|
CMinBLEIC::MinBLEICSetScale(state,s);
|
|
v=0;
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
state.m_f=CMath::Sqr(state.m_x[0]);
|
|
v=MathMax(v,MathAbs(state.m_x[0]));
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
r=v/(s[0]*diffstep);
|
|
//--- search errors
|
|
err=err || MathAbs(MathLog(r))>MathLog(1+1000*CMath::m_machineepsilon);
|
|
//--- Test stpmax
|
|
n=1;
|
|
ArrayResize(x,n);
|
|
ArrayResize(bl,n);
|
|
ArrayResize(bu,n);
|
|
//--- change values
|
|
x[0]=100;
|
|
bl[0]=2*CMath::RandomReal()-1;
|
|
bu[0]=CInfOrNaN::PositiveInfinity();
|
|
stpmax=0.05+0.05*CMath::RandomReal();
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
|
|
CMinBLEIC::MinBLEICSetXRep(state,true);
|
|
CMinBLEIC::MinBLEICSetStpMax(state,stpmax);
|
|
xprev=x[0];
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]);
|
|
state.m_g[0]=MathExp(state.m_x[0])-MathExp(-state.m_x[0]);
|
|
err=err || MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax);
|
|
}
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
err=err || MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax);
|
|
xprev=state.m_x[0];
|
|
}
|
|
}
|
|
//--- Ability to solve problems with function which is unbounded from below
|
|
n=1;
|
|
ArrayResize(x,n);
|
|
ArrayResize(bl,n);
|
|
ArrayResize(bu,n);
|
|
bl[0]=4*CMath::RandomReal()+1;
|
|
bu[0]=bl[0]+1;
|
|
x[0]=0.5*(bl[0]+bu[0]);
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=-(1.0E8*CMath::Sqr(state.m_x[0]));
|
|
state.m_g[0]=-(2.0E8*state.m_x[0]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- search errors
|
|
err=err || MathAbs(x[0]-bu[0])>epsc;
|
|
//--- Test correctness of the scaling:
|
|
//--- * initial point is random point from [+1,+2]^N
|
|
//--- * f(x)=SUM(A[i]*x[i]^4),C[i] is random from [0.01,100]
|
|
//--- * function is EFFECTIVELY unconstrained;it has formal constraints,
|
|
//--- but they are inactive at the solution;we try different variants
|
|
//--- in order to explore different control paths of the optimizer:
|
|
//--- 0) absense of constraints
|
|
//--- 1) bound constraints -100000<=x[i]<=100000
|
|
//--- 2) one linear constraint 0*x=0
|
|
//--- 3) combination of (1) and (2)
|
|
//--- * we use random scaling matrix
|
|
//--- * we test different variants of the preconditioning:
|
|
//--- 0) unit preconditioner
|
|
//--- 1) random diagonal from [0.01,100]
|
|
//--- 2) scale preconditioner
|
|
//--- * we set very mild outer stopping conditions - OuterEpsX=1.0,but
|
|
//--- inner conditions are very stringent
|
|
//--- * and we test that in the extremum inner stopping conditions are
|
|
//--- satisfied subject to the current scaling coefficients.
|
|
tmpeps=1.0E-10;
|
|
for(n=1;n<=10;n++)
|
|
{
|
|
for(ckind=0;ckind<=3;ckind++)
|
|
{
|
|
for(pkind=0;pkind<=2;pkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(a,n);
|
|
ArrayResize(s,n);
|
|
ArrayResize(h,n);
|
|
ArrayResize(bl,n);
|
|
ArrayResize(bu,n);
|
|
c.Resize(1,n+1);
|
|
ArrayResize(ct,1);
|
|
ct[0]=0;
|
|
c[0].Set(n,0);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=CMath::RandomReal()+1;
|
|
bl[i]=-100000;
|
|
bu[i]=100000;
|
|
c[0].Set(i,0);
|
|
a[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
s[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
h[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
}
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
//--- check
|
|
if(ckind==1 || ckind==3)
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
//--- check
|
|
if(ckind==2 || ckind==3)
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,1);
|
|
//--- check
|
|
if(pkind==1)
|
|
CMinBLEIC::MinBLEICSetPrecDiag(state,h);
|
|
//--- check
|
|
if(pkind==2)
|
|
CMinBLEIC::MinBLEICSetPrecScale(state);
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,tmpeps,0,0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,1.0,1.0E-8);
|
|
CMinBLEIC::MinBLEICSetScale(state,s);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_f=state.m_f+a[i]*MathPow(state.m_x[i],4);
|
|
state.m_g[i]=4*a[i]*MathPow(state.m_x[i],3);
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- change value
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+CMath::Sqr(s[i]*4*a[i]*MathPow(x[i],3));
|
|
v=MathSqrt(v);
|
|
//--- search errors
|
|
err=err || v>tmpeps;
|
|
}
|
|
}
|
|
}
|
|
//--- Check correctness of the "trimming".
|
|
//--- Trimming is a technique which is used to help algorithm
|
|
//--- cope with unbounded functions. In order to check this
|
|
//--- technique we will try to solve following optimization
|
|
//--- problem:
|
|
//--- min f(x) subject to no constraints on X
|
|
//--- { 1/(1-x) + 1/(1+x) + c*x,if -0.999999<x<0.999999
|
|
//--- f(x)={
|
|
//--- { M,if x<=-0.999999 or x>=0.999999
|
|
//--- where c is either 1.0 or 1.0E+6,M is either 1.0E8,1.0E20 or +INF
|
|
//--- (we try different combinations)
|
|
//
|
|
for(ckind=0;ckind<=1;ckind++)
|
|
{
|
|
for(mkind=0;mkind<=2;mkind++)
|
|
{
|
|
//--- Choose c and M
|
|
if(ckind==0)
|
|
vc=1.0;
|
|
//--- check
|
|
if(ckind==1)
|
|
vc=1.0E+6;
|
|
//--- check
|
|
if(mkind==0)
|
|
vm=1.0E+8;
|
|
//--- check
|
|
if(mkind==1)
|
|
vm=1.0E+20;
|
|
//--- check
|
|
if(mkind==2)
|
|
vm=CInfOrNaN::PositiveInfinity();
|
|
//--- Create optimizer,solve optimization problem
|
|
epsg=1.0E-6*vc;
|
|
ArrayResize(x,1);
|
|
x[0]=0.0;
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICCreate(1,x,state);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,1.0E-6,1.0E-6);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- check
|
|
if(-0.999999<state.m_x[0] && state.m_x[0]<0.999999)
|
|
{
|
|
state.m_f=1/(1-state.m_x[0])+1/(1+state.m_x[0])+vc*state.m_x[0];
|
|
state.m_g[0]=1/CMath::Sqr(1-state.m_x[0])-1/CMath::Sqr(1+state.m_x[0])+vc;
|
|
}
|
|
else
|
|
state.m_f=vm;
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
err=err || MathAbs(1/CMath::Sqr(1-x[0])-1/CMath::Sqr(1+x[0])+vc)>epsg;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests convergence properties. |
|
|
//| We solve several simple problems with different combinations of |
|
|
//| constraints |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinBLEICUnit::TestConv(bool &err)
|
|
{
|
|
//--- create variables
|
|
int passcount=0;
|
|
int pass=0;
|
|
double epsc=0;
|
|
double epsg=0;
|
|
double tol=0;
|
|
//--- create arrays
|
|
double bl[];
|
|
double bu[];
|
|
double x[];
|
|
int ct[];
|
|
//--- create matrix
|
|
CMatrixDouble c;
|
|
//--- objects of classes
|
|
CMinBLEICState state;
|
|
CMinBLEICReport rep;
|
|
//--- initialization
|
|
epsc=1.0E-4;
|
|
epsg=1.0E-8;
|
|
tol=0.001;
|
|
passcount=10;
|
|
//--- Three closely connected problems:
|
|
//--- * 2-dimensional space
|
|
//--- * octagonal area bounded by:
|
|
//--- * -1<=x<=+1
|
|
//--- * -1<=y<=+1
|
|
//--- * x+y<=1.5
|
|
//--- * x-y<=1.5
|
|
//--- * -x+y<=1.5
|
|
//--- * -x-y<=1.5
|
|
//--- * several target functions:
|
|
//--- * f0=x+0.001*y,minimum at x=-1,y=-0.5
|
|
//--- * f1=(x+10)^2+y^2,minimum at x=-1,y=0
|
|
//--- * f2=(x+10)^2+(y-0.6)^2,minimum at x=-1,y=0.5
|
|
ArrayResize(x,2);
|
|
ArrayResize(bl,2);
|
|
ArrayResize(bu,2);
|
|
c.Resize(4,3);
|
|
ArrayResize(ct,4);
|
|
//--- change values
|
|
bl[0]=-1;
|
|
bl[1]=-1;
|
|
bu[0]=1;
|
|
bu[1]=1;
|
|
c[0].Set(0,1);
|
|
c[0].Set(1,1);
|
|
c[0].Set(2,1.5);
|
|
ct[0]=-1;
|
|
c[1].Set(0,1);
|
|
c[1].Set(1,-1);
|
|
c[1].Set(2,1.5);
|
|
ct[1]=-1;
|
|
c[2].Set(0,-1);
|
|
c[2].Set(1,1);
|
|
c[2].Set(2,1.5);
|
|
ct[2]=-1;
|
|
c[3].Set(0,-1);
|
|
c[3].Set(1,-1);
|
|
c[3].Set(2,1.5);
|
|
ct[3]=-1;
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- f0
|
|
x[0]=0.2*CMath::RandomReal()-0.1;
|
|
x[1]=0.2*CMath::RandomReal()-0.1;
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICCreate(2,x,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,4);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=state.m_x[0]+0.001*state.m_x[1];
|
|
state.m_g[0]=1;
|
|
state.m_g[1]=0.001;
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
//--- search errors
|
|
err=err || MathAbs(x[0]+1)>tol;
|
|
err=err || MathAbs(x[1]+0.5)>tol;
|
|
}
|
|
else
|
|
err=true;
|
|
//--- f1
|
|
x[0]=0.2*CMath::RandomReal()-0.1;
|
|
x[1]=0.2*CMath::RandomReal()-0.1;
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICCreate(2,x,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,4);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=CMath::Sqr(state.m_x[0]+10)+CMath::Sqr(state.m_x[1]);
|
|
state.m_g[0]=2*(state.m_x[0]+10);
|
|
state.m_g[1]=2*state.m_x[1];
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
//--- search errors
|
|
err=err || MathAbs(x[0]+1)>tol;
|
|
err=err || MathAbs(x[1])>tol;
|
|
}
|
|
else
|
|
err=true;
|
|
//--- f2
|
|
x[0]=0.2*CMath::RandomReal()-0.1;
|
|
x[1]=0.2*CMath::RandomReal()-0.1;
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICCreate(2,x,state);
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,4);
|
|
CMinBLEIC::MinBLEICSetInnerCond(state,epsg,0,0);
|
|
CMinBLEIC::MinBLEICSetOuterCond(state,epsc,epsc);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=CMath::Sqr(state.m_x[0]+10)+CMath::Sqr(state.m_x[1]-0.6);
|
|
state.m_g[0]=2*(state.m_x[0]+10);
|
|
state.m_g[1]=2*(state.m_x[1]-0.6);
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
//--- search errors
|
|
err=err || MathAbs(x[0]+1)>tol;
|
|
err=err || MathAbs(x[1]-0.5)>tol;
|
|
}
|
|
else
|
|
err=true;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests preconditioning |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinBLEICUnit::TestPreconditioning(bool &err)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int n=0;
|
|
int i=0;
|
|
int k=0;
|
|
int cntb1=0;
|
|
int cntb2=0;
|
|
int cntg1=0;
|
|
int cntg2=0;
|
|
double epsg=0;
|
|
int fkind=0;
|
|
int ckind=0;
|
|
int fk=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double x0[];
|
|
int ct[];
|
|
double bl[];
|
|
double bu[];
|
|
double vd[];
|
|
double d[];
|
|
double units[];
|
|
double s[];
|
|
double diagh[];
|
|
//--- create matrix
|
|
CMatrixDouble v;
|
|
CMatrixDouble c;
|
|
//--- objects of classes
|
|
CMinBLEICState state;
|
|
CMinBLEICReport rep;
|
|
//--- Preconditioner test 1.
|
|
//--- If
|
|
//--- * B1 is default preconditioner with unit scale
|
|
//--- * G1 is diagonal preconditioner based on approximate diagonal of Hessian matrix
|
|
//--- * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
|
|
//--- * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
|
|
//--- then B1 is worse than G1,B2 is worse than G2.
|
|
//--- "Worse" means more iterations to converge.
|
|
//--- Test problem setup:
|
|
//--- * f(x)=sum( ((i*i+1)^FK*x[i])^2,i=0..N-1)
|
|
//--- * FK is either +1 or -1 (we try both to test different aspects of preconditioning)
|
|
//--- * constraints:
|
|
//--- 0) absent
|
|
//--- 1) boundary only
|
|
//--- 2) linear equality only
|
|
//--- 3) combination of boundary and linear equality constraints
|
|
//--- N - problem size
|
|
//--- K - number of repeated passes (should be large enough to average out random factors)
|
|
k=30;
|
|
epsg=1.0E-10;
|
|
for(n=5;n<=8;n++)
|
|
{
|
|
for(fkind=0;fkind<=1;fkind++)
|
|
{
|
|
for(ckind=0;ckind<=3;ckind++)
|
|
{
|
|
fk=1-2*fkind;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(units,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=0;
|
|
units[i]=1;
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICCreate(n,x,state);
|
|
//--- check
|
|
if(ckind==1 || ckind==3)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(bl,n);
|
|
ArrayResize(bu,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
bl[i]=-1;
|
|
bu[i]=1;
|
|
}
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICSetBC(state,bl,bu);
|
|
}
|
|
//--- check
|
|
if(ckind==2 || ckind==3)
|
|
{
|
|
//--- allocation
|
|
c.Resize(1,n+1);
|
|
ArrayResize(ct,1);
|
|
//--- change value
|
|
ct[0]=CMath::RandomInteger(3)-1;
|
|
for(i=0;i<=n-1;i++)
|
|
c[0].Set(i,2*CMath::RandomReal()-1);
|
|
c[0].Set(n,0);
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICSetLC(state,c,ct,1);
|
|
}
|
|
//--- Test it with default preconditioner VS. perturbed diagonal preconditioner
|
|
CMinBLEIC::MinBLEICSetPrecDefault(state);
|
|
CMinBLEIC::MinBLEICSetScale(state,units);
|
|
//--- calculation
|
|
cntb1=0;
|
|
for(pass=0;pass<=k-1;pass++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
CalcIIP2(state,n,fk);
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
cntb1=cntb1+rep.m_inneriterationscount;
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(diagh,n);
|
|
for(i=0;i<=n-1;i++)
|
|
diagh[i]=2*MathPow(i*i+1,2*fk)*(0.8+0.4*CMath::RandomReal());
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICSetPrecDiag(state,diagh);
|
|
CMinBLEIC::MinBLEICSetScale(state,units);
|
|
//--- calculation
|
|
cntg1=0;
|
|
for(pass=0;pass<=k-1;pass++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
CalcIIP2(state,n,fk);
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
cntg1=cntg1+rep.m_inneriterationscount;
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
}
|
|
//--- search errors
|
|
err=err || cntb1<cntg1;
|
|
//--- Test it with scale-based preconditioner
|
|
ArrayResize(s,n);
|
|
for(i=0;i<=n-1;i++)
|
|
s[i]=1/MathSqrt(2*MathPow(i*i+1,2*fk)*(0.8+0.4*CMath::RandomReal()));
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICSetPrecDefault(state);
|
|
CMinBLEIC::MinBLEICSetScale(state,s);
|
|
//--- change values
|
|
cntb2=0;
|
|
for(pass=0;pass<=k-1;pass++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
CalcIIP2(state,n,fk);
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
cntb2=cntb2+rep.m_inneriterationscount;
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
}
|
|
//--- function calls
|
|
CMinBLEIC::MinBLEICSetPrecScale(state);
|
|
CMinBLEIC::MinBLEICSetScale(state,s);
|
|
//--- change values
|
|
cntg2=0;
|
|
for(pass=0;pass<=k-1;pass++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinBLEIC::MinBLEICIteration(state))
|
|
CalcIIP2(state,n,fk);
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICResults(state,x,rep);
|
|
cntg2=cntg2+rep.m_inneriterationscount;
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
}
|
|
//--- search errors
|
|
err=err || cntb2<cntg2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets random preconditioner: |
|
|
//| * unit one,for PrecKind=0 |
|
|
//| * diagonal-based one,for PrecKind=1 |
|
|
//| * scale-based one,for PrecKind=2 |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinBLEICUnit::SetRandomPreconditioner(CMinBLEICState &state,
|
|
const int n,
|
|
const int preckind)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create array
|
|
double p[];
|
|
//--- check
|
|
if(preckind==1)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(p,n);
|
|
for(i=0;i<=n-1;i++)
|
|
p[i]=MathExp(10*CMath::RandomReal()-5);
|
|
//--- function call
|
|
CMinBLEIC::MinBLEICSetPrecDiag(state,p);
|
|
}
|
|
else
|
|
CMinBLEIC::MinBLEICSetPrecDefault(state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMarkovCPD |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMCPDUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void TestSimple(bool &err);
|
|
static void TestEntryExit(bool &err);
|
|
static void TestEC(bool &err);
|
|
static void TestBC(bool &err);
|
|
static void TestLC(bool &err);
|
|
static void CreateEE(const int n,const int entrystate,const int exitstate,CMCPDState &s);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestMCPDUnit(void);
|
|
~CTestMCPDUnit(void);
|
|
//--- public method
|
|
static bool TestMCPD(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestMCPDUnit::CTestMCPDUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestMCPDUnit::~CTestMCPDUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMarkovCPD |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMCPDUnit::TestMCPD(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool simpleerrors;
|
|
bool entryexiterrors;
|
|
bool ecerrors;
|
|
bool bcerrors;
|
|
bool lcerrors;
|
|
bool othererrors;
|
|
//--- Init
|
|
waserrors=false;
|
|
othererrors=false;
|
|
simpleerrors=false;
|
|
entryexiterrors=false;
|
|
ecerrors=false;
|
|
bcerrors=false;
|
|
lcerrors=false;
|
|
//--- Test
|
|
TestSimple(simpleerrors);
|
|
TestEntryExit(entryexiterrors);
|
|
TestEC(ecerrors);
|
|
TestBC(bcerrors);
|
|
TestLC(lcerrors);
|
|
//--- Final report
|
|
waserrors=((((othererrors || simpleerrors) || entryexiterrors) || ecerrors) || bcerrors) || lcerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("MCPD TEST");
|
|
Print("TOTAL RESULTS: ");
|
|
//--- check
|
|
if(!waserrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* SIMPLE: ");
|
|
//--- check
|
|
if(!simpleerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* ENTRY/EXIT: ");
|
|
//--- check
|
|
if(!entryexiterrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* EQUALITY CONSTRAINTS: ");
|
|
//--- check
|
|
if(!ecerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* BOUND CONSTRAINTS: ");
|
|
//--- check
|
|
if(!bcerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* LINEAR CONSTRAINTS: ");
|
|
//--- check
|
|
if(!lcerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* OTHER PROPERTIES: ");
|
|
//--- check
|
|
if(!othererrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST SUMMARY: FAILED");
|
|
else
|
|
Print("TEST SUMMARY: PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Simple test with no "entry"/"exit" states |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMCPDUnit::TestSimple(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
double threshold=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double v0=0;
|
|
double offdiagonal=0;
|
|
//--- objects of classes
|
|
CMCPDState s;
|
|
CMCPDReport rep;
|
|
//--- create matrix
|
|
CMatrixDouble pexact;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble p;
|
|
//--- initialization
|
|
threshold=1.0E-2;
|
|
//--- First test:
|
|
//--- * N-dimensional problem
|
|
//--- * proportional data
|
|
//--- * no "entry"/"exit" states
|
|
//--- * N tracks,each includes only two states
|
|
//--- * first record in I-th track is [0 ... 1 ... 0] with 1 is in I-th position
|
|
//--- * all tracks are modelled using randomly generated transition matrix P
|
|
for(n=1;n<=5;n++)
|
|
{
|
|
//--- Initialize "exact" P:
|
|
//--- * fill by random values
|
|
//--- * make sure that each column sums to non-zero value
|
|
//--- * normalize
|
|
pexact.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
pexact[i].Set(j,CMath::RandomReal());
|
|
}
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
i=CMath::RandomInteger(n);
|
|
pexact[i].Set(j,pexact[i][j]+0.1);
|
|
}
|
|
//--- calculation
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+pexact[i][j];
|
|
for(i=0;i<=n-1;i++)
|
|
pexact[i].Set(j,pexact[i][j]/v);
|
|
}
|
|
//--- Initialize solver:
|
|
//--- * create object
|
|
//--- * add tracks
|
|
CMarkovCPD::MCPDCreate(n,s);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
xy.Resize(2,n);
|
|
//--- change values
|
|
for(j=0;j<=n-1;j++)
|
|
xy[0].Set(j,0);
|
|
xy[0].Set(i,1);
|
|
for(j=0;j<=n-1;j++)
|
|
xy[1].Set(j,pexact[j][i]);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,2);
|
|
}
|
|
//--- Solve and test
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
err=err || MathAbs(p[i][j]-pexact[i][j])>threshold;
|
|
}
|
|
}
|
|
else
|
|
err=true;
|
|
}
|
|
//--- Second test:
|
|
//--- * N-dimensional problem
|
|
//--- * proportional data
|
|
//--- * no "entry"/"exit" states
|
|
//--- * N tracks,each includes only two states
|
|
//--- * first record in I-th track is [0 ...0.1 0.8 0.1 ... 0] with 0.8 is in I-th position
|
|
//--- * all tracks are modelled using randomly generated transition matrix P
|
|
offdiagonal=0.1;
|
|
for(n=1;n<=5;n++)
|
|
{
|
|
//--- Initialize "exact" P:
|
|
//--- * fill by random values
|
|
//--- * make sure that each column sums to non-zero value
|
|
//--- * normalize
|
|
pexact.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
pexact[i].Set(j,CMath::RandomReal());
|
|
}
|
|
//--- calculation
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
i=CMath::RandomInteger(n);
|
|
pexact[i].Set(j,pexact[i][j]+0.1);
|
|
}
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+pexact[i][j];
|
|
for(i=0;i<=n-1;i++)
|
|
pexact[i].Set(j,pexact[i][j]/v);
|
|
}
|
|
//--- Initialize solver:
|
|
//--- * create object
|
|
//--- * add tracks
|
|
CMarkovCPD::MCPDCreate(n,s);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- allocation
|
|
xy.Resize(2,n);
|
|
for(j=0;j<=n-1;j++)
|
|
xy[0].Set(j,0);
|
|
//--- "main" element
|
|
xy[0].Set(i,1.0-2*offdiagonal);
|
|
for(j=0;j<=n-1;j++)
|
|
xy[1].Set(j,(1.0-2*offdiagonal)*pexact[j][i]);
|
|
//--- off-diagonal ones
|
|
if(i>0)
|
|
{
|
|
xy[0].Set(i-1,offdiagonal);
|
|
for(j=0;j<=n-1;j++)
|
|
xy[1].Set(j,xy[1][j]+offdiagonal*pexact[j][i-1]);
|
|
}
|
|
//--- check
|
|
if(i<n-1)
|
|
{
|
|
xy[0].Set(i+1,offdiagonal);
|
|
for(j=0;j<=n-1;j++)
|
|
xy[1].Set(j,xy[1][j]+offdiagonal*pexact[j][i+1]);
|
|
}
|
|
//--- function call
|
|
CMarkovCPD::MCPDAddTrack(s,xy,2);
|
|
}
|
|
//--- Solve and test
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
err=err || MathAbs(p[i][j]-pexact[i][j])>threshold;
|
|
}
|
|
}
|
|
else
|
|
err=true;
|
|
}
|
|
//--- Third test:
|
|
//--- * N-dimensional problem
|
|
//--- * population data
|
|
//--- * no "entry"/"exit" states
|
|
//--- * N tracks,each includes only two states
|
|
//--- * first record in I-th track is V*[0 ...0.1 0.8 0.1 ... 0] with 0.8 is in I-th position,V in [1,10]
|
|
//--- * all tracks are modelled using randomly generated transition matrix P
|
|
offdiagonal=0.1;
|
|
for(n=1;n<=5;n++)
|
|
{
|
|
//--- Initialize "exact" P:
|
|
//--- * fill by random values
|
|
//--- * make sure that each column sums to non-zero value
|
|
//--- * normalize
|
|
pexact.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
pexact[i].Set(j,CMath::RandomReal());
|
|
}
|
|
//--- change values
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
i=CMath::RandomInteger(n);
|
|
pexact[i].Set(j,pexact[i][j]+0.1);
|
|
}
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+pexact[i][j];
|
|
for(i=0;i<=n-1;i++)
|
|
pexact[i].Set(j,pexact[i][j]/v);
|
|
}
|
|
//--- Initialize solver:
|
|
//--- * create object
|
|
//--- * add tracks
|
|
CMarkovCPD::MCPDCreate(n,s);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- allocation
|
|
xy.Resize(2,n);
|
|
for(j=0;j<=n-1;j++)
|
|
xy[0].Set(j,0);
|
|
//--- "main" element
|
|
v0=9*CMath::RandomReal()+1;
|
|
xy[0].Set(i,v0*(1.0-2*offdiagonal));
|
|
for(j=0;j<=n-1;j++)
|
|
xy[1].Set(j,v0*(1.0-2*offdiagonal)*pexact[j][i]);
|
|
//--- off-diagonal ones
|
|
if(i>0)
|
|
{
|
|
xy[0].Set(i-1,v0*offdiagonal);
|
|
for(j=0;j<=n-1;j++)
|
|
xy[1].Set(j,xy[1][j]+v0*offdiagonal*pexact[j][i-1]);
|
|
}
|
|
//--- check
|
|
if(i<n-1)
|
|
{
|
|
xy[0].Set(i+1,v0*offdiagonal);
|
|
for(j=0;j<=n-1;j++)
|
|
xy[1].Set(j,xy[1][j]+v0*offdiagonal*pexact[j][i+1]);
|
|
}
|
|
//--- function call
|
|
CMarkovCPD::MCPDAddTrack(s,xy,2);
|
|
}
|
|
//--- Solve and test
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
err=err || MathAbs(p[i][j]-pexact[i][j])>threshold;
|
|
}
|
|
}
|
|
else
|
|
err=true;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test for different combinations of "entry"/"exit" models |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMCPDUnit::TestEntryExit(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
double threshold=0;
|
|
int entrystate=0;
|
|
int exitstate=0;
|
|
int entrykind=0;
|
|
int exitkind=0;
|
|
int popkind=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create matrix
|
|
CMatrixDouble p;
|
|
CMatrixDouble pexact;
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CMCPDState s;
|
|
CMCPDReport rep;
|
|
//--- initialization
|
|
threshold=1.0E-3;
|
|
//--- calculation
|
|
for(n=2;n<=5;n++)
|
|
{
|
|
for(entrykind=0;entrykind<=1;entrykind++)
|
|
{
|
|
for(exitkind=0;exitkind<=1;exitkind++)
|
|
{
|
|
for(popkind=0;popkind<=1;popkind++)
|
|
{
|
|
//--- Generate EntryState/ExitState such that one of the following is True:
|
|
//--- * EntryState<>ExitState
|
|
//--- * EntryState=-1 or ExitState=-1
|
|
do
|
|
{
|
|
//--- check
|
|
if(entrykind==0)
|
|
entrystate=-1;
|
|
else
|
|
entrystate=CMath::RandomInteger(n);
|
|
//--- check
|
|
if(exitkind==0)
|
|
exitstate=-1;
|
|
else
|
|
exitstate=CMath::RandomInteger(n);
|
|
}
|
|
while(!((entrystate==-1 || exitstate==-1) || entrystate!=exitstate));
|
|
//--- Generate transition matrix P such that:
|
|
//--- * columns corresponding to non-exit states sums to 1.0
|
|
//--- * columns corresponding to exit states sums to 0.0
|
|
//--- * rows corresponding to entry states are zero
|
|
pexact.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
pexact[i].Set(j,1+CMath::RandomInteger(5));
|
|
//--- check
|
|
if(i==entrystate)
|
|
pexact[i].Set(j,0.0);
|
|
//--- check
|
|
if(j==exitstate)
|
|
pexact[i].Set(j,0.0);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+pexact[i][j];
|
|
//--- check
|
|
if(v!=0.0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
pexact[i].Set(j,pexact[i][j]/v);
|
|
}
|
|
}
|
|
//--- Create MCPD solver
|
|
if(entrystate<0 && exitstate<0)
|
|
CMarkovCPD::MCPDCreate(n,s);
|
|
//--- check
|
|
if(entrystate>=0 && exitstate<0)
|
|
CMarkovCPD::MCPDCreateEntry(n,entrystate,s);
|
|
//--- check
|
|
if(entrystate<0 && exitstate>=0)
|
|
CMarkovCPD::MCPDCreateExit(n,exitstate,s);
|
|
//--- check
|
|
if(entrystate>=0 && exitstate>=0)
|
|
CMarkovCPD::MCPDCreateEntryExit(n,entrystate,exitstate,s);
|
|
//--- Add N tracks.
|
|
//--- K-th track starts from vector with large value of
|
|
//--- K-th component and small random noise in other components.
|
|
//--- Track contains from 2 to 4 elements.
|
|
//--- Tracks contain proportional (normalized) or
|
|
//--- population data,depending on PopKind variable.
|
|
for(k=0;k<=n-1;k++)
|
|
{
|
|
//--- Generate track whose length is in 2..4
|
|
xy.Resize(2+CMath::RandomInteger(3),n);
|
|
for(j=0;j<=n-1;j++)
|
|
xy[0].Set(j,0.05*CMath::RandomReal());
|
|
xy[0].Set(k,1+CMath::RandomReal());
|
|
//--- calculation
|
|
for(i=1;i<=CAp::Rows(xy)-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(j!=entrystate)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=pexact[j][i_]*xy[i-1][i_];
|
|
xy[i].Set(j,v);
|
|
}
|
|
else
|
|
xy[i].Set(j,CMath::RandomReal());
|
|
}
|
|
}
|
|
//--- Normalize,if needed
|
|
if(popkind==1)
|
|
{
|
|
for(i=0;i<=CAp::Rows(xy)-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(j=0;j<=n-1;j++)
|
|
v=v+xy[i][j];
|
|
//--- check
|
|
if(v>0.0)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
xy[i].Set(j,xy[i][j]/v);
|
|
}
|
|
}
|
|
}
|
|
//--- Add track
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
}
|
|
//--- Solve and test
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
err=err || MathAbs(p[i][j]-pexact[i][j])>threshold;
|
|
}
|
|
}
|
|
else
|
|
err=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test equality constraints. |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMCPDUnit::TestEC(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int entrystate=0;
|
|
int exitstate=0;
|
|
int entrykind=0;
|
|
int exitkind=0;
|
|
int i=0;
|
|
int j=0;
|
|
int ic=0;
|
|
int jc=0;
|
|
double vc=0;
|
|
//--- create matrix
|
|
CMatrixDouble p;
|
|
CMatrixDouble ec;
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CMCPDState s;
|
|
CMCPDReport rep;
|
|
//--- We try different problems with following properties:
|
|
//--- * N is large enough - we won't have problems with inconsistent constraints
|
|
//--- * first state is either "entry" or "normal"
|
|
//--- * last state is either "exit" or "normal"
|
|
//--- * we have one long random track
|
|
//--- We test several properties which are described in comments below
|
|
for(n=4;n<=6;n++)
|
|
{
|
|
for(entrykind=0;entrykind<=1;entrykind++)
|
|
{
|
|
for(exitkind=0;exitkind<=1;exitkind++)
|
|
{
|
|
//--- Prepare problem
|
|
if(entrykind==0)
|
|
entrystate=-1;
|
|
else
|
|
entrystate=0;
|
|
//--- check
|
|
if(exitkind==0)
|
|
exitstate=-1;
|
|
else
|
|
exitstate=n-1;
|
|
//--- allocation
|
|
xy.Resize(2*n,n);
|
|
for(i=0;i<=CAp::Rows(xy)-1;i++)
|
|
{
|
|
for(j=0;j<=CAp::Cols(xy)-1;j++)
|
|
xy[i].Set(j,CMath::RandomReal());
|
|
}
|
|
//--- Test that single equality constraint on non-entry
|
|
//--- non-exit elements of P is satisfied.
|
|
//--- NOTE: this test needs N>=4 because smaller values
|
|
//--- can give us inconsistent constraints
|
|
if(!CAp::Assert(n>=4,"TestEC: expectation failed"))
|
|
return;
|
|
ic=1+CMath::RandomInteger(n-2);
|
|
jc=1+CMath::RandomInteger(n-2);
|
|
vc=CMath::RandomReal();
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddEC(s,ic,jc,vc);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
err=err || p[ic][jc]!=vc;
|
|
else
|
|
err=true;
|
|
//--- Test interaction with default "sum-to-one" constraint
|
|
//--- on columns of P.
|
|
//--- We set N-1 equality constraints on random non-exit column
|
|
//--- of P,which are inconsistent with this default constraint
|
|
//--- (sum will be greater that 1.0).
|
|
//--- Algorithm must detect inconsistency.
|
|
//--- NOTE:
|
|
//--- 1. we do not set constraints for the first element of
|
|
//--- the column,because this element may be constrained by
|
|
//--- "exit state" constraint.
|
|
//--- 2. this test needs N>=3
|
|
if(!CAp::Assert(n>=3,"TestEC: expectation failed"))
|
|
return;
|
|
jc=CMath::RandomInteger(n-1);
|
|
vc=0.95;
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
for(i=1;i<=n-1;i++)
|
|
CMarkovCPD::MCPDAddEC(s,i,jc,vc);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype!=-3;
|
|
//--- Test interaction with constrains on entry states.
|
|
//--- When model has entry state,corresponding row of P
|
|
//--- must be zero. We try to set two kinds of constraints
|
|
//--- on random element of this row:
|
|
//--- * zero equality constraint,which must be consistent
|
|
//--- * non-zero equality constraint,which must be inconsistent
|
|
if(entrystate>=0)
|
|
{
|
|
jc=CMath::RandomInteger(n);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddEC(s,entrystate,jc,0.0);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddEC(s,entrystate,jc,0.5);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype!=-3;
|
|
}
|
|
//--- Test interaction with constrains on exit states.
|
|
//--- When model has exit state,corresponding column of P
|
|
//--- must be zero. We try to set two kinds of constraints
|
|
//--- on random element of this column:
|
|
//--- * zero equality constraint,which must be consistent
|
|
//--- * non-zero equality constraint,which must be inconsistent
|
|
if(exitstate>=0)
|
|
{
|
|
ic=CMath::RandomInteger(n);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddEC(s,ic,exitstate,0.0);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddEC(s,ic,exitstate,0.5);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype!=-3;
|
|
}
|
|
//--- Test SetEC() call - we constrain subset of non-entry
|
|
//--- non-exit elements and test it.
|
|
if(!CAp::Assert(n>=4,"TestEC: expectation failed"))
|
|
return;
|
|
//--- allocation
|
|
ec.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
ec[i].Set(j,CInfOrNaN::NaN());
|
|
}
|
|
for(j=1;j<=n-2;j++)
|
|
ec[1+CMath::RandomInteger(n-2)].Set(j,0.1+0.1*CMath::RandomReal());
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetEC(s,ec);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(CMath::IsFinite(ec[i][j]))
|
|
err=err || p[i][j]!=ec[i][j];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
err=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test bound constraints. |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMCPDUnit::TestBC(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int entrystate=0;
|
|
int exitstate=0;
|
|
int entrykind=0;
|
|
int exitkind=0;
|
|
int i=0;
|
|
int j=0;
|
|
int ic=0;
|
|
int jc=0;
|
|
double vl=0;
|
|
double vu=0;
|
|
//--- create matrix
|
|
CMatrixDouble p;
|
|
CMatrixDouble bndl;
|
|
CMatrixDouble bndu;
|
|
CMatrixDouble xy;
|
|
//--- ������ �������
|
|
CMCPDState s;
|
|
CMCPDReport rep;
|
|
//--- We try different problems with following properties:
|
|
//--- * N is large enough - we won't have problems with inconsistent constraints
|
|
//--- * first state is either "entry" or "normal"
|
|
//--- * last state is either "exit" or "normal"
|
|
//--- * we have one long random track
|
|
//--- We test several properties which are described in comments below
|
|
for(n=4;n<=6;n++)
|
|
{
|
|
for(entrykind=0;entrykind<=1;entrykind++)
|
|
{
|
|
for(exitkind=0;exitkind<=1;exitkind++)
|
|
{
|
|
//--- Prepare problem
|
|
if(entrykind==0)
|
|
entrystate=-1;
|
|
else
|
|
entrystate=0;
|
|
//--- check
|
|
if(exitkind==0)
|
|
exitstate=-1;
|
|
else
|
|
exitstate=n-1;
|
|
//--- allocation
|
|
xy.Resize(2*n,n);
|
|
for(i=0;i<=CAp::Rows(xy)-1;i++)
|
|
{
|
|
for(j=0;j<=CAp::Cols(xy)-1;j++)
|
|
xy[i].Set(j,CMath::RandomReal());
|
|
}
|
|
//--- Test that single bound constraint on non-entry
|
|
//--- non-exit elements of P is satisfied.
|
|
//--- NOTE 1: this test needs N>=4 because smaller values
|
|
//--- can give us inconsistent constraints
|
|
if(!CAp::Assert(n>=4,"TestBC: expectation failed"))
|
|
return;
|
|
//--- change values
|
|
ic=1+CMath::RandomInteger(n-2);
|
|
jc=1+CMath::RandomInteger(n-2);
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
vl=0.3*CMath::RandomReal();
|
|
else
|
|
vl=CInfOrNaN::NegativeInfinity();
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
vu=0.5+0.3*CMath::RandomReal();
|
|
else
|
|
vu=CInfOrNaN::PositiveInfinity();
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddBC(s,ic,jc,vl,vu);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
err=err || p[ic][jc]<vl;
|
|
err=err || p[ic][jc]>vu;
|
|
}
|
|
else
|
|
err=true;
|
|
//--- Test interaction with default "sum-to-one" constraint
|
|
//--- on columns of P.
|
|
//--- We set N-1 bound constraints on random non-exit column
|
|
//--- of P,which are inconsistent with this default constraint
|
|
//--- (sum will be greater that 1.0).
|
|
//--- Algorithm must detect inconsistency.
|
|
//--- NOTE:
|
|
//--- 1. we do not set constraints for the first element of
|
|
//--- the column,because this element may be constrained by
|
|
//--- "exit state" constraint.
|
|
//--- 2. this test needs N>=3
|
|
if(!CAp::Assert(n>=3,"TestEC: expectation failed"))
|
|
return;
|
|
jc=CMath::RandomInteger(n-1);
|
|
vl=0.85;
|
|
vu=0.95;
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
for(i=1;i<=n-1;i++)
|
|
CMarkovCPD::MCPDAddBC(s,i,jc,vl,vu);
|
|
//--- function calls
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype!=-3;
|
|
//--- Test interaction with constrains on entry states.
|
|
//--- When model has entry state,corresponding row of P
|
|
//--- must be zero. We try to set two kinds of constraints
|
|
//--- on random element of this row:
|
|
//--- * bound constraint with zero lower bound,which must be consistent
|
|
//--- * bound constraint with non-zero lower bound,which must be inconsistent
|
|
if(entrystate>=0)
|
|
{
|
|
jc=CMath::RandomInteger(n);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddBC(s,entrystate,jc,0.0,1.0);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddBC(s,entrystate,jc,0.5,1.0);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype!=-3;
|
|
}
|
|
//--- Test interaction with constrains on exit states.
|
|
//--- When model has exit state,corresponding column of P
|
|
//--- must be zero. We try to set two kinds of constraints
|
|
//--- on random element of this column:
|
|
//--- * bound constraint with zero lower bound,which must be consistent
|
|
//--- * bound constraint with non-zero lower bound,which must be inconsistent
|
|
if(exitstate>=0)
|
|
{
|
|
ic=CMath::RandomInteger(n);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddBC(s,ic,exitstate,0.0,1.0);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDAddBC(s,ic,exitstate,0.5,1.0);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype!=-3;
|
|
}
|
|
//--- Test SetBC() call - we constrain subset of non-entry
|
|
//--- non-exit elements and test it.
|
|
if(!CAp::Assert(n>=4,"TestBC: expectation failed"))
|
|
return;
|
|
//--- allocation
|
|
bndl.Resize(n,n);
|
|
bndu.Resize(n,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
bndl[i].Set(j,CInfOrNaN::NegativeInfinity());
|
|
bndu[i].Set(j,CInfOrNaN::PositiveInfinity());
|
|
}
|
|
}
|
|
//--- change values
|
|
for(j=1;j<=n-2;j++)
|
|
{
|
|
i=1+CMath::RandomInteger(n-2);
|
|
bndl[i].Set(j,0.5-0.1*CMath::RandomReal());
|
|
bndu[i].Set(j,0.5+0.1*CMath::RandomReal());
|
|
}
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetBC(s,bndl,bndu);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
err=err || p[i][j]<bndl[i][j];
|
|
err=err || p[i][j]>bndu[i][j];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
err=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test bound constraints. |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMCPDUnit::TestLC(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int entrystate=0;
|
|
int exitstate=0;
|
|
int entrykind=0;
|
|
int exitkind=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int t=0;
|
|
int jc=0;
|
|
double v=0;
|
|
double threshold=0;
|
|
//--- create array
|
|
int ct[];
|
|
//--- create matrix
|
|
CMatrixDouble p;
|
|
CMatrixDouble c;
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CMCPDState s;
|
|
CMCPDReport rep;
|
|
//--- initialization
|
|
threshold=1.0E5*CMath::m_machineepsilon;
|
|
//--- We try different problems with following properties:
|
|
//--- * N is large enough - we won't have problems with inconsistent constraints
|
|
//--- * first state is either "entry" or "normal"
|
|
//--- * last state is either "exit" or "normal"
|
|
//--- * we have one long random track
|
|
//--- We test several properties which are described in comments below
|
|
for(n=4;n<=6;n++)
|
|
{
|
|
for(entrykind=0;entrykind<=1;entrykind++)
|
|
{
|
|
for(exitkind=0;exitkind<=1;exitkind++)
|
|
{
|
|
//--- Prepare problem
|
|
if(entrykind==0)
|
|
entrystate=-1;
|
|
else
|
|
entrystate=0;
|
|
//--- check
|
|
if(exitkind==0)
|
|
exitstate=-1;
|
|
else
|
|
exitstate=n-1;
|
|
//--- allocation
|
|
xy.Resize(2*n,n);
|
|
for(i=0;i<=CAp::Rows(xy)-1;i++)
|
|
{
|
|
for(j=0;j<=CAp::Cols(xy)-1;j++)
|
|
xy[i].Set(j,CMath::RandomReal());
|
|
}
|
|
//--- Test that single linear equality/inequality constraint
|
|
//--- on non-entry non-exit elements of P is satisfied.
|
|
//--- NOTE 1: this test needs N>=4 because smaller values
|
|
//--- can give us inconsistent constraints
|
|
//--- NOTE 2: Constraints are generated is such a way that P=(1/N ... 1/N)
|
|
//--- is always feasible. It guarantees that there always exists
|
|
//--- at least one feasible point
|
|
//--- NOTE 3: If we have inequality constraint,we "shift" right part
|
|
//--- in order to make feasible some neighborhood of P=(1/N ... 1/N).
|
|
if(!CAp::Assert(n>=4,"TestLC: expectation failed"))
|
|
return;
|
|
//--- allocation
|
|
c.Resize(1,n*n+1);
|
|
ArrayResize(ct,1);
|
|
//--- calculation
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(((i==0 || i==n-1) || j==0) || j==n-1)
|
|
c[0].Set(i*n+j,0);
|
|
else
|
|
{
|
|
c[0].Set(i*n+j,CMath::RandomReal());
|
|
v=v+c[0][i*n+j]*(1.0/(double)n);
|
|
}
|
|
}
|
|
}
|
|
//--- change value
|
|
c[0].Set(n*n,v);
|
|
ct[0]=CMath::RandomInteger(3)-1;
|
|
//--- check
|
|
if(ct[0]<0)
|
|
c[0].Set(n*n,c[0][n*n]+0.1);
|
|
//--- check
|
|
if(ct[0]>0)
|
|
c[0].Set(n*n,c[0][n*n]-0.1);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetLC(s,c,ct,1);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
v=v+p[i][j]*c[0][i*n+j];
|
|
}
|
|
//--- check
|
|
if(ct[0]<0)
|
|
err=err || v>=c[0][n*n]+threshold;
|
|
//--- check
|
|
if(ct[0]==0)
|
|
err=err || MathAbs(v-c[0][n*n])>=threshold;
|
|
//--- check
|
|
if(ct[0]>0)
|
|
err=err || v<=c[0][n*n]-threshold;
|
|
}
|
|
else
|
|
err=true;
|
|
//--- Test interaction with default "sum-to-one" constraint
|
|
//--- on columns of P.
|
|
//--- We set linear constraint which has for "sum-to-X" on
|
|
//--- on random non-exit column of P. This constraint can be
|
|
//--- either consistent (X=1.0) or inconsistent (X<>1.0) with
|
|
//--- this default constraint.
|
|
//--- Algorithm must detect inconsistency.
|
|
//--- NOTE:
|
|
//--- 1. this test needs N>=2
|
|
if(!CAp::Assert(n>=2,"TestLC: expectation failed"))
|
|
return;
|
|
jc=CMath::RandomInteger(n-1);
|
|
//--- allocation
|
|
c.Resize(1,n*n+1);
|
|
ArrayResize(ct,1);
|
|
//--- change values
|
|
for(i=0;i<=n*n-1;i++)
|
|
c[0].Set(i,0.0);
|
|
for(i=0;i<=n-1;i++)
|
|
c[0].Set(n*i+jc,1.0);
|
|
c[0].Set(n*n,1.0);
|
|
ct[0]=0;
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetLC(s,c,ct,1);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
c[0].Set(n*n,2.0);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetLC(s,c,ct,1);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype!=-3;
|
|
//--- Test interaction with constrains on entry states.
|
|
//--- When model has entry state,corresponding row of P
|
|
//--- must be zero. We try to set two kinds of constraints
|
|
//--- on elements of this row:
|
|
//--- * sums-to-zero constraint,which must be consistent
|
|
//--- * sums-to-one constraint,which must be inconsistent
|
|
if(entrystate>=0)
|
|
{
|
|
c.Resize(1,n*n+1);
|
|
ArrayResize(ct,1);
|
|
for(i=0;i<=n*n-1;i++)
|
|
c[0].Set(i,0.0);
|
|
for(j=0;j<=n-1;j++)
|
|
c[0].Set(n*entrystate+j,1.0);
|
|
ct[0]=0;
|
|
c[0].Set(n*n,0.0);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetLC(s,c,ct,1);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
c[0].Set(n*n,1.0);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetLC(s,c,ct,1);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype!=-3;
|
|
}
|
|
//--- Test interaction with constrains on exit states.
|
|
//--- When model has exit state,corresponding column of P
|
|
//--- must be zero. We try to set two kinds of constraints
|
|
//--- on elements of this column:
|
|
//--- * sums-to-zero constraint,which must be consistent
|
|
//--- * sums-to-one constraint,which must be inconsistent
|
|
if(exitstate>=0)
|
|
{
|
|
c.Resize(1,n*n+1);
|
|
ArrayResize(ct,1);
|
|
//--- change values
|
|
for(i=0;i<=n*n-1;i++)
|
|
c[0].Set(i,0.0);
|
|
for(i=0;i<=n-1;i++)
|
|
c[0].Set(n*i+exitstate,1.0);
|
|
ct[0]=0;
|
|
c[0].Set(n*n,0.0);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetLC(s,c,ct,1);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
c[0].Set(n*n,1.0);
|
|
//--- function calls
|
|
CreateEE(n,entrystate,exitstate,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetLC(s,c,ct,1);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype!=-3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Final test - we generate several random constraints and
|
|
//--- test SetLC() function.
|
|
//--- NOTES:
|
|
//--- 1. Constraints are generated is such a way that P=(1/N ... 1/N)
|
|
//--- is always feasible. It guarantees that there always exists
|
|
//--- at least one feasible point
|
|
//--- 2. For simplicity of the test we do not use entry/exit states
|
|
//--- in our model
|
|
for(n=1;n<=4;n++)
|
|
{
|
|
for(k=1;k<=2*n;k++)
|
|
{
|
|
//--- Generate track
|
|
xy.Resize(2*n,n);
|
|
for(i=0;i<=CAp::Rows(xy)-1;i++)
|
|
{
|
|
for(j=0;j<=CAp::Cols(xy)-1;j++)
|
|
xy[i].Set(j,CMath::RandomReal());
|
|
}
|
|
//--- Generate random constraints
|
|
c.Resize(k,n*n+1);
|
|
ArrayResize(ct,k);
|
|
//--- calculation
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
//--- Generate constraint and its right part
|
|
c[i].Set(n*n,0);
|
|
for(j=0;j<=n*n-1;j++)
|
|
{
|
|
c[i].Set(j,2*CMath::RandomReal()-1);
|
|
c[i].Set(n*n,c[i][n*n]+c[i][j]*(1.0/(double)n));
|
|
}
|
|
ct[i]=CMath::RandomInteger(3)-1;
|
|
//--- If we have inequality constraint,we "shift" right part
|
|
//--- in order to make feasible some neighborhood of P=(1/N ... 1/N).
|
|
if(ct[i]<0)
|
|
c[i].Set(n*n,c[i][n*n]+0.1);
|
|
//--- check
|
|
if(ct[i]>0)
|
|
c[i].Set(n*n,c[i][n*n]-0.1);
|
|
}
|
|
//--- Test
|
|
CreateEE(n,-1,-1,s);
|
|
CMarkovCPD::MCPDAddTrack(s,xy,CAp::Rows(xy));
|
|
CMarkovCPD::MCPDSetLC(s,c,ct,k);
|
|
CMarkovCPD::MCPDSolve(s);
|
|
CMarkovCPD::MCPDResults(s,p,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
for(t=0;t<=k-1;t++)
|
|
{
|
|
//--- change values
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
v=v+p[i][j]*c[t][i*n+j];
|
|
}
|
|
//--- check
|
|
if(ct[t]<0)
|
|
err=err || v>=c[t][n*n]+threshold;
|
|
//--- check
|
|
if(ct[t]==0)
|
|
err=err || MathAbs(v-c[t][n*n])>=threshold;
|
|
//--- check
|
|
if(ct[t]>0)
|
|
err=err || v<=c[t][n*n]-threshold;
|
|
}
|
|
}
|
|
else
|
|
err=true;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function is used to create MCPD object with arbitrary |
|
|
//| combination of entry and exit states |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMCPDUnit::CreateEE(const int n,const int entrystate,
|
|
const int exitstate,CMCPDState &s)
|
|
{
|
|
//--- check
|
|
if(entrystate<0 && exitstate<0)
|
|
CMarkovCPD::MCPDCreate(n,s);
|
|
//--- check
|
|
if(entrystate>=0 && exitstate<0)
|
|
CMarkovCPD::MCPDCreateEntry(n,entrystate,s);
|
|
//--- check
|
|
if(entrystate<0 && exitstate>=0)
|
|
CMarkovCPD::MCPDCreateExit(n,exitstate,s);
|
|
//--- check
|
|
if(entrystate>=0 && exitstate>=0)
|
|
CMarkovCPD::MCPDCreateEntryExit(n,entrystate,exitstate,s);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CFbls |
|
|
//+------------------------------------------------------------------+
|
|
class CTestFblsUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestFblsUnit(void);
|
|
~CTestFblsUnit(void);
|
|
//--- public method
|
|
static bool TestFbls(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestFblsUnit::CTestFblsUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestFblsUnit::~CTestFblsUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestFblsUnit::TestFbls(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int m=0;
|
|
int mx=0;
|
|
int i=0;
|
|
int j=0;
|
|
bool waserrors;
|
|
bool cgerrors;
|
|
double v=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double alpha=0;
|
|
double e1=0;
|
|
double e2=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double tmp1[];
|
|
double tmp2[];
|
|
double b[];
|
|
double x[];
|
|
double xe[];
|
|
double buf[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- object of class
|
|
CFblsLinCgState cgstate;
|
|
//--- initialization
|
|
mx=10;
|
|
waserrors=false;
|
|
cgerrors=false;
|
|
//--- Test CG solver:
|
|
//--- * generate problem (A,B,Alpha,XE - exact solution) and initial approximation X
|
|
//--- * E1=||A'A*x-b||
|
|
//--- * solve
|
|
//--- * E2=||A'A*x-b||
|
|
//--- * test that E2<0.001*E1
|
|
for(n=1;n<=mx;n++)
|
|
{
|
|
for(m=1;m<=mx;m++)
|
|
{
|
|
//--- allocation
|
|
a.Resize(m,n);
|
|
ArrayResize(b,n);
|
|
ArrayResize(x,n);
|
|
ArrayResize(xe,n);
|
|
ArrayResize(tmp1,m);
|
|
ArrayResize(tmp2,n);
|
|
//--- init A,alpha,B,X (initial approximation),XE (exact solution)
|
|
//--- X is initialized in such way that is has no chances to be equal to XE.
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- change values
|
|
alpha=CMath::RandomReal()+0.1;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
b[i]=2*CMath::RandomReal()-1;
|
|
xe[i]=2*CMath::RandomReal()-1;
|
|
x[i]=(2*CMath::RandomInteger(2)-1)*(2+CMath::RandomReal());
|
|
}
|
|
//--- Test dense CG (which solves A'A*x=b and accepts dense A)
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=(2*CMath::RandomInteger(2)-1)*(2+CMath::RandomReal());
|
|
//--- function calls
|
|
CAblas::RMatrixMVect(m,n,a,0,0,0,x,0,tmp1,0);
|
|
CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,tmp2,0);
|
|
//--- calculation
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp2[i_]=tmp2[i_]+alpha*x[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp2[i_]=tmp2[i_]-b[i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=tmp2[i_]*tmp2[i_];
|
|
e1=MathSqrt(v);
|
|
//--- function calls
|
|
CFbls::FblsSolveCGx(a,m,n,alpha,b,x,buf);
|
|
CAblas::RMatrixMVect(m,n,a,0,0,0,x,0,tmp1,0);
|
|
CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,tmp2,0);
|
|
//--- calculation
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp2[i_]=tmp2[i_]+alpha*x[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp2[i_]=tmp2[i_]-b[i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=tmp2[i_]*tmp2[i_];
|
|
e2=MathSqrt(v);
|
|
//--- search errors
|
|
cgerrors=cgerrors || e2>0.001*e1;
|
|
//--- Test sparse CG (which relies on reverse communication)
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=(2*CMath::RandomInteger(2)-1)*(2+CMath::RandomReal());
|
|
//--- function calls
|
|
CAblas::RMatrixMVect(m,n,a,0,0,0,x,0,tmp1,0);
|
|
CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,tmp2,0);
|
|
//--- calculation
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp2[i_]=tmp2[i_]+alpha*x[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp2[i_]=tmp2[i_]-b[i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=tmp2[i_]*tmp2[i_];
|
|
e1=MathSqrt(v);
|
|
//--- function call
|
|
CFbls::FblsCGCreate(x,b,n,cgstate);
|
|
//--- cycle
|
|
while(CFbls::FblsCGIteration(cgstate))
|
|
{
|
|
//--- function calls
|
|
CAblas::RMatrixMVect(m,n,a,0,0,0,cgstate.m_x,0,tmp1,0);
|
|
CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,cgstate.m_ax,0);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
cgstate.m_ax[i_]=cgstate.m_ax[i_]+alpha*cgstate.m_x[i_];
|
|
//--- change values
|
|
v1=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v1+=tmp1[i_]*tmp1[i_];
|
|
v2=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v2+=cgstate.m_x[i_]*cgstate.m_x[i_];
|
|
cgstate.m_xax=v1+alpha*v2;
|
|
}
|
|
//--- function calls
|
|
CAblas::RMatrixMVect(m,n,a,0,0,0,cgstate.m_xk,0,tmp1,0);
|
|
CAblas::RMatrixMVect(n,m,a,0,0,1,tmp1,0,tmp2,0);
|
|
//--- calculation
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp2[i_]=tmp2[i_]+alpha*cgstate.m_xk[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
tmp2[i_]=tmp2[i_]-b[i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=tmp2[i_]*tmp2[i_];
|
|
e2=MathSqrt(v);
|
|
//--- search errors
|
|
cgerrors=cgerrors || MathAbs(e1-cgstate.m_e1)>100*CMath::m_machineepsilon*e1;
|
|
cgerrors=cgerrors || MathAbs(e2-cgstate.m_e2)>100*CMath::m_machineepsilon*e1;
|
|
cgerrors=cgerrors || e2>0.001*e1;
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=cgerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING FBLS");
|
|
Print("CG ERRORS: ");
|
|
//--- check
|
|
if(cgerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMinLBFGS |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMinLBFGSUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void TestFunc1(CMinLBFGSState &state);
|
|
static void TestFunc2(CMinLBFGSState &state);
|
|
static void TestFunc3(CMinLBFGSState &state);
|
|
static void CalcIIP2(CMinLBFGSState &state,const int n);
|
|
static void TestPreconditioning(bool &err);
|
|
static void TestOther(bool &err);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestMinLBFGSUnit(void);
|
|
~CTestMinLBFGSUnit(void);
|
|
//--- public method
|
|
static bool TestMinLBFGS(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestMinLBFGSUnit::CTestMinLBFGSUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestMinLBFGSUnit::~CTestMinLBFGSUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMinLBFGS |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMinLBFGSUnit::TestMinLBFGS(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool referror;
|
|
bool nonconverror;
|
|
bool eqerror;
|
|
bool converror;
|
|
bool crashtest;
|
|
bool othererrors;
|
|
bool restartserror;
|
|
bool precerror;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int maxits=0;
|
|
double diffstep=0;
|
|
int dkind=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double xe[];
|
|
double b[];
|
|
double xlast[];
|
|
double diagh[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- objects of classes
|
|
CMinLBFGSState state;
|
|
CMinLBFGSReport rep;
|
|
//--- initialization
|
|
waserrors=false;
|
|
precerror=false;
|
|
nonconverror=false;
|
|
restartserror=false;
|
|
eqerror=false;
|
|
converror=false;
|
|
crashtest=false;
|
|
othererrors=false;
|
|
referror=false;
|
|
//--- function calls
|
|
TestPreconditioning(precerror);
|
|
TestOther(othererrors);
|
|
//--- Reference problem
|
|
diffstep=1.0E-6;
|
|
for(dkind=0;dkind<=1;dkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,3);
|
|
n=3;
|
|
m=2;
|
|
x[0]=100*CMath::RandomReal()-50;
|
|
x[1]=100*CMath::RandomReal()-50;
|
|
x[2]=100*CMath::RandomReal()-50;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,0);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(state.m_x[0]-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_g[0]=2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2]);
|
|
state.m_g[1]=2*state.m_x[1];
|
|
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
referror=((rep.m_terminationtype<=0 || MathAbs(x[0]-2)>0.001) || MathAbs(x[1])>0.001) || MathAbs(x[2]-2)>0.001;
|
|
}
|
|
//--- nonconvex problems with complex surface: we start from point with very small
|
|
//--- gradient,but we need ever smaller gradient in the next step due to
|
|
//--- Wolfe conditions.
|
|
diffstep=1.0E-6;
|
|
for(dkind=0;dkind<=1;dkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
n=1;
|
|
m=1;
|
|
v=-100;
|
|
//--- calculation
|
|
while(v<0.1)
|
|
{
|
|
x[0]=v;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,1.0E-9,0,0,0);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(state.m_x[0])/(1+CMath::Sqr(state.m_x[0]));
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g[0]=(2*state.m_x[0]*(1+CMath::Sqr(state.m_x[0]))-CMath::Sqr(state.m_x[0])*2*state.m_x[0])/CMath::Sqr(1+CMath::Sqr(state.m_x[0]));
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
nonconverror=(nonconverror || rep.m_terminationtype<=0) || MathAbs(x[0])>0.001;
|
|
v=v+0.1;
|
|
}
|
|
}
|
|
//--- F2 problem with restarts:
|
|
//--- * make several iterations and restart BEFORE termination
|
|
//--- * iterate and restart AFTER termination
|
|
//--- NOTE: step is bounded from above to avoid premature convergence
|
|
diffstep=1.0E-6;
|
|
for(dkind=0;dkind<=1;dkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,3);
|
|
//--- change values
|
|
n=3;
|
|
m=2;
|
|
x[0]=10+10*CMath::RandomReal();
|
|
x[1]=10+10*CMath::RandomReal();
|
|
x[2]=10+10*CMath::RandomReal();
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSSetStpMax(state,0.1);
|
|
CMinLBFGS::MinLBFGSSetCond(state,0.0000001,0.0,0.0,0);
|
|
//--- calculation
|
|
for(i=0;i<=10;i++)
|
|
{
|
|
//--- check
|
|
if(!CMinLBFGS::MinLBFGSIteration(state))
|
|
break;
|
|
TestFunc2(state);
|
|
}
|
|
//--- change values
|
|
x[0]=10+10*CMath::RandomReal();
|
|
x[1]=10+10*CMath::RandomReal();
|
|
x[2]=10+10*CMath::RandomReal();
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
TestFunc2(state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
restartserror=(((restartserror || rep.m_terminationtype<=0) || MathAbs(x[0]-MathLog(2))>0.01) || MathAbs(x[1])>0.01) || MathAbs(x[2]-MathLog(2))>0.01;
|
|
//--- change values
|
|
x[0]=10+10*CMath::RandomReal();
|
|
x[1]=10+10*CMath::RandomReal();
|
|
x[2]=10+10*CMath::RandomReal();
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
TestFunc2(state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
restartserror=(((restartserror || rep.m_terminationtype<=0) || MathAbs(x[0]-MathLog(2))>0.01) || MathAbs(x[1])>0.01) || MathAbs(x[2]-MathLog(2))>0.01;
|
|
}
|
|
//--- Linear equations
|
|
diffstep=1.0E-6;
|
|
for(n=1;n<=10;n++)
|
|
{
|
|
//--- Prepare task
|
|
a.Resize(n,n);
|
|
ArrayResize(x,n);
|
|
ArrayResize(xe,n);
|
|
ArrayResize(b,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
xe[i]=2*CMath::RandomReal()-1;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
a[i].Set(i,a[i][i]+3*MathSign(a[i][i]));
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*xe[i_];
|
|
b[i]=v;
|
|
}
|
|
//--- Test different M/DKind
|
|
for(m=1;m<=n;m++)
|
|
{
|
|
for(dkind=0;dkind<=1;dkind++)
|
|
{
|
|
//--- Solve task
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,0);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_g[i]=0;
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*state.m_x[i_];
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=state.m_f+CMath::Sqr(v-b[i]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
state.m_g[j]=state.m_g[j]+2*(v-b[i])*a[i][j];
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
eqerror=eqerror || rep.m_terminationtype<=0;
|
|
for(i=0;i<=n-1;i++)
|
|
eqerror=eqerror || MathAbs(x[i]-xe[i])>0.001;
|
|
}
|
|
}
|
|
}
|
|
//--- Testing convergence properties
|
|
diffstep=1.0E-6;
|
|
for(dkind=0;dkind<=1;dkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,3);
|
|
//--- change values
|
|
n=3;
|
|
m=2;
|
|
for(i=0;i<=2;i++)
|
|
x[i]=6*CMath::RandomReal()-3;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,0.001,0,0,0);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
TestFunc3(state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
converror=converror || rep.m_terminationtype!=4;
|
|
//--- change values
|
|
for(i=0;i<=2;i++)
|
|
x[i]=6*CMath::RandomReal()-3;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0.001,0,0);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
TestFunc3(state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
converror=converror || rep.m_terminationtype!=1;
|
|
//--- change values
|
|
for(i=0;i<=2;i++)
|
|
x[i]=6*CMath::RandomReal()-3;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0,0.001,0);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
TestFunc3(state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
converror=converror || rep.m_terminationtype!=2;
|
|
//--- change values
|
|
for(i=0;i<=2;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,10);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
TestFunc3(state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
converror=(converror || rep.m_terminationtype!=5) || rep.m_iterationscount!=10;
|
|
}
|
|
//--- Crash test: too many iterations on a simple tasks
|
|
//--- May fail when encounter zero step,underflow or something like that
|
|
ArrayResize(x,3);
|
|
n=3;
|
|
m=2;
|
|
maxits=10000;
|
|
for(i=0;i<=2;i++)
|
|
x[i]=6*CMath::RandomReal()-3;
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,maxits);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
|
|
state.m_g[1]=2*state.m_x[1];
|
|
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
crashtest=crashtest || rep.m_terminationtype<=0;
|
|
//--- end
|
|
waserrors=((((((referror || nonconverror) || eqerror) || converror) || crashtest) || othererrors) || restartserror) || precerror;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING L-BFGS OPTIMIZATION");
|
|
Print("REFERENCE PROBLEM: ");
|
|
//--- check
|
|
if(referror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("NON-CONVEX PROBLEM: ");
|
|
//--- check
|
|
if(nonconverror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("LINEAR EQUATIONS: ");
|
|
//--- check
|
|
if(eqerror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("RESTARTS: ");
|
|
//--- check
|
|
if(restartserror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("PRECONDITIONER: ");
|
|
//--- check
|
|
if(precerror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("CONVERGENCE PROPERTIES: ");
|
|
//--- check
|
|
if(converror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("CRASH TEST: ");
|
|
//--- check
|
|
if(crashtest)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("OTHER PROPERTIES: ");
|
|
//--- check
|
|
if(othererrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function #1 |
|
|
//| It may show very interesting behavior when optimized with |
|
|
//| 'x[0]>=ln(2)' constraint. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinLBFGSUnit::TestFunc1(CMinLBFGSState &state)
|
|
{
|
|
//--- check
|
|
if(state.m_x[0]<100.0)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- calculation
|
|
state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
|
|
state.m_g[1]=2*state.m_x[1];
|
|
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=MathSqrt(CMath::m_maxrealnumber);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- calculation
|
|
state.m_g[0]=MathSqrt(CMath::m_maxrealnumber);
|
|
state.m_g[1]=0;
|
|
state.m_g[2]=0;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function #2 |
|
|
//| Simple variation of #1,much more nonlinear,which makes unlikely |
|
|
//| premature convergence of algorithm. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinLBFGSUnit::TestFunc2(CMinLBFGSState &state)
|
|
{
|
|
//--- check
|
|
if(state.m_x[0]<100.0)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1]))+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- calculation
|
|
state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
|
|
state.m_g[1]=4*state.m_x[1]*CMath::Sqr(state.m_x[1]);
|
|
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=MathSqrt(CMath::m_maxrealnumber);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- calculation
|
|
state.m_g[0]=MathSqrt(CMath::m_maxrealnumber);
|
|
state.m_g[1]=0;
|
|
state.m_g[2]=0;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function #3 |
|
|
//| Simple variation of #1, much more nonlinear, with non-zero value |
|
|
//| at minimum. It achieve two goals: |
|
|
//| * makes unlikely premature convergence of algorithm . |
|
|
//| * solves some issues with EpsF stopping condition which arise |
|
|
//| when F(minimum) is zero |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinLBFGSUnit::TestFunc3(CMinLBFGSState &state)
|
|
{
|
|
//--- create a variable
|
|
double s=0;
|
|
//--- initialization
|
|
s=0.001;
|
|
//--- check
|
|
if(state.m_x[0]<100.0)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1])+s)+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- calculation
|
|
state.m_g[0]=2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
|
|
state.m_g[1]=2*(CMath::Sqr(state.m_x[1])+s)*2*state.m_x[1];
|
|
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=MathSqrt(CMath::m_maxrealnumber);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- calculation
|
|
state.m_g[0]=MathSqrt(CMath::m_maxrealnumber);
|
|
state.m_g[1]=0;
|
|
state.m_g[2]=0;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculate test function IIP2 |
|
|
//| f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1) |
|
|
//| It has high condition number which makes fast convergence |
|
|
//| unlikely without good preconditioner. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinLBFGSUnit::CalcIIP2(CMinLBFGSState &state,const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=state.m_f+CMath::Sqr(i*i+1)*CMath::Sqr(state.m_x[i]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g[i]=CMath::Sqr(i*i+1)*2*state.m_x[i];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests preconditioning |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinLBFGSUnit::TestPreconditioning(bool &err)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int cntb1=0;
|
|
int cntb2=0;
|
|
int cntg1=0;
|
|
int cntg2=0;
|
|
double epsg=0;
|
|
int pkind=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double s[];
|
|
double diagh[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- objects of classes
|
|
CMinLBFGSState state;
|
|
CMinLBFGSReport rep;
|
|
//--- initialization
|
|
m=1;
|
|
k=50;
|
|
epsg=1.0E-10;
|
|
//--- Preconditioner test1.
|
|
//--- If
|
|
//--- * B1 is default preconditioner
|
|
//--- * B2 is Cholesky preconditioner with unit diagonal
|
|
//--- * G1 is Cholesky preconditioner based on exact Hessian with perturbations
|
|
//--- * G2 is diagonal precomditioner based on approximate diagonal of Hessian matrix
|
|
//--- then "bad" preconditioners (B1/B2/..) are worse than "good" ones (G1/G2/..).
|
|
//--- "Worse" means more iterations to converge.
|
|
//--- We test it using f(x)=sum( ((i*i+1)*x[i])^2,i=0..N-1) and L-BFGS
|
|
//--- optimizer with deliberately small M=1.
|
|
//--- N - problem size
|
|
//--- PKind - zero for upper triangular preconditioner,one for lower triangular.
|
|
//--- K - number of repeated passes (should be large enough to average out random factors)
|
|
for(n=10;n<=15;n++)
|
|
{
|
|
pkind=CMath::RandomInteger(2);
|
|
ArrayResize(x,n);
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=0;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- Test it with default preconditioner
|
|
CMinLBFGS::MinLBFGSSetPrecDefault(state);
|
|
cntb1=0;
|
|
for(pass=0;pass<=k-1;pass++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
cntb1=cntb1+rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
}
|
|
//--- Test it with unit preconditioner
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
a[i].Set(i,1);
|
|
else
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetPrecCholesky(state,a,pkind==0);
|
|
//--- change values
|
|
cntb2=0;
|
|
for(pass=0;pass<=k-1;pass++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
cntb2=cntb2+rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
}
|
|
//--- Test it with perturbed Hessian preconditioner
|
|
a.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
a[i].Set(i,(i*i+1)*(0.8+0.4*CMath::RandomReal()));
|
|
else
|
|
{
|
|
//--- check
|
|
if((pkind==0 && j>i) || (pkind==1 && j<i))
|
|
a[i].Set(j,0.1*CMath::RandomReal()-0.05);
|
|
else
|
|
a[i].Set(j,CInfOrNaN::NaN());
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetPrecCholesky(state,a,pkind==0);
|
|
cntg1=0;
|
|
for(pass=0;pass<=k-1;pass++)
|
|
{
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
cntg1=cntg1+rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
}
|
|
//--- Test it with perturbed diagonal preconditioner
|
|
ArrayResize(diagh,n);
|
|
for(i=0;i<=n-1;i++)
|
|
diagh[i]=2*CMath::Sqr(i*i+1)*(0.8+0.4*CMath::RandomReal());
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetPrecDiag(state,diagh);
|
|
cntg2=0;
|
|
for(pass=0;pass<=k-1;pass++)
|
|
{
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
cntg2=cntg2+rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
}
|
|
//--- Compare
|
|
err=err || cntb1<cntg1;
|
|
err=err || cntb2<cntg1;
|
|
err=err || cntb1<cntg2;
|
|
err=err || cntb2<cntg2;
|
|
}
|
|
//--- Preconditioner test 2.
|
|
//--- If
|
|
//--- * B2 is default preconditioner with non-unit scale S[i]=1/sqrt(h[i])
|
|
//--- * G2 is scale-based preconditioner with non-unit scale S[i]=1/sqrt(h[i])
|
|
//--- then B2 is worse than G2.
|
|
//--- "Worse" means more iterations to converge.
|
|
for(n=10;n<=15;n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=0;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- allocation
|
|
ArrayResize(s,n);
|
|
for(i=0;i<=n-1;i++)
|
|
s[i]=1/MathSqrt(2*MathPow(i*i+1,2)*(0.8+0.4*CMath::RandomReal()));
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSSetPrecDefault(state);
|
|
CMinLBFGS::MinLBFGSSetScale(state,s);
|
|
cntb2=0;
|
|
//--- calculation
|
|
for(pass=0;pass<=k-1;pass++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
cntb2=cntb2+rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
}
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSSetPrecScale(state);
|
|
CMinLBFGS::MinLBFGSSetScale(state,s);
|
|
cntg2=0;
|
|
//--- calculation
|
|
for(pass=0;pass<=k-1;pass++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
CalcIIP2(state,n);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
cntg2=cntg2+rep.m_iterationscount;
|
|
//--- search errors
|
|
err=err || rep.m_terminationtype<=0;
|
|
}
|
|
//--- search errors
|
|
err=err || cntb2<cntg2;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tests other properties |
|
|
//| On failure sets Err to True (leaves it unchanged otherwise) |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinLBFGSUnit::TestOther(bool &err)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int m=0;
|
|
bool hasxlast;
|
|
double lastscaledstep=0;
|
|
int i=0;
|
|
double fprev=0;
|
|
double xprev=0;
|
|
double v=0;
|
|
double stpmax=0;
|
|
double tmpeps=0;
|
|
double epsg=0;
|
|
int pkind=0;
|
|
int ckind=0;
|
|
int mkind=0;
|
|
double vc=0;
|
|
double vm=0;
|
|
double diffstep=0;
|
|
int dkind=0;
|
|
bool wasf;
|
|
bool wasfg;
|
|
double r=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double a[];
|
|
double s[];
|
|
double h[];
|
|
double xlast[];
|
|
//--- objects of classes
|
|
CMinLBFGSState state;
|
|
CMinLBFGSReport rep;
|
|
//--- Test reports (F should form monotone sequence)
|
|
n=50;
|
|
m=2;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=1;
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,100);
|
|
CMinLBFGS::MinLBFGSSetXRep(state,true);
|
|
fprev=CMath::m_maxrealnumber;
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]);
|
|
state.m_g[i]=2*(1+i)*state.m_x[i];
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
err=err || state.m_f>fprev;
|
|
//--- check
|
|
if(fprev==CMath::m_maxrealnumber)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
err=err || state.m_x[i]!=x[i];
|
|
}
|
|
//--- change values
|
|
fprev=state.m_f;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xlast[i_]=state.m_x[i_];
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
err=err || x[i]!=xlast[i];
|
|
//--- Test differentiation vs. analytic gradient
|
|
//--- (first one issues NeedF requests,second one issues NeedFG requests)
|
|
n=50;
|
|
m=5;
|
|
diffstep=1.0E-6;
|
|
//--- calculation
|
|
for(dkind=0;dkind<=1;dkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=1;
|
|
//--- check
|
|
if(dkind==0)
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
//--- check
|
|
if(dkind==1)
|
|
CMinLBFGS::MinLBFGSCreateF(n,m,x,diffstep,state);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0,0,n/2);
|
|
wasf=false;
|
|
wasfg=false;
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfg)
|
|
state.m_f=state.m_f+CMath::Sqr((1+i)*state.m_x[i]);
|
|
//--- check
|
|
if(state.m_needfg)
|
|
state.m_g[i]=2*(1+i)*state.m_x[i];
|
|
}
|
|
//--- search errors
|
|
wasf=wasf || state.m_needf;
|
|
wasfg=wasfg || state.m_needfg;
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- check
|
|
if(dkind==0)
|
|
err=(err || wasf) || !wasfg;
|
|
//--- check
|
|
if(dkind==1)
|
|
err=(err || !wasf) || wasfg;
|
|
}
|
|
//--- Test that numerical differentiation uses scaling.
|
|
//--- In order to test that we solve simple optimization
|
|
//--- problem: min(x^2) with initial x equal to 0.0.
|
|
//--- We choose random DiffStep and S,then we check that
|
|
//--- optimizer evaluates function at +-DiffStep*S only.
|
|
ArrayResize(x,1);
|
|
ArrayResize(s,1);
|
|
//--- change values
|
|
diffstep=CMath::RandomReal()*1.0E-6;
|
|
s[0]=MathExp(CMath::RandomReal()*4-2);
|
|
x[0]=0;
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSCreateF(1,1,x,diffstep,state);
|
|
CMinLBFGS::MinLBFGSSetCond(state,1.0E-6,0,0,0);
|
|
CMinLBFGS::MinLBFGSSetScale(state,s);
|
|
v=0;
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
state.m_f=CMath::Sqr(state.m_x[0]);
|
|
v=MathMax(v,MathAbs(state.m_x[0]));
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
r=v/(s[0]*diffstep);
|
|
//--- search errors
|
|
err=err || MathAbs(MathLog(r))>MathLog(1+1000*CMath::m_machineepsilon);
|
|
//--- test maximum step
|
|
n=1;
|
|
m=1;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
x[0]=100;
|
|
stpmax=0.05+0.05*CMath::RandomReal();
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
CMinLBFGS::MinLBFGSSetCond(state,1.0E-9,0,0,0);
|
|
CMinLBFGS::MinLBFGSSetStpMax(state,stpmax);
|
|
CMinLBFGS::MinLBFGSSetXRep(state,true);
|
|
xprev=x[0];
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]);
|
|
state.m_g[0]=MathExp(state.m_x[0])-MathExp(-state.m_x[0]);
|
|
//--- search errors
|
|
err=err || MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax);
|
|
}
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
//--- search errors
|
|
err=err || MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax);
|
|
xprev=state.m_x[0];
|
|
}
|
|
}
|
|
//--- Test correctness of the scaling:
|
|
//--- * initial point is random point from [+1,+2]^N
|
|
//--- * f(x)=SUM(A[i]*x[i]^4),C[i] is random from [0.01,100]
|
|
//--- * we use random scaling matrix
|
|
//--- * we test different variants of the preconditioning:
|
|
//--- 0) unit preconditioner
|
|
//--- 1) random diagonal from [0.01,100]
|
|
//--- 2) scale preconditioner
|
|
//--- * we set stringent stopping conditions (we try EpsG and EpsX)
|
|
//--- * and we test that in the extremum stopping conditions are
|
|
//--- satisfied subject to the current scaling coefficients.
|
|
tmpeps=1.0E-10;
|
|
m=1;
|
|
for(n=1;n<=10;n++)
|
|
{
|
|
for(pkind=0;pkind<=2;pkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
ArrayResize(a,n);
|
|
ArrayResize(s,n);
|
|
ArrayResize(h,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=CMath::RandomReal()+1;
|
|
a[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
s[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
h[i]=MathExp(MathLog(100)*(2*CMath::RandomReal()-1));
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSCreate(n,m,x,state);
|
|
CMinLBFGS::MinLBFGSSetScale(state,s);
|
|
CMinLBFGS::MinLBFGSSetXRep(state,true);
|
|
//--- check
|
|
if(pkind==1)
|
|
CMinLBFGS::MinLBFGSSetPrecDiag(state,h);
|
|
//--- check
|
|
if(pkind==2)
|
|
CMinLBFGS::MinLBFGSSetPrecScale(state);
|
|
//--- Test gradient-based stopping condition
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=CMath::RandomReal()+1;
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSSetCond(state,tmpeps,0,0,0);
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_f=state.m_f+a[i]*MathPow(state.m_x[i],4);
|
|
state.m_g[i]=4*a[i]*MathPow(state.m_x[i],3);
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- change value
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+CMath::Sqr(s[i]*4*a[i]*MathPow(x[i],3));
|
|
v=MathSqrt(v);
|
|
//--- search errors
|
|
err=err || v>tmpeps;
|
|
//--- Test step-based stopping condition
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=CMath::RandomReal()+1;
|
|
hasxlast=false;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state,0,0,tmpeps,0);
|
|
CMinLBFGS::MinLBFGSRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_f=state.m_f+a[i]*MathPow(state.m_x[i],4);
|
|
state.m_g[i]=4*a[i]*MathPow(state.m_x[i],3);
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
//--- check
|
|
if(hasxlast)
|
|
{
|
|
lastscaledstep=0;
|
|
for(i=0;i<=n-1;i++)
|
|
lastscaledstep=lastscaledstep+CMath::Sqr(state.m_x[i]-xlast[i])/CMath::Sqr(s[i]);
|
|
lastscaledstep=MathSqrt(lastscaledstep);
|
|
}
|
|
else
|
|
lastscaledstep=0;
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xlast[i_]=state.m_x[i_];
|
|
hasxlast=true;
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
err=err || lastscaledstep>tmpeps;
|
|
}
|
|
}
|
|
//--- Check correctness of the "trimming".
|
|
//--- Trimming is a technique which is used to help algorithm
|
|
//--- cope with unbounded functions. In order to check this
|
|
//--- technique we will try to solve following optimization
|
|
//--- problem:
|
|
//--- min f(x) subject to no constraints on X
|
|
//--- { 1/(1-x) + 1/(1+x) + c*x,if -0.999999<x<0.999999
|
|
//--- f(x)={
|
|
//--- { M,if x<=-0.999999 or x>=0.999999
|
|
//--- where c is either 1.0 or 1.0E+6,M is either 1.0E8,1.0E20 or +INF
|
|
//--- (we try different combinations)
|
|
for(ckind=0;ckind<=1;ckind++)
|
|
{
|
|
for(mkind=0;mkind<=2;mkind++)
|
|
{
|
|
//--- Choose c and M
|
|
if(ckind==0)
|
|
vc=1.0;
|
|
//--- check
|
|
if(ckind==1)
|
|
vc=1.0E+6;
|
|
//--- check
|
|
if(mkind==0)
|
|
vm=1.0E+8;
|
|
//--- check
|
|
if(mkind==1)
|
|
vm=1.0E+20;
|
|
//--- check
|
|
if(mkind==2)
|
|
vm=CInfOrNaN::PositiveInfinity();
|
|
//--- Create optimizer,solve optimization problem
|
|
epsg=1.0E-6*vc;
|
|
ArrayResize(x,1);
|
|
x[0]=0.0;
|
|
//--- function calls
|
|
CMinLBFGS::MinLBFGSCreate(1,1,x,state);
|
|
CMinLBFGS::MinLBFGSSetCond(state,epsg,0,0,0);
|
|
//--- cycle
|
|
while(CMinLBFGS::MinLBFGSIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- check
|
|
if(-0.999999<state.m_x[0] && state.m_x[0]<0.999999)
|
|
{
|
|
state.m_f=1/(1-state.m_x[0])+1/(1+state.m_x[0])+vc*state.m_x[0];
|
|
state.m_g[0]=1/CMath::Sqr(1-state.m_x[0])-1/CMath::Sqr(1+state.m_x[0])+vc;
|
|
}
|
|
else
|
|
state.m_f=vm;
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
{
|
|
err=true;
|
|
return;
|
|
}
|
|
//--- search errors
|
|
err=err || MathAbs(1/CMath::Sqr(1-x[0])-1/CMath::Sqr(1+x[0])+vc)>epsg;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMLPBase � CMLPTrain |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMLPTrainUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void CreateNetwork(CMultilayerPerceptron &network,const int nkind,const double a1,const double a2,const int nin,const int nhid1,const int nhid2,const int nout);
|
|
static void UnsetNetwork(CMultilayerPerceptron &network);
|
|
static void TestInformational(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err);
|
|
static void TestProcessing(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err);
|
|
static void TestGradient(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err);
|
|
static void TestHessian(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int passcount,bool &err);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestMLPTrainUnit(void);
|
|
~CTestMLPTrainUnit(void);
|
|
//--- public method
|
|
static bool TestMLPTrain(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestMLPTrainUnit::CTestMLPTrainUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestMLPTrainUnit::~CTestMLPTrainUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMLPBase � CMLPTrain |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMLPTrainUnit::TestMLPTrain(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
int passcount=0;
|
|
int maxn=0;
|
|
int maxhid=0;
|
|
int info=0;
|
|
int nf=0;
|
|
int nl=0;
|
|
int nhid1=0;
|
|
int nhid2=0;
|
|
int nkind=0;
|
|
int i=0;
|
|
int ncount=0;
|
|
bool inferrors;
|
|
bool procerrors;
|
|
bool graderrors;
|
|
bool hesserrors;
|
|
bool trnerrors;
|
|
//--- objects of classes �������
|
|
CMultilayerPerceptron network;
|
|
CMultilayerPerceptron network2;
|
|
CMLPReport rep;
|
|
CMLPCVReport cvrep;
|
|
CMatrixDouble xy;
|
|
CMatrixDouble valxy;
|
|
//--- initialization
|
|
waserrors=false;
|
|
inferrors=false;
|
|
procerrors=false;
|
|
graderrors=false;
|
|
hesserrors=false;
|
|
trnerrors=false;
|
|
passcount=10;
|
|
maxn=4;
|
|
maxhid=4;
|
|
//--- General multilayer network tests
|
|
for(nf=1;nf<=maxn;nf++)
|
|
{
|
|
for(nl=1;nl<=maxn;nl++)
|
|
{
|
|
for(nhid1=0;nhid1<=maxhid;nhid1++)
|
|
{
|
|
for(nhid2=0;nhid2<=0;nhid2++)
|
|
{
|
|
for(nkind=0;nkind<=3;nkind++)
|
|
{
|
|
//--- Skip meaningless parameters combinations
|
|
if(nkind==1 && nl<2)
|
|
continue;
|
|
//--- check
|
|
if(nhid1==0 && nhid2!=0)
|
|
continue;
|
|
//--- Tests
|
|
TestInformational(nkind,nf,nhid1,nhid2,nl,passcount,inferrors);
|
|
TestProcessing(nkind,nf,nhid1,nhid2,nl,passcount,procerrors);
|
|
TestGradient(nkind,nf,nhid1,nhid2,nl,passcount,graderrors);
|
|
TestHessian(nkind,nf,nhid1,nhid2,nl,passcount,hesserrors);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test network training on simple XOR problem
|
|
xy.Resize(4,3);
|
|
xy[0].Set(0,-1);
|
|
xy[0].Set(1,-1);
|
|
xy[0].Set(2,-1);
|
|
xy[1].Set(0,1);
|
|
xy[1].Set(1,-1);
|
|
xy[1].Set(2,1);
|
|
xy[2].Set(0,-1);
|
|
xy[2].Set(1,1);
|
|
xy[2].Set(2,1);
|
|
xy[3].Set(0,1);
|
|
xy[3].Set(1,1);
|
|
xy[3].Set(2,-1);
|
|
//--- function calls
|
|
CMLPBase::MLPCreate1(2,2,1,network);
|
|
CMLPTrain::MLPTrainLM(network,xy,4,0.001,10,info,rep);
|
|
//--- search errors
|
|
trnerrors=trnerrors || CMLPBase::MLPRMSError(network,xy,4)>0.1;
|
|
//--- Test CV on random noisy problem
|
|
ncount=100;
|
|
xy.Resize(ncount,2);
|
|
//--- change values
|
|
for(i=0;i<=ncount-1;i++)
|
|
{
|
|
xy[i].Set(0,2*CMath::RandomReal()-1);
|
|
xy[i].Set(1,CMath::RandomInteger(4));
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPCreateC0(1,4,network);
|
|
CMLPTrain::MLPKFoldCVLM(network,xy,ncount,0.001,5,10,info,rep,cvrep);
|
|
//--- Final report
|
|
waserrors=(((inferrors || procerrors) || graderrors) || hesserrors) || trnerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("MLP TEST");
|
|
Print("INFORMATIONAL FUNCTIONS: ");
|
|
//--- check
|
|
if(!inferrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("BASIC PROCESSING: ");
|
|
//--- check
|
|
if(!procerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("GRADIENT CALCULATION: ");
|
|
//--- check
|
|
if(!graderrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("HESSIAN CALCULATION: ");
|
|
//--- check
|
|
if(!hesserrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("TRAINING: ");
|
|
//--- check
|
|
if(!trnerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST SUMMARY: FAILED");
|
|
else
|
|
Print("TEST SUMMARY: PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Network creation |
|
|
//| This function creates network with desired structure. Network is |
|
|
//| created using one of the three methods: |
|
|
//| a) straighforward creation using MLPCreate???() |
|
|
//| b) MLPCreate???() for proxy object,which is copied with |
|
|
//| PassThroughSerializer() |
|
|
//| c) MLPCreate???() for proxy object,which is copied with MLPCopy()|
|
|
//| One of these methods is chosen with probability 1/3. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMLPTrainUnit::CreateNetwork(CMultilayerPerceptron &network,
|
|
const int nkind,const double a1,
|
|
const double a2,const int nin,
|
|
const int nhid1,const int nhid2,
|
|
const int nout)
|
|
{
|
|
//--- create a variable
|
|
int mkind=0;
|
|
//--- object of class
|
|
CMultilayerPerceptron tmp;
|
|
//--- check
|
|
if(!CAp::Assert(((nin>0 && nhid1>=0) && nhid2>=0) && nout>0,"CreateNetwork error"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(nhid1!=0 || nhid2==0,"CreateNetwork error"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(nkind!=1 || nout>=2,"CreateNetwork error"))
|
|
return;
|
|
//--- change value
|
|
mkind=CMath::RandomInteger(3);
|
|
//--- check
|
|
if(nhid1==0)
|
|
{
|
|
//--- No hidden layers
|
|
if(nkind==0)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreate0(nin,nout,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreate0(nin,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreate0(nin,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==1)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateC0(nin,nout,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateC0(nin,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateC0(nin,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==2)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateB0(nin,nout,a1,a2,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateB0(nin,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateB0(nin,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==3)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateR0(nin,nout,a1,a2,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateR0(nin,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateR0(nin,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
return;
|
|
}
|
|
//--- check
|
|
if(nhid2==0)
|
|
{
|
|
//--- One hidden layer
|
|
if(nkind==0)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreate1(nin,nhid1,nout,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreate1(nin,nhid1,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreate1(nin,nhid1,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==1)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateC1(nin,nhid1,nout,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateC1(nin,nhid1,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateC1(nin,nhid1,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==2)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateB1(nin,nhid1,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==3)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateR1(nin,nhid1,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
return;
|
|
}
|
|
//--- Two hidden layers
|
|
if(nkind==0)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreate2(nin,nhid1,nhid2,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==1)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateC2(nin,nhid1,nhid2,nout,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==2)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateB2(nin,nhid1,nhid2,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==3)
|
|
{
|
|
//--- check
|
|
if(mkind==0)
|
|
CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,network);
|
|
//--- check
|
|
if(mkind==1)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,tmp);
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,tmp);
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,tmp);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
}
|
|
}
|
|
//--- check
|
|
if(mkind==2)
|
|
{
|
|
CMLPBase::MLPCreateR2(nin,nhid1,nhid2,nout,a1,a2,tmp);
|
|
CMLPBase::MLPCopy(tmp,network);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets network (initialize it to smallest network possible |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMLPTrainUnit::UnsetNetwork(CMultilayerPerceptron &network)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPCreate0(1,1,network);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Informational functions test |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMLPTrainUnit::TestInformational(const int nkind,const int nin,
|
|
const int nhid1,const int nhid2,
|
|
const int nout,const int passcount,
|
|
bool &err)
|
|
{
|
|
//--- create variables
|
|
int n1=0;
|
|
int n2=0;
|
|
int wcount=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double threshold=0;
|
|
int nlayers=0;
|
|
int nmax=0;
|
|
bool issoftmax;
|
|
double mean=0;
|
|
double sigma=0;
|
|
int fkind=0;
|
|
double c=0;
|
|
double f=0;
|
|
double df=0;
|
|
double d2f=0;
|
|
double s=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble neurons;
|
|
//--- object of class
|
|
CMultilayerPerceptron network;
|
|
//--- initialization
|
|
threshold=100000*CMath::m_machineepsilon;
|
|
//--- function call
|
|
CreateNetwork(network,nkind,0.0,0.0,nin,nhid1,nhid2,nout);
|
|
//--- test MLPProperties()
|
|
CMLPBase::MLPProperties(network,n1,n2,wcount);
|
|
//--- search errors
|
|
err=((err || n1!=nin) || n2!=nout) || wcount<=0;
|
|
//--- Test network geometry functions
|
|
//--- In order to do this we calculate neural network output using
|
|
//--- informational functions only,and compare results with ones
|
|
//--- obtained with MLPProcess():
|
|
//--- 1. we allocate 2-dimensional array of neurons and fill it by zeros
|
|
//--- 2. we full first layer of neurons by input values
|
|
//--- 3. we move through array,calculating values of subsequent layers
|
|
//--- 4. if we have classification network,we SOFTMAX-normalize output layer
|
|
//--- 5. we apply scaling to the outputs
|
|
//--- 6. we compare results with ones obtained by MLPProcess()
|
|
//--- NOTE: it is important to do (4) before (5),because on SOFTMAX network
|
|
//--- MLPGetOutputScaling() must return Mean=0 and Sigma=1. In order
|
|
//--- to test it implicitly,we apply it to the classifier results
|
|
//--- (already normalized). If one of the coefficients deviates from
|
|
//--- expected values,we will get error during (6).
|
|
nlayers=2;
|
|
nmax=MathMax(nin,nout);
|
|
issoftmax=nkind==1;
|
|
//--- check
|
|
if(nhid1!=0)
|
|
{
|
|
nlayers=3;
|
|
nmax=MathMax(nmax,nhid1);
|
|
}
|
|
//--- check
|
|
if(nhid2!=0)
|
|
{
|
|
nlayers=4;
|
|
nmax=MathMax(nmax,nhid2);
|
|
}
|
|
//--- allocation
|
|
neurons.Resize(nlayers,nmax);
|
|
for(i=0;i<=nlayers-1;i++)
|
|
{
|
|
for(j=0;j<=nmax-1;j++)
|
|
neurons[i].Set(j,0);
|
|
}
|
|
//--- allocation
|
|
ArrayResize(x,nin);
|
|
//--- change values
|
|
for(i=0;i<=nin-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(y,nout);
|
|
//--- change values
|
|
for(i=0;i<=nout-1;i++)
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
for(j=0;j<=nin-1;j++)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPGetInputScaling(network,j,mean,sigma);
|
|
neurons[0].Set(j,(x[j]-mean)/sigma);
|
|
}
|
|
//--- calculation
|
|
for(i=1;i<=nlayers-1;i++)
|
|
{
|
|
for(j=0;j<=CMLPBase::MLPGetLayerSize(network,i)-1;j++)
|
|
{
|
|
for(k=0;k<=CMLPBase::MLPGetLayerSize(network,i-1)-1;k++)
|
|
neurons[i].Set(j,neurons[i][j]+CMLPBase::MLPGetWeight(network,i-1,k,i,j)*neurons[i-1][k]);
|
|
//--- function calls
|
|
CMLPBase::MLPGetNeuronInfo(network,i,j,fkind,c);
|
|
CMLPBase::MLPActivationFunction(neurons[i][j]-c,fkind,f,df,d2f);
|
|
neurons[i].Set(j,f);
|
|
}
|
|
}
|
|
//--- check
|
|
if(nkind==1)
|
|
{
|
|
s=0;
|
|
for(j=0;j<=nout-1;j++)
|
|
s=s+MathExp(neurons[nlayers-1][j]);
|
|
for(j=0;j<=nout-1;j++)
|
|
neurons[nlayers-1].Set(j,MathExp(neurons[nlayers-1][j])/s);
|
|
}
|
|
//--- calculation
|
|
for(j=0;j<=nout-1;j++)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPGetOutputScaling(network,j,mean,sigma);
|
|
neurons[nlayers-1].Set(j,neurons[nlayers-1][j]*sigma+mean);
|
|
}
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x,y);
|
|
//--- search errors
|
|
for(j=0;j<=nout-1;j++)
|
|
err=err || MathAbs(neurons[nlayers-1][j]-y[j])>threshold;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Processing functions test |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMLPTrainUnit::TestProcessing(const int nkind,const int nin,
|
|
const int nhid1,const int nhid2,
|
|
const int nout,const int passcount,
|
|
bool &err)
|
|
{
|
|
//--- create variables
|
|
int n1=0;
|
|
int n2=0;
|
|
int wcount=0;
|
|
bool zeronet;
|
|
double a1=0;
|
|
double a2=0;
|
|
int pass=0;
|
|
int i=0;
|
|
bool allsame;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x1[];
|
|
double x2[];
|
|
double y1[];
|
|
double y2[];
|
|
//--- objects of classes
|
|
CMultilayerPerceptron network;
|
|
CMultilayerPerceptron network2;
|
|
//--- check
|
|
if(!CAp::Assert(passcount>=2,"PassCount<2!"))
|
|
return;
|
|
//--- Prepare network
|
|
a1=0;
|
|
a2=0;
|
|
//--- check
|
|
if(nkind==2)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- check
|
|
if(nkind==3)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
|
|
}
|
|
//--- function calls
|
|
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
|
|
CMLPBase::MLPProperties(network,n1,n2,wcount);
|
|
//--- Initialize arrays
|
|
ArrayResize(x1,nin);
|
|
ArrayResize(x2,nin);
|
|
ArrayResize(y1,nout);
|
|
ArrayResize(y2,nout);
|
|
//--- Main cycle
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Last run is made on zero network
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
zeronet=false;
|
|
//--- check
|
|
if(pass==passcount)
|
|
{
|
|
for(i_=0;i_<=wcount-1;i_++)
|
|
network.m_weights[i_]=0*network.m_weights[i_];
|
|
zeronet=true;
|
|
}
|
|
//--- Same inputs leads to same outputs
|
|
for(i=0;i<=nin-1;i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
for(i=0;i<=nout-1;i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network,x2,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0;i<=nout-1;i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err || !allsame;
|
|
//--- Same inputs on original network leads to same outputs
|
|
//--- on copy created using MLPCopy
|
|
UnsetNetwork(network2);
|
|
CMLPBase::MLPCopy(network,network2);
|
|
//--- change values
|
|
for(i=0;i<=nin-1;i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
for(i=0;i<=nout-1;i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network2,x2,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0;i<=nout-1;i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err || !allsame;
|
|
//--- Same inputs on original network leads to same outputs
|
|
//--- on copy created using MLPSerialize
|
|
UnsetNetwork(network2);
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CMLPBase::MLPAlloc(_local_serializer,network);
|
|
_local_serializer.SStart_Str();
|
|
CMLPBase::MLPSerialize(_local_serializer,network);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CMLPBase::MLPUnserialize(_local_serializer,network2);
|
|
_local_serializer.Stop();
|
|
//--- change values
|
|
for(i=0;i<=nin-1;i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
for(i=0;i<=nout-1;i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network2,x2,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0;i<=nout-1;i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err || !allsame;
|
|
//--- Different inputs leads to different outputs (non-zero network)
|
|
if(!zeronet)
|
|
{
|
|
for(i=0;i<=nin-1;i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
for(i=0;i<=nout-1;i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=y1[i];
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network,x2,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0;i<=nout-1;i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err || allsame;
|
|
}
|
|
//--- Randomization changes outputs (when inputs are unchanged,non-zero network)
|
|
if(!zeronet)
|
|
{
|
|
for(i=0;i<=nin-1;i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
for(i=0;i<=nout-1;i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=y1[i];
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPCopy(network,network2);
|
|
CMLPBase::MLPRandomize(network2);
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network2,x1,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0;i<=nout-1;i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err || allsame;
|
|
}
|
|
//--- Full randomization changes outputs (when inputs are unchanged,non-zero network)
|
|
if(!zeronet)
|
|
{
|
|
for(i=0;i<=nin-1;i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
for(i=0;i<=nout-1;i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=y1[i];
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPCopy(network,network2);
|
|
CMLPBase::MLPRandomizeFull(network2);
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
CMLPBase::MLPProcess(network2,x1,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0;i<=nout-1;i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err || allsame;
|
|
}
|
|
//--- Normalization properties
|
|
if(nkind==1)
|
|
{
|
|
//--- Classifier network outputs are normalized
|
|
for(i=0;i<=nin-1;i++)
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
v=0;
|
|
for(i=0;i<=nout-1;i++)
|
|
{
|
|
v=v+y1[i];
|
|
//--- search errors
|
|
err=err || y1[i]<0.0;
|
|
}
|
|
//--- search errors
|
|
err=err || MathAbs(v-1)>1000*CMath::m_machineepsilon;
|
|
}
|
|
//--- check
|
|
if(nkind==2)
|
|
{
|
|
//--- B-type network outputs are bounded from above/below
|
|
for(i=0;i<=nin-1;i++)
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
for(i=0;i<=nout-1;i++)
|
|
{
|
|
//--- check
|
|
if(a2>=0.0)
|
|
err=err || y1[i]<a1;
|
|
else
|
|
err=err || y1[i]>a1;
|
|
}
|
|
}
|
|
//--- check
|
|
if(nkind==3)
|
|
{
|
|
//--- R-type network outputs are within [A1,A2] (or [A2,A1])
|
|
for(i=0;i<=nin-1;i++)
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x1,y1);
|
|
//--- search errors
|
|
for(i=0;i<=nout-1;i++)
|
|
err=(err || y1[i]<MathMin(a1,a2)) || y1[i]>MathMax(a1,a2);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Gradient functions test |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMLPTrainUnit::TestGradient(const int nkind,const int nin,
|
|
const int nhid1,const int nhid2,
|
|
const int nout,const int passcount,
|
|
bool &err)
|
|
{
|
|
//--- create variables
|
|
int n1=0;
|
|
int n2=0;
|
|
int wcount=0;
|
|
double h=0;
|
|
double etol=0;
|
|
double a1=0;
|
|
double a2=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int ssize=0;
|
|
double v=0;
|
|
double e=0;
|
|
double e1=0;
|
|
double e2=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double v3=0;
|
|
double v4=0;
|
|
double wprev=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double grad1[];
|
|
double grad2[];
|
|
double x[];
|
|
double y[];
|
|
double x1[];
|
|
double x2[];
|
|
double y1[];
|
|
double y2[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- object of class
|
|
CMultilayerPerceptron network;
|
|
//--- check
|
|
if(!CAp::Assert(passcount>=2,"PassCount<2!"))
|
|
return;
|
|
//--- change values
|
|
a1=0;
|
|
a2=0;
|
|
//--- check
|
|
if(nkind==2)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- check
|
|
if(nkind==3)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
|
|
}
|
|
//--- function calls
|
|
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
|
|
CMLPBase::MLPProperties(network,n1,n2,wcount);
|
|
//--- change values
|
|
h=0.0001;
|
|
etol=0.01;
|
|
//--- Initialize
|
|
ArrayResize(x,nin);
|
|
ArrayResize(x1,nin);
|
|
ArrayResize(x2,nin);
|
|
ArrayResize(y,nout);
|
|
ArrayResize(y1,nout);
|
|
ArrayResize(y2,nout);
|
|
ArrayResize(grad1,wcount);
|
|
ArrayResize(grad2,wcount);
|
|
//--- Process
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
//--- Test error/gradient calculation (least squares)
|
|
xy.Resize(1,nin+nout);
|
|
for(i=0;i<=nin-1;i++)
|
|
x[i]=4*CMath::RandomReal()-2;
|
|
for(i_=0;i_<=nin-1;i_++)
|
|
xy[0].Set(i_,x[i_]);
|
|
//--- check
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(i=0;i<=nout-1;i++)
|
|
y[i]=0;
|
|
xy[0].Set(nin,CMath::RandomInteger(nout));
|
|
y[(int)MathRound(xy[0][nin])]=1;
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=nout-1;i++)
|
|
y[i]=4*CMath::RandomReal()-2;
|
|
i1_=-nin;
|
|
for(i_=nin;i_<=nin+nout-1;i_++)
|
|
xy[0].Set(i_,y[i_+i1_]);
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPGrad(network,x,y,e,grad2);
|
|
CMLPBase::MLPProcess(network,x,y2);
|
|
for(i_=0;i_<=nout-1;i_++)
|
|
y2[i_]=y2[i_]-y[i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=nout-1;i_++)
|
|
v+=y2[i_]*y2[i_];
|
|
v=v/2;
|
|
//--- search errors
|
|
err=err || MathAbs((v-e)/v)>etol;
|
|
err=err || MathAbs((CMLPBase::MLPError(network,xy,1)-v)/v)>etol;
|
|
for(i=0;i<=wcount-1;i++)
|
|
{
|
|
wprev=network.m_weights[i];
|
|
network.m_weights[i]=wprev-2*h;
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x,y1);
|
|
for(i_=0;i_<=nout-1;i_++)
|
|
y1[i_]=y1[i_]-y[i_];
|
|
//--- change value
|
|
v1=0.0;
|
|
for(i_=0;i_<=nout-1;i_++)
|
|
v1+=y1[i_]*y1[i_];
|
|
v1=v1/2;
|
|
network.m_weights[i]=wprev-h;
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x,y1);
|
|
for(i_=0;i_<=nout-1;i_++)
|
|
y1[i_]=y1[i_]-y[i_];
|
|
//--- change value
|
|
v2=0.0;
|
|
for(i_=0;i_<=nout-1;i_++)
|
|
v2+=y1[i_]*y1[i_];
|
|
v2=v2/2;
|
|
network.m_weights[i]=wprev+h;
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x,y1);
|
|
for(i_=0;i_<=nout-1;i_++)
|
|
y1[i_]=y1[i_]-y[i_];
|
|
//--- change value
|
|
v3=0.0;
|
|
for(i_=0;i_<=nout-1;i_++)
|
|
v3+=y1[i_]*y1[i_];
|
|
v3=v3/2;
|
|
network.m_weights[i]=wprev+2*h;
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x,y1);
|
|
for(i_=0;i_<=nout-1;i_++)
|
|
y1[i_]=y1[i_]-y[i_];
|
|
//--- change value
|
|
v4=0.0;
|
|
for(i_=0;i_<=nout-1;i_++)
|
|
v4+=y1[i_]*y1[i_];
|
|
v4=v4/2;
|
|
network.m_weights[i]=wprev;
|
|
grad1[i]=(v1-8*v2+8*v3-v4)/(12*h);
|
|
//--- check
|
|
if(MathAbs(grad1[i])>1.0E-3)
|
|
err=err || MathAbs((grad2[i]-grad1[i])/grad1[i])>etol;
|
|
else
|
|
err=err || MathAbs(grad2[i]-grad1[i])>etol;
|
|
}
|
|
//--- Test error/gradient calculation (natural).
|
|
//--- Testing on non-random structure networks
|
|
//--- (because NKind is representative only in that case).
|
|
xy.Resize(1,nin+nout);
|
|
for(i=0;i<=nin-1;i++)
|
|
x[i]=4*CMath::RandomReal()-2;
|
|
for(i_=0;i_<=nin-1;i_++)
|
|
xy[0].Set(i_,x[i_]);
|
|
//--- check
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(i=0;i<=nout-1;i++)
|
|
y[i]=0;
|
|
xy[0].Set(nin,CMath::RandomInteger(nout));
|
|
y[(int)MathRound(xy[0][nin])]=1;
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=nout-1;i++)
|
|
y[i]=4*CMath::RandomReal()-2;
|
|
i1_=-nin;
|
|
for(i_=nin;i_<=nin+nout-1;i_++)
|
|
xy[0].Set(i_,y[i_+i1_]);
|
|
}
|
|
//--- function calls
|
|
CMLPBase::MLPGradN(network,x,y,e,grad2);
|
|
CMLPBase::MLPProcess(network,x,y2);
|
|
v=0;
|
|
//--- check
|
|
if(nkind!=1)
|
|
{
|
|
for(i=0;i<=nout-1;i++)
|
|
v=v+0.5*CMath::Sqr(y2[i]-y[i]);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=nout-1;i++)
|
|
{
|
|
//--- check
|
|
if(y[i]!=0.0)
|
|
{
|
|
//--- check
|
|
if(y2[i]==0.0)
|
|
v=v+y[i]*MathLog(CMath::m_maxrealnumber);
|
|
else
|
|
v=v+y[i]*MathLog(y[i]/y2[i]);
|
|
}
|
|
}
|
|
}
|
|
//--- search errors
|
|
err=err || MathAbs((v-e)/v)>etol;
|
|
err=err || MathAbs((CMLPBase::MLPErrorN(network,xy,1)-v)/v)>etol;
|
|
for(i=0;i<=wcount-1;i++)
|
|
{
|
|
wprev=network.m_weights[i];
|
|
network.m_weights[i]=wprev+h;
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x,y2);
|
|
network.m_weights[i]=wprev-h;
|
|
//--- function call
|
|
CMLPBase::MLPProcess(network,x,y1);
|
|
network.m_weights[i]=wprev;
|
|
v=0;
|
|
//--- check
|
|
if(nkind!=1)
|
|
{
|
|
for(j=0;j<=nout-1;j++)
|
|
v=v+0.5*(CMath::Sqr(y2[j]-y[j])-CMath::Sqr(y1[j]-y[j]))/(2*h);
|
|
}
|
|
else
|
|
{
|
|
for(j=0;j<=nout-1;j++)
|
|
{
|
|
//--- check
|
|
if(y[j]!=0.0)
|
|
{
|
|
//--- check
|
|
if(y2[j]==0.0)
|
|
v=v+y[j]*MathLog(CMath::m_maxrealnumber);
|
|
else
|
|
v=v+y[j]*MathLog(y[j]/y2[j]);
|
|
//--- check
|
|
if(y1[j]==0.0)
|
|
v=v-y[j]*MathLog(CMath::m_maxrealnumber);
|
|
else
|
|
v=v-y[j]*MathLog(y[j]/y1[j]);
|
|
}
|
|
}
|
|
v=v/(2*h);
|
|
}
|
|
grad1[i]=v;
|
|
//--- check
|
|
if(MathAbs(grad1[i])>1.0E-3)
|
|
err=err || MathAbs((grad2[i]-grad1[i])/grad1[i])>etol;
|
|
else
|
|
err=err || MathAbs(grad2[i]-grad1[i])>etol;
|
|
}
|
|
//--- Test gradient calculation: batch (least squares)
|
|
ssize=1+CMath::RandomInteger(10);
|
|
xy.Resize(ssize,nin+nout);
|
|
for(i=0;i<=wcount-1;i++)
|
|
grad1[i]=0;
|
|
//--- calculation
|
|
e1=0;
|
|
for(i=0;i<=ssize-1;i++)
|
|
{
|
|
for(j=0;j<=nin-1;j++)
|
|
x1[j]=4*CMath::RandomReal()-2;
|
|
for(i_=0;i_<=nin-1;i_++)
|
|
xy[i].Set(i_,x1[i_]);
|
|
//--- check
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(j=0;j<=nout-1;j++)
|
|
y1[j]=0;
|
|
//--- change values
|
|
xy[i].Set(nin,CMath::RandomInteger(nout));
|
|
y1[(int)MathRound(xy[i][nin])]=1;
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
for(j=0;j<=nout-1;j++)
|
|
y1[j]=4*CMath::RandomReal()-2;
|
|
i1_=-nin;
|
|
for(i_=nin;i_<=nin+nout-1;i_++)
|
|
xy[i].Set(i_,y1[i_+i1_]);
|
|
}
|
|
//--- function call
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad2);
|
|
//--- change values
|
|
e1=e1+v;
|
|
for(i_=0;i_<=wcount-1;i_++)
|
|
grad1[i_]=grad1[i_]+grad2[i_];
|
|
}
|
|
//--- function call
|
|
CMLPBase::MLPGradBatch(network,xy,ssize,e2,grad2);
|
|
//--- search errors
|
|
err=err || MathAbs(e1-e2)/e1>0.01;
|
|
for(i=0;i<=wcount-1;i++)
|
|
{
|
|
//--- check
|
|
if(grad1[i]!=0.0)
|
|
err=err || MathAbs((grad2[i]-grad1[i])/grad1[i])>etol;
|
|
else
|
|
err=err || grad2[i]!=grad1[i];
|
|
}
|
|
//--- Test gradient calculation: batch (natural error func)
|
|
ssize=1+CMath::RandomInteger(10);
|
|
xy.Resize(ssize,nin+nout);
|
|
for(i=0;i<=wcount-1;i++)
|
|
grad1[i]=0;
|
|
//--- calculation
|
|
e1=0;
|
|
for(i=0;i<=ssize-1;i++)
|
|
{
|
|
for(j=0;j<=nin-1;j++)
|
|
x1[j]=4*CMath::RandomReal()-2;
|
|
for(i_=0;i_<=nin-1;i_++)
|
|
xy[i].Set(i_,x1[i_]);
|
|
//--- check
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(j=0;j<=nout-1;j++)
|
|
y1[j]=0;
|
|
//--- change values
|
|
xy[i].Set(nin,CMath::RandomInteger(nout));
|
|
y1[(int)MathRound(xy[i][nin])]=1;
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
for(j=0;j<=nout-1;j++)
|
|
y1[j]=4*CMath::RandomReal()-2;
|
|
i1_=-nin;
|
|
for(i_=nin;i_<=nin+nout-1;i_++)
|
|
xy[i].Set(i_,y1[i_+i1_]);
|
|
}
|
|
//--- function call
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad2);
|
|
//--- change values
|
|
e1=e1+v;
|
|
for(i_=0;i_<=wcount-1;i_++)
|
|
grad1[i_]=grad1[i_]+grad2[i_];
|
|
}
|
|
//--- function call
|
|
CMLPBase::MLPGradNBatch(network,xy,ssize,e2,grad2);
|
|
//--- search errors
|
|
err=err || MathAbs(e1-e2)/e1>etol;
|
|
for(i=0;i<=wcount-1;i++)
|
|
{
|
|
//--- check
|
|
if(grad1[i]!=0.0)
|
|
err=err || MathAbs((grad2[i]-grad1[i])/grad1[i])>etol;
|
|
else
|
|
err=err || grad2[i]!=grad1[i];
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Hessian functions test |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMLPTrainUnit::TestHessian(const int nkind,const int nin,
|
|
const int nhid1,const int nhid2,
|
|
const int nout,const int passcount,
|
|
bool &err)
|
|
{
|
|
//--- create variables
|
|
int hkind=0;
|
|
int n1=0;
|
|
int n2=0;
|
|
int wcount=0;
|
|
double h=0;
|
|
double etol=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int ssize=0;
|
|
double a1=0;
|
|
double a2=0;
|
|
double v=0;
|
|
double e1=0;
|
|
double e2=0;
|
|
double wprev=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create arrays
|
|
double grad1[];
|
|
double grad2[];
|
|
double grad3[];
|
|
double x[];
|
|
double y[];
|
|
double x1[];
|
|
double x2[];
|
|
double y1[];
|
|
double y2[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
CMatrixDouble h1;
|
|
CMatrixDouble h2;
|
|
//--- ������ ������
|
|
CMultilayerPerceptron network;
|
|
//--- check
|
|
if(!CAp::Assert(passcount>=2,"PassCount<2!"))
|
|
return;
|
|
//--- change values
|
|
a1=0;
|
|
a2=0;
|
|
//--- check
|
|
if(nkind==2)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- check
|
|
if(nkind==3)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
|
|
}
|
|
//--- function calls
|
|
CreateNetwork(network,nkind,a1,a2,nin,nhid1,nhid2,nout);
|
|
CMLPBase::MLPProperties(network,n1,n2,wcount);
|
|
//--- change values
|
|
h=0.0001;
|
|
etol=0.05;
|
|
//--- Initialize
|
|
ArrayResize(x,nin);
|
|
ArrayResize(x1,nin);
|
|
ArrayResize(x2,nin);
|
|
ArrayResize(y,nout);
|
|
ArrayResize(y1,nout);
|
|
ArrayResize(y2,nout);
|
|
ArrayResize(grad1,wcount);
|
|
ArrayResize(grad2,wcount);
|
|
ArrayResize(grad3,wcount);
|
|
h1.Resize(wcount,wcount);
|
|
h2.Resize(wcount,wcount);
|
|
//--- Process
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- function call
|
|
CMLPBase::MLPRandomizeFull(network);
|
|
//--- Test hessian calculation .
|
|
//--- E1 contains total error (calculated using MLPGrad/MLPGradN)
|
|
//--- Grad1 contains total gradient (calculated using MLPGrad/MLPGradN)
|
|
//--- H1 contains Hessian calculated using differences of gradients
|
|
//--- E2,Grad2 and H2 contains corresponing values calculated using MLPHessianBatch/MLPHessianNBatch
|
|
for(hkind=0;hkind<=1;hkind++)
|
|
{
|
|
ssize=1+CMath::RandomInteger(10);
|
|
xy.Resize(ssize,nin+nout);
|
|
for(i=0;i<=wcount-1;i++)
|
|
grad1[i]=0;
|
|
//--- change values
|
|
for(i=0;i<=wcount-1;i++)
|
|
{
|
|
for(j=0;j<=wcount-1;j++)
|
|
h1[i].Set(j,0);
|
|
}
|
|
//--- calculation
|
|
e1=0;
|
|
for(i=0;i<=ssize-1;i++)
|
|
{
|
|
//--- X,Y
|
|
for(j=0;j<=nin-1;j++)
|
|
x1[j]=4*CMath::RandomReal()-2;
|
|
for(i_=0;i_<=nin-1;i_++)
|
|
xy[i].Set(i_,x1[i_]);
|
|
//--- check
|
|
if(CMLPBase::MLPIsSoftMax(network))
|
|
{
|
|
for(j=0;j<=nout-1;j++)
|
|
y1[j]=0;
|
|
//--- change values
|
|
xy[i].Set(nin,CMath::RandomInteger(nout));
|
|
y1[(int)MathRound(xy[i][nin])]=1;
|
|
}
|
|
else
|
|
{
|
|
//--- change values
|
|
for(j=0;j<=nout-1;j++)
|
|
y1[j]=4*CMath::RandomReal()-2;
|
|
i1_=-nin;
|
|
for(i_=nin;i_<=nin+nout-1;i_++)
|
|
xy[i].Set(i_,y1[i_+i1_]);
|
|
}
|
|
//--- E1,Grad1
|
|
if(hkind==0)
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad2);
|
|
else
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad2);
|
|
//--- change values
|
|
e1=e1+v;
|
|
for(i_=0;i_<=wcount-1;i_++)
|
|
grad1[i_]=grad1[i_]+grad2[i_];
|
|
//--- H1
|
|
for(j=0;j<=wcount-1;j++)
|
|
{
|
|
wprev=network.m_weights[j];
|
|
network.m_weights[j]=wprev-2*h;
|
|
//--- check
|
|
if(hkind==0)
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad2);
|
|
else
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad2);
|
|
network.m_weights[j]=wprev-h;
|
|
//--- check
|
|
if(hkind==0)
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad3);
|
|
else
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad3);
|
|
//--- change values
|
|
for(i_=0;i_<=wcount-1;i_++)
|
|
grad2[i_]=grad2[i_]-8*grad3[i_];
|
|
network.m_weights[j]=wprev+h;
|
|
//--- check
|
|
if(hkind==0)
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad3);
|
|
else
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad3);
|
|
//--- change values
|
|
for(i_=0;i_<=wcount-1;i_++)
|
|
grad2[i_]=grad2[i_]+8*grad3[i_];
|
|
network.m_weights[j]=wprev+2*h;
|
|
//--- check
|
|
if(hkind==0)
|
|
CMLPBase::MLPGrad(network,x1,y1,v,grad3);
|
|
else
|
|
CMLPBase::MLPGradN(network,x1,y1,v,grad3);
|
|
//--- change values
|
|
for(i_=0;i_<=wcount-1;i_++)
|
|
grad2[i_]=grad2[i_]-grad3[i_];
|
|
v=1/(12*h);
|
|
for(i_=0;i_<=wcount-1;i_++)
|
|
h1[j].Set(i_,h1[j][i_]+v*grad2[i_]);
|
|
network.m_weights[j]=wprev;
|
|
}
|
|
}
|
|
//--- check
|
|
if(hkind==0)
|
|
CMLPBase::MLPHessianBatch(network,xy,ssize,e2,grad2,h2);
|
|
else
|
|
CMLPBase::MLPHessianNBatch(network,xy,ssize,e2,grad2,h2);
|
|
//--- search errors
|
|
err=err || MathAbs(e1-e2)/e1>etol;
|
|
for(i=0;i<=wcount-1;i++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(grad1[i])>1.0E-2)
|
|
err=err || MathAbs((grad2[i]-grad1[i])/grad1[i])>etol;
|
|
else
|
|
err=err || MathAbs(grad2[i]-grad1[i])>etol;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=wcount-1;i++)
|
|
{
|
|
for(j=0;j<=wcount-1;j++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(h1[i][j])>5.0E-2)
|
|
err=err || MathAbs((h1[i][j]-h2[i][j])/h1[i][j])>etol;
|
|
else
|
|
err=err || MathAbs(h2[i][j]-h1[i][j])>etol;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMLPE |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMLPEUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void CreateEnsemble(CMLPEnsemble &ensemble,const int nkind,const double a1,const double a2,const int nin,const int nhid1,const int nhid2,const int nout,const int ec);
|
|
static void UnsetEnsemble(CMLPEnsemble &ensemble);
|
|
static void TestInformational(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int ec,const int passcount,bool &err);
|
|
static void TestProcessing(const int nkind,const int nin,const int nhid1,const int nhid2,const int nout,const int ec,const int passcount,bool &err);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestMLPEUnit(void);
|
|
~CTestMLPEUnit(void);
|
|
//--- public method
|
|
static bool TestMLPE(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestMLPEUnit::CTestMLPEUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestMLPEUnit::~CTestMLPEUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMLPE |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMLPEUnit::TestMLPE(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
int passcount=0;
|
|
int maxn=0;
|
|
int maxhid=0;
|
|
int nf=0;
|
|
int nhid=0;
|
|
int nl=0;
|
|
int nhid1=0;
|
|
int nhid2=0;
|
|
int ec=0;
|
|
int nkind=0;
|
|
int algtype=0;
|
|
int tasktype=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int nin=0;
|
|
int nout=0;
|
|
int npoints=0;
|
|
double e=0;
|
|
int info=0;
|
|
int nless=0;
|
|
int nall=0;
|
|
int nclasses=0;
|
|
bool inferrors;
|
|
bool procerrors;
|
|
bool trnerrors;
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CMLPEnsemble ensemble;
|
|
CMLPReport rep;
|
|
CMLPCVReport oobrep;
|
|
//--- initialization
|
|
waserrors=false;
|
|
inferrors=false;
|
|
procerrors=false;
|
|
trnerrors=false;
|
|
passcount=10;
|
|
maxn=4;
|
|
maxhid=4;
|
|
//--- General MLP ensembles tests
|
|
for(nf=1;nf<=maxn;nf++)
|
|
{
|
|
for(nl=1;nl<=maxn;nl++)
|
|
{
|
|
for(nhid1=0;nhid1<=maxhid;nhid1++)
|
|
{
|
|
for(nhid2=0;nhid2<=0;nhid2++)
|
|
{
|
|
for(nkind=0;nkind<=3;nkind++)
|
|
{
|
|
for(ec=1;ec<=3;ec++)
|
|
{
|
|
//--- Skip meaningless parameters combinations
|
|
if(nkind==1 && nl<2)
|
|
continue;
|
|
//--- check
|
|
if(nhid1==0 && nhid2!=0)
|
|
continue;
|
|
//--- Tests
|
|
TestInformational(nkind,nf,nhid1,nhid2,nl,ec,passcount,inferrors);
|
|
TestProcessing(nkind,nf,nhid1,nhid2,nl,ec,passcount,procerrors);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- network training must reduce error
|
|
//--- test on random regression task
|
|
nin=3;
|
|
nout=2;
|
|
nhid=5;
|
|
npoints=100;
|
|
nless=0;
|
|
nall=0;
|
|
//--- calculation
|
|
for(pass=1;pass<=10;pass++)
|
|
{
|
|
for(algtype=0;algtype<=1;algtype++)
|
|
{
|
|
for(tasktype=0;tasktype<=1;tasktype++)
|
|
{
|
|
//--- check
|
|
if(tasktype==0)
|
|
{
|
|
//--- allocation
|
|
xy.Resize(npoints,nin+nout);
|
|
//--- change values
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
for(j=0;j<=nin+nout-1;j++)
|
|
xy[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function call
|
|
CMLPE::MLPECreate1(nin,nhid,nout,1+CMath::RandomInteger(3),ensemble);
|
|
}
|
|
else
|
|
{
|
|
//--- allocation
|
|
xy.Resize(npoints,nin+1);
|
|
//--- change values
|
|
nclasses=2+CMath::RandomInteger(2);
|
|
for(i=0;i<=npoints-1;i++)
|
|
{
|
|
for(j=0;j<=nin-1;j++)
|
|
xy[i].Set(j,2*CMath::RandomReal()-1);
|
|
xy[i].Set(nin,CMath::RandomInteger(nclasses));
|
|
}
|
|
//--- function call
|
|
CMLPE::MLPECreateC1(nin,nhid,nclasses,1+CMath::RandomInteger(3),ensemble);
|
|
}
|
|
e=CMLPE::MLPERMSError(ensemble,xy,npoints);
|
|
//--- check
|
|
if(algtype==0)
|
|
CMLPE::MLPEBaggingLM(ensemble,xy,npoints,0.001,1,info,rep,oobrep);
|
|
else
|
|
CMLPE::MLPEBaggingLBFGS(ensemble,xy,npoints,0.001,1,0.01,0,info,rep,oobrep);
|
|
//--- check
|
|
if(info<0)
|
|
trnerrors=true;
|
|
else
|
|
{
|
|
//--- check
|
|
if(CMLPE::MLPERMSError(ensemble,xy,npoints)<e)
|
|
nless=nless+1;
|
|
}
|
|
//--- change value
|
|
nall=nall+1;
|
|
}
|
|
}
|
|
}
|
|
//--- search errors
|
|
trnerrors=trnerrors || nall-nless>0.3*nall;
|
|
//--- Final report
|
|
waserrors=(inferrors || procerrors) || trnerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("MLP ENSEMBLE TEST");
|
|
Print("INFORMATIONAL FUNCTIONS: ");
|
|
//--- check
|
|
if(!inferrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("BASIC PROCESSING: ");
|
|
//--- check
|
|
if(!procerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("TRAINING: ");
|
|
//--- check
|
|
if(!trnerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST SUMMARY: FAILED");
|
|
else
|
|
Print("TEST SUMMARY: PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Network creation |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMLPEUnit::CreateEnsemble(CMLPEnsemble &ensemble,const int nkind,
|
|
const double a1,const double a2,
|
|
const int nin,const int nhid1,
|
|
const int nhid2,const int nout,
|
|
const int ec)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(((nin>0 && nhid1>=0) && nhid2>=0) && nout>0,"CreateNetwork error"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(nhid1!=0 || nhid2==0,"CreateNetwork error"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(nkind!=1 || nout>=2,"CreateNetwork error"))
|
|
return;
|
|
//--- check
|
|
if(nhid1==0)
|
|
{
|
|
//--- No hidden layers
|
|
if(nkind==0)
|
|
CMLPE::MLPECreate0(nin,nout,ec,ensemble);
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==1)
|
|
CMLPE::MLPECreateC0(nin,nout,ec,ensemble);
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==2)
|
|
CMLPE::MLPECreateB0(nin,nout,a1,a2,ec,ensemble);
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==3)
|
|
CMLPE::MLPECreateR0(nin,nout,a1,a2,ec,ensemble);
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(nhid2==0)
|
|
{
|
|
//--- One hidden layer
|
|
if(nkind==0)
|
|
CMLPE::MLPECreate1(nin,nhid1,nout,ec,ensemble);
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==1)
|
|
CMLPE::MLPECreateC1(nin,nhid1,nout,ec,ensemble);
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==2)
|
|
CMLPE::MLPECreateB1(nin,nhid1,nout,a1,a2,ec,ensemble);
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==3)
|
|
CMLPE::MLPECreateR1(nin,nhid1,nout,a1,a2,ec,ensemble);
|
|
}
|
|
}
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- Two hidden layers
|
|
if(nkind==0)
|
|
CMLPE::MLPECreate2(nin,nhid1,nhid2,nout,ec,ensemble);
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==1)
|
|
CMLPE::MLPECreateC2(nin,nhid1,nhid2,nout,ec,ensemble);
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==2)
|
|
CMLPE::MLPECreateB2(nin,nhid1,nhid2,nout,a1,a2,ec,ensemble);
|
|
else
|
|
{
|
|
//--- check
|
|
if(nkind==3)
|
|
CMLPE::MLPECreateR2(nin,nhid1,nhid2,nout,a1,a2,ec,ensemble);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets network (initialize it to smallest network possible |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMLPEUnit::UnsetEnsemble(CMLPEnsemble &ensemble)
|
|
{
|
|
//--- function call
|
|
CMLPE::MLPECreate0(1,1,1,ensemble);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Iformational functions test |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMLPEUnit::TestInformational(const int nkind,const int nin,
|
|
const int nhid1,const int nhid2,
|
|
const int nout,const int ec,
|
|
const int passcount,bool &err)
|
|
{
|
|
//--- create variables
|
|
int n1=0;
|
|
int n2=0;
|
|
//--- object of class
|
|
CMLPEnsemble ensemble;
|
|
//--- function calls
|
|
CreateEnsemble(ensemble,nkind,-1.0,1.0,nin,nhid1,nhid2,nout,ec);
|
|
CMLPE::MLPEProperties(ensemble,n1,n2);
|
|
//--- search errors
|
|
err=(err || n1!=nin) || n2!=nout;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Processing functions test |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMLPEUnit::TestProcessing(const int nkind,const int nin,
|
|
const int nhid1,const int nhid2,
|
|
const int nout,const int ec,
|
|
const int passcount,bool &err)
|
|
{
|
|
//--- create variables
|
|
double a1=0;
|
|
double a2=0;
|
|
int pass=0;
|
|
int i=0;
|
|
bool allsame;
|
|
int rlen=0;
|
|
double v=0;
|
|
//--- create arrays
|
|
double x1[];
|
|
double x2[];
|
|
double y1[];
|
|
double y2[];
|
|
double ra[];
|
|
double ra2[];
|
|
//--- objects of classes ������
|
|
CMLPEnsemble ensemble;
|
|
CMLPEnsemble ensemble2;
|
|
//--- Prepare network
|
|
a1=0;
|
|
a2=0;
|
|
//--- check
|
|
if(nkind==2)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- check
|
|
if(nkind==3)
|
|
{
|
|
a1=1000*CMath::RandomReal()-500;
|
|
a2=a1+(2*CMath::RandomInteger(2)-1)*(0.1+0.9*CMath::RandomReal());
|
|
}
|
|
//--- Initialize arrays
|
|
ArrayResize(x1,nin);
|
|
ArrayResize(x2,nin);
|
|
ArrayResize(y1,nout);
|
|
ArrayResize(y2,nout);
|
|
//--- Main cycle
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- function call
|
|
CreateEnsemble(ensemble,nkind,a1,a2,nin,nhid1,nhid2,nout,ec);
|
|
//--- Same inputs leads to same outputs
|
|
for(i=0;i<=nin-1;i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
for(i=0;i<=nout-1;i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function calls
|
|
CMLPE::MLPEProcess(ensemble,x1,y1);
|
|
CMLPE::MLPEProcess(ensemble,x2,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0;i<=nout-1;i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err || !allsame;
|
|
//--- Same inputs on original network leads to same outputs
|
|
//--- on copy created using MLPCopy
|
|
UnsetEnsemble(ensemble2);
|
|
CMLPE::MLPECopy(ensemble,ensemble2);
|
|
for(i=0;i<=nin-1;i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
for(i=0;i<=nout-1;i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function calls
|
|
CMLPE::MLPEProcess(ensemble,x1,y1);
|
|
CMLPE::MLPEProcess(ensemble2,x2,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0;i<=nout-1;i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err || !allsame;
|
|
//--- Same inputs on original network leads to same outputs
|
|
//--- on copy created using MLPSerialize
|
|
UnsetEnsemble(ensemble2);
|
|
CMLPE::MLPESerialize(ensemble,ra,rlen);
|
|
//--- allocation
|
|
ArrayResize(ra2,rlen);
|
|
//--- copy
|
|
for(i=0;i<=rlen-1;i++)
|
|
ra2[i]=ra[i];
|
|
//--- function call
|
|
CMLPE::MLPEUnserialize(ra2,ensemble2);
|
|
for(i=0;i<=nin-1;i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=x1[i];
|
|
}
|
|
for(i=0;i<=nout-1;i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function calls
|
|
CMLPE::MLPEProcess(ensemble,x1,y1);
|
|
CMLPE::MLPEProcess(ensemble2,x2,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0;i<=nout-1;i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err || !allsame;
|
|
//--- Different inputs leads to different outputs (non-zero network)
|
|
for(i=0;i<=nin-1;i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
for(i=0;i<=nout-1;i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=y1[i];
|
|
}
|
|
//--- function calls
|
|
CMLPE::MLPEProcess(ensemble,x1,y1);
|
|
CMLPE::MLPEProcess(ensemble,x2,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0;i<=nout-1;i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err || allsame;
|
|
//--- Randomization changes outputs (when inputs are unchanged,non-zero network)
|
|
for(i=0;i<=nin-1;i++)
|
|
{
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
x2[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
for(i=0;i<=nout-1;i++)
|
|
{
|
|
y1[i]=2*CMath::RandomReal()-1;
|
|
y2[i]=y1[i];
|
|
}
|
|
//--- function calls
|
|
CMLPE::MLPECopy(ensemble,ensemble2);
|
|
CMLPE::MLPERandomize(ensemble2);
|
|
CMLPE::MLPEProcess(ensemble,x1,y1);
|
|
CMLPE::MLPEProcess(ensemble2,x1,y2);
|
|
//--- search errors
|
|
allsame=true;
|
|
for(i=0;i<=nout-1;i++)
|
|
allsame=allsame && y1[i]==y2[i];
|
|
err=err || allsame;
|
|
//--- Normalization properties
|
|
if(nkind==1)
|
|
{
|
|
//--- Classifier network outputs are normalized
|
|
for(i=0;i<=nin-1;i++)
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMLPE::MLPEProcess(ensemble,x1,y1);
|
|
v=0;
|
|
//--- search errors
|
|
for(i=0;i<=nout-1;i++)
|
|
{
|
|
v=v+y1[i];
|
|
err=err || y1[i]<0.0;
|
|
}
|
|
err=err || MathAbs(v-1)>1000*CMath::m_machineepsilon;
|
|
}
|
|
//--- check
|
|
if(nkind==2)
|
|
{
|
|
//--- B-type network outputs are bounded from above/below
|
|
for(i=0;i<=nin-1;i++)
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMLPE::MLPEProcess(ensemble,x1,y1);
|
|
for(i=0;i<=nout-1;i++)
|
|
{
|
|
//--- check
|
|
if(a2>=0.0)
|
|
err=err || y1[i]<a1;
|
|
else
|
|
err=err || y1[i]>a1;
|
|
}
|
|
}
|
|
//--- check
|
|
if(nkind==3)
|
|
{
|
|
//--- R-type network outputs are within [A1,A2] (or [A2,A1])
|
|
for(i=0;i<=nin-1;i++)
|
|
x1[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMLPE::MLPEProcess(ensemble,x1,y1);
|
|
//--- search errors
|
|
for(i=0;i<=nout-1;i++)
|
|
err=(err || y1[i]<MathMin(a1,a2)) || y1[i]>MathMax(a1,a2);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CPCAnalysis |
|
|
//+------------------------------------------------------------------+
|
|
class CTestPCAUnit
|
|
{
|
|
private:
|
|
//--- private method
|
|
static void CalculateMV(double &x[],const int n,double &mean,double &means,double &stddev,double &stddevs);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestPCAUnit(void);
|
|
~CTestPCAUnit(void);
|
|
//--- public method
|
|
static bool TestPCA(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestPCAUnit::CTestPCAUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestPCAUnit::~CTestPCAUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CPCAnalysis |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestPCAUnit::TestPCA(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int passcount=0;
|
|
int maxn=0;
|
|
int maxm=0;
|
|
double threshold=0;
|
|
int m=0;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int info=0;
|
|
double t=0;
|
|
double h=0;
|
|
double tmean=0;
|
|
double tmeans=0;
|
|
double tstddev=0;
|
|
double tstddevs=0;
|
|
double tmean2=0;
|
|
double tmeans2=0;
|
|
double tstddev2=0;
|
|
double tstddevs2=0;
|
|
bool pcaconverrors;
|
|
bool pcaorterrors;
|
|
bool pcavarerrors;
|
|
bool pcaopterrors;
|
|
bool waserrors;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double means[];
|
|
double s[];
|
|
double t2[];
|
|
double t3[];
|
|
//--- create matrix
|
|
CMatrixDouble v;
|
|
CMatrixDouble x;
|
|
//--- Primary settings
|
|
maxm=10;
|
|
maxn=100;
|
|
passcount=1;
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
waserrors=false;
|
|
pcaconverrors=false;
|
|
pcaorterrors=false;
|
|
pcavarerrors=false;
|
|
pcaopterrors=false;
|
|
//--- Test 1: N random points in M-dimensional space
|
|
for(m=1;m<=maxm;m++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- Generate task
|
|
x.Resize(n,m);
|
|
ArrayResize(means,m);
|
|
//--- change values
|
|
for(j=0;j<=m-1;j++)
|
|
means[j]=1.5*CMath::RandomReal()-0.75;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
x[i].Set(j,means[j]+(2*CMath::RandomReal()-1));
|
|
}
|
|
//--- Solve
|
|
CPCAnalysis::PCABuildBasis(x,n,m,info,s,v);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
pcaconverrors=true;
|
|
continue;
|
|
}
|
|
//--- Orthogonality test
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
t=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
t+=v[i_][i]*v[i_][j];
|
|
//--- check
|
|
if(i==j)
|
|
t=t-1;
|
|
//--- search errors
|
|
pcaorterrors=pcaorterrors || MathAbs(t)>threshold;
|
|
}
|
|
}
|
|
//--- Variance test
|
|
ArrayResize(t2,n);
|
|
for(k=0;k<=m-1;k++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
t=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
t+=x[i][i_]*v[i_][k];
|
|
t2[i]=t;
|
|
}
|
|
//--- function call
|
|
CalculateMV(t2,n,tmean,tmeans,tstddev,tstddevs);
|
|
//--- check
|
|
if(n!=1)
|
|
t=CMath::Sqr(tstddev)*n/(n-1);
|
|
else
|
|
t=0;
|
|
//--- search errors
|
|
pcavarerrors=pcavarerrors || MathAbs(t-s[k])>threshold;
|
|
}
|
|
//--- search errors
|
|
for(k=0;k<=m-2;k++)
|
|
pcavarerrors=pcavarerrors || s[k]<s[k+1];
|
|
//--- Optimality: different perturbations in V[..,0] can't
|
|
//--- increase variance of projection - can only decrease.
|
|
ArrayResize(t2,n);
|
|
ArrayResize(t3,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change values
|
|
t=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
t+=x[i][i_]*v[i_][0];
|
|
t2[i]=t;
|
|
}
|
|
//--- function call
|
|
CalculateMV(t2,n,tmean,tmeans,tstddev,tstddevs);
|
|
//--- calculation
|
|
for(k=0;k<=2*m-1;k++)
|
|
{
|
|
h=0.001;
|
|
//--- check
|
|
if(k%2!=0)
|
|
h=-h;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
t3[i_]=t2[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
t3[i_]=t3[i_]+h*x[i_][k/2];
|
|
//--- change value
|
|
t=0;
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- check
|
|
if(j!=k/2)
|
|
t=t+CMath::Sqr(v[j][0]);
|
|
else
|
|
t=t+CMath::Sqr(v[j][0]+h);
|
|
}
|
|
t=1/MathSqrt(t);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
t3[i_]=t*t3[i_];
|
|
//--- function call
|
|
CalculateMV(t3,n,tmean2,tmeans2,tstddev2,tstddevs2);
|
|
//--- search errors
|
|
pcaopterrors=pcaopterrors || tstddev2>tstddev+threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- Special test for N=0
|
|
for(m=1;m<=maxm;m++)
|
|
{
|
|
//--- Solve
|
|
CPCAnalysis::PCABuildBasis(x,0,m,info,s,v);
|
|
//--- check
|
|
if(info!=1)
|
|
{
|
|
pcaconverrors=true;
|
|
continue;
|
|
}
|
|
//--- Orthogonality test
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- change value
|
|
t=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
t+=v[i_][i]*v[i_][j];
|
|
//--- check
|
|
if(i==j)
|
|
t=t-1;
|
|
//--- search errors
|
|
pcaorterrors=pcaorterrors || MathAbs(t)>threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- Final report
|
|
waserrors=((pcaconverrors || pcaorterrors) || pcavarerrors) || pcaopterrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("PCA TEST");
|
|
Print("TOTAL RESULTS: ");
|
|
//--- check
|
|
if(!waserrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* CONVERGENCE ");
|
|
//--- check
|
|
if(!pcaconverrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* ORTOGONALITY ");
|
|
//--- check
|
|
if(!pcaorterrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* VARIANCE REPORT ");
|
|
//--- check
|
|
if(!pcavarerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* OPTIMALITY ");
|
|
//--- check
|
|
if(!pcaopterrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST SUMMARY: FAILED");
|
|
else
|
|
Print("TEST SUMMARY: PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Moments estimates and their errors |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestPCAUnit::CalculateMV(double &x[],const int n,double &mean,
|
|
double &means,double &stddev,double &stddevs)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double variance=0;
|
|
//--- initialization
|
|
mean=0;
|
|
means=1;
|
|
stddev=0;
|
|
stddevs=1;
|
|
variance=0;
|
|
//--- check
|
|
if(n<=1)
|
|
return;
|
|
//--- Mean
|
|
for(i=0;i<=n-1;i++)
|
|
mean=mean+x[i];
|
|
mean=mean/n;
|
|
//--- Variance (using corrected two-pass algorithm)
|
|
if(n!=1)
|
|
{
|
|
//--- change values
|
|
v1=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v1=v1+CMath::Sqr(x[i]-mean);
|
|
v2=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v2=v2+(x[i]-mean);
|
|
v2=CMath::Sqr(v2)/n;
|
|
variance=(v1-v2)/n;
|
|
//--- check
|
|
if(variance<0.0)
|
|
variance=0;
|
|
stddev=MathSqrt(variance);
|
|
}
|
|
//--- Errors
|
|
means=stddev/MathSqrt(n);
|
|
stddevs=stddev*MathSqrt(2)/MathSqrt(n-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CODESolver |
|
|
//+------------------------------------------------------------------+
|
|
class CTestODESolverUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void Unset2D(CMatrixDouble &x);
|
|
static void Unset1D(double &x[]);
|
|
static void UnsetRep(CODESolverReport &rep);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestODESolverUnit(void);
|
|
~CTestODESolverUnit(void);
|
|
//--- public method
|
|
static bool TestODESolver(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestODESolverUnit::CTestODESolverUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestODESolverUnit::~CTestODESolverUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestODESolverUnit::TestODESolver(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int passcount=0;
|
|
bool curerrors;
|
|
bool rkckerrors;
|
|
bool waserrors;
|
|
double h=0;
|
|
double eps=0;
|
|
int solver=0;
|
|
int pass=0;
|
|
int mynfev=0;
|
|
double v=0;
|
|
int m=0;
|
|
int m2=0;
|
|
int i=0;
|
|
double err=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double xtbl[];
|
|
double xg[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble ytbl;
|
|
//--- objects of classes
|
|
CODESolverReport rep;
|
|
CODESolverState state;
|
|
//--- initialization
|
|
rkckerrors=false;
|
|
waserrors=false;
|
|
passcount=10;
|
|
//--- simple test: just A*sin(x)+B*cos(x)
|
|
if(!CAp::Assert(passcount>=2))
|
|
return(false);
|
|
for(pass=0;pass<=passcount-1;pass++)
|
|
{
|
|
for(solver=0;solver<=0;solver++)
|
|
{
|
|
//--- prepare
|
|
h=1.0E-2;
|
|
eps=1.0E-5;
|
|
//--- check
|
|
if(pass%2==0)
|
|
eps=-eps;
|
|
//--- allocation
|
|
ArrayResize(y,2);
|
|
//--- change values
|
|
for(i=0;i<=1;i++)
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
m=2+CMath::RandomInteger(10);
|
|
//--- allocation
|
|
ArrayResize(xg,m);
|
|
xg[0]=(m-1)*CMath::RandomReal();
|
|
for(i=1;i<=m-1;i++)
|
|
xg[i]=xg[i-1]+CMath::RandomReal();
|
|
//--- change values
|
|
v=2*M_PI/(xg[m-1]-xg[0]);
|
|
for(i_=0;i_<=m-1;i_++)
|
|
xg[i_]=v*xg[i_];
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
for(i_=0;i_<=m-1;i_++)
|
|
xg[i_]=-1*xg[i_];
|
|
}
|
|
mynfev=0;
|
|
//--- choose solver
|
|
if(solver==0)
|
|
CODESolver::ODESolverRKCK(y,2,xg,m,eps,h,state);
|
|
//--- solve
|
|
while(CODESolver::ODESolverIteration(state))
|
|
{
|
|
state.m_dy[0]=state.m_y[1];
|
|
state.m_dy[1]=-state.m_y[0];
|
|
mynfev=mynfev+1;
|
|
}
|
|
//--- function call
|
|
CODESolver::ODESolverResults(state,m2,xtbl,ytbl,rep);
|
|
//--- check results
|
|
curerrors=false;
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
curerrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
curerrors=curerrors || m2!=m;
|
|
err=0;
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
err=MathMax(err,MathAbs(ytbl[i][0]-(y[0]*MathCos(xtbl[i]-xtbl[0])+y[1]*MathSin(xtbl[i]-xtbl[0]))));
|
|
err=MathMax(err,MathAbs(ytbl[i][1]-(-(y[0]*MathSin(xtbl[i]-xtbl[0]))+y[1]*MathCos(xtbl[i]-xtbl[0]))));
|
|
}
|
|
curerrors=curerrors || err>10*MathAbs(eps);
|
|
curerrors=curerrors || mynfev!=rep.m_nfev;
|
|
}
|
|
//--- check
|
|
if(solver==0)
|
|
rkckerrors=rkckerrors || curerrors;
|
|
}
|
|
}
|
|
//--- another test:
|
|
//--- y(0)=0
|
|
//--- dy/dx=f(x,y)
|
|
//--- f(x,y)=0, x<1
|
|
//--- x-1,x>=1
|
|
//--- with BOTH absolute and fractional tolerances.
|
|
//--- Starting from zero will be real challenge for
|
|
//--- fractional tolerance.
|
|
if(!CAp::Assert(passcount>=2))
|
|
return(false);
|
|
for(pass=0;pass<=passcount-1;pass++)
|
|
{
|
|
h=1.0E-4;
|
|
eps=1.0E-4;
|
|
//--- check
|
|
if(pass%2==0)
|
|
eps=-eps;
|
|
//--- allocation
|
|
ArrayResize(y,1);
|
|
y[0]=0;
|
|
m=21;
|
|
//--- allocation
|
|
ArrayResize(xg,m);
|
|
for(i=0;i<=m-1;i++)
|
|
xg[i]=(double)(2*i)/(double)(m-1);
|
|
mynfev=0;
|
|
//--- function call
|
|
CODESolver::ODESolverRKCK(y,1,xg,m,eps,h,state);
|
|
//--- cycle
|
|
while(CODESolver::ODESolverIteration(state))
|
|
{
|
|
state.m_dy[0]=MathMax(state.m_x-1,0);
|
|
mynfev=mynfev+1;
|
|
}
|
|
//--- function call
|
|
CODESolver::ODESolverResults(state,m2,xtbl,ytbl,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
rkckerrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
rkckerrors=rkckerrors || m2!=m;
|
|
err=0;
|
|
for(i=0;i<=m-1;i++)
|
|
err=MathMax(err,MathAbs(ytbl[i][0]-CMath::Sqr(MathMax(xg[i]-1,0))/2));
|
|
rkckerrors=rkckerrors || err>MathAbs(eps);
|
|
rkckerrors=rkckerrors || mynfev!=rep.m_nfev;
|
|
}
|
|
}
|
|
//--- end
|
|
waserrors=rkckerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING ODE SOLVER");
|
|
Print("* RK CASH-KARP: ");
|
|
//--- check
|
|
if(rkckerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real matrix |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestODESolverUnit::Unset2D(CMatrixDouble &x)
|
|
{
|
|
//--- allocation
|
|
x.Resize(1,1);
|
|
//--- change value
|
|
x[0].Set(0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real vector |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestODESolverUnit::Unset1D(double &x[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
//--- change value
|
|
x[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets report |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestODESolverUnit::UnsetRep(CODESolverReport &rep)
|
|
{
|
|
//--- change value
|
|
rep.m_nfev=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CFastFourierTransform |
|
|
//+------------------------------------------------------------------+
|
|
class CTestFFTUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void RefFFTC1D(complex &a[],const int n);
|
|
static void RefFFTC1DInv(complex &a[],const int n);
|
|
static void RefInternalCFFT(double &a[],const int nn,const bool inversefft);
|
|
static void RefInternalRFFT(double &a[],const int nn,complex &f[]);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestFFTUnit(void);
|
|
~CTestFFTUnit(void);
|
|
//--- public method
|
|
static bool TestFFT(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestFFTUnit::CTestFFTUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestFFTUnit::~CTestFFTUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestFFTUnit::TestFFT(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i=0;
|
|
int k=0;
|
|
int maxn=0;
|
|
double bidierr=0;
|
|
double bidirerr=0;
|
|
double referr=0;
|
|
double refrerr=0;
|
|
double reinterr=0;
|
|
double errtol=0;
|
|
bool referrors;
|
|
bool bidierrors;
|
|
bool refrerrors;
|
|
bool bidirerrors;
|
|
bool reinterrors;
|
|
bool waserrors;
|
|
int i_=0;
|
|
//--- create arrays
|
|
complex a1[];
|
|
complex a2[];
|
|
complex a3[];
|
|
double r1[];
|
|
double r2[];
|
|
double buf[];
|
|
//--- object of class
|
|
CFtPlan plan;
|
|
//--- initialization
|
|
maxn=128;
|
|
errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon;
|
|
bidierrors=false;
|
|
referrors=false;
|
|
bidirerrors=false;
|
|
refrerrors=false;
|
|
reinterrors=false;
|
|
waserrors=false;
|
|
//--- Test bi-directional error: norm(x-invFFT(FFT(x)))
|
|
bidierr=0;
|
|
bidirerr=0;
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- Complex FFT/invFFT
|
|
ArrayResize(a1,n);
|
|
ArrayResize(a2,n);
|
|
ArrayResize(a3,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a1[i].re=2*CMath::RandomReal()-1;
|
|
a1[i].im=2*CMath::RandomReal()-1;
|
|
a2[i]=a1[i];
|
|
a3[i]=a1[i];
|
|
}
|
|
//--- function calls
|
|
CFastFourierTransform::FFTC1D(a2,n);
|
|
CFastFourierTransform::FFTC1DInv(a2,n);
|
|
CFastFourierTransform::FFTC1DInv(a3,n);
|
|
CFastFourierTransform::FFTC1D(a3,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
bidierr=MathMax(bidierr,CMath::AbsComplex(a1[i]-a2[i]));
|
|
bidierr=MathMax(bidierr,CMath::AbsComplex(a1[i]-a3[i]));
|
|
}
|
|
//--- Real
|
|
ArrayResize(r1,n);
|
|
ArrayResize(r2,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
r1[i]=2*CMath::RandomReal()-1;
|
|
r2[i]=r1[i];
|
|
}
|
|
//--- function call
|
|
CFastFourierTransform::FFTR1D(r2,n,a1);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
r2[i_]=0*r2[i_];
|
|
//--- function call
|
|
CFastFourierTransform::FFTR1DInv(a1,n,r2);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
bidirerr=MathMax(bidirerr,CMath::AbsComplex(r1[i]-r2[i]));
|
|
}
|
|
//--- search errors
|
|
bidierrors=bidierrors || bidierr>errtol;
|
|
bidirerrors=bidirerrors || bidirerr>errtol;
|
|
//--- Test against reference O(N^2) implementation
|
|
referr=0;
|
|
refrerr=0;
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- Complex FFT
|
|
ArrayResize(a1,n);
|
|
ArrayResize(a2,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a1[i].re=2*CMath::RandomReal()-1;
|
|
a1[i].im=2*CMath::RandomReal()-1;
|
|
a2[i]=a1[i];
|
|
}
|
|
//--- function calls
|
|
CFastFourierTransform::FFTC1D(a1,n);
|
|
RefFFTC1D(a2,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
referr=MathMax(referr,CMath::AbsComplex(a1[i]-a2[i]));
|
|
//--- Complex inverse FFT
|
|
ArrayResize(a1,n);
|
|
ArrayResize(a2,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a1[i].re=2*CMath::RandomReal()-1;
|
|
a1[i].im=2*CMath::RandomReal()-1;
|
|
a2[i]=a1[i];
|
|
}
|
|
//--- function calls
|
|
CFastFourierTransform::FFTC1DInv(a1,n);
|
|
RefFFTC1DInv(a2,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
referr=MathMax(referr,CMath::AbsComplex(a1[i]-a2[i]));
|
|
//--- Real forward/inverse FFT:
|
|
//--- * calculate and check forward FFT
|
|
//--- * use precalculated FFT to check backward FFT
|
|
//--- fill unused parts of frequencies array with random numbers
|
|
//--- to ensure that they are not really used
|
|
ArrayResize(r1,n);
|
|
ArrayResize(r2,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
r1[i]=2*CMath::RandomReal()-1;
|
|
r2[i]=r1[i];
|
|
}
|
|
//--- function calls
|
|
CFastFourierTransform::FFTR1D(r1,n,a1);
|
|
RefInternalRFFT(r2,n,a2);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
refrerr=MathMax(refrerr,CMath::AbsComplex(a1[i]-a2[i]));
|
|
//--- allocation
|
|
ArrayResize(a3,(int)MathFloor((double)n/2.0)+1);
|
|
for(i=0;i<=(int)MathFloor((double)n/2.0);i++)
|
|
a3[i]=a2[i];
|
|
a3[0].im=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(n%2==0)
|
|
a3[(int)MathFloor((double)n/2.0)].im=2*CMath::RandomReal()-1;
|
|
for(i=0;i<=n-1;i++)
|
|
r1[i]=0;
|
|
//--- function call
|
|
CFastFourierTransform::FFTR1DInv(a3,n,r1);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
refrerr=MathMax(refrerr,MathAbs(r2[i]-r1[i]));
|
|
}
|
|
//--- search errors
|
|
referrors=referrors || referr>errtol;
|
|
refrerrors=refrerrors || refrerr>errtol;
|
|
//--- test internal real even FFT
|
|
reinterr=0;
|
|
for(k=1;k<=maxn/2;k++)
|
|
{
|
|
n=2*k;
|
|
//--- Real forward FFT
|
|
ArrayResize(r1,n);
|
|
ArrayResize(r2,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
r1[i]=2*CMath::RandomReal()-1;
|
|
r2[i]=r1[i];
|
|
}
|
|
//--- function call
|
|
CFtBase::FtBaseGenerateComplexFFtPlan(n/2,plan);
|
|
//--- allocation
|
|
ArrayResize(buf,n);
|
|
//--- function calls
|
|
CFastFourierTransform::FFTR1DInternalEven(r1,n,buf,plan);
|
|
RefInternalRFFT(r2,n,a2);
|
|
//--- search errors
|
|
reinterr=MathMax(reinterr,MathAbs(r1[0]-a2[0].re));
|
|
reinterr=MathMax(reinterr,MathAbs(r1[1]-a2[n/2].re));
|
|
for(i=1;i<=n/2-1;i++)
|
|
{
|
|
reinterr=MathMax(reinterr,MathAbs(r1[2*i+0]-a2[i].re));
|
|
reinterr=MathMax(reinterr,MathAbs(r1[2*i+1]-a2[i].im));
|
|
}
|
|
//--- Real backward FFT
|
|
ArrayResize(r1,n);
|
|
for(i=0;i<=n-1;i++)
|
|
r1[i]=2*CMath::RandomReal()-1;
|
|
ArrayResize(a2,(int)MathFloor((double)n/2.0)+1);
|
|
a2[0]=r1[0];
|
|
for(i=1;i<=(int)MathFloor((double)n/2.0)-1;i++)
|
|
{
|
|
a2[i].re=r1[2*i+0];
|
|
a2[i].im=r1[2*i+1];
|
|
}
|
|
a2[(int)MathFloor((double)n/2.0)]=r1[1];
|
|
//--- function call
|
|
CFtBase::FtBaseGenerateComplexFFtPlan(n/2,plan);
|
|
//--- allocation
|
|
ArrayResize(buf,n);
|
|
//--- function calls
|
|
CFastFourierTransform::FFTR1DInvInternalEven(r1,n,buf,plan);
|
|
CFastFourierTransform::FFTR1DInv(a2,n,r2);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
reinterr=MathMax(reinterr,MathAbs(r1[i]-r2[i]));
|
|
}
|
|
//--- search errors
|
|
reinterrors=reinterrors || reinterr>errtol;
|
|
//--- end
|
|
waserrors=(((bidierrors || bidirerrors) || referrors) || refrerrors) || reinterrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING FFT");
|
|
Print("FINAL RESULT: ");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* BI-DIRECTIONAL COMPLEX TEST: ");
|
|
//--- check
|
|
if(bidierrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* AGAINST REFERENCE COMPLEX FFT: ");
|
|
//--- check
|
|
if(referrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* BI-DIRECTIONAL REAL TEST: ");
|
|
//--- check
|
|
if(bidirerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* AGAINST REFERENCE REAL FFT: ");
|
|
//--- check
|
|
if(refrerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* INTERNAL EVEN FFT: ");
|
|
//--- check
|
|
if(reinterrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference FFT |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestFFTUnit::RefFFTC1D(complex &a[],const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
//--- create array
|
|
double buf[];
|
|
//--- check
|
|
if(!CAp::Assert(n>0,"FFTC1D: incorrect N!"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResize(buf,2*n);
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
buf[2*i+0]=a[i].re;
|
|
buf[2*i+1]=a[i].im;
|
|
}
|
|
//--- function call
|
|
RefInternalCFFT(buf,n,false);
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i].re=buf[2*i+0];
|
|
a[i].im=buf[2*i+1];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference inverse FFT |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestFFTUnit::RefFFTC1DInv(complex &a[],const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
//--- create array
|
|
double buf[];
|
|
//--- check
|
|
if(!CAp::Assert(n>0,"FFTC1DInv: incorrect N!"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResize(buf,2*n);
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
buf[2*i+0]=a[i].re;
|
|
buf[2*i+1]=a[i].im;
|
|
}
|
|
//--- function call
|
|
RefInternalCFFT(buf,n,true);
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i].re=buf[2*i+0];
|
|
a[i].im=buf[2*i+1];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal complex FFT stub. |
|
|
//| Uses straightforward formula with O(N^2) complexity. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestFFTUnit::RefInternalCFFT(double &a[],const int nn,
|
|
const bool inversefft)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int k=0;
|
|
double hre=0;
|
|
double him=0;
|
|
double c=0;
|
|
double s=0;
|
|
double re=0;
|
|
double im=0;
|
|
//--- create array
|
|
double tmp[];
|
|
//--- allocation
|
|
ArrayResize(tmp,2*nn);
|
|
//--- check
|
|
if(!inversefft)
|
|
{
|
|
for(i=0;i<=nn-1;i++)
|
|
{
|
|
//--- change values
|
|
hre=0;
|
|
him=0;
|
|
//--- calculation
|
|
for(k=0;k<=nn-1;k++)
|
|
{
|
|
re=a[2*k];
|
|
im=a[2*k+1];
|
|
c=MathCos(-(2*M_PI*k*i/nn));
|
|
s=MathSin(-(2*M_PI*k*i/nn));
|
|
hre=hre+c*re-s*im;
|
|
him=him+c*im+s*re;
|
|
}
|
|
//--- change values
|
|
tmp[2*i]=hre;
|
|
tmp[2*i+1]=him;
|
|
}
|
|
for(i=0;i<=2*nn-1;i++)
|
|
a[i]=tmp[i];
|
|
}
|
|
else
|
|
{
|
|
for(k=0;k<=nn-1;k++)
|
|
{
|
|
//--- change values
|
|
hre=0;
|
|
him=0;
|
|
//--- calculation
|
|
for(i=0;i<=nn-1;i++)
|
|
{
|
|
re=a[2*i];
|
|
im=a[2*i+1];
|
|
c=MathCos(2*M_PI*k*i/nn);
|
|
s=MathSin(2*M_PI*k*i/nn);
|
|
hre=hre+c*re-s*im;
|
|
him=him+c*im+s*re;
|
|
}
|
|
//--- change values
|
|
tmp[2*k]=hre/nn;
|
|
tmp[2*k+1]=him/nn;
|
|
}
|
|
for(i=0;i<=2*nn-1;i++)
|
|
a[i]=tmp[i];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal real FFT stub. |
|
|
//| Uses straightforward formula with O(N^2) complexity. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestFFTUnit::RefInternalRFFT(double &a[],const int nn,complex &f[])
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create array
|
|
double tmp[];
|
|
//--- allocation
|
|
ArrayResize(tmp,2*nn);
|
|
//--- copy
|
|
for(i=0;i<=nn-1;i++)
|
|
{
|
|
tmp[2*i]=a[i];
|
|
tmp[2*i+1]=0;
|
|
}
|
|
//--- function call
|
|
RefInternalCFFT(tmp,nn,false);
|
|
//--- allocation
|
|
ArrayResize(f,nn);
|
|
//--- copy
|
|
for(i=0;i<=nn-1;i++)
|
|
{
|
|
f[i].re=tmp[2*i+0];
|
|
f[i].im=tmp[2*i+1];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CConv |
|
|
//+------------------------------------------------------------------+
|
|
class CTestConvUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void RefConvC1D(complex &a[],const int m,complex &b[],const int n,complex &r[]);
|
|
static void RefConvC1DCircular(complex &a[],const int m,complex &b[],const int n,complex &r[]);
|
|
static void RefConvR1D(double &a[],const int m,double &b[],const int n,double &r[]);
|
|
static void RefConvR1DCircular(double &a[],const int m,double &b[],const int n,double &r[]);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestConvUnit(void);
|
|
~CTestConvUnit(void);
|
|
//--- public method
|
|
static bool TestConv(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestConvUnit::CTestConvUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestConvUnit::~CTestConvUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestConvUnit::TestConv(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int m=0;
|
|
int n=0;
|
|
int i=0;
|
|
int rkind=0;
|
|
int circkind=0;
|
|
int maxn=0;
|
|
double referr=0;
|
|
double refrerr=0;
|
|
double inverr=0;
|
|
double invrerr=0;
|
|
double errtol=0;
|
|
bool referrors;
|
|
bool refrerrors;
|
|
bool inverrors;
|
|
bool invrerrors;
|
|
bool waserrors;
|
|
//--- create arrays
|
|
double ra[];
|
|
double rb[];
|
|
double rr1[];
|
|
double rr2[];
|
|
complex ca[];
|
|
complex cb[];
|
|
complex cr1[];
|
|
complex cr2[];
|
|
//--- initialization
|
|
maxn=32;
|
|
errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon;
|
|
referrors=false;
|
|
refrerrors=false;
|
|
inverrors=false;
|
|
invrerrors=false;
|
|
waserrors=false;
|
|
//--- Test against reference O(N^2) implementation.
|
|
//--- Automatic ConvC1D() and different algorithms of ConvC1DX() are tested.
|
|
referr=0;
|
|
refrerr=0;
|
|
for(m=1;m<=maxn;m++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
for(circkind=0;circkind<=1;circkind++)
|
|
{
|
|
for(rkind=-3;rkind<=1;rkind++)
|
|
{
|
|
//--- skip impossible combinations of parameters:
|
|
//--- * circular convolution,M<N,RKind<>-3 - internal subroutine does not support M<N.
|
|
if((circkind!=0 && m<n) && rkind!=-3)
|
|
continue;
|
|
//--- Complex convolution
|
|
ArrayResize(ca,m);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
ca[i].re=2*CMath::RandomReal()-1;
|
|
ca[i].im=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cb,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
cb[i].re=2*CMath::RandomReal()-1;
|
|
cb[i].im=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cr1,1);
|
|
//--- check
|
|
if(rkind==-3)
|
|
{
|
|
//--- test wrapper subroutine:
|
|
//--- * circular/non-circular
|
|
if(circkind==0)
|
|
CConv::ConvC1D(ca,m,cb,n,cr1);
|
|
else
|
|
CConv::ConvC1DCircular(ca,m,cb,n,cr1);
|
|
}
|
|
else
|
|
{
|
|
//--- test internal subroutine
|
|
if(m>=n)
|
|
{
|
|
//--- test internal subroutine:
|
|
//--- * circular/non-circular mode
|
|
CConv::ConvC1DX(ca,m,cb,n,circkind!=0,rkind,0,cr1);
|
|
}
|
|
else
|
|
{
|
|
//--- test internal subroutine - circular mode only
|
|
if(!CAp::Assert(circkind==0,"Convolution test: internal error!"))
|
|
return(false);
|
|
//--- function call
|
|
CConv::ConvC1DX(cb,n,ca,m,false,rkind,0,cr1);
|
|
}
|
|
}
|
|
//--- check
|
|
if(circkind==0)
|
|
RefConvC1D(ca,m,cb,n,cr2);
|
|
else
|
|
RefConvC1DCircular(ca,m,cb,n,cr2);
|
|
//--- check
|
|
if(circkind==0)
|
|
{
|
|
for(i=0;i<=m+n-2;i++)
|
|
referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i]));
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i]));
|
|
}
|
|
//--- Real convolution
|
|
ArrayResize(ra,m);
|
|
for(i=0;i<=m-1;i++)
|
|
ra[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(rb,n);
|
|
for(i=0;i<=n-1;i++)
|
|
rb[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(rr1,1);
|
|
//--- check
|
|
if(rkind==-3)
|
|
{
|
|
//--- test wrapper subroutine:
|
|
//--- * circular/non-circular
|
|
if(circkind==0)
|
|
CConv::ConvR1D(ra,m,rb,n,rr1);
|
|
else
|
|
CConv::ConvR1DCircular(ra,m,rb,n,rr1);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(m>=n)
|
|
{
|
|
//--- test internal subroutine:
|
|
//--- * circular/non-circular mode
|
|
CConv::ConvR1DX(ra,m,rb,n,circkind!=0,rkind,0,rr1);
|
|
}
|
|
else
|
|
{
|
|
//--- test internal subroutine - non-circular mode only
|
|
CConv::ConvR1DX(rb,n,ra,m,circkind!=0,rkind,0,rr1);
|
|
}
|
|
}
|
|
//--- check
|
|
if(circkind==0)
|
|
RefConvR1D(ra,m,rb,n,rr2);
|
|
else
|
|
RefConvR1DCircular(ra,m,rb,n,rr2);
|
|
//--- check
|
|
if(circkind==0)
|
|
{
|
|
for(i=0;i<=m+n-2;i++)
|
|
refrerr=MathMax(refrerr,MathAbs(rr1[i]-rr2[i]));
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
refrerr=MathMax(refrerr,MathAbs(rr1[i]-rr2[i]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- search errors
|
|
referrors=referrors || referr>errtol;
|
|
refrerrors=refrerrors || refrerr>errtol;
|
|
//--- Test inverse convolution
|
|
inverr=0;
|
|
invrerr=0;
|
|
for(m=1;m<=maxn;m++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- Complex circilar and non-circular
|
|
ArrayResize(ca,m);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
ca[i].re=2*CMath::RandomReal()-1;
|
|
ca[i].im=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cb,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
cb[i].re=2*CMath::RandomReal()-1;
|
|
cb[i].im=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cr1,1);
|
|
ArrayResize(cr2,1);
|
|
//--- function calls
|
|
CConv::ConvC1D(ca,m,cb,n,cr2);
|
|
CConv::ConvC1DInv(cr2,m+n-1,cb,n,cr1);
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
inverr=MathMax(inverr,CMath::AbsComplex(cr1[i]-ca[i]));
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cr1,1);
|
|
ArrayResize(cr2,1);
|
|
//--- function calls
|
|
CConv::ConvC1DCircular(ca,m,cb,n,cr2);
|
|
CConv::ConvC1DCircularInv(cr2,m,cb,n,cr1);
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
inverr=MathMax(inverr,CMath::AbsComplex(cr1[i]-ca[i]));
|
|
//--- Real circilar and non-circular
|
|
ArrayResize(ra,m);
|
|
for(i=0;i<=m-1;i++)
|
|
ra[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(rb,n);
|
|
for(i=0;i<=n-1;i++)
|
|
rb[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(rr1,1);
|
|
ArrayResize(rr2,1);
|
|
//--- function calls
|
|
CConv::ConvR1D(ra,m,rb,n,rr2);
|
|
CConv::ConvR1DInv(rr2,m+n-1,rb,n,rr1);
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
invrerr=MathMax(invrerr,MathAbs(rr1[i]-ra[i]));
|
|
//--- allocation
|
|
ArrayResize(rr1,1);
|
|
ArrayResize(rr2,1);
|
|
//--- function calls
|
|
CConv::ConvR1DCircular(ra,m,rb,n,rr2);
|
|
CConv::ConvR1DCircularInv(rr2,m,rb,n,rr1);
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
invrerr=MathMax(invrerr,MathAbs(rr1[i]-ra[i]));
|
|
}
|
|
}
|
|
//--- search errors
|
|
inverrors=inverrors || inverr>errtol;
|
|
invrerrors=invrerrors || invrerr>errtol;
|
|
//--- end
|
|
waserrors=((referrors || refrerrors) || inverrors) || invrerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING CONVOLUTION");
|
|
Print("FINAL RESULT: ");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* AGAINST REFERENCE COMPLEX CONV: ");
|
|
//--- check
|
|
if(referrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* AGAINST REFERENCE REAL CONV: ");
|
|
//--- check
|
|
if(refrerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* COMPLEX INVERSE: ");
|
|
//--- check
|
|
if(inverrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* REAL INVERSE: ");
|
|
//--- check
|
|
if(invrerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestConvUnit::RefConvC1D(complex &a[],const int m,complex &b[],
|
|
const int n,complex &r[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- allocation
|
|
ArrayResize(r,m+n-1);
|
|
//--- initialization
|
|
for(i=0;i<=m+n-2;i++)
|
|
r[i]=0;
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
v=a[i];
|
|
i1_=-i;
|
|
for(i_=i;i_<=i+n-1;i_++)
|
|
r[i_]=r[i_]+v*b[i_+i1_];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestConvUnit::RefConvC1DCircular(complex &a[],const int m,
|
|
complex &b[],const int n,
|
|
complex &r[])
|
|
{
|
|
//--- create variables
|
|
int i1=0;
|
|
int i2=0;
|
|
int j2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
complex buf[];
|
|
//--- function call
|
|
RefConvC1D(a,m,b,n,buf);
|
|
//--- allocation
|
|
ArrayResize(r,m);
|
|
//--- copy
|
|
for(i_=0;i_<=m-1;i_++)
|
|
r[i_]=buf[i_];
|
|
//--- calculation
|
|
i1=m;
|
|
while(i1<=m+n-2)
|
|
{
|
|
//--- change values
|
|
i2=MathMin(i1+m-1,m+n-2);
|
|
j2=i2-i1;
|
|
i1_=i1;
|
|
for(i_=0;i_<=j2;i_++)
|
|
r[i_]=r[i_]+buf[i_+i1_];
|
|
i1=i1+m;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference FFT |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestConvUnit::RefConvR1D(double &a[],const int m,double &b[],
|
|
const int n,double &r[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- allocation
|
|
ArrayResize(r,m+n-1);
|
|
//--- initialization
|
|
for(i=0;i<=m+n-2;i++)
|
|
r[i]=0;
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
v=a[i];
|
|
i1_=-i;
|
|
for(i_=i;i_<=i+n-1;i_++)
|
|
r[i_]=r[i_]+v*b[i_+i1_];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestConvUnit::RefConvR1DCircular(double &a[],const int m,
|
|
double &b[],const int n,
|
|
double &r[])
|
|
{
|
|
//--- create variables
|
|
int i1=0;
|
|
int i2=0;
|
|
int j2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
double buf[];
|
|
//--- function call
|
|
RefConvR1D(a,m,b,n,buf);
|
|
//--- allocation
|
|
ArrayResize(r,m);
|
|
//--- copy
|
|
for(i_=0;i_<=m-1;i_++)
|
|
r[i_]=buf[i_];
|
|
//--- calculation
|
|
i1=m;
|
|
while(i1<=m+n-2)
|
|
{
|
|
//--- change values
|
|
i2=MathMin(i1+m-1,m+n-2);
|
|
j2=i2-i1;
|
|
i1_=i1;
|
|
for(i_=0;i_<=j2;i_++)
|
|
r[i_]=r[i_]+buf[i_+i1_];
|
|
i1=i1+m;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CCorr |
|
|
//+------------------------------------------------------------------+
|
|
class CTestCorrUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void RefCorrC1D(complex &signal[],const int n,complex &pattern[],const int m,complex &r[]);
|
|
static void RefCorrC1DCircular(complex &signal[],const int n,complex &pattern[],const int m,complex &r[]);
|
|
static void RefCorrR1D(double &signal[],const int n,double &pattern[],const int m,double &r[]);
|
|
static void RefCorrR1DCircular(double &signal[],const int n,double &pattern[],const int m,double &r[]);
|
|
static void RefConvC1D(complex &a[],const int m,complex &b[],const int n,complex &r[]);
|
|
static void RefConvC1DCircular(complex &a[],const int m,complex &b[],const int n,complex &r[]);
|
|
static void RefConvR1D(double &a[],const int m,double &b[],const int n,double &r[]);
|
|
static void RefConvR1DCircular(double &a[],const int m,double &b[],const int n,double &r[]);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestCorrUnit(void);
|
|
~CTestCorrUnit(void);
|
|
//--- public method
|
|
static bool TestCorr(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestCorrUnit::CTestCorrUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestCorrUnit::~CTestCorrUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CCorr |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestCorrUnit::TestCorr(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int m=0;
|
|
int n=0;
|
|
int i=0;
|
|
int maxn=0;
|
|
double referr=0;
|
|
double refrerr=0;
|
|
double errtol=0;
|
|
bool referrors;
|
|
bool refrerrors;
|
|
bool inverrors;
|
|
bool invrerrors;
|
|
bool waserrors;
|
|
//--- create arrays
|
|
double ra[];
|
|
double rb[];
|
|
double rr1[];
|
|
double rr2[];
|
|
complex ca[];
|
|
complex cb[];
|
|
complex cr1[];
|
|
complex cr2[];
|
|
//--- initialization
|
|
maxn=32;
|
|
errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon;
|
|
referrors=false;
|
|
refrerrors=false;
|
|
inverrors=false;
|
|
invrerrors=false;
|
|
waserrors=false;
|
|
//--- Test against reference O(N^2) implementation.
|
|
referr=0;
|
|
refrerr=0;
|
|
for(m=1;m<=maxn;m++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- Complex correlation
|
|
ArrayResize(ca,m);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
ca[i].re=2*CMath::RandomReal()-1;
|
|
ca[i].im=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cb,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
cb[i].re=2*CMath::RandomReal()-1;
|
|
cb[i].im=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(cr1,1);
|
|
//--- function calls
|
|
CCorr::CorrC1D(ca,m,cb,n,cr1);
|
|
RefCorrC1D(ca,m,cb,n,cr2);
|
|
//--- search errors
|
|
for(i=0;i<=m+n-2;i++)
|
|
referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i]));
|
|
//--- allocation
|
|
ArrayResize(cr1,1);
|
|
//--- function calls
|
|
CCorr::CorrC1DCircular(ca,m,cb,n,cr1);
|
|
RefCorrC1DCircular(ca,m,cb,n,cr2);
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
referr=MathMax(referr,CMath::AbsComplex(cr1[i]-cr2[i]));
|
|
//--- Real correlation
|
|
ArrayResize(ra,m);
|
|
for(i=0;i<=m-1;i++)
|
|
ra[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(rb,n);
|
|
for(i=0;i<=n-1;i++)
|
|
rb[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(rr1,1);
|
|
//--- function calls
|
|
CCorr::CorrR1D(ra,m,rb,n,rr1);
|
|
RefCorrR1D(ra,m,rb,n,rr2);
|
|
//--- search errors
|
|
for(i=0;i<=m+n-2;i++)
|
|
refrerr=MathMax(refrerr,MathAbs(rr1[i]-rr2[i]));
|
|
//--- allocation
|
|
ArrayResize(rr1,1);
|
|
//--- function calls
|
|
CCorr::CorrR1DCircular(ra,m,rb,n,rr1);
|
|
RefCorrR1DCircular(ra,m,rb,n,rr2);
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
refrerr=MathMax(refrerr,MathAbs(rr1[i]-rr2[i]));
|
|
}
|
|
}
|
|
//--- search errors
|
|
referrors=referrors || referr>errtol;
|
|
refrerrors=refrerrors || refrerr>errtol;
|
|
//--- end
|
|
waserrors=referrors || refrerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING CORRELATION");
|
|
Print("FINAL RESULT: ");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* AGAINST REFERENCE COMPLEX CORR: ");
|
|
//--- check
|
|
if(referrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* AGAINST REFERENCE REAL CORR: ");
|
|
//--- check
|
|
if(refrerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestCorrUnit::RefCorrC1D(complex &signal[],const int n,
|
|
complex &pattern[],const int m,
|
|
complex &r[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
complex s[];
|
|
//--- allocation
|
|
ArrayResize(s,m+n-1);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
s[i_]=signal[i_];
|
|
for(i=n;i<=m+n-2;i++)
|
|
s[i]=0;
|
|
//--- allocation
|
|
ArrayResize(r,m+n-1);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v=0;
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- check
|
|
if(i+j>=n)
|
|
break;
|
|
v=v+CMath::Conj(pattern[j])*s[i+j];
|
|
}
|
|
r[i]=v;
|
|
}
|
|
//--- calculation
|
|
for(i=1;i<=m-1;i++)
|
|
{
|
|
v=0;
|
|
for(j=i;j<=m-1;j++)
|
|
v=v+CMath::Conj(pattern[j])*s[j-i];
|
|
r[m+n-1-i]=v;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestCorrUnit::RefCorrC1DCircular(complex &signal[],const int n,
|
|
complex &pattern[],const int m,
|
|
complex &r[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
complex v=0;
|
|
//--- allocation
|
|
ArrayResize(r,n);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0;
|
|
for(j=0;j<=m-1;j++)
|
|
v=v+CMath::Conj(pattern[j])*signal[(i+j)%n];
|
|
r[i]=v;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestCorrUnit::RefCorrR1D(double &signal[],const int n,
|
|
double &pattern[],const int m,
|
|
double &r[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double s[];
|
|
//--- allocation
|
|
ArrayResize(s,m+n-1);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
s[i_]=signal[i_];
|
|
for(i=n;i<=m+n-2;i++)
|
|
s[i]=0;
|
|
//--- allocation
|
|
ArrayResize(r,m+n-1);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v=0;
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- check
|
|
if(i+j>=n)
|
|
break;
|
|
v=v+pattern[j]*s[i+j];
|
|
}
|
|
r[i]=v;
|
|
}
|
|
//--- calculation
|
|
for(i=1;i<=m-1;i++)
|
|
{
|
|
v=0;
|
|
for(j=i;j<=m-1;j++)
|
|
v=v+pattern[j]*s[-i+j];
|
|
r[m+n-1-i]=v;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestCorrUnit::RefCorrR1DCircular(double &signal[],const int n,
|
|
double &pattern[],const int m,
|
|
double &r[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
//--- allocation
|
|
ArrayResize(r,n);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v=0;
|
|
for(j=0;j<=m-1;j++)
|
|
v=v+pattern[j]*signal[(i+j)%n];
|
|
r[i]=v;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestCorrUnit::RefConvC1D(complex &a[],const int m,complex &b[],
|
|
const int n,complex &r[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
complex v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- allocation
|
|
ArrayResize(r,m+n-1);
|
|
//--- initialization
|
|
for(i=0;i<=m+n-2;i++)
|
|
r[i]=0;
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
v=a[i];
|
|
i1_=-i;
|
|
for(i_=i;i_<=i+n-1;i_++)
|
|
r[i_]=r[i_]+v*b[i_+i1_];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestCorrUnit::RefConvC1DCircular(complex &a[],const int m,
|
|
complex &b[],const int n,
|
|
complex &r[])
|
|
{
|
|
//--- create variables
|
|
int i1=0;
|
|
int i2=0;
|
|
int j2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
complex buf[];
|
|
//--- function call
|
|
RefConvC1D(a,m,b,n,buf);
|
|
//--- allocation
|
|
ArrayResize(r,m);
|
|
//--- copy
|
|
for(i_=0;i_<=m-1;i_++)
|
|
r[i_]=buf[i_];
|
|
//--- calculation
|
|
i1=m;
|
|
while(i1<=m+n-2)
|
|
{
|
|
//--- change values
|
|
i2=MathMin(i1+m-1,m+n-2);
|
|
j2=i2-i1;
|
|
i1_=i1;
|
|
for(i_=0;i_<=j2;i_++)
|
|
r[i_]=r[i_]+buf[i_+i1_];
|
|
i1=i1+m;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference FFT |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestCorrUnit::RefConvR1D(double &a[],const int m,double &b[],
|
|
const int n,double &r[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- allocation
|
|
ArrayResize(r,m+n-1);
|
|
//--- initialization
|
|
for(i=0;i<=m+n-2;i++)
|
|
r[i]=0;
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
v=a[i];
|
|
i1_=-i;
|
|
for(i_=i;i_<=i+n-1;i_++)
|
|
r[i_]=r[i_]+v*b[i_+i1_];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference implementation |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestCorrUnit::RefConvR1DCircular(double &a[],const int m,
|
|
double &b[],const int n,
|
|
double &r[])
|
|
{
|
|
//--- create variables
|
|
int i1=0;
|
|
int i2=0;
|
|
int j2=0;
|
|
int i_=0;
|
|
int i1_=0;
|
|
//--- create array
|
|
double buf[];
|
|
//--- function call
|
|
RefConvR1D(a,m,b,n,buf);
|
|
//--- allocation
|
|
ArrayResize(r,m);
|
|
//--- copy
|
|
for(i_=0;i_<=m-1;i_++)
|
|
r[i_]=buf[i_];
|
|
//--- calculation
|
|
i1=m;
|
|
while(i1<=m+n-2)
|
|
{
|
|
//--- change values
|
|
i2=MathMin(i1+m-1,m+n-2);
|
|
j2=i2-i1;
|
|
i1_=i1;
|
|
for(i_=0;i_<=j2;i_++)
|
|
r[i_]=r[i_]+buf[i_+i1_];
|
|
i1=i1+m;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CFastHartleyTransform |
|
|
//+------------------------------------------------------------------+
|
|
class CTestFHTUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void RefFHTR1D(double &a[],const int n);
|
|
static void RefFHTR1DInv(double &a[],const int n);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestFHTUnit(void);
|
|
~CTestFHTUnit(void);
|
|
//--- public method
|
|
static bool TestFHT(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestFHTUnit::CTestFHTUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestFHTUnit::~CTestFHTUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestFHTUnit::TestFHT(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i=0;
|
|
int maxn=0;
|
|
double bidierr=0;
|
|
double referr=0;
|
|
double errtol=0;
|
|
bool referrors;
|
|
bool bidierrors;
|
|
bool waserrors;
|
|
//--- create arrays
|
|
double r1[];
|
|
double r2[];
|
|
double r3[];
|
|
//--- initialization
|
|
maxn=128;
|
|
errtol=100000*MathPow(maxn,3.0/2.0)*CMath::m_machineepsilon;
|
|
bidierrors=false;
|
|
referrors=false;
|
|
waserrors=false;
|
|
//--- Test bi-directional error: norm(x-invFHT(FHT(x)))
|
|
bidierr=0;
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- FHT/invFHT
|
|
ArrayResize(r1,n);
|
|
ArrayResize(r2,n);
|
|
ArrayResize(r3,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
r1[i]=2*CMath::RandomReal()-1;
|
|
r2[i]=r1[i];
|
|
r3[i]=r1[i];
|
|
}
|
|
//--- function calls
|
|
CFastHartleyTransform::FHTR1D(r2,n);
|
|
CFastHartleyTransform::FHTR1DInv(r2,n);
|
|
CFastHartleyTransform::FHTR1DInv(r3,n);
|
|
CFastHartleyTransform::FHTR1D(r3,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
bidierr=MathMax(bidierr,MathAbs(r1[i]-r2[i]));
|
|
bidierr=MathMax(bidierr,MathAbs(r1[i]-r3[i]));
|
|
}
|
|
}
|
|
//--- search errors
|
|
bidierrors=bidierrors || bidierr>errtol;
|
|
//--- Test against reference O(N^2) implementation
|
|
referr=0;
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- FHT
|
|
ArrayResize(r1,n);
|
|
ArrayResize(r2,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
r1[i]=2*CMath::RandomReal()-1;
|
|
r2[i]=r1[i];
|
|
}
|
|
//--- function calls
|
|
CFastHartleyTransform::FHTR1D(r1,n);
|
|
RefFHTR1D(r2,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
referr=MathMax(referr,MathAbs(r1[i]-r2[i]));
|
|
//--- inverse FHT
|
|
ArrayResize(r1,n);
|
|
ArrayResize(r2,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
r1[i]=2*CMath::RandomReal()-1;
|
|
r2[i]=r1[i];
|
|
}
|
|
//--- function calls
|
|
CFastHartleyTransform::FHTR1DInv(r1,n);
|
|
RefFHTR1DInv(r2,n);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
referr=MathMax(referr,MathAbs(r1[i]-r2[i]));
|
|
}
|
|
//--- search errors
|
|
referrors=referrors || referr>errtol;
|
|
//--- end
|
|
waserrors=bidierrors || referrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING FHT");
|
|
Print("FINAL RESULT: ");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* BI-DIRECTIONAL TEST: ");
|
|
//--- check
|
|
if(bidierrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* AGAINST REFERENCE FHT: ");
|
|
//--- check
|
|
if(referrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference FHT |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestFHTUnit::RefFHTR1D(double &a[],const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
//--- create array
|
|
double buf[];
|
|
//--- check
|
|
if(!CAp::Assert(n>0,"RefFHTR1D: incorrect N!"))
|
|
return;
|
|
//--- allocation
|
|
ArrayResize(buf,n);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v=0;
|
|
for(j=0;j<=n-1;j++)
|
|
v=v+a[j]*(MathCos(2*M_PI*i*j/n)+MathSin(2*M_PI*i*j/n));
|
|
buf[i]=v;
|
|
}
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
a[i]=buf[i];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Reference inverse FHT |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestFHTUnit::RefFHTR1DInv(double &a[],const int n)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>0,"RefFHTR1DInv: incorrect N!"))
|
|
return;
|
|
//--- function call
|
|
RefFHTR1D(a,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
a[i]=a[i]/n;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CGaussQ |
|
|
//+------------------------------------------------------------------+
|
|
class CTestGQUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static double MapKind(const int k);
|
|
static void BuildGaussLegendreQuadrature(const int n,double &x[],double &w[]);
|
|
static void BuildGaussJacobiQuadrature(const int n,const double alpha,const double beta,double &x[],double &w[]);
|
|
static void BuildGaussLaguerreQuadrature(const int n,const double alpha,double &x[],double &w[]);
|
|
static void BuildGaussHermiteQuadrature(const int n,double &x[],double &w[]);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestGQUnit(void);
|
|
~CTestGQUnit(void);
|
|
//--- public method
|
|
static bool TestGQ(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestGQUnit::CTestGQUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestGQUnit::~CTestGQUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestGQUnit::TestGQ(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double err=0;
|
|
int n=0;
|
|
int i=0;
|
|
int info=0;
|
|
int akind=0;
|
|
int bkind=0;
|
|
double alphac=0;
|
|
double betac=0;
|
|
double errtol=0;
|
|
double nonstricterrtol=0;
|
|
double stricterrtol=0;
|
|
bool recerrors;
|
|
bool specerrors;
|
|
bool waserrors;
|
|
//--- create arrays
|
|
double alpha[];
|
|
double beta[];
|
|
double x[];
|
|
double w[];
|
|
double x2[];
|
|
double w2[];
|
|
//--- initialization
|
|
recerrors=false;
|
|
specerrors=false;
|
|
waserrors=false;
|
|
errtol=1.0E-12;
|
|
nonstricterrtol=1.0E-6;
|
|
stricterrtol=1000*CMath::m_machineepsilon;
|
|
//--- Three tests for rec-based Gauss quadratures with known weights/nodes:
|
|
//--- 1. Gauss-Legendre with N=2
|
|
//--- 2. Gauss-Legendre with N=5
|
|
//--- 3. Gauss-Chebyshev with N=1,2,4,8,...,512
|
|
err=0;
|
|
ArrayResize(alpha,2);
|
|
ArrayResize(beta,2);
|
|
alpha[0]=0;
|
|
alpha[1]=0;
|
|
beta[1]=1.0/(double)(4*1*1-1);
|
|
//--- function call
|
|
CGaussQ::GQGenerateRec(alpha,beta,2.0,2,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(x[0]+MathSqrt(3)/3));
|
|
err=MathMax(err,MathAbs(x[1]-MathSqrt(3)/3));
|
|
err=MathMax(err,MathAbs(w[0]-1));
|
|
err=MathMax(err,MathAbs(w[1]-1));
|
|
for(i=0;i<=0;i++)
|
|
recerrors=recerrors || x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
//--- allocation
|
|
ArrayResize(alpha,5);
|
|
ArrayResize(beta,5);
|
|
//--- change values
|
|
alpha[0]=0;
|
|
for(i=1;i<=4;i++)
|
|
{
|
|
alpha[i]=0;
|
|
beta[i]=CMath::Sqr(i)/(4*CMath::Sqr(i)-1);
|
|
}
|
|
//--- function call
|
|
CGaussQ::GQGenerateRec(alpha,beta,2.0,5,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(x[0]+MathSqrt(245+14*MathSqrt(70))/21));
|
|
err=MathMax(err,MathAbs(x[0]+x[4]));
|
|
err=MathMax(err,MathAbs(x[1]+MathSqrt(245-14*MathSqrt(70))/21));
|
|
err=MathMax(err,MathAbs(x[1]+x[3]));
|
|
err=MathMax(err,MathAbs(x[2]));
|
|
err=MathMax(err,MathAbs(w[0]-(322-13*MathSqrt(70))/900));
|
|
err=MathMax(err,MathAbs(w[0]-w[4]));
|
|
err=MathMax(err,MathAbs(w[1]-(322+13*MathSqrt(70))/900));
|
|
err=MathMax(err,MathAbs(w[1]-w[3]));
|
|
err=MathMax(err,MathAbs(w[2]-128.0/225.0));
|
|
for(i=0;i<=3;i++)
|
|
recerrors=recerrors || x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
//--- calculation
|
|
n=1;
|
|
while(n<=512)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(alpha,n);
|
|
ArrayResize(beta,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
alpha[i]=0;
|
|
//--- check
|
|
if(i==0)
|
|
beta[i]=0;
|
|
//--- check
|
|
if(i==1)
|
|
beta[i]=1.0/2.0;
|
|
//--- check
|
|
if(i>1)
|
|
beta[i]=1.0/4.0;
|
|
}
|
|
//--- function call
|
|
CGaussQ::GQGenerateRec(alpha,beta,M_PI,n,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
err=MathMax(err,MathAbs(x[i]-MathCos(M_PI*(n-i-0.5)/n)));
|
|
err=MathMax(err,MathAbs(w[i]-M_PI/n));
|
|
}
|
|
for(i=0;i<=n-2;i++)
|
|
recerrors=recerrors || x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
n=n*2;
|
|
}
|
|
//--- search errors
|
|
recerrors=recerrors || err>errtol;
|
|
//--- Three tests for rec-based Gauss-Lobatto quadratures with known weights/nodes:
|
|
//--- 1. Gauss-Lobatto with N=3
|
|
//--- 2. Gauss-Lobatto with N=4
|
|
//--- 3. Gauss-Lobatto with N=6
|
|
err=0;
|
|
ArrayResize(alpha,2);
|
|
ArrayResize(beta,2);
|
|
alpha[0]=0;
|
|
alpha[1]=0;
|
|
beta[0]=0;
|
|
beta[1]=(double)(1*1)/(double)(4*1*1-1);
|
|
//--- function call
|
|
CGaussQ::GQGenerateGaussLobattoRec(alpha,beta,2.0,-1,1,3,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(x[0]+1));
|
|
err=MathMax(err,MathAbs(x[1]));
|
|
err=MathMax(err,MathAbs(x[2]-1));
|
|
err=MathMax(err,MathAbs(w[0]-1.0/3.0));
|
|
err=MathMax(err,MathAbs(w[1]-4.0/3.0));
|
|
err=MathMax(err,MathAbs(w[2]-1.0/3.0));
|
|
for(i=0;i<=1;i++)
|
|
recerrors=recerrors || x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
//--- allocation
|
|
ArrayResize(alpha,3);
|
|
ArrayResize(beta,3);
|
|
//--- change values
|
|
alpha[0]=0;
|
|
alpha[1]=0;
|
|
alpha[2]=0;
|
|
beta[0]=0;
|
|
beta[1]=(double)(1*1)/(double)(4*1*1-1);
|
|
beta[2]=(double)(2*2)/(double)(4*2*2-1);
|
|
//--- function call
|
|
CGaussQ::GQGenerateGaussLobattoRec(alpha,beta,2.0,-1,1,4,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(x[0]+1));
|
|
err=MathMax(err,MathAbs(x[1]+MathSqrt(5)/5));
|
|
err=MathMax(err,MathAbs(x[2]-MathSqrt(5)/5));
|
|
err=MathMax(err,MathAbs(x[3]-1));
|
|
err=MathMax(err,MathAbs(w[0]-1.0/6.0));
|
|
err=MathMax(err,MathAbs(w[1]-5.0/6.0));
|
|
err=MathMax(err,MathAbs(w[2]-5.0/6.0));
|
|
err=MathMax(err,MathAbs(w[3]-1.0/6.0));
|
|
for(i=0;i<=2;i++)
|
|
recerrors=recerrors || x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
//--- allocation
|
|
ArrayResize(alpha,5);
|
|
ArrayResize(beta,5);
|
|
//--- change values
|
|
alpha[0]=0;
|
|
alpha[1]=0;
|
|
alpha[2]=0;
|
|
alpha[3]=0;
|
|
alpha[4]=0;
|
|
beta[0]=0;
|
|
beta[1]=(double)(1*1)/(double)(4*1*1-1);
|
|
beta[2]=(double)(2*2)/(double)(4*2*2-1);
|
|
beta[3]=(double)(3*3)/(double)(4*3*3-1);
|
|
beta[4]=(double)(4*4)/(double)(4*4*4-1);
|
|
//--- function call
|
|
CGaussQ::GQGenerateGaussLobattoRec(alpha,beta,2.0,-1,1,6,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(x[0]+1));
|
|
err=MathMax(err,MathAbs(x[1]+MathSqrt((7+2*MathSqrt(7))/21)));
|
|
err=MathMax(err,MathAbs(x[2]+MathSqrt((7-2*MathSqrt(7))/21)));
|
|
err=MathMax(err,MathAbs(x[3]-MathSqrt((7-2*MathSqrt(7))/21)));
|
|
err=MathMax(err,MathAbs(x[4]-MathSqrt((7+2*MathSqrt(7))/21)));
|
|
err=MathMax(err,MathAbs(x[5]-1));
|
|
err=MathMax(err,MathAbs(w[0]-1.0/(double)15));
|
|
err=MathMax(err,MathAbs(w[1]-(14-MathSqrt(7))/30));
|
|
err=MathMax(err,MathAbs(w[2]-(14+MathSqrt(7))/30));
|
|
err=MathMax(err,MathAbs(w[3]-(14+MathSqrt(7))/30));
|
|
err=MathMax(err,MathAbs(w[4]-(14-MathSqrt(7))/30));
|
|
err=MathMax(err,MathAbs(w[5]-1.0/(double)15));
|
|
for(i=0;i<=4;i++)
|
|
recerrors=recerrors || x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
recerrors=recerrors || err>errtol;
|
|
//--- Three tests for rec-based Gauss-Radau quadratures with known weights/nodes:
|
|
//--- 1. Gauss-Radau with N=2
|
|
//--- 2. Gauss-Radau with N=3
|
|
//--- 3. Gauss-Radau with N=3 (another case)
|
|
err=0;
|
|
ArrayResize(alpha,1);
|
|
ArrayResize(beta,2);
|
|
alpha[0]=0;
|
|
beta[0]=0;
|
|
beta[1]=(double)(1*1)/(double)(4*1*1-1);
|
|
//--- function call
|
|
CGaussQ::GQGenerateGaussRadauRec(alpha,beta,2.0,-1,2,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(x[0]+1));
|
|
err=MathMax(err,MathAbs(x[1]-1.0/3.0));
|
|
err=MathMax(err,MathAbs(w[0]-0.5));
|
|
err=MathMax(err,MathAbs(w[1]-1.5));
|
|
for(i=0;i<=0;i++)
|
|
recerrors=recerrors || x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
//--- allocation
|
|
ArrayResize(alpha,2);
|
|
ArrayResize(beta,3);
|
|
//--- change values
|
|
alpha[0]=0;
|
|
alpha[1]=0;
|
|
for(i=0;i<=2;i++)
|
|
beta[i]=CMath::Sqr(i)/(4*CMath::Sqr(i)-1);
|
|
//--- function call
|
|
CGaussQ::GQGenerateGaussRadauRec(alpha,beta,2.0,-1,3,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(x[0]+1));
|
|
err=MathMax(err,MathAbs(x[1]-(1-MathSqrt(6))/5));
|
|
err=MathMax(err,MathAbs(x[2]-(1+MathSqrt(6))/5));
|
|
err=MathMax(err,MathAbs(w[0]-2.0/9.0));
|
|
err=MathMax(err,MathAbs(w[1]-(16+MathSqrt(6))/18));
|
|
err=MathMax(err,MathAbs(w[2]-(16-MathSqrt(6))/18));
|
|
for(i=0;i<=1;i++)
|
|
recerrors=recerrors || x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
//--- allocation
|
|
ArrayResize(alpha,2);
|
|
ArrayResize(beta,3);
|
|
alpha[0]=0;
|
|
alpha[1]=0;
|
|
for(i=0;i<=2;i++)
|
|
beta[i]=CMath::Sqr(i)/(4*CMath::Sqr(i)-1);
|
|
//--- function call
|
|
CGaussQ::GQGenerateGaussRadauRec(alpha,beta,2.0,1,3,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(x[2]-1));
|
|
err=MathMax(err,MathAbs(x[1]+(1-MathSqrt(6))/5));
|
|
err=MathMax(err,MathAbs(x[0]+(1+MathSqrt(6))/5));
|
|
err=MathMax(err,MathAbs(w[2]-2.0/9.0));
|
|
err=MathMax(err,MathAbs(w[1]-(16+MathSqrt(6))/18));
|
|
err=MathMax(err,MathAbs(w[0]-(16-MathSqrt(6))/18));
|
|
for(i=0;i<=1;i++)
|
|
recerrors=recerrors || x[i]>=x[i+1];
|
|
}
|
|
else
|
|
recerrors=true;
|
|
//--- search errors
|
|
recerrors=recerrors || err>errtol;
|
|
//--- test recurrence-based special cases (Legendre,Jacobi,Hermite,...)
|
|
//--- against another implementation (polynomial root-finder)
|
|
for(n=1;n<=20;n++)
|
|
{
|
|
//--- test gauss-legendre
|
|
err=0;
|
|
CGaussQ::GQGenerateGaussLegendre(n,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
BuildGaussLegendreQuadrature(n,x2,w2);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
err=MathMax(err,MathAbs(x[i]-x2[i]));
|
|
err=MathMax(err,MathAbs(w[i]-w2[i]));
|
|
}
|
|
}
|
|
else
|
|
specerrors=true;
|
|
//--- search errors
|
|
specerrors=specerrors || err>errtol;
|
|
//--- Test Gauss-Jacobi.
|
|
//--- Since task is much more difficult we will use less strict
|
|
//--- threshold.
|
|
err=0;
|
|
for(akind=0;akind<=9;akind++)
|
|
{
|
|
for(bkind=0;bkind<=9;bkind++)
|
|
{
|
|
alphac=MapKind(akind);
|
|
betac=MapKind(bkind);
|
|
//--- function call
|
|
CGaussQ::GQGenerateGaussJacobi(n,alphac,betac,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
BuildGaussJacobiQuadrature(n,alphac,betac,x2,w2);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
err=MathMax(err,MathAbs(x[i]-x2[i]));
|
|
err=MathMax(err,MathAbs(w[i]-w2[i]));
|
|
}
|
|
}
|
|
else
|
|
specerrors=true;
|
|
}
|
|
}
|
|
//--- search errors
|
|
specerrors=specerrors || err>nonstricterrtol;
|
|
//--- special test for Gauss-Jacobi (Chebyshev weight
|
|
//--- function with analytically known nodes/weights)
|
|
err=0;
|
|
CGaussQ::GQGenerateGaussJacobi(n,-0.5,-0.5,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
err=MathMax(err,MathAbs(x[i]+MathCos(M_PI*(i+0.5)/n)));
|
|
err=MathMax(err,MathAbs(w[i]-M_PI/n));
|
|
}
|
|
}
|
|
else
|
|
specerrors=true;
|
|
//--- search errors
|
|
specerrors=specerrors || err>stricterrtol;
|
|
//--- Test Gauss-Laguerre
|
|
err=0;
|
|
for(akind=0;akind<=9;akind++)
|
|
{
|
|
alphac=MapKind(akind);
|
|
//--- function call
|
|
CGaussQ::GQGenerateGaussLaguerre(n,alphac,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
BuildGaussLaguerreQuadrature(n,alphac,x2,w2);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
err=MathMax(err,MathAbs(x[i]-x2[i]));
|
|
err=MathMax(err,MathAbs(w[i]-w2[i]));
|
|
}
|
|
}
|
|
else
|
|
specerrors=true;
|
|
}
|
|
//--- search errors
|
|
specerrors=specerrors || err>nonstricterrtol;
|
|
//--- Test Gauss-Hermite
|
|
err=0;
|
|
CGaussQ::GQGenerateGaussHermite(n,info,x,w);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
BuildGaussHermiteQuadrature(n,x2,w2);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
err=MathMax(err,MathAbs(x[i]-x2[i]));
|
|
err=MathMax(err,MathAbs(w[i]-w2[i]));
|
|
}
|
|
}
|
|
else
|
|
specerrors=true;
|
|
//--- search errors
|
|
specerrors=specerrors || err>nonstricterrtol;
|
|
}
|
|
//--- end
|
|
waserrors=recerrors || specerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING GAUSS QUADRATURES");
|
|
Print("FINAL RESULT: ");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* SPECIAL CASES (LEGENDRE/JACOBI/..) ");
|
|
//--- check
|
|
if(specerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* RECURRENCE-BASED: ");
|
|
//--- check
|
|
if(recerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Maps: |
|
|
//| 0=> -0.9 |
|
|
//| 1=> -0.5 |
|
|
//| 2=> -0.1 |
|
|
//| 3=> 0.0 |
|
|
//| 4=> +0.1 |
|
|
//| 5=> +0.5 |
|
|
//| 6=> +0.9 |
|
|
//| 7=> +1.0 |
|
|
//| 8=> +1.5 |
|
|
//| 9=> +2.0 |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestGQUnit::MapKind(const int k)
|
|
{
|
|
//--- create a variable
|
|
double result=0;
|
|
//--- check
|
|
if(k==0)
|
|
result=-0.9;
|
|
//--- check
|
|
if(k==1)
|
|
result=-0.5;
|
|
//--- check
|
|
if(k==2)
|
|
result=-0.1;
|
|
//--- check
|
|
if(k==3)
|
|
result=0.0;
|
|
//--- check
|
|
if(k==4)
|
|
result=0.1;
|
|
//--- check
|
|
if(k==5)
|
|
result=0.5;
|
|
//--- check
|
|
if(k==6)
|
|
result=0.9;
|
|
//--- check
|
|
if(k==7)
|
|
result=1.0;
|
|
//--- check
|
|
if(k==8)
|
|
result=1.5;
|
|
//--- check
|
|
if(k==9)
|
|
result=2.0;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Gauss-Legendre, another variant |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestGQUnit::BuildGaussLegendreQuadrature(const int n,double &x[],
|
|
double &w[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double r=0;
|
|
double r1=0;
|
|
double p1=0;
|
|
double p2=0;
|
|
double p3=0;
|
|
double dp3=0;
|
|
double tmp=0;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(w,n);
|
|
//--- calculation
|
|
for(i=0;i<=(n+1)/2-1;i++)
|
|
{
|
|
r=MathCos(M_PI*(4*i+3)/(4*n+2));
|
|
//--- cycle
|
|
do
|
|
{
|
|
//--- change values
|
|
p2=0;
|
|
p3=1;
|
|
//--- calculation
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
p1=p2;
|
|
p2=p3;
|
|
p3=((2*j+1)*r*p2-j*p1)/(j+1);
|
|
}
|
|
dp3=n*(r*p3-p2)/(r*r-1);
|
|
r1=r;
|
|
r=r-p3/dp3;
|
|
}
|
|
while(MathAbs(r-r1)>=CMath::m_machineepsilon*(1+MathAbs(r))*100);
|
|
//--- calculation
|
|
x[i]=r;
|
|
x[n-1-i]=-r;
|
|
w[i]=2/((1-r*r)*dp3*dp3);
|
|
w[n-1-i]=2/((1-r*r)*dp3*dp3);
|
|
}
|
|
//--- shift
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-2-i;j++)
|
|
{
|
|
//--- check
|
|
if(x[j]>=x[j+1])
|
|
{
|
|
tmp=x[j];
|
|
x[j]=x[j+1];
|
|
x[j+1]=tmp;
|
|
tmp=w[j];
|
|
w[j]=w[j+1];
|
|
w[j+1]=tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Gauss-Jacobi, another variant |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestGQUnit::BuildGaussJacobiQuadrature(const int n,const double alpha,
|
|
const double beta,double &x[],
|
|
double &w[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double r=0;
|
|
double r1=0;
|
|
double t1=0;
|
|
double t2=0;
|
|
double t3=0;
|
|
double p1=0;
|
|
double p2=0;
|
|
double p3=0;
|
|
double pp=0;
|
|
double an=0;
|
|
double bn=0;
|
|
double a=0;
|
|
double b=0;
|
|
double c=0;
|
|
double tmpsgn=0;
|
|
double tmp=0;
|
|
double alfbet=0;
|
|
double temp=0;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(w,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i==0)
|
|
{
|
|
//--- calculation
|
|
an=alpha/n;
|
|
bn=beta/n;
|
|
t1=(1+alpha)*(2.78/(4+n*n)+0.768*an/n);
|
|
t2=1+1.48*an+0.96*bn+0.452*an*an+0.83*an*bn;
|
|
r=(t2-t1)/t2;
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==1)
|
|
{
|
|
//--- calculation
|
|
t1=(4.1+alpha)/((1+alpha)*(1+0.156*alpha));
|
|
t2=1+0.06*(n-8)*(1+0.12*alpha)/n;
|
|
t3=1+0.012*beta*(1+0.25*MathAbs(alpha))/n;
|
|
r=r-t1*t2*t3*(1-r);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==2)
|
|
{
|
|
//--- calculation
|
|
t1=(1.67+0.28*alpha)/(1+0.37*alpha);
|
|
t2=1+0.22*(n-8)/n;
|
|
t3=1+8*beta/((6.28+beta)*n*n);
|
|
r=r-t1*t2*t3*(x[0]-r);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(i<n-2)
|
|
r=3*x[i-1]-3*x[i-2]+x[i-3];
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==n-2)
|
|
{
|
|
//--- calculation
|
|
t1=(1+0.235*beta)/(0.766+0.119*beta);
|
|
t2=1/(1+0.639*(n-4)/(1+0.71*(n-4)));
|
|
t3=1/(1+20*alpha/((7.5+alpha)*n*n));
|
|
r=r+t1*t2*t3*(r-x[i-2]);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==n-1)
|
|
{
|
|
//--- calculation
|
|
t1=(1+0.37*beta)/(1.67+0.28*beta);
|
|
t2=1/(1+0.22*(n-8)/n);
|
|
t3=1/(1+8*alpha/((6.28+alpha)*n*n));
|
|
r=r+t1*t2*t3*(r-x[i-2]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
alfbet=alpha+beta;
|
|
//--- cycle
|
|
do
|
|
{
|
|
//--- change values
|
|
temp=2+alfbet;
|
|
p1=(alpha-beta+temp*r)*0.5;
|
|
p2=1;
|
|
//--- calculation
|
|
for(j=2;j<=n;j++)
|
|
{
|
|
p3=p2;
|
|
p2=p1;
|
|
temp=2*j+alfbet;
|
|
a=2*j*(j+alfbet)*(temp-2);
|
|
b=(temp-1)*(alpha*alpha-beta*beta+temp*(temp-2)*r);
|
|
c=2*(j-1+alpha)*(j-1+beta)*temp;
|
|
p1=(b*p2-c*p3)/a;
|
|
}
|
|
pp=(n*(alpha-beta-temp*r)*p1+2*(n+alpha)*(n+beta)*p2)/(temp*(1-r*r));
|
|
r1=r;
|
|
r=r1-p1/pp;
|
|
}
|
|
while(MathAbs(r-r1)>=CMath::m_machineepsilon*(1+MathAbs(r))*100);
|
|
//--- change values
|
|
x[i]=r;
|
|
w[i]=MathExp(CGammaFunc::LnGamma(alpha+n,tmpsgn)+CGammaFunc::LnGamma(beta+n,tmpsgn)-CGammaFunc::LnGamma(n+1,tmpsgn)-CGammaFunc::LnGamma(n+alfbet+1,tmpsgn))*temp*MathPow(2,alfbet)/(pp*p2);
|
|
}
|
|
//--- shift
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-2-i;j++)
|
|
{
|
|
//--- check
|
|
if(x[j]>=x[j+1])
|
|
{
|
|
tmp=x[j];
|
|
x[j]=x[j+1];
|
|
x[j+1]=tmp;
|
|
tmp=w[j];
|
|
w[j]=w[j+1];
|
|
w[j+1]=tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Gauss-Laguerre, another variant |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestGQUnit::BuildGaussLaguerreQuadrature(const int n,const double alpha,
|
|
double &x[],double &w[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double r=0;
|
|
double r1=0;
|
|
double p1=0;
|
|
double p2=0;
|
|
double p3=0;
|
|
double dp3=0;
|
|
double tsg=0;
|
|
double tmp=0;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(w,n);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i==0)
|
|
r=(1+alpha)*(3+0.92*alpha)/(1+2.4*n+1.8*alpha);
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==1)
|
|
r=r+(15+6.25*alpha)/(1+0.9*alpha+2.5*n);
|
|
else
|
|
r=r+((1+2.55*(i-1))/(1.9*(i-1))+1.26*(i-1)*alpha/(1+3.5*(i-1)))/(1+0.3*alpha)*(r-x[i-2]);
|
|
}
|
|
do
|
|
{
|
|
//--- change values
|
|
p2=0;
|
|
p3=1;
|
|
//--- calculation
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
p1=p2;
|
|
p2=p3;
|
|
p3=((-r+2*j+alpha+1)*p2-(j+alpha)*p1)/(j+1);
|
|
}
|
|
dp3=(n*p3-(n+alpha)*p2)/r;
|
|
r1=r;
|
|
r=r-p3/dp3;
|
|
}
|
|
//--- change values
|
|
while(MathAbs(r-r1)>=CMath::m_machineepsilon*(1+MathAbs(r))*100);
|
|
//--- change values
|
|
x[i]=r;
|
|
w[i]=-(MathExp(CGammaFunc::LnGamma(alpha+n,tsg)-CGammaFunc::LnGamma(n,tsg))/(dp3*n*p2));
|
|
}
|
|
//--- shift
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-2-i;j++)
|
|
{
|
|
//--- check
|
|
if(x[j]>=x[j+1])
|
|
{
|
|
tmp=x[j];
|
|
x[j]=x[j+1];
|
|
x[j+1]=tmp;
|
|
tmp=w[j];
|
|
w[j]=w[j+1];
|
|
w[j+1]=tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Gauss-Hermite, another variant |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestGQUnit::BuildGaussHermiteQuadrature(const int n,double &x[],
|
|
double &w[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double r=0;
|
|
double r1=0;
|
|
double p1=0;
|
|
double p2=0;
|
|
double p3=0;
|
|
double dp3=0;
|
|
double pipm4=0;
|
|
double tmp=0;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(w,n);
|
|
//--- calculation
|
|
pipm4=MathPow(M_PI,-0.25);
|
|
for(i=0;i<=(n+1)/2-1;i++)
|
|
{
|
|
//--- check
|
|
if(i==0)
|
|
r=MathSqrt(2*n+1)-1.85575*MathPow(2*n+1,-(1.0/6.0));
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==1)
|
|
r=r-1.14*MathPow(n,0.426)/r;
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==2)
|
|
r=1.86*r-0.86*x[0];
|
|
else
|
|
{
|
|
//--- check
|
|
if(i==3)
|
|
r=1.91*r-0.91*x[1];
|
|
else
|
|
r=2*r-x[i-2];
|
|
}
|
|
}
|
|
}
|
|
//--- cycle
|
|
do
|
|
{
|
|
//--- change values
|
|
p2=0;
|
|
p3=pipm4;
|
|
//--- calculation
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
p1=p2;
|
|
p2=p3;
|
|
p3=p2*r*MathSqrt(2.0/(double)(j+1))-p1*MathSqrt((double)j/(double)(j+1));
|
|
}
|
|
dp3=MathSqrt(2*j)*p2;
|
|
r1=r;
|
|
r=r-p3/dp3;
|
|
}
|
|
while(MathAbs(r-r1)>=CMath::m_machineepsilon*(1+MathAbs(r))*100);
|
|
//--- change values
|
|
x[i]=r;
|
|
w[i]=2/(dp3*dp3);
|
|
x[n-1-i]=-x[i];
|
|
w[n-1-i]=w[i];
|
|
}
|
|
//--- shift
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-2-i;j++)
|
|
{
|
|
//--- check
|
|
if(x[j]>=x[j+1])
|
|
{
|
|
tmp=x[j];
|
|
x[j]=x[j+1];
|
|
x[j+1]=tmp;
|
|
tmp=w[j];
|
|
w[j]=w[j+1];
|
|
w[j+1]=tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CGaussKronrodQ |
|
|
//+------------------------------------------------------------------+
|
|
class CTestGKQUnit
|
|
{
|
|
private:
|
|
//--- private method
|
|
static double MapKind(const int k);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestGKQUnit(void);
|
|
~CTestGKQUnit(void);
|
|
//--- public method
|
|
static bool TestGKQ(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestGKQUnit::CTestGKQUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestGKQUnit::~CTestGKQUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestGKQUnit::TestGKQ(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int pkind=0;
|
|
double errtol=0;
|
|
double eps=0;
|
|
double nonstricterrtol=0;
|
|
int n=0;
|
|
int i=0;
|
|
int k=0;
|
|
int info=0;
|
|
double err=0;
|
|
int akind=0;
|
|
int bkind=0;
|
|
double alphac=0;
|
|
double betac=0;
|
|
int info1=0;
|
|
int info2=0;
|
|
bool successatleastonce;
|
|
bool intblerrors;
|
|
bool vstblerrors;
|
|
bool generrors;
|
|
bool waserrors;
|
|
//--- create arrays
|
|
double x1[];
|
|
double wg1[];
|
|
double wk1[];
|
|
double x2[];
|
|
double wg2[];
|
|
double wk2[];
|
|
//--- initialization
|
|
intblerrors=false;
|
|
vstblerrors=false;
|
|
generrors=false;
|
|
waserrors=false;
|
|
errtol=10000*CMath::m_machineepsilon;
|
|
nonstricterrtol=1000*errtol;
|
|
//--- test recurrence-based Legendre nodes against the precalculated table
|
|
for(pkind=0;pkind<=5;pkind++)
|
|
{
|
|
n=0;
|
|
//--- check
|
|
if(pkind==0)
|
|
n=15;
|
|
//--- check
|
|
if(pkind==1)
|
|
n=21;
|
|
//--- check
|
|
if(pkind==2)
|
|
n=31;
|
|
//--- check
|
|
if(pkind==3)
|
|
n=41;
|
|
//--- check
|
|
if(pkind==4)
|
|
n=51;
|
|
//--- check
|
|
if(pkind==5)
|
|
n=61;
|
|
//--- function calls
|
|
CGaussKronrodQ::GKQLegendreCalc(n,info,x1,wk1,wg1);
|
|
CGaussKronrodQ::GKQLegendreTbl(n,x2,wk2,wg2,eps);
|
|
//--- check
|
|
if(info<=0)
|
|
{
|
|
generrors=true;
|
|
break;
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
vstblerrors=vstblerrors || MathAbs(x1[i]-x2[i])>errtol;
|
|
vstblerrors=vstblerrors || MathAbs(wk1[i]-wk2[i])>errtol;
|
|
vstblerrors=vstblerrors || MathAbs(wg1[i]-wg2[i])>errtol;
|
|
}
|
|
}
|
|
//--- Test recurrence-baced Gauss-Kronrod nodes against Gauss-only nodes
|
|
//--- calculated with subroutines from GQ unit.
|
|
for(k=1;k<=30;k++)
|
|
{
|
|
n=2*k+1;
|
|
//--- Gauss-Legendre
|
|
err=0;
|
|
CGaussKronrodQ::GKQGenerateGaussLegendre(n,info1,x1,wk1,wg1);
|
|
CGaussQ::GQGenerateGaussLegendre(k,info2,x2,wg2);
|
|
//--- check
|
|
if(info1>0 && info2>0)
|
|
{
|
|
//--- search errors
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
err=MathMax(err,MathAbs(x1[2*i+1]-x2[i]));
|
|
err=MathMax(err,MathAbs(wg1[2*i+1]-wg2[i]));
|
|
}
|
|
}
|
|
else
|
|
generrors=true;
|
|
//--- search errors
|
|
generrors=generrors || err>errtol;
|
|
}
|
|
//--- calculation
|
|
for(k=1;k<=15;k++)
|
|
{
|
|
n=2*k+1;
|
|
//--- Gauss-Jacobi
|
|
successatleastonce=false;
|
|
err=0;
|
|
for(akind=0;akind<=9;akind++)
|
|
{
|
|
for(bkind=0;bkind<=9;bkind++)
|
|
{
|
|
//--- change values
|
|
alphac=MapKind(akind);
|
|
betac=MapKind(bkind);
|
|
//--- function calls
|
|
CGaussKronrodQ::GKQGenerateGaussJacobi(n,alphac,betac,info1,x1,wk1,wg1);
|
|
CGaussQ::GQGenerateGaussJacobi(k,alphac,betac,info2,x2,wg2);
|
|
//--- check
|
|
if(info1>0 && info2>0)
|
|
{
|
|
successatleastonce=true;
|
|
//--- search errors
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
err=MathMax(err,MathAbs(x1[2*i+1]-x2[i]));
|
|
err=MathMax(err,MathAbs(wg1[2*i+1]-wg2[i]));
|
|
}
|
|
}
|
|
else
|
|
generrors=generrors || info1!=-5;
|
|
}
|
|
}
|
|
//--- search errors
|
|
generrors=(generrors || err>errtol) || !successatleastonce;
|
|
}
|
|
//--- end
|
|
waserrors=(intblerrors || vstblerrors) || generrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING GAUSS-KRONROD QUADRATURES");
|
|
Print("FINAL RESULT: ");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* PRE-CALCULATED TABLE: ");
|
|
//--- check
|
|
if(intblerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* CALCULATED AGAINST THE TABLE: ");
|
|
//--- check
|
|
if(vstblerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* GENERAL PROPERTIES: ");
|
|
//--- check
|
|
if(generrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Maps: |
|
|
//| 0=> -0.9 |
|
|
//| 1=> -0.5 |
|
|
//| 2=> -0.1 |
|
|
//| 3=> 0.0 |
|
|
//| 4=> +0.1 |
|
|
//| 5=> +0.5 |
|
|
//| 6=> +0.9 |
|
|
//| 7=> +1.0 |
|
|
//| 8=> +1.5 |
|
|
//| 9=> +2.0 |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestGKQUnit::MapKind(const int k)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
//--- check
|
|
if(k==0)
|
|
result=-0.9;
|
|
//--- check
|
|
if(k==1)
|
|
result=-0.5;
|
|
//--- check
|
|
if(k==2)
|
|
result=-0.1;
|
|
//--- check
|
|
if(k==3)
|
|
result=0.0;
|
|
//--- check
|
|
if(k==4)
|
|
result=0.1;
|
|
//--- check
|
|
if(k==5)
|
|
result=0.5;
|
|
//--- check
|
|
if(k==6)
|
|
result=0.9;
|
|
//--- check
|
|
if(k==7)
|
|
result=1.0;
|
|
//--- check
|
|
if(k==8)
|
|
result=1.5;
|
|
//--- check
|
|
if(k==9)
|
|
result=2.0;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CAutoGK |
|
|
//+------------------------------------------------------------------+
|
|
class CTestAutoGKUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestAutoGKUnit(void);
|
|
~CTestAutoGKUnit(void);
|
|
//--- public method
|
|
static bool TestAutoGK(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestAutoGKUnit::CTestAutoGKUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestAutoGKUnit::~CTestAutoGKUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestAutoGKUnit::TestAutoGK(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double a=0;
|
|
double b=0;
|
|
double v=0;
|
|
double exact=0;
|
|
double eabs=0;
|
|
double alpha=0;
|
|
int pkind=0;
|
|
double errtol=0;
|
|
bool simpleerrors;
|
|
bool sngenderrors;
|
|
bool waserrors;
|
|
//--- objects of classes
|
|
CAutoGKState state;
|
|
CAutoGKReport rep;
|
|
//--- initialization
|
|
simpleerrors=false;
|
|
sngenderrors=false;
|
|
waserrors=false;
|
|
errtol=10000*CMath::m_machineepsilon;
|
|
//--- Simple test: integral(exp(x),+-1,+-2),no maximum width requirements
|
|
a=(2*CMath::RandomInteger(2)-1)*1.0;
|
|
b=(2*CMath::RandomInteger(2)-1)*2.0;
|
|
//--- function call
|
|
CAutoGK::AutoGKSmooth(a,b,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
state.m_f=MathExp(state.m_x);
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,v,rep);
|
|
//--- change values
|
|
exact=MathExp(b)-MathExp(a);
|
|
eabs=MathAbs(MathExp(b)-MathExp(a));
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
simpleerrors=true;
|
|
else
|
|
simpleerrors=simpleerrors || MathAbs(exact-v)>errtol*eabs;
|
|
//--- Simple test: integral(exp(x),+-1,+-2),XWidth=0.1
|
|
a=(2*CMath::RandomInteger(2)-1)*1.0;
|
|
b=(2*CMath::RandomInteger(2)-1)*2.0;
|
|
//--- function call
|
|
CAutoGK::AutoGKSmoothW(a,b,0.1,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
state.m_f=MathExp(state.m_x);
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,v,rep);
|
|
//--- change values
|
|
exact=MathExp(b)-MathExp(a);
|
|
eabs=MathAbs(MathExp(b)-MathExp(a));
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
simpleerrors=true;
|
|
else
|
|
simpleerrors=simpleerrors || MathAbs(exact-v)>errtol*eabs;
|
|
//--- Simple test: integral(cos(100*x),0,2*pi),no maximum width requirements
|
|
a=0;
|
|
b=2*M_PI;
|
|
//--- function call
|
|
CAutoGK::AutoGKSmooth(a,b,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
state.m_f=MathCos(100*state.m_x);
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,v,rep);
|
|
//--- change values
|
|
exact=0;
|
|
eabs=4;
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
simpleerrors=true;
|
|
else
|
|
simpleerrors=simpleerrors || MathAbs(exact-v)>errtol*eabs;
|
|
//--- Simple test: integral(cos(100*x),0,2*pi),XWidth=0.3
|
|
a=0;
|
|
b=2*M_PI;
|
|
//--- function call
|
|
CAutoGK::AutoGKSmoothW(a,b,0.3,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
state.m_f=MathCos(100*state.m_x);
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,v,rep);
|
|
//--- change values
|
|
exact=0;
|
|
eabs=4;
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
simpleerrors=true;
|
|
else
|
|
simpleerrors=simpleerrors || MathAbs(exact-v)>errtol*eabs;
|
|
//--- singular problem on [a,b]=[0.1,0.5]
|
|
//--- f2(x)=(1+x)*(b-x)^alpha,-1 < alpha < 1
|
|
for(pkind=0;pkind<=6;pkind++)
|
|
{
|
|
a=0.1;
|
|
b=0.5;
|
|
//--- check
|
|
if(pkind==0)
|
|
alpha=-0.9;
|
|
//--- check
|
|
if(pkind==1)
|
|
alpha=-0.5;
|
|
//--- check
|
|
if(pkind==2)
|
|
alpha=-0.1;
|
|
//--- check
|
|
if(pkind==3)
|
|
alpha=0.0;
|
|
//--- check
|
|
if(pkind==4)
|
|
alpha=0.1;
|
|
//--- check
|
|
if(pkind==5)
|
|
alpha=0.5;
|
|
//--- check
|
|
if(pkind==6)
|
|
alpha=0.9;
|
|
//--- f1(x)=(1+x)*(x-a)^alpha,-1 < alpha < 1
|
|
//--- 1. use singular integrator for [a,b]
|
|
//--- 2. use singular integrator for [b,a]
|
|
exact=MathPow(b-a,alpha+2)/(alpha+2)+(1+a)*MathPow(b-a,alpha+1)/(alpha+1);
|
|
eabs=MathAbs(exact);
|
|
//--- function call
|
|
CAutoGK::AutoGKSingular(a,b,alpha,0.0,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_xminusa<0.01)
|
|
state.m_f=MathPow(state.m_xminusa,alpha)*(1+state.m_x);
|
|
else
|
|
state.m_f=MathPow(state.m_x-a,alpha)*(1+state.m_x);
|
|
}
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,v,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
sngenderrors=true;
|
|
else
|
|
sngenderrors=sngenderrors || MathAbs(v-exact)>errtol*eabs;
|
|
//--- function call
|
|
CAutoGK::AutoGKSingular(b,a,0.0,alpha,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_bminusx>-0.01)
|
|
state.m_f=MathPow(-state.m_bminusx,alpha)*(1+state.m_x);
|
|
else
|
|
state.m_f=MathPow(state.m_x-a,alpha)*(1+state.m_x);
|
|
}
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,v,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
sngenderrors=true;
|
|
else
|
|
sngenderrors=sngenderrors || MathAbs(-v-exact)>errtol*eabs;
|
|
//--- f1(x)=(1+x)*(b-x)^alpha,-1 < alpha < 1
|
|
//--- 1. use singular integrator for [a,b]
|
|
//--- 2. use singular integrator for [b,a]
|
|
exact=(1+b)*MathPow(b-a,alpha+1)/(alpha+1)-MathPow(b-a,alpha+2)/(alpha+2);
|
|
eabs=MathAbs(exact);
|
|
//--- function call
|
|
CAutoGK::AutoGKSingular(a,b,0.0,alpha,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_bminusx<0.01)
|
|
state.m_f=MathPow(state.m_bminusx,alpha)*(1+state.m_x);
|
|
else
|
|
state.m_f=MathPow(b-state.m_x,alpha)*(1+state.m_x);
|
|
}
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,v,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
sngenderrors=true;
|
|
else
|
|
sngenderrors=sngenderrors || MathAbs(v-exact)>errtol*eabs;
|
|
//--- function call
|
|
CAutoGK::AutoGKSingular(b,a,alpha,0.0,state);
|
|
//--- cycle
|
|
while(CAutoGK::AutoGKIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_xminusa>-0.01)
|
|
state.m_f=MathPow(-state.m_xminusa,alpha)*(1+state.m_x);
|
|
else
|
|
state.m_f=MathPow(b-state.m_x,alpha)*(1+state.m_x);
|
|
}
|
|
//--- function call
|
|
CAutoGK::AutoGKResults(state,v,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype<=0)
|
|
sngenderrors=true;
|
|
else
|
|
sngenderrors=sngenderrors || MathAbs(-v-exact)>errtol*eabs;
|
|
}
|
|
//--- end
|
|
waserrors=simpleerrors || sngenderrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING AUTOGK");
|
|
Print("INTEGRATION WITH GIVEN ACCURACY: ");
|
|
//--- check
|
|
if(simpleerrors || sngenderrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* SIMPLE PROBLEMS: ");
|
|
//--- check
|
|
if(simpleerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("* SINGULAR PROBLEMS (ENDS OF INTERVAL): ");
|
|
//--- check
|
|
if(sngenderrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CIDWInt |
|
|
//+------------------------------------------------------------------+
|
|
class CTestIDWIntUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void Unset2D(CMatrixComplex &a);
|
|
static void Unset1D(double &a[]);
|
|
static void TestXY(CMatrixDouble &xy,const int n,const int nx,const int d,const int nq,const int nw,bool &idwerrors);
|
|
static void TestRXY(CMatrixDouble &xy,const int n,const int nx,const double r,bool &idwerrors);
|
|
static void TestDegree(const int n,const int nx,const int d,const int dtask,bool &idwerrors);
|
|
static void TestNoisy(bool &idwerrors);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestIDWIntUnit(void);
|
|
~CTestIDWIntUnit(void);
|
|
//--- public method
|
|
static bool TestIDWInt(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestIDWIntUnit::CTestIDWIntUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestIDWIntUnit::~CTestIDWIntUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing IDW interpolation |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestIDWIntUnit::TestIDWInt(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double vx=0;
|
|
double vy=0;
|
|
double vz=0;
|
|
int d=0;
|
|
int dtask=0;
|
|
int nx=0;
|
|
int nq=0;
|
|
int nw=0;
|
|
int smalln=0;
|
|
int largen=0;
|
|
bool waserrors;
|
|
bool idwerrors;
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- initialization
|
|
idwerrors=false;
|
|
smalln=256;
|
|
largen=1024;
|
|
nq=10;
|
|
nw=18;
|
|
//--- Simple test:
|
|
//--- * F=x^3 + sin(pi*y)*z^2 - (x+y)^2
|
|
//--- * space is either R1=[-1,+1] (other dimensions are
|
|
//--- fixed at 0),R1^2 or R1^3.
|
|
//* D=-1,0,1,2
|
|
for(nx=1;nx<=2;nx++)
|
|
{
|
|
//--- allocation
|
|
xy.Resize(largen,nx+1);
|
|
for(i=0;i<=largen-1;i++)
|
|
{
|
|
for(j=0;j<=nx-1;j++)
|
|
xy[i].Set(j,2*CMath::RandomReal()-1);
|
|
//--- check
|
|
if(nx>=1)
|
|
vx=xy[i][0];
|
|
else
|
|
vx=0;
|
|
//--- check
|
|
if(nx>=2)
|
|
vy=xy[i][1];
|
|
else
|
|
vy=0;
|
|
//--- check
|
|
if(nx>=3)
|
|
vz=xy[i][2];
|
|
else
|
|
vz=0;
|
|
//--- calculation
|
|
xy[i].Set(nx,vx*vx*vx+MathSin(M_PI*vy)*CMath::Sqr(vz)-CMath::Sqr(vx+vy));
|
|
}
|
|
for(d=-1;d<=2;d++)
|
|
TestXY(xy,largen,nx,d,nq,nw,idwerrors);
|
|
}
|
|
//--- Another simple test:
|
|
//--- * five points in 2D - (0,0),(0,1),(1,0),(-1,0) (0,-1)
|
|
//--- * F is random
|
|
//--- * D=-1,0,1,2
|
|
nx=2;
|
|
xy.Resize(5,nx+1);
|
|
//--- change values
|
|
xy[0].Set(0,0);
|
|
xy[0].Set(1,0);
|
|
xy[0].Set(2,2*CMath::RandomReal()-1);
|
|
xy[1].Set(0,1);
|
|
xy[1].Set(1,0);
|
|
xy[1].Set(2,2*CMath::RandomReal()-1);
|
|
xy[2].Set(0,0);
|
|
xy[2].Set(1,1);
|
|
xy[2].Set(2,2*CMath::RandomReal()-1);
|
|
xy[3].Set(0,-1);
|
|
xy[3].Set(1,0);
|
|
xy[3].Set(2,2*CMath::RandomReal()-1);
|
|
xy[4].Set(0,0);
|
|
xy[4].Set(1,-1);
|
|
xy[4].Set(2,2*CMath::RandomReal()-1);
|
|
//--- calculation
|
|
for(d=-1;d<=2;d++)
|
|
TestXY(xy,5,nx,d,nq,nw,idwerrors);
|
|
//--- Degree test.
|
|
//--- F is either:
|
|
//--- * constant (DTask=0)
|
|
//--- * linear (DTask=1)
|
|
//--- * quadratic (DTask=2)
|
|
//--- Nodal functions are either
|
|
//--- * constant (D=0)
|
|
//--- * linear (D=1)
|
|
//--- * quadratic (D=2)
|
|
//--- When DTask<=D,we can interpolate without errors.
|
|
//--- When DTask>D,we MUST have errors.
|
|
for(nx=1;nx<=3;nx++)
|
|
{
|
|
for(d=0;d<=2;d++)
|
|
for(dtask=0;dtask<=2;dtask++)
|
|
{
|
|
TestDegree(smalln,nx,d,dtask,idwerrors);
|
|
}
|
|
}
|
|
//--- Noisy test
|
|
TestNoisy(idwerrors);
|
|
//--- report
|
|
waserrors=idwerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING INVERSE DISTANCE WEIGHTING");
|
|
Print("* IDW: ");
|
|
//--- check
|
|
if(!idwerrors)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 2D array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestIDWIntUnit::Unset2D(CMatrixComplex &a)
|
|
{
|
|
//--- allocation
|
|
a.Resize(1,1);
|
|
//--- change value
|
|
a[0].Set(0,2*CMath::RandomReal()-1);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets 1D array. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestIDWIntUnit::Unset1D(double &a[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(a,1);
|
|
//--- change value
|
|
a[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing IDW: |
|
|
//| * generate model using N/NX/D/NQ/NW |
|
|
//| * test basic properties |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestIDWIntUnit::TestXY(CMatrixDouble &xy,const int n,const int nx,
|
|
const int d,const int nq,const int nw,
|
|
bool &idwerrors)
|
|
{
|
|
//--- create variables
|
|
double threshold=0;
|
|
double lipschitzstep=0;
|
|
int i=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
double v=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double t=0;
|
|
double l1=0;
|
|
double l2=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double x[];
|
|
//--- object of class
|
|
CIDWInterpolant z1;
|
|
//--- initialization
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
lipschitzstep=0.001;
|
|
//--- allocation
|
|
ArrayResize(x,nx);
|
|
//--- build
|
|
CIDWInt::IDWBuildModifiedShepard(xy,n,nx,d,nq,nw,z1);
|
|
//--- first,test interpolation properties at nodes
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=xy[i][i_];
|
|
//--- search errors
|
|
idwerrors=idwerrors || CIDWInt::IDWCalc(z1,x)!=xy[i][nx];
|
|
}
|
|
//--- test Lipschitz continuity
|
|
i1=CMath::RandomInteger(n);
|
|
do
|
|
{
|
|
i2=CMath::RandomInteger(n);
|
|
}
|
|
while(i2==i1);
|
|
//--- change values
|
|
l1=0;
|
|
t=0;
|
|
//--- cycle
|
|
while(t<1.0)
|
|
{
|
|
//--- calculation
|
|
v=1-t;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=v*xy[i1][i_];
|
|
v=t;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=x[i_]+v*xy[i2][i_];
|
|
v1=CIDWInt::IDWCalc(z1,x);
|
|
v=1-(t+lipschitzstep);
|
|
//--- calculation
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=v*xy[i1][i_];
|
|
v=t+lipschitzstep;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=x[i_]+v*xy[i2][i_];
|
|
v2=CIDWInt::IDWCalc(z1,x);
|
|
l1=MathMax(l1,MathAbs(v2-v1)/lipschitzstep);
|
|
t=t+lipschitzstep;
|
|
}
|
|
//--- change values
|
|
l2=0;
|
|
t=0;
|
|
//--- cycle
|
|
while(t<1.0)
|
|
{
|
|
//--- calculation
|
|
v=1-t;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=v*xy[i1][i_];
|
|
v=t;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=x[i_]+v*xy[i2][i_];
|
|
v1=CIDWInt::IDWCalc(z1,x);
|
|
v=1-(t+lipschitzstep/3);
|
|
//--- calculation
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=v*xy[i1][i_];
|
|
v=t+lipschitzstep/3;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=x[i_]+v*xy[i2][i_];
|
|
v2=CIDWInt::IDWCalc(z1,x);
|
|
l2=MathMax(l2,MathAbs(v2-v1)/(lipschitzstep/3));
|
|
t=t+lipschitzstep/3;
|
|
}
|
|
//--- search errors
|
|
idwerrors=idwerrors || l2>2.0*l1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing IDW: |
|
|
//| * generate model using R-based model |
|
|
//| * test basic properties |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestIDWIntUnit::TestRXY(CMatrixDouble &xy,const int n,const int nx,
|
|
const double r,bool &idwerrors)
|
|
{
|
|
//--- create variables
|
|
double threshold=0;
|
|
double lipschitzstep=0;
|
|
int i=0;
|
|
int i1=0;
|
|
int i2=0;
|
|
double v=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double t=0;
|
|
double l1=0;
|
|
double l2=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double x[];
|
|
//--- object of class
|
|
CIDWInterpolant z1;
|
|
//--- initialization
|
|
threshold=1000*CMath::m_machineepsilon;
|
|
lipschitzstep=0.001;
|
|
//--- allocation
|
|
ArrayResize(x,nx);
|
|
//--- build
|
|
CIDWInt::IDWBuildModifiedShepardR(xy,n,nx,r,z1);
|
|
//--- first,test interpolation properties at nodes
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=xy[i][i_];
|
|
//--- search errors
|
|
idwerrors=idwerrors || CIDWInt::IDWCalc(z1,x)!=xy[i][nx];
|
|
}
|
|
//--- test Lipschitz continuity
|
|
i1=CMath::RandomInteger(n);
|
|
do
|
|
{
|
|
i2=CMath::RandomInteger(n);
|
|
}
|
|
while(i2==i1);
|
|
//--- change values
|
|
l1=0;
|
|
t=0;
|
|
//--- cycle
|
|
while(t<1.0)
|
|
{
|
|
//--- calculation
|
|
v=1-t;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=v*xy[i1][i_];
|
|
v=t;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=x[i_]+v*xy[i2][i_];
|
|
v1=CIDWInt::IDWCalc(z1,x);
|
|
v=1-(t+lipschitzstep);
|
|
//--- calculation
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=v*xy[i1][i_];
|
|
v=t+lipschitzstep;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=x[i_]+v*xy[i2][i_];
|
|
v2=CIDWInt::IDWCalc(z1,x);
|
|
l1=MathMax(l1,MathAbs(v2-v1)/lipschitzstep);
|
|
t=t+lipschitzstep;
|
|
}
|
|
//--- change values
|
|
l2=0;
|
|
t=0;
|
|
//--- cycle
|
|
while(t<1.0)
|
|
{
|
|
//--- calculation
|
|
v=1-t;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=v*xy[i1][i_];
|
|
v=t;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=x[i_]+v*xy[i2][i_];
|
|
v1=CIDWInt::IDWCalc(z1,x);
|
|
v=1-(t+lipschitzstep/3);
|
|
//--- calculation
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=v*xy[i1][i_];
|
|
v=t+lipschitzstep/3;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
x[i_]=x[i_]+v*xy[i2][i_];
|
|
v2=CIDWInt::IDWCalc(z1,x);
|
|
l2=MathMax(l2,MathAbs(v2-v1)/(lipschitzstep/3));
|
|
t=t+lipschitzstep/3;
|
|
}
|
|
//--- search errors
|
|
idwerrors=idwerrors || l2>2.0*l1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing degree properties |
|
|
//| F is either: |
|
|
//| * constant (DTask=0) |
|
|
//| * linear (DTask=1) |
|
|
//| * quadratic (DTask=2) |
|
|
//| Nodal functions are either |
|
|
//| * constant (D=0) |
|
|
//| * linear (D=1) |
|
|
//| * quadratic (D=2) |
|
|
//| When DTask<=D,we can interpolate without errors. |
|
|
//| When DTask>D,we MUST have errors. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestIDWIntUnit::TestDegree(const int n,const int nx,const int d,
|
|
const int dtask,bool &idwerrors)
|
|
{
|
|
//--- create variables
|
|
double threshold=0;
|
|
int nq=0;
|
|
int nw=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double c0=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double c1[];
|
|
double x[];
|
|
//--- create matrix
|
|
CMatrixDouble c2;
|
|
CMatrixDouble xy;
|
|
//--- object of class
|
|
CIDWInterpolant z1;
|
|
//--- initialization
|
|
threshold=1.0E6*CMath::m_machineepsilon;
|
|
nq=2*(nx*nx+nx+1);
|
|
nw=10;
|
|
//--- check
|
|
if(!CAp::Assert(nq<=n,"TestDegree: internal error"))
|
|
return;
|
|
//--- prepare model
|
|
c0=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(c1,nx);
|
|
for(i=0;i<=nx-1;i++)
|
|
c1[i]=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
c2.Resize(nx,nx);
|
|
for(i=0;i<=nx-1;i++)
|
|
{
|
|
for(j=i+1;j<=nx-1;j++)
|
|
{
|
|
c2[i].Set(j,2*CMath::RandomReal()-1);
|
|
c2[j].Set(i,c2[i][j]);
|
|
}
|
|
//--- cycle
|
|
do
|
|
{
|
|
c2[i].Set(i,2*CMath::RandomReal()-1);
|
|
}
|
|
while(MathAbs(c2[i][i])<=0.3);
|
|
}
|
|
//--- prepare points
|
|
xy.Resize(n,nx+1);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=nx-1;j++)
|
|
xy[i].Set(j,4*CMath::RandomReal()-2);
|
|
xy[i].Set(nx,c0);
|
|
//--- check
|
|
if(dtask>=1)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
v+=c1[i_]*xy[i][i_];
|
|
xy[i].Set(nx,xy[i][nx]+v);
|
|
}
|
|
//--- check
|
|
if(dtask==2)
|
|
{
|
|
for(j=0;j<=nx-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
v+=c2[j][i_]*xy[i][i_];
|
|
xy[i].Set(nx,xy[i][nx]+xy[i][j]*v);
|
|
}
|
|
}
|
|
}
|
|
//--- build interpolant,calculate value at random point
|
|
CIDWInt::IDWBuildModifiedShepard(xy,n,nx,d,nq,nw,z1);
|
|
ArrayResize(x,nx);
|
|
for(i=0;i<=nx-1;i++)
|
|
x[i]=4*CMath::RandomReal()-2;
|
|
v1=CIDWInt::IDWCalc(z1,x);
|
|
//--- calculate model value at the same point
|
|
v2=c0;
|
|
//--- check
|
|
if(dtask>=1)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
v+=c1[i_]*x[i_];
|
|
v2=v2+v;
|
|
}
|
|
//--- check
|
|
if(dtask==2)
|
|
{
|
|
for(j=0;j<=nx-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=nx-1;i_++)
|
|
v+=c2[j][i_]*x[i_];
|
|
v2=v2+x[j]*v;
|
|
}
|
|
}
|
|
//--- Compare
|
|
if(dtask<=d)
|
|
idwerrors=idwerrors || MathAbs(v2-v1)>threshold;
|
|
else
|
|
idwerrors=idwerrors || MathAbs(v2-v1)<threshold;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Noisy test: |
|
|
//| * F=x^2 + y^2 + z^2 + noise on [-1,+1]^3 |
|
|
//| * space is either R1=[-1,+1] (other dimensions are |
|
|
//| fixed at 0),R1^2 or R1^3. |
|
|
//| * D=1,2 |
|
|
//| * 4096 points is used for function generation, |
|
|
//| 4096 points - for testing |
|
|
//| * RMS error of "noisy" model on test set must be |
|
|
//| lower than RMS error of interpolation model. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestIDWIntUnit::TestNoisy(bool &idwerrors)
|
|
{
|
|
//--- create variables
|
|
double noiselevel=0;
|
|
int nq=0;
|
|
int nw=0;
|
|
int d=0;
|
|
int nx=0;
|
|
int ntrn=0;
|
|
int ntst=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double t=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double ve=0;
|
|
double rms1=0;
|
|
double rms2=0;
|
|
//--- create array
|
|
double x[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- objects of classes
|
|
CIDWInterpolant z1;
|
|
CIDWInterpolant z2;
|
|
//--- initialization
|
|
nq=20;
|
|
nw=40;
|
|
noiselevel=0.2;
|
|
ntrn=256;
|
|
ntst=1024;
|
|
//--- calculation
|
|
for(d=1;d<=2;d++)
|
|
{
|
|
for(nx=1;nx<=2;nx++)
|
|
{
|
|
//--- prepare dataset
|
|
xy.Resize(ntrn,nx+1);
|
|
for(i=0;i<=ntrn-1;i++)
|
|
{
|
|
v=noiselevel*(2*CMath::RandomReal()-1);
|
|
//--- change values
|
|
for(j=0;j<=nx-1;j++)
|
|
{
|
|
t=2*CMath::RandomReal()-1;
|
|
v=v+CMath::Sqr(t);
|
|
xy[i].Set(j,t);
|
|
}
|
|
xy[i].Set(nx,v);
|
|
}
|
|
//--- build interpolants
|
|
CIDWInt::IDWBuildModifiedShepard(xy,ntrn,nx,d,nq,nw,z1);
|
|
CIDWInt::IDWBuildNoisy(xy,ntrn,nx,d,nq,nw,z2);
|
|
//--- calculate RMS errors
|
|
ArrayResize(x,nx);
|
|
rms1=0;
|
|
rms2=0;
|
|
for(i=0;i<=ntst-1;i++)
|
|
{
|
|
ve=0;
|
|
for(j=0;j<=nx-1;j++)
|
|
{
|
|
t=2*CMath::RandomReal()-1;
|
|
x[j]=t;
|
|
ve=ve+CMath::Sqr(t);
|
|
}
|
|
//--- change values
|
|
v1=CIDWInt::IDWCalc(z1,x);
|
|
v2=CIDWInt::IDWCalc(z2,x);
|
|
rms1=rms1+CMath::Sqr(v1-ve);
|
|
rms2=rms2+CMath::Sqr(v2-ve);
|
|
}
|
|
//--- search errors
|
|
idwerrors=idwerrors || rms2>rms1;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CRatInt |
|
|
//+------------------------------------------------------------------+
|
|
class CTestRatIntUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void PolDiff2(double &x[],double &cf[],int n,double t,double &p,double &dp,double &d2p);
|
|
static void BRCunSet(CBarycentricInterpolant &b);
|
|
static bool Is1DSolution(const int n,double &y[],double &w[],double c);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestRatIntUnit(void);
|
|
~CTestRatIntUnit(void);
|
|
//--- public method
|
|
static bool TestRatInt(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestRatIntUnit::CTestRatIntUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestRatIntUnit::~CTestRatIntUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CRatInt |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestRatIntUnit::TestRatInt(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool bcerrors;
|
|
bool nperrors;
|
|
double threshold=0;
|
|
double lipschitztol=0;
|
|
int maxn=0;
|
|
int passcount=0;
|
|
double h=0;
|
|
double s1=0;
|
|
double s2=0;
|
|
int n=0;
|
|
int n2=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int d=0;
|
|
int pass=0;
|
|
double maxerr=0;
|
|
double t=0;
|
|
double a=0;
|
|
double b=0;
|
|
double s=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double v3=0;
|
|
double d0=0;
|
|
double d1=0;
|
|
double d2=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double x2[];
|
|
double y[];
|
|
double y2[];
|
|
double w[];
|
|
double w2[];
|
|
double xc[];
|
|
double yc[];
|
|
int dc[];
|
|
//--- objects of classes
|
|
CBarycentricInterpolant b1;
|
|
CBarycentricInterpolant b2;
|
|
//--- initialization
|
|
nperrors=false;
|
|
bcerrors=false;
|
|
waserrors=false;
|
|
//--- PassCount number of repeated passes
|
|
//--- Threshold error tolerance
|
|
//--- LipschitzTol Lipschitz constant increase allowed
|
|
//--- when calculating constant on a twice denser grid
|
|
passcount=5;
|
|
maxn=15;
|
|
threshold=1000000*CMath::m_machineepsilon;
|
|
lipschitztol=1.3;
|
|
//--- Basic barycentric functions
|
|
for(n=1;n<=10;n++)
|
|
{
|
|
//--- randomized tests
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- generate weights from polynomial interpolation
|
|
v0=1+0.4*CMath::RandomReal()-0.2;
|
|
v1=2*CMath::RandomReal()-1;
|
|
v2=2*CMath::RandomReal()-1;
|
|
v3=2*CMath::RandomReal()-1;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(n==1)
|
|
x[i]=0;
|
|
else
|
|
x[i]=v0*MathCos(i*M_PI/(n-1));
|
|
y[i]=MathSin(v1*x[i])+MathCos(v2*x[i])+MathExp(v3*x[i]);
|
|
}
|
|
//--- calculation
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
w[j]=1;
|
|
for(k=0;k<=n-1;k++)
|
|
{
|
|
//--- check
|
|
if(k!=j)
|
|
w[j]=w[j]/(x[j]-x[k]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x,y,w,n,b1);
|
|
//--- unpack,then pack again and compare
|
|
BRCunSet(b2);
|
|
CRatInt::BarycentricUnpack(b1,n2,x2,y2,w2);
|
|
//--- search errors
|
|
bcerrors=bcerrors || n2!=n;
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x2,y2,w2,n2,b2);
|
|
t=2*CMath::RandomReal()-1;
|
|
//--- search errors
|
|
bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,t)-CRatInt::BarycentricCalc(b2,t))>threshold;
|
|
//--- copy,compare
|
|
BRCunSet(b2);
|
|
CRatInt::BarycentricCopy(b1,b2);
|
|
t=2*CMath::RandomReal()-1;
|
|
//--- search errors
|
|
bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,t)-CRatInt::BarycentricCalc(b2,t))>threshold;
|
|
//--- test interpolation properties
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- test interpolation at nodes
|
|
bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,x[i])-y[i])>threshold*MathAbs(y[i]);
|
|
//--- compare with polynomial interpolation
|
|
t=2*CMath::RandomReal()-1;
|
|
PolDiff2(x,y,n,t,v0,v1,v2);
|
|
//--- search errors
|
|
bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,t)-v0)>threshold*MathMax(MathAbs(v0),1);
|
|
//--- test continuity between nodes
|
|
//--- calculate Lipschitz constant on two grids -
|
|
//--- dense and even more dense. If Lipschitz constant
|
|
//--- on a denser grid is significantly increased,
|
|
//--- continuity test is failed
|
|
t=3.0;
|
|
k=100;
|
|
s1=0;
|
|
for(j=0;j<=k-1;j++)
|
|
{
|
|
v1=x[i]+(t-x[i])*j/k;
|
|
v2=x[i]+(t-x[i])*(j+1)/k;
|
|
s1=MathMax(s1,MathAbs(CRatInt::BarycentricCalc(b1,v2)-CRatInt::BarycentricCalc(b1,v1))/MathAbs(v2-v1));
|
|
}
|
|
//--- change values
|
|
k=2*k;
|
|
s2=0;
|
|
for(j=0;j<=k-1;j++)
|
|
{
|
|
v1=x[i]+(t-x[i])*j/k;
|
|
v2=x[i]+(t-x[i])*(j+1)/k;
|
|
s2=MathMax(s2,MathAbs(CRatInt::BarycentricCalc(b1,v2)-CRatInt::BarycentricCalc(b1,v1))/MathAbs(v2-v1));
|
|
}
|
|
//--- search errors
|
|
bcerrors=bcerrors || (s2>lipschitztol*s1 && s1>threshold*k);
|
|
}
|
|
//--- test differentiation properties
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
t=2*CMath::RandomReal()-1;
|
|
PolDiff2(x,y,n,t,v0,v1,v2);
|
|
d0=0;
|
|
d1=0;
|
|
d2=0;
|
|
//--- function call
|
|
CRatInt::BarycentricDiff1(b1,t,d0,d1);
|
|
//--- search errors
|
|
bcerrors=bcerrors || MathAbs(v0-d0)>threshold*MathMax(MathAbs(v0),1);
|
|
bcerrors=bcerrors || MathAbs(v1-d1)>threshold*MathMax(MathAbs(v1),1);
|
|
//--- change values
|
|
d0=0;
|
|
d1=0;
|
|
d2=0;
|
|
//--- function call
|
|
CRatInt::BarycentricDiff2(b1,t,d0,d1,d2);
|
|
//--- search errors
|
|
bcerrors=bcerrors || MathAbs(v0-d0)>threshold*MathMax(MathAbs(v0),1);
|
|
bcerrors=bcerrors || MathAbs(v1-d1)>threshold*MathMax(MathAbs(v1),1);
|
|
bcerrors=bcerrors || MathAbs(v2-d2)>MathSqrt(threshold)*MathMax(MathAbs(v2),1);
|
|
}
|
|
//--- test linear translation
|
|
t=2*CMath::RandomReal()-1;
|
|
a=2*CMath::RandomReal()-1;
|
|
b=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
BRCunSet(b2);
|
|
CRatInt::BarycentricCopy(b1,b2);
|
|
CRatInt::BarycentricLinTransX(b2,a,b);
|
|
//--- search errors
|
|
bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,a*t+b)-CRatInt::BarycentricCalc(b2,t))>threshold;
|
|
//--- change values
|
|
a=0;
|
|
b=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
BRCunSet(b2);
|
|
CRatInt::BarycentricCopy(b1,b2);
|
|
CRatInt::BarycentricLinTransX(b2,a,b);
|
|
//--- search errors
|
|
bcerrors=bcerrors || MathAbs(CRatInt::BarycentricCalc(b1,a*t+b)-CRatInt::BarycentricCalc(b2,t))>threshold;
|
|
//--- change values
|
|
a=2*CMath::RandomReal()-1;
|
|
b=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
BRCunSet(b2);
|
|
CRatInt::BarycentricCopy(b1,b2);
|
|
CRatInt::BarycentricLinTransY(b2,a,b);
|
|
//--- search errors
|
|
bcerrors=bcerrors || MathAbs(a*CRatInt::BarycentricCalc(b1,t)+b-CRatInt::BarycentricCalc(b2,t))>threshold;
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(pass=0;pass<=3;pass++)
|
|
{
|
|
//--- Crash-test: small numbers,large numbers
|
|
ArrayResize(x,4);
|
|
ArrayResize(y,4);
|
|
ArrayResize(w,4);
|
|
h=1;
|
|
//--- check
|
|
if(pass%2==0)
|
|
h=100*CMath::m_minrealnumber;
|
|
//--- check
|
|
if(pass%2==1)
|
|
h=0.01*CMath::m_maxrealnumber;
|
|
//--- change values
|
|
x[0]=0*h;
|
|
x[1]=1*h;
|
|
x[2]=2*h;
|
|
x[3]=3*h;
|
|
y[0]=0*h;
|
|
y[1]=1*h;
|
|
y[2]=2*h;
|
|
y[3]=3*h;
|
|
w[0]=-(1/(x[1]-x[0]));
|
|
w[1]=1*(1/(x[1]-x[0])+1/(x[2]-x[1]));
|
|
w[2]=-(1*(1/(x[2]-x[1])+1/(x[3]-x[2])));
|
|
w[3]=1/(x[3]-x[2]);
|
|
//--- check
|
|
if(pass/2==0)
|
|
v0=0;
|
|
//--- check
|
|
if(pass/2==1)
|
|
v0=0.6*h;
|
|
//--- function calls
|
|
CRatInt::BarycentricBuildXYW(x,y,w,4,b1);
|
|
t=CRatInt::BarycentricCalc(b1,v0);
|
|
//--- change values
|
|
d0=0;
|
|
d1=0;
|
|
d2=0;
|
|
//--- function call
|
|
CRatInt::BarycentricDiff1(b1,v0,d0,d1);
|
|
//--- search errors
|
|
bcerrors=bcerrors || MathAbs(t-v0)>threshold*v0;
|
|
bcerrors=bcerrors || MathAbs(d0-v0)>threshold*v0;
|
|
bcerrors=bcerrors || MathAbs(d1-1)>1000*threshold;
|
|
}
|
|
//--- crash test: large abscissas,small argument
|
|
//--- test for errors in D0 is not very strict
|
|
//--- because renormalization used in Diff1()
|
|
//--- destroys part of precision.
|
|
ArrayResize(x,4);
|
|
ArrayResize(y,4);
|
|
ArrayResize(w,4);
|
|
//--- change values
|
|
h=0.01*CMath::m_maxrealnumber;
|
|
x[0]=0*h;
|
|
x[1]=1*h;
|
|
x[2]=2*h;
|
|
x[3]=3*h;
|
|
y[0]=0*h;
|
|
y[1]=1*h;
|
|
y[2]=2*h;
|
|
y[3]=3*h;
|
|
w[0]=-(1/(x[1]-x[0]));
|
|
w[1]=1*(1/(x[1]-x[0])+1/(x[2]-x[1]));
|
|
w[2]=-(1*(1/(x[2]-x[1])+1/(x[3]-x[2])));
|
|
w[3]=1/(x[3]-x[2]);
|
|
v0=100*CMath::m_minrealnumber;
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x,y,w,4,b1);
|
|
t=CRatInt::BarycentricCalc(b1,v0);
|
|
//--- change values
|
|
d0=0;
|
|
d1=0;
|
|
d2=0;
|
|
//--- function call
|
|
CRatInt::BarycentricDiff1(b1,v0,d0,d1);
|
|
//--- search errors
|
|
bcerrors=bcerrors || MathAbs(t)>v0*(1+threshold);
|
|
bcerrors=bcerrors || MathAbs(d0)>v0*(1+threshold);
|
|
bcerrors=bcerrors || MathAbs(d1-1)>1000*threshold;
|
|
//--- crash test: test safe barycentric formula
|
|
ArrayResize(x,4);
|
|
ArrayResize(y,4);
|
|
ArrayResize(w,4);
|
|
//--- change values
|
|
h=2*CMath::m_minrealnumber;
|
|
x[0]=0*h;
|
|
x[1]=1*h;
|
|
x[2]=2*h;
|
|
x[3]=3*h;
|
|
y[0]=0*h;
|
|
y[1]=1*h;
|
|
y[2]=2*h;
|
|
y[3]=3*h;
|
|
w[0]=-(1/(x[1]-x[0]));
|
|
w[1]=1*(1/(x[1]-x[0])+1/(x[2]-x[1]));
|
|
w[2]=-(1*(1/(x[2]-x[1])+1/(x[3]-x[2])));
|
|
w[3]=1/(x[3]-x[2]);
|
|
v0=CMath::m_minrealnumber;
|
|
//--- function calls
|
|
CRatInt::BarycentricBuildXYW(x,y,w,4,b1);
|
|
t=CRatInt::BarycentricCalc(b1,v0);
|
|
//--- search errors
|
|
bcerrors=bcerrors || MathAbs(t-v0)/v0>threshold;
|
|
//--- Testing "No Poles" interpolation
|
|
maxerr=0;
|
|
for(pass=1;pass<=passcount-1;pass++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
ArrayResize(y,1);
|
|
x[0]=2*CMath::RandomReal()-1;
|
|
y[0]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CRatInt::BarycentricBuildFloaterHormann(x,y,1,1,b1);
|
|
//--- search errors
|
|
maxerr=MathMax(maxerr,MathAbs(CRatInt::BarycentricCalc(b1,2*CMath::RandomReal()-1)-y[0]));
|
|
}
|
|
//--- calculation
|
|
for(n=2;n<=10;n++)
|
|
{
|
|
//--- compare interpolant built by subroutine
|
|
//--- with interpolant built by hands
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
ArrayResize(w2,n);
|
|
//--- D=1,non-equidistant nodes
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Initialize X,Y,W
|
|
a=-1-1*CMath::RandomReal();
|
|
b=1+1*CMath::RandomReal();
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=MathArctan((b-a)*i/(n-1)+a);
|
|
for(i=0;i<=n-1;i++)
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[0]=-(1/(x[1]-x[0]));
|
|
s=1;
|
|
//--- calculation
|
|
for(i=1;i<=n-2;i++)
|
|
{
|
|
w[i]=s*(1/(x[i]-x[i-1])+1/(x[i+1]-x[i]));
|
|
s=-s;
|
|
}
|
|
w[n-1]=s/(x[n-1]-x[n-2]);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
k=CMath::RandomInteger(n);
|
|
//--- check
|
|
if(k!=i)
|
|
{
|
|
t=x[i];
|
|
x[i]=x[k];
|
|
x[k]=t;
|
|
t=y[i];
|
|
y[i]=y[k];
|
|
y[k]=t;
|
|
t=w[i];
|
|
w[i]=w[k];
|
|
w[k]=t;
|
|
}
|
|
}
|
|
//--- Build and test
|
|
CRatInt::BarycentricBuildFloaterHormann(x,y,n,1,b1);
|
|
CRatInt::BarycentricBuildXYW(x,y,w,n,b2);
|
|
//--- search errors
|
|
for(i=1;i<=2*n;i++)
|
|
{
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
maxerr=MathMax(maxerr,MathAbs(CRatInt::BarycentricCalc(b1,t)-CRatInt::BarycentricCalc(b2,t)));
|
|
}
|
|
}
|
|
//--- D=0,1,2. Equidistant nodes.
|
|
for(d=0;d<=2;d++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Skip incorrect (N,D) pairs
|
|
if(n<2*d)
|
|
continue;
|
|
//--- Initialize X,Y,W
|
|
a=-1-1*CMath::RandomReal();
|
|
b=1+1*CMath::RandomReal();
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=(b-a)*i/(n-1)+a;
|
|
for(i=0;i<=n-1;i++)
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
s=1;
|
|
//--- check
|
|
if(d==0)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
w[i]=s;
|
|
s=-s;
|
|
}
|
|
}
|
|
//--- check
|
|
if(d==1)
|
|
{
|
|
w[0]=-s;
|
|
for(i=1;i<=n-2;i++)
|
|
{
|
|
w[i]=2*s;
|
|
s=-s;
|
|
}
|
|
w[n-1]=s;
|
|
}
|
|
//--- check
|
|
if(d==2)
|
|
{
|
|
//--- calculation
|
|
w[0]=s;
|
|
w[1]=-(3*s);
|
|
for(i=2;i<=n-3;i++)
|
|
{
|
|
w[i]=4*s;
|
|
s=-s;
|
|
}
|
|
w[n-2]=3*s;
|
|
w[n-1]=-s;
|
|
}
|
|
//--- Mix
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
k=CMath::RandomInteger(n);
|
|
//--- check
|
|
if(k!=i)
|
|
{
|
|
t=x[i];
|
|
x[i]=x[k];
|
|
x[k]=t;
|
|
t=y[i];
|
|
y[i]=y[k];
|
|
y[k]=t;
|
|
t=w[i];
|
|
w[i]=w[k];
|
|
w[k]=t;
|
|
}
|
|
}
|
|
//--- Build and test
|
|
CRatInt::BarycentricBuildFloaterHormann(x,y,n,d,b1);
|
|
CRatInt::BarycentricBuildXYW(x,y,w,n,b2);
|
|
//--- search errors
|
|
for(i=1;i<=2*n;i++)
|
|
{
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
maxerr=MathMax(maxerr,MathAbs(CRatInt::BarycentricCalc(b1,t)-CRatInt::BarycentricCalc(b2,t)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(maxerr>threshold)
|
|
nperrors=true;
|
|
//--- report
|
|
waserrors=bcerrors || nperrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING RATIONAL INTERPOLATION");
|
|
Print("BASIC BARYCENTRIC FUNCTIONS: ");
|
|
//--- check
|
|
if(bcerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("FLOATER-HORMANN: ");
|
|
//--- check
|
|
if(nperrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- end
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestRatIntUnit::PolDiff2(double &x[],double &cf[],int n,
|
|
double t,double &p,double &dp,
|
|
double &d2p)
|
|
{
|
|
//--- create variables
|
|
int m=0;
|
|
int i=0;
|
|
//--- create arrays
|
|
double df[];
|
|
double d2f[];
|
|
double f[];
|
|
//--- copy
|
|
ArrayCopy(f,cf);
|
|
//--- change values
|
|
p=0;
|
|
dp=0;
|
|
d2p=0;
|
|
n=n-1;
|
|
//--- allocation
|
|
ArrayResize(df,n+1);
|
|
ArrayResize(d2f,n+1);
|
|
for(i=0;i<=n;i++)
|
|
{
|
|
d2f[i]=0;
|
|
df[i]=0;
|
|
}
|
|
//--- calculation
|
|
for(m=1;m<=n;m++)
|
|
{
|
|
for(i=0;i<=n-m;i++)
|
|
{
|
|
d2f[i]=((t-x[i+m])*d2f[i]+(x[i]-t)*d2f[i+1]+2*df[i]-2*df[i+1])/(x[i]-x[i+m]);
|
|
df[i]=((t-x[i+m])*df[i]+f[i]+(x[i]-t)*df[i+1]-f[i+1])/(x[i]-x[i+m]);
|
|
f[i]=((t-x[i+m])*f[i]+(x[i]-t)*f[i+1])/(x[i]-x[i+m]);
|
|
}
|
|
}
|
|
//--- change values
|
|
p=f[0];
|
|
dp=df[0];
|
|
d2p=d2f[0];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestRatIntUnit::BRCunSet(CBarycentricInterpolant &b)
|
|
{
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double w[];
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
ArrayResize(y,1);
|
|
ArrayResize(w,1);
|
|
//--- change values
|
|
x[0]=0;
|
|
y[0]=0;
|
|
w[0]=1;
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x,y,w,1,b);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests whether constant C is solution of 1D LLS problem |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestRatIntUnit::Is1DSolution(const int n,double &y[],
|
|
double &w[],double c)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
double s1=0;
|
|
double s2=0;
|
|
double s3=0;
|
|
double delta=0;
|
|
//--- initialization
|
|
delta=0.001;
|
|
//--- Test result
|
|
s1=0;
|
|
for(i=0;i<=n-1;i++)
|
|
s1=s1+CMath::Sqr(w[i]*(c-y[i]));
|
|
//--- calculation
|
|
s2=0;
|
|
s3=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
s2=s2+CMath::Sqr(w[i]*(c+delta-y[i]));
|
|
s3=s3+CMath::Sqr(w[i]*(c-delta-y[i]));
|
|
}
|
|
result=s2>=s1 && s3>=s1;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CPolInt |
|
|
//+------------------------------------------------------------------+
|
|
class CTestPolIntUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static double InternalPolInt(double &x[],double &cf[],int n,const double t);
|
|
static void BRCunSet(CBarycentricInterpolant &b);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestPolIntUnit(void);
|
|
~CTestPolIntUnit(void);
|
|
//--- public method
|
|
static bool TestPolInt(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestPolIntUnit::CTestPolIntUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestPolIntUnit::~CTestPolIntUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unit test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestPolIntUnit::TestPolInt(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool interrors;
|
|
double threshold=0;
|
|
double a=0;
|
|
double b=0;
|
|
double t=0;
|
|
int i=0;
|
|
int k=0;
|
|
double v=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double v3=0;
|
|
double v4=0;
|
|
double pscale=0;
|
|
double poffset=0;
|
|
int n=0;
|
|
int maxn=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double w[];
|
|
double c[];
|
|
double x2[];
|
|
double y2[];
|
|
double w2[];
|
|
double xfull[];
|
|
double yfull[];
|
|
double xc[];
|
|
double yc[];
|
|
int dc[];
|
|
//--- objects of classes
|
|
CBarycentricInterpolant p;
|
|
CBarycentricInterpolant p1;
|
|
CBarycentricInterpolant p2;
|
|
//--- initialization
|
|
waserrors=false;
|
|
interrors=false;
|
|
maxn=5;
|
|
passcount=20;
|
|
threshold=1.0E8*CMath::m_machineepsilon;
|
|
//--- Test equidistant interpolation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- prepare task:
|
|
//--- * equidistant points
|
|
//--- * random Y
|
|
//--- * T in [A,B] or near (within 10% of its width)
|
|
do
|
|
{
|
|
a=2*CMath::RandomReal()-1;
|
|
b=2*CMath::RandomReal()-1;
|
|
}
|
|
while(MathAbs(a-b)<=0.2);
|
|
//--- change value
|
|
t=a+(1.2*CMath::RandomReal()-0.1)*(b-a);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DEquidist(a,b,n,x,y);
|
|
//--- test "fast" equidistant interpolation (no barycentric model)
|
|
interrors=interrors || MathAbs(CPolInt::PolynomialCalcEqDist(a,b,y,n,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
//--- test "slow" equidistant interpolation (create barycentric model)
|
|
BRCunSet(p);
|
|
CPolInt::PolynomialBuild(x,y,n,p);
|
|
//--- search errors
|
|
interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
//--- test "fast" interpolation (create "fast" barycentric model)
|
|
BRCunSet(p);
|
|
CPolInt::PolynomialBuildEqDist(a,b,y,n,p);
|
|
//--- search errors
|
|
interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
}
|
|
}
|
|
//--- Test Chebyshev-1 interpolation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- prepare task:
|
|
//--- * equidistant points
|
|
//--- * random Y
|
|
//--- * T in [A,B] or near (within 10% of its width)
|
|
do
|
|
{
|
|
a=2*CMath::RandomReal()-1;
|
|
b=2*CMath::RandomReal()-1;
|
|
}
|
|
while(MathAbs(a-b)<=0.2);
|
|
//--- change value
|
|
t=a+(1.2*CMath::RandomReal()-0.1)*(b-a);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DCheb1(a,b,n,x,y);
|
|
//--- test "fast" interpolation (no barycentric model)
|
|
interrors=interrors || MathAbs(CPolInt::PolynomialCalcCheb1(a,b,y,n,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
//--- test "slow" interpolation (create barycentric model)
|
|
BRCunSet(p);
|
|
CPolInt::PolynomialBuild(x,y,n,p);
|
|
//--- search errors
|
|
interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
//--- test "fast" interpolation (create "fast" barycentric model)
|
|
BRCunSet(p);
|
|
CPolInt::PolynomialBuildCheb1(a,b,y,n,p);
|
|
//--- search errors
|
|
interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
}
|
|
}
|
|
//--- Test Chebyshev-2 interpolation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- prepare task:
|
|
//--- * equidistant points
|
|
//--- * random Y
|
|
//--- * T in [A,B] or near (within 10% of its width)
|
|
do
|
|
{
|
|
a=2*CMath::RandomReal()-1;
|
|
b=2*CMath::RandomReal()-1;
|
|
}
|
|
while(MathAbs(a-b)<=0.2);
|
|
//--- change value
|
|
t=a+(1.2*CMath::RandomReal()-0.1)*(b-a);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DCheb2(a,b,n,x,y);
|
|
//--- test "fast" interpolation (no barycentric model)
|
|
interrors=interrors || MathAbs(CPolInt::PolynomialCalcCheb2(a,b,y,n,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
//--- test "slow" interpolation (create barycentric model)
|
|
BRCunSet(p);
|
|
CPolInt::PolynomialBuild(x,y,n,p);
|
|
//--- search errors
|
|
interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
//--- test "fast" interpolation (create "fast" barycentric model)
|
|
BRCunSet(p);
|
|
CPolInt::PolynomialBuildCheb2(a,b,y,n,p);
|
|
//--- search errors
|
|
interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,t)-InternalPolInt(x,y,n,t))>threshold;
|
|
}
|
|
}
|
|
//--- Testing conversion Barycentric<->Chebyshev
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(k=1;k<=3;k++)
|
|
{
|
|
//--- Allocate
|
|
ArrayResize(x,k);
|
|
ArrayResize(y,k);
|
|
//--- Generate problem
|
|
a=2*CMath::RandomReal()-1;
|
|
b=a+(0.1+CMath::RandomReal())*(2*CMath::RandomInteger(2)-1);
|
|
v0=2*CMath::RandomReal()-1;
|
|
v1=2*CMath::RandomReal()-1;
|
|
v2=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(k==1)
|
|
{
|
|
x[0]=0.5*(a+b);
|
|
y[0]=v0;
|
|
}
|
|
//--- check
|
|
if(k==2)
|
|
{
|
|
x[0]=a;
|
|
y[0]=v0-v1;
|
|
x[1]=b;
|
|
y[1]=v0+v1;
|
|
}
|
|
//--- check
|
|
if(k==3)
|
|
{
|
|
x[0]=a;
|
|
y[0]=v0-v1+v2;
|
|
x[1]=0.5*(a+b);
|
|
y[1]=v0-v2;
|
|
x[2]=b;
|
|
y[2]=v0+v1+v2;
|
|
}
|
|
//--- Test forward conversion
|
|
CPolInt::PolynomialBuild(x,y,k,p);
|
|
ArrayResize(c,1);
|
|
CPolInt::PolynomialBar2Cheb(p,a,b,c);
|
|
//--- search errors
|
|
interrors=interrors || CAp::Len(c)!=k;
|
|
//--- check
|
|
if(k>=1)
|
|
interrors=interrors || MathAbs(c[0]-v0)>threshold;
|
|
//--- check
|
|
if(k>=2)
|
|
interrors=interrors || MathAbs(c[1]-v1)>threshold;
|
|
//--- check
|
|
if(k>=3)
|
|
interrors=interrors || MathAbs(c[2]-v2)>threshold;
|
|
//--- Test backward conversion
|
|
CPolInt::PolynomialCheb2Bar(c,k,a,b,p2);
|
|
v=a+CMath::RandomReal()*(b-a);
|
|
//--- search errors
|
|
interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,v)-CRatInt::BarycentricCalc(p2,v))>threshold;
|
|
}
|
|
}
|
|
//--- Testing conversion Barycentric<->Power
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(k=1;k<=5;k++)
|
|
{
|
|
//--- Allocate
|
|
ArrayResize(x,k);
|
|
ArrayResize(y,k);
|
|
//--- Generate problem
|
|
poffset=2*CMath::RandomReal()-1;
|
|
pscale=(0.1+CMath::RandomReal())*(2*CMath::RandomInteger(2)-1);
|
|
v0=2*CMath::RandomReal()-1;
|
|
v1=2*CMath::RandomReal()-1;
|
|
v2=2*CMath::RandomReal()-1;
|
|
v3=2*CMath::RandomReal()-1;
|
|
v4=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(k==1)
|
|
{
|
|
x[0]=poffset;
|
|
y[0]=v0;
|
|
}
|
|
//--- check
|
|
if(k==2)
|
|
{
|
|
x[0]=poffset-pscale;
|
|
y[0]=v0-v1;
|
|
x[1]=poffset+pscale;
|
|
y[1]=v0+v1;
|
|
}
|
|
//--- check
|
|
if(k==3)
|
|
{
|
|
x[0]=poffset-pscale;
|
|
y[0]=v0-v1+v2;
|
|
x[1]=poffset;
|
|
y[1]=v0;
|
|
x[2]=poffset+pscale;
|
|
y[2]=v0+v1+v2;
|
|
}
|
|
//--- check
|
|
if(k==4)
|
|
{
|
|
x[0]=poffset-pscale;
|
|
y[0]=v0-v1+v2-v3;
|
|
x[1]=poffset-0.5*pscale;
|
|
y[1]=v0-0.5*v1+0.25*v2-0.125*v3;
|
|
x[2]=poffset+0.5*pscale;
|
|
y[2]=v0+0.5*v1+0.25*v2+0.125*v3;
|
|
x[3]=poffset+pscale;
|
|
y[3]=v0+v1+v2+v3;
|
|
}
|
|
//--- check
|
|
if(k==5)
|
|
{
|
|
x[0]=poffset-pscale;
|
|
y[0]=v0-v1+v2-v3+v4;
|
|
x[1]=poffset-0.5*pscale;
|
|
y[1]=v0-0.5*v1+0.25*v2-0.125*v3+0.0625*v4;
|
|
x[2]=poffset;
|
|
y[2]=v0;
|
|
x[3]=poffset+0.5*pscale;
|
|
y[3]=v0+0.5*v1+0.25*v2+0.125*v3+0.0625*v4;
|
|
x[4]=poffset+pscale;
|
|
y[4]=v0+v1+v2+v3+v4;
|
|
}
|
|
//--- Test forward conversion
|
|
CPolInt::PolynomialBuild(x,y,k,p);
|
|
ArrayResize(c,1);
|
|
CPolInt::PolynomialBar2Pow(p,poffset,pscale,c);
|
|
//--- search errors
|
|
interrors=interrors || CAp::Len(c)!=k;
|
|
//--- check
|
|
if(k>=1)
|
|
interrors=interrors || MathAbs(c[0]-v0)>threshold;
|
|
//--- check
|
|
if(k>=2)
|
|
interrors=interrors || MathAbs(c[1]-v1)>threshold;
|
|
//--- check
|
|
if(k>=3)
|
|
interrors=interrors || MathAbs(c[2]-v2)>threshold;
|
|
//--- check
|
|
if(k>=4)
|
|
interrors=interrors || MathAbs(c[3]-v3)>threshold;
|
|
//--- check
|
|
if(k>=5)
|
|
interrors=interrors || MathAbs(c[4]-v4)>threshold;
|
|
//--- Test backward conversion
|
|
CPolInt::PolynomialPow2Bar(c,k,poffset,pscale,p2);
|
|
v=poffset+(2*CMath::RandomReal()-1)*pscale;
|
|
//--- search errors
|
|
interrors=interrors || MathAbs(CRatInt::BarycentricCalc(p,v)-CRatInt::BarycentricCalc(p2,v))>threshold;
|
|
}
|
|
}
|
|
//--- crash-test: ability to solve tasks which will overflow/underflow
|
|
//--- weights with straightforward implementation
|
|
for(n=1;n<=20;n++)
|
|
{
|
|
a=-(0.1*CMath::m_maxrealnumber);
|
|
b=0.1*CMath::m_maxrealnumber;
|
|
CApServ::TaskGenInt1DEquidist(a,b,n,x,y);
|
|
CPolInt::PolynomialBuild(x,y,n,p);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
interrors=interrors || p.m_w[i]==0.0;
|
|
}
|
|
//--- report
|
|
waserrors=interrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING POLYNOMIAL INTERPOLATION");
|
|
//--- Normal tests
|
|
Print("INTERPOLATION TEST: ");
|
|
//--- check
|
|
if(interrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- end
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestPolIntUnit::InternalPolInt(double &x[],double &cf[],
|
|
int n,const double t)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- create array
|
|
double f[];
|
|
//--- copy
|
|
ArrayCopy(f,cf);
|
|
//--- calculation
|
|
n=n-1;
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
for(i=j+1;i<=n;i++)
|
|
f[i]=((t-x[j])*f[i]-(t-x[i])*f[j])/(x[i]-x[j]);
|
|
}
|
|
//--- return result
|
|
return(f[n]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestPolIntUnit::BRCunSet(CBarycentricInterpolant &b)
|
|
{
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double w[];
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
ArrayResize(y,1);
|
|
ArrayResize(w,1);
|
|
//--- change values
|
|
x[0]=0;
|
|
y[0]=0;
|
|
w[0]=1;
|
|
//--- function call
|
|
CRatInt::BarycentricBuildXYW(x,y,w,1,b);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSpline1D |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSpline1DUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void LConst(const double a,const double b,CSpline1DInterpolant &c,const double lstep,double &l0,double &l1,double &l2);
|
|
static bool TestUnpack(CSpline1DInterpolant &c,double &x[]);
|
|
static void UnsetSpline1D(CSpline1DInterpolant &c);
|
|
static void Unset1D(double &x[]);
|
|
static bool Is1DSolution(const int n,double &y[],double &w[],const double c);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestSpline1DUnit(void);
|
|
~CTestSpline1DUnit(void);
|
|
//--- public method
|
|
static bool TestSpline1D(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestSpline1DUnit::CTestSpline1DUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestSpline1DUnit::~CTestSpline1DUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSpline1D |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestSpline1DUnit::TestSpline1D(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool crserrors;
|
|
bool cserrors;
|
|
bool hserrors;
|
|
bool aserrors;
|
|
bool lserrors;
|
|
bool dserrors;
|
|
bool uperrors;
|
|
bool cperrors;
|
|
bool lterrors;
|
|
bool ierrors;
|
|
double nonstrictthreshold=0;
|
|
double threshold=0;
|
|
int passcount=0;
|
|
double lstep=0;
|
|
double h=0;
|
|
int maxn=0;
|
|
int bltype=0;
|
|
int brtype=0;
|
|
bool periodiccond;
|
|
int n=0;
|
|
int i=0;
|
|
int k=0;
|
|
int pass=0;
|
|
double a=0;
|
|
double b=0;
|
|
double bl=0;
|
|
double br=0;
|
|
double t=0;
|
|
double sa=0;
|
|
double sb=0;
|
|
int n2=0;
|
|
double v=0;
|
|
double l10=0;
|
|
double l11=0;
|
|
double l12=0;
|
|
double l20=0;
|
|
double l21=0;
|
|
double l22=0;
|
|
double p0=0;
|
|
double p1=0;
|
|
double p2=0;
|
|
double s=0;
|
|
double ds=0;
|
|
double d2s=0;
|
|
double s2=0;
|
|
double ds2=0;
|
|
double d2s2=0;
|
|
double vl=0;
|
|
double vm=0;
|
|
double vr=0;
|
|
double err=0;
|
|
double tension=0;
|
|
double intab=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double yp[];
|
|
double w[];
|
|
double w2[];
|
|
double y2[];
|
|
double d[];
|
|
double xc[];
|
|
double yc[];
|
|
double tmp0[];
|
|
double tmp1[];
|
|
double tmp2[];
|
|
double tmpx[];
|
|
int dc[];
|
|
//--- objects of classes
|
|
CSpline1DInterpolant c;
|
|
CSpline1DInterpolant c2;
|
|
//--- initialization
|
|
waserrors=false;
|
|
passcount=20;
|
|
lstep=0.005;
|
|
h=0.00001;
|
|
maxn=10;
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
nonstrictthreshold=0.00001;
|
|
lserrors=false;
|
|
cserrors=false;
|
|
crserrors=false;
|
|
hserrors=false;
|
|
aserrors=false;
|
|
dserrors=false;
|
|
cperrors=false;
|
|
uperrors=false;
|
|
lterrors=false;
|
|
ierrors=false;
|
|
//--- General test: linear,cubic,Hermite,Akima
|
|
for(n=2;n<=maxn;n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(yp,n);
|
|
ArrayResize(d,n);
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Prepare task:
|
|
//--- * X contains abscissas from [A,B]
|
|
//--- * Y contains function values
|
|
//--- * YP contains periodic function values
|
|
a=-1-CMath::RandomReal();
|
|
b=1+CMath::RandomReal();
|
|
bl=2*CMath::RandomReal()-1;
|
|
br=2*CMath::RandomReal()-1;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=0.5*(b+a)+0.5*(b-a)*MathCos(M_PI*(2*i+1)/(2*n));
|
|
//--- check
|
|
if(i==0)
|
|
x[i]=a;
|
|
//--- check
|
|
if(i==n-1)
|
|
x[i]=b;
|
|
//--- change values
|
|
y[i]=MathCos(1.3*M_PI*x[i]+0.4);
|
|
yp[i]=y[i];
|
|
d[i]=-(1.3*M_PI*MathSin(1.3*M_PI*x[i]+0.4));
|
|
}
|
|
yp[n-1]=yp[0];
|
|
//--- swap
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
k=CMath::RandomInteger(n);
|
|
//--- check
|
|
if(k!=i)
|
|
{
|
|
t=x[i];
|
|
x[i]=x[k];
|
|
x[k]=t;
|
|
t=y[i];
|
|
y[i]=y[k];
|
|
y[k]=t;
|
|
t=yp[i];
|
|
yp[i]=yp[k];
|
|
yp[k]=t;
|
|
t=d[i];
|
|
d[i]=d[k];
|
|
d[k]=t;
|
|
}
|
|
}
|
|
//--- Build linear spline
|
|
//--- Test for general interpolation scheme properties:
|
|
//--- * values at nodes
|
|
//--- * continuous function
|
|
//--- Test for specific properties is implemented below.
|
|
CSpline1D::Spline1DBuildLinear(x,y,n,c);
|
|
//--- search errors
|
|
err=0;
|
|
for(i=0;i<=n-1;i++)
|
|
err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i])));
|
|
//--- search errors
|
|
lserrors=lserrors || err>threshold;
|
|
LConst(a,b,c,lstep,l10,l11,l12);
|
|
LConst(a,b,c,lstep/3,l20,l21,l22);
|
|
//--- search errors
|
|
lserrors=lserrors || l20/l10>1.2;
|
|
//--- Build cubic spline.
|
|
//--- Test for interpolation scheme properties:
|
|
//--- * values at nodes
|
|
//--- * boundary conditions
|
|
//--- * continuous function
|
|
//--- * continuous first derivative
|
|
//--- * continuous second derivative
|
|
//--- * periodicity properties
|
|
//--- * Spline1DGridDiff(),Spline1DGridDiff2() and Spline1DDiff()
|
|
//--- calls must return same results
|
|
for(bltype=-1;bltype<=2;bltype++)
|
|
{
|
|
for(brtype=-1;brtype<=2;brtype++)
|
|
{
|
|
//--- skip meaningless combination of boundary conditions
|
|
//--- (one condition is periodic,another is not)
|
|
periodiccond=bltype==-1 || brtype==-1;
|
|
//--- check
|
|
if(periodiccond && bltype!=brtype)
|
|
continue;
|
|
//--- build
|
|
if(periodiccond)
|
|
CSpline1D::Spline1DBuildCubic(x,yp,n,bltype,bl,brtype,br,c);
|
|
else
|
|
CSpline1D::Spline1DBuildCubic(x,y,n,bltype,bl,brtype,br,c);
|
|
//--- interpolation properties
|
|
err=0;
|
|
//--- check
|
|
if(periodiccond)
|
|
{
|
|
//--- * check values at nodes;spline is periodic so
|
|
//--- we add random number of periods to nodes
|
|
//--- * we also test for periodicity of derivatives
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v=x[i];
|
|
vm=v+(b-a)*(CMath::RandomInteger(5)-2);
|
|
t=yp[i]-CSpline1D::Spline1DCalc(c,vm);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(t));
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,v,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,vm,s2,ds2,d2s2);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-s2));
|
|
err=MathMax(err,MathAbs(ds-ds2));
|
|
err=MathMax(err,MathAbs(d2s-d2s2));
|
|
}
|
|
//--- periodicity between nodes
|
|
v=a+(b-a)*CMath::RandomReal();
|
|
vm=v+(b-a)*(CMath::RandomInteger(5)-2);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,v)-CSpline1D::Spline1DCalc(c,vm)));
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,v,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,vm,s2,ds2,d2s2);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-s2));
|
|
err=MathMax(err,MathAbs(ds-ds2));
|
|
err=MathMax(err,MathAbs(d2s-d2s2));
|
|
}
|
|
else
|
|
{
|
|
//--- * check values at nodes
|
|
for(i=0;i<=n-1;i++)
|
|
err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i])));
|
|
}
|
|
//--- search errors
|
|
cserrors=cserrors || err>threshold;
|
|
//--- check boundary conditions
|
|
err=0;
|
|
//--- check
|
|
if(bltype==0)
|
|
{
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,a-h,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,a+h,s2,ds2,d2s2);
|
|
t=(d2s2-d2s)/(2*h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(t));
|
|
}
|
|
//--- check
|
|
if(bltype==1)
|
|
{
|
|
t=(CSpline1D::Spline1DCalc(c,a+h)-CSpline1D::Spline1DCalc(c,a-h))/(2*h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(bl-t));
|
|
}
|
|
//--- check
|
|
if(bltype==2)
|
|
{
|
|
t=(CSpline1D::Spline1DCalc(c,a+h)-2*CSpline1D::Spline1DCalc(c,a)+CSpline1D::Spline1DCalc(c,a-h))/CMath::Sqr(h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(bl-t));
|
|
}
|
|
//--- check
|
|
if(brtype==0)
|
|
{
|
|
CSpline1D::Spline1DDiff(c,b-h,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,b+h,s2,ds2,d2s2);
|
|
t=(d2s2-d2s)/(2*h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(t));
|
|
}
|
|
//--- check
|
|
if(brtype==1)
|
|
{
|
|
t=(CSpline1D::Spline1DCalc(c,b+h)-CSpline1D::Spline1DCalc(c,b-h))/(2*h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(br-t));
|
|
}
|
|
//--- check
|
|
if(brtype==2)
|
|
{
|
|
t=(CSpline1D::Spline1DCalc(c,b+h)-2*CSpline1D::Spline1DCalc(c,b)+CSpline1D::Spline1DCalc(c,b-h))/CMath::Sqr(h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(br-t));
|
|
}
|
|
//--- check
|
|
if(bltype==-1 || brtype==-1)
|
|
{
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,a+100*CMath::m_machineepsilon,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,b-100*CMath::m_machineepsilon,s2,ds2,d2s2);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-s2));
|
|
err=MathMax(err,MathAbs(ds-ds2));
|
|
err=MathMax(err,MathAbs(d2s-d2s2));
|
|
}
|
|
//--- search errors
|
|
cserrors=cserrors || err>1.0E-3;
|
|
//--- Check Lipschitz continuity
|
|
LConst(a,b,c,lstep,l10,l11,l12);
|
|
LConst(a,b,c,lstep/3,l20,l21,l22);
|
|
//--- check
|
|
if(l10>1.0E-6)
|
|
cserrors=cserrors || l20/l10>1.2;
|
|
//--- check
|
|
if(l11>1.0E-6)
|
|
cserrors=cserrors || l21/l11>1.2;
|
|
//--- check
|
|
if(l12>1.0E-6)
|
|
cserrors=cserrors || l22/l12>1.2;
|
|
//--- compare spline1dgriddiff() and spline1ddiff() results
|
|
err=0;
|
|
//--- check
|
|
if(periodiccond)
|
|
CSpline1D::Spline1DGridDiffCubic(x,yp,n,bltype,bl,brtype,br,tmp1);
|
|
else
|
|
CSpline1D::Spline1DGridDiffCubic(x,y,n,bltype,bl,brtype,br,tmp1);
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(tmp1)>=n))
|
|
return(false);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
CSpline1D::Spline1DDiff(c,x[i],s,ds,d2s);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(ds-tmp1[i]));
|
|
}
|
|
//--- check
|
|
if(periodiccond)
|
|
CSpline1D::Spline1DGridDiff2Cubic(x,yp,n,bltype,bl,brtype,br,tmp1,tmp2);
|
|
else
|
|
CSpline1D::Spline1DGridDiff2Cubic(x,y,n,bltype,bl,brtype,br,tmp1,tmp2);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
CSpline1D::Spline1DDiff(c,x[i],s,ds,d2s);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(ds-tmp1[i]));
|
|
err=MathMax(err,MathAbs(d2s-tmp2[i]));
|
|
}
|
|
//--- search errors
|
|
cserrors=cserrors || err>threshold;
|
|
//--- compare spline1dconv()/convdiff()/convdiff2() and spline1ddiff() results
|
|
n2=2+CMath::RandomInteger(2*n);
|
|
ArrayResize(tmpx,n2);
|
|
//--- calculation
|
|
for(i=0;i<=n2-1;i++)
|
|
tmpx[i]=0.5*(a+b)+(a-b)*(2*CMath::RandomReal()-1);
|
|
err=0;
|
|
//--- check
|
|
if(periodiccond)
|
|
CSpline1D::Spline1DConvCubic(x,yp,n,bltype,bl,brtype,br,tmpx,n2,tmp0);
|
|
else
|
|
CSpline1D::Spline1DConvCubic(x,y,n,bltype,bl,brtype,br,tmpx,n2,tmp0);
|
|
for(i=0;i<=n2-1;i++)
|
|
{
|
|
CSpline1D::Spline1DDiff(c,tmpx[i],s,ds,d2s);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-tmp0[i]));
|
|
}
|
|
//--- check
|
|
if(periodiccond)
|
|
CSpline1D::Spline1DConvDiffCubic(x,yp,n,bltype,bl,brtype,br,tmpx,n2,tmp0,tmp1);
|
|
else
|
|
CSpline1D::Spline1DConvDiffCubic(x,y,n,bltype,bl,brtype,br,tmpx,n2,tmp0,tmp1);
|
|
for(i=0;i<=n2-1;i++)
|
|
{
|
|
CSpline1D::Spline1DDiff(c,tmpx[i],s,ds,d2s);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-tmp0[i]));
|
|
err=MathMax(err,MathAbs(ds-tmp1[i]));
|
|
}
|
|
//--- check
|
|
if(periodiccond)
|
|
CSpline1D::Spline1DConvDiff2Cubic(x,yp,n,bltype,bl,brtype,br,tmpx,n2,tmp0,tmp1,tmp2);
|
|
else
|
|
CSpline1D::Spline1DConvDiff2Cubic(x,y,n,bltype,bl,brtype,br,tmpx,n2,tmp0,tmp1,tmp2);
|
|
for(i=0;i<=n2-1;i++)
|
|
{
|
|
CSpline1D::Spline1DDiff(c,tmpx[i],s,ds,d2s);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-tmp0[i]));
|
|
err=MathMax(err,MathAbs(ds-tmp1[i]));
|
|
err=MathMax(err,MathAbs(d2s-tmp2[i]));
|
|
}
|
|
//--- search errors
|
|
cserrors=cserrors || err>threshold;
|
|
}
|
|
}
|
|
//--- Build Catmull-Rom spline.
|
|
//--- Test for interpolation scheme properties:
|
|
//--- * values at nodes
|
|
//--- * boundary conditions
|
|
//--- * continuous function
|
|
//--- * continuous first derivative
|
|
//--- * periodicity properties
|
|
for(bltype=-1;bltype<=0;bltype++)
|
|
{
|
|
periodiccond=bltype==-1;
|
|
//--- select random tension value,then build
|
|
if(CMath::RandomReal()>0.5)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>0.5)
|
|
tension=0;
|
|
else
|
|
tension=1;
|
|
}
|
|
else
|
|
tension=CMath::RandomReal();
|
|
//--- check
|
|
if(periodiccond)
|
|
CSpline1D::Spline1DBuildCatmullRom(x,yp,n,bltype,tension,c);
|
|
else
|
|
CSpline1D::Spline1DBuildCatmullRom(x,y,n,bltype,tension,c);
|
|
//--- interpolation properties
|
|
err=0;
|
|
//--- check
|
|
if(periodiccond)
|
|
{
|
|
//--- * check values at nodes;spline is periodic so
|
|
//--- we add random number of periods to nodes
|
|
//--- * we also test for periodicity of first derivative
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v=x[i];
|
|
vm=v+(b-a)*(CMath::RandomInteger(5)-2);
|
|
t=yp[i]-CSpline1D::Spline1DCalc(c,vm);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(t));
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,v,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,vm,s2,ds2,d2s2);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-s2));
|
|
err=MathMax(err,MathAbs(ds-ds2));
|
|
}
|
|
//--- periodicity between nodes
|
|
v=a+(b-a)*CMath::RandomReal();
|
|
vm=v+(b-a)*(CMath::RandomInteger(5)-2);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,v)-CSpline1D::Spline1DCalc(c,vm)));
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,v,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,vm,s2,ds2,d2s2);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-s2));
|
|
err=MathMax(err,MathAbs(ds-ds2));
|
|
}
|
|
else
|
|
{
|
|
//--- * check values at nodes
|
|
for(i=0;i<=n-1;i++)
|
|
err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i])));
|
|
}
|
|
//--- search errors
|
|
crserrors=crserrors || err>threshold;
|
|
//--- check boundary conditions
|
|
err=0;
|
|
//--- check
|
|
if(bltype==0)
|
|
{
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,a-h,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,a+h,s2,ds2,d2s2);
|
|
t=(d2s2-d2s)/(2*h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(t));
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,b-h,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,b+h,s2,ds2,d2s2);
|
|
t=(d2s2-d2s)/(2*h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(t));
|
|
}
|
|
//--- check
|
|
if(bltype==-1)
|
|
{
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,a+100*CMath::m_machineepsilon,s,ds,d2s);
|
|
CSpline1D::Spline1DDiff(c,b-100*CMath::m_machineepsilon,s2,ds2,d2s2);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-s2));
|
|
err=MathMax(err,MathAbs(ds-ds2));
|
|
}
|
|
//--- search errors
|
|
crserrors=crserrors || err>1.0E-3;
|
|
//--- Check Lipschitz continuity
|
|
LConst(a,b,c,lstep,l10,l11,l12);
|
|
LConst(a,b,c,lstep/3,l20,l21,l22);
|
|
//--- check
|
|
if(l10>1.0E-6)
|
|
crserrors=crserrors || l20/l10>1.2;
|
|
//--- check
|
|
if(l11>1.0E-6)
|
|
crserrors=crserrors || l21/l11>1.2;
|
|
}
|
|
//--- Build Hermite spline.
|
|
//--- Test for interpolation scheme properties:
|
|
//--- * values and derivatives at nodes
|
|
//--- * continuous function
|
|
//--- * continuous first derivative
|
|
CSpline1D::Spline1DBuildHermite(x,y,d,n,c);
|
|
//--- search errors
|
|
err=0;
|
|
for(i=0;i<=n-1;i++)
|
|
err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i])));
|
|
//--- search errors
|
|
hserrors=hserrors || err>threshold;
|
|
err=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
t=(CSpline1D::Spline1DCalc(c,x[i]+h)-CSpline1D::Spline1DCalc(c,x[i]-h))/(2*h);
|
|
err=MathMax(err,MathAbs(d[i]-t));
|
|
}
|
|
//--- search errors
|
|
hserrors=hserrors || err>1.0E-3;
|
|
LConst(a,b,c,lstep,l10,l11,l12);
|
|
LConst(a,b,c,lstep/3,l20,l21,l22);
|
|
//--- search errors
|
|
hserrors=hserrors || l20/l10>1.2;
|
|
hserrors=hserrors || l21/l11>1.2;
|
|
//--- Build Akima spline
|
|
//--- Test for general interpolation scheme properties:
|
|
//--- * values at nodes
|
|
//--- * continuous function
|
|
//--- * continuous first derivative
|
|
//--- Test for specific properties is implemented below.
|
|
if(n>=5)
|
|
{
|
|
CSpline1D::Spline1DBuildAkima(x,y,n,c);
|
|
//--- search errors
|
|
err=0;
|
|
for(i=0;i<=n-1;i++)
|
|
err=MathMax(err,MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i])));
|
|
//--- search errors
|
|
aserrors=aserrors || err>threshold;
|
|
LConst(a,b,c,lstep,l10,l11,l12);
|
|
LConst(a,b,c,lstep/3,l20,l21,l22);
|
|
//--- search errors
|
|
hserrors=hserrors || l20/l10>1.2;
|
|
hserrors=hserrors || l21/l11>1.2;
|
|
}
|
|
}
|
|
}
|
|
//--- Special linear spline test:
|
|
//--- test for linearity between x[i] and x[i+1]
|
|
for(n=2;n<=maxn;n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
//--- Prepare task
|
|
a=-1;
|
|
b=1;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=a+(b-a)*i/(n-1);
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildLinear(x,y,n,c);
|
|
//--- Test
|
|
err=0;
|
|
for(k=0;k<=n-2;k++)
|
|
{
|
|
a=x[k];
|
|
b=x[k+1];
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
v=y[k]+(t-a)/(b-a)*(y[k+1]-y[k]);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-v));
|
|
}
|
|
}
|
|
//--- search errors
|
|
lserrors=lserrors || err>threshold;
|
|
}
|
|
//--- Special Akima test: test outlier sensitivity
|
|
//--- Spline value at (x[i],x[i+1]) should depend from
|
|
//--- f[i-2],f[i-1],f[i],f[i+1],f[i+2],f[i+3] only.
|
|
for(n=5;n<=maxn;n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(y2,n);
|
|
//--- Prepare unperturbed Akima spline
|
|
a=-1;
|
|
b=1;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=a+(b-a)*i/(n-1);
|
|
y[i]=MathCos(1.3*M_PI*x[i]+0.4);
|
|
}
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildAkima(x,y,n,c);
|
|
//--- Process perturbed tasks
|
|
err=0;
|
|
for(k=0;k<=n-1;k++)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
y2[i_]=y[i_];
|
|
y2[k]=5;
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildAkima(x,y2,n,c2);
|
|
//--- Test left part independence
|
|
if(k-3>=1)
|
|
{
|
|
a=-1;
|
|
b=x[k-3];
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-CSpline1D::Spline1DCalc(c2,t)));
|
|
}
|
|
}
|
|
//--- Test right part independence
|
|
if(k+3<=n-2)
|
|
{
|
|
a=x[k+3];
|
|
b=1;
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-CSpline1D::Spline1DCalc(c2,t)));
|
|
}
|
|
}
|
|
}
|
|
//--- search errors
|
|
aserrors=aserrors || err>threshold;
|
|
}
|
|
//--- Differentiation,copy/unpack test
|
|
for(n=2;n<=maxn;n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
//--- Prepare cubic spline
|
|
a=-1-CMath::RandomReal();
|
|
b=1+CMath::RandomReal();
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=a+(b-a)*i/(n-1);
|
|
y[i]=MathCos(1.3*M_PI*x[i]+0.4);
|
|
}
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(x,y,n,2,0.0,2,0.0,c);
|
|
//--- Test diff
|
|
err=0;
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
//--- function calls
|
|
CSpline1D::Spline1DDiff(c,t,s,ds,d2s);
|
|
vl=CSpline1D::Spline1DCalc(c,t-h);
|
|
vm=CSpline1D::Spline1DCalc(c,t);
|
|
vr=CSpline1D::Spline1DCalc(c,t+h);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(s-vm));
|
|
err=MathMax(err,MathAbs(ds-(vr-vl)/(2*h)));
|
|
err=MathMax(err,MathAbs(d2s-(vr-2*vm+vl)/CMath::Sqr(h)));
|
|
}
|
|
//--- search errors
|
|
dserrors=dserrors || err>0.001;
|
|
//--- Test copy
|
|
UnsetSpline1D(c2);
|
|
CSpline1D::Spline1DCopy(c,c2);
|
|
//--- search errors
|
|
err=0;
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-CSpline1D::Spline1DCalc(c2,t)));
|
|
}
|
|
//--- search errors
|
|
cperrors=cperrors || err>threshold;
|
|
//--- Test unpack
|
|
uperrors=uperrors || !TestUnpack(c,x);
|
|
//--- Test lin.trans.
|
|
err=0;
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- LinTransX,general A
|
|
sa=4*CMath::RandomReal()-2;
|
|
sb=2*CMath::RandomReal()-1;
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
//--- function calls
|
|
CSpline1D::Spline1DCopy(c,c2);
|
|
CSpline1D::Spline1DLinTransX(c2,sa,sb);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,t)-CSpline1D::Spline1DCalc(c2,(t-sb)/sa)));
|
|
//--- LinTransX,special case: A=0
|
|
sb=2*CMath::RandomReal()-1;
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
//--- function calls
|
|
CSpline1D::Spline1DCopy(c,c2);
|
|
CSpline1D::Spline1DLinTransX(c2,0,sb);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline1D::Spline1DCalc(c,sb)-CSpline1D::Spline1DCalc(c2,t)));
|
|
//--- LinTransY
|
|
sa=2*CMath::RandomReal()-1;
|
|
sb=2*CMath::RandomReal()-1;
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
//--- function calls
|
|
CSpline1D::Spline1DCopy(c,c2);
|
|
CSpline1D::Spline1DLinTransY(c2,sa,sb);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(sa*CSpline1D::Spline1DCalc(c,t)+sb-CSpline1D::Spline1DCalc(c2,t)));
|
|
}
|
|
//--- search errors
|
|
lterrors=lterrors || err>threshold;
|
|
}
|
|
//--- Testing integration.
|
|
//--- Three tests are performed:
|
|
//--- * approximate test (well behaved smooth function,many points,
|
|
//--- integration inside [a,b]),non-periodic spline
|
|
//--- * exact test (integration of parabola,outside of [a,b],non-periodic spline
|
|
//--- * approximate test for periodic splines. F(x)=cos(2*pi*x)+1.
|
|
//--- Period length is equals to 1.0,so all operations with
|
|
//--- multiples of period are done exactly. For each value of PERIOD
|
|
//--- we calculate and test integral at four points:
|
|
//--- - 0 < t0 < PERIOD
|
|
//--- - t1=PERIOD-eps
|
|
//--- - t2=PERIOD
|
|
//--- - t3=PERIOD+eps
|
|
err=0;
|
|
for(n=20;n<=35;n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Prepare cubic spline
|
|
a=-1-0.2*CMath::RandomReal();
|
|
b=1+0.2*CMath::RandomReal();
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=a+(b-a)*i/(n-1);
|
|
y[i]=MathSin(M_PI*x[i]+0.4)+MathExp(x[i]);
|
|
}
|
|
//--- change values
|
|
bl=M_PI*MathCos(M_PI*a+0.4)+MathExp(a);
|
|
br=M_PI*MathCos(M_PI*b+0.4)+MathExp(b);
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(x,y,n,1,bl,1,br,c);
|
|
//--- Test
|
|
t=a+(b-a)*CMath::RandomReal();
|
|
v=-(MathCos(M_PI*a+0.4)/M_PI)+MathExp(a);
|
|
v=-(MathCos(M_PI*t+0.4)/M_PI)+MathExp(t)-v;
|
|
v=v-CSpline1D::Spline1DIntegrate(c,t);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v));
|
|
}
|
|
}
|
|
//--- search errors
|
|
ierrors=ierrors || err>0.001;
|
|
p0=2*CMath::RandomReal()-1;
|
|
p1=2*CMath::RandomReal()-1;
|
|
p2=2*CMath::RandomReal()-1;
|
|
a=-CMath::RandomReal()-0.5;
|
|
b=CMath::RandomReal()+0.5;
|
|
n=2;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(d,n);
|
|
x[0]=a;
|
|
y[0]=p0+p1*a+p2*CMath::Sqr(a);
|
|
d[0]=p1+2*p2*a;
|
|
x[1]=b;
|
|
y[1]=p0+p1*b+p2*CMath::Sqr(b);
|
|
d[1]=p1+2*p2*b;
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildHermite(x,y,d,n,c);
|
|
bl=MathMin(a,b)-MathAbs(b-a);
|
|
br=MathMin(a,b)+MathAbs(b-a);
|
|
err=0;
|
|
//--- calculation
|
|
for(pass=1;pass<=100;pass++)
|
|
{
|
|
t=bl+(br-bl)*CMath::RandomReal();
|
|
v=p0*t+p1*CMath::Sqr(t)/2+p2*CMath::Sqr(t)*t/3-(p0*a+p1*CMath::Sqr(a)/2+p2*CMath::Sqr(a)*a/3);
|
|
v=v-CSpline1D::Spline1DIntegrate(c,t);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v));
|
|
}
|
|
//--- search errors
|
|
ierrors=ierrors || err>threshold;
|
|
n=100;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=(double)i/(double)(n-1);
|
|
y[i]=MathCos(2*M_PI*x[i])+1;
|
|
}
|
|
//--- change values
|
|
y[0]=2;
|
|
y[n-1]=2;
|
|
//--- function calls
|
|
CSpline1D::Spline1DBuildCubic(x,y,n,-1,0.0,-1,0.0,c);
|
|
intab=CSpline1D::Spline1DIntegrate(c,1.0);
|
|
v=CMath::RandomReal();
|
|
vr=CSpline1D::Spline1DIntegrate(c,v);
|
|
//--- search errors
|
|
ierrors=ierrors || MathAbs(intab-1)>0.001;
|
|
for(i=-10;i<=10;i++)
|
|
{
|
|
ierrors=ierrors || MathAbs(CSpline1D::Spline1DIntegrate(c,i+v)-(i*intab+vr))>0.001;
|
|
ierrors=ierrors || MathAbs(CSpline1D::Spline1DIntegrate(c,i-1000*CMath::m_machineepsilon)-i*intab)>0.001;
|
|
ierrors=ierrors || MathAbs(CSpline1D::Spline1DIntegrate(c,i)-i*intab)>0.001;
|
|
ierrors=ierrors || MathAbs(CSpline1D::Spline1DIntegrate(c,i+1000*CMath::m_machineepsilon)-i*intab)>0.001;
|
|
}
|
|
//--- report
|
|
waserrors=((((((((lserrors || cserrors) || crserrors) || hserrors) || aserrors) || dserrors) || cperrors) || uperrors) || lterrors) || ierrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING SPLINE INTERPOLATION");
|
|
//--- Normal tests
|
|
Print("LINEAR SPLINE TEST: ");
|
|
//--- check
|
|
if(lserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("CUBIC SPLINE TEST: ");
|
|
//--- check
|
|
if(cserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("CATMULL-ROM SPLINE TEST: ");
|
|
//--- check
|
|
if(crserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("HERMITE SPLINE TEST: ");
|
|
//--- check
|
|
if(hserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("AKIMA SPLINE TEST: ");
|
|
//--- check
|
|
if(aserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("DIFFERENTIATION TEST: ");
|
|
//--- check
|
|
if(dserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("COPY/SERIALIZATION TEST: ");
|
|
//--- check
|
|
if(cperrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("UNPACK TEST: ");
|
|
//--- check
|
|
if(uperrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("LIN.TRANS. TEST: ");
|
|
//--- check
|
|
if(lterrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("INTEGRATION TEST: ");
|
|
//--- check
|
|
if(ierrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- end
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Lipschitz constants for spline inself,first and second |
|
|
//| derivatives. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestSpline1DUnit::LConst(const double a,const double b,
|
|
CSpline1DInterpolant &c,
|
|
const double lstep,double &l0,
|
|
double &l1,double &l2)
|
|
{
|
|
//--- create variables
|
|
double t=0;
|
|
double vl=0;
|
|
double vm=0;
|
|
double vr=0;
|
|
double prevf=0;
|
|
double prevd=0;
|
|
double prevd2=0;
|
|
double f=0;
|
|
double d=0;
|
|
double d2=0;
|
|
//--- change values
|
|
l0=0;
|
|
l1=0;
|
|
l2=0;
|
|
t=a-0.1;
|
|
vl=CSpline1D::Spline1DCalc(c,t-2*lstep);
|
|
vm=CSpline1D::Spline1DCalc(c,t-lstep);
|
|
vr=CSpline1D::Spline1DCalc(c,t);
|
|
f=vm;
|
|
d=(vr-vl)/(2*lstep);
|
|
d2=(vr-2*vm+vl)/CMath::Sqr(lstep);
|
|
//--- calculation
|
|
while(t<=b+0.1)
|
|
{
|
|
//--- change values
|
|
prevf=f;
|
|
prevd=d;
|
|
prevd2=d2;
|
|
vl=vm;
|
|
vm=vr;
|
|
vr=CSpline1D::Spline1DCalc(c,t+lstep);
|
|
f=vm;
|
|
d=(vr-vl)/(2*lstep);
|
|
d2=(vr-2*vm+vl)/CMath::Sqr(lstep);
|
|
l0=MathMax(l0,MathAbs((f-prevf)/lstep));
|
|
l1=MathMax(l1,MathAbs((d-prevd)/lstep));
|
|
l2=MathMax(l2,MathAbs((d2-prevd2)/lstep));
|
|
t=t+lstep;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpack testing |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestSpline1DUnit::TestUnpack(CSpline1DInterpolant &c,double &x[])
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int n=0;
|
|
double err=0;
|
|
double t=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
//--- create matrix
|
|
CMatrixDouble tbl;
|
|
//--- initialization
|
|
passcount=20;
|
|
err=0;
|
|
//--- function call
|
|
CSpline1D::Spline1DUnpack(c,n,tbl);
|
|
//--- calculation
|
|
for(i=0;i<=n-2;i++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
t=CMath::RandomReal()*(tbl[i][1]-tbl[i][0]);
|
|
v1=tbl[i][2]+t*tbl[i][3]+CMath::Sqr(t)*tbl[i][4]+t*CMath::Sqr(t)*tbl[i][5];
|
|
v2=CSpline1D::Spline1DCalc(c,tbl[i][0]+t);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v1-v2));
|
|
}
|
|
}
|
|
//--- search errors
|
|
for(i=0;i<=n-2;i++)
|
|
err=MathMax(err,MathAbs(x[i]-tbl[i][0]));
|
|
//--- search errors
|
|
for(i=0;i<=n-2;i++)
|
|
err=MathMax(err,MathAbs(x[i+1]-tbl[i][1]));
|
|
//--- get result
|
|
result=err<100*CMath::m_machineepsilon;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unset spline,i.e. initialize it with random garbage |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestSpline1DUnit::UnsetSpline1D(CSpline1DInterpolant &c)
|
|
{
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double d[];
|
|
//--- allocation
|
|
ArrayResize(x,2);
|
|
ArrayResize(y,2);
|
|
ArrayResize(d,2);
|
|
//--- change values
|
|
x[0]=-1;
|
|
y[0]=CMath::RandomReal();
|
|
d[0]=CMath::RandomReal();
|
|
x[1]=1;
|
|
y[1]=CMath::RandomReal();
|
|
d[1]=CMath::RandomReal();
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildHermite(x,y,d,2,c);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real vector |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestSpline1DUnit::Unset1D(double &x[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
//--- change value
|
|
x[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests whether constant C is solution of 1D LLS problem |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestSpline1DUnit::Is1DSolution(const int n,double &y[],
|
|
double &w[],const double c)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
double s1=0;
|
|
double s2=0;
|
|
double s3=0;
|
|
double delta=0;
|
|
//--- initialization
|
|
delta=0.001;
|
|
//--- Test result
|
|
s1=0;
|
|
for(i=0;i<=n-1;i++)
|
|
s1=s1+CMath::Sqr(w[i]*(c-y[i]));
|
|
s2=0;
|
|
s3=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
s2=s2+CMath::Sqr(w[i]*(c+delta-y[i]));
|
|
s3=s3+CMath::Sqr(w[i]*(c-delta-y[i]));
|
|
result=s2>=s1 && s3>=s1;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMinLM |
|
|
//+------------------------------------------------------------------+
|
|
class CTestMinLMUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestMinLMUnit(void);
|
|
~CTestMinLMUnit(void);
|
|
//--- public methods
|
|
static bool TestMinLM(const bool silent);
|
|
static bool RKindVSStateCheck(const int rkind,CMinLMState &state);
|
|
static void AXMB(CMinLMState &state,CMatrixDouble &a,double &b[],const int n);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestMinLMUnit::CTestMinLMUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestMinLMUnit::~CTestMinLMUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CMinLM |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMinLMUnit::TestMinLM(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool referror;
|
|
bool lin1error;
|
|
bool lin2error;
|
|
bool eqerror;
|
|
bool converror;
|
|
bool scerror;
|
|
bool restartserror;
|
|
bool othererrors;
|
|
int rkind=0;
|
|
int ckind=0;
|
|
int tmpkind=0;
|
|
double epsf=0;
|
|
double epsx=0;
|
|
double epsg=0;
|
|
int maxits=0;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double s=0;
|
|
double stpmax=0;
|
|
double h=0;
|
|
double fprev=0;
|
|
double xprev=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double xe[];
|
|
double b[];
|
|
double bl[];
|
|
double bu[];
|
|
double xlast[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- objects of classes
|
|
CMinLMState state;
|
|
CMinLMReport rep;
|
|
//--- initialization
|
|
waserrors=false;
|
|
referror=false;
|
|
lin1error=false;
|
|
lin2error=false;
|
|
eqerror=false;
|
|
converror=false;
|
|
scerror=false;
|
|
othererrors=false;
|
|
restartserror=false;
|
|
//--- Reference problem.
|
|
//--- See comments for RKindVsStateCheck() for more info about RKind.
|
|
//--- NOTES: we also test negative RKind's corresponding to "inexact" schemes
|
|
//--- which use approximate finite difference Jacobian.
|
|
ArrayResize(x,3);
|
|
n=3;
|
|
m=3;
|
|
h=0.0001;
|
|
//--- calculation
|
|
for(rkind=-2;rkind<=5;rkind++)
|
|
{
|
|
//--- change values
|
|
x[0]=100*CMath::RandomReal()-50;
|
|
x[1]=100*CMath::RandomReal()-50;
|
|
x[2]=100*CMath::RandomReal()-50;
|
|
//--- check
|
|
if(rkind==-2)
|
|
{
|
|
CMinLM::MinLMCreateV(n,m,x,h,state);
|
|
CMinLM::MinLMSetAccType(state,1);
|
|
}
|
|
//--- check
|
|
if(rkind==-1)
|
|
{
|
|
CMinLM::MinLMCreateV(n,m,x,h,state);
|
|
CMinLM::MinLMSetAccType(state,0);
|
|
}
|
|
//--- check
|
|
if(rkind==0)
|
|
CMinLM::MinLMCreateFJ(n,m,x,state);
|
|
//--- check
|
|
if(rkind==1)
|
|
CMinLM::MinLMCreateFGJ(n,m,x,state);
|
|
//--- check
|
|
if(rkind==2)
|
|
CMinLM::MinLMCreateFGH(n,x,state);
|
|
//--- check
|
|
if(rkind==3)
|
|
{
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,0);
|
|
}
|
|
//--- check
|
|
if(rkind==4)
|
|
{
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,1);
|
|
}
|
|
//--- check
|
|
if(rkind==5)
|
|
{
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,2);
|
|
}
|
|
//--- cycle
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
//--- (x-2)^2 + y^2 + (z-x)^2
|
|
if(state.m_needfi)
|
|
{
|
|
state.m_fi[0]=state.m_x[0]-2;
|
|
state.m_fi[1]=state.m_x[1];
|
|
state.m_fi[2]=state.m_x[2]-state.m_x[0];
|
|
}
|
|
//--- check
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi[0]=state.m_x[0]-2;
|
|
state.m_fi[1]=state.m_x[1];
|
|
state.m_fi[2]=state.m_x[2]-state.m_x[0];
|
|
state.m_j[0].Set(0,1);
|
|
state.m_j[0].Set(1,0);
|
|
state.m_j[0].Set(2,0);
|
|
state.m_j[1].Set(0,0);
|
|
state.m_j[1].Set(1,1);
|
|
state.m_j[1].Set(2,0);
|
|
state.m_j[2].Set(0,-1);
|
|
state.m_j[2].Set(1,0);
|
|
state.m_j[2].Set(2,1);
|
|
}
|
|
//--- check
|
|
if((state.m_needf || state.m_needfg) || state.m_needfgh)
|
|
state.m_f=CMath::Sqr(state.m_x[0]-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg || state.m_needfgh)
|
|
{
|
|
state.m_g[0]=2*(state.m_x[0]-2)+2*(state.m_x[0]-state.m_x[2]);
|
|
state.m_g[1]=2*state.m_x[1];
|
|
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
|
|
}
|
|
//--- check
|
|
if(state.m_needfgh)
|
|
{
|
|
state.m_h[0].Set(0,4);
|
|
state.m_h[0].Set(1,0);
|
|
state.m_h[0].Set(2,-2);
|
|
state.m_h[1].Set(0,0);
|
|
state.m_h[1].Set(1,2);
|
|
state.m_h[1].Set(2,0);
|
|
state.m_h[2].Set(0,-2);
|
|
state.m_h[2].Set(1,0);
|
|
state.m_h[2].Set(2,2);
|
|
}
|
|
//--- search errors
|
|
scerror=scerror || !RKindVSStateCheck(rkind,state);
|
|
}
|
|
//--- function call
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
//--- search errors
|
|
referror=(((referror || rep.m_terminationtype<=0) || MathAbs(x[0]-2)>0.001) || MathAbs(x[1])>0.001) || MathAbs(x[2]-2)>0.001;
|
|
}
|
|
//--- Reference bound constrained problem:
|
|
//--- min sum((x[i]-xe[i])^4) subject to 0<=x[i]<=1
|
|
//--- NOTES:
|
|
//--- 1. we test only two optimization modes - V and FGH,
|
|
//--- because from algorithm internals we can assume that actual
|
|
//--- mode being used doesn't matter for bound constrained optimization
|
|
//--- process.
|
|
for(tmpkind=0;tmpkind<=1;tmpkind++)
|
|
{
|
|
for(n=1;n<=5;n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(bl,n);
|
|
ArrayResize(bu,n);
|
|
ArrayResize(xe,n);
|
|
ArrayResize(x,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
bl[i]=0;
|
|
bu[i]=1;
|
|
xe[i]=3*CMath::RandomReal()-1;
|
|
x[i]=CMath::RandomReal();
|
|
}
|
|
//--- check
|
|
if(tmpkind==0)
|
|
CMinLM::MinLMCreateFGH(n,x,state);
|
|
//--- check
|
|
if(tmpkind==1)
|
|
CMinLM::MinLMCreateV(n,n,x,1.0E-3,state);
|
|
//--- function calls
|
|
CMinLM::MinLMSetCond(state,1.0E-6,0,0,0);
|
|
CMinLM::MinLMSetBC(state,bl,bu);
|
|
//--- cycle
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfi)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_fi[i]=MathPow(state.m_x[i]-xe[i],2);
|
|
}
|
|
//--- check
|
|
if((state.m_needf || state.m_needfg) || state.m_needfgh)
|
|
{
|
|
//--- change value
|
|
state.m_f=0;
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_f=state.m_f+MathPow(state.m_x[i]-xe[i],4);
|
|
}
|
|
//--- check
|
|
if(state.m_needfg || state.m_needfgh)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_g[i]=4*MathPow(state.m_x[i]-xe[i],3);
|
|
}
|
|
//--- check
|
|
if(state.m_needfgh)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
state.m_h[i].Set(j,0);
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_h[i].Set(i,12*MathPow(state.m_x[i]-xe[i],2));
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype==4)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
referror=referror || MathAbs(x[i]-CApServ::BoundVal(xe[i],bl[i],bu[i]))>(double)(5.0E-2);
|
|
}
|
|
else
|
|
referror=true;
|
|
}
|
|
}
|
|
//--- 1D problem #1
|
|
//--- NOTES: we also test negative RKind's corresponding to "inexact" schemes
|
|
//--- which use approximate finite difference Jacobian.
|
|
for(rkind=-2;rkind<=5;rkind++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
n=1;
|
|
m=1;
|
|
h=0.00001;
|
|
x[0]=100*CMath::RandomReal()-50;
|
|
//--- check
|
|
if(rkind==-2)
|
|
{
|
|
CMinLM::MinLMCreateV(n,m,x,h,state);
|
|
CMinLM::MinLMSetAccType(state,1);
|
|
}
|
|
//--- check
|
|
if(rkind==-1)
|
|
{
|
|
CMinLM::MinLMCreateV(n,m,x,h,state);
|
|
CMinLM::MinLMSetAccType(state,0);
|
|
}
|
|
//--- check
|
|
if(rkind==0)
|
|
CMinLM::MinLMCreateFJ(n,m,x,state);
|
|
//--- check
|
|
if(rkind==1)
|
|
CMinLM::MinLMCreateFGJ(n,m,x,state);
|
|
//--- check
|
|
if(rkind==2)
|
|
CMinLM::MinLMCreateFGH(n,x,state);
|
|
//--- check
|
|
if(rkind==3)
|
|
{
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,0);
|
|
}
|
|
//--- check
|
|
if(rkind==4)
|
|
{
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,1);
|
|
}
|
|
//--- check
|
|
if(rkind==5)
|
|
{
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,2);
|
|
}
|
|
//--- cycle
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfi)
|
|
state.m_fi[0]=MathSin(state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi[0]=MathSin(state.m_x[0]);
|
|
state.m_j[0].Set(0,MathCos(state.m_x[0]));
|
|
}
|
|
//--- check
|
|
if((state.m_needf || state.m_needfg) || state.m_needfgh)
|
|
state.m_f=CMath::Sqr(MathSin(state.m_x[0]));
|
|
//--- check
|
|
if(state.m_needfg || state.m_needfgh)
|
|
state.m_g[0]=2*MathSin(state.m_x[0])*MathCos(state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfgh)
|
|
state.m_h[0].Set(0,2*(MathCos(state.m_x[0])*MathCos(state.m_x[0])-MathSin(state.m_x[0])*MathSin(state.m_x[0])));
|
|
//--- search errors
|
|
scerror=scerror || !RKindVSStateCheck(rkind,state);
|
|
}
|
|
//--- function call
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
//--- search errors
|
|
lin1error=rep.m_terminationtype<=0 || MathAbs(x[0]/M_PI-(int)MathRound(x[0]/M_PI))>0.001;
|
|
}
|
|
//--- Linear equations: test normal optimization and optimization with restarts
|
|
for(n=1;n<=10;n++)
|
|
{
|
|
//--- Prepare task
|
|
h=0.00001;
|
|
//--- function call
|
|
CMatGen::RMatrixRndCond(n,100,a);
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(xe,n);
|
|
ArrayResize(b,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
xe[i]=2*CMath::RandomReal()-1;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*xe[i_];
|
|
b[i]=v;
|
|
}
|
|
//--- Test different RKind
|
|
//--- NOTES: we also test negative RKind's corresponding to "inexact" schemes
|
|
//--- which use approximate finite difference Jacobian.
|
|
for(rkind=-2;rkind<=5;rkind++)
|
|
{
|
|
//--- Solve task (first attempt)
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(rkind==-2)
|
|
{
|
|
CMinLM::MinLMCreateV(n,n,x,h,state);
|
|
CMinLM::MinLMSetAccType(state,1);
|
|
}
|
|
//--- check
|
|
if(rkind==-1)
|
|
{
|
|
CMinLM::MinLMCreateV(n,n,x,h,state);
|
|
CMinLM::MinLMSetAccType(state,0);
|
|
}
|
|
//--- check
|
|
if(rkind==0)
|
|
CMinLM::MinLMCreateFJ(n,n,x,state);
|
|
//--- check
|
|
if(rkind==1)
|
|
CMinLM::MinLMCreateFGJ(n,n,x,state);
|
|
//--- check
|
|
if(rkind==2)
|
|
CMinLM::MinLMCreateFGH(n,x,state);
|
|
//--- check
|
|
if(rkind==3)
|
|
{
|
|
CMinLM::MinLMCreateVJ(n,n,x,state);
|
|
CMinLM::MinLMSetAccType(state,0);
|
|
}
|
|
//--- check
|
|
if(rkind==4)
|
|
{
|
|
CMinLM::MinLMCreateVJ(n,n,x,state);
|
|
CMinLM::MinLMSetAccType(state,1);
|
|
}
|
|
//--- check
|
|
if(rkind==5)
|
|
{
|
|
CMinLM::MinLMCreateVJ(n,n,x,state);
|
|
CMinLM::MinLMSetAccType(state,2);
|
|
}
|
|
//--- cycle
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
AXMB(state,a,b,n);
|
|
scerror=scerror || !RKindVSStateCheck(rkind,state);
|
|
}
|
|
//--- function call
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
//--- search errors
|
|
eqerror=eqerror || rep.m_terminationtype<=0;
|
|
for(i=0;i<=n-1;i++)
|
|
eqerror=eqerror || MathAbs(x[i]-xe[i])>0.001;
|
|
//--- Now we try to restart algorithm from new point
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
//--- function call
|
|
CMinLM::MinLMRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
AXMB(state,a,b,n);
|
|
scerror=scerror || !RKindVSStateCheck(rkind,state);
|
|
}
|
|
//--- function call
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
//--- search errors
|
|
restartserror=restartserror || rep.m_terminationtype<=0;
|
|
for(i=0;i<=n-1;i++)
|
|
restartserror=restartserror || MathAbs(x[i]-xe[i])>0.001;
|
|
}
|
|
}
|
|
//--- Testing convergence properties using
|
|
//--- different optimizer types and different conditions.
|
|
//--- Only limited subset of optimizers is tested because some
|
|
//--- optimizers converge too quickly.
|
|
s=100;
|
|
for(rkind=0;rkind<=5;rkind++)
|
|
{
|
|
//--- Skip FGH optimizer - it converges too quickly
|
|
if(rkind==2)
|
|
continue;
|
|
//--- Test
|
|
for(ckind=0;ckind<=3;ckind++)
|
|
{
|
|
epsg=0;
|
|
epsf=0;
|
|
epsx=0;
|
|
maxits=0;
|
|
//--- check
|
|
if(ckind==0)
|
|
epsf=0.000001;
|
|
//--- check
|
|
if(ckind==1)
|
|
epsx=0.000001;
|
|
//--- check
|
|
if(ckind==2)
|
|
maxits=2;
|
|
//--- check
|
|
if(ckind==3)
|
|
epsg=0.0001;
|
|
//--- allocation
|
|
ArrayResize(x,3);
|
|
//--- change values
|
|
n=3;
|
|
m=3;
|
|
for(i=0;i<=2;i++)
|
|
x[i]=6;
|
|
//--- check
|
|
if(rkind==0)
|
|
CMinLM::MinLMCreateFJ(n,m,x,state);
|
|
//--- check
|
|
if(rkind==1)
|
|
CMinLM::MinLMCreateFGJ(n,m,x,state);
|
|
//--- check
|
|
if(!CAp::Assert(rkind!=2))
|
|
return(false);
|
|
//--- check
|
|
if(rkind==3)
|
|
{
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,0);
|
|
}
|
|
//--- check
|
|
if(rkind==4)
|
|
{
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,1);
|
|
}
|
|
//--- check
|
|
if(rkind==5)
|
|
{
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,2);
|
|
}
|
|
//--- function call
|
|
CMinLM::MinLMSetCond(state,epsg,epsf,epsx,maxits);
|
|
//--- cycle
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfi || state.m_needfij)
|
|
{
|
|
state.m_fi[0]=s*(MathExp(state.m_x[0])-2);
|
|
state.m_fi[1]=CMath::Sqr(state.m_x[1])+1;
|
|
state.m_fi[2]=state.m_x[2]-state.m_x[0];
|
|
}
|
|
//--- check
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_j[0].Set(0,s*MathExp(state.m_x[0]));
|
|
state.m_j[0].Set(1,0);
|
|
state.m_j[0].Set(2,0);
|
|
state.m_j[1].Set(0,0);
|
|
state.m_j[1].Set(1,2*state.m_x[1]);
|
|
state.m_j[1].Set(2,0);
|
|
state.m_j[2].Set(0,-1);
|
|
state.m_j[2].Set(1,0);
|
|
state.m_j[2].Set(2,1);
|
|
}
|
|
//--- check
|
|
if((state.m_needf || state.m_needfg) || state.m_needfgh)
|
|
state.m_f=s*CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(CMath::Sqr(state.m_x[1])+1)+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg || state.m_needfgh)
|
|
{
|
|
state.m_g[0]=s*2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
|
|
state.m_g[1]=2*(CMath::Sqr(state.m_x[1])+1)*2*state.m_x[1];
|
|
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
|
|
}
|
|
//--- check
|
|
if(state.m_needfgh)
|
|
{
|
|
state.m_h[0].Set(0,s*(4*CMath::Sqr(MathExp(state.m_x[0]))-4*MathExp(state.m_x[0]))+2);
|
|
state.m_h[0].Set(1,0);
|
|
state.m_h[0].Set(2,-2);
|
|
state.m_h[1].Set(0,0);
|
|
state.m_h[1].Set(1,12*CMath::Sqr(state.m_x[1])+4);
|
|
state.m_h[1].Set(2,0);
|
|
state.m_h[2].Set(0,-2);
|
|
state.m_h[2].Set(1,0);
|
|
state.m_h[2].Set(2,2);
|
|
}
|
|
//--- search errors
|
|
scerror=scerror || !RKindVSStateCheck(rkind,state);
|
|
}
|
|
//--- function call
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
//--- check
|
|
if(ckind==0)
|
|
{
|
|
//--- search errors
|
|
converror=converror || MathAbs(x[0]-MathLog(2))>0.05;
|
|
converror=converror || MathAbs(x[1])>0.05;
|
|
converror=converror || MathAbs(x[2]-MathLog(2))>0.05;
|
|
converror=converror || rep.m_terminationtype!=1;
|
|
}
|
|
//--- check
|
|
if(ckind==1)
|
|
{
|
|
//--- search errors
|
|
converror=converror || MathAbs(x[0]-MathLog(2))>0.05;
|
|
converror=converror || MathAbs(x[1])>0.05;
|
|
converror=converror || MathAbs(x[2]-MathLog(2))>0.05;
|
|
converror=converror || rep.m_terminationtype!=2;
|
|
}
|
|
//--- check
|
|
if(ckind==2)
|
|
{
|
|
//--- search errors
|
|
converror=(converror || rep.m_terminationtype!=5) || rep.m_iterationscount!=maxits;
|
|
}
|
|
//--- check
|
|
if(ckind==3)
|
|
{
|
|
//--- search errors
|
|
converror=converror || MathAbs(x[0]-MathLog(2))>0.05;
|
|
converror=converror || MathAbs(x[1])>0.05;
|
|
converror=converror || MathAbs(x[2]-MathLog(2))>0.05;
|
|
converror=converror || rep.m_terminationtype!=4;
|
|
}
|
|
}
|
|
}
|
|
//--- Other properties:
|
|
//--- 1. test reports (F should form monotone sequence)
|
|
//--- 2. test maximum step
|
|
for(rkind=0;rkind<=5;rkind++)
|
|
{
|
|
//--- reports:
|
|
//--- * check that first report is initial point
|
|
//--- * check that F is monotone decreasing
|
|
//--- * check that last report is final result
|
|
n=3;
|
|
m=3;
|
|
s=100;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=6;
|
|
//--- check
|
|
if(rkind==0)
|
|
CMinLM::MinLMCreateFJ(n,m,x,state);
|
|
//--- check
|
|
if(rkind==1)
|
|
CMinLM::MinLMCreateFGJ(n,m,x,state);
|
|
//--- check
|
|
if(rkind==2)
|
|
CMinLM::MinLMCreateFGH(n,x,state);
|
|
//--- check
|
|
if(rkind==3)
|
|
{
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,0);
|
|
}
|
|
//--- check
|
|
if(rkind==4)
|
|
{
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,1);
|
|
}
|
|
//--- check
|
|
if(rkind==5)
|
|
{
|
|
CMinLM::MinLMCreateVJ(n,m,x,state);
|
|
CMinLM::MinLMSetAccType(state,2);
|
|
}
|
|
//--- function calls
|
|
CMinLM::MinLMSetCond(state,0,0,0,4);
|
|
CMinLM::MinLMSetXRep(state,true);
|
|
//--- change value
|
|
fprev=CMath::m_maxrealnumber;
|
|
//--- cycle
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needfi || state.m_needfij)
|
|
{
|
|
state.m_fi[0]=MathSqrt(s)*(MathExp(state.m_x[0])-2);
|
|
state.m_fi[1]=state.m_x[1];
|
|
state.m_fi[2]=state.m_x[2]-state.m_x[0];
|
|
}
|
|
//--- check
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_j[0].Set(0,MathSqrt(s)*MathExp(state.m_x[0]));
|
|
state.m_j[0].Set(1,0);
|
|
state.m_j[0].Set(2,0);
|
|
state.m_j[1].Set(0,0);
|
|
state.m_j[1].Set(1,1);
|
|
state.m_j[1].Set(2,0);
|
|
state.m_j[2].Set(0,-1);
|
|
state.m_j[2].Set(1,0);
|
|
state.m_j[2].Set(2,1);
|
|
}
|
|
//--- check
|
|
if((state.m_needf || state.m_needfg) || state.m_needfgh)
|
|
state.m_f=s*CMath::Sqr(MathExp(state.m_x[0])-2)+CMath::Sqr(state.m_x[1])+CMath::Sqr(state.m_x[2]-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg || state.m_needfgh)
|
|
{
|
|
state.m_g[0]=s*2*(MathExp(state.m_x[0])-2)*MathExp(state.m_x[0])+2*(state.m_x[0]-state.m_x[2]);
|
|
state.m_g[1]=2*state.m_x[1];
|
|
state.m_g[2]=2*(state.m_x[2]-state.m_x[0]);
|
|
}
|
|
//--- check
|
|
if(state.m_needfgh)
|
|
{
|
|
state.m_h[0].Set(0,s*(4*CMath::Sqr(MathExp(state.m_x[0]))-4*MathExp(state.m_x[0]))+2);
|
|
state.m_h[0].Set(1,0);
|
|
state.m_h[0].Set(2,-2);
|
|
state.m_h[1].Set(0,0);
|
|
state.m_h[1].Set(1,2);
|
|
state.m_h[1].Set(2,0);
|
|
state.m_h[2].Set(0,-2);
|
|
state.m_h[2].Set(1,0);
|
|
state.m_h[2].Set(2,2);
|
|
}
|
|
//--- search errors
|
|
scerror=scerror || !RKindVSStateCheck(rkind,state);
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
othererrors=othererrors || state.m_f>fprev;
|
|
//--- check
|
|
if(fprev==CMath::m_maxrealnumber)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
othererrors=othererrors || state.m_x[i]!=x[i];
|
|
}
|
|
fprev=state.m_f;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xlast[i_]=state.m_x[i_];
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinLM::MinLMResults(state,x,rep);
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
othererrors=othererrors || x[i]!=xlast[i];
|
|
}
|
|
n=1;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
x[0]=100;
|
|
stpmax=0.05+0.05*CMath::RandomReal();
|
|
//--- function calls
|
|
CMinLM::MinLMCreateFGH(n,x,state);
|
|
CMinLM::MinLMSetCond(state,1.0E-9,0,0,0);
|
|
CMinLM::MinLMSetStpMax(state,stpmax);
|
|
CMinLM::MinLMSetXRep(state,true);
|
|
xprev=x[0];
|
|
//--- cycle
|
|
while(CMinLM::MinLMIteration(state))
|
|
{
|
|
//--- check
|
|
if((state.m_needf || state.m_needfg) || state.m_needfgh)
|
|
state.m_f=MathExp(state.m_x[0])+MathExp(-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfg || state.m_needfgh)
|
|
state.m_g[0]=MathExp(state.m_x[0])-MathExp(-state.m_x[0]);
|
|
//--- check
|
|
if(state.m_needfgh)
|
|
state.m_h[0].Set(0,MathExp(state.m_x[0])+MathExp(-state.m_x[0]));
|
|
//--- search errors
|
|
othererrors=othererrors || MathAbs(state.m_x[0]-xprev)>(double)((1+MathSqrt(CMath::m_machineepsilon))*stpmax);
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
xprev=state.m_x[0];
|
|
}
|
|
//--- end
|
|
waserrors=((((((referror || lin1error) || lin2error) || eqerror) || converror) || scerror) || othererrors) || restartserror;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING LEVENBERG-MARQUARDT OPTIMIZATION");
|
|
Print("REFERENCE PROBLEMS: ");
|
|
//--- check
|
|
if(referror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("1-D PROBLEM #1: ");
|
|
//--- check
|
|
if(lin1error)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("1-D PROBLEM #2: ");
|
|
//--- check
|
|
if(lin2error)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("LINEAR EQUATIONS: ");
|
|
//--- check
|
|
if(eqerror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("RESTARTS: ");
|
|
//--- check
|
|
if(restartserror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("CONVERGENCE PROPERTIES: ");
|
|
//--- check
|
|
if(converror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("STATE FIELDS CONSISTENCY: ");
|
|
//--- check
|
|
if(scerror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("OTHER PROPERTIES: ");
|
|
//--- check
|
|
if(othererrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Asserts that State fields are consistent with RKind. |
|
|
//| Returns False otherwise. |
|
|
//| RKind is an algorithm selector: |
|
|
//| * -2=V,AccType=1 |
|
|
//| * -1=V,AccType=0 |
|
|
//| * 0=FJ |
|
|
//| * 1=FGJ |
|
|
//| * 2=FGH |
|
|
//| * 3=VJ,AccType=0 |
|
|
//| * 4=VJ,AccType=1 |
|
|
//| * 5=VJ,AccType=2 |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestMinLMUnit::RKindVSStateCheck(const int rkind,CMinLMState &state)
|
|
{
|
|
//--- create a variable
|
|
int nset=0;
|
|
//--- check
|
|
if(state.m_needfi)
|
|
nset=nset+1;
|
|
//--- check
|
|
if(state.m_needf)
|
|
nset=nset+1;
|
|
//--- check
|
|
if(state.m_needfg)
|
|
nset=nset+1;
|
|
//--- check
|
|
if(state.m_needfij)
|
|
nset=nset+1;
|
|
//--- check
|
|
if(state.m_needfgh)
|
|
nset=nset+1;
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
nset=nset+1;
|
|
//--- check
|
|
if(nset!=1)
|
|
return(false);
|
|
//--- check
|
|
if(rkind==-2)
|
|
return(state.m_needfi || state.m_xupdated);
|
|
//--- check
|
|
if(rkind==-1)
|
|
return(state.m_needfi || state.m_xupdated);
|
|
//--- check
|
|
if(rkind==0)
|
|
return(state.m_needf || state.m_needfij || state.m_xupdated);
|
|
//--- check
|
|
if(rkind==1)
|
|
return(state.m_needf || state.m_needfij || state.m_needfg || state.m_xupdated);
|
|
//--- check
|
|
if(rkind==2)
|
|
return(state.m_needf || state.m_needfg || state.m_needfgh || state.m_xupdated);
|
|
//--- check
|
|
if(rkind==3)
|
|
return(state.m_needfi || state.m_needfij || state.m_xupdated);
|
|
//--- check
|
|
if(rkind==4)
|
|
return(state.m_needfi || state.m_needfij || state.m_xupdated);
|
|
//--- check
|
|
if(rkind==5)
|
|
return(state.m_needfi || state.m_needfij || state.m_xupdated);
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Calculates FI/F/G/H for problem min(||Ax-b||) |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestMinLMUnit::AXMB(CMinLMState &state,CMatrixDouble &a,
|
|
double &b[],const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- check
|
|
if((state.m_needf || state.m_needfg) || state.m_needfgh)
|
|
state.m_f=0;
|
|
//--- check
|
|
if(state.m_needfg || state.m_needfgh)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_g[i]=0;
|
|
}
|
|
//--- check
|
|
if(state.m_needfgh)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
state.m_h[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=a[i][i_]*state.m_x[i_];
|
|
//--- check
|
|
if((state.m_needf || state.m_needfg) || state.m_needfgh)
|
|
state.m_f=state.m_f+CMath::Sqr(v-b[i]);
|
|
//--- check
|
|
if(state.m_needfg || state.m_needfgh)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
state.m_g[j]=state.m_g[j]+2*(v-b[i])*a[i][j];
|
|
}
|
|
//--- check
|
|
if(state.m_needfgh)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
for(k=0;k<=n-1;k++)
|
|
state.m_h[j].Set(k,state.m_h[j][k]+2*a[i][j]*a[i][k]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_needfi)
|
|
state.m_fi[i]=v-b[i];
|
|
//--- check
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi[i]=v-b[i];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_j[i].Set(i_,a[i][i_]);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLSFit |
|
|
//+------------------------------------------------------------------+
|
|
class CTestLSFitUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void TestPolynomialFitting(bool &fiterrors);
|
|
static void TestRationalFitting(bool &fiterrors);
|
|
static void TestSplineFitting(bool &fiterrors);
|
|
static void TestGeneralFitting(bool &llserrors,bool &nlserrors);
|
|
static bool IsGLSSolution(const int n,const int m,const int k,double &y[],double &w[],CMatrixDouble &fmatrix,CMatrixDouble &cmatrix,double &cc[]);
|
|
static double GetGLSError(const int n,const int m,double &y[],double &w[],CMatrixDouble &fmatrix,double &c[]);
|
|
static void FitLinearNonlinear(const int m,const int deravailable,CMatrixDouble &xy,CLSFitState &state,bool &nlserrors);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestLSFitUnit(void);
|
|
~CTestLSFitUnit(void);
|
|
//--- public method
|
|
static bool TestLSFit(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestLSFitUnit::CTestLSFitUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestLSFitUnit::~CTestLSFitUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLSFit |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestLSFitUnit::TestLSFit(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool llserrors;
|
|
bool nlserrors;
|
|
bool polfiterrors;
|
|
bool ratfiterrors;
|
|
bool splfiterrors;
|
|
//--- initialization
|
|
waserrors=false;
|
|
//--- function calls
|
|
TestPolynomialFitting(polfiterrors);
|
|
TestRationalFitting(ratfiterrors);
|
|
TestSplineFitting(splfiterrors);
|
|
TestGeneralFitting(llserrors,nlserrors);
|
|
//--- report
|
|
waserrors=(((llserrors || nlserrors) || polfiterrors) || ratfiterrors) || splfiterrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING LEAST SQUARES");
|
|
Print("POLYNOMIAL LEAST SQUARES: ");
|
|
//--- check
|
|
if(polfiterrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("RATIONAL LEAST SQUARES: ");
|
|
//--- check
|
|
if(ratfiterrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("SPLINE LEAST SQUARES: ");
|
|
//--- check
|
|
if(splfiterrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("LINEAR LEAST SQUARES: ");
|
|
//--- check
|
|
if(llserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("NON-LINEAR LEAST SQUARES: ");
|
|
//--- check
|
|
if(nlserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- end
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unit test |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestLSFitUnit::TestPolynomialFitting(bool &fiterrors)
|
|
{
|
|
//--- create variables
|
|
double threshold=0;
|
|
double t=0;
|
|
int i=0;
|
|
int k=0;
|
|
int info=0;
|
|
int info2=0;
|
|
double v=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double s=0;
|
|
double xmin=0;
|
|
double xmax=0;
|
|
double refrms=0;
|
|
double refavg=0;
|
|
double refavgrel=0;
|
|
double refmax=0;
|
|
int n=0;
|
|
int m=0;
|
|
int maxn=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double w[];
|
|
double x2[];
|
|
double y2[];
|
|
double w2[];
|
|
double xfull[];
|
|
double yfull[];
|
|
double xc[];
|
|
double yc[];
|
|
int dc[];
|
|
//--- objects of classes �������
|
|
CBarycentricInterpolant p;
|
|
CBarycentricInterpolant p1;
|
|
CBarycentricInterpolant p2;
|
|
CPolynomialFitReport rep;
|
|
CPolynomialFitReport rep2;
|
|
//--- initialization
|
|
fiterrors=false;
|
|
maxn=5;
|
|
passcount=20;
|
|
threshold=1.0E8*CMath::m_machineepsilon;
|
|
//--- Test polunomial fitting
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- N=M+K fitting (i.e. interpolation)
|
|
for(k=0;k<=n-1;k++)
|
|
{
|
|
CApServ::TaskGenInt1D(-1,1,n,xfull,yfull);
|
|
//--- allocation
|
|
ArrayResize(x,n-k);
|
|
ArrayResize(y,n-k);
|
|
ArrayResize(w,n-k);
|
|
//--- check
|
|
if(k>0)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(xc,k);
|
|
ArrayResize(yc,k);
|
|
ArrayResize(dc,k);
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-k-1;i++)
|
|
{
|
|
x[i]=xfull[i];
|
|
y[i]=yfull[i];
|
|
w[i]=1+CMath::RandomReal();
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
xc[i]=xfull[n-k+i];
|
|
yc[i]=yfull[n-k+i];
|
|
dc[i]=0;
|
|
}
|
|
//--- function call
|
|
CLSFit::PolynomialFitWC(x,y,w,n-k,xc,yc,dc,k,n,info,p1,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
for(i=0;i<=n-k-1;i++)
|
|
fiterrors=fiterrors || MathAbs(CRatInt::BarycentricCalc(p1,x[i])-y[i])>threshold;
|
|
for(i=0;i<=k-1;i++)
|
|
fiterrors=fiterrors || MathAbs(CRatInt::BarycentricCalc(p1,xc[i])-yc[i])>threshold;
|
|
}
|
|
}
|
|
//--- Testing constraints on derivatives.
|
|
//--- Special tasks which will always have solution:
|
|
//--- 1. P(0)=YC[0]
|
|
//--- 2. P(0)=YC[0],P'(0)=YC[1]
|
|
if(n>1)
|
|
{
|
|
for(m=3;m<=5;m++)
|
|
{
|
|
for(k=1;k<=2;k++)
|
|
{
|
|
CApServ::TaskGenInt1D(-1,1,n,x,y);
|
|
//--- allocation
|
|
ArrayResize(w,n);
|
|
ArrayResize(xc,2);
|
|
ArrayResize(yc,2);
|
|
ArrayResize(dc,2);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
w[i]=1+CMath::RandomReal();
|
|
xc[0]=0;
|
|
yc[0]=2*CMath::RandomReal()-1;
|
|
dc[0]=0;
|
|
xc[1]=0;
|
|
yc[1]=2*CMath::RandomReal()-1;
|
|
dc[1]=1;
|
|
//--- function call
|
|
CLSFit::PolynomialFitWC(x,y,w,n,xc,yc,dc,k,m,info,p1,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- function call
|
|
CRatInt::BarycentricDiff1(p1,0.0,v0,v1);
|
|
fiterrors=fiterrors || MathAbs(v0-yc[0])>threshold;
|
|
//--- check
|
|
if(k==2)
|
|
fiterrors=fiterrors || MathAbs(v1-yc[1])>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(m=2;m<=8;m++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- General fitting
|
|
//--- interpolating function through M nodes should have
|
|
//--- greater RMS error than fitting it through the same M nodes
|
|
n=100;
|
|
ArrayResize(x2,n);
|
|
ArrayResize(y2,n);
|
|
ArrayResize(w2,n);
|
|
xmin=0;
|
|
xmax=2*M_PI;
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x2[i]=2*M_PI*CMath::RandomReal();
|
|
y2[i]=MathSin(x2[i]);
|
|
w2[i]=1;
|
|
}
|
|
//--- allocation
|
|
ArrayResize(x,m);
|
|
ArrayResize(y,m);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
x[i]=xmin+(xmax-xmin)*i/(m-1);
|
|
y[i]=MathSin(x[i]);
|
|
}
|
|
//--- function calls
|
|
CPolInt::PolynomialBuild(x,y,m,p1);
|
|
CLSFit::PolynomialFitWC(x2,y2,w2,n,xc,yc,dc,0,m,info,p2,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- calculate P1 (interpolant) RMS error,compare with P2 error
|
|
v1=0;
|
|
v2=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v1=v1+CMath::Sqr(CRatInt::BarycentricCalc(p1,x2[i])-y2[i]);
|
|
v2=v2+CMath::Sqr(CRatInt::BarycentricCalc(p2,x2[i])-y2[i]);
|
|
}
|
|
v1=MathSqrt(v1/n);
|
|
v2=MathSqrt(v2/n);
|
|
//--- search errors
|
|
fiterrors=fiterrors || v2>v1;
|
|
fiterrors=fiterrors || MathAbs(v2-rep.m_rmserror)>threshold;
|
|
}
|
|
//--- compare weighted and non-weighted
|
|
n=20;
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1;
|
|
}
|
|
//--- function calls
|
|
CLSFit::PolynomialFitWC(x,y,w,n,xc,yc,dc,0,m,info,p1,rep);
|
|
CLSFit::PolynomialFit(x,y,n,m,info2,p2,rep2);
|
|
//--- check
|
|
if(info<=0 || info2<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- calculate P1 (interpolant),compare with P2 error
|
|
//--- compare RMS errors
|
|
t=2*CMath::RandomReal()-1;
|
|
v1=CRatInt::BarycentricCalc(p1,t);
|
|
v2=CRatInt::BarycentricCalc(p2,t);
|
|
//--- search errors
|
|
fiterrors=fiterrors || v2!=v1;
|
|
fiterrors=fiterrors || rep.m_rmserror!=rep2.m_rmserror;
|
|
fiterrors=fiterrors || rep.m_avgerror!=rep2.m_avgerror;
|
|
fiterrors=fiterrors || rep.m_avgrelerror!=rep2.m_avgrelerror;
|
|
fiterrors=fiterrors || rep.m_maxerror!=rep2.m_maxerror;
|
|
}
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(m=1;m<=maxn;m++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(passcount>=2,"PassCount should be 2 or greater!"))
|
|
return;
|
|
//--- solve simple task (all X[] are the same,Y[] are specially
|
|
//--- calculated to ensure simple form of all types of errors)
|
|
//--- and check correctness of the errors calculated by subroutines
|
|
//--- First pass is done with zero Y[],other passes - with random Y[].
|
|
//--- It should test both ability to correctly calculate errors and
|
|
//--- ability to not fail while working with zeros :)
|
|
n=4*maxn;
|
|
//--- check
|
|
if(pass==1)
|
|
{
|
|
v1=0;
|
|
v2=0;
|
|
v=0;
|
|
}
|
|
else
|
|
{
|
|
v1=CMath::RandomReal();
|
|
v2=CMath::RandomReal();
|
|
v=1+CMath::RandomReal();
|
|
}
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
//--- change values
|
|
for(i=0;i<=maxn-1;i++)
|
|
{
|
|
x[4*i+0]=i;
|
|
y[4*i+0]=v-v2;
|
|
w[4*i+0]=1;
|
|
x[4*i+1]=i;
|
|
y[4*i+1]=v-v1;
|
|
w[4*i+1]=1;
|
|
x[4*i+2]=i;
|
|
y[4*i+2]=v+v1;
|
|
w[4*i+2]=1;
|
|
x[4*i+3]=i;
|
|
y[4*i+3]=v+v2;
|
|
w[4*i+3]=1;
|
|
}
|
|
//--- change values
|
|
refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2);
|
|
refavg=(MathAbs(v1)+MathAbs(v2))/2;
|
|
//--- check
|
|
if(pass==1)
|
|
refavgrel=0;
|
|
else
|
|
refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2));
|
|
refmax=MathMax(v1,v2);
|
|
//--- Test errors correctness
|
|
CLSFit::PolynomialFit(x,y,n,m,info,p,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
s=CRatInt::BarycentricCalc(p,0);
|
|
//--- search errors
|
|
fiterrors=fiterrors || MathAbs(s-v)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_rmserror-refrms)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_avgerror-refavg)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_maxerror-refmax)>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestLSFitUnit::TestRationalFitting(bool &fiterrors)
|
|
{
|
|
//--- create variables
|
|
double threshold=0;
|
|
int maxn=0;
|
|
int passcount=0;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int k=0;
|
|
int pass=0;
|
|
double t=0;
|
|
double s=0;
|
|
double v=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
int info=0;
|
|
int info2=0;
|
|
double xmin=0;
|
|
double xmax=0;
|
|
double refrms=0;
|
|
double refavg=0;
|
|
double refavgrel=0;
|
|
double refmax=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double x2[];
|
|
double y[];
|
|
double y2[];
|
|
double w[];
|
|
double w2[];
|
|
double xc[];
|
|
double yc[];
|
|
int dc[];
|
|
//--- objects of classes
|
|
CBarycentricInterpolant b1;
|
|
CBarycentricInterpolant b2;
|
|
CBarycentricFitReport rep;
|
|
CBarycentricFitReport rep2;
|
|
//--- initialization
|
|
fiterrors=false;
|
|
//--- PassCount number of repeated passes
|
|
//--- Threshold error tolerance
|
|
//--- LipschitzTol Lipschitz constant increase allowed
|
|
//--- when calculating constant on a twice denser grid
|
|
passcount=5;
|
|
maxn=15;
|
|
threshold=1000000*CMath::m_machineepsilon;
|
|
//--- Test rational fitting:
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(n=2;n<=maxn;n++)
|
|
{
|
|
//--- N=M+K fitting (i.e. interpolation)
|
|
for(k=0;k<=n-1;k++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n-k);
|
|
ArrayResize(y,n-k);
|
|
ArrayResize(w,n-k);
|
|
//--- check
|
|
if(k>0)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(xc,k);
|
|
ArrayResize(yc,k);
|
|
ArrayResize(dc,k);
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-k-1;i++)
|
|
{
|
|
x[i]=(double)i/(double)(n-1);
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1+CMath::RandomReal();
|
|
}
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
xc[i]=(double)(n-k+i)/(double)(n-1);
|
|
yc[i]=2*CMath::RandomReal()-1;
|
|
dc[i]=0;
|
|
}
|
|
//--- function call
|
|
CLSFit::BarycentricFitFloaterHormannWC(x,y,w,n-k,xc,yc,dc,k,n,info,b1,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
for(i=0;i<=n-k-1;i++)
|
|
fiterrors=fiterrors || MathAbs(CRatInt::BarycentricCalc(b1,x[i])-y[i])>threshold;
|
|
for(i=0;i<=k-1;i++)
|
|
fiterrors=fiterrors || MathAbs(CRatInt::BarycentricCalc(b1,xc[i])-yc[i])>threshold;
|
|
}
|
|
}
|
|
//--- Testing constraints on derivatives:
|
|
//--- * several M's are tried
|
|
//--- * several K's are tried - 1,2.
|
|
//--- * constraints at the ends of the interval
|
|
for(m=3;m<=5;m++)
|
|
{
|
|
for(k=1;k<=2;k++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
ArrayResize(xc,2);
|
|
ArrayResize(yc,2);
|
|
ArrayResize(dc,2);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1+CMath::RandomReal();
|
|
}
|
|
//--- change values
|
|
xc[0]=-1;
|
|
yc[0]=2*CMath::RandomReal()-1;
|
|
dc[0]=0;
|
|
xc[1]=1;
|
|
yc[1]=2*CMath::RandomReal()-1;
|
|
dc[1]=0;
|
|
//--- function call
|
|
CLSFit::BarycentricFitFloaterHormannWC(x,y,w,n,xc,yc,dc,k,m,info,b1,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
CRatInt::BarycentricDiff1(b1,xc[i],v0,v1);
|
|
//--- search errors
|
|
fiterrors=fiterrors || MathAbs(v0-yc[i])>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(m=2;m<=8;m++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- General fitting
|
|
//--- interpolating function through M nodes should have
|
|
//--- greater RMS error than fitting it through the same M nodes
|
|
n=100;
|
|
ArrayResize(x2,n);
|
|
ArrayResize(y2,n);
|
|
ArrayResize(w2,n);
|
|
//--- change values
|
|
xmin=CMath::m_maxrealnumber;
|
|
xmax=-CMath::m_maxrealnumber;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x2[i]=2*M_PI*CMath::RandomReal();
|
|
y2[i]=MathSin(x2[i]);
|
|
w2[i]=1;
|
|
xmin=MathMin(xmin,x2[i]);
|
|
xmax=MathMax(xmax,x2[i]);
|
|
}
|
|
//--- allocation
|
|
ArrayResize(x,m);
|
|
ArrayResize(y,m);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
x[i]=xmin+(xmax-xmin)*i/(m-1);
|
|
y[i]=MathSin(x[i]);
|
|
}
|
|
//--- function call
|
|
CRatInt::BarycentricBuildFloaterHormann(x,y,m,3,b1);
|
|
CLSFit::BarycentricFitFloaterHormannWC(x2,y2,w2,n,xc,yc,dc,0,m,info,b2,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- calculate B1 (interpolant) RMS error,compare with B2 error
|
|
v1=0;
|
|
v2=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v1=v1+CMath::Sqr(CRatInt::BarycentricCalc(b1,x2[i])-y2[i]);
|
|
v2=v2+CMath::Sqr(CRatInt::BarycentricCalc(b2,x2[i])-y2[i]);
|
|
}
|
|
v1=MathSqrt(v1/n);
|
|
v2=MathSqrt(v2/n);
|
|
//--- search errors
|
|
fiterrors=fiterrors || v2>v1;
|
|
fiterrors=fiterrors || MathAbs(v2-rep.m_rmserror)>threshold;
|
|
}
|
|
//--- compare weighted and non-weighted
|
|
n=20;
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1;
|
|
}
|
|
//--- function calls
|
|
CLSFit::BarycentricFitFloaterHormannWC(x,y,w,n,xc,yc,dc,0,m,info,b1,rep);
|
|
CLSFit::BarycentricFitFloaterHormann(x,y,n,m,info2,b2,rep2);
|
|
//--- check
|
|
if(info<=0 || info2<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- calculate B1 (interpolant),compare with B2
|
|
//--- compare RMS errors
|
|
t=2*CMath::RandomReal()-1;
|
|
v1=CRatInt::BarycentricCalc(b1,t);
|
|
v2=CRatInt::BarycentricCalc(b2,t);
|
|
//--- search errors
|
|
fiterrors=fiterrors || v2!=v1;
|
|
fiterrors=fiterrors || rep.m_rmserror!=rep2.m_rmserror;
|
|
fiterrors=fiterrors || rep.m_avgerror!=rep2.m_avgerror;
|
|
fiterrors=fiterrors || rep.m_avgrelerror!=rep2.m_avgrelerror;
|
|
fiterrors=fiterrors || rep.m_maxerror!=rep2.m_maxerror;
|
|
}
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(passcount>=2,"PassCount should be 2 or greater!"))
|
|
return;
|
|
//--- solve simple task (all X[] are the same,Y[] are specially
|
|
//--- calculated to ensure simple form of all types of errors)
|
|
//--- and check correctness of the errors calculated by subroutines
|
|
//--- First pass is done with zero Y[],other passes - with random Y[].
|
|
//--- It should test both ability to correctly calculate errors and
|
|
//--- ability to not fail while working with zeros :)
|
|
n=4;
|
|
if(pass==1)
|
|
{
|
|
v1=0;
|
|
v2=0;
|
|
v=0;
|
|
}
|
|
else
|
|
{
|
|
v1=CMath::RandomReal();
|
|
v2=CMath::RandomReal();
|
|
v=1+CMath::RandomReal();
|
|
}
|
|
//--- allocation
|
|
ArrayResize(x,4);
|
|
ArrayResize(y,4);
|
|
ArrayResize(w,4);
|
|
//--- change values
|
|
x[0]=0;
|
|
y[0]=v-v2;
|
|
w[0]=1;
|
|
x[1]=0;
|
|
y[1]=v-v1;
|
|
w[1]=1;
|
|
x[2]=0;
|
|
y[2]=v+v1;
|
|
w[2]=1;
|
|
x[3]=0;
|
|
y[3]=v+v2;
|
|
w[3]=1;
|
|
refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2);
|
|
refavg=(MathAbs(v1)+MathAbs(v2))/2;
|
|
//--- check
|
|
if(pass==1)
|
|
refavgrel=0;
|
|
else
|
|
refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2));
|
|
refmax=MathMax(v1,v2);
|
|
//--- Test errors correctness
|
|
CLSFit::BarycentricFitFloaterHormann(x,y,4,2,info,b1,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
s=CRatInt::BarycentricCalc(b1,0);
|
|
//--- search errors
|
|
fiterrors=fiterrors || MathAbs(s-v)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_rmserror-refrms)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_avgerror-refavg)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_maxerror-refmax)>threshold;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestLSFitUnit::TestSplineFitting(bool &fiterrors)
|
|
{
|
|
//--- create variables
|
|
double threshold=0;
|
|
double nonstrictthreshold=0;
|
|
int passcount=0;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int k=0;
|
|
int pass=0;
|
|
double sa=0;
|
|
double sb=0;
|
|
int info=0;
|
|
int info1=0;
|
|
int info2=0;
|
|
double s=0;
|
|
double ds=0;
|
|
double d2s=0;
|
|
int stype=0;
|
|
double t=0;
|
|
double v=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double refrms=0;
|
|
double refavg=0;
|
|
double refavgrel=0;
|
|
double refmax=0;
|
|
double rho=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double w[];
|
|
double w2[];
|
|
double xc[];
|
|
double yc[];
|
|
double d[];
|
|
int dc[];
|
|
//--- objects of classes �������
|
|
CSpline1DInterpolant c;
|
|
CSpline1DInterpolant c2;
|
|
CSpline1DFitReport rep;
|
|
CSpline1DFitReport rep2;
|
|
//--- Valyes:
|
|
//--- * pass count
|
|
//--- * threshold - for tests which must be satisfied exactly
|
|
//--- * nonstrictthreshold - for approximate tests
|
|
passcount=20;
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
nonstrictthreshold=1.0E-4;
|
|
fiterrors=false;
|
|
//--- Test fitting by Cubic and Hermite splines (obsolete,but still supported)
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Cubic splines
|
|
//--- Ability to handle boundary constraints (1-4 constraints on F,dF/dx).
|
|
for(m=4;m<=8;m++)
|
|
{
|
|
for(k=1;k<=4;k++)
|
|
{
|
|
//--- check
|
|
if(k>=m)
|
|
continue;
|
|
n=100;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
ArrayResize(xc,4);
|
|
ArrayResize(yc,4);
|
|
ArrayResize(dc,4);
|
|
//--- change values
|
|
sa=1+CMath::RandomReal();
|
|
sb=2*CMath::RandomReal()-1;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=sa*CMath::RandomReal()+sb;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1+CMath::RandomReal();
|
|
}
|
|
xc[0]=sb;
|
|
yc[0]=2*CMath::RandomReal()-1;
|
|
dc[0]=0;
|
|
xc[1]=sb;
|
|
yc[1]=2*CMath::RandomReal()-1;
|
|
dc[1]=1;
|
|
xc[2]=sa+sb;
|
|
yc[2]=2*CMath::RandomReal()-1;
|
|
dc[2]=0;
|
|
xc[3]=sa+sb;
|
|
yc[3]=2*CMath::RandomReal()-1;
|
|
dc[3]=1;
|
|
//--- function call
|
|
CLSFit::Spline1DFitCubicWC(x,y,w,n,xc,yc,dc,k,m,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- Check that constraints are satisfied
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
CSpline1D::Spline1DDiff(c,xc[i],s,ds,d2s);
|
|
//--- check
|
|
if(dc[i]==0)
|
|
fiterrors=fiterrors || MathAbs(s-yc[i])>threshold;
|
|
//--- check
|
|
if(dc[i]==1)
|
|
fiterrors=fiterrors || MathAbs(ds-yc[i])>threshold;
|
|
//--- check
|
|
if(dc[i]==2)
|
|
fiterrors=fiterrors || MathAbs(d2s-yc[i])>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Cubic splines
|
|
//--- Ability to handle one internal constraint
|
|
for(m=4;m<=8;m++)
|
|
{
|
|
n=100;
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
ArrayResize(xc,1);
|
|
ArrayResize(yc,1);
|
|
ArrayResize(dc,1);
|
|
//--- change values
|
|
sa=1+CMath::RandomReal();
|
|
sb=2*CMath::RandomReal()-1;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=sa*CMath::RandomReal()+sb;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1+CMath::RandomReal();
|
|
}
|
|
xc[0]=sa*CMath::RandomReal()+sb;
|
|
yc[0]=2*CMath::RandomReal()-1;
|
|
dc[0]=CMath::RandomInteger(2);
|
|
//--- function call
|
|
CLSFit::Spline1DFitCubicWC(x,y,w,n,xc,yc,dc,1,m,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- Check that constraints are satisfied
|
|
CSpline1D::Spline1DDiff(c,xc[0],s,ds,d2s);
|
|
//--- check
|
|
if(dc[0]==0)
|
|
fiterrors=fiterrors || MathAbs(s-yc[0])>threshold;
|
|
//--- check
|
|
if(dc[0]==1)
|
|
fiterrors=fiterrors || MathAbs(ds-yc[0])>threshold;
|
|
//--- check
|
|
if(dc[0]==2)
|
|
fiterrors=fiterrors || MathAbs(d2s-yc[0])>threshold;
|
|
}
|
|
}
|
|
//--- Hermite splines
|
|
//--- Ability to handle boundary constraints (1-4 constraints on F,dF/dx).
|
|
for(m=4;m<=8;m++)
|
|
{
|
|
for(k=1;k<=4;k++)
|
|
{
|
|
//--- check
|
|
if(k>=m)
|
|
continue;
|
|
//--- check
|
|
if(m%2!=0)
|
|
continue;
|
|
n=100;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
ArrayResize(xc,4);
|
|
ArrayResize(yc,4);
|
|
ArrayResize(dc,4);
|
|
//--- change values
|
|
sa=1+CMath::RandomReal();
|
|
sb=2*CMath::RandomReal()-1;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=sa*CMath::RandomReal()+sb;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1+CMath::RandomReal();
|
|
}
|
|
xc[0]=sb;
|
|
yc[0]=2*CMath::RandomReal()-1;
|
|
dc[0]=0;
|
|
xc[1]=sb;
|
|
yc[1]=2*CMath::RandomReal()-1;
|
|
dc[1]=1;
|
|
xc[2]=sa+sb;
|
|
yc[2]=2*CMath::RandomReal()-1;
|
|
dc[2]=0;
|
|
xc[3]=sa+sb;
|
|
yc[3]=2*CMath::RandomReal()-1;
|
|
dc[3]=1;
|
|
//--- function call
|
|
CLSFit::Spline1DFitHermiteWC(x,y,w,n,xc,yc,dc,k,m,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- Check that constraints are satisfied
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
CSpline1D::Spline1DDiff(c,xc[i],s,ds,d2s);
|
|
//--- check
|
|
if(dc[i]==0)
|
|
fiterrors=fiterrors || MathAbs(s-yc[i])>threshold;
|
|
//--- check
|
|
if(dc[i]==1)
|
|
fiterrors=fiterrors || MathAbs(ds-yc[i])>threshold;
|
|
//--- check
|
|
if(dc[i]==2)
|
|
fiterrors=fiterrors || MathAbs(d2s-yc[i])>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Hermite splines
|
|
//--- Ability to handle one internal constraint
|
|
for(m=4;m<=8;m++)
|
|
{
|
|
//--- check
|
|
if(m%2!=0)
|
|
continue;
|
|
n=100;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
ArrayResize(xc,1);
|
|
ArrayResize(yc,1);
|
|
ArrayResize(dc,1);
|
|
//--- change values
|
|
sa=1+CMath::RandomReal();
|
|
sb=2*CMath::RandomReal()-1;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=sa*CMath::RandomReal()+sb;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1+CMath::RandomReal();
|
|
}
|
|
xc[0]=sa*CMath::RandomReal()+sb;
|
|
yc[0]=2*CMath::RandomReal()-1;
|
|
dc[0]=CMath::RandomInteger(2);
|
|
//--- function call
|
|
CLSFit::Spline1DFitHermiteWC(x,y,w,n,xc,yc,dc,1,m,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
//--- Check that constraints are satisfied
|
|
CSpline1D::Spline1DDiff(c,xc[0],s,ds,d2s);
|
|
//--- check
|
|
if(dc[0]==0)
|
|
fiterrors=fiterrors || MathAbs(s-yc[0])>threshold;
|
|
//--- check
|
|
if(dc[0]==1)
|
|
fiterrors=fiterrors || MathAbs(ds-yc[0])>threshold;
|
|
//--- check
|
|
if(dc[0]==2)
|
|
fiterrors=fiterrors || MathAbs(d2s-yc[0])>threshold;
|
|
}
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(m=4;m<=8;m++)
|
|
{
|
|
for(stype=0;stype<=1;stype++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- check
|
|
if(stype==1 && m%2!=0)
|
|
continue;
|
|
//--- cubic/Hermite spline fitting:
|
|
//--- * generate "template spline" C2
|
|
//--- * generate 2*N points from C2,such that result of
|
|
//--- ideal fit should be equal to C2
|
|
//--- * fit,store in C
|
|
//--- * compare C and C2
|
|
sa=1+CMath::RandomReal();
|
|
sb=2*CMath::RandomReal()-1;
|
|
//--- check
|
|
if(stype==0)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,m-2);
|
|
ArrayResize(y,m-2);
|
|
//--- change values
|
|
for(i=0;i<=m-2-1;i++)
|
|
{
|
|
x[i]=sa*i/(m-2-1)+sb;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildCubic(x,y,m-2,1,2*CMath::RandomReal()-1,1,2*CMath::RandomReal()-1,c2);
|
|
}
|
|
//--- check
|
|
if(stype==1)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,m/2);
|
|
ArrayResize(y,m/2);
|
|
ArrayResize(d,m/2);
|
|
//--- change values
|
|
for(i=0;i<=m/2-1;i++)
|
|
{
|
|
x[i]=sa*i/(m/2-1)+sb;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
d[i]=2*CMath::RandomReal()-1;
|
|
}
|
|
//--- function call
|
|
CSpline1D::Spline1DBuildHermite(x,y,d,m/2,c2);
|
|
}
|
|
n=50;
|
|
//--- allocation
|
|
ArrayResize(x,2*n);
|
|
ArrayResize(y,2*n);
|
|
ArrayResize(w,2*n);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- "if i=0" and "if i=1" are needed to
|
|
//--- synchronize interval size for C2 and
|
|
//--- spline being fitted (i.e. C).
|
|
t=CMath::RandomReal();
|
|
x[i]=sa*CMath::RandomReal()+sb;
|
|
//--- check
|
|
if(i==0)
|
|
x[i]=sb;
|
|
//--- check
|
|
if(i==1)
|
|
x[i]=sa+sb;
|
|
//--- change values
|
|
v=CSpline1D::Spline1DCalc(c2,x[i]);
|
|
y[i]=v+t;
|
|
w[i]=1+CMath::RandomReal();
|
|
x[n+i]=x[i];
|
|
y[n+i]=v-t;
|
|
w[n+i]=w[i];
|
|
}
|
|
//--- check
|
|
if(stype==0)
|
|
CLSFit::Spline1DFitCubicWC(x,y,w,2*n,xc,yc,dc,0,m,info,c,rep);
|
|
//--- check
|
|
if(stype==1)
|
|
CLSFit::Spline1DFitHermiteWC(x,y,w,2*n,xc,yc,dc,0,m,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v=sa*CMath::RandomReal()+sb;
|
|
//--- search errors
|
|
fiterrors=fiterrors || MathAbs(CSpline1D::Spline1DCalc(c,v)-CSpline1D::Spline1DCalc(c2,v))>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(m=4;m<=8;m++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- prepare points/weights
|
|
sa=1+CMath::RandomReal();
|
|
sb=2*CMath::RandomReal()-1;
|
|
n=10+CMath::RandomInteger(10);
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=sa*CMath::RandomReal()+sb;
|
|
y[i]=2*CMath::RandomReal()-1;
|
|
w[i]=1;
|
|
}
|
|
//--- Fit cubic with unity weights,without weights,then compare
|
|
if(m>=4)
|
|
{
|
|
//--- function calls
|
|
CLSFit::Spline1DFitCubicWC(x,y,w,n,xc,yc,dc,0,m,info1,c,rep);
|
|
CLSFit::Spline1DFitCubic(x,y,n,m,info2,c2,rep2);
|
|
//--- check
|
|
if(info1<=0 || info2<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v=sa*CMath::RandomReal()+sb;
|
|
//--- search errors
|
|
fiterrors=fiterrors || CSpline1D::Spline1DCalc(c,v)!=CSpline1D::Spline1DCalc(c2,v);
|
|
fiterrors=fiterrors || rep.m_taskrcond!=rep2.m_taskrcond;
|
|
fiterrors=fiterrors || rep.m_rmserror!=rep2.m_rmserror;
|
|
fiterrors=fiterrors || rep.m_avgerror!=rep2.m_avgerror;
|
|
fiterrors=fiterrors || rep.m_avgrelerror!=rep2.m_avgrelerror;
|
|
fiterrors=fiterrors || rep.m_maxerror!=rep2.m_maxerror;
|
|
}
|
|
}
|
|
}
|
|
//--- Fit Hermite with unity weights,without weights,then compare
|
|
if(m>=4 && m%2==0)
|
|
{
|
|
CLSFit::Spline1DFitHermiteWC(x,y,w,n,xc,yc,dc,0,m,info1,c,rep);
|
|
CLSFit::Spline1DFitHermite(x,y,n,m,info2,c2,rep2);
|
|
//--- check
|
|
if(info1<=0 || info2<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v=sa*CMath::RandomReal()+sb;
|
|
//--- search errors
|
|
fiterrors=fiterrors || CSpline1D::Spline1DCalc(c,v)!=CSpline1D::Spline1DCalc(c2,v);
|
|
fiterrors=fiterrors || rep.m_taskrcond!=rep2.m_taskrcond;
|
|
fiterrors=fiterrors || rep.m_rmserror!=rep2.m_rmserror;
|
|
fiterrors=fiterrors || rep.m_avgerror!=rep2.m_avgerror;
|
|
fiterrors=fiterrors || rep.m_avgrelerror!=rep2.m_avgrelerror;
|
|
fiterrors=fiterrors || rep.m_maxerror!=rep2.m_maxerror;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- check basic properties of penalized splines which are
|
|
//--- preserved independently of Rho parameter.
|
|
for(m=4;m<=10;m++)
|
|
{
|
|
for(k=-5;k<=5;k++)
|
|
{
|
|
rho=k;
|
|
//--- when we have two points (even with different weights),
|
|
//--- resulting spline must be equal to the straight line
|
|
ArrayResize(x,2);
|
|
ArrayResize(y,2);
|
|
ArrayResize(w,2);
|
|
x[0]=-0.5-CMath::RandomReal();
|
|
y[0]=0.5+CMath::RandomReal();
|
|
w[0]=1+CMath::RandomReal();
|
|
x[1]=0.5+CMath::RandomReal();
|
|
y[1]=0.5+CMath::RandomReal();
|
|
w[1]=1+CMath::RandomReal();
|
|
//--- function call
|
|
CLSFit::Spline1DFitPenalized(x,y,2,m,rho,info,c,rep);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
v=2*CMath::RandomReal()-1;
|
|
v1=(v-x[0])/(x[1]-x[0])*y[1]+(v-x[1])/(x[0]-x[1])*y[0];
|
|
//--- search errors
|
|
fiterrors=fiterrors || MathAbs(v1-CSpline1D::Spline1DCalc(c,v))>nonstrictthreshold;
|
|
}
|
|
else
|
|
fiterrors=true;
|
|
//--- function call
|
|
CLSFit::Spline1DFitPenalizedW(x,y,w,2,m,rho,info,c,rep);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
v=2*CMath::RandomReal()-1;
|
|
v1=(v-x[0])/(x[1]-x[0])*y[1]+(v-x[1])/(x[0]-x[1])*y[0];
|
|
//--- search errors
|
|
fiterrors=fiterrors || MathAbs(v1-CSpline1D::Spline1DCalc(c,v))>nonstrictthreshold;
|
|
}
|
|
else
|
|
fiterrors=true;
|
|
//--- spline fitting is invariant with respect to
|
|
//--- scaling of weights (of course,ANY fitting algorithm
|
|
//--- must be invariant,but we want to test this property
|
|
//--- just to be sure that it is correctly implemented)
|
|
for(n=2;n<=2*m;n++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
ArrayResize(w2,n);
|
|
//--- change values
|
|
s=1+MathExp(10*CMath::RandomReal());
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=(double)i/(double)(n-1);
|
|
y[i]=CMath::RandomReal();
|
|
w[i]=0.1+CMath::RandomReal();
|
|
w2[i]=w[i]*s;
|
|
}
|
|
//--- function calls
|
|
CLSFit::Spline1DFitPenalizedW(x,y,w,n,m,rho,info,c,rep);
|
|
CLSFit::Spline1DFitPenalizedW(x,y,w2,n,m,rho,info2,c2,rep2);
|
|
//--- check
|
|
if(info>0 && info2>0)
|
|
{
|
|
v=CMath::RandomReal();
|
|
v1=CSpline1D::Spline1DCalc(c,v);
|
|
v2=CSpline1D::Spline1DCalc(c2,v);
|
|
//--- search errors
|
|
fiterrors=fiterrors || MathAbs(v1-v2)>nonstrictthreshold;
|
|
}
|
|
else
|
|
fiterrors=true;
|
|
}
|
|
}
|
|
}
|
|
//--- Advanced proprties:
|
|
//--- * penalized spline with M about 5*N and sufficiently small Rho
|
|
//--- must pass through all points on equidistant grid
|
|
for(n=2;n<=10;n++)
|
|
{
|
|
m=5*n;
|
|
rho=-5;
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=(double)i/(double)(n-1);
|
|
y[i]=CMath::RandomReal();
|
|
w[i]=0.1+CMath::RandomReal();
|
|
}
|
|
//--- function call
|
|
CLSFit::Spline1DFitPenalized(x,y,n,m,rho,info,c,rep);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
fiterrors=fiterrors || MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i]))>nonstrictthreshold;
|
|
}
|
|
else
|
|
fiterrors=true;
|
|
//--- function call
|
|
CLSFit::Spline1DFitPenalizedW(x,y,w,n,m,rho,info,c,rep);
|
|
//--- check
|
|
if(info>0)
|
|
{
|
|
//--- search errors
|
|
for(i=0;i<=n-1;i++)
|
|
fiterrors=fiterrors || MathAbs(y[i]-CSpline1D::Spline1DCalc(c,x[i]))>nonstrictthreshold;
|
|
}
|
|
else
|
|
fiterrors=true;
|
|
}
|
|
//--- Check correctness of error reports
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(passcount>=2,"PassCount should be 2 or greater!"))
|
|
return;
|
|
//--- solve simple task (all X[] are the same,Y[] are specially
|
|
//--- calculated to ensure simple form of all types of errors)
|
|
//--- and check correctness of the errors calculated by subroutines
|
|
//--- First pass is done with zero Y[],other passes - with random Y[].
|
|
//--- It should test both ability to correctly calculate errors and
|
|
//--- ability to not fail while working with zeros :)
|
|
n=4;
|
|
if(pass==1)
|
|
{
|
|
v1=0;
|
|
v2=0;
|
|
v=0;
|
|
}
|
|
else
|
|
{
|
|
v1=CMath::RandomReal();
|
|
v2=CMath::RandomReal();
|
|
v=1+CMath::RandomReal();
|
|
}
|
|
//--- allocation
|
|
ArrayResize(x,4);
|
|
ArrayResize(y,4);
|
|
ArrayResize(w,4);
|
|
//--- change values
|
|
x[0]=0;
|
|
y[0]=v-v2;
|
|
w[0]=1;
|
|
x[1]=0;
|
|
y[1]=v-v1;
|
|
w[1]=1;
|
|
x[2]=0;
|
|
y[2]=v+v1;
|
|
w[2]=1;
|
|
x[3]=0;
|
|
y[3]=v+v2;
|
|
w[3]=1;
|
|
refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2);
|
|
refavg=(MathAbs(v1)+MathAbs(v2))/2;
|
|
//--- check
|
|
if(pass==1)
|
|
refavgrel=0;
|
|
else
|
|
refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2));
|
|
refmax=MathMax(v1,v2);
|
|
//--- Test penalized spline
|
|
CLSFit::Spline1DFitPenalizedW(x,y,w,4,4,0.0,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
s=CSpline1D::Spline1DCalc(c,0);
|
|
//--- search errors
|
|
fiterrors=fiterrors || MathAbs(s-v)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_rmserror-refrms)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_avgerror-refavg)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_maxerror-refmax)>threshold;
|
|
}
|
|
//--- Test cubic fitting
|
|
CLSFit::Spline1DFitCubic(x,y,4,4,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
s=CSpline1D::Spline1DCalc(c,0);
|
|
//--- search errors
|
|
fiterrors=fiterrors || MathAbs(s-v)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_rmserror-refrms)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_avgerror-refavg)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_maxerror-refmax)>threshold;
|
|
}
|
|
//--- Test Hermite fitting
|
|
CLSFit::Spline1DFitHermite(x,y,4,4,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
fiterrors=true;
|
|
else
|
|
{
|
|
s=CSpline1D::Spline1DCalc(c,0);
|
|
//--- search errors
|
|
fiterrors=fiterrors || MathAbs(s-v)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_rmserror-refrms)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_avgerror-refavg)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
|
|
fiterrors=fiterrors || MathAbs(rep.m_maxerror-refmax)>threshold;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestLSFitUnit::TestGeneralFitting(bool &llserrors,bool &nlserrors)
|
|
{
|
|
//--- create variables
|
|
double threshold=0;
|
|
double nlthreshold=0;
|
|
int maxn=0;
|
|
int maxm=0;
|
|
int passcount=0;
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int pass=0;
|
|
double xscale=0;
|
|
double diffstep=0;
|
|
double v=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
int info=0;
|
|
int info2=0;
|
|
double refrms=0;
|
|
double refavg=0;
|
|
double refavgrel=0;
|
|
double refmax=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double w[];
|
|
double w2[];
|
|
double c[];
|
|
double c2[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble a2;
|
|
CMatrixDouble cm;
|
|
//--- objects of classes
|
|
CLSFitReport rep;
|
|
CLSFitReport rep2;
|
|
CLSFitState state;
|
|
//--- initialization
|
|
llserrors=false;
|
|
nlserrors=false;
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
nlthreshold=0.00001;
|
|
diffstep=0.0001;
|
|
maxn=6;
|
|
maxm=6;
|
|
passcount=4;
|
|
//--- Testing unconstrained least squares (linear/nonlinear)
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
for(m=1;m<=maxm;m++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Solve non-degenerate linear least squares task
|
|
//--- Use Chebyshev basis. Its condition number is very good.
|
|
a.Resize(n,m);
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
xscale=0.9+0.1*CMath::RandomReal();
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(n==1)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
else
|
|
x[i]=xscale*((double)(2*i)/(double)(n-1)-1);
|
|
//--- change values
|
|
y[i]=3*x[i]+MathExp(x[i]);
|
|
w[i]=1+CMath::RandomReal();
|
|
a[i].Set(0,1);
|
|
//--- check
|
|
if(m>1)
|
|
a[i].Set(1,x[i]);
|
|
for(j=2;j<=m-1;j++)
|
|
a[i].Set(j,2*x[i]*a[i][j-1]-a[i][j-2]);
|
|
}
|
|
//--- 1. test weighted fitting (optimality)
|
|
//--- 2. Solve degenerate least squares task built on the basis
|
|
//--- of previous task
|
|
CLSFit::LSFitLinearW(y,w,a,n,m,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
llserrors=true;
|
|
else
|
|
llserrors=llserrors || !IsGLSSolution(n,m,0,y,w,a,cm,c);
|
|
//--- allocation
|
|
a2.Resize(n,2*m);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
a2[i].Set(2*j+0,a[i][j]);
|
|
a2[i].Set(2*j+1,a[i][j]);
|
|
}
|
|
}
|
|
//--- function call
|
|
CLSFit::LSFitLinearW(y,w,a2,n,2*m,info,c2,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
llserrors=true;
|
|
else
|
|
{
|
|
//--- test answer correctness using design matrix properties
|
|
//--- and previous task solution
|
|
for(j=0;j<=m-1;j++)
|
|
llserrors=llserrors || MathAbs(c2[2*j+0]+c2[2*j+1]-c[j])>threshold;
|
|
}
|
|
//--- test non-weighted fitting
|
|
ArrayResize(w2,n);
|
|
for(i=0;i<=n-1;i++)
|
|
w2[i]=1;
|
|
//--- function calls
|
|
CLSFit::LSFitLinearW(y,w2,a,n,m,info,c,rep);
|
|
CLSFit::LSFitLinear(y,a,n,m,info2,c2,rep2);
|
|
//--- check
|
|
if(info<=0 || info2<=0)
|
|
llserrors=true;
|
|
else
|
|
{
|
|
//--- test answer correctness
|
|
for(j=0;j<=m-1;j++)
|
|
llserrors=llserrors || MathAbs(c[j]-c2[j])>threshold;
|
|
//--- search errors
|
|
llserrors=llserrors || MathAbs(rep.m_taskrcond-rep2.m_taskrcond)>threshold;
|
|
}
|
|
//--- test nonlinear fitting on the linear task
|
|
//--- (only non-degenerate tasks are tested)
|
|
//--- and compare with answer from linear fitting subroutine
|
|
if(n>=m)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(c2,m);
|
|
//--- test function/gradient/Hessian-based weighted fitting
|
|
CLSFit::LSFitLinearW(y,w,a,n,m,info,c,rep);
|
|
for(i=0;i<=m-1;i++)
|
|
c2[i]=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
CLSFit::LSFitCreateWF(a,y,w,c2,n,m,m,diffstep,state);
|
|
CLSFit::LSFitSetCond(state,0.0,nlthreshold,0);
|
|
FitLinearNonlinear(m,0,a,state,nlserrors);
|
|
CLSFit::LSFitResults(state,info,c2,rep2);
|
|
//--- check
|
|
if(info<=0)
|
|
nlserrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold;
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
c2[i]=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
CLSFit::LSFitCreateWFG(a,y,w,c2,n,m,m,CMath::RandomReal()>0.5,state);
|
|
CLSFit::LSFitSetCond(state,0.0,nlthreshold,0);
|
|
FitLinearNonlinear(m,1,a,state,nlserrors);
|
|
CLSFit::LSFitResults(state,info,c2,rep2);
|
|
//--- check
|
|
if(info<=0)
|
|
nlserrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold;
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
c2[i]=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
CLSFit::LSFitCreateWFGH(a,y,w,c2,n,m,m,state);
|
|
CLSFit::LSFitSetCond(state,0.0,nlthreshold,0);
|
|
FitLinearNonlinear(m,2,a,state,nlserrors);
|
|
CLSFit::LSFitResults(state,info,c2,rep2);
|
|
//--- check
|
|
if(info<=0)
|
|
nlserrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold;
|
|
}
|
|
//--- test gradient-only or Hessian-based fitting without weights
|
|
CLSFit::LSFitLinear(y,a,n,m,info,c,rep);
|
|
for(i=0;i<=m-1;i++)
|
|
c2[i]=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
CLSFit::LSFitCreateF(a,y,c2,n,m,m,diffstep,state);
|
|
CLSFit::LSFitSetCond(state,0.0,nlthreshold,0);
|
|
FitLinearNonlinear(m,0,a,state,nlserrors);
|
|
CLSFit::LSFitResults(state,info,c2,rep2);
|
|
//--- check
|
|
if(info<=0)
|
|
nlserrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold;
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
c2[i]=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
CLSFit::LSFitCreateFG(a,y,c2,n,m,m,CMath::RandomReal()>0.5,state);
|
|
CLSFit::LSFitSetCond(state,0.0,nlthreshold,0);
|
|
FitLinearNonlinear(m,1,a,state,nlserrors);
|
|
CLSFit::LSFitResults(state,info,c2,rep2);
|
|
//--- check
|
|
if(info<=0)
|
|
nlserrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold;
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=m-1;i++)
|
|
c2[i]=2*CMath::RandomReal()-1;
|
|
//--- function calls
|
|
CLSFit::LSFitCreateFGH(a,y,c2,n,m,m,state);
|
|
CLSFit::LSFitSetCond(state,0.0,nlthreshold,0);
|
|
FitLinearNonlinear(m,2,a,state,nlserrors);
|
|
CLSFit::LSFitResults(state,info,c2,rep2);
|
|
//--- check
|
|
if(info<=0)
|
|
nlserrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
for(i=0;i<=m-1;i++)
|
|
nlserrors=nlserrors || MathAbs(c[i]-c2[i])>100*nlthreshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- test correctness of the RCond field
|
|
a.Resize(n,n);
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
//--- change values
|
|
v1=CMath::m_maxrealnumber;
|
|
v2=CMath::m_minrealnumber;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
x[i]=0.1+0.9*CMath::RandomReal();
|
|
y[i]=0.1+0.9*CMath::RandomReal();
|
|
w[i]=1;
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
{
|
|
a[i].Set(i,0.1+0.9*CMath::RandomReal());
|
|
v1=MathMin(v1,a[i][i]);
|
|
v2=MathMax(v2,a[i][i]);
|
|
}
|
|
else
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- function call
|
|
CLSFit::LSFitLinearW(y,w,a,n,n,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
llserrors=true;
|
|
else
|
|
llserrors=llserrors || MathAbs(rep.m_taskrcond-v1/v2)>threshold;
|
|
}
|
|
//--- Test constrained least squares
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
for(m=1;m<=maxm;m++)
|
|
{
|
|
//--- test for K<>0
|
|
for(k=1;k<=m-1;k++)
|
|
{
|
|
//--- Prepare Chebyshev basis. Its condition number is very good.
|
|
//--- Prepare constraints (random numbers)
|
|
a.Resize(n,m);
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,n);
|
|
ArrayResize(w,n);
|
|
xscale=0.9+0.1*CMath::RandomReal();
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(n==1)
|
|
x[i]=2*CMath::RandomReal()-1;
|
|
else
|
|
x[i]=xscale*((double)(2*i)/(double)(n-1)-1);
|
|
//--- change values
|
|
y[i]=3*x[i]+MathExp(x[i]);
|
|
w[i]=1+CMath::RandomReal();
|
|
a[i].Set(0,1);
|
|
//--- check
|
|
if(m>1)
|
|
a[i].Set(1,x[i]);
|
|
for(j=2;j<=m-1;j++)
|
|
a[i].Set(j,2*x[i]*a[i][j-1]-a[i][j-2]);
|
|
}
|
|
//--- allocation
|
|
cm.Resize(k,m+1);
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
for(j=0;j<=m;j++)
|
|
cm[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- Solve constrained task
|
|
CLSFit::LSFitLinearWC(y,w,a,cm,n,m,k,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
llserrors=true;
|
|
else
|
|
llserrors=llserrors || !IsGLSSolution(n,m,k,y,w,a,cm,c);
|
|
//--- test non-weighted fitting
|
|
ArrayResize(w2,n);
|
|
for(i=0;i<=n-1;i++)
|
|
w2[i]=1;
|
|
//--- function calls
|
|
CLSFit::LSFitLinearWC(y,w2,a,cm,n,m,k,info,c,rep);
|
|
CLSFit::LSFitLinearC(y,a,cm,n,m,k,info2,c2,rep2);
|
|
//--- check
|
|
if(info<=0 || info2<=0)
|
|
llserrors=true;
|
|
else
|
|
{
|
|
//--- test answer correctness
|
|
for(j=0;j<=m-1;j++)
|
|
llserrors=llserrors || MathAbs(c[j]-c2[j])>threshold;
|
|
llserrors=llserrors || MathAbs(rep.m_taskrcond-rep2.m_taskrcond)>threshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- nonlinear task for nonlinear fitting:
|
|
//--- f(X,C)=1/(1+C*X^2),
|
|
//--- C(true)=2.
|
|
n=100;
|
|
ArrayResize(c,1);
|
|
c[0]=1+2*CMath::RandomReal();
|
|
//--- allocation
|
|
a.Resize(n,1);
|
|
ArrayResize(y,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
a[i].Set(0,4*CMath::RandomReal()-2);
|
|
y[i]=1/(1+2*CMath::Sqr(a[i][0]));
|
|
}
|
|
//--- function call
|
|
CLSFit::LSFitCreateFG(a,y,c,n,1,1,true,state);
|
|
CLSFit::LSFitSetCond(state,0.0,nlthreshold,0);
|
|
//--- cycle
|
|
while(CLSFit::LSFitIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf)
|
|
state.m_f=1/(1+state.m_c[0]*CMath::Sqr(state.m_x[0]));
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=1/(1+state.m_c[0]*CMath::Sqr(state.m_x[0]));
|
|
state.m_g[0]=-(CMath::Sqr(state.m_x[0])/CMath::Sqr(1+state.m_c[0]*CMath::Sqr(state.m_x[0])));
|
|
}
|
|
}
|
|
//--- function call
|
|
CLSFit::LSFitResults(state,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
nlserrors=true;
|
|
else
|
|
nlserrors=nlserrors || MathAbs(c[0]-2)>100*nlthreshold;
|
|
//--- solve simple task (fitting by constant function) and check
|
|
//--- correctness of the errors calculated by subroutines
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- test on task with non-zero Yi
|
|
n=4;
|
|
v1=CMath::RandomReal();
|
|
v2=CMath::RandomReal();
|
|
v=1+CMath::RandomReal();
|
|
//--- allocation
|
|
ArrayResize(c,1);
|
|
c[0]=1+2*CMath::RandomReal();
|
|
//--- allocation
|
|
a.Resize(4,1);
|
|
ArrayResize(y,4);
|
|
//--- change values
|
|
a[0].Set(0,1);
|
|
y[0]=v-v2;
|
|
a[1].Set(0,1);
|
|
y[1]=v-v1;
|
|
a[2].Set(0,1);
|
|
y[2]=v+v1;
|
|
a[3].Set(0,1);
|
|
y[3]=v+v2;
|
|
refrms=MathSqrt((CMath::Sqr(v1)+CMath::Sqr(v2))/2);
|
|
refavg=(MathAbs(v1)+MathAbs(v2))/2;
|
|
refavgrel=0.25*(MathAbs(v2)/MathAbs(v-v2)+MathAbs(v1)/MathAbs(v-v1)+MathAbs(v1)/MathAbs(v+v1)+MathAbs(v2)/MathAbs(v+v2));
|
|
refmax=MathMax(v1,v2);
|
|
//--- Test LLS
|
|
CLSFit::LSFitLinear(y,a,4,1,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
llserrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
llserrors=llserrors || MathAbs(c[0]-v)>threshold;
|
|
llserrors=llserrors || MathAbs(rep.m_rmserror-refrms)>threshold;
|
|
llserrors=llserrors || MathAbs(rep.m_avgerror-refavg)>threshold;
|
|
llserrors=llserrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
|
|
llserrors=llserrors || MathAbs(rep.m_maxerror-refmax)>threshold;
|
|
}
|
|
//--- Test NLS
|
|
CLSFit::LSFitCreateFG(a,y,c,4,1,1,true,state);
|
|
CLSFit::LSFitSetCond(state,0.0,nlthreshold,0);
|
|
//--- cycle
|
|
while(CLSFit::LSFitIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf)
|
|
state.m_f=state.m_c[0];
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
state.m_f=state.m_c[0];
|
|
state.m_g[0]=1;
|
|
}
|
|
}
|
|
//--- function call
|
|
CLSFit::LSFitResults(state,info,c,rep);
|
|
//--- check
|
|
if(info<=0)
|
|
nlserrors=true;
|
|
else
|
|
{
|
|
//--- search errors
|
|
nlserrors=nlserrors || MathAbs(c[0]-v)>threshold;
|
|
nlserrors=nlserrors || MathAbs(rep.m_rmserror-refrms)>threshold;
|
|
nlserrors=nlserrors || MathAbs(rep.m_avgerror-refavg)>threshold;
|
|
nlserrors=nlserrors || MathAbs(rep.m_avgrelerror-refavgrel)>threshold;
|
|
nlserrors=nlserrors || MathAbs(rep.m_maxerror-refmax)>threshold;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests whether C is solution of (possibly) constrained LLS problem|
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestLSFitUnit::IsGLSSolution(const int n,const int m,const int k,
|
|
double &y[],double &w[],CMatrixDouble &fmatrix,
|
|
CMatrixDouble &cmatrix,double &cc[])
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double s1=0;
|
|
double s2=0;
|
|
double s3=0;
|
|
double delta=0;
|
|
double threshold=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double c[];
|
|
double c2[];
|
|
double sv[];
|
|
double deltac[];
|
|
double deltaproj[];
|
|
//--- create matrix
|
|
CMatrixDouble u;
|
|
CMatrixDouble vt;
|
|
//--- copy array
|
|
ArrayCopy(c,cc);
|
|
//--- Setup.
|
|
//--- Threshold is small because CMatrix may be ill-conditioned
|
|
delta=0.001;
|
|
threshold=MathSqrt(CMath::m_machineepsilon);
|
|
//--- allocation
|
|
ArrayResize(c2,m);
|
|
ArrayResize(deltac,m);
|
|
ArrayResize(deltaproj,m);
|
|
//--- test whether C is feasible point or not (projC must be close to C)
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=cmatrix[i][i_]*c[i_];
|
|
//--- check
|
|
if(MathAbs(v-cmatrix[i][m])>threshold)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
}
|
|
//--- find orthogonal basis of Null(CMatrix) (stored in rows from K to M-1)
|
|
if(k>0)
|
|
CSingValueDecompose::RMatrixSVD(cmatrix,k,m,0,2,2,sv,u,vt);
|
|
//--- Test result
|
|
result=true;
|
|
s1=GetGLSError(n,m,y,w,fmatrix,c);
|
|
//--- calculation
|
|
for(j=0;j<=m-1;j++)
|
|
{
|
|
//--- prepare modification of C which leave us in the feasible set.
|
|
//--- let deltaC be increment on Jth coordinate,then project
|
|
//--- deltaC in the Null(CMatrix) and store result in DeltaProj
|
|
for(i_=0;i_<=m-1;i_++)
|
|
c2[i_]=c[i_];
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
deltac[i]=delta;
|
|
else
|
|
deltac[i]=0;
|
|
}
|
|
//--- check
|
|
if(k==0)
|
|
{
|
|
for(i_=0;i_<=m-1;i_++)
|
|
deltaproj[i_]=deltac[i_];
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
deltaproj[i]=0;
|
|
for(i=k;i<=m-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=vt[i][i_]*deltac[i_];
|
|
for(i_=0;i_<=m-1;i_++)
|
|
deltaproj[i_]=deltaproj[i_]+v*vt[i][i_];
|
|
}
|
|
}
|
|
//--- now we have DeltaProj such that if C is feasible,
|
|
//--- then C+DeltaProj is feasible too
|
|
for(i_=0;i_<=m-1;i_++)
|
|
c2[i_]=c[i_];
|
|
for(i_=0;i_<=m-1;i_++)
|
|
c2[i_]=c2[i_]+deltaproj[i_];
|
|
s2=GetGLSError(n,m,y,w,fmatrix,c2);
|
|
for(i_=0;i_<=m-1;i_++)
|
|
c2[i_]=c[i_];
|
|
for(i_=0;i_<=m-1;i_++)
|
|
c2[i_]=c2[i_]-deltaproj[i_];
|
|
s3=GetGLSError(n,m,y,w,fmatrix,c2);
|
|
result=(result && s2>=(double)(s1/(1+threshold))) && s3>=(double)(s1/(1+threshold));
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests whether C is solution of LLS problem |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestLSFitUnit::GetGLSError(const int n,const int m,double &y[],
|
|
double &w[],CMatrixDouble &fmatrix,double &c[])
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=fmatrix[i][i_]*c[i_];
|
|
result=result+CMath::Sqr(w[i]*(v-y[i]));
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Subroutine for nonlinear fitting of linear problem |
|
|
//| DerAvailable: |
|
|
//| * 0 when only function value should be used |
|
|
//| * 1 when we can provide gradient/function |
|
|
//| * 2 when we can provide Hessian/gradient/function |
|
|
//| When something which is not permitted by DerAvailable is |
|
|
//| requested, this function sets NLSErrors to True. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestLSFitUnit::FitLinearNonlinear(const int m,const int deravailable,
|
|
CMatrixDouble &xy,CLSFitState &state,
|
|
bool &nlserrors)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- cycle
|
|
while(CLSFit::LSFitIteration(state))
|
|
{
|
|
//--- assume that one and only one of flags is set
|
|
//--- test that we didn't request hessian in hessian-free setting
|
|
if(deravailable<1 && state.m_needfg)
|
|
nlserrors=true;
|
|
//--- check
|
|
if(deravailable<2 && state.m_needfgh)
|
|
nlserrors=true;
|
|
i=0;
|
|
//--- check
|
|
if(state.m_needf)
|
|
i=i+1;
|
|
//--- check
|
|
if(state.m_needfg)
|
|
i=i+1;
|
|
//--- check
|
|
if(state.m_needfgh)
|
|
i=i+1;
|
|
//--- check
|
|
if(i!=1)
|
|
nlserrors=true;
|
|
//--- test that PointIndex is consistent with actual point passed
|
|
for(i=0;i<=m-1;i++)
|
|
nlserrors=nlserrors || xy[state.m_pointindex][i]!=state.m_x[i];
|
|
//--- calculate
|
|
if(state.m_needf)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=state.m_x[i_]*state.m_c[i_];
|
|
state.m_f=v;
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(state.m_needfg)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=state.m_x[i_]*state.m_c[i_];
|
|
state.m_f=v;
|
|
//--- copy
|
|
for(i_=0;i_<=m-1;i_++)
|
|
state.m_g[i_]=state.m_x[i_];
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(state.m_needfgh)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=state.m_x[i_]*state.m_c[i_];
|
|
state.m_f=v;
|
|
//--- copy
|
|
for(i_=0;i_<=m-1;i_++)
|
|
state.m_g[i_]=state.m_x[i_];
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=m-1;j++)
|
|
state.m_h[i].Set(j,0);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CPSpline |
|
|
//+------------------------------------------------------------------+
|
|
class CTestPSplineUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void UnsetP2(CPSpline2Interpolant &p);
|
|
static void UnsetP3(CPSpline3Interpolant &p);
|
|
static void Unset1D(double &x[]);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestPSplineUnit(void);
|
|
~CTestPSplineUnit(void);
|
|
//--- public method
|
|
static bool TestPSpline(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestPSplineUnit::CTestPSplineUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestPSplineUnit::~CTestPSplineUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CPSpline |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestPSplineUnit::TestPSpline(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool p2errors;
|
|
bool p3errors;
|
|
double nonstrictthreshold=0;
|
|
double threshold=0;
|
|
int passcount=0;
|
|
double lstep=0;
|
|
double h=0;
|
|
int maxn=0;
|
|
int periodicity=0;
|
|
int skind=0;
|
|
int pkind=0;
|
|
bool periodic;
|
|
double a=0;
|
|
double b=0;
|
|
int n=0;
|
|
int tmpn=0;
|
|
int i=0;
|
|
double vx=0;
|
|
double vy=0;
|
|
double vz=0;
|
|
double vx2=0;
|
|
double vy2=0;
|
|
double vz2=0;
|
|
double vdx=0;
|
|
double vdy=0;
|
|
double vdz=0;
|
|
double vdx2=0;
|
|
double vdy2=0;
|
|
double vdz2=0;
|
|
double vd2x=0;
|
|
double vd2y=0;
|
|
double vd2z=0;
|
|
double vd2x2=0;
|
|
double vd2y2=0;
|
|
double vd2z2=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double z[];
|
|
double t[];
|
|
double t2[];
|
|
double t3[];
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
CMatrixDouble xyz;
|
|
//--- objects of classes
|
|
CPSpline2Interpolant p2;
|
|
CPSpline3Interpolant p3;
|
|
CSpline1DInterpolant s;
|
|
//--- initialization
|
|
waserrors=false;
|
|
passcount=20;
|
|
lstep=0.005;
|
|
h=0.00001;
|
|
maxn=10;
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
nonstrictthreshold=0.00001;
|
|
p2errors=false;
|
|
p3errors=false;
|
|
//--- Test basic properties of 2- and 3-dimensional splines:
|
|
//--- * PSpline2ParameterValues() properties
|
|
//--- * values at nodes
|
|
//--- * for periodic splines - periodicity properties
|
|
//--- Variables used:
|
|
//--- * N points count
|
|
//--- * SKind spline
|
|
//--- * PKind parameterization
|
|
//--- * Periodicity whether we have periodic spline or not
|
|
for(n=2;n<=maxn;n++)
|
|
{
|
|
for(skind=0;skind<=2;skind++)
|
|
{
|
|
for(pkind=0;pkind<=2;pkind++)
|
|
{
|
|
for(periodicity=0;periodicity<=1;periodicity++)
|
|
{
|
|
periodic=periodicity==1;
|
|
//--- skip unsupported combinations of parameters
|
|
if(periodic && n<3)
|
|
continue;
|
|
//--- check
|
|
if(periodic && skind==0)
|
|
continue;
|
|
//--- check
|
|
if(n<5 && skind==0)
|
|
continue;
|
|
//--- init
|
|
xy.Resize(n,2);
|
|
xyz.Resize(n,3);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DEquidist(-1,1,n,t2,x);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xy[i_].Set(0,x[i_]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xyz[i_].Set(0,x[i_]);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DEquidist(-1,1,n,t2,y);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xy[i_].Set(1,y[i_]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xyz[i_].Set(1,y[i_]);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DEquidist(-1,1,n,t2,z);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xyz[i_].Set(2,z[i_]);
|
|
//--- function calls
|
|
UnsetP2(p2);
|
|
UnsetP3(p3);
|
|
//--- check
|
|
if(periodic)
|
|
{
|
|
CPSpline::PSpline2BuildPeriodic(xy,n,skind,pkind,p2);
|
|
CPSpline::PSpline3BuildPeriodic(xyz,n,skind,pkind,p3);
|
|
}
|
|
else
|
|
{
|
|
CPSpline::PSpline2Build(xy,n,skind,pkind,p2);
|
|
CPSpline::PSpline3Build(xyz,n,skind,pkind,p3);
|
|
}
|
|
//--- PSpline2ParameterValues() properties
|
|
CPSpline::PSpline2ParameterValues(p2,tmpn,t2);
|
|
//--- check
|
|
if(tmpn!=n)
|
|
{
|
|
p2errors=true;
|
|
continue;
|
|
}
|
|
//--- function call
|
|
CPSpline::PSpline3ParameterValues(p3,tmpn,t3);
|
|
//--- check
|
|
if(tmpn!=n)
|
|
{
|
|
p3errors=true;
|
|
continue;
|
|
}
|
|
//--- search errors
|
|
p2errors=p2errors || t2[0]!=0.0;
|
|
p3errors=p3errors || t3[0]!=0.0;
|
|
for(i=1;i<=n-1;i++)
|
|
{
|
|
p2errors=p2errors || t2[i]<=t2[i-1];
|
|
p3errors=p3errors || t3[i]<=t3[i-1];
|
|
}
|
|
//--- check
|
|
if(periodic)
|
|
{
|
|
p2errors=p2errors || t2[n-1]>=1.0;
|
|
p3errors=p3errors || t3[n-1]>=1.0;
|
|
}
|
|
else
|
|
{
|
|
p2errors=p2errors || t2[n-1]!=1.0;
|
|
p3errors=p3errors || t3[n-1]!=1.0;
|
|
}
|
|
//--- Now we have parameter values stored at T,
|
|
//--- and want to test whether the actully correspond to
|
|
//--- points
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- 2-dimensional test
|
|
CPSpline::PSpline2Calc(p2,t2[i],vx,vy);
|
|
p2errors=p2errors || MathAbs(vx-x[i])>threshold;
|
|
p2errors=p2errors || MathAbs(vy-y[i])>threshold;
|
|
//--- 3-dimensional test
|
|
CPSpline::PSpline3Calc(p3,t3[i],vx,vy,vz);
|
|
p3errors=p3errors || MathAbs(vx-x[i])>threshold;
|
|
p3errors=p3errors || MathAbs(vy-y[i])>threshold;
|
|
p3errors=p3errors || MathAbs(vz-z[i])>threshold;
|
|
}
|
|
//--- Test periodicity (if needed)
|
|
if(periodic)
|
|
{
|
|
//--- periodicity at nodes
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- 2-dimensional test
|
|
CPSpline::PSpline2Calc(p2,t2[i]+CMath::RandomInteger(10)-5,vx,vy);
|
|
//--- search errors
|
|
p2errors=p2errors || MathAbs(vx-x[i])>threshold;
|
|
p2errors=p2errors || MathAbs(vy-y[i])>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline2Diff(p2,t2[i]+CMath::RandomInteger(10)-5,vx,vdx,vy,vdy);
|
|
//--- search errors
|
|
p2errors=p2errors || MathAbs(vx-x[i])>threshold;
|
|
p2errors=p2errors || MathAbs(vy-y[i])>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline2Diff2(p2,t2[i]+CMath::RandomInteger(10)-5,vx,vdx,vd2x,vy,vdy,vd2y);
|
|
//--- search errors
|
|
p2errors=p2errors || MathAbs(vx-x[i])>threshold;
|
|
p2errors=p2errors || MathAbs(vy-y[i])>threshold;
|
|
//--- 3-dimensional test
|
|
CPSpline::PSpline3Calc(p3,t3[i]+CMath::RandomInteger(10)-5,vx,vy,vz);
|
|
//--- search errors
|
|
p3errors=p3errors || MathAbs(vx-x[i])>threshold;
|
|
p3errors=p3errors || MathAbs(vy-y[i])>threshold;
|
|
p3errors=p3errors || MathAbs(vz-z[i])>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline3Diff(p3,t3[i]+CMath::RandomInteger(10)-5,vx,vdx,vy,vdy,vz,vdz);
|
|
//--- search errors
|
|
p3errors=p3errors || MathAbs(vx-x[i])>threshold;
|
|
p3errors=p3errors || MathAbs(vy-y[i])>threshold;
|
|
p3errors=p3errors || MathAbs(vz-z[i])>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline3Diff2(p3,t3[i]+CMath::RandomInteger(10)-5,vx,vdx,vd2x,vy,vdy,vd2y,vz,vdz,vd2z);
|
|
//--- search errors
|
|
p3errors=p3errors || MathAbs(vx-x[i])>threshold;
|
|
p3errors=p3errors || MathAbs(vy-y[i])>threshold;
|
|
p3errors=p3errors || MathAbs(vz-z[i])>threshold;
|
|
}
|
|
//--- periodicity between nodes
|
|
v0=CMath::RandomReal();
|
|
CPSpline::PSpline2Calc(p2,v0,vx,vy);
|
|
CPSpline::PSpline2Calc(p2,v0+CMath::RandomInteger(10)-5,vx2,vy2);
|
|
//--- search errors
|
|
p2errors=p2errors || MathAbs(vx-vx2)>threshold;
|
|
p2errors=p2errors || MathAbs(vy-vy2)>threshold;
|
|
//--- function calls
|
|
CPSpline::PSpline3Calc(p3,v0,vx,vy,vz);
|
|
CPSpline::PSpline3Calc(p3,v0+CMath::RandomInteger(10)-5,vx2,vy2,vz2);
|
|
//--- search errors
|
|
p3errors=p3errors || MathAbs(vx-vx2)>threshold;
|
|
p3errors=p3errors || MathAbs(vy-vy2)>threshold;
|
|
p3errors=p3errors || MathAbs(vz-vz2)>threshold;
|
|
//--- near-boundary test for continuity of function values and derivatives:
|
|
//--- 2-dimensional curve
|
|
if(!CAp::Assert(skind==1 || skind==2,"TEST: unexpected spline type!"))
|
|
return(false);
|
|
//--- change values
|
|
v0=100*CMath::m_machineepsilon;
|
|
v1=1-v0;
|
|
//--- function calls
|
|
CPSpline::PSpline2Calc(p2,v0,vx,vy);
|
|
CPSpline::PSpline2Calc(p2,v1,vx2,vy2);
|
|
//--- search errors
|
|
p2errors=p2errors || MathAbs(vx-vx2)>threshold;
|
|
p2errors=p2errors || MathAbs(vy-vy2)>threshold;
|
|
//--- function calls
|
|
CPSpline::PSpline2Diff(p2,v0,vx,vdx,vy,vdy);
|
|
CPSpline::PSpline2Diff(p2,v1,vx2,vdx2,vy2,vdy2);
|
|
//--- search errors
|
|
p2errors=p2errors || MathAbs(vx-vx2)>threshold;
|
|
p2errors=p2errors || MathAbs(vy-vy2)>threshold;
|
|
p2errors=p2errors || MathAbs(vdx-vdx2)>nonstrictthreshold;
|
|
p2errors=p2errors || MathAbs(vdy-vdy2)>nonstrictthreshold;
|
|
//--- function calls
|
|
CPSpline::PSpline2Diff2(p2,v0,vx,vdx,vd2x,vy,vdy,vd2y);
|
|
CPSpline::PSpline2Diff2(p2,v1,vx2,vdx2,vd2x2,vy2,vdy2,vd2y2);
|
|
//--- search errors
|
|
p2errors=p2errors || MathAbs(vx-vx2)>threshold;
|
|
p2errors=p2errors || MathAbs(vy-vy2)>threshold;
|
|
p2errors=p2errors || MathAbs(vdx-vdx2)>nonstrictthreshold;
|
|
p2errors=p2errors || MathAbs(vdy-vdy2)>nonstrictthreshold;
|
|
//--- check
|
|
if(skind==2)
|
|
{
|
|
//--- second derivative test only for cubic splines
|
|
p2errors=p2errors || MathAbs(vd2x-vd2x2)>nonstrictthreshold;
|
|
p2errors=p2errors || MathAbs(vd2y-vd2y2)>nonstrictthreshold;
|
|
}
|
|
//--- near-boundary test for continuity of function values and derivatives:
|
|
//--- 3-dimensional curve
|
|
if(!CAp::Assert(skind==1 || skind==2,"TEST: unexpected spline type!"))
|
|
return(false);
|
|
//--- change values
|
|
v0=100*CMath::m_machineepsilon;
|
|
v1=1-v0;
|
|
//--- function calls
|
|
CPSpline::PSpline3Calc(p3,v0,vx,vy,vz);
|
|
CPSpline::PSpline3Calc(p3,v1,vx2,vy2,vz2);
|
|
//--- search errors
|
|
p3errors=p3errors || MathAbs(vx-vx2)>threshold;
|
|
p3errors=p3errors || MathAbs(vy-vy2)>threshold;
|
|
p3errors=p3errors || MathAbs(vz-vz2)>threshold;
|
|
//--- function calls
|
|
CPSpline::PSpline3Diff(p3,v0,vx,vdx,vy,vdy,vz,vdz);
|
|
CPSpline::PSpline3Diff(p3,v1,vx2,vdx2,vy2,vdy2,vz2,vdz2);
|
|
//--- search errors
|
|
p3errors=p3errors || MathAbs(vx-vx2)>threshold;
|
|
p3errors=p3errors || MathAbs(vy-vy2)>threshold;
|
|
p3errors=p3errors || MathAbs(vz-vz2)>threshold;
|
|
p3errors=p3errors || MathAbs(vdx-vdx2)>nonstrictthreshold;
|
|
p3errors=p3errors || MathAbs(vdy-vdy2)>nonstrictthreshold;
|
|
p3errors=p3errors || MathAbs(vdz-vdz2)>nonstrictthreshold;
|
|
//--- function calls
|
|
CPSpline::PSpline3Diff2(p3,v0,vx,vdx,vd2x,vy,vdy,vd2y,vz,vdz,vd2z);
|
|
CPSpline::PSpline3Diff2(p3,v1,vx2,vdx2,vd2x2,vy2,vdy2,vd2y2,vz2,vdz2,vd2z2);
|
|
//--- search errors
|
|
p3errors=p3errors || MathAbs(vx-vx2)>threshold;
|
|
p3errors=p3errors || MathAbs(vy-vy2)>threshold;
|
|
p3errors=p3errors || MathAbs(vz-vz2)>threshold;
|
|
p3errors=p3errors || MathAbs(vdx-vdx2)>nonstrictthreshold;
|
|
p3errors=p3errors || MathAbs(vdy-vdy2)>nonstrictthreshold;
|
|
p3errors=p3errors || MathAbs(vdz-vdz2)>nonstrictthreshold;
|
|
//--- check
|
|
if(skind==2)
|
|
{
|
|
//--- second derivative test only for cubic splines
|
|
p3errors=p3errors || MathAbs(vd2x-vd2x2)>nonstrictthreshold;
|
|
p3errors=p3errors || MathAbs(vd2y-vd2y2)>nonstrictthreshold;
|
|
p3errors=p3errors || MathAbs(vd2z-vd2z2)>nonstrictthreshold;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- Test differentiation,tangents,calculation between nodes.
|
|
//--- Because differentiation is done in parameterization/spline/periodicity
|
|
//--- oblivious manner,we don't have to test all possible combinations
|
|
//--- of spline types and parameterizations.
|
|
//--- Actually we test special combination with properties which allow us
|
|
//--- to easily solve this problem:
|
|
//--- * 2 (3) variables
|
|
//--- * first variable is sampled from equidistant grid on [0][1]
|
|
//--- * other variables are random
|
|
//--- * uniform parameterization is used
|
|
//--- * periodicity - none
|
|
//--- * spline type - any (we use cubic splines)
|
|
//--- Same problem allows us to test calculation BETWEEN nodes.
|
|
for(n=2;n<=maxn;n++)
|
|
{
|
|
//--- init
|
|
xy.Resize(n,2);
|
|
xyz.Resize(n,3);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DEquidist(0,1,n,t,x);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xy[i_].Set(0,x[i_]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xyz[i_].Set(0,x[i_]);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DEquidist(0,1,n,t,y);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xy[i_].Set(1,y[i_]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xyz[i_].Set(1,y[i_]);
|
|
//--- function call
|
|
CApServ::TaskGenInt1DEquidist(0,1,n,t,z);
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xyz[i_].Set(2,z[i_]);
|
|
//--- function call
|
|
UnsetP2(p2);
|
|
UnsetP3(p3);
|
|
CPSpline::PSpline2Build(xy,n,2,0,p2);
|
|
CPSpline::PSpline3Build(xyz,n,2,0,p3);
|
|
//--- Test 2D/3D spline:
|
|
//--- * build non-parametric cubic spline from T and X/Y
|
|
//--- * calculate its value and derivatives at V0
|
|
//--- * compare with Spline2Calc/Spline2Diff/Spline2Diff2
|
|
//--- Because of task properties both variants should
|
|
//--- return same answer.
|
|
v0=CMath::RandomReal();
|
|
CSpline1D::Spline1DBuildCubic(t,x,n,0,0.0,0,0.0,s);
|
|
CSpline1D::Spline1DDiff(s,v0,vx2,vdx2,vd2x2);
|
|
CSpline1D::Spline1DBuildCubic(t,y,n,0,0.0,0,0.0,s);
|
|
CSpline1D::Spline1DDiff(s,v0,vy2,vdy2,vd2y2);
|
|
CSpline1D::Spline1DBuildCubic(t,z,n,0,0.0,0,0.0,s);
|
|
CSpline1D::Spline1DDiff(s,v0,vz2,vdz2,vd2z2);
|
|
//--- 2D test
|
|
CPSpline::PSpline2Calc(p2,v0,vx,vy);
|
|
//--- search errors
|
|
p2errors=p2errors || MathAbs(vx-vx2)>threshold;
|
|
p2errors=p2errors || MathAbs(vy-vy2)>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline2Diff(p2,v0,vx,vdx,vy,vdy);
|
|
//--- search errors
|
|
p2errors=p2errors || MathAbs(vx-vx2)>threshold;
|
|
p2errors=p2errors || MathAbs(vy-vy2)>threshold;
|
|
p2errors=p2errors || MathAbs(vdx-vdx2)>threshold;
|
|
p2errors=p2errors || MathAbs(vdy-vdy2)>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline2Diff2(p2,v0,vx,vdx,vd2x,vy,vdy,vd2y);
|
|
//--- search errors
|
|
p2errors=p2errors || MathAbs(vx-vx2)>threshold;
|
|
p2errors=p2errors || MathAbs(vy-vy2)>threshold;
|
|
p2errors=p2errors || MathAbs(vdx-vdx2)>threshold;
|
|
p2errors=p2errors || MathAbs(vdy-vdy2)>threshold;
|
|
p2errors=p2errors || MathAbs(vd2x-vd2x2)>threshold;
|
|
p2errors=p2errors || MathAbs(vd2y-vd2y2)>threshold;
|
|
//--- 3D test
|
|
CPSpline::PSpline3Calc(p3,v0,vx,vy,vz);
|
|
//--- search errors
|
|
p3errors=p3errors || MathAbs(vx-vx2)>threshold;
|
|
p3errors=p3errors || MathAbs(vy-vy2)>threshold;
|
|
p3errors=p3errors || MathAbs(vz-vz2)>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline3Diff(p3,v0,vx,vdx,vy,vdy,vz,vdz);
|
|
//--- search errors
|
|
p3errors=p3errors || MathAbs(vx-vx2)>threshold;
|
|
p3errors=p3errors || MathAbs(vy-vy2)>threshold;
|
|
p3errors=p3errors || MathAbs(vz-vz2)>threshold;
|
|
p3errors=p3errors || MathAbs(vdx-vdx2)>threshold;
|
|
p3errors=p3errors || MathAbs(vdy-vdy2)>threshold;
|
|
p3errors=p3errors || MathAbs(vdz-vdz2)>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline3Diff2(p3,v0,vx,vdx,vd2x,vy,vdy,vd2y,vz,vdz,vd2z);
|
|
//--- search errors
|
|
p3errors=p3errors || MathAbs(vx-vx2)>threshold;
|
|
p3errors=p3errors || MathAbs(vy-vy2)>threshold;
|
|
p3errors=p3errors || MathAbs(vz-vz2)>threshold;
|
|
p3errors=p3errors || MathAbs(vdx-vdx2)>threshold;
|
|
p3errors=p3errors || MathAbs(vdy-vdy2)>threshold;
|
|
p3errors=p3errors || MathAbs(vdz-vdz2)>threshold;
|
|
p3errors=p3errors || MathAbs(vd2x-vd2x2)>threshold;
|
|
p3errors=p3errors || MathAbs(vd2y-vd2y2)>threshold;
|
|
p3errors=p3errors || MathAbs(vd2z-vd2z2)>threshold;
|
|
//--- Test tangents for 2D/3D
|
|
CPSpline::PSpline2Tangent(p2,v0,vx,vy);
|
|
//--- search errors
|
|
p2errors=p2errors || MathAbs(vx-vdx2/CApServ::SafePythag2(vdx2,vdy2))>threshold;
|
|
p2errors=p2errors || MathAbs(vy-vdy2/CApServ::SafePythag2(vdx2,vdy2))>threshold;
|
|
//--- function call
|
|
CPSpline::PSpline3Tangent(p3,v0,vx,vy,vz);
|
|
//--- search errors
|
|
p3errors=p3errors || MathAbs(vx-vdx2/CApServ::SafePythag3(vdx2,vdy2,vdz2))>threshold;
|
|
p3errors=p3errors || MathAbs(vy-vdy2/CApServ::SafePythag3(vdx2,vdy2,vdz2))>threshold;
|
|
p3errors=p3errors || MathAbs(vz-vdz2/CApServ::SafePythag3(vdx2,vdy2,vdz2))>threshold;
|
|
}
|
|
//--- Arc length test.
|
|
//--- Simple problem with easy solution (points on a straight line with
|
|
//--- uniform parameterization).
|
|
for(n=2;n<=maxn;n++)
|
|
{
|
|
//--- allocation
|
|
xy.Resize(n,2);
|
|
xyz.Resize(n,3);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
xy[i].Set(0,i);
|
|
xy[i].Set(1,i);
|
|
xyz[i].Set(0,i);
|
|
xyz[i].Set(1,i);
|
|
xyz[i].Set(2,i);
|
|
}
|
|
//--- function calls
|
|
CPSpline::PSpline2Build(xy,n,1,0,p2);
|
|
CPSpline::PSpline3Build(xyz,n,1,0,p3);
|
|
a=CMath::RandomReal();
|
|
b=CMath::RandomReal();
|
|
//--- search errors
|
|
p2errors=p2errors || MathAbs(CPSpline::PSpline2ArcLength(p2,a,b)-(b-a)*MathSqrt(2)*(n-1))>nonstrictthreshold;
|
|
p3errors=p3errors || MathAbs(CPSpline::PSpline3ArcLength(p3,a,b)-(b-a)*MathSqrt(3)*(n-1))>nonstrictthreshold;
|
|
}
|
|
//--- report
|
|
waserrors=p2errors || p3errors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING SPLINE INTERPOLATION");
|
|
//--- Normal tests
|
|
Print("2D TEST: ");
|
|
//--- check
|
|
if(p2errors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("3D TEST: ");
|
|
//--- check
|
|
if(p3errors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- end
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unset spline, i.e. initialize it with random garbage |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestPSplineUnit::UnsetP2(CPSpline2Interpolant &p)
|
|
{
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- allocation
|
|
xy.Resize(2,2);
|
|
//--- initialization
|
|
xy[0].Set(0,-1);
|
|
xy[0].Set(1,-1);
|
|
xy[1].Set(0,1);
|
|
xy[1].Set(1,1);
|
|
//--- function call
|
|
CPSpline::PSpline2Build(xy,2,1,0,p);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unset spline, i.e. initialize it with random garbage |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestPSplineUnit::UnsetP3(CPSpline3Interpolant &p)
|
|
{
|
|
//--- create matrix
|
|
CMatrixDouble xy;
|
|
//--- allocation
|
|
xy.Resize(2,3);
|
|
//--- initialization
|
|
xy[0].Set(0,-1);
|
|
xy[0].Set(1,-1);
|
|
xy[0].Set(2,-1);
|
|
xy[1].Set(0,1);
|
|
xy[1].Set(1,1);
|
|
xy[1].Set(2,1);
|
|
//--- function call
|
|
CPSpline::PSpline3Build(xy,2,1,0,p);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unsets real vector |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestPSplineUnit::Unset1D(double &x[])
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,1);
|
|
//--- change value
|
|
x[0]=2*CMath::RandomReal()-1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSpline2D |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSpline2DUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestSpline2DUnit(void);
|
|
~CTestSpline2DUnit(void);
|
|
//--- public methods
|
|
static bool TestSpline2D(const bool silent);
|
|
static void LConst(CSpline2DInterpolant &c,double &lx[],double &ly[],const int m,const int n,const double lstep,double &lc,double &lcx,double &lcy,double &lcxy);
|
|
static void TwodNumder(CSpline2DInterpolant &c,const double x,const double y,const double h,double &f,double &fx,double &fy,double &fxy);
|
|
static bool TestUnpack(CSpline2DInterpolant &c,double &lx[],double &ly[]);
|
|
static bool TestLinTrans(CSpline2DInterpolant &c,const double ax,const double bx,const double ay,const double by);
|
|
static void UnsetSpline2D(CSpline2DInterpolant &c);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestSpline2DUnit::CTestSpline2DUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestSpline2DUnit::~CTestSpline2DUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSpline2D |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestSpline2DUnit::TestSpline2D(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool blerrors;
|
|
bool bcerrors;
|
|
bool dserrors;
|
|
bool cperrors;
|
|
bool uperrors;
|
|
bool lterrors;
|
|
bool syerrors;
|
|
bool rlerrors;
|
|
bool rcerrors;
|
|
int pass=0;
|
|
int passcount=0;
|
|
int jobtype=0;
|
|
double lstep=0;
|
|
double h=0;
|
|
double ax=0;
|
|
double ay=0;
|
|
double bx=0;
|
|
double by=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int m=0;
|
|
int n2=0;
|
|
int m2=0;
|
|
double err=0;
|
|
double t=0;
|
|
double t1=0;
|
|
double t2=0;
|
|
double l1=0;
|
|
double l1x=0;
|
|
double l1y=0;
|
|
double l1xy=0;
|
|
double l2=0;
|
|
double l2x=0;
|
|
double l2y=0;
|
|
double l2xy=0;
|
|
double fm=0;
|
|
double f1=0;
|
|
double f2=0;
|
|
double f3=0;
|
|
double f4=0;
|
|
double v1=0;
|
|
double v1x=0;
|
|
double v1y=0;
|
|
double v1xy=0;
|
|
double v2=0;
|
|
double v2x=0;
|
|
double v2y=0;
|
|
double v2xy=0;
|
|
double mf=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
double lx[];
|
|
double ly[];
|
|
//--- create matrix
|
|
CMatrixDouble f;
|
|
CMatrixDouble fr;
|
|
CMatrixDouble ft;
|
|
//--- objects of classes
|
|
CSpline2DInterpolant c;
|
|
CSpline2DInterpolant c2;
|
|
//--- initialization
|
|
waserrors=false;
|
|
passcount=10;
|
|
h=0.00001;
|
|
lstep=0.001;
|
|
blerrors=false;
|
|
bcerrors=false;
|
|
dserrors=false;
|
|
cperrors=false;
|
|
uperrors=false;
|
|
lterrors=false;
|
|
syerrors=false;
|
|
rlerrors=false;
|
|
rcerrors=false;
|
|
//--- Test: bilinear,bicubic
|
|
for(n=2;n<=7;n++)
|
|
{
|
|
for(m=2;m<=7;m++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,m);
|
|
ArrayResize(lx,2*n-2+1);
|
|
ArrayResize(ly,2*m-2+1);
|
|
f.Resize(m,n);
|
|
//--- calculation
|
|
ft.Resize(n,m);
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Prepare task:
|
|
//--- * X and Y stores grid
|
|
//--- * F stores function values
|
|
//--- * LX and LY stores twice dense grid (for Lipschitz testing)
|
|
ax=-1-CMath::RandomReal();
|
|
bx=1+CMath::RandomReal();
|
|
ay=-1-CMath::RandomReal();
|
|
by=1+CMath::RandomReal();
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
x[j]=0.5*(bx+ax)-0.5*(bx-ax)*MathCos(M_PI*(2*j+1)/(2*n));
|
|
//--- check
|
|
if(j==0)
|
|
x[j]=ax;
|
|
//--- check
|
|
if(j==n-1)
|
|
x[j]=bx;
|
|
lx[2*j]=x[j];
|
|
//--- check
|
|
if(j>0)
|
|
lx[2*j-1]=0.5*(x[j]+x[j-1]);
|
|
}
|
|
//--- swap
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
k=CMath::RandomInteger(n);
|
|
//--- check
|
|
if(k!=j)
|
|
{
|
|
t=x[j];
|
|
x[j]=x[k];
|
|
x[k]=t;
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
y[i]=0.5*(by+ay)-0.5*(by-ay)*MathCos(M_PI*(2*i+1)/(2*m));
|
|
//--- check
|
|
if(i==0)
|
|
y[i]=ay;
|
|
//--- check
|
|
if(i==m-1)
|
|
y[i]=by;
|
|
ly[2*i]=y[i];
|
|
//--- check
|
|
if(i>0)
|
|
ly[2*i-1]=0.5*(y[i]+y[i-1]);
|
|
}
|
|
//--- swap
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
k=CMath::RandomInteger(m);
|
|
//--- check
|
|
if(k!=i)
|
|
{
|
|
t=y[i];
|
|
y[i]=y[k];
|
|
y[k]=t;
|
|
}
|
|
}
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
f[i].Set(j,MathExp(0.6*x[j])-MathExp(-(0.3*y[i])+0.08*x[j])+2*MathCos(M_PI*(x[j]+1.2*y[i]))+0.1*MathCos(20*x[j]+15*y[i]));
|
|
}
|
|
//--- Test bilinear interpolation:
|
|
//--- * interpolation at the nodes
|
|
//--- * linearity
|
|
//--- * continuity
|
|
//--- * differentiation in the inner points
|
|
CSpline2D::Spline2DBuildBilinear(x,y,f,m,n,c);
|
|
//--- search errors
|
|
err=0;
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
err=MathMax(err,MathAbs(f[i][j]-CSpline2D::Spline2DCalc(c,x[j],y[i])));
|
|
}
|
|
//--- search errors
|
|
blerrors=blerrors || err>10000*CMath::m_machineepsilon;
|
|
err=0;
|
|
for(i=0;i<=m-2;i++)
|
|
{
|
|
for(j=0;j<=n-2;j++)
|
|
{
|
|
//--- Test for linearity between grid points
|
|
//--- (test point - geometric center of the cell)
|
|
fm=CSpline2D::Spline2DCalc(c,lx[2*j+1],ly[2*i+1]);
|
|
f1=CSpline2D::Spline2DCalc(c,lx[2*j],ly[2*i]);
|
|
f2=CSpline2D::Spline2DCalc(c,lx[2*j+2],ly[2*i]);
|
|
f3=CSpline2D::Spline2DCalc(c,lx[2*j+2],ly[2*i+2]);
|
|
f4=CSpline2D::Spline2DCalc(c,lx[2*j],ly[2*i+2]);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(0.25*(f1+f2+f3+f4)-fm));
|
|
}
|
|
}
|
|
//--- search errors
|
|
blerrors=blerrors || err>10000*CMath::m_machineepsilon;
|
|
//--- function calls
|
|
LConst(c,lx,ly,m,n,lstep,l1,l1x,l1y,l1xy);
|
|
LConst(c,lx,ly,m,n,lstep/3,l2,l2x,l2y,l2xy);
|
|
//--- search errors
|
|
blerrors=blerrors || l2/l1>1.2;
|
|
err=0;
|
|
for(i=0;i<=m-2;i++)
|
|
{
|
|
for(j=0;j<=n-2;j++)
|
|
{
|
|
CSpline2D::Spline2DDiff(c,lx[2*j+1],ly[2*i+1],v1,v1x,v1y,v1xy);
|
|
TwodNumder(c,lx[2*j+1],ly[2*i+1],h,v2,v2x,v2y,v2xy);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v1-v2));
|
|
err=MathMax(err,MathAbs(v1x-v2x));
|
|
err=MathMax(err,MathAbs(v1y-v2y));
|
|
err=MathMax(err,MathAbs(v1xy-v2xy));
|
|
}
|
|
}
|
|
//--- search errors
|
|
dserrors=dserrors || err>1.0E-3;
|
|
uperrors=uperrors || !TestUnpack(c,lx,ly);
|
|
lterrors=lterrors || !TestLinTrans(c,ax,bx,ay,by);
|
|
//--- Test bicubic interpolation.
|
|
//--- * interpolation at the nodes
|
|
//--- * smoothness
|
|
//--- * differentiation
|
|
CSpline2D::Spline2DBuildBicubic(x,y,f,m,n,c);
|
|
//--- search errors
|
|
err=0;
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
err=MathMax(err,MathAbs(f[i][j]-CSpline2D::Spline2DCalc(c,x[j],y[i])));
|
|
}
|
|
//--- search errors
|
|
bcerrors=bcerrors || err>10000*CMath::m_machineepsilon;
|
|
LConst(c,lx,ly,m,n,lstep,l1,l1x,l1y,l1xy);
|
|
LConst(c,lx,ly,m,n,lstep/3,l2,l2x,l2y,l2xy);
|
|
//--- search errors
|
|
bcerrors=bcerrors || l2/l1>1.2;
|
|
bcerrors=bcerrors || l2x/l1x>1.2;
|
|
bcerrors=bcerrors || l2y/l1y>1.2;
|
|
//--- check
|
|
if(l2xy>0.01 && l1xy>0.01)
|
|
{
|
|
//--- Cross-derivative continuity is tested only when
|
|
//--- bigger than 0.01. When the task size is too
|
|
//--- small,the d2F/dXdY is nearly zero and Lipschitz
|
|
//--- constant ratio is ill-conditioned.
|
|
bcerrors=bcerrors || l2xy/l1xy>1.2;
|
|
}
|
|
err=0;
|
|
for(i=0;i<=2*m-2;i++)
|
|
{
|
|
for(j=0;j<=2*n-2;j++)
|
|
{
|
|
CSpline2D::Spline2DDiff(c,lx[j],ly[i],v1,v1x,v1y,v1xy);
|
|
TwodNumder(c,lx[j],ly[i],h,v2,v2x,v2y,v2xy);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v1-v2));
|
|
err=MathMax(err,MathAbs(v1x-v2x));
|
|
err=MathMax(err,MathAbs(v1y-v2y));
|
|
err=MathMax(err,MathAbs(v1xy-v2xy));
|
|
}
|
|
}
|
|
//--- search errors
|
|
dserrors=dserrors || err>1.0E-3;
|
|
uperrors=uperrors || !TestUnpack(c,lx,ly);
|
|
lterrors=lterrors || !TestLinTrans(c,ax,bx,ay,by);
|
|
//--- Copy/Serialise test
|
|
if(CMath::RandomReal()>0.5)
|
|
CSpline2D::Spline2DBuildBicubic(x,y,f,m,n,c);
|
|
else
|
|
CSpline2D::Spline2DBuildBilinear(x,y,f,m,n,c);
|
|
//--- function calls
|
|
UnsetSpline2D(c2);
|
|
CSpline2D::Spline2DCopy(c,c2);
|
|
//--- calculation
|
|
err=0;
|
|
for(i=1;i<=5;i++)
|
|
{
|
|
t1=ax+(bx-ax)*CMath::RandomReal();
|
|
t2=ay+(by-ay)*CMath::RandomReal();
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline2D::Spline2DCalc(c,t1,t2)-CSpline2D::Spline2DCalc(c2,t1,t2)));
|
|
}
|
|
//--- search errors
|
|
cperrors=cperrors || err>10000*CMath::m_machineepsilon;
|
|
//--- Special symmetry test
|
|
err=0;
|
|
for(jobtype=0;jobtype<=1;jobtype++)
|
|
{
|
|
//--- Prepare
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
ft[j].Set(i,f[i][j]);
|
|
}
|
|
//--- check
|
|
if(jobtype==0)
|
|
{
|
|
CSpline2D::Spline2DBuildBilinear(x,y,f,m,n,c);
|
|
CSpline2D::Spline2DBuildBilinear(y,x,ft,n,m,c2);
|
|
}
|
|
else
|
|
{
|
|
CSpline2D::Spline2DBuildBicubic(x,y,f,m,n,c);
|
|
CSpline2D::Spline2DBuildBicubic(y,x,ft,n,m,c2);
|
|
}
|
|
//--- Test
|
|
for(i=1;i<=10;i++)
|
|
{
|
|
t1=ax+(bx-ax)*CMath::RandomReal();
|
|
t2=ay+(by-ay)*CMath::RandomReal();
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(CSpline2D::Spline2DCalc(c,t1,t2)-CSpline2D::Spline2DCalc(c2,t2,t1)));
|
|
}
|
|
}
|
|
//--- search errors
|
|
syerrors=syerrors || err>10000*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
}
|
|
//--- Test resample
|
|
for(m=2;m<=6;m++)
|
|
{
|
|
for(n=2;n<=6;n++)
|
|
{
|
|
//--- allocation
|
|
f.Resize(m,n);
|
|
ArrayResize(x,n);
|
|
ArrayResize(y,m);
|
|
//--- change values
|
|
for(j=0;j<=n-1;j++)
|
|
x[j]=(double)j/(double)(n-1);
|
|
for(i=0;i<=m-1;i++)
|
|
y[i]=(double)i/(double)(m-1);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
f[i].Set(j,MathExp(0.6*x[j])-MathExp(-(0.3*y[i])+0.08*x[j])+2*MathCos(M_PI*(x[j]+1.2*y[i]))+0.1*MathCos(20*x[j]+15*y[i]));
|
|
}
|
|
//--- calculation
|
|
for(m2=2;m2<=6;m2++)
|
|
{
|
|
for(n2=2;n2<=6;n2++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(jobtype=0;jobtype<=1;jobtype++)
|
|
{
|
|
//--- check
|
|
if(jobtype==0)
|
|
{
|
|
CSpline2D::Spline2DResampleBilinear(f,m,n,fr,m2,n2);
|
|
CSpline2D::Spline2DBuildBilinear(x,y,f,m,n,c);
|
|
}
|
|
//--- check
|
|
if(jobtype==1)
|
|
{
|
|
CSpline2D::Spline2DResampleBicubic(f,m,n,fr,m2,n2);
|
|
CSpline2D::Spline2DBuildBicubic(x,y,f,m,n,c);
|
|
}
|
|
//--- change values
|
|
err=0;
|
|
mf=0;
|
|
//--- calculation
|
|
for(i=0;i<=m2-1;i++)
|
|
{
|
|
for(j=0;j<=n2-1;j++)
|
|
{
|
|
v1=CSpline2D::Spline2DCalc(c,(double)j/(double)(n2-1),(double)i/(double)(m2-1));
|
|
v2=fr[i][j];
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v1-v2));
|
|
mf=MathMax(mf,MathAbs(v1));
|
|
}
|
|
}
|
|
//--- check
|
|
if(jobtype==0)
|
|
rlerrors=rlerrors || err/mf>10000*CMath::m_machineepsilon;
|
|
//--- check
|
|
if(jobtype==1)
|
|
rcerrors=rcerrors || err/mf>10000*CMath::m_machineepsilon;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=(((((((blerrors || bcerrors) || dserrors) || cperrors) || uperrors) || lterrors) || syerrors) || rlerrors) || rcerrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING 2D INTERPOLATION");
|
|
//--- Normal tests
|
|
Print("BILINEAR TEST: ");
|
|
//--- check
|
|
if(blerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("BICUBIC TEST: ");
|
|
//--- check
|
|
if(bcerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("DIFFERENTIATION TEST: ");
|
|
//--- check
|
|
if(dserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("COPY/SERIALIZE TEST: ");
|
|
//--- check
|
|
if(cperrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("UNPACK TEST: ");
|
|
//--- check
|
|
if(uperrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("LIN.TRANS. TEST: ");
|
|
//--- check
|
|
if(lterrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("SPECIAL SYMMETRY TEST: ");
|
|
//--- check
|
|
if(syerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("BILINEAR RESAMPLING TEST: ");
|
|
//--- check
|
|
if(rlerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("BICUBIC RESAMPLING TEST: ");
|
|
//--- check
|
|
if(rcerrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- Summary
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- end
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Lipschitz constants for spline inself,first and second |
|
|
//| derivatives. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestSpline2DUnit::LConst(CSpline2DInterpolant &c,double &lx[],
|
|
double &ly[],const int m,const int n,
|
|
const double lstep,double &lc,
|
|
double &lcx,double &lcy,double &lcxy)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
double f1=0;
|
|
double f2=0;
|
|
double f3=0;
|
|
double f4=0;
|
|
double fx1=0;
|
|
double fx2=0;
|
|
double fx3=0;
|
|
double fx4=0;
|
|
double fy1=0;
|
|
double fy2=0;
|
|
double fy3=0;
|
|
double fy4=0;
|
|
double fxy1=0;
|
|
double fxy2=0;
|
|
double fxy3=0;
|
|
double fxy4=0;
|
|
double s2lstep=0;
|
|
//--- initialization
|
|
lc=0;
|
|
lcx=0;
|
|
lcy=0;
|
|
lcxy=0;
|
|
s2lstep=MathSqrt(2)*lstep;
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- Calculate
|
|
TwodNumder(c,lx[j]-lstep/2,ly[i]-lstep/2,lstep/4,f1,fx1,fy1,fxy1);
|
|
TwodNumder(c,lx[j]+lstep/2,ly[i]-lstep/2,lstep/4,f2,fx2,fy2,fxy2);
|
|
TwodNumder(c,lx[j]+lstep/2,ly[i]+lstep/2,lstep/4,f3,fx3,fy3,fxy3);
|
|
TwodNumder(c,lx[j]-lstep/2,ly[i]+lstep/2,lstep/4,f4,fx4,fy4,fxy4);
|
|
//--- Lipschitz constant for the function itself
|
|
lc=MathMax(lc,MathAbs((f1-f2)/lstep));
|
|
lc=MathMax(lc,MathAbs((f2-f3)/lstep));
|
|
lc=MathMax(lc,MathAbs((f3-f4)/lstep));
|
|
lc=MathMax(lc,MathAbs((f4-f1)/lstep));
|
|
lc=MathMax(lc,MathAbs((f1-f3)/s2lstep));
|
|
lc=MathMax(lc,MathAbs((f2-f4)/s2lstep));
|
|
//--- Lipschitz constant for the first derivative
|
|
lcx=MathMax(lcx,MathAbs((fx1-fx2)/lstep));
|
|
lcx=MathMax(lcx,MathAbs((fx2-fx3)/lstep));
|
|
lcx=MathMax(lcx,MathAbs((fx3-fx4)/lstep));
|
|
lcx=MathMax(lcx,MathAbs((fx4-fx1)/lstep));
|
|
lcx=MathMax(lcx,MathAbs((fx1-fx3)/s2lstep));
|
|
lcx=MathMax(lcx,MathAbs((fx2-fx4)/s2lstep));
|
|
//--- Lipschitz constant for the first derivative
|
|
lcy=MathMax(lcy,MathAbs((fy1-fy2)/lstep));
|
|
lcy=MathMax(lcy,MathAbs((fy2-fy3)/lstep));
|
|
lcy=MathMax(lcy,MathAbs((fy3-fy4)/lstep));
|
|
lcy=MathMax(lcy,MathAbs((fy4-fy1)/lstep));
|
|
lcy=MathMax(lcy,MathAbs((fy1-fy3)/s2lstep));
|
|
lcy=MathMax(lcy,MathAbs((fy2-fy4)/s2lstep));
|
|
//--- Lipschitz constant for the cross-derivative
|
|
lcxy=MathMax(lcxy,MathAbs((fxy1-fxy2)/lstep));
|
|
lcxy=MathMax(lcxy,MathAbs((fxy2-fxy3)/lstep));
|
|
lcxy=MathMax(lcxy,MathAbs((fxy3-fxy4)/lstep));
|
|
lcxy=MathMax(lcxy,MathAbs((fxy4-fxy1)/lstep));
|
|
lcxy=MathMax(lcxy,MathAbs((fxy1-fxy3)/s2lstep));
|
|
lcxy=MathMax(lcxy,MathAbs((fxy2-fxy4)/s2lstep));
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Numerical differentiation. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestSpline2DUnit::TwodNumder(CSpline2DInterpolant &c,const double x,
|
|
const double y,const double h,
|
|
double &f,double &fx,double &fy,
|
|
double &fxy)
|
|
{
|
|
//--- calculation
|
|
f=CSpline2D::Spline2DCalc(c,x,y);
|
|
fx=(CSpline2D::Spline2DCalc(c,x+h,y)-CSpline2D::Spline2DCalc(c,x-h,y))/(2*h);
|
|
fy=(CSpline2D::Spline2DCalc(c,x,y+h)-CSpline2D::Spline2DCalc(c,x,y-h))/(2*h);
|
|
fxy=(CSpline2D::Spline2DCalc(c,x+h,y+h)-CSpline2D::Spline2DCalc(c,x-h,y+h)-CSpline2D::Spline2DCalc(c,x+h,y-h)+CSpline2D::Spline2DCalc(c,x-h,y-h))/CMath::Sqr(2*h);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unpack test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestSpline2DUnit::TestUnpack(CSpline2DInterpolant &c,double &lx[],
|
|
double &ly[])
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
int n=0;
|
|
int m=0;
|
|
int ci=0;
|
|
int cj=0;
|
|
int p=0;
|
|
double err=0;
|
|
double tx=0;
|
|
double ty=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
//--- create matrix
|
|
CMatrixDouble tbl;
|
|
//--- initialization
|
|
passcount=20;
|
|
err=0;
|
|
//--- function call
|
|
CSpline2D::Spline2DUnpack(c,m,n,tbl);
|
|
//--- calculation
|
|
for(i=0;i<=m-2;i++)
|
|
{
|
|
for(j=0;j<=n-2;j++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
p=(n-1)*i+j;
|
|
tx=(0.001+0.999*CMath::RandomReal())*(tbl[p][1]-tbl[p][0]);
|
|
ty=(0.001+0.999*CMath::RandomReal())*(tbl[p][3]-tbl[p][2]);
|
|
//--- Interpolation properties
|
|
v1=0;
|
|
for(ci=0;ci<=3;ci++)
|
|
{
|
|
for(cj=0;cj<=3;cj++)
|
|
v1=v1+tbl[p][4+ci*4+cj]*MathPow(tx,ci)*MathPow(ty,cj);
|
|
}
|
|
v2=CSpline2D::Spline2DCalc(c,tbl[p][0]+tx,tbl[p][2]+ty);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v1-v2));
|
|
//--- Grid correctness
|
|
err=MathMax(err,MathAbs(lx[2*j]-tbl[p][0]));
|
|
err=MathMax(err,MathAbs(lx[2*(j+1)]-tbl[p][1]));
|
|
err=MathMax(err,MathAbs(ly[2*i]-tbl[p][2]));
|
|
err=MathMax(err,MathAbs(ly[2*(i+1)]-tbl[p][3]));
|
|
}
|
|
}
|
|
}
|
|
//--- get result
|
|
result=err<10000*CMath::m_machineepsilon;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LinTrans test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestSpline2DUnit::TestLinTrans(CSpline2DInterpolant &c,
|
|
const double ax,const double bx,
|
|
const double ay,const double by)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
double err=0;
|
|
double a1=0;
|
|
double a2=0;
|
|
double b1=0;
|
|
double b2=0;
|
|
double tx=0;
|
|
double ty=0;
|
|
double vx=0;
|
|
double vy=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
int xjob=0;
|
|
int yjob=0;
|
|
//--- object of class
|
|
CSpline2DInterpolant c2;
|
|
//--- initialization
|
|
passcount=5;
|
|
err=0;
|
|
//--- calculation
|
|
for(xjob=0;xjob<=1;xjob++)
|
|
{
|
|
for(yjob=0;yjob<=1;yjob++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Prepare
|
|
do
|
|
{
|
|
a1=2*CMath::RandomReal()-1;
|
|
}
|
|
while(a1==0.0);
|
|
//--- change values
|
|
a1=a1*xjob;
|
|
b1=2*CMath::RandomReal()-1;
|
|
do
|
|
{
|
|
a2=2*CMath::RandomReal()-1;
|
|
}
|
|
while(a2==0.0);
|
|
//--- change values
|
|
a2=a2*yjob;
|
|
b2=2*CMath::RandomReal()-1;
|
|
//--- Test XY
|
|
CSpline2D::Spline2DCopy(c,c2);
|
|
CSpline2D::Spline2DLinTransXY(c2,a1,b1,a2,b2);
|
|
tx=ax+CMath::RandomReal()*(bx-ax);
|
|
ty=ay+CMath::RandomReal()*(by-ay);
|
|
//--- check
|
|
if(xjob==0)
|
|
{
|
|
tx=b1;
|
|
vx=ax+CMath::RandomReal()*(bx-ax);
|
|
}
|
|
else
|
|
vx=(tx-b1)/a1;
|
|
//--- check
|
|
if(yjob==0)
|
|
{
|
|
ty=b2;
|
|
vy=ay+CMath::RandomReal()*(by-ay);
|
|
}
|
|
else
|
|
vy=(ty-b2)/a2;
|
|
v1=CSpline2D::Spline2DCalc(c,tx,ty);
|
|
v2=CSpline2D::Spline2DCalc(c2,vx,vy);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v1-v2));
|
|
//--- Test F
|
|
CSpline2D::Spline2DCopy(c,c2);
|
|
CSpline2D::Spline2DLinTransF(c2,a1,b1);
|
|
//--- change values
|
|
tx=ax+CMath::RandomReal()*(bx-ax);
|
|
ty=ay+CMath::RandomReal()*(by-ay);
|
|
v1=CSpline2D::Spline2DCalc(c,tx,ty);
|
|
v2=CSpline2D::Spline2DCalc(c2,tx,ty);
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(a1*v1+b1-v2));
|
|
}
|
|
}
|
|
}
|
|
//--- get result
|
|
result=err<10000*CMath::m_machineepsilon;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Unset spline,i.e. initialize it with random garbage |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestSpline2DUnit::UnsetSpline2D(CSpline2DInterpolant &c)
|
|
{
|
|
//--- create arrays
|
|
double x[];
|
|
double y[];
|
|
//--- create matrix
|
|
CMatrixDouble f;
|
|
//--- allocation
|
|
ArrayResize(x,2);
|
|
ArrayResize(y,2);
|
|
f.Resize(2,2);
|
|
//--- initialization
|
|
x[0]=-1;
|
|
x[1]=1;
|
|
y[0]=-1;
|
|
y[1]=1;
|
|
f[0].Set(0,0);
|
|
f[0].Set(1,0);
|
|
f[1].Set(0,0);
|
|
f[1].Set(1,0);
|
|
//--- function call
|
|
CSpline2D::Spline2DBuildBilinear(x,y,f,2,2,c);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSpdGEVD |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSpdGEVDUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestSpdGEVDUnit(void);
|
|
~CTestSpdGEVDUnit(void);
|
|
//--- public method
|
|
static bool TestSpdGEVD(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestSpdGEVDUnit::CTestSpdGEVDUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestSpdGEVDUnit::~CTestSpdGEVDUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSpdGEVD |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestSpdGEVDUnit::TestSpdGEVD(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int pass=0;
|
|
int n=0;
|
|
int passcount=0;
|
|
int maxn=0;
|
|
int atask=0;
|
|
int btask=0;
|
|
bool isuppera;
|
|
bool isupperb;
|
|
int i=0;
|
|
int j=0;
|
|
int minij=0;
|
|
double v=0;
|
|
double v1=0;
|
|
double v2=0;
|
|
double err=0;
|
|
double valerr=0;
|
|
double threshold=0;
|
|
bool waserrors;
|
|
bool wfailed;
|
|
bool wnsorted;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double d[];
|
|
double t1[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble b;
|
|
CMatrixDouble afull;
|
|
CMatrixDouble bfull;
|
|
CMatrixDouble l;
|
|
CMatrixDouble z;
|
|
//--- initialization
|
|
threshold=10000*CMath::m_machineepsilon;
|
|
valerr=0;
|
|
wfailed=false;
|
|
wnsorted=false;
|
|
maxn=20;
|
|
passcount=5;
|
|
//--- Main cycle
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(atask=0;atask<=1;atask++)
|
|
{
|
|
for(btask=0;btask<=1;btask++)
|
|
{
|
|
isuppera=atask==0;
|
|
isupperb=btask==0;
|
|
//--- Initialize A,B,AFull,BFull
|
|
ArrayResize(t1,n);
|
|
a.Resize(n,n);
|
|
b.Resize(n,n);
|
|
afull.Resize(n,n);
|
|
bfull.Resize(n,n);
|
|
l.Resize(n,n);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
a[j].Set(i,a[i][j]);
|
|
afull[i].Set(j,a[i][j]);
|
|
afull[j].Set(i,a[i][j]);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=i+1;j<=n-1;j++)
|
|
{
|
|
l[i].Set(j,CMath::RandomReal());
|
|
l[j].Set(i,l[i][j]);
|
|
}
|
|
l[i].Set(i,1.5+CMath::RandomReal());
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
minij=MathMin(i,j);
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=minij;i_++)
|
|
{
|
|
v+=l[i][i_]*l[i_][j];
|
|
}
|
|
b[i].Set(j,v);
|
|
b[j].Set(i,v);
|
|
bfull[i].Set(j,v);
|
|
bfull[j].Set(i,v);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(isuppera)
|
|
{
|
|
//--- check
|
|
if(j<i)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(i<j)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- check
|
|
if(isupperb)
|
|
{
|
|
//--- check
|
|
if(j<i)
|
|
b[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(i<j)
|
|
b[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
}
|
|
}
|
|
//--- Problem 1
|
|
if(!CSpdGEVD::SMatrixGEVD(a,n,isuppera,b,isupperb,1,1,d,z))
|
|
{
|
|
wfailed=true;
|
|
continue;
|
|
}
|
|
//--- calculation
|
|
err=0;
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v1=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v1+=afull[i][i_]*z[i_][j];
|
|
v2=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v2+=bfull[i][i_]*z[i_][j];
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v1-d[j]*v2));
|
|
}
|
|
}
|
|
//--- search errors
|
|
valerr=MathMax(err,valerr);
|
|
//--- Problem 2
|
|
if(!CSpdGEVD::SMatrixGEVD(a,n,isuppera,b,isupperb,1,2,d,z))
|
|
{
|
|
wfailed=true;
|
|
continue;
|
|
}
|
|
//--- calculation
|
|
err=0;
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v1=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v1+=bfull[i][i_]*z[i_][j];
|
|
t1[i]=v1;
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v2=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v2+=afull[i][i_]*t1[i_];
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v2-d[j]*z[i][j]));
|
|
}
|
|
}
|
|
//--- search errors
|
|
valerr=MathMax(err,valerr);
|
|
//--- Test problem 3
|
|
if(!CSpdGEVD::SMatrixGEVD(a,n,isuppera,b,isupperb,1,3,d,z))
|
|
{
|
|
wfailed=true;
|
|
continue;
|
|
}
|
|
//--- calculation
|
|
err=0;
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v1=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v1+=afull[i][i_]*z[i_][j];
|
|
t1[i]=v1;
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v2=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v2+=bfull[i][i_]*t1[i_];
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v2-d[j]*z[i][j]));
|
|
}
|
|
}
|
|
//--- search errors
|
|
valerr=MathMax(err,valerr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=(valerr>threshold || wfailed) || wnsorted;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING SYMMETRIC GEVD");
|
|
Print("Av-lambdav error (generalized): ");
|
|
Print("{0,5:E3}",valerr);
|
|
Print("Eigen values order: ");
|
|
//--- check
|
|
if(!wnsorted)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("Always converged: ");
|
|
//--- check
|
|
if(!wfailed)
|
|
Print("YES");
|
|
else
|
|
Print("NO");
|
|
Print("Threshold: ");
|
|
Print("{0,5:E3}",threshold);
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CInverseUpdate |
|
|
//+------------------------------------------------------------------+
|
|
class CTestInverseUpdateUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void MakeACopy(CMatrixDouble &a,const int m,const int n,CMatrixDouble &b);
|
|
static void MatLU(CMatrixDouble &a,const int m,const int n,int &pivots[]);
|
|
static void GenerateRandomOrthogonalMatrix(CMatrixDouble &a0,const int n);
|
|
static void GenerateRandomMatrixCond(CMatrixDouble &a0,const int n,const double c);
|
|
static bool InvMatTr(CMatrixDouble &a,const int n,const bool isupper,const bool isunittriangular);
|
|
static bool InvMatLU(CMatrixDouble &a,int &pivots[],const int n);
|
|
static bool InvMat(CMatrixDouble &a,const int n);
|
|
static double MatrixDiff(CMatrixDouble &a,CMatrixDouble &b,const int m,const int n);
|
|
static bool UpdAndInv(CMatrixDouble &a,double &u[],double &v[],const int n);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestInverseUpdateUnit(void);
|
|
~CTestInverseUpdateUnit(void);
|
|
//--- public method
|
|
static bool TestInverseUpdate(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestInverseUpdateUnit::CTestInverseUpdateUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestInverseUpdateUnit::~CTestInverseUpdateUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CInverseUpdate |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestInverseUpdateUnit::TestInverseUpdate(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int maxn=0;
|
|
int i=0;
|
|
int updrow=0;
|
|
int updcol=0;
|
|
double val=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
bool waserrors;
|
|
double threshold=0;
|
|
double c=0;
|
|
//--- create arrays
|
|
double u[];
|
|
double v[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
CMatrixDouble inva;
|
|
CMatrixDouble b1;
|
|
CMatrixDouble b2;
|
|
//--- initialization
|
|
waserrors=false;
|
|
maxn=10;
|
|
passcount=100;
|
|
threshold=1.0E-6;
|
|
//--- process
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- allocation
|
|
a.Resize(n,n);
|
|
b1.Resize(n,n);
|
|
b2.Resize(n,n);
|
|
ArrayResize(u,n);
|
|
ArrayResize(v,n);
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
c=MathExp(CMath::RandomReal()*MathLog(10));
|
|
GenerateRandomMatrixCond(a,n,c);
|
|
MakeACopy(a,n,n,inva);
|
|
//--- check
|
|
if(!InvMat(inva,n))
|
|
{
|
|
waserrors=true;
|
|
break;
|
|
}
|
|
//--- Test simple update
|
|
updrow=CMath::RandomInteger(n);
|
|
updcol=CMath::RandomInteger(n);
|
|
val=0.1*(2*CMath::RandomReal()-1);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i==updrow)
|
|
u[i]=val;
|
|
else
|
|
u[i]=0;
|
|
//--- check
|
|
if(i==updcol)
|
|
v[i]=1;
|
|
else
|
|
v[i]=0;
|
|
}
|
|
//--- function call
|
|
MakeACopy(a,n,n,b1);
|
|
//--- check
|
|
if(!UpdAndInv(b1,u,v,n))
|
|
{
|
|
waserrors=true;
|
|
break;
|
|
}
|
|
//--- function calls
|
|
MakeACopy(inva,n,n,b2);
|
|
CInverseUpdate::RMatrixInvUpdateSimple(b2,n,updrow,updcol,val);
|
|
//--- search errors
|
|
waserrors=waserrors || MatrixDiff(b1,b2,n,n)>threshold;
|
|
//--- Test row update
|
|
updrow=CMath::RandomInteger(n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i==updrow)
|
|
u[i]=1;
|
|
else
|
|
u[i]=0;
|
|
v[i]=0.1*(2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function call
|
|
MakeACopy(a,n,n,b1);
|
|
//--- check
|
|
if(!UpdAndInv(b1,u,v,n))
|
|
{
|
|
waserrors=true;
|
|
break;
|
|
}
|
|
//--- function calls
|
|
MakeACopy(inva,n,n,b2);
|
|
CInverseUpdate::RMatrixInvUpdateRow(b2,n,updrow,v);
|
|
//--- search errors
|
|
waserrors=waserrors || MatrixDiff(b1,b2,n,n)>threshold;
|
|
//--- Test column update
|
|
updcol=CMath::RandomInteger(n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i==updcol)
|
|
v[i]=1;
|
|
else
|
|
v[i]=0;
|
|
u[i]=0.1*(2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function call
|
|
MakeACopy(a,n,n,b1);
|
|
//--- check
|
|
if(!UpdAndInv(b1,u,v,n))
|
|
{
|
|
waserrors=true;
|
|
break;
|
|
}
|
|
//--- function calls
|
|
MakeACopy(inva,n,n,b2);
|
|
CInverseUpdate::RMatrixInvUpdateColumn(b2,n,updcol,u);
|
|
//--- search errors
|
|
waserrors=waserrors || MatrixDiff(b1,b2,n,n)>threshold;
|
|
//--- Test full update
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v[i]=0.1*(2*CMath::RandomReal()-1);
|
|
u[i]=0.1*(2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function call
|
|
MakeACopy(a,n,n,b1);
|
|
//--- check
|
|
if(!UpdAndInv(b1,u,v,n))
|
|
{
|
|
waserrors=true;
|
|
break;
|
|
}
|
|
//--- function calls
|
|
MakeACopy(inva,n,n,b2);
|
|
CInverseUpdate::RMatrixInvUpdateUV(b2,n,u,v);
|
|
//--- search errors
|
|
waserrors=waserrors || MatrixDiff(b1,b2,n,n)>threshold;
|
|
}
|
|
}
|
|
//--- report
|
|
if(!silent)
|
|
{
|
|
Print("TESTING INVERSE UPDATE (REAL)");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestInverseUpdateUnit::MakeACopy(CMatrixDouble &a,const int m,
|
|
const int n,CMatrixDouble &b)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- allocation
|
|
b.Resize(m,n);
|
|
//--- copy
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
b[i].Set(j,a[i][j]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LU decomposition |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestInverseUpdateUnit::MatLU(CMatrixDouble &a,const int m,
|
|
const int n,int &pivots[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int jp=0;
|
|
double s=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double t1[];
|
|
//--- allocation
|
|
ArrayResize(pivots,MathMin(m-1,n-1)+1);
|
|
ArrayResize(t1,MathMax(m-1,n-1)+1);
|
|
//--- check
|
|
if(!CAp::Assert(m>=0 && n>=0,"Error in LUDecomposition: incorrect function arguments"))
|
|
return;
|
|
//--- Quick return if possible
|
|
if(m==0 || n==0)
|
|
return;
|
|
//--- calculation
|
|
for(j=0;j<=MathMin(m-1,n-1);j++)
|
|
{
|
|
//--- Find pivot and test for singularity.
|
|
jp=j;
|
|
for(i=j+1;i<=m-1;i++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(a[i][j])>MathAbs(a[jp][j]))
|
|
jp=i;
|
|
}
|
|
pivots[j]=jp;
|
|
//--- check
|
|
if(a[jp][j]!=0.0)
|
|
{
|
|
//--- Apply the interchange to rows
|
|
if(jp!=j)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
t1[i_]=a[j][i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[j].Set(i_,a[jp][i_]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[jp].Set(i_,t1[i_]);
|
|
}
|
|
//--- Compute elements J+1:M of J-th column.
|
|
if(j<m)
|
|
{
|
|
jp=j+1;
|
|
s=1/a[j][j];
|
|
for(i_=jp;i_<=m-1;i_++)
|
|
a[i_].Set(j,s*a[i_][j]);
|
|
}
|
|
}
|
|
//--- check
|
|
if(j<MathMin(m,n)-1)
|
|
{
|
|
//--- Update trailing submatrix.
|
|
jp=j+1;
|
|
for(i=j+1;i<=m-1;i++)
|
|
{
|
|
s=a[i][j];
|
|
for(i_=jp;i_<=n-1;i_++)
|
|
a[i].Set(i_,a[i][i_]-s*a[j][i_]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Generate matrix with given condition number C (2-norm) |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestInverseUpdateUnit::GenerateRandomOrthogonalMatrix(CMatrixDouble &a0,
|
|
const int n)
|
|
{
|
|
//--- create variables
|
|
double t=0;
|
|
double lambdav=0;
|
|
int s=0;
|
|
int i=0;
|
|
int j=0;
|
|
double u1=0;
|
|
double u2=0;
|
|
double sm=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double w[];
|
|
double v[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- check
|
|
if(n<=0)
|
|
return;
|
|
//--- allocation
|
|
ArrayResize(w,n+1);
|
|
ArrayResize(v,n+1);
|
|
a.Resize(n+1,n+1);
|
|
a0.Resize(n,n);
|
|
//--- Prepare A
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
{
|
|
//--- check
|
|
if(i==j)
|
|
a[i].Set(j,1);
|
|
else
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- Calculate A using Stewart algorithm
|
|
for(s=2;s<=n;s++)
|
|
{
|
|
//--- Prepare v and Lambda=v'*v
|
|
do
|
|
{
|
|
i=1;
|
|
while(i<=s)
|
|
{
|
|
//--- change values
|
|
u1=2*CMath::RandomReal()-1;
|
|
u2=2*CMath::RandomReal()-1;
|
|
sm=u1*u1+u2*u2;
|
|
//--- check
|
|
if(sm==0.0 || sm>1.0)
|
|
continue;
|
|
sm=MathSqrt(-(2*MathLog(sm)/sm));
|
|
v[i]=u1*sm;
|
|
//--- check
|
|
if(i+1<=s)
|
|
v[i+1]=u2*sm;
|
|
i=i+2;
|
|
}
|
|
//--- change value
|
|
lambdav=0.0;
|
|
for(i_=1;i_<=s;i_++)
|
|
lambdav+=v[i_]*v[i_];
|
|
}
|
|
while((double)(lambdav)==0.0);
|
|
lambdav=2/lambdav;
|
|
//--- A * (I - 2 vv'/v'v )=
|
|
//--- =A - (2/v'v) * A * v * v'=
|
|
//--- =A - (2/v'v) * w * v'
|
|
//--- where w=Av
|
|
for(i=1;i<=s;i++)
|
|
{
|
|
t=0.0;
|
|
for(i_=1;i_<=s;i_++)
|
|
t+=a[i][i_]*v[i_];
|
|
w[i]=t;
|
|
}
|
|
//--- calculation
|
|
for(i=1;i<=s;i++)
|
|
{
|
|
t=w[i]*lambdav;
|
|
for(i_=1;i_<=s;i_++)
|
|
a[i].Set(i_,a[i][i_]-t*v[i_]);
|
|
}
|
|
}
|
|
//--- copy
|
|
for(i=1;i<=n;i++)
|
|
{
|
|
for(j=1;j<=n;j++)
|
|
a0[i-1].Set(j-1,a[i][j]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestInverseUpdateUnit::GenerateRandomMatrixCond(CMatrixDouble &a0,
|
|
const int n,
|
|
const double c)
|
|
{
|
|
//--- create variables
|
|
double l1=0;
|
|
double l2=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
//--- create array
|
|
double cc[];
|
|
//--- create matrix
|
|
CMatrixDouble q1;
|
|
CMatrixDouble q2;
|
|
//--- function calls
|
|
GenerateRandomOrthogonalMatrix(q1,n);
|
|
GenerateRandomOrthogonalMatrix(q2,n);
|
|
//--- allocation
|
|
ArrayResize(cc,n);
|
|
//--- change values
|
|
l1=0;
|
|
l2=MathLog(1/c);
|
|
cc[0]=MathExp(l1);
|
|
for(i=1;i<=n-2;i++)
|
|
cc[i]=MathExp(CMath::RandomReal()*(l2-l1)+l1);
|
|
cc[n-1]=MathExp(l2);
|
|
//--- allocation
|
|
a0.Resize(n,n);
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
a0[i].Set(j,0);
|
|
for(k=0;k<=n-1;k++)
|
|
a0[i].Set(j,a0[i][j]+q1[i][k]*cc[k]*q2[j][k]);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| triangular inverse |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestInverseUpdateUnit::InvMatTr(CMatrixDouble &a,const int n,
|
|
const bool isupper,
|
|
const bool isunittriangular)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool nounit;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double ajj=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double t[];
|
|
//--- initialization
|
|
result=true;
|
|
//--- allocation
|
|
ArrayResize(t,n);
|
|
//--- Test the input parameters.
|
|
nounit=!isunittriangular;
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
//--- Compute inverse of upper triangular matrix.
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0.0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
a[j].Set(j,1/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- Compute elements 1:j-1 of j-th column.
|
|
if(j>0)
|
|
{
|
|
for(i_=0;i_<=j-1;i_++)
|
|
t[i_]=a[i_][j];
|
|
for(i=0;i<=j-1;i++)
|
|
{
|
|
//--- check
|
|
if(i<j-1)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=i+1;i_<=j-1;i_++)
|
|
v+=a[i][i_]*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a[i].Set(j,v+a[i][i]*t[i]);
|
|
else
|
|
a[i].Set(j,v+t[i]);
|
|
}
|
|
//--- change values
|
|
for(i_=0;i_<=j-1;i_++)
|
|
a[i_].Set(j,ajj*a[i_][j]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- Compute inverse of lower triangular matrix.
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
//--- check
|
|
if(nounit)
|
|
{
|
|
//--- check
|
|
if(a[j][j]==0.0)
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
a[j].Set(j,1/a[j][j]);
|
|
ajj=-a[j][j];
|
|
}
|
|
else
|
|
ajj=-1;
|
|
//--- check
|
|
if(j<n-1)
|
|
{
|
|
//--- Compute elements j+1:n of j-th column.
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
t[i_]=a[i_][j];
|
|
for(i=j+1;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(i>j+1)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=j+1;i_<=i-1;i_++)
|
|
v+=a[i][i_]*t[i_];
|
|
}
|
|
else
|
|
v=0;
|
|
//--- check
|
|
if(nounit)
|
|
a[i].Set(j,v+a[i][i]*t[i]);
|
|
else
|
|
a[i].Set(j,v+t[i]);
|
|
}
|
|
//--- change values
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
a[i_].Set(j,ajj*a[i_][j]);
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LU inverse |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestInverseUpdateUnit::InvMatLU(CMatrixDouble &a,int &pivots[],
|
|
const int n)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int j=0;
|
|
int jp=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- create array
|
|
double work[];
|
|
//--- initialization
|
|
result=true;
|
|
//--- Quick return if possible
|
|
if(n==0)
|
|
{
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- allocation
|
|
ArrayResize(work,n);
|
|
//--- Form inv(U)
|
|
if(!InvMatTr(a,n,true,false))
|
|
{
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- Solve the equation inv(A)*L=inv(U) for inv(A).
|
|
for(j=n-1;j>=0;j--)
|
|
{
|
|
//--- Copy current column of L to WORK and replace with zeros.
|
|
for(i=j+1;i<=n-1;i++)
|
|
{
|
|
work[i]=a[i][j];
|
|
a[i].Set(j,0);
|
|
}
|
|
//--- Compute current column of inv(A).
|
|
if(j<n-1)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=j+1;i_<=n-1;i_++)
|
|
v+=a[i][i_]*work[i_];
|
|
a[i].Set(j,a[i][j]-v);
|
|
}
|
|
}
|
|
}
|
|
//--- Apply column interchanges.
|
|
for(j=n-2;j>=0;j--)
|
|
{
|
|
jp=pivots[j];
|
|
//--- check
|
|
if(jp!=j)
|
|
{
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
work[i_]=a[i_][j];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(j,a[i_][jp]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i_].Set(jp,work[i_]);
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Matrix inverse |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestInverseUpdateUnit::InvMat(CMatrixDouble &a,const int n)
|
|
{
|
|
//--- create array
|
|
int pivots[];
|
|
//--- function call
|
|
MatLU(a,n,n,pivots);
|
|
//--- return result
|
|
return(InvMatLU(a,pivots,n));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Diff |
|
|
//+------------------------------------------------------------------+
|
|
static double CTestInverseUpdateUnit::MatrixDiff(CMatrixDouble &a,
|
|
CMatrixDouble &b,
|
|
const int m,const int n)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int j=0;
|
|
//--- calculation
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
result=MathMax(result,MathAbs(b[i][j]-a[i][j]));
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Update and inverse |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestInverseUpdateUnit::UpdAndInv(CMatrixDouble &a,double &u[],
|
|
double &v[],const int n)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double r=0;
|
|
int i_=0;
|
|
//--- create array
|
|
int pivots[];
|
|
//--- calculation
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
r=u[i];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
a[i].Set(i_,a[i][i_]+r*v[i_]);
|
|
}
|
|
//--- function call
|
|
MatLU(a,n,n,pivots);
|
|
//--- return result
|
|
return(InvMatLU(a,pivots,n));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CSchur |
|
|
//+------------------------------------------------------------------+
|
|
class CTestSchurUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void FillsParseA(CMatrixDouble &a,const int n,const double sparcity);
|
|
static void TestSchurProblem(CMatrixDouble &a,const int n,double &materr,double &orterr,bool &errstruct,bool &wfailed);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestSchurUnit(void);
|
|
~CTestSchurUnit(void);
|
|
//--- public method
|
|
static bool TestSchur(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestSchurUnit::CTestSchurUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestSchurUnit::~CTestSchurUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing Schur decomposition subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestSchurUnit::TestSchur(const bool silent)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int maxn=0;
|
|
int i=0;
|
|
int j=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
bool waserrors;
|
|
bool errstruct;
|
|
bool wfailed;
|
|
double materr=0;
|
|
double orterr=0;
|
|
double threshold=0;
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- initialization
|
|
materr=0;
|
|
orterr=0;
|
|
errstruct=false;
|
|
wfailed=false;
|
|
waserrors=false;
|
|
maxn=70;
|
|
passcount=1;
|
|
threshold=5*100*CMath::m_machineepsilon;
|
|
//--- allocation
|
|
a.Resize(maxn,maxn);
|
|
//--- zero matrix,several cases
|
|
for(i=0;i<=maxn-1;i++)
|
|
{
|
|
for(j=0;j<=maxn-1;j++)
|
|
a[i].Set(j,0);
|
|
}
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- check
|
|
if(n>30 && n%2==0)
|
|
continue;
|
|
//--- function call
|
|
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
|
|
}
|
|
//--- Dense matrix
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- check
|
|
if(n>30 && n%2==0)
|
|
continue;
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
}
|
|
//--- function call
|
|
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
|
|
}
|
|
}
|
|
//--- Sparse matrices,very sparse matrices,incredible sparse matrices
|
|
for(pass=1;pass<=1;pass++)
|
|
{
|
|
for(n=1;n<=maxn;n++)
|
|
{
|
|
//--- check
|
|
if(n>30 && n%3!=0)
|
|
continue;
|
|
//--- function calls
|
|
FillsParseA(a,n,0.8);
|
|
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
|
|
FillsParseA(a,n,0.9);
|
|
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
|
|
FillsParseA(a,n,0.95);
|
|
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
|
|
FillsParseA(a,n,0.997);
|
|
TestSchurProblem(a,n,materr,orterr,errstruct,wfailed);
|
|
}
|
|
}
|
|
//--- report
|
|
waserrors=((materr>threshold || orterr>threshold) || errstruct) || wfailed;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING SCHUR DECOMPOSITION");
|
|
Print("Schur decomposition error: ");
|
|
Print("{0,5:E3}",materr);
|
|
Print("Schur orthogonality error: ");
|
|
Print("{0,5:E3}",orterr);
|
|
Print("T matrix structure: ");
|
|
//--- check
|
|
if(!errstruct)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("Always converged: ");
|
|
//--- check
|
|
if(!wfailed)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("Threshold: ");
|
|
Print("{0,5:E3}",threshold);
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestSchurUnit::FillsParseA(CMatrixDouble &a,const int n,
|
|
const double sparcity)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- check
|
|
if(CMath::RandomReal()>=sparcity)
|
|
a[i].Set(j,2*CMath::RandomReal()-1);
|
|
else
|
|
a[i].Set(j,0);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestSchurUnit::TestSchurProblem(CMatrixDouble &a,const int n,
|
|
double &materr,double &orterr,
|
|
bool &errstruct,bool &wfailed)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
double v=0;
|
|
double locerr=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double sr[];
|
|
double astc[];
|
|
double sastc[];
|
|
//--- create matrix
|
|
CMatrixDouble s;
|
|
CMatrixDouble t;
|
|
//--- allocation
|
|
ArrayResize(sr,n);
|
|
ArrayResize(astc,n);
|
|
ArrayResize(sastc,n);
|
|
//--- Schur decomposition,convergence test
|
|
t.Resize(n,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
t[i].Set(j,a[i][j]);
|
|
}
|
|
//--- check
|
|
if(!CSchur::RMatrixSchur(t,n,s))
|
|
{
|
|
wfailed=true;
|
|
return;
|
|
}
|
|
//--- decomposition error
|
|
locerr=0;
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
sr[i_]=s[j][i_];
|
|
//--- calculation
|
|
for(k=0;k<=n-1;k++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=t[k][i_]*sr[i_];
|
|
astc[k]=v;
|
|
}
|
|
//--- calculation
|
|
for(k=0;k<=n-1;k++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=s[k][i_]*astc[i_];
|
|
sastc[k]=v;
|
|
}
|
|
//--- search errors
|
|
for(k=0;k<=n-1;k++)
|
|
locerr=MathMax(locerr,MathAbs(sastc[k]-a[k][j]));
|
|
}
|
|
//--- search errors
|
|
materr=MathMax(materr,locerr);
|
|
//--- orthogonality error
|
|
locerr=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=s[i_][i]*s[i_][j];
|
|
//--- check
|
|
if(i!=j)
|
|
locerr=MathMax(locerr,MathAbs(v));
|
|
else
|
|
locerr=MathMax(locerr,MathAbs(v-1));
|
|
}
|
|
}
|
|
//--- search errors
|
|
orterr=MathMax(orterr,locerr);
|
|
//--- T matrix structure
|
|
for(j=0;j<=n-1;j++)
|
|
{
|
|
for(i=j+2;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(t[i][j]!=0.0)
|
|
errstruct=true;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CNlEq |
|
|
//+------------------------------------------------------------------+
|
|
class CTestNlEqUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void TestFuncHBM(CNlEqState &state);
|
|
static void TestFuncHB1(CNlEqState &state);
|
|
static void TestFuncSHBM(CNlEqState &state);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestNlEqUnit(void);
|
|
~CTestNlEqUnit(void);
|
|
//--- public method
|
|
static bool TestNlEq(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestNlEqUnit::CTestNlEqUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestNlEqUnit::~CTestNlEqUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CNlEq |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestNlEqUnit::TestNlEq(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool waserrors;
|
|
bool basicserrors;
|
|
bool converror;
|
|
bool othererrors;
|
|
int n=0;
|
|
int i=0;
|
|
int k=0;
|
|
double v=0;
|
|
double flast=0;
|
|
bool firstrep;
|
|
int nfunc=0;
|
|
int njac=0;
|
|
int itcnt=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
double epsf=0;
|
|
double stpmax=0;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double x[];
|
|
double xlast[];
|
|
//--- objects of classes
|
|
CNlEqState state;
|
|
CNlEqReport rep;
|
|
//--- initialization
|
|
waserrors=false;
|
|
basicserrors=false;
|
|
converror=false;
|
|
othererrors=false;
|
|
//--- Basic tests
|
|
//--- Test with Himmelblau's function (M):
|
|
//--- * ability to find correct result
|
|
//--- * ability to work after soft restart (restart after finish)
|
|
//--- * ability to work after hard restart (restart in the middle of optimization)
|
|
passcount=100;
|
|
for(pass=0;pass<=passcount-1;pass++)
|
|
{
|
|
//--- Ability to find correct result
|
|
ArrayResize(x,2);
|
|
x[0]=20*CMath::RandomReal()-10;
|
|
x[1]=20*CMath::RandomReal()-10;
|
|
//--- function call
|
|
CNlEq::NlEqCreateLM(2,2,x,state);
|
|
epsf=1.0E-9;
|
|
//--- function call
|
|
CNlEq::NlEqSetCond(state,epsf,0);
|
|
//--- cycle
|
|
while(CNlEq::NlEqIteration(state))
|
|
TestFuncHBM(state);
|
|
//--- function call
|
|
CNlEq::NlEqResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
basicserrors=basicserrors || CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7)>CMath::Sqr(epsf);
|
|
else
|
|
basicserrors=true;
|
|
//--- Ability to work after soft restart
|
|
ArrayResize(x,2);
|
|
x[0]=20*CMath::RandomReal()-10;
|
|
x[1]=20*CMath::RandomReal()-10;
|
|
//--- function call
|
|
CNlEq::NlEqCreateLM(2,2,x,state);
|
|
epsf=1.0E-9;
|
|
//--- function call
|
|
CNlEq::NlEqSetCond(state,epsf,0);
|
|
//--- cycle
|
|
while(CNlEq::NlEqIteration(state))
|
|
TestFuncHBM(state);
|
|
//--- function call
|
|
CNlEq::NlEqResults(state,x,rep);
|
|
//--- allocation
|
|
ArrayResize(x,2);
|
|
//--- change values
|
|
x[0]=20*CMath::RandomReal()-10;
|
|
x[1]=20*CMath::RandomReal()-10;
|
|
//--- function call
|
|
CNlEq::NlEqRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CNlEq::NlEqIteration(state))
|
|
TestFuncHBM(state);
|
|
//--- function call
|
|
CNlEq::NlEqResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
basicserrors=basicserrors || CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7)>CMath::Sqr(epsf);
|
|
else
|
|
basicserrors=true;
|
|
//--- Ability to work after hard restart:
|
|
//--- * stopping condition: small F
|
|
//--- * StpMax is so small that we need about 10000 iterations to
|
|
//--- find solution (steps are small)
|
|
//--- * choose random K significantly less that 9999
|
|
//--- * iterate for some time,then break,restart optimization
|
|
ArrayResize(x,2);
|
|
x[0]=100;
|
|
x[1]=100;
|
|
//--- function call
|
|
CNlEq::NlEqCreateLM(2,2,x,state);
|
|
epsf=1.0E-9;
|
|
//--- function calls
|
|
CNlEq::NlEqSetCond(state,epsf,0);
|
|
CNlEq::NlEqSetStpMax(state,0.01);
|
|
k=1+CMath::RandomInteger(100);
|
|
//--- calculation
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CNlEq::NlEqIteration(state))
|
|
break;
|
|
TestFuncHBM(state);
|
|
}
|
|
//--- allocation
|
|
ArrayResize(x,2);
|
|
//--- change values
|
|
x[0]=20*CMath::RandomReal()-10;
|
|
x[1]=20*CMath::RandomReal()-10;
|
|
//--- function call
|
|
CNlEq::NlEqRestartFrom(state,x);
|
|
//--- cycle
|
|
while(CNlEq::NlEqIteration(state))
|
|
TestFuncHBM(state);
|
|
//--- function call
|
|
CNlEq::NlEqResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
basicserrors=basicserrors || CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7)>CMath::Sqr(epsf);
|
|
else
|
|
basicserrors=true;
|
|
}
|
|
//--- Basic tests
|
|
//--- Test with Himmelblau's function (1):
|
|
//--- * ability to find correct result
|
|
passcount=100;
|
|
for(pass=0;pass<=passcount-1;pass++)
|
|
{
|
|
//--- Ability to find correct result
|
|
ArrayResize(x,2);
|
|
x[0]=20*CMath::RandomReal()-10;
|
|
x[1]=20*CMath::RandomReal()-10;
|
|
//--- function call
|
|
CNlEq::NlEqCreateLM(2,1,x,state);
|
|
epsf=1.0E-9;
|
|
//--- function call
|
|
CNlEq::NlEqSetCond(state,epsf,0);
|
|
//--- cycle
|
|
while(CNlEq::NlEqIteration(state))
|
|
TestFuncHB1(state);
|
|
//--- function call
|
|
CNlEq::NlEqResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
basicserrors=basicserrors || CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7)>(double)(epsf);
|
|
else
|
|
basicserrors=true;
|
|
}
|
|
//--- Basic tests
|
|
//--- Ability to detect situation when we can't find minimum
|
|
passcount=100;
|
|
for(pass=0;pass<=passcount-1;pass++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(x,2);
|
|
x[0]=20*CMath::RandomReal()-10;
|
|
x[1]=20*CMath::RandomReal()-10;
|
|
//--- function call
|
|
CNlEq::NlEqCreateLM(2,3,x,state);
|
|
epsf=1.0E-9;
|
|
//--- function call
|
|
CNlEq::NlEqSetCond(state,epsf,0);
|
|
//--- cycle
|
|
while(CNlEq::NlEqIteration(state))
|
|
TestFuncSHBM(state);
|
|
//--- function call
|
|
CNlEq::NlEqResults(state,x,rep);
|
|
//--- search errors
|
|
basicserrors=basicserrors || rep.m_terminationtype!=-4;
|
|
}
|
|
//--- Test correctness of intermediate reports and final report:
|
|
//--- * first report is starting point
|
|
//--- * function value decreases on subsequent reports
|
|
//--- * function value is correctly reported
|
|
//--- * last report is final point
|
|
//--- * NFunc and NJac are compared with values counted directly
|
|
//--- * IterationsCount is compared with value counter directly
|
|
n=2;
|
|
ArrayResize(x,n);
|
|
ArrayResize(xlast,n);
|
|
//--- change values
|
|
x[0]=20*CMath::RandomReal()-10;
|
|
x[1]=20*CMath::RandomReal()-10;
|
|
xlast[0]=CMath::m_maxrealnumber;
|
|
xlast[1]=CMath::m_maxrealnumber;
|
|
//--- function calls
|
|
CNlEq::NlEqCreateLM(n,2,x,state);
|
|
CNlEq::NlEqSetCond(state,1.0E-6,0);
|
|
CNlEq::NlEqSetXRep(state,true);
|
|
//--- change values
|
|
firstrep=true;
|
|
flast=CMath::m_maxrealnumber;
|
|
nfunc=0;
|
|
njac=0;
|
|
itcnt=0;
|
|
//--- cycle
|
|
while(CNlEq::NlEqIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
//--- first report must be starting point
|
|
if(firstrep)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
othererrors=othererrors || state.m_x[i]!=x[i];
|
|
firstrep=false;
|
|
}
|
|
//--- function value must decrease
|
|
othererrors=othererrors || state.m_f>flast;
|
|
//--- check correctness of function value
|
|
v=CMath::Sqr(state.m_x[0]*state.m_x[0]+state.m_x[1]-11)+CMath::Sqr(state.m_x[0]+state.m_x[1]*state.m_x[1]-7);
|
|
othererrors=othererrors || MathAbs(v-state.m_f)/MathMax(v,1)>100*CMath::m_machineepsilon;
|
|
//--- update info and continue
|
|
for(i_=0;i_<=n-1;i_++)
|
|
xlast[i_]=state.m_x[i_];
|
|
flast=state.m_f;
|
|
itcnt=itcnt+1;
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(state.m_needf)
|
|
nfunc=nfunc+1;
|
|
//--- check
|
|
if(state.m_needfij)
|
|
{
|
|
nfunc=nfunc+1;
|
|
njac=njac+1;
|
|
}
|
|
//--- function call
|
|
TestFuncHBM(state);
|
|
}
|
|
//--- function call
|
|
CNlEq::NlEqResults(state,x,rep);
|
|
//--- check
|
|
if(rep.m_terminationtype>0)
|
|
{
|
|
othererrors=(othererrors || xlast[0]!=x[0]) || xlast[1]!=x[1];
|
|
v=CMath::Sqr(x[0]*x[0]+x[1]-11)+CMath::Sqr(x[0]+x[1]*x[1]-7);
|
|
othererrors=othererrors || MathAbs(flast-v)/MathMax(v,1)>100*CMath::m_machineepsilon;
|
|
}
|
|
else
|
|
converror=true;
|
|
//--- search errors
|
|
othererrors=othererrors || rep.m_nfunc!=nfunc;
|
|
othererrors=othererrors || rep.m_njac!=njac;
|
|
othererrors=othererrors || rep.m_iterationscount!=itcnt-1;
|
|
//--- Test ability to set limit on algorithm steps
|
|
ArrayResize(x,2);
|
|
ArrayResize(xlast,2);
|
|
//--- change values
|
|
x[0]=20*CMath::RandomReal()+20;
|
|
x[1]=20*CMath::RandomReal()+20;
|
|
xlast[0]=x[0];
|
|
xlast[1]=x[1];
|
|
stpmax=0.1+0.1*CMath::RandomReal();
|
|
epsf=1.0E-9;
|
|
//--- function calls
|
|
CNlEq::NlEqCreateLM(2,3,x,state);
|
|
CNlEq::NlEqSetStpMax(state,stpmax);
|
|
CNlEq::NlEqSetCond(state,epsf,0);
|
|
CNlEq::NlEqSetXRep(state,true);
|
|
//--- cycle
|
|
while(CNlEq::NlEqIteration(state))
|
|
{
|
|
//--- check
|
|
if(state.m_needf || state.m_needfij)
|
|
TestFuncHBM(state);
|
|
//--- check
|
|
if((state.m_needf || state.m_needfij) || state.m_xupdated)
|
|
othererrors=othererrors || MathSqrt(CMath::Sqr(state.m_x[0]-xlast[0])+CMath::Sqr(state.m_x[1]-xlast[1]))>1.00001*stpmax;
|
|
//--- check
|
|
if(state.m_xupdated)
|
|
{
|
|
xlast[0]=state.m_x[0];
|
|
xlast[1]=state.m_x[1];
|
|
}
|
|
}
|
|
//--- end
|
|
waserrors=(basicserrors || converror) || othererrors;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING NLEQ SOLVER");
|
|
Print("BASIC FUNCTIONALITY: ");
|
|
//--- check
|
|
if(basicserrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("CONVERGENCE: ");
|
|
//--- check
|
|
if(converror)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
Print("OTHER PROPERTIES: ");
|
|
//--- check
|
|
if(othererrors)
|
|
Print("FAILED");
|
|
else
|
|
Print("OK");
|
|
//--- check
|
|
if(waserrors)
|
|
Print("TEST FAILED");
|
|
else
|
|
Print("TEST PASSED");
|
|
Print("");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Himmelblau's function |
|
|
//| F=(x^2+y-11)^2 + (x+y^2-7)^2 |
|
|
//| posed as system of M functions: |
|
|
//| f0=x^2+y-11 |
|
|
//| f1=x+y^2-7 |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestNlEqUnit::TestFuncHBM(CNlEqState &state)
|
|
{
|
|
//--- create variables
|
|
double x=0;
|
|
double y=0;
|
|
//--- check
|
|
if(!CAp::Assert(state.m_needf || state.m_needfij,"TestNLEQUnit: internal error!"))
|
|
return;
|
|
//--- change values
|
|
x=state.m_x[0];
|
|
y=state.m_x[1];
|
|
//--- check
|
|
if(state.m_needf)
|
|
{
|
|
state.m_f=CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7);
|
|
return;
|
|
}
|
|
//--- check
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi[0]=x*x+y-11;
|
|
state.m_fi[1]=x+y*y-7;
|
|
state.m_j[0].Set(0,2*x);
|
|
state.m_j[0].Set(1,1);
|
|
state.m_j[1].Set(0,1);
|
|
state.m_j[1].Set(1,2*y);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Himmelblau's function |
|
|
//| F=(x^2+y-11)^2 + (x+y^2-7)^2 |
|
|
//| posed as system of 1 function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestNlEqUnit::TestFuncHB1(CNlEqState &state)
|
|
{
|
|
//--- create variables
|
|
double x=0;
|
|
double y=0;
|
|
//--- check
|
|
if(!CAp::Assert(state.m_needf || state.m_needfij,"TestNLEQUnit: internal error!"))
|
|
return;
|
|
//--- change values
|
|
x=state.m_x[0];
|
|
y=state.m_x[1];
|
|
//--- check
|
|
if(state.m_needf)
|
|
{
|
|
state.m_f=CMath::Sqr(CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7));
|
|
return;
|
|
}
|
|
//--- check
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi[0]=CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7);
|
|
state.m_j[0].Set(0,2*(x*x+y-11)*2*x+2*(x+y*y-7));
|
|
state.m_j[0].Set(1,2*(x*x+y-11)+2*(x+y*y-7)*2*y);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Shifted Himmelblau's function |
|
|
//| F=(x^2+y-11)^2 + (x+y^2-7)^2 + 1 |
|
|
//| posed as system of M functions: |
|
|
//| f0=x^2+y-11 |
|
|
//| f1=x+y^2-7 |
|
|
//| f2=1 |
|
|
//| This function is used to test algorithm on problem which has no |
|
|
//| solution. |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestNlEqUnit::TestFuncSHBM(CNlEqState &state)
|
|
{
|
|
//--- create variables
|
|
double x=0;
|
|
double y=0;
|
|
//--- check
|
|
if(!CAp::Assert(state.m_needf || state.m_needfij,"TestNLEQUnit: internal error!"))
|
|
return;
|
|
//--- change values
|
|
x=state.m_x[0];
|
|
y=state.m_x[1];
|
|
//--- check
|
|
if(state.m_needf)
|
|
{
|
|
state.m_f=CMath::Sqr(x*x+y-11)+CMath::Sqr(x+y*y-7)+1;
|
|
return;
|
|
}
|
|
//--- check
|
|
if(state.m_needfij)
|
|
{
|
|
state.m_fi[0]=x*x+y-11;
|
|
state.m_fi[1]=x+y*y-7;
|
|
state.m_fi[2]=1;
|
|
state.m_j[0].Set(0,2*x);
|
|
state.m_j[0].Set(1,1);
|
|
state.m_j[1].Set(0,1);
|
|
state.m_j[1].Set(1,2*y);
|
|
state.m_j[2].Set(0,0);
|
|
state.m_j[2].Set(1,0);
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CChebyshev |
|
|
//+------------------------------------------------------------------+
|
|
class CTestChebyshevUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestChebyshevUnit(void);
|
|
~CTestChebyshevUnit(void);
|
|
//--- public method
|
|
static bool TestChebyshev(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestChebyshevUnit::CTestChebyshevUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestChebyshevUnit::~CTestChebyshevUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CChebyshev |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestChebyshevUnit::TestChebyshev(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double err=0;
|
|
double sumerr=0;
|
|
double cerr=0;
|
|
double ferr=0;
|
|
double threshold=0;
|
|
double x=0;
|
|
double v=0;
|
|
int pass=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
int maxn=0;
|
|
bool waserrors;
|
|
int i_=0;
|
|
//--- create arrays
|
|
double c[];
|
|
double p1[];
|
|
double p2[];
|
|
//--- create matrix
|
|
CMatrixDouble a;
|
|
//--- initialization
|
|
err=0;
|
|
sumerr=0;
|
|
cerr=0;
|
|
ferr=0;
|
|
threshold=1.0E-9;
|
|
waserrors=false;
|
|
//--- Testing Chebyshev polynomials of the first kind
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,0,0.00)-1));
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,0,0.33)-1));
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,0,-0.42)-1));
|
|
x=0.2;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.2));
|
|
x=0.4;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.4));
|
|
x=0.6;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.6));
|
|
x=0.8;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-0.8));
|
|
x=1.0;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,1,x)-1.0));
|
|
x=0.2;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)+0.92));
|
|
x=0.4;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)+0.68));
|
|
x=0.6;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)+0.28));
|
|
x=0.8;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)-0.28));
|
|
x=1.0;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,2,x)-1.00));
|
|
n=10;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,n,0.2)-0.4284556288));
|
|
n=11;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,n,0.2)+0.7996160205));
|
|
n=12;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(1,n,0.2)+0.7483020370));
|
|
//--- Testing Chebyshev polynomials of the second kind
|
|
n=0;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-1.0000000000));
|
|
n=1;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-0.4000000000));
|
|
n=2;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.8400000000));
|
|
n=3;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.7360000000));
|
|
n=4;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-0.5456000000));
|
|
n=10;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)-0.6128946176));
|
|
n=11;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.6770370970));
|
|
n=12;
|
|
err=MathMax(err,MathAbs(CChebyshev::ChebyshevCalculate(2,n,0.2)+0.8837094564));
|
|
//--- Testing Clenshaw summation
|
|
maxn=20;
|
|
ArrayResize(c,maxn+1);
|
|
for(k=1;k<=2;k++)
|
|
{
|
|
for(pass=1;pass<=10;pass++)
|
|
{
|
|
x=2*CMath::RandomReal()-1;
|
|
v=0;
|
|
//--- calculation
|
|
for(n=0;n<=maxn;n++)
|
|
{
|
|
c[n]=2*CMath::RandomReal()-1;
|
|
v=v+CChebyshev::ChebyshevCalculate(k,n,x)*c[n];
|
|
//--- search errors
|
|
sumerr=MathMax(sumerr,MathAbs(v-CChebyshev::ChebyshevSum(c,k,n,x)));
|
|
}
|
|
}
|
|
}
|
|
//--- Testing coefficients
|
|
CChebyshev::ChebyshevCoefficients(0,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-1));
|
|
//--- function call
|
|
CChebyshev::ChebyshevCoefficients(1,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-1));
|
|
//--- function call
|
|
CChebyshev::ChebyshevCoefficients(2,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]+1));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-2));
|
|
//--- function call
|
|
CChebyshev::ChebyshevCoefficients(3,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+3));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]-4));
|
|
//--- function call
|
|
CChebyshev::ChebyshevCoefficients(4,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-1));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]+8));
|
|
cerr=MathMax(cerr,MathAbs(c[3]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-8));
|
|
//--- function call
|
|
CChebyshev::ChebyshevCoefficients(9,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-9));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]+120));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[5]-432));
|
|
cerr=MathMax(cerr,MathAbs(c[6]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[7]+576));
|
|
cerr=MathMax(cerr,MathAbs(c[8]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[9]-256));
|
|
//--- Testing FromChebyshev
|
|
maxn=10;
|
|
a.Resize(maxn+1,maxn+1);
|
|
for(i=0;i<=maxn;i++)
|
|
{
|
|
for(j=0;j<=maxn;j++)
|
|
a[i].Set(j,0);
|
|
//--- function call
|
|
CChebyshev::ChebyshevCoefficients(i,c);
|
|
for(i_=0;i_<=i;i_++)
|
|
a[i].Set(i_,c[i_]);
|
|
}
|
|
//--- allocation
|
|
ArrayResize(c,maxn+1);
|
|
ArrayResize(p1,maxn+1);
|
|
//--- calculation
|
|
for(n=0;n<=maxn;n++)
|
|
{
|
|
for(pass=1;pass<=10;pass++)
|
|
{
|
|
for(i=0;i<=n;i++)
|
|
p1[i]=0;
|
|
for(i=0;i<=n;i++)
|
|
{
|
|
//--- change values
|
|
c[i]=2*CMath::RandomReal()-1;
|
|
v=c[i];
|
|
for(i_=0;i_<=i;i_++)
|
|
p1[i_]=p1[i_]+v*a[i][i_];
|
|
}
|
|
//--- function call
|
|
CChebyshev::FromChebyshev(c,n,p2);
|
|
for(i=0;i<=n;i++)
|
|
ferr=MathMax(ferr,MathAbs(p1[i]-p2[i]));
|
|
}
|
|
}
|
|
//--- Reporting
|
|
waserrors=((err>threshold || sumerr>threshold) || cerr>threshold) || ferr>threshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING CALCULATION OF THE CHEBYSHEV POLYNOMIALS");
|
|
Print("Max error against table ");
|
|
Print("{0,5:E2}",err);
|
|
Print("Summation error ");
|
|
Print("{0,5:E2}",sumerr);
|
|
Print("Coefficients error ");
|
|
Print("{0,5:E2}",cerr);
|
|
Print("FrobChebyshev error ");
|
|
Print("{0,5:E2}",ferr);
|
|
Print("Threshold ");
|
|
Print("{0,5:E2}",threshold);
|
|
//--- check
|
|
if(!waserrors)
|
|
Print("TEST PASSED");
|
|
else
|
|
Print("TEST FAILED");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CHermite |
|
|
//+------------------------------------------------------------------+
|
|
class CTestHermiteUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestHermiteUnit(void);
|
|
~CTestHermiteUnit(void);
|
|
//--- method
|
|
static bool TestHermite(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestHermiteUnit::CTestHermiteUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestHermiteUnit::~CTestHermiteUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CHermite |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestHermiteUnit::TestHermite(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double err=0;
|
|
double sumerr=0;
|
|
double cerr=0;
|
|
double threshold=0;
|
|
int n=0;
|
|
int maxn=0;
|
|
int pass=0;
|
|
double x=0;
|
|
double v=0;
|
|
bool waserrors;
|
|
//--- create array
|
|
double c[];
|
|
//--- initialization
|
|
err=0;
|
|
sumerr=0;
|
|
cerr=0;
|
|
threshold=1.0E-9;
|
|
waserrors=false;
|
|
//--- Testing Hermite polynomials
|
|
n=0;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-1));
|
|
n=1;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-2));
|
|
n=2;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-2));
|
|
n=3;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)+4));
|
|
n=4;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)+20));
|
|
n=5;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)+8));
|
|
n=6;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-184));
|
|
n=7;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-464));
|
|
n=11;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-230848));
|
|
n=12;
|
|
err=MathMax(err,MathAbs(CHermite::HermiteCalculate(n,1)-280768));
|
|
//--- Testing Clenshaw summation
|
|
maxn=10;
|
|
ArrayResize(c,maxn+1);
|
|
for(pass=1;pass<=10;pass++)
|
|
{
|
|
x=2*CMath::RandomReal()-1;
|
|
v=0;
|
|
//--- calculation
|
|
for(n=0;n<=maxn;n++)
|
|
{
|
|
c[n]=2*CMath::RandomReal()-1;
|
|
v=v+CHermite::HermiteCalculate(n,x)*c[n];
|
|
//--- search errors
|
|
sumerr=MathMax(sumerr,MathAbs(v-CHermite::HermiteSum(c,n,x)));
|
|
}
|
|
}
|
|
//--- Testing coefficients
|
|
CHermite::HermiteCoefficients(0,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-1));
|
|
//--- function call
|
|
CHermite::HermiteCoefficients(1,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-2));
|
|
//--- function call
|
|
CHermite::HermiteCoefficients(2,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]+2));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-4));
|
|
//--- function call
|
|
CHermite::HermiteCoefficients(3,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+12));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]-8));
|
|
//--- function call
|
|
CHermite::HermiteCoefficients(4,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-12));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]+48));
|
|
cerr=MathMax(cerr,MathAbs(c[3]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-16));
|
|
//--- function call
|
|
CHermite::HermiteCoefficients(5,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-120));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]+160));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[5]-32));
|
|
//--- function call
|
|
CHermite::HermiteCoefficients(6,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]+120));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-720));
|
|
cerr=MathMax(cerr,MathAbs(c[3]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[4]+480));
|
|
cerr=MathMax(cerr,MathAbs(c[5]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[6]-64));
|
|
//--- Reporting
|
|
waserrors=(err>threshold || sumerr>threshold) || cerr>threshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING CALCULATION OF THE HERMITE POLYNOMIALS");
|
|
Print("Max error ");
|
|
Print("{0,5:E2}",err);
|
|
Print("Summation error ");
|
|
Print("{0,5:E2}",sumerr);
|
|
Print("Coefficients error ");
|
|
Print("{0,5:E2}",cerr);
|
|
Print("Threshold ");
|
|
Print("{0,5:E2}",threshold);
|
|
//--- check
|
|
if(!waserrors)
|
|
Print("TEST PASSED");
|
|
else
|
|
Print("TEST FAILED");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLaguerre |
|
|
//+------------------------------------------------------------------+
|
|
class CTestLaguerreUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestLaguerreUnit(void);
|
|
~CTestLaguerreUnit(void);
|
|
//--- public method
|
|
static bool TestLaguerre(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestLaguerreUnit::CTestLaguerreUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestLaguerreUnit::~CTestLaguerreUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLaguerre |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestLaguerreUnit::TestLaguerre(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double err=0;
|
|
double sumerr=0;
|
|
double cerr=0;
|
|
double threshold=0;
|
|
int n=0;
|
|
int maxn=0;
|
|
int pass=0;
|
|
double x=0;
|
|
double v=0;
|
|
bool waserrors;
|
|
//--- create array
|
|
double c[];
|
|
//--- initialization
|
|
err=0;
|
|
sumerr=0;
|
|
cerr=0;
|
|
threshold=1.0E-9;
|
|
waserrors=false;
|
|
//--- Testing Laguerre polynomials
|
|
n=0;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)-1.0000000000));
|
|
n=1;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)-0.5000000000));
|
|
n=2;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)-0.1250000000));
|
|
n=3;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.1458333333));
|
|
n=4;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.3307291667));
|
|
n=5;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.4455729167));
|
|
n=6;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.5041449653));
|
|
n=7;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.5183392237));
|
|
n=8;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.4983629984));
|
|
n=9;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.4529195204));
|
|
n=10;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.3893744141));
|
|
n=11;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.3139072988));
|
|
n=12;
|
|
err=MathMax(err,MathAbs(CLaguerre::LaguerreCalculate(n,0.5)+0.2316496389));
|
|
//--- Testing Clenshaw summation
|
|
maxn=20;
|
|
ArrayResize(c,maxn+1);
|
|
for(pass=1;pass<=10;pass++)
|
|
{
|
|
x=2*CMath::RandomReal()-1;
|
|
v=0;
|
|
//--- calculation
|
|
for(n=0;n<=maxn;n++)
|
|
{
|
|
c[n]=2*CMath::RandomReal()-1;
|
|
v=v+CLaguerre::LaguerreCalculate(n,x)*c[n];
|
|
//--- search errors
|
|
sumerr=MathMax(sumerr,MathAbs(v-CLaguerre::LaguerreSum(c,n,x)));
|
|
}
|
|
}
|
|
//--- Testing coefficients
|
|
CLaguerre::LaguerreCoefficients(0,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-1));
|
|
//--- function call
|
|
CLaguerre::LaguerreCoefficients(1,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-1));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+1));
|
|
//--- function call
|
|
CLaguerre::LaguerreCoefficients(2,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-2.0/2.0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+4.0/2.0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-1.0/2.0));
|
|
//--- function call
|
|
CLaguerre::LaguerreCoefficients(3,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-6.0/6.0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+18.0/6.0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-9.0/6.0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]+1.0/6.0));
|
|
//--- function call
|
|
CLaguerre::LaguerreCoefficients(4,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-24.0/24.0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+96.0/24.0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-72.0/24.0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]+16.0/24.0));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-1.0/24.0));
|
|
//--- function call
|
|
CLaguerre::LaguerreCoefficients(5,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-120.0/120.0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+600.0/120.0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-600.0/120.0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]+200.0/120.0));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-25.0/120.0));
|
|
cerr=MathMax(cerr,MathAbs(c[5]+1.0/120.0));
|
|
//--- function call
|
|
CLaguerre::LaguerreCoefficients(6,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-720.0/720.0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+4320.0/720.0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-5400.0/720.0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]+2400.0/720.0));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-450.0/720.0));
|
|
cerr=MathMax(cerr,MathAbs(c[5]+36.0/720.0));
|
|
cerr=MathMax(cerr,MathAbs(c[6]-1.0/720.0));
|
|
//--- Reporting
|
|
waserrors=(err>threshold || sumerr>threshold) || cerr>threshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING CALCULATION OF THE LAGUERRE POLYNOMIALS");
|
|
Print("Max error ");
|
|
Print("{0,5:E2}",err);
|
|
Print("Summation error ");
|
|
Print("{0,5:E2}",sumerr);
|
|
Print("Coefficients error ");
|
|
Print("{0,5:E2}",cerr);
|
|
Print("Threshold ");
|
|
Print("{0,5:E2}",threshold);
|
|
//--- check
|
|
if(!waserrors)
|
|
Print("TEST PASSED");
|
|
else
|
|
Print("TEST FAILED");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLegendre |
|
|
//+------------------------------------------------------------------+
|
|
class CTestLegendreUnit
|
|
{
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestLegendreUnit(void);
|
|
~CTestLegendreUnit(void);
|
|
//--- public method
|
|
static bool TestLegendre(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestLegendreUnit::CTestLegendreUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestLegendreUnit::~CTestLegendreUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing class CLegendre |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestLegendreUnit::TestLegendre(const bool silent)
|
|
{
|
|
//--- create variables
|
|
double err=0;
|
|
double sumerr=0;
|
|
double cerr=0;
|
|
double threshold=0;
|
|
int n=0;
|
|
int maxn=0;
|
|
int i=0;
|
|
int pass=0;
|
|
double x=0;
|
|
double v=0;
|
|
double t=0;
|
|
bool waserrors;
|
|
//--- create array
|
|
double c[];
|
|
//--- initialization
|
|
err=0;
|
|
sumerr=0;
|
|
cerr=0;
|
|
threshold=1.0E-9;
|
|
waserrors=false;
|
|
//--- Testing Legendre polynomials values
|
|
for(n=0;n<=10;n++)
|
|
{
|
|
//--- function call
|
|
CLegendre::LegendreCoefficients(n,c);
|
|
for(pass=1;pass<=10;pass++)
|
|
{
|
|
//--- calculation
|
|
x=2*CMath::RandomReal()-1;
|
|
v=CLegendre::LegendreCalculate(n,x);
|
|
t=1;
|
|
for(i=0;i<=n;i++)
|
|
{
|
|
v=v-c[i]*t;
|
|
t=t*x;
|
|
}
|
|
//--- search errors
|
|
err=MathMax(err,MathAbs(v));
|
|
}
|
|
}
|
|
//--- Testing Clenshaw summation
|
|
maxn=20;
|
|
ArrayResize(c,maxn+1);
|
|
for(pass=1;pass<=10;pass++)
|
|
{
|
|
//--- change values
|
|
x=2*CMath::RandomReal()-1;
|
|
v=0;
|
|
for(n=0;n<=maxn;n++)
|
|
{
|
|
c[n]=2*CMath::RandomReal()-1;
|
|
v=v+CLegendre::LegendreCalculate(n,x)*c[n];
|
|
//--- search errors
|
|
sumerr=MathMax(sumerr,MathAbs(v-CLegendre::LegendreSum(c,n,x)));
|
|
}
|
|
}
|
|
//--- Testing coefficients
|
|
CLegendre::LegendreCoefficients(0,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-1));
|
|
//--- calculation
|
|
CLegendre::LegendreCoefficients(1,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-1));
|
|
//--- calculation
|
|
CLegendre::LegendreCoefficients(2,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]+1.0/2.0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-3.0/2.0));
|
|
//--- calculation
|
|
CLegendre::LegendreCoefficients(3,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]+3.0/2.0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]-5.0/2.0));
|
|
//--- calculation
|
|
CLegendre::LegendreCoefficients(4,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-3.0/8.0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]+30.0/8.0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-35.0/8.0));
|
|
//--- calculation
|
|
CLegendre::LegendreCoefficients(9,c);
|
|
//--- search errors
|
|
cerr=MathMax(cerr,MathAbs(c[0]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[1]-315.0/128.0));
|
|
cerr=MathMax(cerr,MathAbs(c[2]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[3]+4620.0/128.0));
|
|
cerr=MathMax(cerr,MathAbs(c[4]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[5]-18018.0/128.0));
|
|
cerr=MathMax(cerr,MathAbs(c[6]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[7]+25740.0/128.0));
|
|
cerr=MathMax(cerr,MathAbs(c[8]-0));
|
|
cerr=MathMax(cerr,MathAbs(c[9]-12155.0/128.0));
|
|
//--- Reporting
|
|
waserrors=(err>threshold || sumerr>threshold) || cerr>threshold;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
Print("TESTING CALCULATION OF THE LEGENDRE POLYNOMIALS");
|
|
Print("Max error ");
|
|
Print("{0,5:E2}",err);
|
|
Print("Summation error ");
|
|
Print("{0,5:E2}",sumerr);
|
|
Print("Coefficients error ");
|
|
Print("{0,5:E2}",cerr);
|
|
Print("Threshold ");
|
|
Print("{0,5:E2}",threshold);
|
|
//--- check
|
|
if(!waserrors)
|
|
Print("TEST PASSED");
|
|
else
|
|
Print("TEST FAILED");
|
|
}
|
|
//--- return result
|
|
return(!waserrors);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary class |
|
|
//+------------------------------------------------------------------+
|
|
class CRec1
|
|
{
|
|
public:
|
|
//--- class variables
|
|
bool m_bfield;
|
|
double m_rfield;
|
|
int m_ifield;
|
|
complex m_cfield;
|
|
//--- arrays
|
|
bool m_b1field[];
|
|
double m_r1field[];
|
|
int m_i1field[];
|
|
complex m_c1field[];
|
|
//--- matrix
|
|
CMatrixInt m_b2field;
|
|
CMatrixDouble m_r2field;
|
|
CMatrixInt m_i2field;
|
|
CMatrixComplex m_c2field;
|
|
//--- constructor, destructor
|
|
CRec1(void);
|
|
~CRec1(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CRec1::CRec1(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CRec1::~CRec1(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary class |
|
|
//+------------------------------------------------------------------+
|
|
class CRec4Serialization
|
|
{
|
|
public:
|
|
//--- arrays
|
|
bool m_b[];
|
|
int m_i[];
|
|
double m_r[];
|
|
//--- constructor, destructor
|
|
CRec4Serialization(void);
|
|
~CRec4Serialization(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CRec4Serialization::CRec4Serialization(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CRec4Serialization::~CRec4Serialization(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing the basic functions |
|
|
//+------------------------------------------------------------------+
|
|
class CTestAlglibBasicsUnit
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static bool TestComplexArithmetics(const bool silent);
|
|
static bool TestIEEESpecial(const bool silent);
|
|
static bool TestSwapFunctions(const bool silent);
|
|
static bool TestSerializationFunctions(const bool silent);
|
|
public:
|
|
//--- constructor, destructor
|
|
CTestAlglibBasicsUnit(void);
|
|
~CTestAlglibBasicsUnit(void);
|
|
//--- public methods
|
|
static void Rec4SerializationAlloc(CSerializer &s,CRec4Serialization &v);
|
|
static void Rec4SerializationSerialize(CSerializer &s,CRec4Serialization &v);
|
|
static void Rec4SerializationUnserialize(CSerializer &s,CRec4Serialization &v);
|
|
static bool TestAlglibBasics(const bool silent);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CTestAlglibBasicsUnit::CTestAlglibBasicsUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CTestAlglibBasicsUnit::~CTestAlglibBasicsUnit(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestAlglibBasicsUnit::Rec4SerializationAlloc(CSerializer &s,
|
|
CRec4Serialization &v)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- boolean fields
|
|
s.Alloc_Entry();
|
|
for(i=0;i<=CAp::Len(v.m_b)-1;i++)
|
|
s.Alloc_Entry();
|
|
//--- integer fields
|
|
s.Alloc_Entry();
|
|
for(i=0;i<=CAp::Len(v.m_i)-1;i++)
|
|
s.Alloc_Entry();
|
|
//--- real fields
|
|
s.Alloc_Entry();
|
|
for(i=0;i<=CAp::Len(v.m_r)-1;i++)
|
|
s.Alloc_Entry();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestAlglibBasicsUnit::Rec4SerializationSerialize(CSerializer &s,
|
|
CRec4Serialization &v)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- boolean fields
|
|
s.Serialize_Int(CAp::Len(v.m_b));
|
|
for(i=0;i<=CAp::Len(v.m_b)-1;i++)
|
|
s.Serialize_Bool(v.m_b[i]);
|
|
//--- integer fields
|
|
s.Serialize_Int(CAp::Len(v.m_i));
|
|
for(i=0;i<=CAp::Len(v.m_i)-1;i++)
|
|
s.Serialize_Int(v.m_i[i]);
|
|
//--- real fields
|
|
s.Serialize_Int(CAp::Len(v.m_r));
|
|
for(i=0;i<=CAp::Len(v.m_r)-1;i++)
|
|
s.Serialize_Double(v.m_r[i]);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The auxiliary function |
|
|
//+------------------------------------------------------------------+
|
|
static void CTestAlglibBasicsUnit::Rec4SerializationUnserialize(CSerializer &s,
|
|
CRec4Serialization &v)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int k=0;
|
|
bool bv;
|
|
int iv=0;
|
|
double rv=0;
|
|
//--- boolean fields
|
|
k=s.Unserialize_Int();
|
|
//--- check
|
|
if(k>0)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(v.m_b,k);
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
bv=s.Unserialize_Bool();
|
|
v.m_b[i]=bv;
|
|
}
|
|
}
|
|
//--- integer fields
|
|
k=s.Unserialize_Int();
|
|
//--- check
|
|
if(k>0)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(v.m_i,k);
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
iv=s.Unserialize_Int();
|
|
v.m_i[i]=iv;
|
|
}
|
|
}
|
|
//--- real fields
|
|
k=s.Unserialize_Int();
|
|
//--- check
|
|
if(k>0)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(v.m_r,k);
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
rv=s.Unserialize_Double();
|
|
v.m_r[i]=rv;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Testing the basic functions |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestAlglibBasicsUnit::TestAlglibBasics(const bool silent)
|
|
{
|
|
//--- create a variable
|
|
bool result;
|
|
//--- function calls
|
|
result=true;
|
|
result=result && TestComplexArithmetics(silent);
|
|
result=result && TestIEEESpecial(silent);
|
|
result=result && TestSwapFunctions(silent);
|
|
result=result && TestSerializationFunctions(silent);
|
|
//--- check
|
|
if(!silent)
|
|
Print("");
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Complex arithmetics test |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestAlglibBasicsUnit::TestComplexArithmetics(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool absc;
|
|
bool addcc;
|
|
bool addcr;
|
|
bool addrc;
|
|
bool subcc;
|
|
bool subcr;
|
|
bool subrc;
|
|
bool mulcc;
|
|
bool mulcr;
|
|
bool mulrc;
|
|
bool divcc;
|
|
bool divcr;
|
|
bool divrc;
|
|
complex ca=0;
|
|
complex cb=0;
|
|
complex res=0;
|
|
double ra=0;
|
|
double rb=0;
|
|
double threshold=0;
|
|
int pass=0;
|
|
int passcount=0;
|
|
//--- initialization
|
|
threshold=100*CMath::m_machineepsilon;
|
|
passcount=1000;
|
|
result=true;
|
|
absc=true;
|
|
addcc=true;
|
|
addcr=true;
|
|
addrc=true;
|
|
subcc=true;
|
|
subcr=true;
|
|
subrc=true;
|
|
mulcc=true;
|
|
mulcr=true;
|
|
mulrc=true;
|
|
divcc=true;
|
|
divcr=true;
|
|
divrc=true;
|
|
//--- calculation
|
|
for(pass=1;pass<=passcount;pass++)
|
|
{
|
|
//--- Test AbsC
|
|
ca.re=2*CMath::RandomReal()-1;
|
|
ca.im=2*CMath::RandomReal()-1;
|
|
ra=CMath::AbsComplex(ca);
|
|
absc=absc && MathAbs(ra-MathSqrt(CMath::Sqr(ca.re)+CMath::Sqr(ca.im)))<threshold;
|
|
//--- test Add
|
|
ca.re=2*CMath::RandomReal()-1;
|
|
ca.im=2*CMath::RandomReal()-1;
|
|
cb.re=2*CMath::RandomReal()-1;
|
|
cb.im=2*CMath::RandomReal()-1;
|
|
ra=2*CMath::RandomReal()-1;
|
|
rb=2*CMath::RandomReal()-1;
|
|
res=ca+cb;
|
|
addcc=(addcc && MathAbs(res.re-ca.re-cb.re)<threshold) && MathAbs(res.im-ca.im-cb.im)<threshold;
|
|
res=ca+rb;
|
|
addcr=(addcr && MathAbs(res.re-ca.re-rb)<threshold) && MathAbs(res.im-ca.im)<threshold;
|
|
res=cb+ra;
|
|
addrc=(addrc && MathAbs(res.re-ra-cb.re)<threshold) && MathAbs(res.im-cb.im)<threshold;
|
|
//--- test Sub
|
|
ca.re=2*CMath::RandomReal()-1;
|
|
ca.im=2*CMath::RandomReal()-1;
|
|
cb.re=2*CMath::RandomReal()-1;
|
|
cb.im=2*CMath::RandomReal()-1;
|
|
ra=2*CMath::RandomReal()-1;
|
|
rb=2*CMath::RandomReal()-1;
|
|
res=ca-cb;
|
|
subcc=(subcc && MathAbs(res.re-(ca.re-cb.re))<threshold) && MathAbs(res.im-(ca.im-cb.im))<threshold;
|
|
res=ca-rb;
|
|
subcr=(subcr && MathAbs(res.re-(ca.re-rb))<threshold) && MathAbs(res.im-ca.im)<threshold;
|
|
res=-cb+ra;
|
|
subrc=(subrc && MathAbs(res.re-(ra-cb.re))<threshold) && MathAbs(res.im+cb.im)<threshold;
|
|
//--- test Mul
|
|
ca.re=2*CMath::RandomReal()-1;
|
|
ca.im=2*CMath::RandomReal()-1;
|
|
cb.re=2*CMath::RandomReal()-1;
|
|
cb.im=2*CMath::RandomReal()-1;
|
|
ra=2*CMath::RandomReal()-1;
|
|
rb=2*CMath::RandomReal()-1;
|
|
res=ca*cb;
|
|
mulcc=(mulcc && MathAbs(res.re-(ca.re*cb.re-ca.im*cb.im))<threshold) && MathAbs(res.im-(ca.re*cb.im+ca.im*cb.re))<threshold;
|
|
res=ca*rb;
|
|
mulcr=(mulcr && MathAbs(res.re-ca.re*rb)<threshold) && MathAbs(res.im-ca.im*rb)<threshold;
|
|
res=cb*ra;
|
|
mulrc=(mulrc && MathAbs(res.re-ra*cb.re)<threshold) && MathAbs(res.im-ra*cb.im)<threshold;
|
|
//--- test Div
|
|
ca.re=2*CMath::RandomReal()-1;
|
|
ca.im=2*CMath::RandomReal()-1;
|
|
do
|
|
{
|
|
cb.re=2*CMath::RandomReal()-1;
|
|
cb.im=2*CMath::RandomReal()-1;
|
|
}
|
|
while(CMath::AbsComplex(cb)<=0.5);
|
|
ra=2*CMath::RandomReal()-1;
|
|
do
|
|
{
|
|
rb=2*CMath::RandomReal()-1;
|
|
}
|
|
while(MathAbs(rb)<=0.5);
|
|
res=ca/cb;
|
|
divcc=(divcc && MathAbs((res*cb).re-ca.re)<threshold) && MathAbs((res*cb).im-ca.im)<threshold;
|
|
res=ca/rb;
|
|
divcr=(divcr && MathAbs(res.re-ca.re/rb)<threshold) && MathAbs(res.im-ca.im/rb)<threshold;
|
|
complex cra=ra;
|
|
res=cra/cb;
|
|
divrc=(divrc && MathAbs((res*cb).re-ra)<threshold) && MathAbs((res*cb).im)<threshold;
|
|
}
|
|
//--- summary
|
|
result=result && absc;
|
|
result=result && addcc;
|
|
result=result && addcr;
|
|
result=result && addrc;
|
|
result=result && subcc;
|
|
result=result && subcr;
|
|
result=result && subrc;
|
|
result=result && mulcc;
|
|
result=result && mulcr;
|
|
result=result && mulrc;
|
|
result=result && divcc;
|
|
result=result && divcr;
|
|
result=result && divrc;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
//--- check
|
|
if(result)
|
|
Print("COMPLEX ARITHMETICS: OK");
|
|
else
|
|
{
|
|
Print("COMPLEX ARITHMETICS: FAILED");
|
|
Print("* AddCC ");
|
|
//--- check
|
|
if(addcc)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* AddCR ");
|
|
//--- check
|
|
if(addcr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* AddRC ");
|
|
//--- check
|
|
if(addrc)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* SubCC ");
|
|
//--- check
|
|
if(subcc)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* SubCR ");
|
|
//--- check
|
|
if(subcr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* SubRC ");
|
|
//--- check
|
|
if(subrc)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* MulCC ");
|
|
//--- check
|
|
if(mulcc)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* MulCR ");
|
|
//--- check
|
|
if(mulcr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* MulRC ");
|
|
//--- check
|
|
if(mulrc)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* DivCC ");
|
|
//--- check
|
|
if(divcc)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* DivCR ");
|
|
//--- check
|
|
if(divcr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* DivRC ");
|
|
//--- check
|
|
if(divrc)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests for IEEE special quantities |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestAlglibBasicsUnit::TestIEEESpecial(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool oknan;
|
|
bool okinf;
|
|
bool okother;
|
|
double v1=0;
|
|
double v2=0;
|
|
//--- initialization
|
|
result=true;
|
|
oknan=true;
|
|
okinf=true;
|
|
okother=true;
|
|
//--- Test classification functions
|
|
okother=okother && !CInfOrNaN::IsInfinity(CInfOrNaN::NaN());
|
|
okother=okother && CInfOrNaN::IsInfinity(CInfOrNaN::PositiveInfinity());
|
|
okother=okother && !CInfOrNaN::IsInfinity(CMath::m_maxrealnumber);
|
|
okother=okother && !CInfOrNaN::IsInfinity(1.0);
|
|
okother=okother && !CInfOrNaN::IsInfinity(CMath::m_minrealnumber);
|
|
okother=okother && !CInfOrNaN::IsInfinity(0.0);
|
|
okother=okother && !CInfOrNaN::IsInfinity(-CMath::m_minrealnumber);
|
|
okother=okother && !CInfOrNaN::IsInfinity(-1.0);
|
|
okother=okother && !CInfOrNaN::IsInfinity(-CMath::m_maxrealnumber);
|
|
okother=okother && CInfOrNaN::IsInfinity(CInfOrNaN::NegativeInfinity());
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(CInfOrNaN::NaN());
|
|
okother=okother && CInfOrNaN::IsPositiveInfinity(CInfOrNaN::PositiveInfinity());
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(CMath::m_maxrealnumber);
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(1.0);
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(CMath::m_minrealnumber);
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(0.0);
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(-CMath::m_minrealnumber);
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(-1.0);
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(-CMath::m_maxrealnumber);
|
|
okother=okother && !CInfOrNaN::IsPositiveInfinity(CInfOrNaN::NegativeInfinity());
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(CInfOrNaN::NaN());
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(CInfOrNaN::PositiveInfinity());
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(CMath::m_maxrealnumber);
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(1.0);
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(CMath::m_minrealnumber);
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(0.0);
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(-CMath::m_minrealnumber);
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(-1.0);
|
|
okother=okother && !CInfOrNaN::IsNegativeInfinity(-CMath::m_maxrealnumber);
|
|
okother=okother && CInfOrNaN::IsNegativeInfinity(CInfOrNaN::NegativeInfinity());
|
|
okother=okother && CInfOrNaN::IsNaN(CInfOrNaN::NaN());
|
|
okother=okother && !CInfOrNaN::IsNaN(CInfOrNaN::PositiveInfinity());
|
|
okother=okother && !CInfOrNaN::IsNaN(CMath::m_maxrealnumber);
|
|
okother=okother && !CInfOrNaN::IsNaN(1.0);
|
|
okother=okother && !CInfOrNaN::IsNaN(CMath::m_minrealnumber);
|
|
okother=okother && !CInfOrNaN::IsNaN(0.0);
|
|
okother=okother && !CInfOrNaN::IsNaN(-CMath::m_minrealnumber);
|
|
okother=okother && !CInfOrNaN::IsNaN(-1.0);
|
|
okother=okother && !CInfOrNaN::IsNaN(-CMath::m_maxrealnumber);
|
|
okother=okother && !CInfOrNaN::IsNaN(CInfOrNaN::NegativeInfinity());
|
|
okother=okother && !CMath::IsFinite(CInfOrNaN::NaN());
|
|
okother=okother && !CMath::IsFinite(CInfOrNaN::PositiveInfinity());
|
|
okother=okother && CMath::IsFinite(CMath::m_maxrealnumber);
|
|
okother=okother && CMath::IsFinite(1.0);
|
|
okother=okother && CMath::IsFinite(CMath::m_minrealnumber);
|
|
okother=okother && CMath::IsFinite(0.0);
|
|
okother=okother && CMath::IsFinite(-CMath::m_minrealnumber);
|
|
okother=okother && CMath::IsFinite(-1.0);
|
|
okother=okother && CMath::IsFinite(-CMath::m_maxrealnumber);
|
|
okother=okother && !CMath::IsFinite(CInfOrNaN::NegativeInfinity());
|
|
//--- Test NAN
|
|
v1=CInfOrNaN::NaN();
|
|
oknan=oknan && CInfOrNaN::IsNaN(v1);
|
|
//--- Test INF:
|
|
//--- * basic properties
|
|
//--- * comparisons involving PosINF on one of the sides
|
|
//--- * comparisons involving NegINF on one of the sides
|
|
v1=CInfOrNaN::PositiveInfinity();
|
|
v2=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::IsInfinity(CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && CInfOrNaN::IsInfinity(v1);
|
|
okinf=okinf && CInfOrNaN::IsInfinity(CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && CInfOrNaN::IsInfinity(v2);
|
|
okinf=okinf && CInfOrNaN::IsPositiveInfinity(CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && CInfOrNaN::IsPositiveInfinity(v1);
|
|
okinf=okinf && !CInfOrNaN::IsPositiveInfinity(CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !CInfOrNaN::IsPositiveInfinity(v2);
|
|
okinf=okinf && !CInfOrNaN::IsNegativeInfinity(CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !CInfOrNaN::IsNegativeInfinity(v1);
|
|
okinf=okinf && CInfOrNaN::IsNegativeInfinity(CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && CInfOrNaN::IsNegativeInfinity(v2);
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()==CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()==v1;
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()==CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()==v2);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()==0.0);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()==1.2);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()==-1.2);
|
|
okinf=okinf && v1==CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(v2==CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(0.0==CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(1.2==CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(-1.2==CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()!=CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()!=v1);
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()!=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()!=v2;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()!=0.0;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()!=1.2;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()!=-1.2;
|
|
okinf=okinf && !(v1!=CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()!=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && v2!=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && 0.0!=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && 1.2!=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && -1.2!=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<v1);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<v2);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<0.0);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<1.2);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<-1.2);
|
|
okinf=okinf && !(v1<CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && v2<CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && 0.0<CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && 1.2<CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && -1.2<CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()<=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()<=v1;
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<=CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<=v2);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<=0.0);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<=1.2);
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()<=-1.2);
|
|
okinf=okinf && v1<=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && v2<=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && 0.0<=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && 1.2<=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && -1.2<=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()>CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::PositiveInfinity()>v1);
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>v2;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>0.0;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>1.2;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>-1.2;
|
|
okinf=okinf && !(v1>CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(v2>CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(0.0>CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(1.2>CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(-1.2>CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>=v1;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>=v2;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>=0.0;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>=1.2;
|
|
okinf=okinf && CInfOrNaN::PositiveInfinity()>=-1.2;
|
|
okinf=okinf && v1>=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(v2>=CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(0.0>=CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(1.2>=CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(-1.2>=CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==v1);
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()==CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()==v2;
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==0.0);
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==1.2);
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()==-1.2);
|
|
okinf=okinf && !(v1==CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()==CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && v2==CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && !(0.0==CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(1.2==CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(-1.2==CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()!=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()!=v1;
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()!=CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()!=v2);
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()!=0.0;
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()!=1.2;
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()!=-1.2;
|
|
okinf=okinf && v1!=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()!=CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(v2!=CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && 0.0!=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && 1.2!=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && -1.2!=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<v1;
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()<CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()<v2);
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<0.0;
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<1.2;
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<-1.2;
|
|
okinf=okinf && !(v1<CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()<CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(v2<CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(0.0<CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(1.2<CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(-1.2<CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=CInfOrNaN::PositiveInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=v1;
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=v2;
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=0.0;
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=1.2;
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=-1.2;
|
|
okinf=okinf && !(v1<=CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()<=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && v2<=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && !(0.0<=CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(1.2<=CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(-1.2<=CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>v1);
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>v2);
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>0.0);
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>1.2);
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>-1.2);
|
|
okinf=okinf && v1>CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && !(v2>CInfOrNaN::NegativeInfinity());
|
|
okinf=okinf && 0.0>CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && 1.2>CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && -1.2>CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=CInfOrNaN::PositiveInfinity());
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=v1);
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()>=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()>=v2;
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=0.0);
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=1.2);
|
|
okinf=okinf && !(CInfOrNaN::NegativeInfinity()>=-1.2);
|
|
okinf=okinf && v1>=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && CInfOrNaN::NegativeInfinity()>=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && v2>=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && 0.0>=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && 1.2>=CInfOrNaN::NegativeInfinity();
|
|
okinf=okinf && -1.2>=CInfOrNaN::NegativeInfinity();
|
|
//--- summary
|
|
result=result && oknan;
|
|
result=result && okinf;
|
|
result=result && okother;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
//--- check
|
|
if(result)
|
|
Print("IEEE SPECIAL VALUES: OK");
|
|
else
|
|
{
|
|
Print("IEEE SPECIAL VALUES: FAILED");
|
|
Print("* NAN ");
|
|
//--- check
|
|
if(oknan)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* INF ");
|
|
//--- check
|
|
if(okinf)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* FUNCTIONS ");
|
|
//--- check
|
|
if(okother)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests for swapping functions |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestAlglibBasicsUnit::TestSwapFunctions(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool okb1;
|
|
bool okb2;
|
|
bool oki1;
|
|
bool oki2;
|
|
bool okr1;
|
|
bool okr2;
|
|
bool okc1;
|
|
bool okc2;
|
|
//--- create arrays
|
|
bool b11[];
|
|
bool b12[];
|
|
int i11[];
|
|
int i12[];
|
|
double r11[];
|
|
double r12[];
|
|
complex c11[];
|
|
complex c12[];
|
|
//--- create matrix
|
|
CMatrixInt b21;
|
|
CMatrixInt b22;
|
|
CMatrixInt i21;
|
|
CMatrixInt i22;
|
|
CMatrixDouble r21;
|
|
CMatrixDouble r22;
|
|
CMatrixComplex c21;
|
|
CMatrixComplex c22;
|
|
//--- initialization
|
|
result=true;
|
|
okb1=true;
|
|
okb2=true;
|
|
oki1=true;
|
|
oki2=true;
|
|
okr1=true;
|
|
okr2=true;
|
|
okc1=true;
|
|
okc2=true;
|
|
//--- Test B1 swaps
|
|
ArrayResize(b11,1);
|
|
ArrayResize(b12,2);
|
|
//--- change values
|
|
b11[0]=true;
|
|
b12[0]=false;
|
|
b12[1]=true;
|
|
//--- function call
|
|
CAp::Swap(b11,b12);
|
|
//--- check
|
|
if(CAp::Len(b11)==2 && CAp::Len(b12)==1)
|
|
{
|
|
okb1=okb1 && !b11[0];
|
|
okb1=okb1 && b11[1];
|
|
okb1=okb1 && b12[0];
|
|
}
|
|
else
|
|
okb1=false;
|
|
//--- Test I1 swaps
|
|
ArrayResize(i11,1);
|
|
ArrayResize(i12,2);
|
|
//--- change values
|
|
i11[0]=1;
|
|
i12[0]=2;
|
|
i12[1]=3;
|
|
//--- function call
|
|
CAp::Swap(i11,i12);
|
|
//--- check
|
|
if(CAp::Len(i11)==2 && CAp::Len(i12)==1)
|
|
{
|
|
oki1=oki1 && i11[0]==2;
|
|
oki1=oki1 && i11[1]==3;
|
|
oki1=oki1 && i12[0]==1;
|
|
}
|
|
else
|
|
oki1=false;
|
|
//--- Test R1 swaps
|
|
ArrayResize(r11,1);
|
|
ArrayResize(r12,2);
|
|
//--- change values
|
|
r11[0]=1.5;
|
|
r12[0]=2.5;
|
|
r12[1]=3.5;
|
|
//--- function call
|
|
CAp::Swap(r11,r12);
|
|
//--- check
|
|
if(CAp::Len(r11)==2 && CAp::Len(r12)==1)
|
|
{
|
|
okr1=okr1 && r11[0]==2.5;
|
|
okr1=okr1 && r11[1]==3.5;
|
|
okr1=okr1 && r12[0]==1.5;
|
|
}
|
|
else
|
|
okr1=false;
|
|
//--- Test C1 swaps
|
|
ArrayResize(c11,1);
|
|
ArrayResize(c12,2);
|
|
//--- change values
|
|
c11[0]=1;
|
|
c12[0]=2;
|
|
c12[1]=3;
|
|
//--- function call
|
|
CAp::Swap(c11,c12);
|
|
//--- check
|
|
if(CAp::Len(c11)==2 && CAp::Len(c12)==1)
|
|
{
|
|
okc1=okc1 && c11[0]==2;
|
|
okc1=okc1 && c11[1]==3;
|
|
okc1=okc1 && c12[0]==1;
|
|
}
|
|
else
|
|
okc1=false;
|
|
//--- Test B2 swaps
|
|
b21.Resize(1,2);
|
|
b22.Resize(2,1);
|
|
//--- change values
|
|
b21[0].Set(0,true);
|
|
b21[0].Set(1,false);
|
|
b22[0].Set(0,false);
|
|
b22[1].Set(0,true);
|
|
//--- function call
|
|
CAp::Swap(b21,b22);
|
|
//--- check
|
|
if(((CAp::Rows(b21)==2 && CAp::Cols(b21)==1) && CAp::Rows(b22)==1) && CAp::Cols(b22)==2)
|
|
{
|
|
okb2=okb2 && !b21[0][0];
|
|
okb2=okb2 && b21[1][0];
|
|
okb2=okb2 && b22[0][0];
|
|
okb2=okb2 && !b22[0][1];
|
|
}
|
|
else
|
|
okb2=false;
|
|
//--- Test I2 swaps
|
|
i21.Resize(1,2);
|
|
i22.Resize(2,1);
|
|
//--- change values
|
|
i21[0].Set(0,1);
|
|
i21[0].Set(1,2);
|
|
i22[0].Set(0,3);
|
|
i22[1].Set(0,4);
|
|
//--- function call
|
|
CAp::Swap(i21,i22);
|
|
//--- check
|
|
if(((CAp::Rows(i21)==2 && CAp::Cols(i21)==1) && CAp::Rows(i22)==1) && CAp::Cols(i22)==2)
|
|
{
|
|
oki2=oki2 && i21[0][0]==3;
|
|
oki2=oki2 && i21[1][0]==4;
|
|
oki2=oki2 && i22[0][0]==1;
|
|
oki2=oki2 && i22[0][1]==2;
|
|
}
|
|
else
|
|
oki2=false;
|
|
//--- Test R2 swaps
|
|
r21.Resize(1,2);
|
|
r22.Resize(2,1);
|
|
//--- change values
|
|
r21[0].Set(0,1);
|
|
r21[0].Set(1,2);
|
|
r22[0].Set(0,3);
|
|
r22[1].Set(0,4);
|
|
//--- function call
|
|
CAp::Swap(r21,r22);
|
|
//--- check
|
|
if(((CAp::Rows(r21)==2 && CAp::Cols(r21)==1) && CAp::Rows(r22)==1) && CAp::Cols(r22)==2)
|
|
{
|
|
okr2=okr2 && r21[0][0]==3.0;
|
|
okr2=okr2 && r21[1][0]==4.0;
|
|
okr2=okr2 && r22[0][0]==1.0;
|
|
okr2=okr2 && r22[0][1]==2.0;
|
|
}
|
|
else
|
|
okr2=false;
|
|
//--- Test C2 swaps
|
|
c21.Resize(1,2);
|
|
c22.Resize(2,1);
|
|
//--- change values
|
|
c21[0].Set(0,1);
|
|
c21[0].Set(1,2);
|
|
c22[0].Set(0,3);
|
|
c22[1].Set(0,4);
|
|
//--- function call
|
|
CAp::Swap(c21,c22);
|
|
//--- check
|
|
if(((CAp::Rows(c21)==2 && CAp::Cols(c21)==1) && CAp::Rows(c22)==1) && CAp::Cols(c22)==2)
|
|
{
|
|
okc2=okc2 && c21[0][0]==3;
|
|
okc2=okc2 && c21[1][0]==4;
|
|
okc2=okc2 && c22[0][0]==1;
|
|
okc2=okc2 && c22[0][1]==2;
|
|
}
|
|
else
|
|
okc2=false;
|
|
//--- summary
|
|
result=result && okb1;
|
|
result=result && okb2;
|
|
result=result && oki1;
|
|
result=result && oki2;
|
|
result=result && okr1;
|
|
result=result && okr2;
|
|
result=result && okc1;
|
|
result=result && okc2;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
//--- check
|
|
if(result)
|
|
Print("SWAPPING FUNCTIONS: OK");
|
|
else
|
|
Print("SWAPPING FUNCTIONS: FAILED");
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Tests for swapping functions |
|
|
//+------------------------------------------------------------------+
|
|
static bool CTestAlglibBasicsUnit::TestSerializationFunctions(const bool silent)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
bool okb;
|
|
bool oki;
|
|
bool okr;
|
|
int nb=0;
|
|
int ni=0;
|
|
int nr=0;
|
|
int i=0;
|
|
//--- objects of classes
|
|
CRec4Serialization r0;
|
|
CRec4Serialization r1;
|
|
//--- initialization
|
|
result=true;
|
|
okb=true;
|
|
oki=true;
|
|
okr=true;
|
|
//--- calculation
|
|
for(nb=1;nb<=4;nb++)
|
|
{
|
|
for(ni=1;ni<=4;ni++)
|
|
{
|
|
for(nr=1;nr<=4;nr++)
|
|
{
|
|
//--- allocation
|
|
ArrayResize(r0.m_b,nb);
|
|
for(i=0;i<=nb-1;i++)
|
|
r0.m_b[i]=CMath::RandomInteger(2)!=0;
|
|
//--- allocation
|
|
ArrayResize(r0.m_i,ni);
|
|
for(i=0;i<=ni-1;i++)
|
|
r0.m_i[i]=CMath::RandomInteger(10)-5;
|
|
//--- allocation
|
|
ArrayResize(r0.m_r,nr);
|
|
for(i=0;i<=nr-1;i++)
|
|
r0.m_r[i]=2*CMath::RandomReal()-1;
|
|
{
|
|
//--- This code passes data structure through serializers
|
|
//--- (serializes it to string and loads back)
|
|
CSerializer _local_serializer;
|
|
string _local_str;
|
|
//--- serialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.Alloc_Start();
|
|
CTestAlglibBasicsUnit::Rec4SerializationAlloc(_local_serializer,r0);
|
|
_local_serializer.SStart_Str();
|
|
CTestAlglibBasicsUnit::Rec4SerializationSerialize(_local_serializer,r0);
|
|
_local_serializer.Stop();
|
|
_local_str=_local_serializer.Get_String();
|
|
//--- unserialization
|
|
_local_serializer.Reset();
|
|
_local_serializer.UStart_Str(_local_str);
|
|
CTestAlglibBasicsUnit::Rec4SerializationUnserialize(_local_serializer,r1);
|
|
_local_serializer.Stop();
|
|
}
|
|
//--- check
|
|
if((CAp::Len(r0.m_b)==CAp::Len(r1.m_b) && CAp::Len(r0.m_i)==CAp::Len(r1.m_i)) && CAp::Len(r0.m_r)==CAp::Len(r1.m_r))
|
|
{
|
|
//--- change value
|
|
for(i=0;i<=nb-1;i++)
|
|
okb=okb && ((r0.m_b[i] && r1.m_b[i]) || (!r0.m_b[i] && !r1.m_b[i]));
|
|
for(i=0;i<=ni-1;i++)
|
|
oki=oki && r0.m_i[i]==r1.m_i[i];
|
|
for(i=0;i<=nr-1;i++)
|
|
okr=okr && r0.m_r[i]==r1.m_r[i];
|
|
}
|
|
else
|
|
oki=false;
|
|
}
|
|
}
|
|
}
|
|
//--- summary
|
|
result=result && okb;
|
|
result=result && oki;
|
|
result=result && okr;
|
|
//--- check
|
|
if(!silent)
|
|
{
|
|
//--- check
|
|
if(result)
|
|
Print("SERIALIZATION FUNCTIONS: OK");
|
|
else
|
|
{
|
|
Print("SERIALIZATION FUNCTIONS: FAILED");
|
|
Print("* BOOLEAN ");
|
|
//--- check
|
|
if(okb)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* INTEGER ");
|
|
//--- check
|
|
if(oki)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
Print("* REAL ");
|
|
//--- check
|
|
if(okr)
|
|
Print("OK");
|
|
else
|
|
Print("FAILED");
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|